@cubicecho/agent-core 2.24.0 → 2.25.1

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.
Files changed (71) hide show
  1. package/README.md +10 -7
  2. package/dist/agent-loop.d.ts +84 -205
  3. package/dist/agent-loop.js +288 -597
  4. package/dist/calibration.d.ts +14 -10
  5. package/dist/calibration.js +30 -23
  6. package/dist/capabilities.d.ts +111 -42
  7. package/dist/capabilities.js +281 -138
  8. package/dist/client.d.ts +104 -35
  9. package/dist/client.js +205 -74
  10. package/dist/compaction.d.ts +103 -60
  11. package/dist/compaction.js +160 -86
  12. package/dist/config.d.ts +48 -12
  13. package/dist/config.js +20 -1
  14. package/dist/continuation.d.ts +24 -16
  15. package/dist/continuation.js +70 -44
  16. package/dist/digest.d.ts +7 -0
  17. package/dist/digest.js +18 -0
  18. package/dist/errors.d.ts +24 -15
  19. package/dist/errors.js +23 -15
  20. package/dist/events.d.ts +100 -63
  21. package/dist/events.js +250 -120
  22. package/dist/guards.d.ts +49 -4
  23. package/dist/guards.js +60 -5
  24. package/dist/hook-events.d.ts +21 -5
  25. package/dist/hook-events.js +23 -9
  26. package/dist/hooks.d.ts +96 -73
  27. package/dist/hooks.js +202 -116
  28. package/dist/index.d.ts +30 -26
  29. package/dist/index.js +29 -24
  30. package/dist/ledger.d.ts +62 -30
  31. package/dist/ledger.js +163 -56
  32. package/dist/platform.d.ts +15 -0
  33. package/dist/platform.js +15 -0
  34. package/dist/preselect.d.ts +223 -0
  35. package/dist/preselect.js +291 -0
  36. package/dist/request-body.d.ts +32 -0
  37. package/dist/request-body.js +47 -0
  38. package/dist/reset.d.ts +1 -0
  39. package/dist/reset.js +7 -6
  40. package/dist/retry.d.ts +44 -138
  41. package/dist/retry.js +83 -281
  42. package/dist/run-calls.d.ts +123 -0
  43. package/dist/run-calls.js +351 -0
  44. package/dist/run-turn.d.ts +36 -15
  45. package/dist/run-turn.js +74 -37
  46. package/dist/runtime.d.ts +23 -17
  47. package/dist/runtime.js +34 -24
  48. package/dist/schema-compat.d.ts +35 -18
  49. package/dist/schema-compat.js +346 -176
  50. package/dist/scope.d.ts +28 -5
  51. package/dist/scope.js +37 -10
  52. package/dist/side-task.d.ts +124 -40
  53. package/dist/side-task.js +186 -66
  54. package/dist/snapshot.d.ts +10 -5
  55. package/dist/snapshot.js +73 -30
  56. package/dist/spec.d.ts +56 -25
  57. package/dist/spec.js +494 -215
  58. package/dist/stream.d.ts +66 -16
  59. package/dist/stream.js +220 -97
  60. package/dist/thinking.d.ts +23 -8
  61. package/dist/thinking.js +92 -44
  62. package/dist/tokens.d.ts +164 -6
  63. package/dist/tokens.js +319 -6
  64. package/dist/tool-calls.d.ts +90 -17
  65. package/dist/tool-calls.js +207 -84
  66. package/dist/tool-loading.d.ts +101 -189
  67. package/dist/tool-loading.js +257 -371
  68. package/dist/wire.d.ts +81 -0
  69. package/dist/wire.js +73 -0
  70. package/llms.txt +76 -39
  71. package/package.json +4 -4
package/README.md CHANGED
@@ -23,28 +23,31 @@ only, Node >=22.
23
23
  | Module | What it does |
24
24
  | --- | --- |
25
25
  | `schema-compat` | Makes an MCP tool schema something a strict or grammar-constrained server will accept. `sanitizeTools`, `relaxTools`, `isGrammarError`, and `sanitizeSchema` and `relaxSchema` for a schema that is not a tool's. |
26
- | `tool-loading` | On-demand tool discovery: a name-only catalogue plus a `load_tools` meta-tool, so a run pays for the schemas it asks for instead of all of them. Plus `preselectByKeywords`, which picks from it without a model, and the proxied form of the same thing — `PROXY_TOOLS`, `proxyLoadResult`, `proxiedCall` — for a server whose prompt cache a growing tool array throws away. |
26
+ | `tool-loading` | On-demand tool discovery: a name-only catalogue plus a `load_tools` meta-tool, so a run pays for the schemas it asks for instead of all of them. Plus the proxied form of the same thing — `PROXY_TOOLS`, `proxyLoadResult`, `proxiedCall` — for a server whose prompt cache a growing tool array throws away. |
27
27
  | `stream` | Reads one streamed turn back into a message: token callbacks, tool-call reassembly, fenced reasoning taken out of the answer, and the idle watchdog that turns a silent endpoint into `EndpointSilent`. |
