@ax-llm/ax 20.0.2 → 21.0.0

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.
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: ax-agent
3
3
  description: This skill helps an LLM generate correct AxAgent code using @ax-llm/ax. Use when the user asks about agent(), child agents, namespaced functions, discovery mode, shared fields, llmQuery(...), RLM code execution, recursionOptions, or agent runtime behavior. For tuning and eval with agent.optimize(...), use ax-agent-optimize.
4
- version: "20.0.2"
4
+ version: "21.0.0"
5
5
  ---
6
6
 
7
7
  # AxAgent Codegen Rules (@ax-llm/ax)
@@ -21,15 +21,16 @@ Your job is not just to write valid code. Your job is to choose the smallest cor
21
21
  - Use `agent(...)`, not `new AxAgent(...)`.
22
22
  - Prefer `fn(...)` for host-side function definitions instead of hand-writing JSON Schema objects.
23
23
  - Prefer namespaced functions such as `utils.search(...)` or `kb.find(...)`.
24
- - Assume the child-agent module is `agents` unless `agentIdentity.namespace` is set.
24
+ - Pass child agents directly in `functions: [...]`. They land under their `agentIdentity.namespace` (or `utils` if unset), exactly like a `fn()` tool.
25
25
  - If `functions.discovery` is `true`, discover callables from modules before using them.
26
26
  - In stdout-mode RLM, use one observable `console.log(...)` step per non-final actor turn.
27
27
  - Prefer `promptLevel: 'default'` for normal use; use `promptLevel: 'detailed'` when you want extra anti-pattern examples and tighter teaching scaffolding in the actor prompt.
28
28
  - Default to `contextPolicy: { preset: 'checkpointed', budget: 'balanced' }` for most RLM tasks.
29
29
  - Prefer `contextPolicy: { preset: 'adaptive', budget: 'balanced' }` when older successful turns should collapse sooner while live runtime state stays visible.
30
- - Prefer `actorModelPolicy` when the actor may need to upgrade after repeated error turns or discovery in specific namespaces without also upgrading the responder.
31
- - Use `actorTurnCallback` when the user needs per-turn observability into generated code, raw runtime result, formatted output, or provider thoughts.
32
- - Use `agentStatusCallback` when the user wants real-time task progress updates from the actor via `await success(message)` and `await failed(message)` calls.
30
+ - Prefer `executorModelPolicy` when the actor may need to upgrade after repeated error turns or discovery in specific namespaces without also upgrading the responder.
31
+ - Use `executorTurnCallback` when the user needs per-turn observability into generated code, raw runtime result, formatted output, or provider thoughts.
32
+ - Use `agentStatusCallback` when the user wants real-time task progress updates from the actor via `await reportSuccess(message)` and `await reportFailure(message)` calls.
33
+ - Use `onFunctionCall` when the user wants to observe every function the actor invokes from the JS runtime (their own registered functions plus internal globals like child agents, `discoverModules`, `discoverFunctions`, `consult`).
33
34
 
34
35
  ## Decision Guide
35
36
 
@@ -38,12 +39,15 @@ Map user intent to agent shape before writing code:
38
39
  - "Use tools and answer" -> plain `agent(...)` with local functions, no recursion, no extra observability.
39
40
  - "Inspect large context with code" -> add `runtime`, `contextFields`, and usually `contextPolicy: { preset: 'checkpointed', budget: 'balanced' }`.
40
41
  - "Delegate focused semantic subtasks" -> use `llmQuery(...)`; add `mode: 'advanced'` only when child tasks need their own runtime, tools, or discovery loop.
41
- - "Need child agents with distinct responsibilities" -> use `agents.local`, and add `fields.shared` only when parent inputs truly need to flow into children.
42
+ - "Need child agents with distinct responsibilities" -> add the child agents to the parent's `functions: [...]` list. Set `agentIdentity.namespace` on each child to control where it lands in the JS runtime (e.g. `team.writer(...)`); otherwise it lands under `utils.<name>` like any other tool.
42
43
  - "Need tool discovery because names/schemas are not stable" -> use `functions.discovery: true` and generate discovery-first code.
43
- - "Need a stronger actor only when the run gets noisy or large" -> use `actorModelPolicy` and keep the responder model separate.
44
- - "Need debugging or traceability" -> start with `debug: true` or `actorTurnCallback`; do not add both unless the user clearly wants both prompt/runtime visibility and structured telemetry.
45
- - "Need real-time progress updates" -> add `agentStatusCallback` so the actor can call `await success(message)` and `await failed(message)` to report sub-task progress.
44
+ - "Need a stronger actor only when the run gets noisy or large" -> use `executorModelPolicy` and keep the responder model separate.
45
+ - "Need debugging or traceability" -> start with `debug: true` or `executorTurnCallback`; do not add both unless the user clearly wants both prompt/runtime visibility and structured telemetry.
46
+ - "Need real-time progress updates" -> add `agentStatusCallback` so the actor can call `await reportSuccess(message)` and `await reportFailure(message)` to report sub-task progress.
47
+ - "Need to log/trace every tool call" -> add `onFunctionCall` to receive `{ name, qualifiedName, args, kind }` for each function invoked by the runtime; `kind` is `'external'` for caller-registered functions and `'internal'` for agent-injected ones (child agents, discovery, skills loader).
46
48
  - "Need certain errors to escape the agent loop" -> add `bubbleErrors` with an array of error classes; those errors propagate through function handlers, actor code, and llmQuery sub-agents all the way to `.forward()`.
49
+ - "Need to pull relevant memories into context" -> add `onMemoriesSearch` with a vector/BM25 search callback; the distiller and executor gain `await recall(searches)` (returns void; results land on `inputs.memories` next turn) and an `inputs.memories` field. Add `onUsedMemories` if you want to observe what gets loaded.
50
+ - "Need to load skill guides into the executor system prompt on demand" -> add `onSkillsSearch`; the executor gains `await consult(searches)` (returns void; loaded skill bodies render under "Loaded Skills" next turn). Add `onUsedSkills` for observability.
47
51
 
48
52
  Choose options based on user needs, not feature completeness:
49
53
 
@@ -53,7 +57,17 @@ Choose options based on user needs, not feature completeness:
53
57
 
54
58
  ## Mental Model
55
59
 
