@ax-llm/ax 20.0.1 → 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.
- package/README.md +4 -9
- package/index.cjs +643 -732
- package/index.cjs.map +1 -1
- package/index.d.cts +4533 -4362
- package/index.d.ts +4533 -4362
- package/index.global.js +643 -732
- package/index.global.js.map +1 -1
- package/index.js +643 -732
- package/index.js.map +1 -1
- package/package.json +1 -1
- package/skills/ax-agent-optimize.md +1 -1
- package/skills/ax-agent.md +336 -102
- package/skills/ax-ai.md +1 -1
- package/skills/ax-flow.md +12 -1
- package/skills/ax-gen.md +3 -2
- package/skills/ax-gepa.md +1 -1
- package/skills/ax-learn.md +1 -1
- package/skills/ax-llm.md +1 -1
- package/skills/ax-signature.md +1 -1
package/skills/ax-agent.md
CHANGED
|
@@ -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: "
|
|
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
|
-
-
|
|
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 `
|
|
31
|
-
- Use `
|
|
32
|
-
- Use `agentStatusCallback` when the user wants real-time task progress updates from the actor via `await
|
|
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" ->
|
|
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 `
|
|
44
|
-
- "Need debugging or traceability" -> start with `debug: true` or `
|
|
45
|
-
- "Need real-time progress updates" -> add `agentStatusCallback` so the actor can call `await
|
|
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
|
-
|
|
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
|
-
- `
|
|
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 `
|
|
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
|
-
- `
|
|
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
|
-
-
|
|
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
|
|
174
|
+
## Child Agents As Tools
|
|
160
175
|
|
|
161
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
200
|
+
Without `agentIdentity.namespace`, the child lands under `utils.<name>` like any other tool:
|
|
207
201
|
|
|
208
202
|
```javascript
|
|
209
|
-
const result = await
|
|
203
|
+
const result = await utils.writer({ draft: '...' });
|
|
210
204
|
```
|
|
211
205
|
|
|
212
206
|
Rules:
|
|
213
207
|
|
|
214
|
-
-
|
|
215
|
-
-
|
|
216
|
-
-
|
|
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
|
|
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 `
|
|
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 `
|
|
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 `
|
|
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
|
-
- `
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 `
|
|
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
|
-
- `
|
|
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
|
-
- `
|
|
884
|
-
- `
|
|
885
|
-
- `
|
|
886
|
-
- `
|
|
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
|
-
|
|
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
|
-
|
|
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. `
|
|
932
|
-
- `
|
|
933
|
-
- Recursive child agents can inherit `
|
|
934
|
-
- `
|
|
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 `
|
|
943
|
-
- Use `
|
|
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
|
-
##
|
|
1167
|
+
## Stage Prompt Controls
|
|
963
1168
|
|
|
964
|
-
|
|
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
|
-
- `
|
|
969
|
-
- `
|
|
970
|
-
- `
|
|
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
|
-
|
|
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 `
|
|
994
|
-
- Pair `contextPolicy: { preset: 'checkpointed', budget: 'balanced' }` with `
|
|
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 (
|
|
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
|
-
|
|
1239
|
-
actorTurnCallback?: (turn: {
|
|
1457
|
+
executorTurnCallback?: (turn: {
|
|
1240
1458
|
turn: number;
|
|
1241
1459
|
actionLogEntryCount: number;
|
|
1242
1460
|
guidanceLogEntryCount: number;
|
|
1243
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1280
|
-
|
|
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
|
-
- `
|
|
1287
|
-
- `
|
|
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
|
|
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
|
|
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
|
-
//
|
|
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
|
|
1329
|
-
console.log(
|
|
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():
|
|
1601
|
+
agent.getChatLog(): readonly AxChatLogEntry[]
|
|
1377
1602
|
agent.getUsage(): { actor: AxProgramUsage[]; responder: AxProgramUsage[] }
|
|
1378
1603
|
agent.resetUsage(): void
|
|
1379
1604
|
|
|
1380
|
-
// AxGen
|
|
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.
|