28
28
  | `capabilities` | What an endpoint turned out not to support — and, under it, what one model on that endpoint did not — plus the loop that answers either when it says so. `capabilitiesFor`, `modelCapabilitiesFor`, `negotiate`. |
29
29
  | `thinking` | Tells a scratchpad fenced inside `content` from the answer: `FenceSplitter` for a stream, `stripThinking` for a whole reply, and the fence tables both read. |
30
- | `side-task` | One-shot calls that support a run without being one — small prompt, short answer, no tools, never worth failing the run over. `askJson` holds the answer to a schema where the server can. |
30
+ | `side-task` | One-shot calls that support a run without being one — small prompt, short answer, no tools, never worth failing the run over. `askJson` holds the answer to a schema where the server can, and `taskCall` turns the settings an agent spec gives a task into the call that honours them. |
31
31
  | `hooks` | The host's side of lifecycle hooks: `gather` before a request and `notify` after, the shared context budget, `withContext` to put what they add on the turn's question, `untrusted` to fence text nobody vouched for, and `turnMessages` to hand them a transcript. Running a hook is a runner the caller passes. |
32
32
  | `events` | The in-memory bus a watcher reads while a run happens: `emit`, `watch`, `history`, `fold`, and `runMetrics` for what a run cost. A watcher's backlog is capped and reports its own gaps. |
33
- | `client` | A pooled `OpenAI` client per endpoint, plus the context window: the served one where a local server says, the listed one otherwise, and their caches. |
34
- | `retry` | What to do when a request is lost, refused or too big: `isTransient`, `isModelLoading`, `backoffMs`, `ContextOverflow`, `EndpointSilent`, `requestTokens`, `contextTokens`. |
33
+ | `client` | A pooled `OpenAI` client per endpoint, plus the context window: the served one where a local server says, the listed one otherwise, and their caches. And `resolveApiKey`, for a caller deciding which key an endpoint gets. |
34
+ | `retry` | What to do when a request is lost, refused or too big: `isTransient`, `isModelLoading`, `backoffMs`, `ContextOverflow`, `EndpointSilent`. |
35
35
  | `calibration` | How many characters a token is worth on one model, learned from the prompt counts its endpoint reports: `charsPerTokenFor`, `calibrate`. |
36
36
  | `continuation` | `continueTurn`: carries on an answer the token ceiling cut off, by prefilling it as a trailing assistant message. |
37
37
  | `config` | The structural interfaces every function here asks for. |
38
38
  | `run-turn` | `runTurn`: one turn with the retry loop around the negotiation around the stream. The whole loop, for a caller that wants it rather than its parts. Sizes the request against an opt-in `contextLimit`. |
39
- | `agent-loop` | `runAgentLoop`: the loop above a turn — `runTurn` per step, the tools between, `load_tools` and preselection handled, until the model stops asking. Plus the parts it is made of: `buildBody`, `preselect`, `preview`, `resolveApiKey` for a caller deciding which key an endpoint gets, and `taskCall` for running a side task on the settings an agent spec gives it. |
40
- | `tool-calls` | Reading what a model meant by a tool call it did not write cleanly: `parseToolArguments` repairs almost-JSON arguments and says when they were cut off, `recoverToolCalls` finds calls written into the reply as text. |
39
+ | `agent-loop` | `runAgentLoop`: the loop above a turn — `runTurn` per step, the tools between, `load_tools` and preselection handled, until the model stops asking. |
40
+ | `request-body` | `buildBody`: the one place a streamed request's body is decided from a config and what the endpoint and the model have refused. |
41
+ | `run-calls` | The tools between two turns: a step's calls read, run and written into the transcript, with every call answered whatever happens. `preview` cuts a long argument or result for a watcher. |
42
+ | `preselect` | Choosing a run's tools before it starts: `preselect` asks a small model, `preselectByKeywords` scores the request's own words against the tool names without one, and `preselectSystem`, `preselectInput` and `preselection` are the prompt and the reading of the reply for a host that makes the call itself. |
43
+ | `tool-calls` | Reading what a model meant by a tool call it did not write cleanly: `parseToolArguments` repairs almost-JSON arguments and says when they were cut off, `recoverToolCalls` finds calls written into the reply as text. Plus the shapes a call is handed round in: `ToolCallRequest`, `ToolCallResult`, `ToolCallOutcome`. |
41
44
  | `compaction` | Keeping a long run inside its window: `pruneToolResults` clears stale tool results, `planCompaction` and `compactTranscript` fold the oldest stretch into a summary. |
