@cubicecho/agent-core 2.22.0 → 2.24.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 +61 -6
- package/dist/agent-loop.d.ts +3 -2
- package/dist/agent-loop.js +385 -303
- package/dist/calibration.js +8 -6
- package/dist/capabilities.d.ts +36 -0
- package/dist/capabilities.js +116 -65
- package/dist/client.d.ts +5 -4
- package/dist/client.js +62 -57
- package/dist/compaction.js +5 -9
- package/dist/continuation.d.ts +4 -4
- package/dist/continuation.js +1 -4
- package/dist/events.d.ts +16 -1
- package/dist/events.js +64 -44
- package/dist/guards.d.ts +22 -0
- package/dist/guards.js +22 -0
- package/dist/hook-events.d.ts +22 -0
- package/dist/hook-events.js +22 -0
- package/dist/hooks.d.ts +15 -16
- package/dist/hooks.js +44 -44
- package/dist/index.d.ts +6 -4
- package/dist/index.js +6 -4
- package/dist/reset.d.ts +6 -2
- package/dist/reset.js +6 -2
- package/dist/retry.d.ts +13 -7
- package/dist/retry.js +14 -7
- package/dist/run-turn.d.ts +8 -1
- package/dist/runtime.d.ts +120 -0
- package/dist/runtime.js +113 -0
- package/dist/schema-compat.d.ts +23 -0
- package/dist/schema-compat.js +76 -59
- package/dist/scope.d.ts +35 -0
- package/dist/scope.js +42 -0
- package/dist/side-task.d.ts +5 -0
- package/dist/side-task.js +24 -52
- package/dist/snapshot.d.ts +2 -2
- package/dist/snapshot.js +27 -48
- package/dist/spec.d.ts +11 -12
- package/dist/spec.js +36 -66
- package/dist/stream.d.ts +7 -0
- package/dist/stream.js +8 -1
- package/dist/thinking.js +6 -4
- package/dist/tokens.d.ts +9 -2
- package/dist/tokens.js +10 -3
- package/dist/tool-calls.d.ts +9 -0
- package/dist/tool-calls.js +11 -6
- package/dist/tool-loading.d.ts +10 -0
- package/dist/tool-loading.js +15 -7
- package/llms.txt +19 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ only, Node >=22.
|
|
|
22
22
|
|
|
23
23
|
| Module | What it does |
|
|
24
24
|
| --- | --- |
|
|
25
|
-
| `schema-compat` | Makes an MCP tool schema something a strict or grammar-constrained server will accept. `sanitizeTools`, `relaxTools`, `isGrammarError
|
|
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
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. |
|
|
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`. |
|
|
@@ -43,6 +43,7 @@ only, Node >=22.
|
|
|
43
43
|
| `snapshot` | `exportCapabilities` and `importCapabilities`: the latched refusals as a JSON blob a consumer stores, so a restart need not learn them again. |
|
|
44
44
|
| `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
45
|
| `reset` | `resetAll`: drops every cache and latch in one call, so a teardown cannot forget one. |
|
|
46
|
+
| `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. |
|
|
46
47
|
| `tokens` | `estimateTokens`: characters over four, deliberately low, for everything here that has to guess at a window. |
|
|
47
48
|
| `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. |
|
|
48
49
|
| `catalog` | `CatalogServer`: the name-only shape `tool-loading` reads a connected server as. |
|
|
@@ -231,7 +232,9 @@ tasks and model listings, the same way.
|
|
|
231
232
|
hold the reply to, so a small model that wraps JSON in prose on its own cannot do so here. The
|
|
232
233
|
schema is normalised the way a tool's parameters are, and relaxed where the endpoint could not
|
|
233
234
|
build a grammar, because llama.cpp reads both with the same converter. It also rides on the system
|
|
234
|
-
prompt, and the reply goes through `parseJson` either way
|
|
235
|
+
prompt, and the reply goes through `parseJson` either way — which finds the JSON inside prose or a
|
|
236
|
+
fence and repairs what a local model gets nearly right: single quotes, `True` and `None`, bare keys,
|
|
237
|
+
a trailing comma.
|
|
235
238
|
|
|
236
239
|
```ts
|
|
237
240
|
const picked = await askJson<{ tools: string[] }>(config, small, system, request, PRESELECT_SCHEMA, {
|
|
@@ -1197,8 +1200,9 @@ const result = await runAgentLoop({ config, preselect: { keywords: true }, compa
|
|
|
1197
1200
|
|
|
1198
1201
|
Four caches outlive any one run: the `OpenAI` clients, the model listings, the latched
|
|
1199
1202
|
capabilities, and `side-task`'s no-thinking hints. A fifth, the characters per token each model was
|
|
1200
|
-
measured at, is keyed on the endpoint's capabilities object, so it goes with them. All four are
|
|
1201
|
-
|
|
1203
|
+
measured at, is keyed on the endpoint's capabilities object, so it goes with them. All four are
|
|
1204
|
+
held once per runtime — once per process, unless you make another, [below](#a-runtime-of-its-own)
|
|
1205
|
+
— and keyed on the same notion of an endpoint — its base URL and its API key — and the clients' key carries the request
|
|
1202
1206
|
timeout as well, since that changes how a request is sent.
|
|
1203
1207
|
|
|
1204
1208
|
**They are keyed per deployment, not per request.** What belongs in them is an endpoint an
|
|
@@ -1209,8 +1213,8 @@ bounding them assumes that, and a consumer that mints an API key per *user* brea
|
|
|
1209
1213
|
|
|
1210
1214
|
That is bounded rather than unbounded: the client pool is a 32-entry LRU, and an evicted client
|
|
1211
1215
|
costs its connection pool and nothing else, since the next request through that endpoint builds
|
|
1212
|
-
another. But churning connection pools is not sharing them, and at that point a
|
|
1213
|
-
|
|
1216
|
+
another. But churning connection pools is not sharing them, and at that point a runtime per
|
|
1217
|
+
tenant, [below](#a-runtime-of-its-own), is the better answer than this.
|
|
1214
1218
|
|
|
1215
1219
|
The listings cache also remembers, for half a minute, that an endpoint did not name a model — the
|
|
1216
1220
|
case where a configured name never matches anything the server serves (a llama.cpp `-a` alias, an
|
|
@@ -1284,6 +1288,57 @@ carries it in the snapshot so an imported latch keeps its real age instead of be
|
|
|
1284
1288
|
every boot. Importing takes the older of the two ages, and a snapshot written before this field
|
|
1285
1289
|
existed reads as met now.
|
|
1286
1290
|
|
|
1291
|
+
### A runtime of its own
|
|
1292
|
+
|
|
1293
|
+
Everything above is the *default runtime*: one set of caches, shared by every caller in the
|
|
1294
|
+
process. `createRuntime` makes another, with its own clients, listings, latches, hints, measured
|
|
1295
|
+
ratios, event bus and hook settings, and the functions that use them as methods of the same names
|
|
1296
|
+
and signatures:
|
|
1297
|
+
|
|
1298
|
+
```ts
|
|
1299
|
+
import { createRuntime } from "@cubicecho/agent-core";
|
|
1300
|
+
|
|
1301
|
+
const runtime = createRuntime({ clients: { maxClients: 8 }, events: { maxEvents: 200 } });
|
|
1302
|
+
|
|
1303
|
+
const run = await runtime.runAgentLoop({ config, messages, dispatch });
|
|
1304
|
+
for await (const event of runtime.watch(runId)) send(event);
|
|
1305
|
+
runtime.resetAll(); // this runtime's caches, and nobody else's
|
|
1306
|
+
```
|
|
1307
|
+
|
|
1308
|
+
It is for the host the section above warns off — one that mints a key per tenant, where a shared
|
|
1309
|
+
pool has tenants evicting each other's clients and a shared bus makes one tenant's run id readable
|
|
1310
|
+
by another — and for tests, where a runtime per case needs no `resetAll` between cases and lets
|
|
1311
|
+
them run in parallel. A host with one deployment's worth of endpoints needs none: the top-level
|
|
1312
|
+
functions are the default runtime's methods, and nothing about them has changed.
|
|
1313
|
+
|
|
1314
|
+
The runtime travels by async context (`AsyncLocalStorage`), not by argument. A method runs its
|
|
1315
|
+
function with the runtime current, and it stays current across every `await` beneath it — so
|
|
1316
|
+
`runtime.runAgentLoop` uses the runtime's clients and latches all the way down, and so does a
|
|
1317
|
+
top-level function your code calls from inside it: an `emit` in `onEvent`, an `ask` in `dispatch`,
|
|
1318
|
+
a `gather` in a hook. `runtime.run(fn)` does the same for code that calls the top-level functions
|
|
1319
|
+
itself:
|
|
1320
|
+
|
|
1321
|
+
```ts
|
|
1322
|
+
await runtime.run(async () => {
|
|
1323
|
+
const limit = await contextLimitFor(config); // the runtime's listing cache
|
|
1324
|
+
emit(runId, { kind: "output", text: "…" }); // the runtime's bus
|
|
1325
|
+
});
|
|
1326
|
+
```
|
|
1327
|
+
|
|
1328
|
+
Outside a method or `run`, a top-level function is the default runtime's again. That is the one
|
|
1329
|
+
thing to keep straight: a request handler that watches or emits to a run a runtime started is not
|
|
1330
|
+
inside it, and calls `runtime.watch` or `runtime.emit`. What a method hands back keeps its runtime
|
|
1331
|
+
— the iterator from `runtime.watch`, the function from `runtime.summariser` — wherever it is read.
|
|
1332
|
+
|
|
1333
|
+
A new runtime starts from the package's defaults plus what `createRuntime` was given; it does not
|
|
1334
|
+
inherit what `configureClients` and the others set on the default one. Nothing needs closing: a
|
|
1335
|
+
runtime is garbage once nothing holds it. `defaultRuntime` is the process's own as an object, for
|
|
1336
|
+
code that takes a `Runtime` and should be handed the shared one.
|
|
1337
|
+
|
|
1338
|
+
The two schema caches (`sanitizeTools`, `relaxTools`) and `requestTokens`' tool-length cache stay
|
|
1339
|
+
process-wide. They are memoised pure functions keyed on the caller's own objects, so there is
|
|
1340
|
+
nothing in them for one tenant to learn about another.
|
|
1341
|
+
|
|
1287
1342
|
## Where the merged behaviour came from
|
|
1288
1343
|
|
|
1289
1344
|
- `schema-compat` — `kanban_server`/`task_server`'s version, which strips **every** sibling of a
|
package/dist/agent-loop.d.ts
CHANGED
|
@@ -4,10 +4,11 @@ import type { CatalogServer } from "./catalog.ts";
|
|
|
4
4
|
import { type CompactionOptions } from "./compaction.ts";
|
|
5
5
|
import type { Endpoint, ModelParams, RetryPolicy, ToolPolicy } from "./config.ts";
|
|
6
6
|
import { type RunEventInput, type RunMetrics } from "./events.ts";
|
|
7
|
-
import
|
|
7
|
+
import type { HookEvent } from "./hook-events.ts";
|
|
8
|
+
import { type HookContext, type HookNote, type HookRunner } from "./hooks.ts";
|
|
8
9
|
import { type TokenLedger } from "./ledger.ts";
|
|
9
10
|
import { type SideTask, type SideTaskOptions } from "./side-task.ts";
|
|
10
|
-
import type
|
|
11
|
+
import { type Turn, type TurnUsage } from "./stream.ts";
|
|
11
12
|
import { type KeywordPreselectOptions, type ToolOrder } from "./tool-loading.ts";
|
|
12
13
|
/**
|
|
13
14
|
* The one place a streamed request's body is decided from a config and what the endpoint and
|