@superdurable/dex-agents 0.0.0-stage → 0.2.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/LICENSE +105 -0
- package/README.md +1981 -2
- 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 +108 -0
- package/dist/src/agent-context-compaction.js +39 -0
- package/dist/src/agent-context-compaction.js.map +1 -0
- package/dist/src/agent-event-writer.d.ts +26 -0
- package/dist/src/agent-event-writer.js +110 -0
- package/dist/src/agent-event-writer.js.map +1 -0
- package/dist/src/agent-framework-runner.d.ts +808 -0
- package/dist/src/agent-framework-runner.js +82 -0
- package/dist/src/agent-framework-runner.js.map +1 -0
- 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 +105 -0
- package/dist/src/agent-session-codecs.js +689 -0
- package/dist/src/agent-session-codecs.js.map +1 -0
- package/dist/src/agent-session-flow.d.ts +416 -0
- package/dist/src/agent-session-flow.js +623 -0
- package/dist/src/agent-session-flow.js.map +1 -0
- package/dist/src/agent-session-persistence.d.ts +133 -0
- package/dist/src/agent-session-persistence.js +74 -0
- package/dist/src/agent-session-persistence.js.map +1 -0
- 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-step-options.d.ts +27 -0
- package/dist/src/agent-session-step-options.js +39 -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 +179 -0
- package/dist/src/agent-session-steps.js +1272 -0
- package/dist/src/agent-session-steps.js.map +1 -0
- 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 +224 -0
- package/dist/src/agent-session-transitions.js +850 -0
- package/dist/src/agent-session-transitions.js.map +1 -0
- package/dist/src/agent-session-values.d.ts +998 -0
- package/dist/src/agent-session-values.js +162 -0
- package/dist/src/agent-session-values.js.map +1 -0
- 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 +10 -0
- package/dist/src/index.js +17 -0
- package/dist/src/index.js.map +1 -0
- 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 +158 -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 +7 -0
- package/dist/src/stepwise/index.js +12 -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 +189 -0
- package/dist/src/stepwise/stepwise-agent-runner.js +804 -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 +56 -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 +205 -0
- package/dist/src/testing/agent-session-test-client.js +290 -0
- package/dist/src/testing/agent-session-test-client.js.map +1 -0
- package/dist/src/testing/agent-test-environment.d.ts +52 -0
- package/dist/src/testing/agent-test-environment.js +86 -0
- package/dist/src/testing/agent-test-environment.js.map +1 -0
- package/dist/src/testing/agent-worker-process.d.ts +91 -0
- package/dist/src/testing/agent-worker-process.js +150 -0
- package/dist/src/testing/agent-worker-process.js.map +1 -0
- package/dist/src/testing/child-process-group.d.ts +33 -0
- package/dist/src/testing/child-process-group.js +164 -0
- package/dist/src/testing/child-process-group.js.map +1 -0
- package/dist/src/testing/coordination.d.ts +99 -0
- package/dist/src/testing/coordination.js +155 -0
- package/dist/src/testing/coordination.js.map +1 -0
- package/dist/src/testing/index.d.ts +8 -0
- package/dist/src/testing/index.js +16 -0
- package/dist/src/testing/index.js.map +1 -0
- package/dist/src/testing/polling.d.ts +31 -0
- package/dist/src/testing/polling.js +51 -0
- package/dist/src/testing/polling.js.map +1 -0
- package/dist/src/testing/private-dex-engine.d.ts +101 -0
- package/dist/src/testing/private-dex-engine.js +223 -0
- package/dist/src/testing/private-dex-engine.js.map +1 -0
- 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/testing/step-history.d.ts +40 -0
- package/dist/src/testing/step-history.js +59 -0
- package/dist/src/testing/step-history.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 +154 -0
- package/dist/src/ui-stream/agent-event-sse.js +159 -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 +72 -0
- package/dist/src/ui-stream/agent-session-record-reader.js +241 -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 +440 -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 +681 -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 +66 -0
- package/dist/src/ui-stream/agent-ui-messages.js +220 -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 +86 -4
|
@@ -0,0 +1,808 @@
|
|
|
1
|
+
import type { BuiltInTool } from "./agent-built-in-tools.js";
|
|
2
|
+
import type { ContextSummaryRequest, ContextSummaryResult } from "./agent-context-compaction.js";
|
|
3
|
+
import type { AgentMessage, AgentPlan, AgentToolCall, AgentToolCallRecord, AgentToolCallStatus, AgentUsage, ContextSummary, SessionSettings, FrameworkMessage, InteractionMode, QuestionAnswer, ToolEffect } from "./agent-session-values.js";
|
|
4
|
+
/**
|
|
5
|
+
* Per-tool override of the effect and approval defaults.
|
|
6
|
+
*
|
|
7
|
+
* Pass policies by tool name in {@link AgentSessionOptions.toolPolicies}. A policy wins over
|
|
8
|
+
* framework metadata such as MCP annotations and over {@link AgentSessionOptions.defaultToolEffect}.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* const toolPolicies = {
|
|
13
|
+
* search_orders: { effect: "read" },
|
|
14
|
+
* refund_order: { effect: "idempotent_write", requiresApproval: true },
|
|
15
|
+
* } satisfies Record<string, ToolPolicy>;
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export interface ToolPolicy {
|
|
19
|
+
/** Effect of the tool. */
|
|
20
|
+
readonly effect: ToolEffect;
|
|
21
|
+
/**
|
|
22
|
+
* Whether every call waits for `resolve_interrupt` before it runs. When omitted, calls with effect
|
|
23
|
+
* `non_idempotent_write` or `unclassified` require approval and the others do not.
|
|
24
|
+
*/
|
|
25
|
+
readonly requiresApproval?: boolean;
|
|
26
|
+
}
|
|
27
|
+
/** Effective policy of one tool, returned by {@link resolveToolPolicy}. */
|
|
28
|
+
export interface ResolvedToolPolicy {
|
|
29
|
+
/** Effective effect after policies, framework metadata, and the session default. */
|
|
30
|
+
readonly effect: ToolEffect;
|
|
31
|
+
/** Effective approval requirement. */
|
|
32
|
+
readonly requiresApproval: boolean;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Returns the effective policy of one tool.
|
|
36
|
+
*
|
|
37
|
+
* Runners normally call {@link AgentTurnContext.resolveToolPolicy}. Call this function when an adapter
|
|
38
|
+
* classifies tools outside a turn, for example while building framework tool wrappers. Precedence: the
|
|
39
|
+
* tool's {@link ToolPolicy}, then `frameworkEffect`, then `defaultToolEffect`.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```ts
|
|
43
|
+
* const policy = resolveToolPolicy("refund_order", flow.options.toolPolicies, flow.options.defaultToolEffect);
|
|
44
|
+
* const label = policy.requiresApproval ? "needs approval" : "runs without approval";
|
|
45
|
+
* ```
|
|
46
|
+
* @param toolName - Tool name as the model calls it.
|
|
47
|
+
* @param toolPolicies - Explicit policies by tool name.
|
|
48
|
+
* @param defaultToolEffect - Effect of a tool without a policy or framework metadata.
|
|
49
|
+
* @param frameworkEffect - Effect declared by framework metadata, or `undefined`.
|
|
50
|
+
* @returns The effective policy.
|
|
51
|
+
*/
|
|
52
|
+
export declare function resolveToolPolicy(toolName: string, toolPolicies: Readonly<Record<string, ToolPolicy>>, defaultToolEffect: ToolEffect, frameworkEffect?: ToolEffect): ResolvedToolPolicy;
|
|
53
|
+
/**
|
|
54
|
+
* Writes best-effort live events to the session's `AgentEvents` Stream.
|
|
55
|
+
*
|
|
56
|
+
* Every event carries {@link turnId} and the Step {@link attempt}. Payload objects are written as JSON
|
|
57
|
+
* with sorted keys. Consecutive `text_delta` or `reasoning_delta` writes are coalesced for up to 100
|
|
58
|
+
* milliseconds or 1,024 characters before they reach the Stream. Writes after the handler returns are
|
|
59
|
+
* dropped. Stream delivery is best effort: a retried turn writes its events again under a new attempt,
|
|
60
|
+
* so a UI rebuilds durable state with `get_agent_snapshot`.
|
|
61
|
+
*/
|
|
62
|
+
export interface AgentEventWriter {
|
|
63
|
+
/** Turn stamped on every event, or `null` outside a turn. */
|
|
64
|
+
readonly turnId: string | null;
|
|
65
|
+
/** One-based attempt of the Step that owns this writer. */
|
|
66
|
+
readonly attempt: number;
|
|
67
|
+
/**
|
|
68
|
+
* Writes one event with a JSON object payload.
|
|
69
|
+
*
|
|
70
|
+
* Prefer the typed methods for standard types. For an adapter-specific type, use an adapter prefix,
|
|
71
|
+
* such as `"vercel_ai.step_finished"`, so it never collides with an {@link AgentEventType}.
|
|
72
|
+
* @param type - Event type stored in `AgentEvent.type`.
|
|
73
|
+
* @param payload - JSON-compatible object stored as `AgentEvent.payload` text.
|
|
74
|
+
* @throws {TypeError} When the payload cannot be serialized as JSON.
|
|
75
|
+
*/
|
|
76
|
+
writeEvent(type: string, payload: Readonly<Record<string, unknown>>): void;
|
|
77
|
+
/**
|
|
78
|
+
* Writes visible assistant text as a `text_delta` event.
|
|
79
|
+
* @param text - Text chunk; empty chunks are ignored.
|
|
80
|
+
*/
|
|
81
|
+
writeTextDelta(text: string): void;
|
|
82
|
+
/**
|
|
83
|
+
* Writes a provider-authored reasoning summary chunk as a `reasoning_delta` event.
|
|
84
|
+
* Never label ordinary text or hidden chain of thought as reasoning.
|
|
85
|
+
* @param text - Text chunk; empty chunks are ignored.
|
|
86
|
+
*/
|
|
87
|
+
writeReasoningDelta(text: string): void;
|
|
88
|
+
/**
|
|
89
|
+
* Writes a `tool_call_started` event. The session writes it for every tool Step; a runner writes it
|
|
90
|
+
* only for a tool its framework cannot defer and runs inside `runModelStep`.
|
|
91
|
+
* @param toolCallId - Framework tool call ID.
|
|
92
|
+
* @param toolName - Tool name.
|
|
93
|
+
*/
|
|
94
|
+
writeToolCallStarted(toolCallId: string, toolName: string): void;
|
|
95
|
+
/**
|
|
96
|
+
* Writes a `tool_call_finished` event.
|
|
97
|
+
* @param toolCallId - Framework tool call ID.
|
|
98
|
+
* @param toolName - Tool name.
|
|
99
|
+
* @param status - `succeeded` or `failed` for a call the runner ran itself, or the final status the
|
|
100
|
+
* session recorded.
|
|
101
|
+
*/
|
|
102
|
+
writeToolCallFinished(toolCallId: string, toolName: string, status: AgentToolCallStatus): void;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Everything a runner needs for one model call of a turn, inside the `RunModel` Step.
|
|
106
|
+
*
|
|
107
|
+
* Each `RunModel` Step makes at most one model call. The first `RunModel` of a turn starts from the
|
|
108
|
+
* stored messages. Every later one resumes from {@link frameworkRunState} after the tool calls of the
|
|
109
|
+
* previous model response reached final records. A crash or thrown error reruns only this model call
|
|
110
|
+
* from the same committed state. Do not retain the context after `runModelStep` settles.
|
|
111
|
+
*/
|
|
112
|
+
export interface AgentTurnContext {
|
|
113
|
+
/** Flow ID, which is the session ID. */
|
|
114
|
+
readonly flowId: string;
|
|
115
|
+
/** Turn ID, `turn-<n>`. */
|
|
116
|
+
readonly turnId: string;
|
|
117
|
+
/**
|
|
118
|
+
* One-based `RunModel` attempt. A rerun after a Worker crash gets the next attempt, but an attempt whose
|
|
119
|
+
* stream half-closed before any output returns as not run and keeps its number, so do not rely on it to
|
|
120
|
+
* detect reruns.
|
|
121
|
+
*/
|
|
122
|
+
readonly attempt: number;
|
|
123
|
+
/**
|
|
124
|
+
* Dex Step execution ID of this `RunModel` Step, shared by all of its attempts.
|
|
125
|
+
*
|
|
126
|
+
* The session always sets this field. It is optional only so that code that builds a context by hand
|
|
127
|
+
* keeps compiling.
|
|
128
|
+
*/
|
|
129
|
+
readonly stepExecutionId?: string;
|
|
130
|
+
/**
|
|
131
|
+
* Every stored session message in ascending sequence order, including the user message that
|
|
132
|
+
* started this turn. Replay each `frameworkItem` to the framework unchanged. After compaction, only the
|
|
133
|
+
* messages after {@link contextSummary}.
|
|
134
|
+
*/
|
|
135
|
+
readonly messages: readonly AgentMessage[];
|
|
136
|
+
/**
|
|
137
|
+
* Framework run state from this turn's previous {@link ToolCallsRequested} outcome, or `null` for
|
|
138
|
+
* the turn's first model call.
|
|
139
|
+
*/
|
|
140
|
+
readonly frameworkRunState: string | null;
|
|
141
|
+
/** Whether this model call continues the turn after tool calls, that is, `frameworkRunState` is set. */
|
|
142
|
+
readonly isResuming: boolean;
|
|
143
|
+
/**
|
|
144
|
+
* Tool call records of this turn, in deferral order; every record is final. Give the framework the
|
|
145
|
+
* recorded result of each call from the previous model response instead of calling the tool. Use
|
|
146
|
+
* {@link describeToolCallOutcome} when the framework needs result text.
|
|
147
|
+
*/
|
|
148
|
+
readonly toolCalls: readonly AgentToolCall[];
|
|
149
|
+
/** Events writer bound to this turn and attempt. */
|
|
150
|
+
readonly events: AgentEventWriter;
|
|
151
|
+
/** Signal aborted when Dex cancels the attempt; pass it to the model call. */
|
|
152
|
+
readonly cancellationSignal: AbortSignal;
|
|
153
|
+
/** Absolute deadline of the current attempt, or `undefined` when Dex supplied none. */
|
|
154
|
+
readonly deadline: Date | undefined;
|
|
155
|
+
/**
|
|
156
|
+
* Built-in tools enabled for this session through {@link AgentSessionOptions.builtInTools}. Offer
|
|
157
|
+
* their definitions from {@link builtInToolDefinitions} to the model; the session handles their calls.
|
|
158
|
+
*
|
|
159
|
+
* The session always sets this field. It is optional only so that code that builds a context by hand,
|
|
160
|
+
* such as a unit test, keeps compiling; treat `undefined` as no built-in tools.
|
|
161
|
+
*/
|
|
162
|
+
readonly enabledBuiltInTools?: ReadonlySet<BuiltInTool>;
|
|
163
|
+
/**
|
|
164
|
+
* Explicit policies by tool name from {@link AgentSessionOptions.toolPolicies}, the same map
|
|
165
|
+
* {@link resolveToolPolicy} applies first. A runner whose framework sets its own approval default, such as
|
|
166
|
+
* a tool's `requiresApproval`, applies that default only to tools without an explicit policy here.
|
|
167
|
+
* Optional for the same reason as {@link enabledBuiltInTools}; treat `undefined` as no policies.
|
|
168
|
+
*/
|
|
169
|
+
readonly toolPolicies?: Readonly<Record<string, ToolPolicy>>;
|
|
170
|
+
/**
|
|
171
|
+
* {@link AgentSessionOptions.defaultToolEffect}, the effect of a tool without a policy or framework
|
|
172
|
+
* metadata. Optional for the same reason as {@link enabledBuiltInTools}.
|
|
173
|
+
*/
|
|
174
|
+
readonly defaultToolEffect?: ToolEffect;
|
|
175
|
+
/**
|
|
176
|
+
* The turn's {@link AgentState.interactionMode}. A runner with {@link AgentFrameworkRunner.supportsPlanMode}
|
|
177
|
+
* offers only `write_todos` in a `planning` turn. The session always sets it; treat `undefined` as `chat`.
|
|
178
|
+
*/
|
|
179
|
+
readonly interactionMode?: InteractionMode;
|
|
180
|
+
/**
|
|
181
|
+
* The {@link AgentPlan} as committed before this model call, or `null` before `write_todos` first
|
|
182
|
+
* succeeded. Show it to the model with {@link buildPlanNote}. The session always sets it; treat
|
|
183
|
+
* `undefined` as `null`.
|
|
184
|
+
*/
|
|
185
|
+
readonly plan?: AgentPlan | null;
|
|
186
|
+
/**
|
|
187
|
+
* The summary of the messages before {@link messages}, or `null`. Only a runner that implements the
|
|
188
|
+
* compaction methods, such as {@link AgentFrameworkRunner.summarizeContext}, receives one; put its text
|
|
189
|
+
* before {@link messages} in the model input. Optional for the same reason as {@link enabledBuiltInTools};
|
|
190
|
+
* treat `undefined` as no summary.
|
|
191
|
+
*/
|
|
192
|
+
readonly contextSummary?: ContextSummary | null;
|
|
193
|
+
/**
|
|
194
|
+
* The {@link SessionSettings} this turn started with, such as `{ model: "gpt-5-mini" }`. Use them to choose
|
|
195
|
+
* the model or the instructions of each model call. `update_session_settings` changes them from the next turn
|
|
196
|
+
* on, so every model call of one turn sees the same value, retries included. Empty when the session has no
|
|
197
|
+
* settings, such as a session started on dex-agents 0.1.0. The session always sets it; it is optional for the
|
|
198
|
+
* same reason as {@link enabledBuiltInTools}, so treat `undefined` as `{}`.
|
|
199
|
+
*/
|
|
200
|
+
readonly sessionSettings?: SessionSettings;
|
|
201
|
+
/**
|
|
202
|
+
* Returns the effective policy of a tool; see {@link resolveToolPolicy} for the precedence.
|
|
203
|
+
* Pass it unchanged in {@link DeferredToolCall.policy}.
|
|
204
|
+
* @param toolName - Tool name as the model calls it.
|
|
205
|
+
* @param frameworkEffect - Effect declared by framework metadata, for example from
|
|
206
|
+
* {@link toolEffectFromMcpToolAnnotations}, or `undefined`.
|
|
207
|
+
* @returns The effective policy.
|
|
208
|
+
*/
|
|
209
|
+
resolveToolPolicy(toolName: string, frameworkEffect?: ToolEffect): ResolvedToolPolicy;
|
|
210
|
+
/**
|
|
211
|
+
* Returns this turn's record for a tool call ID.
|
|
212
|
+
* @param toolCallId - Framework tool call ID.
|
|
213
|
+
* @returns The record, or `undefined` when the turn never requested that call.
|
|
214
|
+
*/
|
|
215
|
+
recordedToolCall(toolCallId: string): AgentToolCall | undefined;
|
|
216
|
+
/**
|
|
217
|
+
* Records a heartbeat for the current attempt.
|
|
218
|
+
*
|
|
219
|
+
* The session already records one every 20 seconds while `runModelStep` is pending. Call it only
|
|
220
|
+
* from a long synchronous loop that could otherwise starve the event loop.
|
|
221
|
+
* @returns A Promise that resolves when the Worker accepted the heartbeat.
|
|
222
|
+
*/
|
|
223
|
+
heartbeat(): Promise<void>;
|
|
224
|
+
}
|
|
225
|
+
/** One tool call the model requested, returned in {@link ToolCallsRequested}; it runs in its own Step. */
|
|
226
|
+
export interface DeferredToolCall {
|
|
227
|
+
/**
|
|
228
|
+
* Framework tool call ID. It must be non-empty, contain no `/`, and be unique within the turn;
|
|
229
|
+
* frameworks may reuse IDs across turns.
|
|
230
|
+
*/
|
|
231
|
+
readonly toolCallId: string;
|
|
232
|
+
/** Tool name. */
|
|
233
|
+
readonly toolName: string;
|
|
234
|
+
/** Tool arguments as JSON text. */
|
|
235
|
+
readonly arguments: string;
|
|
236
|
+
/** Policy from {@link AgentTurnContext.resolveToolPolicy}; it decides approval and the tool Step. */
|
|
237
|
+
readonly policy: ResolvedToolPolicy;
|
|
238
|
+
/**
|
|
239
|
+
* Opaque text that the tool's source stores on the call's record, such as which MCP server and tool
|
|
240
|
+
* to call. The tool Step returns it in {@link DeferredToolExecution.toolCall}, so the source resolves
|
|
241
|
+
* the tool without listing it again. Omit it to store nothing.
|
|
242
|
+
*/
|
|
243
|
+
readonly toolSourceReference?: string;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Model and response of one model call, returned in {@link TurnCompleted} or {@link ToolCallsRequested}
|
|
247
|
+
* for tracing.
|
|
248
|
+
*
|
|
249
|
+
* The session never stores it: it only names the `chat` span and sets its GenAI attributes. Omit a
|
|
250
|
+
* field when the framework does not expose it.
|
|
251
|
+
*
|
|
252
|
+
* @example
|
|
253
|
+
* ```ts
|
|
254
|
+
* const modelCallMetadata: ModelCallMetadata = {
|
|
255
|
+
* requestModel: "gpt-5",
|
|
256
|
+
* responseModel: "gpt-5-2026-08-07",
|
|
257
|
+
* providerName: "openai",
|
|
258
|
+
* responseId: "resp_123",
|
|
259
|
+
* finishReasons: ["tool_calls"],
|
|
260
|
+
* };
|
|
261
|
+
* return { kind: "tool_calls_requested", frameworkRunState, toolCalls, usage, modelCallMetadata };
|
|
262
|
+
* ```
|
|
263
|
+
*/
|
|
264
|
+
export interface ModelCallMetadata {
|
|
265
|
+
/**
|
|
266
|
+
* Model name the call requested, such as `"gpt-5"`. It becomes `gen_ai.request.model` and the span
|
|
267
|
+
* name `chat <requestModel>`.
|
|
268
|
+
*/
|
|
269
|
+
readonly requestModel?: string;
|
|
270
|
+
/** Model name the provider reported. */
|
|
271
|
+
readonly responseModel?: string;
|
|
272
|
+
/**
|
|
273
|
+
* GenAI provider, such as `"openai"`, `"anthropic"`, or `"gcp.gemini"`. Use the well-known
|
|
274
|
+
* `gen_ai.provider.name` value when one applies.
|
|
275
|
+
*/
|
|
276
|
+
readonly providerName?: string;
|
|
277
|
+
/** Provider response ID. */
|
|
278
|
+
readonly responseId?: string;
|
|
279
|
+
/** Provider finish reasons, one per returned choice. */
|
|
280
|
+
readonly finishReasons?: readonly string[];
|
|
281
|
+
}
|
|
282
|
+
/** Outcome of a model call that produced the turn's final answer. */
|
|
283
|
+
export interface TurnCompleted {
|
|
284
|
+
/** Discriminator. */
|
|
285
|
+
readonly kind: "completed";
|
|
286
|
+
/**
|
|
287
|
+
* Every message of the turn that the session has not stored yet, in order: model output, tool
|
|
288
|
+
* requests, tool results, and the final answer. The session appends them in one commit and deletes the
|
|
289
|
+
* turn's tool call records.
|
|
290
|
+
*
|
|
291
|
+
* A runner that never sets {@link ToolCallsRequested.newMessages} returns every message the turn
|
|
292
|
+
* produced after its user message, across all of its model calls. A runner that stores messages
|
|
293
|
+
* incrementally returns only the messages after the ones it already returned.
|
|
294
|
+
*/
|
|
295
|
+
readonly newMessages: readonly FrameworkMessage[];
|
|
296
|
+
/** Usage of this model call only; the session adds it to the cumulative usage. */
|
|
297
|
+
readonly usage: AgentUsage;
|
|
298
|
+
/** Model and response of this model call, for the `chat` span. When omitted, the span is named `chat`. */
|
|
299
|
+
readonly modelCallMetadata?: ModelCallMetadata;
|
|
300
|
+
}
|
|
301
|
+
/** Outcome of a model call that requested tool calls. */
|
|
302
|
+
export interface ToolCallsRequested {
|
|
303
|
+
/** Discriminator. */
|
|
304
|
+
readonly kind: "tool_calls_requested";
|
|
305
|
+
/**
|
|
306
|
+
* Non-empty serialized framework state from which the next `RunModel` continues the turn. The
|
|
307
|
+
* session keeps this value opaquely in `FrameworkRunState`. A runner without
|
|
308
|
+
* {@link newMessages} includes the messages this turn has produced so far; a runner with them keeps
|
|
309
|
+
* only what the stored messages cannot rebuild, so the value does not grow with the turn.
|
|
310
|
+
*/
|
|
311
|
+
readonly frameworkRunState: string;
|
|
312
|
+
/**
|
|
313
|
+
* Messages of the turn to store now, in the same commit that records the tool calls. Defaults to none.
|
|
314
|
+
*
|
|
315
|
+
* Return the previous model response's tool results and this model response, so the turn's
|
|
316
|
+
* transcript is stored while its tools run. A runner that sets this field must implement
|
|
317
|
+
* {@link AgentFrameworkRunner.buildUnfinishedTurnMessages}; the session fails the model call
|
|
318
|
+
* otherwise. The next {@link AgentTurnContext.messages} include these messages.
|
|
319
|
+
*/
|
|
320
|
+
readonly newMessages?: readonly FrameworkMessage[];
|
|
321
|
+
/**
|
|
322
|
+
* Every tool call of the model response, at least one. Each runs in its own Step, in parallel. Do not
|
|
323
|
+
* repeat a call that already has a record in {@link AgentTurnContext.toolCalls}; the session rejects
|
|
324
|
+
* that as a runner defect.
|
|
325
|
+
*/
|
|
326
|
+
readonly toolCalls: readonly DeferredToolCall[];
|
|
327
|
+
/** Usage of this model call only. */
|
|
328
|
+
readonly usage: AgentUsage;
|
|
329
|
+
/** Model and response of this model call, for the `chat` span. When omitted, the span is named `chat`. */
|
|
330
|
+
readonly modelCallMetadata?: ModelCallMetadata;
|
|
331
|
+
}
|
|
332
|
+
/** Result of {@link AgentFrameworkRunner.runModelStep}. */
|
|
333
|
+
export type AgentTurnOutcome = TurnCompleted | ToolCallsRequested;
|
|
334
|
+
/** One tool call that a tool Step asks the runner to run. */
|
|
335
|
+
export interface DeferredToolExecution {
|
|
336
|
+
/** Flow ID, which is the session ID. */
|
|
337
|
+
readonly flowId: string;
|
|
338
|
+
/** The call's record; its `effect` selected `CallTool` or `CallSingleAttemptTool`. */
|
|
339
|
+
readonly toolCall: AgentToolCall;
|
|
340
|
+
/**
|
|
341
|
+
* Stable key `<flowId>/<turnId>/<toolCallId>`, identical across attempts of the call. Pass it to
|
|
342
|
+
* services that deduplicate requests; `CallTool` retries an `idempotent_write` call with the same
|
|
343
|
+
* key. The turn ID keeps keys distinct when a framework reuses tool call IDs across turns.
|
|
344
|
+
*/
|
|
345
|
+
readonly idempotencyKey: string;
|
|
346
|
+
/**
|
|
347
|
+
* One-based attempt of the tool Step. `read` and `idempotent_write` calls get up to three attempts;
|
|
348
|
+
* other calls get one.
|
|
349
|
+
*/
|
|
350
|
+
readonly attempt: number;
|
|
351
|
+
/** Events writer bound to the turn and attempt. */
|
|
352
|
+
readonly events: AgentEventWriter;
|
|
353
|
+
/** Signal aborted when Dex cancels the attempt. */
|
|
354
|
+
readonly cancellationSignal: AbortSignal;
|
|
355
|
+
/** Absolute deadline of the current attempt, or `undefined`. */
|
|
356
|
+
readonly deadline: Date | undefined;
|
|
357
|
+
/**
|
|
358
|
+
* Answers to the questions this call asked by returning {@link ToolInputRequested}, oldest first, or
|
|
359
|
+
* empty. The opaque continuation of the last question is in `toolCall.inputContinuation`.
|
|
360
|
+
*
|
|
361
|
+
* The session always sets this field. It is optional only so that code that builds an execution by
|
|
362
|
+
* hand, such as a unit test, keeps compiling; treat `undefined` as no answers.
|
|
363
|
+
*/
|
|
364
|
+
readonly inputAnswers?: readonly QuestionAnswer[];
|
|
365
|
+
/**
|
|
366
|
+
* Whether the runner implements {@link AgentFrameworkRunner.pollToolTask}, so the tool may return
|
|
367
|
+
* {@link ToolTaskStarted}. When it is not `true`, run remote work to its end inside the call instead.
|
|
368
|
+
* Optional for the same reason as {@link inputAnswers}; treat `undefined` as `false`.
|
|
369
|
+
*/
|
|
370
|
+
readonly supportsToolTasks?: boolean;
|
|
371
|
+
/**
|
|
372
|
+
* Records a heartbeat. The session already records one every 20 seconds.
|
|
373
|
+
* @returns A Promise that resolves when the Worker accepted the heartbeat.
|
|
374
|
+
*/
|
|
375
|
+
heartbeat(): Promise<void>;
|
|
376
|
+
/**
|
|
377
|
+
* The checkpoint an earlier attempt of this call saved with {@link saveHeartbeatCheckpoint}, absent
|
|
378
|
+
* when none did. Use it to resume work that the earlier attempt started, such as polling a
|
|
379
|
+
* long-running job, instead of starting it again.
|
|
380
|
+
*/
|
|
381
|
+
readonly heartbeatCheckpoint?: string;
|
|
382
|
+
/**
|
|
383
|
+
* Records a heartbeat carrying a checkpoint. Every later heartbeat of the attempt, including the
|
|
384
|
+
* session's background heartbeats, carries it too, so the next attempt reads it as
|
|
385
|
+
* {@link heartbeatCheckpoint}. Absent when the caller cannot save checkpoints.
|
|
386
|
+
* @param checkpoint - Text to hand to the next attempt.
|
|
387
|
+
* @returns A Promise that resolves when the Worker accepted the heartbeat.
|
|
388
|
+
*/
|
|
389
|
+
saveHeartbeatCheckpoint?(checkpoint: string): Promise<void>;
|
|
390
|
+
}
|
|
391
|
+
/** Successful result of one deferred tool call. A result without `kind` is a `ToolExecutionResult`. */
|
|
392
|
+
export interface ToolExecutionResult {
|
|
393
|
+
/** Output text recorded on the call and shown to the model. */
|
|
394
|
+
readonly output: string;
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* Result of a deferred tool call that needs an answer from the user before it can take effect.
|
|
398
|
+
*
|
|
399
|
+
* Return it from {@link AgentFrameworkRunner.executeDeferredToolCall} only when the call has had no
|
|
400
|
+
* effect yet. The tool Step records `awaiting_answer` and stores {@link continuation}. `AwaitQuestionAnswer`
|
|
401
|
+
* then raises a `question` interrupt with ID `<toolCallId>-input-<n>`, where `n` counts the call's
|
|
402
|
+
* questions from 1. After `answer_question`, the record returns to `approved` and the same tool Step
|
|
403
|
+
* type runs the call again, with every answer in {@link DeferredToolExecution.inputAnswers} and the
|
|
404
|
+
* continuation in `toolCall.inputContinuation`. The tool's later output, not the answer, is the result.
|
|
405
|
+
* A declined question or a passed deadline records `rejected`, and the tool never runs again.
|
|
406
|
+
*
|
|
407
|
+
* Because the call took no effect, a single-attempt tool that runs again after an answer still never
|
|
408
|
+
* repeats an effect. A crash during the second run records `outcome_unknown`, as for any run.
|
|
409
|
+
*
|
|
410
|
+
* @example
|
|
411
|
+
* ```ts
|
|
412
|
+
* public async executeDeferredToolCall(call: DeferredToolExecution): Promise<DeferredToolOutcome> {
|
|
413
|
+
* const confirmation = call.inputAnswers?.at(-1)?.answer;
|
|
414
|
+
* if (confirmation === undefined) {
|
|
415
|
+
* return { kind: "tool_input_requested", question: "Ship to the saved address?", choices: ["yes", "no"],
|
|
416
|
+
* allowsFreeText: false, continuation: null };
|
|
417
|
+
* }
|
|
418
|
+
* return { output: confirmation === "yes" ? await shipOrder(call.idempotencyKey) : "not shipped" };
|
|
419
|
+
* }
|
|
420
|
+
* ```
|
|
421
|
+
*/
|
|
422
|
+
export interface ToolInputRequested {
|
|
423
|
+
/** Discriminator. */
|
|
424
|
+
readonly kind: "tool_input_requested";
|
|
425
|
+
/** Question for the user; it must not be blank. */
|
|
426
|
+
readonly question: string;
|
|
427
|
+
/** Answers to offer. Defaults to none, which makes the question free text. */
|
|
428
|
+
readonly choices?: readonly string[];
|
|
429
|
+
/** Whether an answer outside {@link choices} is accepted. Defaults to `true`; ignored without choices. */
|
|
430
|
+
readonly allowsFreeText?: boolean;
|
|
431
|
+
/** Opaque text returned to the tool with the answer, such as a server request ID, or `null`. */
|
|
432
|
+
readonly continuation: string | null;
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Result of a deferred tool call that started remote work and returned a handle to poll.
|
|
436
|
+
*
|
|
437
|
+
* Return it from {@link AgentFrameworkRunner.executeDeferredToolCall} when the tool's work runs
|
|
438
|
+
* elsewhere and outlives the call, such as an MCP task. The tool Step records `waiting_for_task` with
|
|
439
|
+
* {@link taskId} and the poll interval, writes `tool_task_started`, and ends. `PollToolTask` then waits
|
|
440
|
+
* on a Timer for the interval and calls {@link AgentFrameworkRunner.pollToolTask} once per execution, so
|
|
441
|
+
* no Step or connection is held while the work runs, and a Worker restart only delays the next poll. The
|
|
442
|
+
* runner must implement `pollToolTask`; without it the session records the call `failed` for a `read`
|
|
443
|
+
* tool and `outcome_unknown` otherwise.
|
|
444
|
+
*
|
|
445
|
+
* The tool Step commits the handle with its result. A `CallTool` attempt that dies after the remote work
|
|
446
|
+
* started runs again, so save the handle with {@link DeferredToolExecution.saveHeartbeatCheckpoint}
|
|
447
|
+
* before you return it, and return the saved handle when {@link DeferredToolExecution.heartbeatCheckpoint}
|
|
448
|
+
* holds one. Once the checkpoint has reached Dex, a retried attempt resumes the same work. Dex may drop a
|
|
449
|
+
* heartbeat checkpoint, though, so an attempt that dies before its checkpoint is saved and before the tool
|
|
450
|
+
* Step commits can still start the work a second time on a `CallTool` retry; pass the idempotency key to
|
|
451
|
+
* the remote system so it can drop the duplicate. Steering waits for a started task. `cancel_turn` abandons
|
|
452
|
+
* it: the session records `outcome_unknown` and asks {@link AgentFrameworkRunner.cancelToolTask} to cancel it.
|
|
453
|
+
*
|
|
454
|
+
* @example
|
|
455
|
+
* ```ts
|
|
456
|
+
* public async executeDeferredToolCall(call: DeferredToolExecution): Promise<DeferredToolOutcome> {
|
|
457
|
+
* if (call.heartbeatCheckpoint !== undefined) {
|
|
458
|
+
* return { kind: "tool_task_started", taskId: call.heartbeatCheckpoint, pollIntervalSeconds: 5 };
|
|
459
|
+
* }
|
|
460
|
+
* const jobId = await reports.start(call.idempotencyKey);
|
|
461
|
+
* await call.saveHeartbeatCheckpoint?.(jobId);
|
|
462
|
+
* return { kind: "tool_task_started", taskId: jobId, pollIntervalSeconds: 5 };
|
|
463
|
+
* }
|
|
464
|
+
* ```
|
|
465
|
+
*/
|
|
466
|
+
export interface ToolTaskStarted {
|
|
467
|
+
/** Discriminator. */
|
|
468
|
+
readonly kind: "tool_task_started";
|
|
469
|
+
/** Handle of the remote work; it must not be blank. The session stores it as the record's `taskId`. */
|
|
470
|
+
readonly taskId: string;
|
|
471
|
+
/**
|
|
472
|
+
* Seconds to wait before the first poll. The session rounds it up to whole seconds, at least 1,
|
|
473
|
+
* because Dex Timers take whole seconds, and shortens it to at most a day.
|
|
474
|
+
*/
|
|
475
|
+
readonly pollIntervalSeconds: number;
|
|
476
|
+
}
|
|
477
|
+
/** Result of {@link AgentFrameworkRunner.pollToolTask} while the polled task still runs. */
|
|
478
|
+
export interface ToolTaskPending {
|
|
479
|
+
/** Discriminator. */
|
|
480
|
+
readonly kind: "tool_task_pending";
|
|
481
|
+
/** Seconds to wait before the next poll. The session rounds it up to whole seconds, from 1 to a day. */
|
|
482
|
+
readonly pollIntervalSeconds: number;
|
|
483
|
+
/**
|
|
484
|
+
* Text the next poll reads as `toolCall.inputContinuation`, such as which question the task took an answer
|
|
485
|
+
* to and when. Omitted or `null` clears it: keep what the next poll needs, and drop a question's
|
|
486
|
+
* continuation once the task took its answer, so an identical later question reads as new.
|
|
487
|
+
*/
|
|
488
|
+
readonly continuation?: string | null;
|
|
489
|
+
}
|
|
490
|
+
/** One task that `PollToolTask` asks the runner to poll. */
|
|
491
|
+
export interface ToolTaskPoll {
|
|
492
|
+
/** Flow ID, which is the session ID. */
|
|
493
|
+
readonly flowId: string;
|
|
494
|
+
/** The call's record, `waiting_for_task`, with `taskId`, `taskPollIntervalSeconds`, and `toolSourceReference`. */
|
|
495
|
+
readonly toolCall: AgentToolCallRecord;
|
|
496
|
+
/** `<flowId>/<turnId>/<toolCallId>`, the key the tool Step passed when it started the task. */
|
|
497
|
+
readonly idempotencyKey: string;
|
|
498
|
+
/** One-based `PollToolTask` attempt. Each poll gets up to three attempts. */
|
|
499
|
+
readonly attempt: number;
|
|
500
|
+
/** How the questions this call asked were resolved, oldest first, whether its tool or its task asked them. */
|
|
501
|
+
readonly inputAnswers: readonly QuestionAnswer[];
|
|
502
|
+
/**
|
|
503
|
+
* The resolution of the task's last question, which has not reached the task yet, or `null`. It is set
|
|
504
|
+
* only on the first poll after `AwaitQuestionAnswer` resolved the question, which runs at once. Deliver it
|
|
505
|
+
* to the task: the answer when `resolution` is `answered`, a decline when it is `declined`, and a
|
|
506
|
+
* cancelled request when it is `deadline_passed`. `toolCall.inputContinuation` holds the question's
|
|
507
|
+
* continuation, so recognize the request it answers and deliver only to that request. Later polls read the
|
|
508
|
+
* continuation the last {@link ToolTaskPending} returned. A retried attempt gets it again; when it cannot
|
|
509
|
+
* tell whether an earlier attempt delivered it, return the task's question as {@link ToolInputRequested}
|
|
510
|
+
* again rather than send it to what may be a later question. When the task cannot take the resolution,
|
|
511
|
+
* cancel the task best-effort and throw an error other than {@link ToolExecutionError}, so the session
|
|
512
|
+
* records the call by its effect.
|
|
513
|
+
*/
|
|
514
|
+
readonly pendingAnswer: QuestionAnswer | null;
|
|
515
|
+
/** Events writer bound to the turn and attempt. */
|
|
516
|
+
readonly events: AgentEventWriter;
|
|
517
|
+
/** Signal aborted when Dex cancels the attempt. */
|
|
518
|
+
readonly cancellationSignal: AbortSignal;
|
|
519
|
+
/** Absolute deadline of the current attempt, or `undefined`. */
|
|
520
|
+
readonly deadline: Date | undefined;
|
|
521
|
+
/**
|
|
522
|
+
* Records a heartbeat. The session already records one every 20 seconds.
|
|
523
|
+
* @returns A Promise that resolves when the Worker accepted the heartbeat.
|
|
524
|
+
*/
|
|
525
|
+
heartbeat(): Promise<void>;
|
|
526
|
+
}
|
|
527
|
+
/** One task that `cancel_turn` abandoned, which `CancelToolTask` asks the runner to cancel. */
|
|
528
|
+
export interface ToolTaskCancellation {
|
|
529
|
+
/** Flow ID, which is the session ID. */
|
|
530
|
+
readonly flowId: string;
|
|
531
|
+
/**
|
|
532
|
+
* The call's record with `taskId` and `toolSourceReference`. Its `cancellationReason` is `turn_cancelled`.
|
|
533
|
+
*/
|
|
534
|
+
readonly toolCall: AgentToolCallRecord;
|
|
535
|
+
/** `<flowId>/<turnId>/<toolCallId>`, the key the tool Step passed when it started the task. */
|
|
536
|
+
readonly idempotencyKey: string;
|
|
537
|
+
/** One-based `CancelToolTask` attempt. Each cancellation gets up to three attempts. */
|
|
538
|
+
readonly attempt: number;
|
|
539
|
+
/** Signal aborted when Dex cancels the attempt, such as when its method timeout passes. */
|
|
540
|
+
readonly cancellationSignal: AbortSignal;
|
|
541
|
+
/**
|
|
542
|
+
* Records a heartbeat. The session already records one every 20 seconds.
|
|
543
|
+
* @returns A Promise that resolves when the Worker accepted the heartbeat.
|
|
544
|
+
*/
|
|
545
|
+
heartbeat(): Promise<void>;
|
|
546
|
+
}
|
|
547
|
+
/** Result of {@link AgentFrameworkRunner.executeDeferredToolCall}. */
|
|
548
|
+
export type DeferredToolOutcome = ToolExecutionResult | ToolInputRequested | ToolTaskStarted;
|
|
549
|
+
/** Result of {@link AgentFrameworkRunner.pollToolTask}. */
|
|
550
|
+
export type ToolTaskPollOutcome = ToolExecutionResult | ToolInputRequested | ToolTaskPending;
|
|
551
|
+
/**
|
|
552
|
+
* Tool failure that the model should see, thrown from {@link AgentFrameworkRunner.executeDeferredToolCall}.
|
|
553
|
+
*
|
|
554
|
+
* The session records the call as `failed` with this error's message and does not retry it. Throw any
|
|
555
|
+
* other error for an unexpected failure: `CallTool` retries it and, once its attempts run out, records
|
|
556
|
+
* `failed` for a `read` tool and `outcome_unknown` for a write; `CallSingleAttemptTool` records
|
|
557
|
+
* `outcome_unknown` at once, because the write may have taken effect.
|
|
558
|
+
*
|
|
559
|
+
* @example
|
|
560
|
+
* ```ts
|
|
561
|
+
* if (response.status === 404) {
|
|
562
|
+
* throw new ToolExecutionError(`order ${orderId} does not exist`);
|
|
563
|
+
* }
|
|
564
|
+
* ```
|
|
565
|
+
*/
|
|
566
|
+
export declare class ToolExecutionError extends Error {
|
|
567
|
+
/**
|
|
568
|
+
* Creates a tool failure.
|
|
569
|
+
* @param message - Error text recorded on the call and shown to the model.
|
|
570
|
+
* @param options - Standard error options, such as `cause`.
|
|
571
|
+
*/
|
|
572
|
+
constructor(message: string, options?: ErrorOptions);
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* Framework-specific logic that the shared agent session Flow calls.
|
|
576
|
+
*
|
|
577
|
+
* An adapter package implements one runner per framework and passes it to
|
|
578
|
+
* {@link createAgentSessionFlow}. The Flow owns durability; the runner only translates between
|
|
579
|
+
* the framework and the session values.
|
|
580
|
+
*
|
|
581
|
+
* Contract:
|
|
582
|
+
* - `runModelStep` makes at most one model call. It must stop before running any tool the framework
|
|
583
|
+
* can defer, and return every requested tool call in {@link ToolCallsRequested}. Each call then runs
|
|
584
|
+
* in its own Step: `CallTool` for `read` and `idempotent_write`, `CallSingleAttemptTool` for the
|
|
585
|
+
* other effects, after `AwaitToolApproval` when the policy requires approval.
|
|
586
|
+
* - When `frameworkRunState` is set, `runModelStep` restores the framework and gives it the recorded
|
|
587
|
+
* result of every call from the previous response before making the next model call.
|
|
588
|
+
* - A tool the framework cannot defer runs inside `runModelStep` and repeats when the model call
|
|
589
|
+
* reruns; the adapter documents such tools as at-least-once.
|
|
590
|
+
* - `runModelStep` and `executeDeferredToolCall` must not keep state between calls; a later call may
|
|
591
|
+
* run in another Worker process.
|
|
592
|
+
* - Throwing from `runModelStep` retries the model call, up to 3 attempts within 30 minutes; then the
|
|
593
|
+
* turn fails, the session records `last_turn_error`, and it waits for the next user message.
|
|
594
|
+
* - A runner may store the turn's transcript incrementally with {@link ToolCallsRequested.newMessages};
|
|
595
|
+
* it then implements {@link buildUnfinishedTurnMessages}. A runner may offer the
|
|
596
|
+
* {@link AgentTurnContext.enabledBuiltInTools} to its model; the session handles their calls.
|
|
597
|
+
*
|
|
598
|
+
* @example
|
|
599
|
+
* ```ts
|
|
600
|
+
* class EchoRunner implements AgentFrameworkRunner {
|
|
601
|
+
* public buildUserMessage(text: string): FrameworkMessage {
|
|
602
|
+
* return { role: "user", text, toolCallId: null, toolName: null, toolArguments: null,
|
|
603
|
+
* toolOutput: null, frameworkItem: JSON.stringify({ role: "user", content: text }) };
|
|
604
|
+
* }
|
|
605
|
+
* public async runModelStep(turn: AgentTurnContext): Promise<AgentTurnOutcome> {
|
|
606
|
+
* const last = turn.messages.at(-1)?.text ?? "";
|
|
607
|
+
* turn.events.writeTextDelta(last);
|
|
608
|
+
* return { kind: "completed", usage: emptyAgentUsage(), newMessages: [{ role: "assistant", text: last,
|
|
609
|
+
* toolCallId: null, toolName: null, toolArguments: null, toolOutput: null,
|
|
610
|
+
* frameworkItem: JSON.stringify({ role: "assistant", content: last }) }] };
|
|
611
|
+
* }
|
|
612
|
+
* public async executeDeferredToolCall(): Promise<ToolExecutionResult> {
|
|
613
|
+
* throw new ToolExecutionError("this agent has no tools");
|
|
614
|
+
* }
|
|
615
|
+
* }
|
|
616
|
+
* ```
|
|
617
|
+
* @typeParam ToolOutcome - What {@link executeDeferredToolCall} returns. It defaults to
|
|
618
|
+
* {@link ToolExecutionResult}; a runner whose tools can ask the user a question first or start a remote
|
|
619
|
+
* task implements `AgentFrameworkRunner<DeferredToolOutcome>`. The session accepts both.
|
|
620
|
+
*/
|
|
621
|
+
export interface AgentFrameworkRunner<ToolOutcome extends DeferredToolOutcome = ToolExecutionResult> {
|
|
622
|
+
/**
|
|
623
|
+
* Converts user text into the framework's user message.
|
|
624
|
+
*
|
|
625
|
+
* The session calls it when it consumes a user message and stores the result before `runModelStep`.
|
|
626
|
+
* It must be synchronous, deterministic, and free of side effects.
|
|
627
|
+
*
|
|
628
|
+
* @param text - User message text, never blank.
|
|
629
|
+
* @returns The framework message, with `role` `"user"`.
|
|
630
|
+
*/
|
|
631
|
+
buildUserMessage(text: string): FrameworkMessage;
|
|
632
|
+
/**
|
|
633
|
+
* Makes the turn's next model call, at most one.
|
|
634
|
+
*
|
|
635
|
+
* @param turn - Turn context; see {@link AgentTurnContext}.
|
|
636
|
+
* @returns {@link TurnCompleted} with every new message of the turn, or {@link ToolCallsRequested}
|
|
637
|
+
* with the framework run state and every tool call of the model response.
|
|
638
|
+
* @throws Any error to retry the model call; after the last attempt the turn fails.
|
|
639
|
+
*/
|
|
640
|
+
runModelStep(turn: AgentTurnContext): Promise<AgentTurnOutcome>;
|
|
641
|
+
/**
|
|
642
|
+
* Runs the real tool for one tool call, in `CallTool` or `CallSingleAttemptTool`.
|
|
643
|
+
*
|
|
644
|
+
* @param call - Deferred call to run.
|
|
645
|
+
* @returns The tool output text; {@link ToolInputRequested} when the tool needs an answer from the
|
|
646
|
+
* user before it takes any effect; or {@link ToolTaskStarted} when the tool started remote work that
|
|
647
|
+
* {@link pollToolTask} polls.
|
|
648
|
+
* @throws {ToolExecutionError} For a failure the model should see; the call is recorded as `failed`.
|
|
649
|
+
* @throws Any other error for an unexpected failure; after the last attempt the record becomes
|
|
650
|
+
* `failed` for a `read` tool and `outcome_unknown` for a write. See {@link ToolExecutionError}.
|
|
651
|
+
*/
|
|
652
|
+
executeDeferredToolCall(call: DeferredToolExecution): Promise<ToolOutcome>;
|
|
653
|
+
/**
|
|
654
|
+
* Returns the turn's messages that the session has not stored yet, when the turn ends without a final
|
|
655
|
+
* answer. Optional.
|
|
656
|
+
*
|
|
657
|
+
* Implement it when the runner sets {@link ToolCallsRequested.newMessages}. `RecordTurnFailure` calls
|
|
658
|
+
* it before it appends the turn-failure note, with every record of the turn final. Return the tool
|
|
659
|
+
* result of every tool call already stored or returned that has no stored result, so no tool call is
|
|
660
|
+
* left without a result. It must be synchronous and make no model call. When it throws, the session
|
|
661
|
+
* stores only the turn-failure note, so a runner defect never blocks the failure route.
|
|
662
|
+
*
|
|
663
|
+
* @param turn - The ended turn; {@link AgentTurnContext.toolCalls} holds its final records.
|
|
664
|
+
* @returns The messages to append, in order, possibly none.
|
|
665
|
+
*/
|
|
666
|
+
buildUnfinishedTurnMessages?(turn: AgentTurnContext): readonly FrameworkMessage[];
|
|
667
|
+
/**
|
|
668
|
+
* Whether the runner honors plan mode. Optional; {@link AgentSessionOptions.planModeEnabled} needs `true`,
|
|
669
|
+
* and `createAgentSessionFlow` throws otherwise.
|
|
670
|
+
*
|
|
671
|
+
* Such a runner reads {@link AgentTurnContext.interactionMode} in every `runModelStep`. In a `planning`
|
|
672
|
+
* turn it offers only the `write_todos` definition from {@link builtInToolDefinitions}. It may force
|
|
673
|
+
* `write_todos` on the turn's first model call while the plan is not a draft yet, where the model accepts a
|
|
674
|
+
* forced tool choice; Stepwise does with `planningToolChoice: "required"`. In every
|
|
675
|
+
* turn it shows the plan with {@link buildPlanNote} as user text before the turn's messages. The Stepwise
|
|
676
|
+
* runner implements it; a framework adapter that cannot change the tools it offers between turns leaves
|
|
677
|
+
* plan mode off.
|
|
678
|
+
*/
|
|
679
|
+
readonly supportsPlanMode?: boolean;
|
|
680
|
+
/**
|
|
681
|
+
* Polls one remote task that a tool started with {@link ToolTaskStarted}, once. Optional.
|
|
682
|
+
*
|
|
683
|
+
* Implement it when a tool returns `ToolTaskStarted`. `PollToolTask` calls it once per execution,
|
|
684
|
+
* after a Timer for the call's poll interval, until it returns a result. Polling must only read the
|
|
685
|
+
* task, and deliver `poll.pendingAnswer` when it is set, because the session may poll again after a
|
|
686
|
+
* crash. Steering never stops a poll. `cancel_turn` aborts `poll.cancellationSignal` of a running poll,
|
|
687
|
+
* and the session drops whatever it returns.
|
|
688
|
+
*
|
|
689
|
+
* @example
|
|
690
|
+
* ```ts
|
|
691
|
+
* public async pollToolTask(poll: ToolTaskPoll): Promise<ToolTaskPollOutcome> {
|
|
692
|
+
* const job = await reports.get(poll.toolCall.taskId!);
|
|
693
|
+
* return job.done ? { output: job.summary } : { kind: "tool_task_pending", pollIntervalSeconds: 10 };
|
|
694
|
+
* }
|
|
695
|
+
* ```
|
|
696
|
+
* @param poll - The call's record and its idempotency key.
|
|
697
|
+
* @returns {@link ToolExecutionResult} when the task finished, which the model sees;
|
|
698
|
+
* {@link ToolTaskPending} while it runs; or {@link ToolInputRequested} when it needs the user's answer.
|
|
699
|
+
* The session asks the question through `AwaitQuestionAnswer` and polls again at once with its
|
|
700
|
+
* resolution, answered, declined, or past its deadline, as `pendingAnswer`. The task's later result is
|
|
701
|
+
* the call's result.
|
|
702
|
+
* @throws {ToolExecutionError} When the task failed in a way the model should see; the call is
|
|
703
|
+
* recorded as `failed`.
|
|
704
|
+
* @throws Any other error to fail the poll attempt; after three attempts the record becomes `failed`
|
|
705
|
+
* for a `read` call and `outcome_unknown` otherwise, because the task may still run.
|
|
706
|
+
*/
|
|
707
|
+
pollToolTask?(poll: ToolTaskPoll): Promise<ToolTaskPollOutcome>;
|
|
708
|
+
/**
|
|
709
|
+
* Asks the remote system to stop one task that `cancel_turn` abandoned. Optional.
|
|
710
|
+
*
|
|
711
|
+
* `cancel_turn` does not wait for a started task: the session records the call `outcome_unknown` with
|
|
712
|
+
* `cancellationReason` `turn_cancelled`, and the `CancelToolTask` Step calls this method best-effort before
|
|
713
|
+
* it records the call. A runner without it leaves the task running. Steering never cancels a task. The
|
|
714
|
+
* session may call it more than once for one task, after a crash or a failed attempt, so make it idempotent
|
|
715
|
+
* by `taskId`. A task that already finished, or that the remote system no longer knows, is cancelled: return
|
|
716
|
+
* normally. The record is final whatever this method does, and a result the task returns later is dropped.
|
|
717
|
+
*
|
|
718
|
+
* @example
|
|
719
|
+
* ```ts
|
|
720
|
+
* public async cancelToolTask(cancellation: ToolTaskCancellation): Promise<void> {
|
|
721
|
+
* await reports.cancel(cancellation.toolCall.taskId!);
|
|
722
|
+
* }
|
|
723
|
+
* ```
|
|
724
|
+
* @param cancellation - The abandoned call's record and its idempotency key.
|
|
725
|
+
* @returns A Promise that resolves once the remote system took the cancellation.
|
|
726
|
+
* @throws Any error when the cancellation did not reach the remote system; the session retries it up to
|
|
727
|
+
* three times, then records the call `outcome_unknown` anyway, so the turn always ends.
|
|
728
|
+
*/
|
|
729
|
+
cancelToolTask?(cancellation: ToolTaskCancellation): Promise<void>;
|
|
730
|
+
/**
|
|
731
|
+
* Estimates the size of the model input that the next {@link runModelStep} sends. Optional; one of the
|
|
732
|
+
* three compaction methods that {@link AgentSessionOptions.compaction} requires.
|
|
733
|
+
*
|
|
734
|
+
* `RunModel` calls it before every model call of a session with a compaction policy. At the policy's
|
|
735
|
+
* threshold it calls {@link selectCompactionCutoff}, and when that returns a sequence after the current
|
|
736
|
+
* summary, `CompactContext` calls {@link summarizeContext} once and stores the result as the
|
|
737
|
+
* `ContextSummary`. From then on, {@link AgentTurnContext.messages} start after the summary and
|
|
738
|
+
* {@link AgentTurnContext.contextSummary} holds it. A runner without these methods never compacts.
|
|
739
|
+
*
|
|
740
|
+
* @example
|
|
741
|
+
* ```ts
|
|
742
|
+
* class CompactingRunner extends MyRunner {
|
|
743
|
+
* public estimateInputTokens(turn: AgentTurnContext): number {
|
|
744
|
+
* return countTokens(buildModelInput(turn));
|
|
745
|
+
* }
|
|
746
|
+
* public selectCompactionCutoff(turn: AgentTurnContext, retainedTokenBudget: number): number | null {
|
|
747
|
+
* return lastSequenceBeforeNewestMessages(turn.messages, retainedTokenBudget);
|
|
748
|
+
* }
|
|
749
|
+
* public async summarizeContext(request: ContextSummaryRequest): Promise<ContextSummaryResult> {
|
|
750
|
+
* const response = await model.summarize(request.previousSummary, request.messages);
|
|
751
|
+
* return { text: response.text, usage: response.usage };
|
|
752
|
+
* }
|
|
753
|
+
* }
|
|
754
|
+
* ```
|
|
755
|
+
* @param turn - The same context the next {@link runModelStep} receives.
|
|
756
|
+
* @returns The estimated input tokens, in the unit of `CompactionPolicy.contextWindowTokens`.
|
|
757
|
+
*/
|
|
758
|
+
estimateInputTokens?(turn: AgentTurnContext): number | Promise<number>;
|
|
759
|
+
/**
|
|
760
|
+
* Chooses the last message to summarize, so the newest messages fit a token budget. Optional; see
|
|
761
|
+
* {@link estimateInputTokens}.
|
|
762
|
+
*
|
|
763
|
+
* Never separate a tool call from its result: the messages after the cutoff must not start with a tool
|
|
764
|
+
* result or hold a result whose call is before the cutoff. Keep the turn's unstored tool results
|
|
765
|
+
* answerable.
|
|
766
|
+
*
|
|
767
|
+
* @param turn - The context of the model call that crossed the threshold.
|
|
768
|
+
* @param retainedTokenBudget - Tokens that the messages after the cutoff may use.
|
|
769
|
+
* @returns The sequence of the last message to summarize, one of {@link AgentTurnContext.messages}; or
|
|
770
|
+
* `null` when nothing can be summarized, which lets the model call proceed.
|
|
771
|
+
*/
|
|
772
|
+
selectCompactionCutoff?(turn: AgentTurnContext, retainedTokenBudget: number): number | null | Promise<number | null>;
|
|
773
|
+
/**
|
|
774
|
+
* Folds the previous summary and a range of messages into a new summary, with at most one model call.
|
|
775
|
+
* Optional; see {@link estimateInputTokens}. A crash during the call reruns only this call.
|
|
776
|
+
*
|
|
777
|
+
* @param request - The previous summary and the messages to summarize.
|
|
778
|
+
* @returns The new summary text and the usage of the call.
|
|
779
|
+
* @throws Any error to fail the `CompactContext` attempt; it retries up to 3 attempts within 30
|
|
780
|
+
* minutes, then the turn fails through `RecordTurnFailure`.
|
|
781
|
+
*/
|
|
782
|
+
summarizeContext?(request: ContextSummaryRequest): Promise<ContextSummaryResult>;
|
|
783
|
+
}
|
|
784
|
+
/** MCP tool annotations that classify a tool's side effects. */
|
|
785
|
+
export interface McpToolAnnotations {
|
|
786
|
+
/** MCP `readOnlyHint`: the tool does not modify its environment. */
|
|
787
|
+
readonly readOnlyHint?: boolean;
|
|
788
|
+
/** MCP `idempotentHint`: repeated calls with the same arguments have no additional effect. */
|
|
789
|
+
readonly idempotentHint?: boolean;
|
|
790
|
+
/** MCP `destructiveHint`: the tool may perform destructive updates. */
|
|
791
|
+
readonly destructiveHint?: boolean;
|
|
792
|
+
}
|
|
793
|
+
/**
|
|
794
|
+
* Maps MCP tool annotations to a framework effect for {@link AgentTurnContext.resolveToolPolicy}.
|
|
795
|
+
*
|
|
796
|
+
* Annotations are hints from the tool server. Pass the result only for servers the application
|
|
797
|
+
* trusts; otherwise leave the effect to policies and the session default.
|
|
798
|
+
*
|
|
799
|
+
* @example
|
|
800
|
+
* ```ts
|
|
801
|
+
* const policy = turn.resolveToolPolicy(tool.name, toolEffectFromMcpToolAnnotations(tool.annotations ?? {}));
|
|
802
|
+
* ```
|
|
803
|
+
* @param annotations - The tool's MCP annotations.
|
|
804
|
+
* @returns `read` when `readOnlyHint` is `true`; otherwise `idempotent_write` when `idempotentHint` is
|
|
805
|
+
* `true`, whatever `destructiveHint` says, as in the Python core; otherwise `undefined`, so the session
|
|
806
|
+
* default applies.
|
|
807
|
+
*/
|
|
808
|
+
export declare function toolEffectFromMcpToolAnnotations(annotations: McpToolAnnotations): ToolEffect | undefined;
|