42
45
  | `ledger` | What stretches of a transcript cost, read off the prompt counts the server reported rather than estimated: `recordRequest`, `tokensBetween`, `estimateFrom`, `rebaseLedger`. |
43
46
  | `snapshot` | `exportCapabilities` and `importCapabilities`: the latched refusals as a JSON blob a consumer stores, so a restart need not learn them again. |
44
47
  | `spec` | `parseSpec`, `resolveAgentSpec` and `exportSpec`: an agent as a versioned JSON document, layered into the flat config the loop takes. Imports nothing but types, and is published separately at `@cubicecho/agent-core/spec`. |
45
48
  | `reset` | `resetAll`: drops every cache and latch in one call, so a teardown cannot forget one. |
46
49
  | `runtime` | `createRuntime`: a second set of those caches, with the functions that use them as methods — for a host whose tenants must not share a client pool, a latch or an event bus. `defaultRuntime` is the one the top-level functions use. |
47
- | `tokens` | `estimateTokens`: characters over four, deliberately low, for everything here that has to guess at a window. |
50
+ | `tokens` | How big a request is, estimated: `estimateTokens` — characters over four, deliberately low — and what is built on it for everything here that has to guess at a window: `messageTokens`, `requestTokens`, `contextTokens`. |
48
51
  | `errors` | `errorMessage`: a caught `unknown` turned into something a run row can hold. And what `runAgentLoop` throws, with the run as it stood: `AgentLoopError`, `ToolIterationLimit`, `AgentLoopOverflow`, and `failedRun` to read it off any of them. |
49
52
  | `catalog` | `CatalogServer`: the name-only shape `tool-loading` reads a connected server as. |
50
53
 