56
- Treat `AxAgent` as a long-running JavaScript REPL that the actor steers over multiple turns, not as a fresh script generator on every turn.
60
+ `AxAgent` is a three-stage pipeline. Each `forward()` call walks (some subset of) the stages in order:
61
+
62
+ ```
63
+ distiller (RLM actor) → executor (RLM actor) → responder (synthesizer)
64
+ ```
65
+
66
+ - **distiller** always runs first. It sees all original inputs so it can understand and normalize the task; declared `contextFields` stay runtime-only when present. It distils relevant evidence by writing JS code in a multi-turn loop, then calls `final(request, evidence)`. The request becomes the executor's `inputs.executorRequest`; the distiller should expand the original user task with facts found in context, including follow-ups like "yes, do it". When no `contextFields` are configured, it still performs request normalization over the original inputs with `contextFields: []`. **The distiller has no tools** — it only reads, narrows, and forwards. If the user asks for an action (e.g. "run a command"), the distiller forwards it via `final(request, {})`; refusing on the grounds of "no tools" is wrong.
67
+ - **executor** always runs. It receives non-context inputs plus `inputs.executorRequest` and `inputs.distilledContext` from the distiller's `final(request, evidence)` payload. Raw context fields are not present in the executor stage. The executor owns tool use and decides whether to call its available functions or finish directly from the distilled evidence.
68
+ - **responder** always runs last. It synthesizes the user's output signature from whichever upstream actor finished the run.
69
+
70
+ Treat both actor stages (distiller, executor) as long-running JavaScript REPLs that the actor steers over multiple turns, not as fresh script generators on every turn.
57
71
 
58
72
  - Successful code leaves variables, functions, imports, and computed values available in the runtime session.
59
73
  - The actor should continue from existing runtime state instead of recreating prior work.
@@ -90,7 +104,7 @@ Treat these knobs as a bundle:
90
104
 
91
105
  - `contextPolicy.preset` decides how much raw history the actor keeps seeing.
92
106
  - `promptLevel` decides whether the actor gets just the standard rules or those rules plus detailed anti-pattern examples.
93
- - `actorModelPolicy` decides when the actor switches to an override model without changing the responder.
107
+ - `executorModelPolicy` decides when the actor switches to an override model without changing the responder.
94
108
  - Model size decides how well the actor can recover from compressed context and terse guidance.
95
109
 
96
110
  Recommended combinations:
@@ -99,7 +113,7 @@ Recommended combinations:
99
113
  - Long multi-turn task, general default, medium-to-strong model: `preset: 'checkpointed', budget: 'balanced'`.
100
114
  - Long task where you want older successful work summarized sooner: `preset: 'adaptive', budget: 'balanced'`.
101
115
  - Very long task under token pressure, stronger model only: `preset: 'lean'`.
102
- - Discovery-heavy work with a cheaper default actor: keep the responder cheap and add `actorModelPolicy` so only the actor upgrades under pressure.
116
+ - Discovery-heavy work with a cheaper default actor: keep the responder cheap and add `executorModelPolicy` so only the actor upgrades under pressure.
103
117
 
104
118
  Practical rule:
105
119
 
@@ -108,13 +122,14 @@ Practical rule:
108
122
  - `checkpointed + balanced` is the default middle ground for real agent work.
109
123
  - `adaptive + balanced` is the proactive-summarization variant when you want older successful work compressed sooner.
110
124
  - `lean` should be reserved for models that can reason well from runtime state plus summaries instead of exact old code/output.
111
- - `actorModelPolicy` is usually better than globally upgrading the whole agent when the bottleneck is actor exploration rather than responder synthesis.
125
+ - `executorModelPolicy` is usually better than globally upgrading the whole agent when the bottleneck is actor exploration rather than responder synthesis.
112
126
 
113
127
  ## Critical Rules
114
128
 
115
129
  - Use `agent(...)` factory syntax for new code.
116
- - If `agentIdentity.namespace` is set, call child agents through that module, not `agents`.
130
+ - Add child agents to the parent's `functions: [...]` list. Each child's `agentIdentity.namespace` (or `utils`, the default) determines the runtime call site, e.g. `await team.writer({...})`.
117
131
  - If `functions.discovery` is `true`, call `discoverModules(...)` first, then `discoverFunctions(...)`, then call only discovered functions.
132
+ - The `Javascript Code` output field uses Ax's normal field-pair response shape, but its value must be executable JavaScript only; do not emit plain `task:` / `evidence:` labels, prose, markdown fences, or `<think>` tags as the value.
118
133
  - In stdout-mode RLM, non-final turns must emit exactly one `console.log(...)` and stop immediately after it.
119
134
  - Never combine `console.log(...)` with `await final(...)` or `await askClarification(...)` in the same actor turn.
120
135
  - Inside actor-authored JavaScript, `await final(...)` and `await askClarification(...)` end the current turn immediately; code after them is dead code.
@@ -156,21 +171,22 @@ const result = await assistant.forward(llm, { query: 'What is TypeScript?' });
156
171
  console.log(result.answer);
157
172
  ```
158
173
 
159
- ## Child Agents And Module Namespace
174
+ ## Child Agents As Tools
160
175
 
161
- Default child-agent module:
176
+ Child agents are passed in the parent's `functions` list — there's no separate `agents` option. Each child agent's `agentIdentity.namespace` (or `utils`, the default) determines where it lands in the JS runtime:
162
177
 
163
178
  ```typescript
164
179
  const writer = agent('draft:string -> revision:string', {
165
180
  agentIdentity: {
166
181
  name: 'Writer',
167
182
  description: 'Polishes drafts',
183
+ namespace: 'team',
168
184
  },
169
185
  contextFields: [],
170
186
  });
171
187
 
172
188
  const coordinator = agent('query:string -> answer:string', {
173
- agents: { local: [writer] },
189
+ functions: [writer],
174
190
  contextFields: [],
175
191
  });
176
192
  ```
@@ -178,42 +194,40 @@ const coordinator = agent('query:string -> answer:string', {
178
194
  Generated runtime call:
179
195
 
180
196
  ```javascript
181
- const result = await agents.writer({ draft: '...' });
182
- ```
183
-
184
- Custom child-agent module:
185
-
186
- ```typescript
187
- const writer = agent('draft:string -> revision:string', {
188
- agentIdentity: {
189
- name: 'Writer',
190
- description: 'Polishes drafts',
191
- },
192
- contextFields: [],
193
- });
194
-
195
- const coordinator = agent('query:string -> answer:string', {
196
- agentIdentity: {
197
- name: 'Coordinator',
198
- description: 'Routes work',
199
- namespace: 'team',
200
- },
201
- agents: { local: [writer] },
202
- contextFields: [],
203
- });
197
+ const result = await team.writer({ draft: '...' });
204
198
  ```
205
199
 
206
- Generated runtime call:
200
+ Without `agentIdentity.namespace`, the child lands under `utils.<name>` like any other tool:
207
201
 
208
202
  ```javascript
