@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/dist/guards.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The checks a value read off the wire is put through before anything is read from it.
|
|
3
|
+
*
|
|
4
|
+
* A leaf with no imports, because the modules that need these — a browser-safe `spec`, the schema
|
|
5
|
+
* walk, the tool-call reader, the snapshot importer — otherwise share nothing, and each had grown
|
|
6
|
+
* its own copy.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Whether a value is a plain keyed object: not null, and not an array.
|
|
10
|
+
*
|
|
11
|
+
* @param value Anything, usually just parsed. An array is an object to `typeof` and not to this.
|
|
12
|
+
*/
|
|
13
|
+
export const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
14
|
+
/**
|
|
15
|
+
* Whether a value is a number above zero, which is what a limit has to be to be one.
|
|
16
|
+
*
|
|
17
|
+
* `Infinity` passes and `NaN` does not, so a caller lifting a bound and one handing over a
|
|
18
|
+
* half-built config are told apart without either being asked.
|
|
19
|
+
*
|
|
20
|
+
* @param value Anything, usually a setting that may be absent, zero or mistyped.
|
|
21
|
+
*/
|
|
22
|
+
export const isPositive = (value) => typeof value === "number" && value > 0;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The points in a session a hook can be bound to, apart from everything that runs one.
|
|
3
|
+
*
|
|
4
|
+
* A leaf with no imports, because two modules need these lists and only one of them may load
|
|
5
|
+
* `node:crypto`: `hooks.ts` hashes a turn into the uuid a memory server dedups on, and `spec.ts`
|
|
6
|
+
* has to stay importable in a browser. Before this each kept its own copy, with a test holding
|
|
7
|
+
* one pair together and nothing holding the other.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* A point in a session a hook can be bound to. Named after Claude Code's hooks of the same shape
|
|
11
|
+
* — `sessionStart` (SessionStart), `beforeTurn` (UserPromptSubmit), `afterTurn` (Stop),
|
|
12
|
+
* `beforeCompact` (PreCompact), `sessionEnd` (SessionEnd) — plus `sessionDelete`, for when the host
|
|
13
|
+
* deletes a session's record.
|
|
14
|
+
*/
|
|
15
|
+
export type HookEvent = "sessionStart" | "beforeTurn" | "afterTurn" | "beforeCompact" | "sessionEnd" | "sessionDelete";
|
|
16
|
+
/** Every event a hook can be bound to, in the order a session meets them. */
|
|
17
|
+
export declare const HOOK_EVENTS: readonly HookEvent[];
|
|
18
|
+
/**
|
|
19
|
+
* The events whose hooks run before a request, and so the only ones whose output can reach it.
|
|
20
|
+
* Anything later runs once the model has already answered.
|
|
21
|
+
*/
|
|
22
|
+
export declare const INJECT_EVENTS: ReadonlySet<HookEvent>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The points in a session a hook can be bound to, apart from everything that runs one.
|
|
3
|
+
*
|
|
4
|
+
* A leaf with no imports, because two modules need these lists and only one of them may load
|
|
5
|
+
* `node:crypto`: `hooks.ts` hashes a turn into the uuid a memory server dedups on, and `spec.ts`
|
|
6
|
+
* has to stay importable in a browser. Before this each kept its own copy, with a test holding
|
|
7
|
+
* one pair together and nothing holding the other.
|
|
8
|
+
*/
|
|
9
|
+
/** Every event a hook can be bound to, in the order a session meets them. */
|
|
10
|
+
export const HOOK_EVENTS = [
|
|
11
|
+
"sessionStart",
|
|
12
|
+
"beforeTurn",
|
|
13
|
+
"afterTurn",
|
|
14
|
+
"beforeCompact",
|
|
15
|
+
"sessionEnd",
|
|
16
|
+
"sessionDelete",
|
|
17
|
+
];
|
|
18
|
+
/**
|
|
19
|
+
* The events whose hooks run before a request, and so the only ones whose output can reach it.
|
|
20
|
+
* Anything later runs once the model has already answered.
|
|
21
|
+
*/
|
|
22
|
+
export const INJECT_EVENTS = new Set(["sessionStart", "beforeTurn"]);
|
package/dist/hooks.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type OpenAI from "openai";
|
|
2
|
+
import { type HookEvent } from "./hook-events.ts";
|
|
2
3
|
/**
|
|
3
4
|
* Lifecycle hooks, from the host's side: what a session looks like to them, where their context
|
|
4
5
|
* lands in a request, and what is said about each one.
|
|
@@ -9,20 +10,6 @@ import type OpenAI from "openai";
|
|
|
9
10
|
* `runHooks` is a runner as it stands and its outcomes pass straight through, without this
|
|
10
11
|
* package depending on it.
|
|
11
12
|
*/
|
|
12
|
-
/**
|
|
13
|
-
* A point in a session a hook can be bound to. Named after Claude Code's hooks of the same shape
|
|
14
|
-
* — `sessionStart` (SessionStart), `beforeTurn` (UserPromptSubmit), `afterTurn` (Stop),
|
|
15
|
-
* `beforeCompact` (PreCompact), `sessionEnd` (SessionEnd) — plus `sessionDelete`, for when the host
|
|
16
|
-
* deletes a session's record.
|
|
17
|
-
*/
|
|
18
|
-
export type HookEvent = "sessionStart" | "beforeTurn" | "afterTurn" | "beforeCompact" | "sessionEnd" | "sessionDelete";
|
|
19
|
-
/** Every event a hook can be bound to, in the order a session meets them. */
|
|
20
|
-
export declare const HOOK_EVENTS: readonly HookEvent[];
|
|
21
|
-
/**
|
|
22
|
-
* The events whose hooks run before a request, and so the only ones whose output can reach it.
|
|
23
|
-
* Anything later runs once the model has already answered.
|
|
24
|
-
*/
|
|
25
|
-
export declare const INJECT_EVENTS: ReadonlySet<HookEvent>;
|
|
26
13
|
/**
|
|
27
14
|
* One message of a session as a hook is handed it. The shape a memory server's `remember` takes,
|
|
28
15
|
* so a template can pass a turn straight through.
|
|
@@ -160,8 +147,8 @@ export interface HookOptions {
|
|
|
160
147
|
* Changes what hooks are held to, for a process whose windows are not the size these defaults
|
|
161
148
|
* were chosen for, or whose host wants its own name above the context.
|
|
162
149
|
*
|
|
163
|
-
*
|
|
164
|
-
* settings, said once at startup. A caller that sizes the budget per model or per agent — a 128k
|
|
150
|
+
* One setting per runtime for the same reason `configureEvents` is: a budget and a preface are a
|
|
151
|
+
* deployment's settings, said once at startup. A caller that sizes the budget per model or per agent — a 128k
|
|
165
152
|
* window can afford more recall than an 8k one — passes `maxTokens` to `gather` instead, and a
|
|
166
153
|
* `preface` passed to `withContext` or `runAgentLoop`'s hooks wins over this one the same way.
|
|
167
154
|
*
|
|
@@ -248,6 +235,18 @@ export declare const UNTRUSTED_PREFACE: string;
|
|
|
248
235
|
export declare function untrusted(text: string, { source }?: {
|
|
249
236
|
source?: string;
|
|
250
237
|
}): string;
|
|
238
|
+
/**
|
|
239
|
+
* A message's text, whether its content is a string or a list of parts.
|
|
240
|
+
*
|
|
241
|
+
* Parts are joined with nothing between them, because they are one text an API split rather than
|
|
242
|
+
* several: a space put in is a character the sender never wrote, and `turnMessages` hashes this
|
|
243
|
+
* into the uuid a memory server dedups on. Compaction reads the same function, so the summariser
|
|
244
|
+
* and the hook told about a fold see one message the same way. A refusal part is left out.
|
|
245
|
+
*
|
|
246
|
+
* @param content A message's `content`, in any of the shapes the API allows. One that is neither
|
|
247
|
+
* a string nor a list reads as empty.
|
|
248
|
+
*/
|
|
249
|
+
export declare const textOf: (content: unknown) => string;
|
|
251
250
|
/**
|
|
252
251
|
* A stretch of a transcript as a hook reads it: what the user and the assistant said, and nothing
|
|
253
252
|
* else.
|
package/dist/hooks.js
CHANGED
|
@@ -1,20 +1,9 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { errorMessage } from "./errors.js";
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
"beforeTurn",
|
|
8
|
-
"afterTurn",
|
|
9
|
-
"beforeCompact",
|
|
10
|
-
"sessionEnd",
|
|
11
|
-
"sessionDelete",
|
|
12
|
-
];
|
|
13
|
-
/**
|
|
14
|
-
* The events whose hooks run before a request, and so the only ones whose output can reach it.
|
|
15
|
-
* Anything later runs once the model has already answered.
|
|
16
|
-
*/
|
|
17
|
-
export const INJECT_EVENTS = new Set(["sessionStart", "beforeTurn"]);
|
|
3
|
+
import { isPositive } from "./guards.js";
|
|
4
|
+
import { INJECT_EVENTS } from "./hook-events.js";
|
|
5
|
+
import { scoped } from "./scope.js";
|
|
6
|
+
import { CHARS_PER_TOKEN, estimateTokens } from "./tokens.js";
|
|
18
7
|
/**
|
|
19
8
|
* The most context all of a request's hooks add between them by default, in estimated tokens.
|
|
20
9
|
*
|
|
@@ -36,13 +25,13 @@ const HOOK_DEFAULTS = {
|
|
|
36
25
|
preface: HOOK_PREFACE,
|
|
37
26
|
};
|
|
38
27
|
/** What is in force now. Read where it is used, so a change applies from the next request. */
|
|
39
|
-
|
|
28
|
+
const hookSettings = scoped(() => ({ ...HOOK_DEFAULTS }));
|
|
40
29
|
/**
|
|
41
30
|
* Changes what hooks are held to, for a process whose windows are not the size these defaults
|
|
42
31
|
* were chosen for, or whose host wants its own name above the context.
|
|
43
32
|
*
|
|
44
|
-
*
|
|
45
|
-
* settings, said once at startup. A caller that sizes the budget per model or per agent — a 128k
|
|
33
|
+
* One setting per runtime for the same reason `configureEvents` is: a budget and a preface are a
|
|
34
|
+
* deployment's settings, said once at startup. A caller that sizes the budget per model or per agent — a 128k
|
|
46
35
|
* window can afford more recall than an 8k one — passes `maxTokens` to `gather` instead, and a
|
|
47
36
|
* `preface` passed to `withContext` or `runAgentLoop`'s hooks wins over this one the same way.
|
|
48
37
|
*
|
|
@@ -54,12 +43,11 @@ let hookSettings = { ...HOOK_DEFAULTS };
|
|
|
54
43
|
*/
|
|
55
44
|
export function configureHooks(options = {}) {
|
|
56
45
|
const { contextTokens, preface } = options;
|
|
57
|
-
if (
|
|
58
|
-
hookSettings.contextTokens = contextTokens;
|
|
59
|
-
}
|
|
46
|
+
if (isPositive(contextTokens))
|
|
47
|
+
hookSettings().contextTokens = contextTokens;
|
|
60
48
|
if (typeof preface === "string")
|
|
61
|
-
hookSettings.preface = preface;
|
|
62
|
-
return { ...hookSettings };
|
|
49
|
+
hookSettings().preface = preface;
|
|
50
|
+
return { ...hookSettings() };
|
|
63
51
|
}
|
|
64
52
|
/**
|
|
65
53
|
* Test seam: puts `configureHooks` back to the defaults, so one test's budget or preface is not the
|
|
@@ -67,15 +55,26 @@ export function configureHooks(options = {}) {
|
|
|
67
55
|
* `resetAll` calls it.
|
|
68
56
|
*/
|
|
69
57
|
export const resetHooks = () => {
|
|
70
|
-
hookSettings
|
|
58
|
+
hookSettings.reset();
|
|
71
59
|
};
|
|
72
60
|
/**
|
|
73
61
|
* The budget a call is held to: its own when it gave a usable one, the process's otherwise. The
|
|
74
62
|
* same rule `configureHooks` applies, so a `0` threaded through for "no opinion" does not quietly
|
|
75
63
|
* turn every hook's context off.
|
|
76
64
|
*/
|
|
77
|
-
const budget = (given) =>
|
|
65
|
+
const budget = (given) => (isPositive(given) ? given : hookSettings().contextTokens);
|
|
78
66
|
const attribute = (text) => text.replaceAll("&", "&").replaceAll('"', """).replaceAll("<", "<");
|
|
67
|
+
/** What every note about an outcome opens with: which hook it was, and on which event. */
|
|
68
|
+
const noteOf = (outcome) => ({
|
|
69
|
+
event: outcome.event,
|
|
70
|
+
source: outcome.label,
|
|
71
|
+
hookId: outcome.hookId,
|
|
72
|
+
});
|
|
73
|
+
/** The note a failed outcome leaves, the same whichever of the three callers reports it. */
|
|
74
|
+
const failureNote = (outcome) => ({
|
|
75
|
+
...noteOf(outcome),
|
|
76
|
+
error: outcome.error ?? "failed",
|
|
77
|
+
});
|
|
79
78
|
/**
|
|
80
79
|
* Builds the context a set of outcomes adds and the notes that go with it.
|
|
81
80
|
*
|
|
@@ -99,9 +98,8 @@ export function assembleContext(outcomes, maxTokens) {
|
|
|
99
98
|
const notes = [];
|
|
100
99
|
let remaining = budget(maxTokens);
|
|
101
100
|
for (const outcome of outcomes) {
|
|
102
|
-
const base = { event: outcome.event, source: outcome.label, hookId: outcome.hookId };
|
|
103
101
|
if (!outcome.ok) {
|
|
104
|
-
notes.push(
|
|
102
|
+
notes.push(failureNote(outcome));
|
|
105
103
|
continue;
|
|
106
104
|
}
|
|
107
105
|
if (!outcome.inject || !INJECT_EVENTS.has(outcome.event))
|
|
@@ -113,11 +111,11 @@ export function assembleContext(outcomes, maxTokens) {
|
|
|
113
111
|
if (cap <= 0)
|
|
114
112
|
continue;
|
|
115
113
|
if (estimateTokens(text) > cap)
|
|
116
|
-
text = `${text.slice(0, cap *
|
|
114
|
+
text = `${text.slice(0, cap * CHARS_PER_TOKEN - 1).trimEnd()}…`;
|
|
117
115
|
const tokens = estimateTokens(text);
|
|
118
116
|
remaining -= tokens;
|
|
119
117
|
blocks.push(`<context source="${attribute(outcome.label)}">\n${text}\n</context>`);
|
|
120
|
-
notes.push({ ...
|
|
118
|
+
notes.push({ ...noteOf(outcome), tokens, text });
|
|
121
119
|
}
|
|
122
120
|
return { context: blocks.join("\n\n"), notes };
|
|
123
121
|
}
|
|
@@ -143,7 +141,7 @@ export function assembleContext(outcomes, maxTokens) {
|
|
|
143
141
|
* unless something moved it. Empty says nothing, rather than leaving a blank line where it was.
|
|
144
142
|
* @returns `history` when there was nothing to add or nowhere to add it, otherwise a new array.
|
|
145
143
|
*/
|
|
146
|
-
export function withContext(history, index, context, preface = hookSettings.preface) {
|
|
144
|
+
export function withContext(history, index, context, preface = hookSettings().preface) {
|
|
147
145
|
const message = history[index];
|
|
148
146
|
if (!context || message?.role !== "user")
|
|
149
147
|
return history;
|
|
@@ -186,8 +184,18 @@ export function untrusted(text, { source } = {}) {
|
|
|
186
184
|
const attrs = source === undefined ? "" : ` source="${attribute(source)}"`;
|
|
187
185
|
return `<untrusted${attrs}>\n${text.replace(UNTRUSTED_TAG, "<$1")}\n</untrusted>`;
|
|
188
186
|
}
|
|
189
|
-
/**
|
|
190
|
-
|
|
187
|
+
/**
|
|
188
|
+
* A message's text, whether its content is a string or a list of parts.
|
|
189
|
+
*
|
|
190
|
+
* Parts are joined with nothing between them, because they are one text an API split rather than
|
|
191
|
+
* several: a space put in is a character the sender never wrote, and `turnMessages` hashes this
|
|
192
|
+
* into the uuid a memory server dedups on. Compaction reads the same function, so the summariser
|
|
193
|
+
* and the hook told about a fold see one message the same way. A refusal part is left out.
|
|
194
|
+
*
|
|
195
|
+
* @param content A message's `content`, in any of the shapes the API allows. One that is neither
|
|
196
|
+
* a string nor a list reads as empty.
|
|
197
|
+
*/
|
|
198
|
+
export const textOf = (content) => {
|
|
191
199
|
if (typeof content === "string")
|
|
192
200
|
return content;
|
|
193
201
|
if (!Array.isArray(content))
|
|
@@ -317,9 +325,7 @@ export async function gather(run, events, context, { signal, onNote, maxTokens,
|
|
|
317
325
|
*/
|
|
318
326
|
export async function notify(run, event, context, onNote) {
|
|
319
327
|
const outcomes = await runSafely(run, event, context);
|
|
320
|
-
const notes = outcomes
|
|
321
|
-
.filter((outcome) => !outcome.ok)
|
|
322
|
-
.map((outcome) => assembleContext([outcome]).notes[0]);
|
|
328
|
+
const notes = outcomes.filter((outcome) => !outcome.ok).map(failureNote);
|
|
323
329
|
for (const note of notes)
|
|
324
330
|
onNote?.(note);
|
|
325
331
|
return notes;
|
|
@@ -344,15 +350,9 @@ export async function consult(run, event, context, onNote) {
|
|
|
344
350
|
const notes = [];
|
|
345
351
|
for (const outcome of outcomes) {
|
|
346
352
|
if (!outcome.ok)
|
|
347
|
-
notes.push(
|
|
348
|
-
else if (outcome.veto === true)
|
|
349
|
-
notes.push({
|
|
350
|
-
event: outcome.event,
|
|
351
|
-
source: outcome.label,
|
|
352
|
-
hookId: outcome.hookId,
|
|
353
|
-
veto: true,
|
|
354
|
-
});
|
|
355
|
-
}
|
|
353
|
+
notes.push(failureNote(outcome));
|
|
354
|
+
else if (outcome.veto === true)
|
|
355
|
+
notes.push({ ...noteOf(outcome), veto: true });
|
|
356
356
|
}
|
|
357
357
|
for (const note of notes)
|
|
358
358
|
onNote?.(note);
|
package/dist/index.d.ts
CHANGED
|
@@ -19,17 +19,19 @@ export type { AgentConfig, Endpoint, ModelParams, RetryPolicy, ToolPolicy, } fro
|
|
|
19
19
|
export { type ContinueTurnOptions, continueTurn, isContinuable, } from "./continuation.ts";
|
|
20
20
|
export { AgentLoopError, type AgentLoopFailure, AgentLoopOverflow, errorMessage, failedRun, ToolIterationLimit, } from "./errors.ts";
|
|
21
21
|
export { configureEvents, type EventBusOptions, emit, endRun, fold, history, type RunEvent, type RunEventInput, type RunEventKind, type RunMetrics, type RunMetricsOptions, type RunUsage, resetEvents, runMetrics, type TurnReport, watch, } from "./events.ts";
|
|
22
|
-
export {
|
|
22
|
+
export { HOOK_EVENTS, type HookEvent, INJECT_EVENTS } from "./hook-events.ts";
|
|
23
|
+
export { assembleContext, configureHooks, consult, type Gathered, gather, HOOK_CONTEXT_TOKENS, HOOK_PREFACE, type HookContext, type HookMessage, type HookNote, type HookOptions, type HookOutcome, type HookRunner, notify, resetHooks, turnIndex, turnMessages, UNTRUSTED_PREFACE, untrusted, withContext, } from "./hooks.ts";
|
|
23
24
|
export { estimateFrom, type LedgerEntry, type LedgerEstimateOptions, type LedgerRequest, rebaseLedger, recordRequest, type TokenLedger, tokensBetween, } from "./ledger.ts";
|
|
24
25
|
export { resetAll } from "./reset.ts";
|
|
25
|
-
export { backoffMs,
|
|
26
|
+
export { backoffMs, type ContextBreakdown, type ContextBreakdownOptions, ContextOverflow, compact, contextChars, contextTokens, EndpointSilent, isModelLoading, isOverflow, isTransient, LOADING_POLL_MS, LOADING_TIMEOUT_MS, messageTokens, requestChars, requestTokens, SMALLEST_LIKELY_WINDOW, sleep, type TokenEstimateOptions, toolsChars, } from "./retry.ts";
|
|
26
27
|
export { type RunTurnOptions, runTurn } from "./run-turn.ts";
|
|
27
|
-
export {
|
|
28
|
+
export { createRuntime, defaultRuntime, type Runtime, type RuntimeOptions } from "./runtime.ts";
|
|
29
|
+
export { isGrammarError, relaxSchema, relaxTools, sanitizeSchema, sanitizeTools, } from "./schema-compat.ts";
|
|
28
30
|
export { type AskJsonOptions, ask, askJson, clean, listLines, parseJson, resetHints, type SideTask, type SideTaskInput, type SideTaskOptions, tryAsk, } from "./side-task.ts";
|
|
29
31
|
export { CAPABILITY_SNAPSHOT_VERSION, type CapabilitySnapshot, type EndpointSnapshot, exportCapabilities, importCapabilities, type ModelSnapshot, } from "./snapshot.ts";
|
|
30
32
|
export { AGENT_SPEC, AGENT_SPEC_VERSION, AGENT_TASKS, type AgentHook, type AgentSpec, type EndpointSpec, type ExportSpecOptions, exportSpec, type ModelSpec, type ParsedSpec, type ParseSpecOptions, type PromptPart, parseSpec, RESOLVED_DEFAULTS, type ResolvedAgent, type ResolvedTask, type RetrySpec, resolveAgentSpec, SPEC_EVENTS, type SpecBundle, type SpecServer, type TaskSpec, type ToolsSpec, } from "./spec.ts";
|
|
31
33
|
export { type Produced, type StreamTurnOptions, streamTurn, type Turn, type TurnUsage, } from "./stream.ts";
|
|
32
34
|
export { ALL_FENCES, DEFAULT_FENCES, type Fence, FenceSplitter, type FenceSplitterOptions, type Split, stripThinking, THINK_FENCE, } from "./thinking.ts";
|
|
33
|
-
export { estimateTokens } from "./tokens.ts";
|
|
35
|
+
export { CHARS_PER_TOKEN, estimateTokens } from "./tokens.ts";
|
|
34
36
|
export { parseToolArguments, recoverToolCalls, ToolArgumentsError, type ToolCall, } from "./tool-calls.ts";
|
|
35
37
|
export { CALL_TOOL, carryOver, catalogList, catalogPrompt, expandNames, holdsDefinitions, inCatalog, KEYWORD_DROPOFF, KEYWORD_MIN_SCORE, type KeywordPreselection, type KeywordPreselectOptions, LOAD_TOOLS, LOAD_TOOLS_DEFINITION, loadedTools, loadResult, MAX_CARRIED, MAX_PER_LOAD, orderTools, PRESELECT_SCHEMA, PRESELECT_SYSTEM, PROXY_TOOLS, preselectByKeywords, preselectInput, preselection, preselectSystem, proxiedCall, proxyCatalogPrompt, proxyLoadResult, requestedNames, shownCall, type ToolMatch, type ToolOrder, } from "./tool-loading.ts";
|
package/dist/index.js
CHANGED
|
@@ -17,17 +17,19 @@ export { applyCompaction, COMPACT_AT, compactTranscript, KEEP_RATIO, planCompact
|
|
|
17
17
|
export { continueTurn, isContinuable, } from "./continuation.js";
|
|
18
18
|
export { AgentLoopError, AgentLoopOverflow, errorMessage, failedRun, ToolIterationLimit, } from "./errors.js";
|
|
19
19
|
export { configureEvents, emit, endRun, fold, history, resetEvents, runMetrics, watch, } from "./events.js";
|
|
20
|
-
export {
|
|
20
|
+
export { HOOK_EVENTS, INJECT_EVENTS } from "./hook-events.js";
|
|
21
|
+
export { assembleContext, configureHooks, consult, gather, HOOK_CONTEXT_TOKENS, HOOK_PREFACE, notify, resetHooks, turnIndex, turnMessages, UNTRUSTED_PREFACE, untrusted, withContext, } from "./hooks.js";
|
|
21
22
|
export { estimateFrom, rebaseLedger, recordRequest, tokensBetween, } from "./ledger.js";
|
|
22
23
|
export { resetAll } from "./reset.js";
|
|
23
|
-
export { backoffMs,
|
|
24
|
+
export { backoffMs, ContextOverflow, compact, contextChars, contextTokens, EndpointSilent, isModelLoading, isOverflow, isTransient, LOADING_POLL_MS, LOADING_TIMEOUT_MS, messageTokens, requestChars, requestTokens, SMALLEST_LIKELY_WINDOW, sleep, toolsChars, } from "./retry.js";
|
|
24
25
|
export { runTurn } from "./run-turn.js";
|
|
25
|
-
export {
|
|
26
|
+
export { createRuntime, defaultRuntime } from "./runtime.js";
|
|
27
|
+
export { isGrammarError, relaxSchema, relaxTools, sanitizeSchema, sanitizeTools, } from "./schema-compat.js";
|
|
26
28
|
export { ask, askJson, clean, listLines, parseJson, resetHints, tryAsk, } from "./side-task.js";
|
|
27
29
|
export { CAPABILITY_SNAPSHOT_VERSION, exportCapabilities, importCapabilities, } from "./snapshot.js";
|
|
28
30
|
export { AGENT_SPEC, AGENT_SPEC_VERSION, AGENT_TASKS, exportSpec, parseSpec, RESOLVED_DEFAULTS, resolveAgentSpec, SPEC_EVENTS, } from "./spec.js";
|
|
29
31
|
export { streamTurn, } from "./stream.js";
|
|
30
32
|
export { ALL_FENCES, DEFAULT_FENCES, FenceSplitter, stripThinking, THINK_FENCE, } from "./thinking.js";
|
|
31
|
-
export { estimateTokens } from "./tokens.js";
|
|
33
|
+
export { CHARS_PER_TOKEN, estimateTokens } from "./tokens.js";
|
|
32
34
|
export { parseToolArguments, recoverToolCalls, ToolArgumentsError, } from "./tool-calls.js";
|
|
33
35
|
export { CALL_TOOL, carryOver, catalogList, catalogPrompt, expandNames, holdsDefinitions, inCatalog, KEYWORD_DROPOFF, KEYWORD_MIN_SCORE, LOAD_TOOLS, LOAD_TOOLS_DEFINITION, loadedTools, loadResult, MAX_CARRIED, MAX_PER_LOAD, orderTools, PRESELECT_SCHEMA, PRESELECT_SYSTEM, PROXY_TOOLS, preselectByKeywords, preselectInput, preselection, preselectSystem, proxiedCall, proxyCatalogPrompt, proxyLoadResult, requestedNames, shownCall, } from "./tool-loading.js";
|
package/dist/reset.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Forgets everything this package remembers between calls.
|
|
3
3
|
*
|
|
4
|
-
* Six modules here keep state for the life of the
|
|
5
|
-
*
|
|
4
|
+
* Six modules here keep state for the life of the runtime — the process's, unless this is called
|
|
5
|
+
* as a method of one `createRuntime` made, which clears that one alone — each for a good reason
|
|
6
|
+
* and each with its own seam: the pooled clients and their model listings, the endpoints that turned
|
|
6
7
|
* out not to take `stream_options` or a grammar, each model's measured characters per token,
|
|
7
8
|
* the models that refused the no-thinking hints, the event bus, and the hooks' configured
|
|
8
9
|
* budget and preface. `resetClients`, `resetCapabilities`, `resetCalibration`, `resetHints`,
|
|
@@ -18,5 +19,8 @@
|
|
|
18
19
|
*
|
|
19
20
|
* It is also the seam that does not need finding again. A seventh module with a cache is a seventh
|
|
20
21
|
* line here, rather than an edit to the teardown of three consumers who will not all notice.
|
|
22
|
+
*
|
|
23
|
+
* A test that builds a `createRuntime` per case has nothing to tear down, and is the better
|
|
24
|
+
* answer where cases run in parallel: this clears state every concurrent caller is sharing.
|
|
21
25
|
*/
|
|
22
26
|
export declare function resetAll(): void;
|
package/dist/reset.js
CHANGED
|
@@ -7,8 +7,9 @@ import { resetHints } from "./side-task.js";
|
|
|
7
7
|
/**
|
|
8
8
|
* Forgets everything this package remembers between calls.
|
|
9
9
|
*
|
|
10
|
-
* Six modules here keep state for the life of the
|
|
11
|
-
*
|
|
10
|
+
* Six modules here keep state for the life of the runtime — the process's, unless this is called
|
|
11
|
+
* as a method of one `createRuntime` made, which clears that one alone — each for a good reason
|
|
12
|
+
* and each with its own seam: the pooled clients and their model listings, the endpoints that turned
|
|
12
13
|
* out not to take `stream_options` or a grammar, each model's measured characters per token,
|
|
13
14
|
* the models that refused the no-thinking hints, the event bus, and the hooks' configured
|
|
14
15
|
* budget and preface. `resetClients`, `resetCapabilities`, `resetCalibration`, `resetHints`,
|
|
@@ -24,6 +25,9 @@ import { resetHints } from "./side-task.js";
|
|
|
24
25
|
*
|
|
25
26
|
* It is also the seam that does not need finding again. A seventh module with a cache is a seventh
|
|
26
27
|
* line here, rather than an edit to the teardown of three consumers who will not all notice.
|
|
28
|
+
*
|
|
29
|
+
* A test that builds a `createRuntime` per case has nothing to tear down, and is the better
|
|
30
|
+
* answer where cases run in parallel: this clears state every concurrent caller is sharing.
|
|
27
31
|
*/
|
|
28
32
|
export function resetAll() {
|
|
29
33
|
resetClients();
|
package/dist/retry.d.ts
CHANGED
|
@@ -29,13 +29,6 @@ export declare class ContextOverflow extends Error {
|
|
|
29
29
|
* @param tokens The count to render.
|
|
30
30
|
*/
|
|
31
31
|
export declare const compact: (tokens: number) => string;
|
|
32
|
-
/**
|
|
33
|
-
* The divisor behind `estimateTokens`, applied here to a character count rather than a string.
|
|
34
|
-
*
|
|
35
|
-
* The fallback, not the rule: once a turn has come back with a reported prompt count, `runTurn`
|
|
36
|
-
* divides by what that endpoint's model was measured at instead. See `charsPerTokenFor`.
|
|
37
|
-
*/
|
|
38
|
-
export declare const CHARS_PER_TOKEN = 4;
|
|
39
32
|
/** What the two token estimates below take besides what they measure. */
|
|
40
33
|
export interface TokenEstimateOptions {
|
|
41
34
|
/**
|
|
@@ -192,6 +185,19 @@ export declare const SMALLEST_LIKELY_WINDOW = 8192;
|
|
|
192
185
|
* @param error The rejection, as caught. What is not an SDK error is not transient.
|
|
193
186
|
*/
|
|
194
187
|
export declare function isTransient(error: unknown): boolean;
|
|
188
|
+
/**
|
|
189
|
+
* Whether a failure is the endpoint refusing the request as written, rather than losing it.
|
|
190
|
+
*
|
|
191
|
+
* 400 and 422 are what a server says when it read the body and disliked it, which makes them the
|
|
192
|
+
* only statuses worth answering by sending something different. Any 4xx is too wide: 401, 404 and
|
|
193
|
+
* 429 are 4xx and none of them is about the fields, and a 429 is one `isTransient` accepts — a
|
|
194
|
+
* caller that re-sent on it at once doubled the rate against a server that had just asked for less.
|
|
195
|
+
* Catching everything is wider still, since an abort or a connection that never landed then
|
|
196
|
+
* latches off whatever the re-send left out.
|
|
197
|
+
*
|
|
198
|
+
* @param error The rejection, as caught. What is not an SDK error refuses nothing.
|
|
199
|
+
*/
|
|
200
|
+
export declare const refusesRequest: (error: unknown) => boolean;
|
|
195
201
|
/**
|
|
196
202
|
* Whether a failure is a local server still loading the model, rather than one failing to serve.
|
|
197
203
|
*
|
package/dist/retry.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import OpenAI from "openai";
|
|
2
|
+
import { CHARS_PER_TOKEN } from "./tokens.js";
|
|
2
3
|
/**
|
|
3
4
|
* Everything about a request failing that is not about what the request said.
|
|
4
5
|
*
|
|
@@ -62,13 +63,6 @@ const REFUSAL_PART = 32;
|
|
|
62
63
|
const REASONING_KEY = 23;
|
|
63
64
|
/** The same for `"reasoning":"",`, OpenRouter's spelling of it. */
|
|
64
65
|
const REASONING_ALT_KEY = 15;
|
|
65
|
-
/**
|
|
66
|
-
* The divisor behind `estimateTokens`, applied here to a character count rather than a string.
|
|
67
|
-
*
|
|
68
|
-
* The fallback, not the rule: once a turn has come back with a reported prompt count, `runTurn`
|
|
69
|
-
* divides by what that endpoint's model was measured at instead. See `charsPerTokenFor`.
|
|
70
|
-
*/
|
|
71
|
-
export const CHARS_PER_TOKEN = 4;
|
|
72
66
|
/** The divisor an option asked for, or the fallback when it asked for nothing usable. */
|
|
73
67
|
const divisor = (charsPerToken) => charsPerToken !== undefined && charsPerToken > 0 ? charsPerToken : CHARS_PER_TOKEN;
|
|
74
68
|
/**
|
|
@@ -349,6 +343,19 @@ export function isTransient(error) {
|
|
|
349
343
|
const { status } = error;
|
|
350
344
|
return status === 408 || status === 409 || status === 429 || (status ?? 0) >= 500;
|
|
351
345
|
}
|
|
346
|
+
/**
|
|
347
|
+
* Whether a failure is the endpoint refusing the request as written, rather than losing it.
|
|
348
|
+
*
|
|
349
|
+
* 400 and 422 are what a server says when it read the body and disliked it, which makes them the
|
|
350
|
+
* only statuses worth answering by sending something different. Any 4xx is too wide: 401, 404 and
|
|
351
|
+
* 429 are 4xx and none of them is about the fields, and a 429 is one `isTransient` accepts — a
|
|
352
|
+
* caller that re-sent on it at once doubled the rate against a server that had just asked for less.
|
|
353
|
+
* Catching everything is wider still, since an abort or a connection that never landed then
|
|
354
|
+
* latches off whatever the re-send left out.
|
|
355
|
+
*
|
|
356
|
+
* @param error The rejection, as caught. What is not an SDK error refuses nothing.
|
|
357
|
+
*/
|
|
358
|
+
export const refusesRequest = (error) => error instanceof OpenAI.APIError && (error.status === 400 || error.status === 422);
|
|
352
359
|
/**
|
|
353
360
|
* Whether a failure is a local server still loading the model, rather than one failing to serve.
|
|
354
361
|
*
|
package/dist/run-turn.d.ts
CHANGED
|
@@ -77,6 +77,13 @@ export interface RunTurnOptions extends Omit<StreamTurnOptions, "produced"> {
|
|
|
77
77
|
*/
|
|
78
78
|
loadingTimeoutMs?: number;
|
|
79
79
|
}
|
|
80
|
+
/**
|
|
81
|
+
* Builds a turn's request body from what the endpoint and the model have refused so far.
|
|
82
|
+
*
|
|
83
|
+
* A function rather than a body because a downgrade changes what is sent, so it is called again
|
|
84
|
+
* per attempt. Its second argument is absent where no model was named.
|
|
85
|
+
*/
|
|
86
|
+
export type RequestBuilder = (supports: Capabilities, model: ModelCapabilities | undefined) => OpenAI.ChatCompletionCreateParamsStreaming;
|
|
80
87
|
/**
|
|
81
88
|
* `request` is a callback rather than a body because the body has to be rebuilt from whatever
|
|
82
89
|
* the last attempt latched off: the tools it sends depend on `strictSchemas`, and `relaxTools`
|
|
@@ -90,4 +97,4 @@ export interface RunTurnOptions extends Omit<StreamTurnOptions, "produced"> {
|
|
|
90
97
|
* @param options Retry budget, context limit, the model to negotiate for, notices, and the
|
|
91
98
|
* stream's own callbacks.
|
|
92
99
|
*/
|
|
93
|
-
export declare function runTurn(client: OpenAI, supports: Capabilities, request:
|
|
100
|
+
export declare function runTurn(client: OpenAI, supports: Capabilities, request: RequestBuilder, { maxRetries, onNotice, contextLimit, model, droppable, loadingTimeoutMs, ...stream }?: RunTurnOptions): Promise<Turn>;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { preselect, runAgentLoop } from "./agent-loop.ts";
|
|
2
|
+
import { calibrate, charsPerTokenFor, resetCalibration } from "./calibration.ts";
|
|
3
|
+
import { capabilitiesFor, expireCapabilities, resetCapabilities } from "./capabilities.ts";
|
|
4
|
+
import { type ClientPoolOptions, configureClients, contextLimitFor, getClient, listModels, resetClients, servedWindow } from "./client.ts";
|
|
5
|
+
import { compactTranscript, runCompaction, summariser } from "./compaction.ts";
|
|
6
|
+
import { continueTurn } from "./continuation.ts";
|
|
7
|
+
import { configureEvents, type EventBusOptions, emit, endRun, history, resetEvents, watch } from "./events.ts";
|
|
8
|
+
import { assembleContext, configureHooks, consult, gather, type HookOptions, notify, resetHooks, withContext } from "./hooks.ts";
|
|
9
|
+
import { resetAll } from "./reset.ts";
|
|
10
|
+
import { runTurn } from "./run-turn.ts";
|
|
11
|
+
import { ask, askJson, resetHints, tryAsk } from "./side-task.ts";
|
|
12
|
+
import { exportCapabilities, importCapabilities } from "./snapshot.ts";
|
|
13
|
+
/**
|
|
14
|
+
* Every exported function that reads or writes what a runtime remembers, itself or through what
|
|
15
|
+
* it calls. The rest of the package is pure, and is the same function whichever runtime it is
|
|
16
|
+
* called beside.
|
|
17
|
+
*/
|
|
18
|
+
declare const STATEFUL: {
|
|
19
|
+
runAgentLoop: typeof runAgentLoop;
|
|
20
|
+
preselect: typeof preselect;
|
|
21
|
+
runTurn: typeof runTurn;
|
|
22
|
+
continueTurn: typeof continueTurn;
|
|
23
|
+
ask: typeof ask;
|
|
24
|
+
askJson: typeof askJson;
|
|
25
|
+
tryAsk: typeof tryAsk;
|
|
26
|
+
summariser: typeof summariser;
|
|
27
|
+
runCompaction: typeof runCompaction;
|
|
28
|
+
compactTranscript: typeof compactTranscript;
|
|
29
|
+
getClient: typeof getClient;
|
|
30
|
+
listModels: typeof listModels;
|
|
31
|
+
contextLimitFor: typeof contextLimitFor;
|
|
32
|
+
servedWindow: typeof servedWindow;
|
|
33
|
+
configureClients: typeof configureClients;
|
|
34
|
+
capabilitiesFor: typeof capabilitiesFor;
|
|
35
|
+
expireCapabilities: typeof expireCapabilities;
|
|
36
|
+
exportCapabilities: typeof exportCapabilities;
|
|
37
|
+
importCapabilities: typeof importCapabilities;
|
|
38
|
+
calibrate: typeof calibrate;
|
|
39
|
+
charsPerTokenFor: typeof charsPerTokenFor;
|
|
40
|
+
emit: typeof emit;
|
|
41
|
+
endRun: typeof endRun;
|
|
42
|
+
history: typeof history;
|
|
43
|
+
watch: typeof watch;
|
|
44
|
+
configureEvents: typeof configureEvents;
|
|
45
|
+
configureHooks: typeof configureHooks;
|
|
46
|
+
gather: typeof gather;
|
|
47
|
+
notify: typeof notify;
|
|
48
|
+
consult: typeof consult;
|
|
49
|
+
assembleContext: typeof assembleContext;
|
|
50
|
+
withContext: typeof withContext;
|
|
51
|
+
resetClients: typeof resetClients;
|
|
52
|
+
resetCapabilities: typeof resetCapabilities;
|
|
53
|
+
resetCalibration: typeof resetCalibration;
|
|
54
|
+
resetHints: typeof resetHints;
|
|
55
|
+
resetEvents: typeof resetEvents;
|
|
56
|
+
resetHooks: typeof resetHooks;
|
|
57
|
+
resetAll: typeof resetAll;
|
|
58
|
+
};
|
|
59
|
+
/** What a runtime starts out configured with. Every field optional; see `createRuntime`. */
|
|
60
|
+
export interface RuntimeOptions {
|
|
61
|
+
/** The client pool's bounds, as `configureClients` takes them. */
|
|
62
|
+
clients?: ClientPoolOptions;
|
|
63
|
+
/** The event bus's bounds, as `configureEvents` takes them. */
|
|
64
|
+
events?: EventBusOptions;
|
|
65
|
+
/** The hooks' budget and preface, as `configureHooks` takes them. */
|
|
66
|
+
hooks?: HookOptions;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* One set of the package's caches, with the functions that use them as methods.
|
|
70
|
+
*
|
|
71
|
+
* Each method is the top-level function of the same name, with the same signature and doc
|
|
72
|
+
* comment, run against this runtime's state instead of the process's: its pooled clients and
|
|
73
|
+
* model listings, latched capabilities, measured characters per token, no-thinking hints, event
|
|
74
|
+
* bus, and hook settings. `resetAll` and the six narrower resets clear this runtime alone.
|
|
75
|
+
*/
|
|
76
|
+
export type Runtime = typeof STATEFUL & {
|
|
77
|
+
/**
|
|
78
|
+
* Runs `fn` with this runtime current, so every top-level function it calls — directly, after
|
|
79
|
+
* an `await`, or from a timer it sets — uses this runtime's state. What the methods do, for
|
|
80
|
+
* code that calls the top-level functions itself.
|
|
81
|
+
*
|
|
82
|
+
* It does not reach a callback that outlives `fn` and is called from elsewhere — a generator
|
|
83
|
+
* resumed later, a function handed back and called by someone else. Call a method there.
|
|
84
|
+
*
|
|
85
|
+
* @param fn What to run. Its return value, or what it throws, is handed straight back.
|
|
86
|
+
*/
|
|
87
|
+
run<T>(fn: () => T): T;
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* A runtime of its own: separate clients, listings, capability latches, hints, event bus and hook
|
|
91
|
+
* settings from the process's and from every other runtime's.
|
|
92
|
+
*
|
|
93
|
+
* For a host that must not share them — one that mints a key per tenant, where a shared pool has
|
|
94
|
+
* tenants evicting each other's clients and a shared bus has one tenant's run id readable by
|
|
95
|
+
* another — and for tests, where a runtime per case needs no `resetAll` between them and can run
|
|
96
|
+
* in parallel. A host with one deployment's worth of endpoints does not need one: the top-level
|
|
97
|
+
* functions are the default runtime's methods.
|
|
98
|
+
*
|
|
99
|
+
* The runtime is carried by async context rather than by argument, so `runAgentLoop` called as a
|
|
100
|
+
* method uses this runtime all the way down, and so does a top-level function the host calls from
|
|
101
|
+
* inside it — from `dispatch`, a hook, `onEvent`. Outside a method or `run`, a top-level function
|
|
102
|
+
* is the default runtime's again; a request handler that emits to a run a runtime started calls
|
|
103
|
+
* `runtime.emit`.
|
|
104
|
+
*
|
|
105
|
+
* Nothing needs closing. A runtime is garbage once nothing holds it; its one timer, the event
|
|
106
|
+
* bus's sweep, is unreferenced and stops rescheduling when its last run has been swept.
|
|
107
|
+
*
|
|
108
|
+
* @param options What to configure it with before first use. A part left out keeps the
|
|
109
|
+
* defaults — not the default runtime's settings, which a new runtime does not inherit.
|
|
110
|
+
*/
|
|
111
|
+
export declare function createRuntime(options?: RuntimeOptions): Runtime;
|
|
112
|
+
/**
|
|
113
|
+
* The runtime the top-level functions use when no other is running.
|
|
114
|
+
*
|
|
115
|
+
* `defaultRuntime.getClient(endpoint)` and `getClient(endpoint)` are the same call outside any
|
|
116
|
+
* runtime. Inside one they differ: the top-level function follows the runtime that is running,
|
|
117
|
+
* and this always reaches the process-wide state.
|
|
118
|
+
*/
|
|
119
|
+
export declare const defaultRuntime: Runtime;
|
|
120
|
+
export {};
|