@@ -1,180 +1,31 @@
1
- import type OpenAI from "openai";
2
- import { type Capabilities, type ModelCapabilities } from "./capabilities.ts";
3
- import type { CatalogServer } from "./catalog.ts";
4
- import { type CompactionOptions } from "./compaction.ts";
5
- import type { Endpoint, ModelParams, RetryPolicy, ToolPolicy } from "./config.ts";
6
- import { type RunEventInput, type RunMetrics } from "./events.ts";
7
- import type { HookEvent } from "./hook-events.ts";
8
- import { type HookContext, type HookNote, type HookRunner } from "./hooks.ts";
9
- import { type TokenLedger } from "./ledger.ts";
10
- import { type SideTask, type SideTaskOptions } from "./side-task.ts";
11
- import { type Turn, type TurnUsage } from "./stream.ts";
12
- import { type KeywordPreselectOptions, type ToolOrder } from "./tool-loading.ts";
1
+ import type OpenAI from 'openai';
2
+ import type { CatalogServer } from './catalog.ts';
3
+ import { type CompactionOptions } from './compaction.ts';
4
+ import type { Endpoint, ModelParams, RetryPolicy, ToolPolicy } from './config.ts';
5
+ import { type RunEventInput, type RunMetrics } from './events.ts';
6
+ import { HookEvent } from './hook-events.ts';
7
+ import { type HookContext, type HookNote, type HookRunner, type OnNote } from './hooks.ts';
8
+ import { type TokenLedger } from './ledger.ts';
9
+ import { type KeywordPreselectOptions } from './preselect.ts';
10
+ import { type ToolDispatch } from './run-calls.ts';
11
+ import { type SideTask } from './side-task.ts';
12
+ import { type Turn, type TurnUsage } from './stream.ts';
13
+ import { type ToolCallOutcome, type ToolCallRequest, type ToolCallResult } from './tool-calls.ts';
14
+ import { type ToolOrder } from './tool-loading.ts';
13
15
  /**
14
- * The one place a streamed request's body is decided from a config and what the endpoint and
15
- * the model have refused.
16
+ * The loop above a turn: send, run the tools the model asked for, send again, until it stops
17
+ * asking.
16
18
  *
17
- * Every field that negotiates lives here: the ceiling's two spellings, a temperature only a
18
- * model that takes ours is sent, a reasoning effort only one that takes it is, `stream_options`
19
- * only where the server has heard of it, relaxed schemas only where it could not build a grammar,
20
- * and `extraBody` last, less whatever the model refused by name. The ceiling is tested
21
- * `=== false` — `modelCapabilitiesFor` starts a model at `legacyTokenLimit: true` and an absent
22
- * one has to read the same — which is the test one of the three copies had inverted.
23
- *
24
- * @param config What to ask for. `maxTokens` of zero or less sends no ceiling; `reasoningEffort`
25
- * absent or `"off"` sends no effort, and one the model has refused by value is stepped up to the
26
- * cheapest it takes by `effortFor`.
27
- * @param supports What the endpoint has refused, as `negotiate` hands it to `send`.
28
- * @param refused What the model has refused, as `negotiate` hands it over. Absent is a model
29
- * that has refused nothing.
30
- * @param messages The request's messages, system prompt included, sent as they are.
31
- * @param tools The tool definitions. Ordered by name, sanitised here — a lookup for a definition
32
- * seen before — and relaxed where the endpoint needs it. Empty sends no `tools` field at all.
33
- * @param order How to order them before sending. `true`, the default, is by name, which keeps the
34
- * cache when the caller's array is assembled differently from one request to the next. See
35
- * `orderTools`.
36
- */
37
- export declare function buildBody(config: ModelParams, supports: Capabilities, refused: ModelCapabilities | undefined, messages: OpenAI.ChatCompletionMessageParam[], tools?: OpenAI.ChatCompletionTool[], order?: ToolOrder): OpenAI.ChatCompletionCreateParamsStreaming;
38
- /**
39
- * A long tool argument or result cut to what a watcher needs, with the full length said.
40
- *
41
- * For events, never for the transcript: the model reads the whole of what a tool returned.
42
- *
43
- * @param text What to show.
44
- * @param limit Characters kept, 2000 by default. Text at or under it comes back as it was.
45
- */
46
- export declare const preview: (text: string, limit?: number) => string;
47
- /**
48
- * The key to send, where an endpoint may inherit one from the settings it overrides.
49
- *
50
- * A credential issued for one endpoint has no business being posted to another. A profile that
51
- * names its own `baseUrl` and no key of its own is sent `NO_KEY` — not the operator's key, and
52
- * not `$OPENAI_API_KEY` — because "I pointed an agent at a friend's server and it sent my OpenAI
53
- * key" is not a mistake worth being able to make, and a local server wants no key anyway. One on
54
- * the same endpoint inherits the key as it inherits everything else, and the environment is the
55
- * last word on the endpoint that was configured rather than overridden.
56
- *
57
- * @param own The endpoint as the agent or profile states it. Its own key always wins. An empty or
58
- * absent `baseUrl` is one that inherits the endpoint too.
59
- * @param inherited The settings it overrides. Absent treats `own` as the configured endpoint, so
60
- * only its key and the environment's are in play.
61
- * @param env Where `OPENAI_API_KEY` is read from, `process.env` by default.
62
- */
63
- export declare function resolveApiKey(own: {
64
- baseUrl?: string;
65
- apiKey?: string;
66
- }, inherited?: {
67
- baseUrl: string;
68
- apiKey?: string;
69
- }, env?: Record<string, string | undefined>): string;
70
- /**
71
- * A side task's settings as the call that honours them: its endpoint, its model, and the options
72
- * `ask`, `askJson`, `summariser` and `preselect` take last.
73
- *
74
- * A helper rather than a second signature on each of the four, so a setting added to a task later
75
- * arrives through `options` without a host's call changing. What the task states wins over the
76
- * same field in `options`: the host's code is written once and the task is what an operator
77
- * configured over it, so a host passes its own ceiling as the value for a task that names none.
78
- * A setting the task leaves out and `options` does too is absent from the result, which is what
79
- * leaves it to the entry point's own default — 0.3, not the agent's temperature.
80
- *
81
- * A resolved spec carries no key, so a task on the agent's own endpoint would otherwise go out
82
- * unkeyed, and be remembered under a second capability entry beside the agent's. Given `agent`,
83
- * such a task is sent the agent's `baseUrl` as the agent spells it and, having no key of its own,
84
- * the agent's key, so `capabilitiesFor` answers both with one object; a task on another endpoint
85
- * is sent only a key of its own, as `resolveApiKey` has it. The environment is not read: the
86
- * agent's key is whatever the main turn is sent.
87
- *
88
- * @param task The task, ordinarily `resolved.tasks.<key>`.
89
- * @param options What the host adds — cancellation, notices, `keywords` — and its own defaults
90
- * for the settings a task may leave out.
91
- * @param agent The endpoint the main turn uses, key included. Absent sends the task's endpoint
92
- * as it stands.
19
+ * Three servers wrote this loop separately after `runTurn` had already been pulled in here, and
20
+ * the copies drifted the way the turn's own had — one noticed a turn cut off at the ceiling and
21
+ * two did not, one tested the ceiling's spelling the other way round, one sent a reasoning effort
22
+ * and two never did. What is here is the part that does not know what the run is for; the
23
+ * prompts, the tools and what a run means stay with the caller.
93
24
  */
