@superdurable/dex-agents 0.1.0 → 0.3.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 +1608 -64
- package/dist/src/agent-built-in-tools.d.ts +81 -0
- package/dist/src/agent-built-in-tools.js +246 -0
- package/dist/src/agent-built-in-tools.js.map +1 -0
- package/dist/src/agent-context-compaction.d.ts +254 -0
- package/dist/src/agent-context-compaction.js +287 -0
- package/dist/src/agent-context-compaction.js.map +1 -0
- package/dist/src/agent-framework-runner.d.ts +456 -16
- package/dist/src/agent-framework-runner.js +4 -3
- package/dist/src/agent-framework-runner.js.map +1 -1
- package/dist/src/agent-plans.d.ts +48 -0
- package/dist/src/agent-plans.js +86 -0
- package/dist/src/agent-plans.js.map +1 -0
- package/dist/src/agent-session-cancellation-steps.d.ts +28 -0
- package/dist/src/agent-session-cancellation-steps.js +154 -0
- package/dist/src/agent-session-cancellation-steps.js.map +1 -0
- package/dist/src/agent-session-codecs.d.ts +65 -4
- package/dist/src/agent-session-codecs.js +343 -8
- package/dist/src/agent-session-codecs.js.map +1 -1
- package/dist/src/agent-session-failure.d.ts +58 -0
- package/dist/src/agent-session-failure.js +126 -0
- package/dist/src/agent-session-failure.js.map +1 -0
- package/dist/src/agent-session-flow-graph-cli.d.ts +2 -0
- package/dist/src/agent-session-flow-graph-cli.js +47 -0
- package/dist/src/agent-session-flow-graph-cli.js.map +1 -0
- package/dist/src/agent-session-flow-graph.d.ts +38 -0
- package/dist/src/agent-session-flow-graph.js +54 -0
- package/dist/src/agent-session-flow-graph.js.map +1 -0
- package/dist/src/agent-session-flow-graph.json +2187 -0
- package/dist/src/agent-session-flow.d.ts +317 -28
- package/dist/src/agent-session-flow.js +373 -21
- package/dist/src/agent-session-flow.js.map +1 -1
- package/dist/src/agent-session-persistence.d.ts +81 -4
- package/dist/src/agent-session-persistence.js +33 -2
- package/dist/src/agent-session-persistence.js.map +1 -1
- package/dist/src/agent-session-settings.d.ts +58 -0
- package/dist/src/agent-session-settings.js +81 -0
- package/dist/src/agent-session-settings.js.map +1 -0
- package/dist/src/agent-session-spend-limit.d.ts +156 -0
- package/dist/src/agent-session-spend-limit.js +42 -0
- package/dist/src/agent-session-spend-limit.js.map +1 -0
- package/dist/src/agent-session-start.d.ts +49 -0
- package/dist/src/agent-session-start.js +67 -0
- package/dist/src/agent-session-start.js.map +1 -0
- package/dist/src/agent-session-step-options.d.ts +78 -0
- package/dist/src/agent-session-step-options.js +71 -0
- package/dist/src/agent-session-step-options.js.map +1 -0
- package/dist/src/agent-session-step-types.d.ts +20 -0
- package/dist/src/agent-session-step-types.js +28 -0
- package/dist/src/agent-session-step-types.js.map +1 -0
- package/dist/src/agent-session-steps.d.ts +103 -8
- package/dist/src/agent-session-steps.js +997 -197
- package/dist/src/agent-session-steps.js.map +1 -1
- package/dist/src/agent-session-tracer.d.ts +61 -0
- package/dist/src/agent-session-tracer.js +608 -0
- package/dist/src/agent-session-tracer.js.map +1 -0
- package/dist/src/agent-session-transitions.d.ts +200 -26
- package/dist/src/agent-session-transitions.js +732 -32
- package/dist/src/agent-session-transitions.js.map +1 -1
- package/dist/src/agent-session-values.d.ts +696 -30
- package/dist/src/agent-session-values.js +39 -4
- package/dist/src/agent-session-values.js.map +1 -1
- package/dist/src/agent-session-wait-steps.d.ts +53 -0
- package/dist/src/agent-session-wait-steps.js +389 -0
- package/dist/src/agent-session-wait-steps.js.map +1 -0
- package/dist/src/agent-tracing-options.d.ts +107 -0
- package/dist/src/agent-tracing-options.js +20 -0
- package/dist/src/agent-tracing-options.js.map +1 -0
- package/dist/src/ai-sdk/ai-sdk-model-client.d.ts +40 -0
- package/dist/src/ai-sdk/ai-sdk-model-client.js +235 -0
- package/dist/src/ai-sdk/ai-sdk-model-client.js.map +1 -0
- package/dist/src/ai-sdk/index.d.ts +1 -0
- package/dist/src/ai-sdk/index.js +9 -0
- package/dist/src/ai-sdk/index.js.map +1 -0
- package/dist/src/index.d.ts +14 -3
- package/dist/src/index.js +12 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/mcp/index.d.ts +3 -0
- package/dist/src/mcp/index.js +10 -0
- package/dist/src/mcp/index.js.map +1 -0
- package/dist/src/mcp/mcp-resource-and-prompt-tools.d.ts +29 -0
- package/dist/src/mcp/mcp-resource-and-prompt-tools.js +185 -0
- package/dist/src/mcp/mcp-resource-and-prompt-tools.js.map +1 -0
- package/dist/src/mcp/mcp-server-config.d.ts +86 -0
- package/dist/src/mcp/mcp-server-config.js +38 -0
- package/dist/src/mcp/mcp-server-config.js.map +1 -0
- package/dist/src/mcp/mcp-tool-source.d.ts +470 -0
- package/dist/src/mcp/mcp-tool-source.js +1204 -0
- package/dist/src/mcp/mcp-tool-source.js.map +1 -0
- package/dist/src/stepwise/agent-definition.d.ts +166 -0
- package/dist/src/stepwise/agent-definition.js +9 -0
- package/dist/src/stepwise/agent-definition.js.map +1 -0
- package/dist/src/stepwise/function-tool.d.ts +130 -0
- package/dist/src/stepwise/function-tool.js +75 -0
- package/dist/src/stepwise/function-tool.js.map +1 -0
- package/dist/src/stepwise/index.d.ts +8 -0
- package/dist/src/stepwise/index.js +13 -0
- package/dist/src/stepwise/index.js.map +1 -0
- package/dist/src/stepwise/json-schema-validation.d.ts +9 -0
- package/dist/src/stepwise/json-schema-validation.js +141 -0
- package/dist/src/stepwise/json-schema-validation.js.map +1 -0
- package/dist/src/stepwise/model-client.d.ts +194 -0
- package/dist/src/stepwise/model-client.js +9 -0
- package/dist/src/stepwise/model-client.js.map +1 -0
- package/dist/src/stepwise/scripted-model-client.d.ts +99 -0
- package/dist/src/stepwise/scripted-model-client.js +156 -0
- package/dist/src/stepwise/scripted-model-client.js.map +1 -0
- package/dist/src/stepwise/stepwise-agent-runner.d.ts +171 -0
- package/dist/src/stepwise/stepwise-agent-runner.js +777 -0
- package/dist/src/stepwise/stepwise-agent-runner.js.map +1 -0
- package/dist/src/stepwise/stepwise-agent-session-flow.d.ts +35 -0
- package/dist/src/stepwise/stepwise-agent-session-flow.js +57 -0
- package/dist/src/stepwise/stepwise-agent-session-flow.js.map +1 -0
- package/dist/src/stepwise/stepwise-input-items.d.ts +12 -0
- package/dist/src/stepwise/stepwise-input-items.js +172 -0
- package/dist/src/stepwise/stepwise-input-items.js.map +1 -0
- package/dist/src/stepwise/tool-source.d.ts +103 -0
- package/dist/src/stepwise/tool-source.js +9 -0
- package/dist/src/stepwise/tool-source.js.map +1 -0
- package/dist/src/testing/agent-session-test-client.d.ts +65 -8
- package/dist/src/testing/agent-session-test-client.js +92 -22
- package/dist/src/testing/agent-session-test-client.js.map +1 -1
- package/dist/src/testing/context-window.d.ts +194 -0
- package/dist/src/testing/context-window.js +360 -0
- package/dist/src/testing/context-window.js.map +1 -0
- package/dist/src/testing/index.d.ts +2 -0
- package/dist/src/testing/index.js +2 -0
- package/dist/src/testing/index.js.map +1 -1
- package/dist/src/testing/private-dex-engine.d.ts +9 -0
- package/dist/src/testing/private-dex-engine.js +9 -1
- package/dist/src/testing/private-dex-engine.js.map +1 -1
- package/dist/src/testing/scripted-model-client.d.ts +1 -0
- package/dist/src/testing/scripted-model-client.js +10 -0
- package/dist/src/testing/scripted-model-client.js.map +1 -0
- package/dist/src/ui-stream/agent-event-json.d.ts +30 -0
- package/dist/src/ui-stream/agent-event-json.js +64 -0
- package/dist/src/ui-stream/agent-event-json.js.map +1 -0
- package/dist/src/ui-stream/agent-event-sse.d.ts +155 -0
- package/dist/src/ui-stream/agent-event-sse.js +167 -0
- package/dist/src/ui-stream/agent-event-sse.js.map +1 -0
- package/dist/src/ui-stream/agent-event-stream-positions.d.ts +42 -0
- package/dist/src/ui-stream/agent-event-stream-positions.js +153 -0
- package/dist/src/ui-stream/agent-event-stream-positions.js.map +1 -0
- package/dist/src/ui-stream/agent-session-record-reader.d.ts +74 -0
- package/dist/src/ui-stream/agent-session-record-reader.js +246 -0
- package/dist/src/ui-stream/agent-session-record-reader.js.map +1 -0
- package/dist/src/ui-stream/agent-ui-message-stream-responses.d.ts +219 -0
- package/dist/src/ui-stream/agent-ui-message-stream-responses.js +441 -0
- package/dist/src/ui-stream/agent-ui-message-stream-responses.js.map +1 -0
- package/dist/src/ui-stream/agent-ui-message-stream.d.ts +138 -0
- package/dist/src/ui-stream/agent-ui-message-stream.js +695 -0
- package/dist/src/ui-stream/agent-ui-message-stream.js.map +1 -0
- package/dist/src/ui-stream/agent-ui-message-types.d.ts +238 -0
- package/dist/src/ui-stream/agent-ui-message-types.js +57 -0
- package/dist/src/ui-stream/agent-ui-message-types.js.map +1 -0
- package/dist/src/ui-stream/agent-ui-messages.d.ts +68 -0
- package/dist/src/ui-stream/agent-ui-messages.js +222 -0
- package/dist/src/ui-stream/agent-ui-messages.js.map +1 -0
- package/dist/src/ui-stream/agent-ui-stream-cursor.d.ts +64 -0
- package/dist/src/ui-stream/agent-ui-stream-cursor.js +186 -0
- package/dist/src/ui-stream/agent-ui-stream-cursor.js.map +1 -0
- package/dist/src/ui-stream/index.d.ts +14 -0
- package/dist/src/ui-stream/index.js +22 -0
- package/dist/src/ui-stream/index.js.map +1 -0
- package/dist/src/ui-stream/server-sent-event-responses.d.ts +6 -0
- package/dist/src/ui-stream/server-sent-event-responses.js +34 -0
- package/dist/src/ui-stream/server-sent-event-responses.js.map +1 -0
- package/package.json +44 -5
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { AgentPlanTask } from "./agent-session-values.js";
|
|
2
|
+
/**
|
|
3
|
+
* Name of a built-in tool that the session itself handles.
|
|
4
|
+
*
|
|
5
|
+
* - `ask_user`: asks the user a question. The session raises a `question` interrupt, waits in
|
|
6
|
+
* `AwaitQuestionAnswer`, and the answer becomes the tool result, so the model continues the same turn.
|
|
7
|
+
* - `durable_wait`: suspends the turn on a Dex Timer in `AwaitTimer`. No model call, coroutine, or
|
|
8
|
+
* Worker holds the wait.
|
|
9
|
+
* - `write_todos`: replaces the session's {@link AgentPlan} with a new revision. `RunModel` records the
|
|
10
|
+
* call in its own commit; no tool Step runs.
|
|
11
|
+
*
|
|
12
|
+
* Enable built-in tools with {@link AgentSessionOptions.builtInTools}. A runner offers the enabled ones
|
|
13
|
+
* to its model, from {@link builtInToolDefinitions}, like any other tool, and returns their calls in
|
|
14
|
+
* {@link ToolCallsRequested} like any other call. The session routes a call whose name is an enabled
|
|
15
|
+
* built-in tool to that tool's handling instead of a tool Step. An enabled built-in name is reserved.
|
|
16
|
+
* A disabled built-in name is an ordinary tool name.
|
|
17
|
+
*/
|
|
18
|
+
export type BuiltInTool = "ask_user" | "durable_wait" | "write_todos";
|
|
19
|
+
/** Model-visible definition of one built-in tool, from {@link builtInToolDefinitions}. */
|
|
20
|
+
export interface BuiltInToolDefinition {
|
|
21
|
+
/** Tool name as the model calls it. */
|
|
22
|
+
readonly name: BuiltInTool;
|
|
23
|
+
/** Tool description for the model; the same text as the Python core. */
|
|
24
|
+
readonly description: string;
|
|
25
|
+
/** JSON Schema of the tool arguments; the same schema as the Python core. */
|
|
26
|
+
readonly parameters: Readonly<Record<string, unknown>>;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Definitions of every built-in tool, with the same names, descriptions, and JSON Schemas as the Python
|
|
30
|
+
* core's `BUILT_IN_TOOL_DEFINITIONS`.
|
|
31
|
+
*
|
|
32
|
+
* A runner offers the definitions of {@link AgentTurnContext.enabledBuiltInTools} to its model. Adapters
|
|
33
|
+
* register them as tools without an implementation, which every adapter already does for deferred
|
|
34
|
+
* application tools.
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```ts
|
|
38
|
+
* const offeredBuiltInTools = builtInToolDefinitions.filter((definition) =>
|
|
39
|
+
* turn.enabledBuiltInTools?.has(definition.name) === true);
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export declare const builtInToolDefinitions: readonly BuiltInToolDefinition[];
|
|
43
|
+
/** Every built-in tool name, in definition order. */
|
|
44
|
+
export declare const builtInToolNames: readonly BuiltInTool[];
|
|
45
|
+
/** Parsed arguments of an `ask_user` call. */
|
|
46
|
+
export interface AskUserArguments {
|
|
47
|
+
readonly question: string;
|
|
48
|
+
readonly choices: readonly string[];
|
|
49
|
+
readonly allowsFreeText: boolean;
|
|
50
|
+
}
|
|
51
|
+
/** Parsed arguments of a `durable_wait` call. */
|
|
52
|
+
export interface DurableWaitArguments {
|
|
53
|
+
readonly durationSeconds: number;
|
|
54
|
+
readonly reason: string;
|
|
55
|
+
}
|
|
56
|
+
/** Error of every `write_todos` call of a model response that has more than one; the same text as the Python core. */
|
|
57
|
+
export declare const writeTodosCalledMoreThanOnce = "write_todos was called more than once in one response";
|
|
58
|
+
/** Result of validating a built-in call's arguments: the parsed arguments or the model-visible error. */
|
|
59
|
+
export type BuiltInArgumentsValidation<Arguments> = {
|
|
60
|
+
readonly isValid: true;
|
|
61
|
+
readonly arguments: Arguments;
|
|
62
|
+
} | {
|
|
63
|
+
readonly isValid: false;
|
|
64
|
+
readonly error: string;
|
|
65
|
+
};
|
|
66
|
+
/** Reports whether text is empty or only whitespace, as Python's `not text.strip()` does. */
|
|
67
|
+
export declare function isBlankText(text: string): boolean;
|
|
68
|
+
export declare function isBuiltInTool(name: string): name is BuiltInTool;
|
|
69
|
+
export declare function validateAskUserArguments(argumentsText: string): BuiltInArgumentsValidation<AskUserArguments>;
|
|
70
|
+
/** A JSON number without a fraction, such as `2.0`, counts as an integer, as JSON Schema defines it. */
|
|
71
|
+
export declare function validateDurableWaitArguments(argumentsText: string, maxDurableWaitSeconds: number): BuiltInArgumentsValidation<DurableWaitArguments>;
|
|
72
|
+
/** Validates `write_todos` arguments; the error is `invalid todos: <reason>`, as in the Python core. */
|
|
73
|
+
export declare function validateWriteTodosArguments(argumentsText: string): BuiltInArgumentsValidation<readonly AgentPlanTask[]>;
|
|
74
|
+
/** Tool output after `write_todos` wrote tasks: `{"status":"updated","revision":<n>,"task_count":<k>}`. */
|
|
75
|
+
export declare function buildPlanUpdatedOutput(revision: number, taskCount: number): string;
|
|
76
|
+
/** Tool output after `write_todos` cleared the plan: `{"status":"cleared","revision":<n>}`. */
|
|
77
|
+
export declare function buildPlanClearedOutput(revision: number): string;
|
|
78
|
+
/** Tool output after the user answered `ask_user`: `{"status":"answered","answer":"<answer>"}`. */
|
|
79
|
+
export declare function buildQuestionAnsweredOutput(answer: string): string;
|
|
80
|
+
/** Tool output after a `durable_wait` Timer ended: `{"status":"completed","waited_seconds":<n>,"reason":"<reason>"}`. */
|
|
81
|
+
export declare function buildDurableWaitCompletedOutput(waitedSeconds: number, reason: string): string;
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
// Copyright (c) 2026 Super Durable, Inc.
|
|
2
|
+
//
|
|
3
|
+
// Licensed under the Sustainable Use License 1.0.
|
|
4
|
+
// You may not use this file except in compliance with the License.
|
|
5
|
+
// See the LICENSE file in the repository root.
|
|
6
|
+
//
|
|
7
|
+
// SPDX-License-Identifier: LicenseRef-Sustainable-Use-1.0
|
|
8
|
+
/**
|
|
9
|
+
* Definitions of every built-in tool, with the same names, descriptions, and JSON Schemas as the Python
|
|
10
|
+
* core's `BUILT_IN_TOOL_DEFINITIONS`.
|
|
11
|
+
*
|
|
12
|
+
* A runner offers the definitions of {@link AgentTurnContext.enabledBuiltInTools} to its model. Adapters
|
|
13
|
+
* register them as tools without an implementation, which every adapter already does for deferred
|
|
14
|
+
* application tools.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* const offeredBuiltInTools = builtInToolDefinitions.filter((definition) =>
|
|
19
|
+
* turn.enabledBuiltInTools?.has(definition.name) === true);
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export const builtInToolDefinitions = Object.freeze([
|
|
23
|
+
Object.freeze({
|
|
24
|
+
name: "ask_user",
|
|
25
|
+
description: "Ask the user one question and wait for the answer, which becomes this tool's result. Use it only " +
|
|
26
|
+
"when you cannot continue without information or a decision from the user. The wait can last hours " +
|
|
27
|
+
"or days.",
|
|
28
|
+
parameters: Object.freeze({
|
|
29
|
+
type: "object",
|
|
30
|
+
properties: {
|
|
31
|
+
question: { type: "string", description: "One concise question for the user." },
|
|
32
|
+
choices: {
|
|
33
|
+
type: "array",
|
|
34
|
+
items: { type: "string" },
|
|
35
|
+
description: "Answers the user can pick. Omit for a free-text answer.",
|
|
36
|
+
},
|
|
37
|
+
allows_free_text: {
|
|
38
|
+
type: "boolean",
|
|
39
|
+
description: "Whether the user may answer with text that is not one of the choices. Defaults to true.",
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
required: ["question"],
|
|
43
|
+
additionalProperties: false,
|
|
44
|
+
}),
|
|
45
|
+
}),
|
|
46
|
+
Object.freeze({
|
|
47
|
+
name: "durable_wait",
|
|
48
|
+
description: "Pause this turn for a number of whole seconds, then continue. Nothing runs during the wait. Use it " +
|
|
49
|
+
"to wait for something outside the conversation, such as a job that needs time to finish.",
|
|
50
|
+
parameters: Object.freeze({
|
|
51
|
+
type: "object",
|
|
52
|
+
properties: {
|
|
53
|
+
duration_seconds: { type: "integer", minimum: 1, description: "Whole seconds to wait." },
|
|
54
|
+
reason: { type: "string", description: "Why the turn waits, shown to the user." },
|
|
55
|
+
},
|
|
56
|
+
required: ["duration_seconds", "reason"],
|
|
57
|
+
additionalProperties: false,
|
|
58
|
+
}),
|
|
59
|
+
}),
|
|
60
|
+
Object.freeze({
|
|
61
|
+
name: "write_todos",
|
|
62
|
+
description: "Replace the plan with a complete, ordered list of tasks. Send every task each time, with its " +
|
|
63
|
+
"status. An empty list clears the plan. Keep the statuses current: mark a task in_progress when you " +
|
|
64
|
+
"start it and completed when it is done.",
|
|
65
|
+
parameters: Object.freeze({
|
|
66
|
+
type: "object",
|
|
67
|
+
properties: {
|
|
68
|
+
todos: {
|
|
69
|
+
type: "array",
|
|
70
|
+
description: "Every task of the plan, in order.",
|
|
71
|
+
items: {
|
|
72
|
+
type: "object",
|
|
73
|
+
properties: {
|
|
74
|
+
content: { type: "string", description: "What the task does, in one short sentence." },
|
|
75
|
+
status: {
|
|
76
|
+
type: "string",
|
|
77
|
+
enum: ["pending", "in_progress", "completed"],
|
|
78
|
+
description: "Where the task stands.",
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
required: ["content", "status"],
|
|
82
|
+
additionalProperties: false,
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
required: ["todos"],
|
|
87
|
+
additionalProperties: false,
|
|
88
|
+
}),
|
|
89
|
+
}),
|
|
90
|
+
]);
|
|
91
|
+
/** Every built-in tool name, in definition order. */
|
|
92
|
+
export const builtInToolNames = Object.freeze(builtInToolDefinitions.map((definition) => definition.name));
|
|
93
|
+
/** Error of every `write_todos` call of a model response that has more than one; the same text as the Python core. */
|
|
94
|
+
export const writeTodosCalledMoreThanOnce = "write_todos was called more than once in one response";
|
|
95
|
+
const planTaskStatuses = ["pending", "in_progress", "completed"];
|
|
96
|
+
// The characters Python's str.isspace() accepts, so blank checks match the Python core exactly.
|
|
97
|
+
const pythonWhitespaceOnly = /^[\t\n\v\f\r\x1c-\x1f \x85\xa0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000]*$/u;
|
|
98
|
+
/** Reports whether text is empty or only whitespace, as Python's `not text.strip()` does. */
|
|
99
|
+
export function isBlankText(text) {
|
|
100
|
+
return pythonWhitespaceOnly.test(text);
|
|
101
|
+
}
|
|
102
|
+
export function isBuiltInTool(name) {
|
|
103
|
+
return builtInToolNames.includes(name);
|
|
104
|
+
}
|
|
105
|
+
export function validateAskUserArguments(argumentsText) {
|
|
106
|
+
const parsed = parseArgumentsObject("ask_user", argumentsText);
|
|
107
|
+
if (!parsed.isValid) {
|
|
108
|
+
return parsed;
|
|
109
|
+
}
|
|
110
|
+
const record = parsed.arguments;
|
|
111
|
+
const question = record["question"];
|
|
112
|
+
if (typeof question !== "string" || isBlankText(question)) {
|
|
113
|
+
return invalidArguments("ask_user", "question must be a non-empty string");
|
|
114
|
+
}
|
|
115
|
+
const choices = record["choices"] ?? [];
|
|
116
|
+
if (!Array.isArray(choices) || !choices.every((choice) => typeof choice === "string" && !isBlankText(choice))) {
|
|
117
|
+
return invalidArguments("ask_user", "choices must be a list of non-empty strings");
|
|
118
|
+
}
|
|
119
|
+
const allowsFreeText = record["allows_free_text"] ?? true;
|
|
120
|
+
if (typeof allowsFreeText !== "boolean") {
|
|
121
|
+
return invalidArguments("ask_user", "allows_free_text must be a boolean");
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
isValid: true,
|
|
125
|
+
arguments: { question, choices: choices, allowsFreeText: allowsFreeText || choices.length === 0 },
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
/** A JSON number without a fraction, such as `2.0`, counts as an integer, as JSON Schema defines it. */
|
|
129
|
+
export function validateDurableWaitArguments(argumentsText, maxDurableWaitSeconds) {
|
|
130
|
+
const parsed = parseArgumentsObject("durable_wait", argumentsText);
|
|
131
|
+
if (!parsed.isValid) {
|
|
132
|
+
return parsed;
|
|
133
|
+
}
|
|
134
|
+
const record = parsed.arguments;
|
|
135
|
+
const durationSeconds = record["duration_seconds"];
|
|
136
|
+
if (typeof durationSeconds !== "number" || !Number.isInteger(durationSeconds)) {
|
|
137
|
+
return invalidArguments("durable_wait", "duration_seconds must be an integer");
|
|
138
|
+
}
|
|
139
|
+
if (durationSeconds < 1 || durationSeconds > maxDurableWaitSeconds) {
|
|
140
|
+
return invalidArguments("durable_wait", `duration_seconds must be between 1 and ${maxDurableWaitSeconds}`);
|
|
141
|
+
}
|
|
142
|
+
const reason = record["reason"];
|
|
143
|
+
if (typeof reason !== "string") {
|
|
144
|
+
return invalidArguments("durable_wait", "reason must be a string");
|
|
145
|
+
}
|
|
146
|
+
return { isValid: true, arguments: { durationSeconds, reason } };
|
|
147
|
+
}
|
|
148
|
+
/** Validates `write_todos` arguments; the error is `invalid todos: <reason>`, as in the Python core. */
|
|
149
|
+
export function validateWriteTodosArguments(argumentsText) {
|
|
150
|
+
let parsed;
|
|
151
|
+
try {
|
|
152
|
+
parsed = JSON.parse(argumentsText);
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
return invalidTodos("arguments must be a JSON object");
|
|
156
|
+
}
|
|
157
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
158
|
+
return invalidTodos("arguments must be a JSON object");
|
|
159
|
+
}
|
|
160
|
+
const [unknownName] = sortByCodePoint(Object.keys(parsed).filter((name) => name !== "todos"));
|
|
161
|
+
if (unknownName !== undefined) {
|
|
162
|
+
return invalidTodos(`unknown argument ${unknownName}`);
|
|
163
|
+
}
|
|
164
|
+
const todos = parsed["todos"];
|
|
165
|
+
if (!Array.isArray(todos)) {
|
|
166
|
+
return invalidTodos("todos must be a list");
|
|
167
|
+
}
|
|
168
|
+
const tasks = [];
|
|
169
|
+
for (const [index, todo] of todos.entries()) {
|
|
170
|
+
if (typeof todo !== "object" || todo === null || Array.isArray(todo)) {
|
|
171
|
+
return invalidTodos(`todos[${index}] must be an object`);
|
|
172
|
+
}
|
|
173
|
+
const [unknownField] = sortByCodePoint(Object.keys(todo).filter((name) => name !== "content" && name !== "status"));
|
|
174
|
+
if (unknownField !== undefined) {
|
|
175
|
+
return invalidTodos(`todos[${index}] has unknown field ${unknownField}`);
|
|
176
|
+
}
|
|
177
|
+
const { content, status } = todo;
|
|
178
|
+
if (typeof content !== "string" || isBlankText(content)) {
|
|
179
|
+
return invalidTodos(`todos[${index}].content must be a non-empty string`);
|
|
180
|
+
}
|
|
181
|
+
if (typeof status !== "string" || !planTaskStatuses.includes(status)) {
|
|
182
|
+
return invalidTodos(`todos[${index}].status must be pending, in_progress, or completed`);
|
|
183
|
+
}
|
|
184
|
+
tasks.push({ content, status: status });
|
|
185
|
+
}
|
|
186
|
+
return { isValid: true, arguments: tasks };
|
|
187
|
+
}
|
|
188
|
+
/** Tool output after `write_todos` wrote tasks: `{"status":"updated","revision":<n>,"task_count":<k>}`. */
|
|
189
|
+
export function buildPlanUpdatedOutput(revision, taskCount) {
|
|
190
|
+
return JSON.stringify({ status: "updated", revision, task_count: taskCount });
|
|
191
|
+
}
|
|
192
|
+
/** Tool output after `write_todos` cleared the plan: `{"status":"cleared","revision":<n>}`. */
|
|
193
|
+
export function buildPlanClearedOutput(revision) {
|
|
194
|
+
return JSON.stringify({ status: "cleared", revision });
|
|
195
|
+
}
|
|
196
|
+
/** Tool output after the user answered `ask_user`: `{"status":"answered","answer":"<answer>"}`. */
|
|
197
|
+
export function buildQuestionAnsweredOutput(answer) {
|
|
198
|
+
return JSON.stringify({ status: "answered", answer });
|
|
199
|
+
}
|
|
200
|
+
/** Tool output after a `durable_wait` Timer ended: `{"status":"completed","waited_seconds":<n>,"reason":"<reason>"}`. */
|
|
201
|
+
export function buildDurableWaitCompletedOutput(waitedSeconds, reason) {
|
|
202
|
+
return JSON.stringify({ status: "completed", waited_seconds: waitedSeconds, reason });
|
|
203
|
+
}
|
|
204
|
+
function invalidTodos(reason) {
|
|
205
|
+
return { isValid: false, error: `invalid todos: ${reason}` };
|
|
206
|
+
}
|
|
207
|
+
/** Sorted by code point, as Python's `sorted` orders strings, so both cores report the same name. */
|
|
208
|
+
function sortByCodePoint(names) {
|
|
209
|
+
return [...names].sort(compareByCodePoint);
|
|
210
|
+
}
|
|
211
|
+
/** JavaScript's `<` compares UTF-16 code units, which orders characters above U+FFFF before U+E000. */
|
|
212
|
+
function compareByCodePoint(left, right) {
|
|
213
|
+
const leftCodePoints = Array.from(left, (character) => character.codePointAt(0) ?? 0);
|
|
214
|
+
const rightCodePoints = Array.from(right, (character) => character.codePointAt(0) ?? 0);
|
|
215
|
+
const sharedLength = Math.min(leftCodePoints.length, rightCodePoints.length);
|
|
216
|
+
for (let index = 0; index < sharedLength; index += 1) {
|
|
217
|
+
const difference = (leftCodePoints[index] ?? 0) - (rightCodePoints[index] ?? 0);
|
|
218
|
+
if (difference !== 0) {
|
|
219
|
+
return difference;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return leftCodePoints.length - rightCodePoints.length;
|
|
223
|
+
}
|
|
224
|
+
function parseArgumentsObject(builtInTool, argumentsText) {
|
|
225
|
+
let parsed;
|
|
226
|
+
try {
|
|
227
|
+
parsed = JSON.parse(argumentsText);
|
|
228
|
+
}
|
|
229
|
+
catch {
|
|
230
|
+
return invalidArguments(builtInTool, "arguments must be a JSON object");
|
|
231
|
+
}
|
|
232
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
233
|
+
return invalidArguments(builtInTool, "arguments must be a JSON object");
|
|
234
|
+
}
|
|
235
|
+
const definition = builtInToolDefinitions.find((candidate) => candidate.name === builtInTool);
|
|
236
|
+
const allowedNames = Object.keys((definition?.parameters["properties"] ?? {}));
|
|
237
|
+
const [unknownName] = sortByCodePoint(Object.keys(parsed).filter((name) => !allowedNames.includes(name)));
|
|
238
|
+
if (unknownName !== undefined) {
|
|
239
|
+
return invalidArguments(builtInTool, `unknown argument ${unknownName}`);
|
|
240
|
+
}
|
|
241
|
+
return { isValid: true, arguments: parsed };
|
|
242
|
+
}
|
|
243
|
+
function invalidArguments(builtInTool, reason) {
|
|
244
|
+
return { isValid: false, error: `invalid ${builtInTool} arguments: ${reason}` };
|
|
245
|
+
}
|
|
246
|
+
//# sourceMappingURL=agent-built-in-tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-built-in-tools.js","sourceRoot":"","sources":["../../src/agent-built-in-tools.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import type { AgentEventWriter, AgentFrameworkRunner, AgentTurnContext, DeferredToolOutcome, ModelCallMetadata } from "./agent-framework-runner.js";
|
|
2
|
+
import type { AgentMessage, AgentUsage, ContextSummary, SessionSettings } from "./agent-session-values.js";
|
|
3
|
+
/**
|
|
4
|
+
* When a session summarizes its earlier messages so the model input stays under the context window, passed
|
|
5
|
+
* as {@link AgentSessionOptions.compaction} or as `AgentDefinition.compaction` for a Stepwise agent.
|
|
6
|
+
*
|
|
7
|
+
* Before each model call, `RunModel` asks the runner for the size of the next model input. When it reaches
|
|
8
|
+
* {@link triggerRatio} of {@link contextWindowTokens}, `RunModel` makes no model call and moves to
|
|
9
|
+
* `CompactContext`, which folds the earlier messages into a {@link ContextSummary}, then back to
|
|
10
|
+
* `RunModel`. The runner must implement {@link AgentFrameworkRunner.estimateInputTokens},
|
|
11
|
+
* {@link AgentFrameworkRunner.selectCompactionCutoff}, and {@link AgentFrameworkRunner.summarizeContext};
|
|
12
|
+
* `createAgentSessionFlow` throws otherwise. Summarized messages stay stored until
|
|
13
|
+
* `AgentSessionOptions.retainedMessageLimit` deletes them; only the model input changes. Enable
|
|
14
|
+
* compaction only after every Worker of the Flow type runs a release with the `CompactContext` Step.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* const flow = createAgentSessionFlow(runner, "SupportAgent", {
|
|
19
|
+
* compaction: { contextWindowTokens: 400_000 },
|
|
20
|
+
* });
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export interface CompactionPolicy {
|
|
24
|
+
/** The model's context window, in the tokens that the runner's `estimateInputTokens` counts. */
|
|
25
|
+
readonly contextWindowTokens: number;
|
|
26
|
+
/** Share of the window at which the session compacts before the next model call. Defaults to 0.85. */
|
|
27
|
+
readonly triggerRatio?: number;
|
|
28
|
+
/** Share of the window that the newest messages may keep unsummarized. Defaults to 0.3. */
|
|
29
|
+
readonly retainedRatio?: number;
|
|
30
|
+
/**
|
|
31
|
+
* Tokens of every model input that the runner's estimate cannot see, such as a system prompt that a
|
|
32
|
+
* framework builds, a dynamic instruction, or tools listed at run time. The session adds them to the
|
|
33
|
+
* estimate. Defaults to 0.
|
|
34
|
+
* @since 0.3.0
|
|
35
|
+
*/
|
|
36
|
+
readonly reservedInputTokens?: number;
|
|
37
|
+
}
|
|
38
|
+
/** A {@link CompactionPolicy} with every default filled in. */
|
|
39
|
+
export interface ResolvedCompactionPolicy {
|
|
40
|
+
/** The model's context window in tokens. */
|
|
41
|
+
readonly contextWindowTokens: number;
|
|
42
|
+
/** Share of the window at which the session compacts. */
|
|
43
|
+
readonly triggerRatio: number;
|
|
44
|
+
/** Share of the window that the newest messages may keep. */
|
|
45
|
+
readonly retainedRatio: number;
|
|
46
|
+
/**
|
|
47
|
+
* Tokens added to every estimate. `undefined` reads as 0.
|
|
48
|
+
* @since 0.3.0
|
|
49
|
+
*/
|
|
50
|
+
readonly reservedInputTokens?: number;
|
|
51
|
+
}
|
|
52
|
+
/** First line of the user text that carries the {@link ContextSummary} into the model input. */
|
|
53
|
+
export declare const contextSummaryNoteHeader = "[Dex] Summary of the earlier conversation:";
|
|
54
|
+
/** Instructions of the model call that writes a {@link ContextSummary}. */
|
|
55
|
+
export declare const contextSummaryInstructions: string;
|
|
56
|
+
/** Last user text of the model call that writes a {@link ContextSummary}. */
|
|
57
|
+
export declare const contextSummaryRequest = "[Dex] Summarize the conversation above. Reply with the summary only.";
|
|
58
|
+
/**
|
|
59
|
+
* First line of the transcript in the prompt of {@link buildContextSummaryPrompt}.
|
|
60
|
+
* @since 0.3.0
|
|
61
|
+
*/
|
|
62
|
+
export declare const contextSummaryTranscriptHeader = "[Dex] The conversation to summarize:";
|
|
63
|
+
/**
|
|
64
|
+
* The most tokens {@link buildContextSummaryPrompt} leaves free for the summary it asks for.
|
|
65
|
+
* @since 0.3.0
|
|
66
|
+
*/
|
|
67
|
+
export declare const contextSummaryOutputTokens = 4096;
|
|
68
|
+
/**
|
|
69
|
+
* Returns the user text that says how many of the oldest messages a summary request left out.
|
|
70
|
+
* @param omittedCount - Messages left out, at least 1.
|
|
71
|
+
* @returns `[Dex] <omittedCount> earlier messages did not fit in this request and are left out.`, or
|
|
72
|
+
* `[Dex] 1 earlier message did not fit in this request and is left out.` for one message.
|
|
73
|
+
*/
|
|
74
|
+
export declare function buildContextSummaryOmissionNote(omittedCount: number): string;
|
|
75
|
+
/**
|
|
76
|
+
* Returns the user text that puts a summary before the newer messages in a model input.
|
|
77
|
+
* @param summary - The session's summary.
|
|
78
|
+
* @returns {@link contextSummaryNoteHeader}, a newline, and the summary text.
|
|
79
|
+
*/
|
|
80
|
+
export declare function buildContextSummaryNote(summary: ContextSummary): string;
|
|
81
|
+
/**
|
|
82
|
+
* Estimates the tokens of a text without a tokenizer, rounding up.
|
|
83
|
+
*
|
|
84
|
+
* Use it where a framework assembles the prompt and offers no token counter. It counts one token per three UTF-8
|
|
85
|
+
* bytes. That is more than a provider counts for English text, and about one token per CJK character, so the
|
|
86
|
+
* estimate errs toward compacting early.
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```ts
|
|
90
|
+
* estimateTextTokens("twelve bytes"); // 4
|
|
91
|
+
* ```
|
|
92
|
+
* @param text - The text to measure.
|
|
93
|
+
* @returns The estimated tokens; 0 for an empty text.
|
|
94
|
+
* @since 0.3.0
|
|
95
|
+
*/
|
|
96
|
+
export declare function estimateTextTokens(text: string): number;
|
|
97
|
+
/**
|
|
98
|
+
* Estimates a model input that a framework builds from the stored items.
|
|
99
|
+
*
|
|
100
|
+
* Call it from `estimateInputTokens` of a framework adapter. It measures each `frameworkItem` as stored, so JSON
|
|
101
|
+
* syntax and opaque provider items count too, plus the summary and any `frameworkRunState`.
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* ```ts
|
|
105
|
+
* public estimateInputTokens(turn: AgentTurnContext): number {
|
|
106
|
+
* return estimateStoredInputTokens(turn, estimateTextTokens(this.instructions));
|
|
107
|
+
* }
|
|
108
|
+
* ```
|
|
109
|
+
* @param turn - The context of the next model call.
|
|
110
|
+
* @param fixedInputTokens - Tokens of what every request repeats, such as instructions and tool definitions.
|
|
111
|
+
* Measure what the framework shows; `CompactionPolicy.reservedInputTokens` covers the rest. Defaults to 0.
|
|
112
|
+
* @returns The estimated input tokens.
|
|
113
|
+
* @since 0.3.0
|
|
114
|
+
*/
|
|
115
|
+
export declare function estimateStoredInputTokens(turn: AgentTurnContext, fixedInputTokens?: number): number;
|
|
116
|
+
/**
|
|
117
|
+
* Chooses the last message to summarize, cutting only between turns.
|
|
118
|
+
*
|
|
119
|
+
* Call it from `selectCompactionCutoff` of a framework adapter. A turn's messages are stored together, so a cut
|
|
120
|
+
* between turns never separates a tool call from its result and never splits a model response, whatever
|
|
121
|
+
* framework items the turn holds. The running turn is always kept, because the adapter reads its opening user
|
|
122
|
+
* message from `turn.messages`.
|
|
123
|
+
*
|
|
124
|
+
* It returns `null` while `turn.frameworkRunState` is set. A resumed model call continues from that value, which
|
|
125
|
+
* may already hold the older messages, so the summary could not replace them. Compaction waits for the next turn.
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* ```ts
|
|
129
|
+
* public selectCompactionCutoff(turn: AgentTurnContext, retainedTokenBudget: number): number | null {
|
|
130
|
+
* return selectTurnBoundaryCutoff(turn, retainedTokenBudget);
|
|
131
|
+
* }
|
|
132
|
+
* ```
|
|
133
|
+
* @param turn - The context of the model call that crossed the threshold.
|
|
134
|
+
* @param retainedTokenBudget - Tokens that the kept messages may use.
|
|
135
|
+
* @returns The sequence of the last message to summarize: the earliest turn boundary whose kept messages fit the
|
|
136
|
+
* budget, else the boundary before the running turn. `null` when the turn is resuming, every message already
|
|
137
|
+
* fits, or no earlier turn is left to summarize.
|
|
138
|
+
* @since 0.3.0
|
|
139
|
+
*/
|
|
140
|
+
export declare function selectTurnBoundaryCutoff(turn: AgentTurnContext, retainedTokenBudget: number): number | null;
|
|
141
|
+
/**
|
|
142
|
+
* Returns a stored message as the plain text a summary prompt shows.
|
|
143
|
+
*
|
|
144
|
+
* Wrap it in the `describeMessage` of {@link buildContextSummaryPrompt} to leave out items that only a provider can
|
|
145
|
+
* read.
|
|
146
|
+
*
|
|
147
|
+
* @param message - The stored message.
|
|
148
|
+
* @returns Its role and text, tool arguments, and tool output. A message with none of them returns its role and
|
|
149
|
+
* `frameworkItem`, such as a model response with several tool calls.
|
|
150
|
+
* @since 0.3.0
|
|
151
|
+
*/
|
|
152
|
+
export declare function describeStoredMessage(message: AgentMessage): string;
|
|
153
|
+
/**
|
|
154
|
+
* Builds the one user prompt of a summary call for a framework adapter.
|
|
155
|
+
*
|
|
156
|
+
* Call it from `summarizeContext` and send the result with {@link contextSummaryInstructions} and no tools. The
|
|
157
|
+
* prompt holds the previous summary, the messages as plain text, and {@link contextSummaryRequest}. Plain text
|
|
158
|
+
* works with any framework: it needs no tool definitions, and no provider sees an item out of its place.
|
|
159
|
+
*
|
|
160
|
+
* The prompt fits `request.inputTokenBudget`, less room for the summary, as {@link estimateTextTokens} counts.
|
|
161
|
+
* When the range is too large, it leaves out the oldest messages and says so with
|
|
162
|
+
* {@link buildContextSummaryOmissionNote}. It shortens a single message only when that message alone is too
|
|
163
|
+
* large.
|
|
164
|
+
*
|
|
165
|
+
* @example
|
|
166
|
+
* ```ts
|
|
167
|
+
* public async summarizeContext(request: ContextSummaryRequest): Promise<ContextSummaryResult> {
|
|
168
|
+
* const result = await generateText({
|
|
169
|
+
* model,
|
|
170
|
+
* system: contextSummaryInstructions,
|
|
171
|
+
* prompt: buildContextSummaryPrompt(request),
|
|
172
|
+
* });
|
|
173
|
+
* return { text: result.text, usage: toAgentUsage(result.usage) };
|
|
174
|
+
* }
|
|
175
|
+
* ```
|
|
176
|
+
* @param request - The previous summary and the messages to summarize.
|
|
177
|
+
* @param describeMessage - Returns a message's text for the prompt, or `null` to leave it out, such as an
|
|
178
|
+
* encrypted reasoning item. Defaults to {@link describeStoredMessage}.
|
|
179
|
+
* @returns The prompt text.
|
|
180
|
+
* @since 0.3.0
|
|
181
|
+
*/
|
|
182
|
+
export declare function buildContextSummaryPrompt(request: ContextSummaryRequest, describeMessage?: (message: AgentMessage) => string | null): string;
|
|
183
|
+
/** What {@link AgentFrameworkRunner.summarizeContext} receives: the messages to fold into a new summary. */
|
|
184
|
+
export interface ContextSummaryRequest {
|
|
185
|
+
/** Flow ID, which is the session ID. */
|
|
186
|
+
readonly flowId: string;
|
|
187
|
+
/** Turn ID of the running turn. */
|
|
188
|
+
readonly turnId: string;
|
|
189
|
+
/** One-based `CompactContext` attempt. */
|
|
190
|
+
readonly attempt: number;
|
|
191
|
+
/** Dex Step execution ID of the `CompactContext` Step, shared by all of its attempts. */
|
|
192
|
+
readonly stepExecutionId: string;
|
|
193
|
+
/** The summary so far, or `null` for the first one. Fold its text into the new summary. */
|
|
194
|
+
readonly previousSummary: ContextSummary | null;
|
|
195
|
+
/**
|
|
196
|
+
* The messages after the previous summary through {@link throughSequence}, in sequence order. No tool
|
|
197
|
+
* call is separated from its result.
|
|
198
|
+
*/
|
|
199
|
+
readonly messages: readonly AgentMessage[];
|
|
200
|
+
/** Sequence of the last message to summarize. */
|
|
201
|
+
readonly throughSequence: number;
|
|
202
|
+
/** Events writer bound to the turn and attempt. Write no model text to it: the summary is not an answer. */
|
|
203
|
+
readonly events: AgentEventWriter;
|
|
204
|
+
/** Signal aborted when Dex cancels the attempt; pass it to the model call. */
|
|
205
|
+
readonly cancellationSignal: AbortSignal;
|
|
206
|
+
/** Absolute deadline of the current attempt, or `undefined` when Dex supplied none. */
|
|
207
|
+
readonly deadline: Date | undefined;
|
|
208
|
+
/**
|
|
209
|
+
* Tokens the summary call may use, the policy's `contextWindowTokens`, or `undefined` for no limit. A runner
|
|
210
|
+
* keeps the input below it, less what it reserves for the summary, such as its `maxOutputTokens`. The range
|
|
211
|
+
* can be larger after the window shrank or when compaction starts on a long session;
|
|
212
|
+
* leave out its oldest part rather than send an over-window request that fails every turn.
|
|
213
|
+
*/
|
|
214
|
+
readonly inputTokenBudget?: number;
|
|
215
|
+
/**
|
|
216
|
+
* The {@link SessionSettings} the running turn started with, as `AgentTurnContext.sessionSettings` has them.
|
|
217
|
+
* Use them to choose the model that writes the summary. The session always sets it; treat `undefined` as `{}`.
|
|
218
|
+
*/
|
|
219
|
+
readonly sessionSettings?: SessionSettings;
|
|
220
|
+
/**
|
|
221
|
+
* Records a heartbeat. The session already records one every 20 seconds while the summary runs.
|
|
222
|
+
* @returns A Promise that resolves when the Worker accepted the heartbeat.
|
|
223
|
+
*/
|
|
224
|
+
heartbeat(): Promise<void>;
|
|
225
|
+
}
|
|
226
|
+
/** What {@link AgentFrameworkRunner.summarizeContext} returns. */
|
|
227
|
+
export interface ContextSummaryResult {
|
|
228
|
+
/** Summary text; it must not be blank. It replaces the previous summary, so it includes what that one said. */
|
|
229
|
+
readonly text: string;
|
|
230
|
+
/** Usage of the summary model call; the session adds it to the session's usage. */
|
|
231
|
+
readonly usage: AgentUsage;
|
|
232
|
+
/**
|
|
233
|
+
* Model and response of the summary call, for the `CompactContext` attempt's `chat` span. When omitted,
|
|
234
|
+
* the span is named `chat`.
|
|
235
|
+
*/
|
|
236
|
+
readonly modelCallMetadata?: ModelCallMetadata;
|
|
237
|
+
}
|
|
238
|
+
/** A runner that implements the three optional compaction methods. */
|
|
239
|
+
export type ContextCompactionRunner = AgentFrameworkRunner<DeferredToolOutcome> & Required<Pick<AgentFrameworkRunner<DeferredToolOutcome>, "estimateInputTokens" | "selectCompactionCutoff" | "summarizeContext">>;
|
|
240
|
+
/** Reports whether a runner implements every compaction method. */
|
|
241
|
+
export declare function isContextCompactionRunner(runner: AgentFrameworkRunner<DeferredToolOutcome>): runner is ContextCompactionRunner;
|
|
242
|
+
/**
|
|
243
|
+
* Fills in a compaction policy's defaults and checks it.
|
|
244
|
+
* @throws {RangeError} When `contextWindowTokens` is not a positive integer, `reservedInputTokens` is not an
|
|
245
|
+
* integer from 0 to below the window, or unless `0 < retainedRatio < triggerRatio <= 1`.
|
|
246
|
+
*/
|
|
247
|
+
export declare function resolveCompactionPolicy(policy: CompactionPolicy): ResolvedCompactionPolicy;
|
|
248
|
+
/**
|
|
249
|
+
* Reports whether a model input of this size makes the session compact before the model call: the estimate
|
|
250
|
+
* plus `reservedInputTokens` reaches `triggerRatio` of the window.
|
|
251
|
+
*/
|
|
252
|
+
export declare function isCompactionTriggered(policy: ResolvedCompactionPolicy, estimatedInputTokens: number): boolean;
|
|
253
|
+
/** Tokens that the newest messages may keep after compaction: the retained share of the window, rounded down. */
|
|
254
|
+
export declare function readRetainedTokenBudget(policy: ResolvedCompactionPolicy): number;
|