209
- const result = await team.writer({ draft: '...' });
203
+ const result = await utils.writer({ draft: '...' });
210
204
  ```
211
205
 
212
206
  Rules:
213
207
 
214
- - Default child-agent module is `agents`.
215
- - If `agentIdentity.namespace` is set, that becomes the child-agent module.
216
- - Do not hardcode `agents.<name>(...)` when a custom namespace is configured.
208
+ - Add child agents to `functions: [...]` — same array as `fn(...)` tools.
209
+ - Set `agentIdentity.namespace` on the child to control its runtime call site.
210
+ - `onFunctionCall` observers receive `kind: 'internal'` for agent-derived calls (vs. `'external'` for user-registered tools).
211
+
212
+ ### Reserved namespace names
213
+
214
+ The agent runtime injects a fixed set of globals into the JS REPL. These names cannot be used as `agentIdentity.namespace` values or as agent-function namespaces — the constructor throws `Agent function namespace "<name>" conflicts with an AxAgent runtime global and is reserved`.
215
+
216
+ ```
217
+ inputs // input field bag
218
+ llmQuery // delegated semantic queries
219
+ final // turn-end signal
220
+ askClarification // request user clarification
221
+ reportSuccess // mid-run success ping (when agentStatusCallback set)
222
+ reportFailure // mid-run failure ping (when agentStatusCallback set)
223
+ inspectRuntime // runtime variable snapshot
224
+ discoverModules // module discovery (when functionDiscovery: true)
225
+ discoverFunctions // function discovery (when functionDiscovery: true)
226
+ consult // skill load (when onSkillsSearch set)
227
+ recall // memory load (when onMemoriesSearch set)
228
+ ```
229
+
230
+ Pick any other lowercase identifier (`utils`, `kb`, `tools`, `team`, `db`, etc.) — the runtime accepts arbitrary names as long as they don't collide with this list.
217
231
 
218
232
  ## Tool Functions And Namespaces
219
233
 
@@ -612,7 +626,7 @@ Reason: turn 2 reuses `customers` from the persistent runtime. `Live Runtime Sta
612
626
 
613
627
  ## AxJSRuntime Security
614
628
 
615
- Default `new AxJSRuntime()` is hardened: no network, no fs, no child_process, `import()` blocked, intrinsics frozen, `ShadowRealm` locked to `undefined`, worker IPC locked in browser/Deno, and on Node 20+ the OS Permission Model auto-engages (using `--permission` on Node 23.5+ or `--experimental-permission` on Node 20–23.4) as a second defense layer. You do not need to configure anything to get the strict profile — opt in only to the capability the user actually asked for.
629
+ Default `new AxJSRuntime()` is hardened: no network, no fs, no child_process, `import()` blocked, intrinsics frozen, `ShadowRealm` locked to `undefined`, worker IPC locked in browser/Deno/Bun, Bun workers use `smol: true`, and on Node 20+ the OS Permission Model auto-engages (using `--permission` on Node 23.5+ or `--experimental-permission` on Node 20–23.4) as a second defense layer. You do not need to configure anything to get the strict profile — opt in only to the capability the user actually asked for.
616
630
 
617
631
  **Permission enum** (`AxJSRuntimePermission`):
618
632
  `NETWORK`, `STORAGE`, `CODE_LOADING`, `COMMUNICATION`, `TIMING`, `WORKERS`, `FILESYSTEM` (new), `CHILD_PROCESS` (new).
@@ -625,9 +639,9 @@ Default `new AxJSRuntime()` is hardened: no network, no fs, no child_process, `i
625
639
  | `allowedModules` | `[]` | Whitelist of specifiers permitted when `blockDynamicImport` is on. |
626
640
  | `freezeIntrinsics` | `true` | Freezes `Object`/`Array`/`Promise`/etc. prototypes. |
627
641
  | `blockShadowRealm` | `true` | Locks `globalThis.ShadowRealm` to `undefined`. |
628
- | `lockWorkerIPC` | `true` | Locks `self.postMessage`/`onmessage` in browser/Deno workers. |
642
+ | `lockWorkerIPC` | `true` | Locks `self.postMessage`/`onmessage` in browser/Deno/Bun workers. |
629
643
  | `preventGlobalThisExtensions` | `false` | Opt-in; breaks top-level `var/let/const` persistence across turns. |
630
- | `useNodePermissionModel` | `'auto'` | Engages Node Permission Model on Node 20+ (`--permission` on 23.5+, `--experimental-permission` on 20–23.4); skips on older runtimes. |
644
+ | `useNodePermissionModel` | `'auto'` | Engages Node Permission Model on Node 20+ (`--permission` on 23.5+, `--experimental-permission` on 20–23.4); skips on Bun, Deno, browsers, and older Node. |
631
645
  | `nodePermissionAllowlist` | `undefined` | Fine-grained `{ fsRead, fsWrite, childProcess, addons, wasi }`. |
632
646
  | `resourceLimits` | `undefined` | `{ maxOldGenerationSizeMb, maxYoungGenerationSizeMb, codeRangeSizeMb, stackSizeMb }`. |
633
647
  | `allowDenoRemoteImport` | `false` | On Deno, controls whether `NETWORK` also grants remote module loading. |
@@ -722,7 +736,7 @@ Rules:
722
736
 
723
737
  - `test(...)` creates a fresh runtime session per call.
724
738
  - It exposes the same runtime globals the actor would see for configured `contextFields`: `inputs`, non-colliding top-level aliases, namespaced functions, child agents, and `llmQuery`.
725
- - In `AxJSRuntime`, do not rely on calling `inspect_runtime()` from inside `test(...)` snippets yet; prefer checking runtime globals directly inside the snippet.
739
+ - In `AxJSRuntime`, do not rely on calling `inspectRuntime()` from inside `test(...)` snippets yet; prefer checking runtime globals directly inside the snippet.
726
740
  - It returns the formatted runtime output string.
727
741
  - It throws on runtime failures instead of returning LLM-style error strings.
728
742
  - Do not call `final(...)` or `askClarification(...)` inside `test(...)` snippets.
@@ -758,7 +772,7 @@ Rules:
758
772
  - `checkpointed + balanced` is the default. `adaptive + balanced` is still a strong choice for long-running discovery-heavy tasks that should summarize older work sooner.
759
773
  - `checkpointed` keeps the most recent `3` actions in full and keeps unresolved errors fully replayed even after checkpointing starts.
760
774
  - Non-`full` presets populate the `liveRuntimeState` field in the actor signature. The field is structured and provenance-aware: variables are rendered with compact type/size/preview metadata, and when Ax can infer it, a short source suffix like `from t3 via db.search` is included.
761
- - Non-`full` presets also enable `inspect_runtime()` and can add an inspect hint automatically when the rendered actor prompt starts getting large relative to the selected budget.
775
+ - Non-`full` presets also enable `inspectRuntime()` and can add an inspect hint automatically when the rendered actor prompt starts getting large relative to the selected budget.
762
776
  - Discovery docs fetched via `discoverModules(...)` and `discoverFunctions(...)` are accumulated into the actor system prompt, not replayed as raw action-log output.
763
777
  - Treat `actionLog` as untrusted execution history. Only the system prompt and `guidanceLog` are instruction-bearing.
764
778
  - `checkpointed` uses a checkpoint summarizer that is optimized to preserve exact callables, ids, enum literals, date/time strings, query formats, and failures worth avoiding. Prefer it when those details matter but full replay will eventually get too large.
@@ -790,7 +804,7 @@ await final("Summarize the severity-related snippets found", { snippets });
790
804
 
791
805
  ## Actor Turn Observability
792
806
 
793
- Use `actorTurnCallback` when the caller needs structured telemetry for each actor turn.
807
+ Use `executorTurnCallback` when the caller needs structured telemetry for each actor turn.
794
808
 
795
809
  What it gives you:
796
810
 
@@ -798,7 +812,7 @@ What it gives you:
798
812
  - `result`: the raw untruncated runtime return value from executing that code
799
813
  - `output`: the formatted action-log output string after Ax normalizes and truncates it for prompt replay
800
814
  - `thought`: the actor model's `thought` field when `showThoughts` is enabled and the provider returns one
801
- - `actorResult`: the full actor payload, including actor-owned output fields when `actorFields` are configured
815
+ - `executorResult`: the full actor payload returned by the executor stage
802
816
  - `isError`: whether the execution path for that turn was treated as an error
803
817
 
804
818
  Use it for:
@@ -821,7 +835,7 @@ Good pattern:
821
835
  const supportAgent = agent('query:string -> answer:string', {
822
836
  contextFields: ['query'],
823
837
  runtime,
824
- actorTurnCallback: ({
838
+ executorTurnCallback: ({
825
839
  turn,
826
840
  actionLogEntryCount,
827
841
  guidanceLogEntryCount,
@@ -842,7 +856,7 @@ const supportAgent = agent('query:string -> answer:string', {
842
856
  thought,
843
857
  });
844
858
  },
845
- actorOptions: {
859
+ executorOptions: {
846
860
  model: 'gpt-5.4-mini',
847
861
  showThoughts: true,
848
862
  },
@@ -851,7 +865,7 @@ const supportAgent = agent('query:string -> answer:string', {
851
865
 
852
866
  ## Agent Status Callback
853
867
 
854
- Use `agentStatusCallback` when the caller wants real-time progress updates from the actor. When set, the actor can call `await success(message)` and `await failed(message)` in its JavaScript turns.
868
+ Use `agentStatusCallback` when the caller wants real-time progress updates from the actor. When set, the actor can call `await reportSuccess(message)` and `await reportFailure(message)` in its JavaScript turns.
855
869
 
856
870
  ```typescript