94
- export declare function taskCall<Options extends SideTaskOptions = SideTaskOptions>(task: SideTask, options?: Options, agent?: {
95
- baseUrl: string;
96
- apiKey?: string;
97
- }): {
98
- endpoint: Endpoint;
99
- model: string;
100
- options: Options;
101
- };
102
- /**
103
- * The tools a request is likely to need, picked by a small model before the run starts, or none.
104
- *
105
- * On-demand loading otherwise spends a round trip on reading the catalogue and calling
106
- * `load_tools`; a small model reading the same catalogue usually names the right tools, and the
107
- * task model opens with them in hand. A wrong guess costs a few hundred tokens for one run, and
108
- * a failed one costs nothing — it is reported through `onNotice` and answered with an empty list,
109
- * since a side task is never worth failing the run. A stop still throws.
110
- *
111
- * With `keywords`, the request's own words are matched against the catalogue first and the model
112
- * is spent only on what they cannot settle, which on a local box is the difference between a run
113
- * starting now and starting in a few seconds. The words have to be clear about it; see
114
- * `preselectByKeywords` for what that means.
115
- *
116
- * @param config The endpoint the preselector is reached through.
117
- * @param model The preselector. An empty name picks nothing, which is what `toolSelectModel`
118
- * means by empty.
119
- * @param catalog The servers to choose from.
120
- * @param prompt The request being planned for. Only its head is read; see `preselectInput`.
121
- * @param options Cancellation, notices, the reply ceiling (256), the temperature and reasoning
122
- * effort as `ask` reads them (0.3 and none when absent), the cap the choice is held to
123
- * (`MAX_PER_LOAD`), and whether to try the words first.
124
- */
125
- export declare function preselect(config: Endpoint, model: string, catalog: CatalogServer[], prompt: string, { signal, onNotice, maxTokens, temperature, reasoningEffort, maxPerLoad, keywords, }?: {
126
- signal?: AbortSignal;
127
- onNotice?: (message: string) => void;
128
- maxTokens?: number;
129
- /** As `SideTaskOptions.temperature`: absent is 0.3. */
130
- temperature?: number;
131
- /** As `SideTaskOptions.reasoningEffort`: absent keeps the no-thinking hints. */
132
- reasoningEffort?: string;
133
- maxPerLoad?: number;
134
- /**
135
- * Try `preselectByKeywords` first and spend the model only on what it cannot settle. `true`
136
- * takes its defaults; an object tunes the thresholds. An empty `model` still means no
137
- * preselection at all, words included — that is what `toolSelectModel: ""` asks for.
138
- */
139
- keywords?: boolean | KeywordPreselectOptions;
140
- }): Promise<string[]>;
141
- /**
142
- * One call the model made, as `dispatch` and `onToolCall` are handed it.
143
- *
144
- * A `call_tool` arrives as the tool it names — that tool's name and arguments, under the
145
- * `call_tool`'s id — so a dispatcher is the same in every discovery mode.
146
- */
147
- export interface ToolCallRequest {
148
- id: string;
149
- name: string;
150
- /**
151
- * Parsed by `parseToolArguments`, repairs and all. Empty for a call whose arguments could not be
152
- * read, which only `onToolCall` is ever handed.
153
- */
154
- args: Record<string, unknown>;
155
- /**
156
- * The arguments as the model wrote them, before any repair. Through `call_tool`, the inner
157
- * arguments as JSON text.
158
- */
159
- raw: string;
160
- }
161
- /** What one tool call did, in the order the model asked. */
162
- export interface ToolCallOutcome {
163
- /**
164
- * The id the model's reply gave the call, which is what tells two calls to one tool apart.
165
- * Distinct within a step — `streamTurn` mints one where the server sent none or repeated one —
166
- * but nothing stops a server using the same id again in a later step. `runAgentLoop` always
167
- * sets it; optional so an outcome built before it existed still compiles.
168
- */
169
- id?: string;
170
- /** The tool that was called. For a `call_tool`, the tool it named. */
171
- name: string;
172
- /** False when the arguments did not parse, the tool threw, or `load_tools` loaded nothing. */
173
- ok: boolean;
174
- }
175
25
  /**
176
26
  * One step's request as `onRequest` is handed it: what the model is about to read.
177
27
  *
28
+ * @remarks
178
29
  * The loop assembles this and nothing else sees it — the system prompt with the catalogue on it,
179
30
  * the hooks' context on the question, the tool array ordered and sanitised as it is sent — so a
180
31
  * host that wants to say what is filling the window has nothing to measure without it.
@@ -187,23 +38,10 @@ export interface AgentLoopRequest {
187
38
  /** Which step of the run this is, from zero, as `beforeStep` and `onTurn` count them. */
188
39
  step: number;
189
40
  }
190
- /**
191
- * One call's whole answer, as `onToolResult` is handed it.
192
- *
193
- * The `tool-result` event carries the same answer cut by `preview`, which is right for a readout
194
- * and wrong for a host that renders or stores the result: this is the text the model reads.
195
- */
196
- export interface ToolCallResult extends ToolCallOutcome {
197
- id: string;
198
- /**
199
- * What went into the transcript for this call, uncut: what the tool returned, what it threw, the
200
- * `load_tools` answer, or why the arguments could not be read.
201
- */
202
- content: string;
203
- }
204
41
  /**
205
42
  * The hooks a loop runs around one question. See `hooks.ts`.
206
43
  *
44
+ * @remarks
207
45
  * The loop gathers before its first request and nothing else can run beside that, so a host with
208
46
  * pre-turn work of its own to overlap with the hooks — compaction, a preselection — calls `gather`
209
47
  * itself, puts the context on with `withContext`, calls `notify` after, and passes no `hooks`.
@@ -212,14 +50,26 @@ export interface AgentLoopHooks {
212
50
  run: HookRunner;
213
51
  /** What the hooks are told. `reply` and `turn` are filled in for `afterTurn`. */
214
52
  context: HookContext;
215
- /** Run before the first request, `["beforeTurn"]` by default. */
53
+ /**
54
+ * Run before the first request.
55
+ *
56
+ * @defaultValue `[HookEvent.BeforeTurn]`
57
+ */
216
58
  events?: readonly HookEvent[];
217
- /** The shared context budget. Absent is `configureHooks`'s. */
59
+ /**
60
+ * The shared context budget.
61
+ *
62
+ * @defaultValue The budget `configureHooks` holds.
63
+ */
218
64
  maxTokens?: number;
219
- /** Said above the context blocks. Absent is `configureHooks`'s; empty is none. */
65
+ /**
66
+ * Said above the context blocks. Empty is none.
67
+ *
68
+ * @defaultValue The preface `configureHooks` holds.
69
+ */
220
70
  preface?: string;
221
71
  /** Hears each note, from before the request and from `afterTurn`. */
222
- onNote?: (note: HookNote) => void;
72
+ onNote?: OnNote;
223
73
  }
224
74
  /** How full the window is as a step is about to be sent, as `beforeStep` is told it. */
225
75
  export interface StepWindow {
@@ -235,6 +85,10 @@ export interface StepWindow {
235
85
  /** The run's ledger so far, indexed into the transcript `beforeStep` is handed beside it. */
236
86
  ledger: TokenLedger;
237
87
  }
88
+ /** The `preselectRouting` that sends the first step the shortlist alone. */
89
+ export declare const PRESELECT_EXCLUSIVE: 'exclusive';
90
+ /** The `preselectRouting` that loads the shortlist after what was carried, and sends the first step like any other. */
91
+ export declare const PRESELECT_APPEND: 'append';
238
92
  /** What `runAgentLoop` takes. */
239
93
  export interface AgentLoopOptions {
240
94
  /**
@@ -243,11 +97,12 @@ export interface AgentLoopOptions {
243
97
  * to `runTurn` as `contextLimit`, which sizes each request against the window before sending.
244
98
  * `"proxy"` is on-demand loading behind a tool array that never changes; see `PROXY_TOOLS`.
245
99
  *
100
+ * @remarks
246
101
  * `tasks` is a resolved agent's, and is read only for the side tasks the loop was asked to run:
247
102
  * `tasks.toolSelect` under `preselect` and `tasks.compaction` under `compact`. `toolSelectModel`
248
103
  * is the flattened spelling of the first, for a host that is not on the spec.
249
104
  */
250
- config: Endpoint & ModelParams & Pick<ToolPolicy, "maxToolIterations"> & Partial<Pick<ToolPolicy, "toolDiscovery" | "toolSelectModel">> & Partial<RetryPolicy> & {
105
+ config: Endpoint & ModelParams & Pick<ToolPolicy, 'maxToolIterations'> & Partial<Pick<ToolPolicy, 'toolDiscovery' | 'toolSelectModel'>> & Partial<RetryPolicy> & {
251
106
  contextLength?: number;
252
107
  tasks?: Readonly<Record<string, SideTask>>;
253
108
  };
@@ -280,6 +135,7 @@ export interface AgentLoopOptions {
280
135
  * has or picks a sibling. Everything comes back on the step after. `preselectRouting` trades
281
136
  * that for a first step shaped like the rest.
282
137
  *
138
+ * @remarks
283
139
  * Proxied, the tool array is fixed and there is no such step. The shortlist is written into the
284
140
  * transcript instead, after the question, as a `load_tools` call the model did not make and the
285
141
  * result that call would have had — an assistant message and a tool message, handed back in
@@ -289,7 +145,7 @@ export interface AgentLoopOptions {
289
145
  */
290
146
  preselected?: readonly string[];
291
147
  /**
292
- * What a preselection does to the first step. `"exclusive"`, the default, is the shortlist alone
148
+ * What a preselection does to the first step. `"exclusive"` is the shortlist alone
293
149
  * as `preselected` describes — and a system prompt and tool array unlike the last request's and
294
150
  * unlike the next step's, so a prompt cache misses the whole transcript on the first step and
295
151
  * again on the second. `"append"` loads the shortlist as a `load_tools` call would have and
@@ -298,18 +154,24 @@ export interface AgentLoopOptions {
298
154
  * back in front of the model. Which costs more has not been measured, which is why the default
299
155
  * has not moved.
300
156
  *
157
+ * @defaultValue `PRESELECT_EXCLUSIVE`
158
+ *
159
+ * @remarks
301
160
  * Appended is before `toolOrder` has its say: sorted, a preselected tool lands at its name's
302
161
  * place and moves every definition after it, so only `toolOrder: false` keeps a carried array
303
162
  * a strict prefix. Either way the first step's system prompt is the second's, and so is its
304
163
  * tool array until something else is loaded. Nothing to do without a preselection, in eager
305
164
  * mode, or proxied, where no step is routed either way.
306
165
  */
307
- preselectRouting?: "exclusive" | "append";
166
+ preselectRouting?: typeof PRESELECT_EXCLUSIVE | typeof PRESELECT_APPEND;
308
167
  /**
309
168
  * Has the loop make the preselection itself, before its first request, with the preselector
310
- * the config names. Off by default: a host that passes a resolved agent and calls `preselect`
169
+ * the config names. Opt-in, so that a host that passes a resolved agent and calls `preselect`
311
170
  * on its own is not sent a second one.
312
171
  *
172
+ * @defaultValue `false`
173
+ *
174
+ * @remarks
313
175
  * The preselector is `config.tasks.toolSelect` with everything it states — its endpoint, model,
314
176
  * ceiling, temperature and reasoning effort, keyed as `taskCall` keys a task against `config` —
315
177
  * and otherwise `config.toolSelectModel` on the config's own endpoint at `preselect`'s defaults.
@@ -331,6 +193,7 @@ export interface AgentLoopOptions {
331
193
  * default, and nothing is folded without that task — there is no falling back to the main model
332
194
  * — or without a `config.contextLength` to plan against.
333
195
  *
196
+ * @remarks
334
197
  * It runs after `beforeStep`, on what that returned, and is `compactTranscript` and nothing
335
198
  * more: the plan is made from the last request's reported prompt, or an estimate before the
336
199
  * first step and after a `beforeStep` rewrite; `hooks`, when given, are told `beforeCompact`
@@ -341,14 +204,14 @@ export interface AgentLoopOptions {
341
204
  * that keeps its transcript append-only wants `runCompaction` and a stored fold instead. `true`
342
205
  * plans by `planCompaction`'s defaults; an object moves its thresholds.
343
206
  */
344
- compact?: boolean | Pick<CompactionOptions, "compactAt" | "keepRatio" | "target">;
207
+ compact?: boolean | Pick<CompactionOptions, 'compactAt' | 'keepRatio' | 'target'>;
345
208
  /**
346
209
  * Tools already loaded, carried from an earlier question. See `carryOver`. Not read in proxied
347
210
  * mode, where a tool is loaded only while its definition is in the history.
348
211
  */
349
212
  loaded?: Iterable<string>;
350
213
  /** Runs one tool call and returns what the model reads. What it throws, the model reads too. */
351
- dispatch: (call: ToolCallRequest, signal?: AbortSignal) => Promise<string>;
214
+ dispatch: ToolDispatch;
352
215
  /**
353
216
  * Runs a step's calls together rather than one after another. Results still go into the
354
217
  * transcript in the order the model asked. See `dedupeToolCalls`, which applies either way.
@@ -358,7 +221,10 @@ export interface AgentLoopOptions {
358
221
  * Answers an identical repeat of a call — the same name and the same arguments, word for word —
359
222
  * within one step from the first one, rather than dispatching it again.
360
223
  *
361
- * On by default, and on whether or not the calls run in `parallel`: a model that asks the same
224
+ * @defaultValue `true`
225
+ *
226
+ * @remarks
227
+ * It holds whether or not the calls run in `parallel`: a model that asks the same
362
228
  * question twice in one reply gets one answer, and two that are still in flight share the
363
229
  * request. A call that threw is not an answer and is made again. The scope is the step and not
364
230
  * the run, because between steps other tools have run and the file the model read may be the
@@ -375,6 +241,7 @@ export interface AgentLoopOptions {
375
241
  * `load_tools`, an identical repeat answered from the first, and a call whose arguments could
376
242
  * not be read, which arrives with empty `args` and the model's own text in `raw`.
377
243
  *
244
+ * @remarks
378
245
  * For a host that shows each call as it is made and fills it in when its result lands, which
379
246
  * `dispatch` alone cannot do for those three. Not awaited: a promise it returns is dropped.
380
247
  */
@@ -382,6 +249,7 @@ export interface AgentLoopOptions {
382
249
  /**
383
250
  * Told of every call's answer as it lands, whole, under the id `onToolCall` announced it by.
384
251
  *
252
+ * @remarks
385
253
  * One per call the model made: a deduplicated repeat gets its own, carrying the answer it
386
254
  * shares with the first. With `parallel` they arrive as the calls finish, which need not be
387
255
  * the order they were made in; the transcript and the result's `toolCalls` keep that order.
@@ -391,6 +259,7 @@ export interface AgentLoopOptions {
391
259
  /**
392
260
  * Hooks gathered onto the question before the first request, and told the reply after.
393
261
  *
262
+ * @remarks
394
263
  * Only this question is touched — the last user message of `messages` — and the context is on
395
264
  * the request, never in the transcript handed back. An earlier question's context is therefore
396
265
  * the host's to send again: keep the result's `context` and `preface` beside the question, and
@@ -419,15 +288,18 @@ export interface AgentLoopOptions {
419
288
  onEvent?: (event: RunEventInput) => void;
420
289
  /**
421
290
  * Takes tool calls a model wrote into its reply as text and runs them as calls, with a notice
422
- * saying so. On by default: a server whose tool-call parser does not match the model's template
423
- * otherwise ends the run on a reply that is only a call nobody made. Off leaves such a reply as
424
- * the answer. See `recoverToolCalls`.
291
+ * saying so. A server whose tool-call parser does not match the model's template otherwise ends
292
+ * the run on a reply that is only a call nobody made. Off leaves such a reply as the answer. See
293
+ * `recoverToolCalls`.
294
+ *
295
+ * @defaultValue `true`
425
296
  */
426
297
  recoverToolCalls?: boolean;
427
298
  /**
428
299
  * Each step's request as first built, before it goes out — for measuring what was sent, not for
429
300
  * changing it, which is `beforeStep`'s.
430
301
  *
302
+ * @remarks
431
303
  * Called once a step, synchronously, and what it throws ends the run. It is not called again
432
304
  * for what `runTurn` sends after that: a retry sends the same request, but a refusal is answered
433
305
  * with a lesser body — relaxed schemas, a field dropped — and a continuation with the reply so
@@ -441,6 +313,7 @@ export interface AgentLoopOptions {
441
313
  * the loop goes on — so a host that stores its transcript has written the assistant message
442
314
  * before its tools run, and every tool result before the next request is sent.
443
315
  *
316
+ * @remarks
444
317
  * The assistant message is the one replayed on later requests — arguments repaired, recovered
445
318
  * calls folded in — and comes with its `turn`, as `onTurn` was handed it, for the `reasoning` a
446
319
  * host keeps beside the message. A tool result comes without one, and so do the two messages
@@ -450,9 +323,11 @@ export interface AgentLoopOptions {
450
323
  */
451
324
  onMessage?: (message: OpenAI.ChatCompletionMessageParam, step: number, turn?: Turn) => void | Promise<void>;
452
325
  /**
453
- * How many times an answer cut off at `maxTokens` is continued, zero — the default — for never.
454
- * Opt-in because it spends another request, and on a server that does not continue a trailing
455
- * assistant message it spends one to find that out. See `continueTurn`.
326
+ * How many times an answer cut off at `maxTokens` is continued, zero for never. Opt-in because
327
+ * it spends another request, and on a server that does not continue a trailing assistant
328
+ * message it spends one to find that out. See `continueTurn`.
329
+ *
330
+ * @defaultValue `0`
456
331
  */
457
332
  maxContinuations?: number;
458
333
  }
@@ -518,6 +393,13 @@ export interface AgentLoopResult {
518
393
  * until a turn asks for none. Throws when `maxToolIterations` is spent, when stopped, and on
519
394
  * whatever `runTurn` throws — `ContextOverflow` among them, however it was found out.
520
395
  *
396
+ * @param options - The config, transcript, tools and dispatcher, plus the optional hooks, events
397
+ * and cancellation. See `AgentLoopOptions`.
398
+ * @returns The turn that asked for no tools and the run around it: the transcript with what the
399
+ * run added, usage, every call, what is loaded and used, the hooks' notes and context, metrics
400
+ * and the ledger. See `AgentLoopResult`.
401
+ *
402
+ * @remarks
521
403
  * What it throws carries the run as it stood — `messages`, `usage`, `toolCalls`, `loaded` and
522
404
  * `used` — because the steps before a failure were real and a host that stores its transcript
523
405
  * has nothing else to store them from. A spent budget is a `ToolIterationLimit`, an overflow is
@@ -555,8 +437,5 @@ export interface AgentLoopResult {
555
437
  * `compact` has it fold the transcript, each by the task `config.tasks` carries for it and on
556
438
  * that task's own settings — a setting the task leaves out is the side task's default, never the
557
439
  * run's `temperature` or `maxTokens`.
558
- *
559
- * @param options The config, transcript, tools and dispatcher, plus the optional hooks, events
560
- * and cancellation. See `AgentLoopOptions`.
561
440
  */
562
441
  export declare function runAgentLoop(options: AgentLoopOptions): Promise<AgentLoopResult>;