857
871
  const supportAgent = agent('query:string -> answer:string', {
@@ -866,24 +880,211 @@ const supportAgent = agent('query:string -> answer:string', {
866
880
  Rules:
867
881
 
868
882
  - `agentStatusCallback` receives `(message: string, status: 'success' | 'failed')`.
869
- - When set, the actor prompt automatically includes `success(message)` and `failed(message)` as available runtime functions.
883
+ - When set, the actor prompt automatically includes `reportSuccess(message)` and `reportFailure(message)` as available runtime functions.
870
884
  - The actor is instructed to keep the user updated of task progress.
871
- - `success` and `failed` are reserved runtime names when the callback is configured.
885
+ - `reportSuccess` and `reportFailure` are reserved runtime names when the callback is configured.
872
886
  - Child agents inherit the callback via the rlm config.
873
887
 
888
+ ## On Function Call
889
+
890
+ Use `onFunctionCall` when the caller wants to observe every function call the actor makes from the JS runtime. Fires before the underlying function runs.
891
+
892
+ ```typescript
893
+ const supportAgent = agent('query:string -> answer:string', {
894
+ contextFields: ['query'],
895
+ runtime,
896
+ agents: [helperAgent],
897
+ functions: [{ name: 'lookupOrder', namespace: 'tools', /* ... */ }],
898
+ onFunctionCall: ({ name, qualifiedName, args, kind }) => {
899
+ console.log(`[${kind}] ${qualifiedName}`, args);
900
+ },
901
+ });
902
+ ```
903
+
904
+ Rules:
905
+
906
+ - Receives `{ name, qualifiedName, args, kind }` where:
907
+ - `name` is the bare function name (e.g. `'lookupOrder'`).
908
+ - `qualifiedName` is the namespaced name as the actor sees it (e.g. `'tools.lookupOrder'`); for un-namespaced runtime globals it equals `name`.
909
+ - `args` is the resolved positional/named arguments object (`Record<string, unknown>`).
910
+ - `kind` is `'external'` for caller-registered `functions`, `'internal'` for agent-injected globals: child `agents`, `discoverModules`, `discoverFunctions` (when `functionDiscovery: true`), and `consult` (when `onSkillsSearch` is set).
911
+ - Fires once per call, before the function executes. Errors thrown inside the callback are swallowed so they cannot break the actor loop.
912
+ - Independent from the DSP-layer `onFunctionCall` on `AxProgramForwardOptions` — that hook is for LLM tool-calls and never fires under AxAgent (the agent injects functions as runtime globals, not as LLM tools).
913
+
914
+ ## Memory Search
915
+
916
+ Use `onMemoriesSearch` when the agent needs to pull task-relevant context — user preferences, prior decisions, project facts, past conversations — from an external store (vector DB, BM25, KV) instead of stuffing everything into the prompt upfront. The actor decides what to load, when, and how much.
917
+
918
+ When `onMemoriesSearch` is set, the distiller and executor stages gain:
919
+
920
+ 1. An `inputs.memories` field — an array of `{ id, content }` entries the actor reads directly. Each `content` is opaque markdown (frontmatter, if any, is not parsed).
921
+ 2. A `recall(searches: string[]): void` global the actor `await`s to load more entries. Recalled entries are appended to `inputs.memories` and visible from the next turn onward — similar to how `guidance` accumulates. **`recall()` returns nothing**; read `inputs.memories` next turn to see what landed.
922
+
923
+ The responder stage does not receive memories.
924
+
925
+ ### Enabling
926
+
927
+ ```typescript
928
+ import { agent } from '@ax-llm/ax';
929
+ import type { AxAgentMemoriesSearchFn } from '@ax-llm/ax';
930
+
931
+ // Each result must be { id: string; content: string }
932
+ const onMemoriesSearch: AxAgentMemoriesSearchFn = async (
933
+ searches,
934
+ alreadyLoaded
935
+ ) => {
936
+ // `searches` is the full array passed to recall(...) — batch your
937
+ // store lookup in one round-trip.
938
+ // `alreadyLoaded` is the snapshot of `inputs.memories` already in
939
+ // scope. Filter your results so you don't refetch what's already
940
+ // loaded (the runtime dedupes by id, but skipping here saves a
941
+ // round-trip and avoids charging the actor for duplicate tokens).
942
+ const skip = new Set(alreadyLoaded.map((m) => m.id));
943
+ const fresh = await myVectorDB.searchBatch(searches, { topK: 3 });
944
+ return fresh.filter((m) => !skip.has(m.id));
945
+ };
946
+
947
+ const myAgent = agent({
948
+ // ...
949
+ onMemoriesSearch,
950
+ });
951
+ ```
952
+
953
+ ### Actor usage (distiller or executor code)
954
+
955
+ ```javascript
956
+ // Turn 1: kick off a batched lookup. Pass all queries in one call —
957
+ // don't loop or use Promise.all (the runtime rejects that as a policy
958
+ // violation; your callback should fan out internally).
959
+ await recall(['user preferences', 'project constraints']);
960
+
961
+ // Turn 2+: matched entries are now visible on `inputs.memories`.
962
+ const prefs = inputs.memories.find(m => m.id === 'user-prefs-v2');
963
+ ```
964
+
965
+ ### Behaviour
966
+
967
+ - `recall()` invokes `onMemoriesSearch` with `(searches, alreadyLoaded)` and returns `void`. `alreadyLoaded` is the current `inputs.memories` snapshot — filter your store results against it to skip duplicates. Results land on `inputs.memories` for subsequent turns.
968
+ - Entries are **deduped by `id`** (last-write-wins) and **sorted by `id`** for prefix-cache stability.
969
+ - Memories loaded by the distiller **thread automatically to the executor** — no second `recall()` needed for those entries.
970
+ - `recall()` may be called multiple times per turn; results accumulate. The merge dedupes against existing entries, so re-running the same search is cheap.
971
+ - **Lifetime is one `.forward()` call.** `inputs.memories` resets between calls. To carry memories across calls, persist them in your store and recall them again on the next call.
972
+
973
+ ### Child agents
974
+
975
+ Child agents do **not** inherit `onMemoriesSearch` automatically. If a recursive `llmQuery` advanced child or a registered child agent should also have `recall()`, set `onMemoriesSearch` on that agent's options explicitly.
976
+
977
+ ### Carrying memories across `.forward()` calls
978
+
979
+ `inputs.memories` resets between runs. To preserve continuity across calls, observe loads with `onUsedMemories` and replay them on the next call's first `recall()` (or via your store):
980
+
981
+ ```typescript
982
+ const carried = new Map<string, string>();
983
+
984
+ const myAgent = agent({
985
+ // ...
986
+ onMemoriesSearch: async (searches) => {
987
+ const fresh = await myVectorDB.searchBatch(searches, { topK: 3 });
988
+ // Re-surface anything that landed on prior runs so the actor sees it
989
+ // alongside fresh matches.
990
+ const carriedAsResults = [...carried.entries()].map(([id, content]) => ({
991
+ id,
992
+ content,
993
+ }));
994
+ return [...carriedAsResults, ...fresh];
995
+ },
996
+ onUsedMemories: (results) => {
997
+ for (const r of results) carried.set(r.id, r.content);
998
+ },
999
+ });
1000
+ ```
1001
+
1002
+ ## Skills Search
1003
+
1004
+ Use `onSkillsSearch` when the agent needs to load skill guides — usage instructions, runbooks, domain conventions — into the executor's system prompt on demand. The actor decides which skills to fetch and when, so you don't pre-render every skill into every prompt.
1005
+
1006
+ When `onSkillsSearch` is set, the executor stage gains:
1007
+
1008
+ 1. A "Loaded Skills" section in the system prompt that renders matched skill bodies (sorted by `name`).
1009
+ 2. A `consult(searches: string[]): void` global the actor `await`s to load more skills. Loaded entries appear in the next turn's prompt — `consult()` itself returns nothing.
1010
+
1011
+ The distiller and responder do not see skills. Only the executor.
1012
+
1013
+ ### Enabling
1014
+
1015
+ ```typescript
1016
+ import { agent } from '@ax-llm/ax';
1017
+ import type { AxAgentSkillsSearchFn } from '@ax-llm/ax';
1018
+
1019
+ // Each result must be { name: string; content: string }
1020
+ const onSkillsSearch: AxAgentSkillsSearchFn = async (searches) => {
1021
+ return mySkillStore.searchBatch(searches, { topK: 2 });
1022
+ };
1023
+
1024
+ const myAgent = agent({
1025
+ // ...
1026
+ onSkillsSearch,
1027
+ });
1028
+ ```
1029
+
1030
+ ### Actor usage (executor code only)
1031
+
1032
+ ```javascript
1033
+ // Pass all queries in one call — don't loop or use Promise.all (the
1034
+ // runtime rejects that as a policy violation; your callback should
1035
+ // fan out internally).
1036
+ await consult(['release-checklist', 'incident-response']);
1037
+
1038
+ // Next turn: the loaded skill bodies render under the "Loaded Skills"
1039
+ // system-prompt section, ready to apply directly.
1040
+ ```
1041
+
1042
+ ### Behaviour
1043
+
1044
+ - `consult()` invokes `onSkillsSearch` with the raw search strings and returns `void`. Matched skills land under "Loaded Skills" for the next turn.
1045
+ - Entries are deduped by `name` (last-write-wins) and sorted by `name` for prefix-cache stability.
1046
+ - **Skills persist on the agent's `currentSkillsPromptState` across `.forward()` calls** (unlike memories). Use `agent.getState()` / `setState(...)` to serialize/restore.
1047
+ - `consult()` may be called multiple times; results accumulate.
1048
+ - Child agents do **not** inherit `onSkillsSearch` — wire it explicitly per agent.
1049
+
1050
+ ### Preloading Skills (`skills` option)
1051
+
1052
+ If the caller already knows which skills are relevant, pass them up-front instead of round-tripping through `consult()`:
1053
+
1054
+ - **Init-time** — `skills` on `AxAgentOptions` (constructor) seeds the executor's prompt at agent creation. They survive `setState(...)` resets, so they're always present from turn 1.
1055
+ - **Forward-time** — `skills` on the `forward(ai, values, { skills })` options merge in at the start of that call (executor stage only — distiller and responder ignore it).
1056
+
1057
+ Both accept the same shape `onSkillsSearch` returns: `readonly AxAgentSkillResult[]` (`{ name, content }[]`). Forward overrides init by `name` (same `Map.set` semantics as runtime-loaded skills). `onUsedSkills` is **not** fired for preset skills — that callback is for runtime `consult(...)` analytics.
1058
+
1059
+ ```ts
1060
+ const agent = new AxAgent(
1061
+ { signature: '...', agentIdentity: { name: 'release-bot', namespace: 'utils' } },
1062
+ { skills: [{ name: 'release-checklist', content: '...' }] }
1063
+ );
1064
+
1065
+ await agent.forward(ai, values, {
1066
+ // overrides any same-named init skill, layers on top of runtime consult() loads
1067
+ skills: [{ name: 'incident-response', content: '...' }],
1068
+ });
1069
+ ```
1070
+
1071
+ You can use `skills` without setting `onSkillsSearch` at all — handy for static guides where the actor never needs to fetch more.
1072
+
874
1073
  ## Option Layout
875
1074
 
876
1075
  Use these top-level controls consistently:
877
1076
 
878
1077
  - `mode`: controls whether `llmQuery(...)` stays simple or delegates to recursive child agents in advanced mode
879
1078
  - `recursionOptions.maxDepth`: limits recursive `llmQuery(...)` depth
880
- - `maxSubAgentCalls`: shared delegated-call budget across the whole run, including recursive children
1079
+ - `maxSubAgentCalls`: shared delegated-call budget across the whole run, including recursive children (default: 100)
881
1080
  - `maxRuntimeChars`: runtime/output truncation ceiling for console logs, tool results, and interpreter output replay. The actual limit is computed dynamically each turn based on remaining context budget (see **Dynamic Output Truncation** below)
882
1081
  - `summarizerOptions`: default model/options for the internal checkpoint summarizer
883
- - `actorOptions`: actor-only forward options such as `description`, `model`, `modelConfig`, `thinkingTokenBudget`, and `showThoughts`
884
- - `actorModelPolicy`: actor-only model override rules based on consecutive error turns or discovery fetches from listed namespaces
885
- - `responderOptions`: responder-only forward options
886
- - `agentStatusCallback`: real-time progress updates from actor via `success(message)` and `failed(message)`
1082
+ - `contextOptions`: distiller-stage forward options (description, model, maxTurns, etc.). One of three peer stage-config bags.
1083
+ - `executorOptions`: executor-stage forward options such as `description`, `model`, `modelConfig`, `thinkingTokenBudget`, and `showThoughts`
1084
+ - `executorModelPolicy`: executor-only model override rules based on consecutive error turns or discovery fetches from listed namespaces
1085
+ - `responderOptions`: responder-stage forward options
1086
+ - `agentStatusCallback`: real-time progress updates from actor via `reportSuccess(message)` and `reportFailure(message)`
1087
+ - `onFunctionCall`: observe every runtime function call (`{ name, qualifiedName, args, kind: 'internal' | 'external' }`)
887
1088
  - `judgeOptions`: built-in judge options for `agent.optimize(...)`; for tuning workflows use the `ax-agent-optimize` skill
888
1089
  - `bubbleErrors`: error classes that propagate out of function handlers, actor code, and llmQuery sub-agents directly to `.forward()` instead of being caught and returned as `[ERROR]` strings
889
1090
 
@@ -906,11 +1107,15 @@ const researchAgent = agent('query:string -> answer:string', {
906
1107
  preset: 'checkpointed',
907
1108
  budget: 'balanced',
908
1109
  },
909
- actorOptions: {
1110
+ contextOptions: {
1111
+ model: 'gpt-5.4-mini',
1112
+ maxTurns: 3,
1113
+ },
1114
+ executorOptions: {
910
1115
  description: 'Use tools first and keep JS steps small.',
911
1116
  model: 'gpt-5.4-mini',
912
1117
  },
913
- actorModelPolicy: [
1118
+ executorModelPolicy: [
914
1119
  {
915
1120
  model: 'gpt-5.4',
916
1121
  aboveErrorTurns: 2,
@@ -928,10 +1133,10 @@ Semantics:
928
1133
  - `mode` stays top-level; there is no `recursionOptions.mode`.
929
1134
  - `maxRuntimeChars` sets the truncation ceiling and is separate from `contextPolicy.budget`. The effective limit per turn is computed dynamically (see below).
930
1135
  - `summarizerOptions` tunes only the internal checkpoint summarizer. It does not change actor or responder model selection.
931
- - The current merged actor model stays the default base model. `actorModelPolicy` only overrides it when a rule matches.
932
- - `actorModelPolicy` only switches the actor model. It does not change `responderOptions.model`.
933
- - Recursive child agents can inherit `actorModelPolicy`; use a child override only when that child needs different routing behavior.
934
- - `actorModelPolicy` entries are ordered from weaker to stronger. If multiple rules match, the last matching entry wins.
1136
+ - The current merged actor model stays the default base model. `executorModelPolicy` only overrides it when a rule matches.
1137
+ - `executorModelPolicy` only switches the actor model. It does not change `responderOptions.model`.
1138
+ - Recursive child agents can inherit `executorModelPolicy`; use a child override only when that child needs different routing behavior.
1139
+ - `executorModelPolicy` entries are ordered from weaker to stronger. If multiple rules match, the last matching entry wins.
935
1140
  - If one entry also defines `namespaces`, any successful `discoverFunctions(...)` fetch from one of those namespaces marks the rule as matched starting on the next actor turn.
936
1141
 
937
1142
  When choosing these options for a user:
@@ -939,8 +1144,8 @@ When choosing these options for a user:
939
1144
  - Do not add `mode: 'advanced'` just because recursion exists as a feature. Add it only when delegated children need their own tool/discovery/runtime loop.
940
1145
  - Do not add `recursionOptions` at all if the user does not need recursive delegation.
941
1146
  - Do not add `judgeOptions` in normal agent examples; reserve that for optimize/eval workflows.
942
- - Keep `actorOptions` focused on actor-only forward concerns such as `description`, `model`, `modelConfig`, `thinkingTokenBudget`, and `showThoughts`.
943
- - Use `actorModelPolicy` when the actor is the bottleneck and you want the responder to stay fixed.
1147
+ - Keep `executorOptions` focused on actor-only forward concerns such as `description`, `model`, `modelConfig`, `thinkingTokenBudget`, and `showThoughts`.
1148
+ - Use `executorModelPolicy` when the actor is the bottleneck and you want the responder to stay fixed.
944
1149
 
945
1150
  ## Dynamic Output Truncation
946
1151
 
@@ -959,15 +1164,17 @@ This means the actor sees structurally informative output even when the char bud
959
1164
 
960
1165
  Users do not need to configure this behavior — it is automatic. `maxRuntimeChars` sets the upper bound; the dynamic system only ever reduces, never exceeds it.
961
1166
 
962
- ## Actor Prompt Controls
1167
+ ## Stage Prompt Controls
963
1168
 
964
- Use `actorOptions` for actor-only forward options and `responderOptions` for responder-only tuning.
1169
+ The pipeline has three peer stage-config bags: `contextOptions` (distiller), `executorOptions` (executor), `responderOptions` (responder). Each accepts the same shape: `description`, `model`, `modelConfig`, `excludeFields`, plus other forward options.
965
1170
 
966
1171
  Key fields:
967
1172
 
968
- - `actorOptions.description`: append extra actor-specific instructions without changing the responder prompt
969
- - `actorOptions.model` / `responderOptions.model`: split model choice across actor and responder when needed
970
- - `actorModelPolicy`: auto-switch only the actor when the run is on a consecutive error streak or discovery fetches land in specific namespaces
1173
+ - `contextOptions.description`: append extra distiller-specific instructions; useful for telling the distiller about domain conventions for narrowing context.
1174
+ - `executorOptions.description`: append extra executor-specific instructions; the typical place for tool-use guidance.
1175
+ - `responderOptions.description`: append extra responder-specific instructions; useful for output-formatting rules.
1176
+ - `contextOptions.model` / `executorOptions.model` / `responderOptions.model`: split model choice across the three stages.
1177
+ - `executorModelPolicy`: auto-switch only the executor when the run is on a consecutive error streak or discovery fetches land in specific namespaces.
971
1178
 
972
1179
  Good split-model pattern:
973
1180
 
@@ -976,7 +1183,7 @@ const researchAgent = agent('query:string -> answer:string', {
976
1183
  contextFields: ['query'],
977
1184
  runtime,
978
1185
  contextPolicy: { preset: 'checkpointed', budget: 'balanced' },
979
- actorOptions: {
1186
+ executorOptions: {
980
1187
  model: 'gpt-5.4',
981
1188
  },
982
1189
  responderOptions: {
@@ -990,8 +1197,8 @@ Model guidance:
990
1197
  - Put the stronger model on the actor when the task depends on multi-turn exploration, discovery, runtime state reuse, or compressed replay.
991
1198
  - Put the stronger model on the responder only when the hard part is final synthesis/formatting rather than exploration.
992
1199
  - For cost-sensitive setups, a common pattern is stronger actor + cheaper responder, not the other way around.
993
- - Prefer `actorModelPolicy` over globally upgrading the whole agent when the actor only needs help after context grows or the run starts thrashing.
994
- - Pair `contextPolicy: { preset: 'checkpointed', budget: 'balanced' }` with `actorModelPolicy` when you want full replay first and actor-only upgrades triggered by errors or discovered tool domains.
1200
+ - Prefer `executorModelPolicy` over globally upgrading the whole agent when the actor only needs help after context grows or the run starts thrashing.
1201
+ - Pair `contextPolicy: { preset: 'checkpointed', budget: 'balanced' }` with `executorModelPolicy` when you want full replay first and actor-only upgrades triggered by errors or discovered tool domains.
995
1202
 
996
1203
  Invalid pattern:
997
1204
 
@@ -1197,13 +1404,25 @@ agentIdentity?: {
1197
1404
  ```
1198
1405
 
1199
1406
  - `name` is normalized to camelCase for child-agent function names.
1407
+ - `name` and `description` are included in the Actor and Responder prompts as the user-facing agent identity.
1200
1408
  - `namespace` changes the child-agent module from default `agents` to a custom module such as `team`.
1201
1409
 
1202
1410
  ### `AxAgentOptions`
1203
1411
 
1412
+ Each `contextFields` entry is either a plain field name string or an object controlling how much of the value is inlined into the distiller prompt:
1413
+
1414
+ - `{ field, promptMaxChars: N }` — **threshold inline**: inlined only when the value's serialized size ≤ N chars; omitted entirely (runtime-only) when larger. Works with any value type.
1415
+ - `{ field, keepInPromptChars: N, reverseTruncate?: boolean }` — **guaranteed excerpt**: always inlined, truncated to N chars with a `...[truncated M chars]` marker. `reverseTruncate: true` keeps the *last* N chars instead of the first. Requires a string value.
1416
+
1417
+ Use `promptMaxChars` when partial data is worse than no data (e.g. JSON objects). Use `keepInPromptChars` when a prefix or suffix alone is useful (e.g. a document header, or a log tail with `reverseTruncate: true`). The two options are mutually exclusive on a single field.
1418
+
1204
1419
  ```typescript
1205
1420
  {
1206
- contextFields: readonly (string | { field: string; promptMaxChars?: number })[];
1421
+ contextFields: readonly (
1422
+ | string
1423
+ | { field: string; promptMaxChars?: number }
1424
+ | { field: string; keepInPromptChars: number; reverseTruncate?: boolean }
1425
+ )[];
1207
1426
 
1208
1427
  agents?: {
1209
1428
  local?: AxAnyAgentic[];
@@ -1229,18 +1448,17 @@ agentIdentity?: {
1229
1448
 
1230
1449
  runtime?: AxCodeRuntime;
1231
1450
  promptLevel?: 'default' | 'detailed';
1232
- maxSubAgentCalls?: number;
1451
+ maxSubAgentCalls?: number; // global cap (default: 100)
1233
1452
  maxBatchedLlmQueryConcurrency?: number;
1234
1453
  maxTurns?: number;
1235
1454
  maxRuntimeChars?: number;
1236
1455
  contextPolicy?: AxContextPolicyConfig;
1237
1456
  summarizerOptions?: Omit<AxProgramForwardOptions<string>, 'functions'>;
1238
- actorFields?: string[];
1239
- actorTurnCallback?: (turn: {
1457
+ executorTurnCallback?: (turn: {
1240
1458
  turn: number;
1241
1459
  actionLogEntryCount: number;
1242
1460
  guidanceLogEntryCount: number;
1243
- actorResult: Record<string, unknown>;
1461
+ executorResult: Record<string, unknown>;
1244
1462
  code: string;
1245
1463
  result: unknown;
1246
1464
  output: string;
@@ -1248,8 +1466,19 @@ agentIdentity?: {
1248
1466
  thought?: string;
1249
1467
  }) => void | Promise<void>;
1250
1468
  inputUpdateCallback?: (currentInputs: Record<string, unknown>) => Promise<Record<string, unknown> | undefined> | Record<string, unknown> | undefined;
1469
+ onFunctionCall?: (call: {
1470
+ name: string;
1471
+ qualifiedName: string;
1472
+ args: Record<string, unknown>;
1473
+ kind: 'internal' | 'external';
1474
+ }) => void | Promise<void>;
1475
+ onMemoriesSearch?: AxAgentMemoriesSearchFn; // (searches: readonly string[]) => readonly AxAgentMemoryResult[] | Promise<...>
1476
+ onUsedMemories?: (results: readonly AxAgentMemoryResult[]) => void | Promise<void>;
1477
+ onSkillsSearch?: AxAgentSkillsSearchFn; // (searches: readonly string[]) => readonly AxAgentSkillResult[] | Promise<...>
1478
+ onUsedSkills?: (results: readonly AxAgentSkillResult[]) => void | Promise<void>;
1479
+ skills?: readonly AxAgentSkillResult[]; // preload skills at construction; also accepted at forward()-time (executor stage only)
1251
1480
  mode?: 'simple' | 'advanced';
1252
- actorModelPolicy?: readonly [
1481
+ executorModelPolicy?: readonly [
1253
1482
  | {
1254
1483
  model: string;
1255
1484
  aboveErrorTurns: number;
@@ -1276,15 +1505,16 @@ agentIdentity?: {
1276
1505
  recursionOptions?: Partial<Omit<AxProgramForwardOptions, 'functions'>> & {
1277
1506
  maxDepth?: number;
1278
1507
  };
1279
- actorOptions?: Partial<AxProgramForwardOptions & { description?: string }>;
1280
- responderOptions?: Partial<AxProgramForwardOptions & { description?: string }>;
1508
+ contextOptions?: AxStageOptions;
1509
+ executorOptions?: AxStageOptions;
1510
+ responderOptions?: AxStageOptions;
1281
1511
  judgeOptions?: Partial<AxJudgeOptions>;
1282
1512
  bubbleErrors?: ReadonlyArray<new (...args: any[]) => Error>;
1283
1513
  }
1284
1514
  ```
1285
1515
 
1286
- - `actorTurnCallback` fires for the root agent and for recursive child agents that run actor turns.
1287
- - `actorModelPolicy` applies to the actor loop and can be inherited by recursive child agents unless you override it there.
1516
+ - `executorTurnCallback` fires for the root agent and for recursive child agents that run actor turns.
1517
+ - `executorModelPolicy` applies to the actor loop and can be inherited by recursive child agents unless you override it there.
1288
1518
  - `namespaces` matches exact discovery namespaces from successful `discoverFunctions(...)` lookups and starts affecting model choice on the next actor turn.
1289
1519
  - Consecutive error turns reset after a successful non-error turn and when checkpoint summarization refreshes to a new fingerprint.
1290
1520
  - `maxSubAgentCalls` is a shared delegated-call budget across the entire run.
@@ -1308,25 +1538,18 @@ Constructor options for `new AxJSRuntime(opts)`. All defaults are secure — see
1308
1538
 
1309
1539
  ## Observability: getChatLog() and getUsage()
1310
1540
 
1311
- `AxAgent` exposes two sub-programs (Actor and Responder). Both `getChatLog()` and `getUsage()` return an object split by role unlike `AxGen`, which returns a flat array.
1541
+ `AxAgent` exposes actor and responder sub-programs. `getChatLog()` returns the same flat `AxChatLogEntry[]` shape as `AxGen` and `AxFlow`; use each entry's optional `name` field to distinguish `distiller`, `executor`, and `responder`. `getUsage()` still returns token usage split by actor/responder.
1312
1542
 
1313
1543
  ### getChatLog()
1314
1544
 
1315
- Returns the full normalized chat history split by actor/responder after any `.forward()` call. Each entry is one `ai.chat()` round-trip. The actor accumulates one entry per turn; the responder typically has one.
1545
+ Returns the full normalized chat history after any `.forward()` call. Each entry is one `ai.chat()` round-trip. Actor stages accumulate one entry per turn; the responder typically has one entry.
1316
1546
 
1317
1547
  ```typescript
1318
1548
  const log = myAgent.getChatLog();
1319
- // { actor: AxChatLogEntry[], responder: AxChatLogEntry[] }
1320
-
1321
- for (const entry of log.actor) {
1322
- console.log('Actor model:', entry.model);
1323
- for (const msg of entry.messages) {
1324
- console.log(`[${msg.role}]`, msg.content);
1325
- }
1326
- }
1549
+ // readonly AxChatLogEntry[]
1327
1550
 
1328
- for (const entry of log.responder) {
1329
- console.log('Responder model:', entry.model);
1551
+ for (const entry of log) {
1552
+ console.log(entry.name, entry.model);
1330
1553
  for (const msg of entry.messages) {
1331
1554
  console.log(`[${msg.role}]`, msg.content);
1332
1555
  }
@@ -1343,9 +1566,11 @@ type AxChatLogMessage =
1343
1566
  | { role: 'tool'; name: string; content: string };
1344
1567
 
1345
1568
  type AxChatLogEntry = {
1569
+ name?: string; // e.g. "distiller", "executor", "responder"
1346
1570
  model: string;
1347
1571
  messages: AxChatLogMessage[];
1348
1572
  modelUsage?: AxProgramUsage;
1573
+ stage?: 'ctx' | 'task';
1349
1574
  };
1350
1575
  ```
1351
1576
 
@@ -1373,11 +1598,11 @@ myAgent.resetUsage();
1373
1598
 
1374
1599
  ```typescript
1375
1600
  // AxAgent
1376
- agent.getChatLog(): { actor: readonly AxChatLogEntry[]; responder: readonly AxChatLogEntry[] }
1601
+ agent.getChatLog(): readonly AxChatLogEntry[]
1377
1602
  agent.getUsage(): { actor: AxProgramUsage[]; responder: AxProgramUsage[] }
1378
1603
  agent.resetUsage(): void
1379
1604
 
1380
- // AxGen (flat — no split)
1605
+ // AxGen / AxFlow
1381
1606
  gen.getChatLog(): readonly AxChatLogEntry[]
1382
1607
  gen.getUsage(): AxProgramUsage[]
1383
1608
  ```
@@ -1397,6 +1622,7 @@ Fetch these for full working code:
1397
1622
  - [RLM Adaptive Replay](https://raw.githubusercontent.com/ax-llm/ax/refs/heads/main/src/examples/rlm-adaptive-replay.ts) — adaptive replay
1398
1623
  - [RLM Live Runtime State](https://raw.githubusercontent.com/ax-llm/ax/refs/heads/main/src/examples/rlm-live-runtime-state.ts) — structured runtime-state rendering
1399
1624
  - [RLM Clarification Resume](https://raw.githubusercontent.com/ax-llm/ax/refs/heads/main/src/examples/rlm-clarification-resume.ts) — clarification exception plus `getState()` / `setState(...)`
1625
+ - [RLM Memories and Skills](https://raw.githubusercontent.com/ax-llm/ax/refs/heads/main/src/examples/rlm-memories-and-skills.ts) — `onMemoriesSearch` + `recall()` and `onSkillsSearch` + `consult()` with observability via `onUsedMemories` / `onUsedSkills`
1400
1626
  - [Customer Support](https://raw.githubusercontent.com/ax-llm/ax/refs/heads/main/src/examples/customer-support.ts) — classification agent
1401
1627
  - [Abort Patterns](https://raw.githubusercontent.com/ax-llm/ax/refs/heads/main/src/examples/abort-patterns.ts) — abort handling
1402
1628
 
@@ -1409,3 +1635,11 @@ Fetch these for full working code:
1409
1635
  - Do not combine `console.log(...)` with `final(...)`.
1410
1636
  - Do not forget `fields.shared` when child agents depend on parent inputs.
1411
1637
  - Do not add `bubbleErrors` for ordinary recoverable tool errors; those should stay as `[ERROR]` strings so the actor can handle them.
1638
+ - Do not call `recall()` from the responder stage — it is only available in distiller and executor.
1639
+ - Do not assign the result of `await recall(...)` or `await consult(...)` — both return `void`. Read `inputs.memories` next turn (or the **Loaded Skills** section for `consult`) to see what landed.
1640
+ - Do not loop `recall()` calls or wrap them in `Promise.all` — the runtime rejects that as a policy violation. Pass all queries in one array to a single `await recall([...])`.
1641
+ - Do not assume child agents inherit `onMemoriesSearch` or `onSkillsSearch` — set each one explicitly on each agent that needs `recall()` / `consult()`.
1642
+ - Do not call `consult()` from the distiller or responder stages — it is only available in the executor.
1643
+ - Do not loop `consult()` calls or wrap them in `Promise.all` — same policy as `recall()`. Pass all queries in one array.
1644
+ - Do not pass `onMemoriesSearch` results via `fields.shared` as a workaround — use the built-in `recall()` primitive instead.
1645
+ - Do not assume `inputs.memories` persists across `.forward()` calls — its lifetime is one run. Persist memories in your store and recall them again on subsequent calls.