@statelyai/agent 2.0.0-alpha.21 → 2.0.0-alpha.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/ai-sdk.cjs +53 -25
  2. package/dist/ai-sdk.d.cts +20 -12
  3. package/dist/ai-sdk.d.mts +20 -12
  4. package/dist/ai-sdk.mjs +53 -25
  5. package/dist/{decision-JWx6n3xR.mjs → decision-BBZ2jMO1.mjs} +542 -84
  6. package/dist/{decision-BnTCsuJv.cjs → decision-Cv6SfNWl.cjs} +629 -99
  7. package/dist/index.cjs +2225 -1032
  8. package/dist/index.d.cts +415 -276
  9. package/dist/index.d.mts +415 -276
  10. package/dist/index.mjs +2163 -983
  11. package/dist/machines.cjs +30 -2
  12. package/dist/machines.d.cts +151 -54
  13. package/dist/machines.d.mts +151 -54
  14. package/dist/machines.mjs +30 -2
  15. package/dist/openai.cjs +447 -0
  16. package/dist/openai.d.cts +177 -0
  17. package/dist/openai.d.mts +177 -0
  18. package/dist/openai.mjs +437 -0
  19. package/dist/otel.cjs +3 -6
  20. package/dist/otel.d.cts +1 -1
  21. package/dist/otel.d.mts +1 -1
  22. package/dist/otel.mjs +3 -6
  23. package/dist/run-agent-Ck7Sa3Tb.d.mts +968 -0
  24. package/dist/run-agent-DWqA8KiI.d.cts +968 -0
  25. package/dist/setup-agent-CeO2fARv.mjs +639 -0
  26. package/dist/setup-agent-Dl2VELLi.cjs +663 -0
  27. package/dist/{text-logic-Mmbgb2jy.d.cts → text-logic-C7bSnH_B.d.cts} +238 -44
  28. package/dist/{text-logic-BR5twXCv.d.mts → text-logic-DJBK3LbG.d.mts} +238 -44
  29. package/dist/{types-DSdj2tGs.d.cts → types-CjHk5OdH.d.cts} +4 -19
  30. package/dist/{types-CvWRGFxP.d.mts → types-DWA6X9R-.d.mts} +4 -19
  31. package/package.json +17 -21
  32. package/readme.md +32 -17
  33. package/dist/errors-BQRk9eiZ.d.cts +0 -19
  34. package/dist/errors-C9rxnWbX.d.mts +0 -19
  35. package/dist/errors-CeSXQx0v.mjs +0 -23
  36. package/dist/errors-DUBBzRLP.cjs +0 -28
  37. package/dist/event-log-store-Bz7HDBkE.d.cts +0 -145
  38. package/dist/event-log-store-DmIDosD6.mjs +0 -427
  39. package/dist/event-log-store-a_TKy1gk.cjs +0 -468
  40. package/dist/event-log-store-hrA1vqtN.d.mts +0 -145
  41. package/dist/run-agent-DBNI8ETM.d.mts +0 -1098
  42. package/dist/run-agent-Do094mGu.d.cts +0 -1098
  43. package/dist/setup-agent-91FSuZbB.cjs +0 -1641
  44. package/dist/setup-agent-DqqdcdNh.mjs +0 -1521
  45. package/dist/sqlite.cjs +0 -129
  46. package/dist/sqlite.d.cts +0 -57
  47. package/dist/sqlite.d.mts +0 -57
  48. package/dist/sqlite.mjs +0 -127
@@ -0,0 +1,177 @@
1
+ import { i as AgentToolChoice, n as AgentMessage, s as AgentTools, u as ChosenEvent } from "./types-DWA6X9R-.mjs";
2
+ import { F as AgentDecisionRequest, K as AgentEventDescriptor, c as AgentRequestExecutorInfo, f as AgentTextRequest, r as AgentFinishReason, t as AgentCallUsage } from "./text-logic-DJBK3LbG.mjs";
3
+ import OpenAI from "openai";
4
+ import { ChatCompletion, ChatCompletionCreateParamsNonStreaming, ChatCompletionMessageParam, ChatCompletionTool } from "openai/resources/chat/completions/completions.js";
5
+
6
+ //#region src/openai/index.d.ts
7
+ /** Maps `AgentTextRequest.messages`/`system`/`prompt` to OpenAI chat messages. */
8
+ declare function toOpenAiMessages(request: Pick<AgentTextRequest, "system" | "prompt"> & {
9
+ messages?: AgentMessage[];
10
+ }): ChatCompletionMessageParam[];
11
+ /**
12
+ * Maps the generation settings a text and a decision request share onto OpenAI
13
+ * Chat Completions parameters, with unset keys omitted.
14
+ *
15
+ * Omitting matters: these are the top layer of a stack — the host's
16
+ * {@link CreateOpenAiExecutorsOptions.settings}, then the request's — and an
17
+ * `undefined` spread over the layer below would erase it.
18
+ *
19
+ * `max_tokens` is deprecated by OpenAI in favor of `max_completion_tokens`, so
20
+ * that's what `maxOutputTokens` targets. Chat Completions has no `top_k`
21
+ * parameter, so `topK` is dropped.
22
+ */
23
+ declare function toOpenAiCallSettings(request: Pick<AgentTextRequest, "temperature" | "maxOutputTokens" | "topP" | "seed" | "stopSequences">): Partial<{
24
+ temperature: number | undefined;
25
+ max_completion_tokens: number | undefined;
26
+ top_p: number | undefined;
27
+ seed: number | undefined;
28
+ stop: string[] | undefined;
29
+ }>;
30
+ /** One OpenAI function tool per `AgentTools` entry. */
31
+ declare function toOpenAiTools(tools: AgentTools): ChatCompletionTool[];
32
+ /**
33
+ * Maps an {@link AgentToolChoice} onto OpenAI's `tool_choice` —
34
+ * `{ type: 'tool', name }` becomes `{ type: 'function', function: { name } }`;
35
+ * `'auto'`/`'none'`/`'required'` are OpenAI's own vocabulary and pass through.
36
+ * `undefined` stays `undefined`, so the key is omitted.
37
+ */
38
+ declare function toOpenAiToolChoice(toolChoice: AgentToolChoice | undefined): "auto" | "none" | "required" | {
39
+ type: "function";
40
+ function: {
41
+ name: string;
42
+ };
43
+ } | undefined;
44
+ /** One OpenAI function tool per candidate decision event — the "tool-per-event
45
+ * + tool_choice: 'required'" recipe, mirroring `toAiSdkEventTools`. */
46
+ declare function toOpenAiEventTools(events: AgentEventDescriptor[]): ChatCompletionTool[];
47
+ /**
48
+ * Messages for a decision request, with prior failed `attempts` rendered as an
49
+ * appended user message so retries converge. Mirrors the AI SDK adapter's
50
+ * `toDecisionMessages`.
51
+ */
52
+ declare function toDecisionMessages(request: Pick<AgentDecisionRequest, "messages" | "prompt" | "events" | "attempts">): ChatCompletionMessageParam[];
53
+ type OpenAiUsage = NonNullable<ChatCompletion["usage"]>;
54
+ /**
55
+ * Folds OpenAI's usage payload onto the flat {@link AgentCallUsage} field names
56
+ * core aggregates. OpenAI nests the reasoning count under
57
+ * `completion_tokens_details` and the cache-read count under
58
+ * `prompt_tokens_details`; a field the response omitted stays omitted, so it
59
+ * never contributes a `0` to a run's partial sums.
60
+ */
61
+ declare function toAgentCallUsage(usage: OpenAiUsage | undefined): AgentCallUsage | undefined;
62
+ /**
63
+ * Maps an OpenAI `finish_reason` onto the portable {@link AgentFinishReason}.
64
+ * `'function_call'` (the deprecated legacy path) joins `'tool_calls'` under
65
+ * `'tool-calls'`; anything unrecognized, including a missing reason, lands on
66
+ * `'other'`. OpenAI's own string stays on the result's `raw`.
67
+ */
68
+ declare function toAgentFinishReason(finishReason: ChatCompletion.Choice["finish_reason"] | null | undefined): AgentFinishReason;
69
+ /**
70
+ * Per-call OpenAI Chat Completions parameters a host can apply on top of what
71
+ * the machine asked for — `reasoning_effort`, `service_tier`,
72
+ * `max_completion_tokens`, and anything else the API accepts. Typed straight
73
+ * off the installed `openai` package, so it tracks that version instead of
74
+ * restating its vocabulary.
75
+ *
76
+ * The request's own fields are excluded: `model`, `messages`, `tools`,
77
+ * `tool_choice`, `response_format`, `n`, and the streaming switches belong to
78
+ * the machine and the adapter, and a host override would silently contradict
79
+ * them.
80
+ */
81
+ type OpenAiCallSettings = Omit<Partial<ChatCompletionCreateParamsNonStreaming>, "model" | "messages" | "tools" | "tool_choice" | "response_format" | "n" | "stream">;
82
+ /**
83
+ * Options for {@link createOpenAiExecutors}.
84
+ *
85
+ * `client` is the injected `OpenAI` instance — this adapter never constructs
86
+ * one, so the API key, base URL, and transport stay the host's business.
87
+ *
88
+ * `resolveModel` maps a machine's model *ref* (e.g. `'ticketTriage'`) to a real
89
+ * OpenAI model id. A machine's requests carry refs, not ids, so a raw-SDK host
90
+ * must resolve them (the AI SDK adapter does this via its `models` map).
91
+ * Defaults to identity, for machines that already use real ids.
92
+ *
93
+ * `settings` carries provider knobs that are the HOST's business rather than
94
+ * the machine's, such as reasoning effort. Pass a map keyed by model ref to
95
+ * give a ref a persona — `deep` thinks harder than `quick` — without the
96
+ * machine naming a provider knob, or a function to vary settings per request
97
+ * (`request.name` is the request's registered key; return `undefined` for the
98
+ * defaults). Settings are merged UNDER what the request declared, so a machine
99
+ * that set `maxOutputTokens` still wins.
100
+ */
101
+ type CreateOpenAiExecutorsOptions = {
102
+ client: OpenAI;
103
+ resolveModel?: (modelRef: string) => string;
104
+ settings?: Record<string, OpenAiCallSettings> | ((request: AgentTextRequest | AgentDecisionRequest) => OpenAiCallSettings | undefined);
105
+ };
106
+ /**
107
+ * Raw result shape from {@link OpenAiExecutors.generateText} — the `{ output }`
108
+ * envelope (the validated structured object for structured-output requests, or
109
+ * the model's text otherwise) plus the call metadata. Core only reads `output`; everything else flows
110
+ * verbatim to `runAgent`'s `onResult(request, { raw })`.
111
+ */
112
+ type OpenAiGenerateResult = {
113
+ output: unknown;
114
+ /** The model's reasoning, present only when the request opted in via
115
+ * `includeReasoning` and the model produced it. Never enters machine
116
+ * context/output. */
117
+ reasoning?: string;
118
+ /** The call's token usage, on the flat field names `runAgent` folds into the
119
+ * run result's aggregated `AgentUsage`. Absent when OpenAI reported none. */
120
+ usage?: AgentCallUsage; /** Why the call stopped, normalized to the portable {@link AgentFinishReason}. OpenAI's own value stays on `raw`. */
121
+ finishReason: AgentFinishReason; /** The untouched OpenAI response. */
122
+ raw: unknown;
123
+ };
124
+ /** Raw result shape from {@link OpenAiExecutors.streamText} — the accumulated text once the stream finishes (chunks are delivered separately via `onChunk`), plus the stream's final usage/finish metadata. */
125
+ type OpenAiStreamResult = {
126
+ output: string; /** Present when the stream carried a usage chunk (the adapter asks for one via `stream_options`). */
127
+ usage?: AgentCallUsage;
128
+ finishReason: AgentFinishReason; /** The last chunk seen, which carries the stream's finish metadata. */
129
+ raw: unknown;
130
+ };
131
+ /** Raw result shape from {@link OpenAiExecutors.decide} — the chosen event plus the call metadata, delivered per decision attempt to `onResult`. */
132
+ type OpenAiDecideResult = {
133
+ event: ChosenEvent;
134
+ usage?: AgentCallUsage;
135
+ finishReason: AgentFinishReason;
136
+ raw: unknown;
137
+ };
138
+ /** `createOpenAiExecutors` always populates all three slots (unlike the general
139
+ * `AgentRequestExecutors`, where `streamText`/`decide` are optional), and its
140
+ * results are concretely typed. */
141
+ interface OpenAiExecutors {
142
+ generateText: (request: AgentTextRequest & {
143
+ tools: AgentTools;
144
+ }, info?: AgentRequestExecutorInfo) => Promise<OpenAiGenerateResult>;
145
+ streamText: (request: AgentTextRequest & {
146
+ tools: AgentTools;
147
+ }, info?: AgentRequestExecutorInfo) => Promise<OpenAiStreamResult>;
148
+ decide: (request: AgentDecisionRequest, info?: AgentRequestExecutorInfo) => Promise<OpenAiDecideResult>;
149
+ }
150
+ /**
151
+ * Adds two calls' usage together, field by field. A tool loop makes several
152
+ * OpenAI calls per request, and core folds ONE `usage` per executor result into
153
+ * the run's totals, so the steps' usages have to be summed here or the run
154
+ * undercounts. A field neither call reported stays absent.
155
+ */
156
+ declare function addAgentCallUsage(total: AgentCallUsage | undefined, next: AgentCallUsage | undefined): AgentCallUsage | undefined;
157
+ /**
158
+ * Builds the `{ generateText, streamText, decide }` executor set for the raw
159
+ * `openai` package's Chat Completions API. Compare `createAiSdkExecutors` —
160
+ * same shape, different SDK underneath.
161
+ *
162
+ * Structured output uses `response_format: { type: 'json_schema' }` around the
163
+ * uniform `{ result, reasoning? }` envelope, and decisions force a tool call
164
+ * with `tool_choice: 'required'`, one function tool per candidate event.
165
+ *
166
+ * @example
167
+ * ```ts
168
+ * const executors = createOpenAiExecutors({
169
+ * client: new OpenAI(),
170
+ * resolveModel: () => 'gpt-5.4-mini',
171
+ * });
172
+ * const result = await runAgent(machine, { input, executors });
173
+ * ```
174
+ */
175
+ declare function createOpenAiExecutors(options: CreateOpenAiExecutorsOptions): OpenAiExecutors;
176
+ //#endregion
177
+ export { CreateOpenAiExecutorsOptions, OpenAiCallSettings, OpenAiDecideResult, OpenAiExecutors, OpenAiGenerateResult, OpenAiStreamResult, addAgentCallUsage, createOpenAiExecutors, toAgentCallUsage, toAgentFinishReason, toDecisionMessages, toOpenAiCallSettings, toOpenAiEventTools, toOpenAiMessages, toOpenAiToolChoice, toOpenAiTools };
@@ -0,0 +1,437 @@
1
+ import { S as getAgentOutputMode, U as getJsonSchema, W as getJsonSchemaSync, Y as isStandardSchema, f as AGENT_USAGE_TOKEN_FIELDS, k as parseStructuredEnvelope, o as renderDecisionAttempts, st as AgentTruncatedError, v as buildEnvelopeSchema } from "./decision-BBZ2jMO1.mjs";
2
+ //#region src/openai/index.ts
3
+ /** Thrown for a message part Chat Completions cannot carry — dropping it would
4
+ * silently change what the model sees. */
5
+ function unsupportedPart(role, type) {
6
+ throw new Error(`createOpenAiExecutors: a ${role} message part of type '${type}' has no Chat Completions equivalent. Send text, image, tool-call, or tool-result parts, or convert it before the request.`);
7
+ }
8
+ /** A `DataContent`/`URL` image as the URL string OpenAI's `image_url` part wants:
9
+ * an http(s) or `data:` string and a `URL` pass through, a bare base64 string and
10
+ * raw bytes are wrapped in a data URL. */
11
+ function toImageUrl(image, mediaType) {
12
+ if (image instanceof URL) return image.href;
13
+ const type = mediaType ?? "image/jpeg";
14
+ if (typeof image === "string") return /^(https?:|data:)/.test(image) ? image : `data:${type};base64,${image}`;
15
+ const bytes = image instanceof Uint8Array ? image : new Uint8Array(image);
16
+ const encode = globalThis.btoa;
17
+ if (!encode) throw new Error("createOpenAiExecutors: binary image parts need a global `btoa` to base64-encode. Pass the image as a URL or a base64 string instead.");
18
+ let binary = "";
19
+ for (const byte of bytes) binary += String.fromCharCode(byte);
20
+ return `data:${type};base64,${encode(binary)}`;
21
+ }
22
+ /** One OpenAI tool message per `ToolResultPart` — the tool role is one message per result. */
23
+ function toToolMessage(part) {
24
+ return {
25
+ role: "tool",
26
+ content: part.output.type === "text" || part.output.type === "error-text" ? part.output.value : JSON.stringify(part.output.value),
27
+ tool_call_id: part.toolCallId
28
+ };
29
+ }
30
+ /** Maps `AgentTextRequest.messages`/`system`/`prompt` to OpenAI chat messages. */
31
+ function toOpenAiMessages(request) {
32
+ if (request.messages) return request.messages.flatMap((message) => {
33
+ switch (message.role) {
34
+ case "system": return [{
35
+ role: "system",
36
+ content: message.content
37
+ }];
38
+ case "user":
39
+ if (typeof message.content === "string") return [{
40
+ role: "user",
41
+ content: message.content
42
+ }];
43
+ return [{
44
+ role: "user",
45
+ content: message.content.map((part) => {
46
+ switch (part.type) {
47
+ case "text": return {
48
+ type: "text",
49
+ text: part.text
50
+ };
51
+ case "image": return {
52
+ type: "image_url",
53
+ image_url: { url: toImageUrl(part.image, part.mediaType) }
54
+ };
55
+ default: return unsupportedPart("user", part.type);
56
+ }
57
+ })
58
+ }];
59
+ case "assistant": {
60
+ if (typeof message.content === "string") return [{
61
+ role: "assistant",
62
+ content: message.content
63
+ }];
64
+ let text = "";
65
+ const toolCalls = [];
66
+ const trailing = [];
67
+ for (const part of message.content) switch (part.type) {
68
+ case "text":
69
+ text += part.text;
70
+ break;
71
+ case "tool-call":
72
+ toolCalls.push({
73
+ id: part.toolCallId,
74
+ type: "function",
75
+ function: {
76
+ name: part.toolName,
77
+ arguments: JSON.stringify(part.input ?? {})
78
+ }
79
+ });
80
+ break;
81
+ case "tool-result":
82
+ trailing.push(toToolMessage(part));
83
+ break;
84
+ default: unsupportedPart("assistant", part.type);
85
+ }
86
+ return [{
87
+ role: "assistant",
88
+ content: text,
89
+ ...toolCalls.length > 0 ? { tool_calls: toolCalls } : {}
90
+ }, ...trailing];
91
+ }
92
+ case "tool": return message.content.map(toToolMessage);
93
+ }
94
+ });
95
+ const messages = [];
96
+ if (request.system) messages.push({
97
+ role: "system",
98
+ content: request.system
99
+ });
100
+ messages.push({
101
+ role: "user",
102
+ content: request.prompt ?? ""
103
+ });
104
+ return messages;
105
+ }
106
+ /** Drops keys whose value is `undefined`, so a spread cannot erase what it lands on. */
107
+ function defined(settings) {
108
+ return Object.fromEntries(Object.entries(settings).filter(([, value]) => value !== void 0));
109
+ }
110
+ /**
111
+ * Maps the generation settings a text and a decision request share onto OpenAI
112
+ * Chat Completions parameters, with unset keys omitted.
113
+ *
114
+ * Omitting matters: these are the top layer of a stack — the host's
115
+ * {@link CreateOpenAiExecutorsOptions.settings}, then the request's — and an
116
+ * `undefined` spread over the layer below would erase it.
117
+ *
118
+ * `max_tokens` is deprecated by OpenAI in favor of `max_completion_tokens`, so
119
+ * that's what `maxOutputTokens` targets. Chat Completions has no `top_k`
120
+ * parameter, so `topK` is dropped.
121
+ */
122
+ function toOpenAiCallSettings(request) {
123
+ return defined({
124
+ temperature: request.temperature,
125
+ max_completion_tokens: request.maxOutputTokens,
126
+ top_p: request.topP,
127
+ seed: request.seed,
128
+ stop: request.stopSequences
129
+ });
130
+ }
131
+ /** A tool `description` may be a string or, for an AI SDK v7 tool, a function
132
+ * of the call's context. A raw JSON payload can only carry the static form. */
133
+ function staticDescription(descriptor) {
134
+ if (typeof descriptor === "function") return;
135
+ return typeof descriptor.description === "string" ? descriptor.description : void 0;
136
+ }
137
+ /** One OpenAI function tool per `AgentTools` entry. */
138
+ function toOpenAiTools(tools) {
139
+ return Object.entries(tools).flatMap(([name, descriptor]) => {
140
+ if (!descriptor) return [];
141
+ const inputSchema = typeof descriptor === "function" ? void 0 : descriptor.inputSchema;
142
+ return [{
143
+ type: "function",
144
+ function: {
145
+ name,
146
+ description: staticDescription(descriptor),
147
+ parameters: (isStandardSchema(inputSchema) ? getJsonSchemaSync(inputSchema) : void 0) ?? {}
148
+ }
149
+ }];
150
+ });
151
+ }
152
+ /**
153
+ * Maps an {@link AgentToolChoice} onto OpenAI's `tool_choice` —
154
+ * `{ type: 'tool', name }` becomes `{ type: 'function', function: { name } }`;
155
+ * `'auto'`/`'none'`/`'required'` are OpenAI's own vocabulary and pass through.
156
+ * `undefined` stays `undefined`, so the key is omitted.
157
+ */
158
+ function toOpenAiToolChoice(toolChoice) {
159
+ if (toolChoice === void 0) return;
160
+ return typeof toolChoice === "object" ? {
161
+ type: "function",
162
+ function: { name: toolChoice.name }
163
+ } : toolChoice;
164
+ }
165
+ /** One OpenAI function tool per candidate decision event — the "tool-per-event
166
+ * + tool_choice: 'required'" recipe, mirroring `toAiSdkEventTools`. */
167
+ function toOpenAiEventTools(events) {
168
+ return events.map((event) => ({
169
+ type: "function",
170
+ function: {
171
+ name: event.toolName,
172
+ description: `Choose the '${event.type}' move.`,
173
+ parameters: getJsonSchemaSync(event.inputSchema) ?? {}
174
+ }
175
+ }));
176
+ }
177
+ /**
178
+ * Messages for a decision request, with prior failed `attempts` rendered as an
179
+ * appended user message so retries converge. Mirrors the AI SDK adapter's
180
+ * `toDecisionMessages`.
181
+ */
182
+ function toDecisionMessages(request) {
183
+ const messages = toOpenAiMessages(request);
184
+ for (const attempt of renderDecisionAttempts(request)) messages.push({
185
+ role: "user",
186
+ content: attempt.content
187
+ });
188
+ return messages;
189
+ }
190
+ /**
191
+ * Folds OpenAI's usage payload onto the flat {@link AgentCallUsage} field names
192
+ * core aggregates. OpenAI nests the reasoning count under
193
+ * `completion_tokens_details` and the cache-read count under
194
+ * `prompt_tokens_details`; a field the response omitted stays omitted, so it
195
+ * never contributes a `0` to a run's partial sums.
196
+ */
197
+ function toAgentCallUsage(usage) {
198
+ if (!usage) return;
199
+ const reasoningTokens = usage.completion_tokens_details?.reasoning_tokens;
200
+ const cachedInputTokens = usage.prompt_tokens_details?.cached_tokens;
201
+ return {
202
+ ...usage.prompt_tokens !== void 0 ? { inputTokens: usage.prompt_tokens } : {},
203
+ ...usage.completion_tokens !== void 0 ? { outputTokens: usage.completion_tokens } : {},
204
+ ...usage.total_tokens !== void 0 ? { totalTokens: usage.total_tokens } : {},
205
+ ...reasoningTokens !== void 0 ? { reasoningTokens } : {},
206
+ ...cachedInputTokens !== void 0 ? { cachedInputTokens } : {}
207
+ };
208
+ }
209
+ /**
210
+ * Maps an OpenAI `finish_reason` onto the portable {@link AgentFinishReason}.
211
+ * `'function_call'` (the deprecated legacy path) joins `'tool_calls'` under
212
+ * `'tool-calls'`; anything unrecognized, including a missing reason, lands on
213
+ * `'other'`. OpenAI's own string stays on the result's `raw`.
214
+ */
215
+ function toAgentFinishReason(finishReason) {
216
+ switch (finishReason) {
217
+ case "stop": return "stop";
218
+ case "length": return "length";
219
+ case "tool_calls":
220
+ case "function_call": return "tool-calls";
221
+ case "content_filter": return "content-filter";
222
+ default: return "other";
223
+ }
224
+ }
225
+ /** Resolves the host's `settings` option for one request. */
226
+ function callSettings(options, request) {
227
+ const settings = options.settings;
228
+ if (typeof settings === "function") return settings(request) ?? {};
229
+ const entry = settings?.[request.model];
230
+ if (entry === void 0) return {};
231
+ if (typeof entry !== "object" || entry === null) throw new Error(`createOpenAiExecutors: \`settings\` is keyed by model ref, so every value must be an object of OpenAI parameters, but '${request.model}' is a ${typeof entry}. Pass a function if you want one set of settings for every call.`);
232
+ return entry;
233
+ }
234
+ function truncated(request, info, partialOutput, cause) {
235
+ return new AgentTruncatedError(`createOpenAiExecutors: request '${request.name ?? "(unnamed)"}' hit the output token limit — its structured output never completed. Raise \`maxOutputTokens\`, shorten the input, or ask for a smaller output schema.`, {
236
+ requestName: request.name ?? "(unnamed)",
237
+ ...info?.requestId !== void 0 ? { requestId: info.requestId } : {},
238
+ ...partialOutput !== void 0 ? { partialOutput } : {},
239
+ ...cause !== void 0 ? { cause } : {}
240
+ });
241
+ }
242
+ function usageField(usage) {
243
+ return usage ? { usage } : {};
244
+ }
245
+ /**
246
+ * Adds two calls' usage together, field by field. A tool loop makes several
247
+ * OpenAI calls per request, and core folds ONE `usage` per executor result into
248
+ * the run's totals, so the steps' usages have to be summed here or the run
249
+ * undercounts. A field neither call reported stays absent.
250
+ */
251
+ function addAgentCallUsage(total, next) {
252
+ if (!total || !next) return total ?? next;
253
+ const sum = {};
254
+ for (const field of AGENT_USAGE_TOKEN_FIELDS) if (total[field] !== void 0 || next[field] !== void 0) sum[field] = (total[field] ?? 0) + (next[field] ?? 0);
255
+ return sum;
256
+ }
257
+ /**
258
+ * The function that runs one of the request's tools host-side, or `undefined`
259
+ * when the tool has no `execute` (a client-side tool: the model's call is
260
+ * handed back to the caller instead of being answered here).
261
+ */
262
+ function toolExecutor(tools, name) {
263
+ const descriptor = tools[name];
264
+ if (!descriptor) return;
265
+ if (typeof descriptor === "function") return (input) => descriptor(input);
266
+ const execute = descriptor.execute;
267
+ return typeof execute === "function" ? (input) => execute(input) : void 0;
268
+ }
269
+ function toToolContent(output) {
270
+ return typeof output === "string" ? output : JSON.stringify(output ?? null);
271
+ }
272
+ /**
273
+ * Builds the `{ generateText, streamText, decide }` executor set for the raw
274
+ * `openai` package's Chat Completions API. Compare `createAiSdkExecutors` —
275
+ * same shape, different SDK underneath.
276
+ *
277
+ * Structured output uses `response_format: { type: 'json_schema' }` around the
278
+ * uniform `{ result, reasoning? }` envelope, and decisions force a tool call
279
+ * with `tool_choice: 'required'`, one function tool per candidate event.
280
+ *
281
+ * @example
282
+ * ```ts
283
+ * const executors = createOpenAiExecutors({
284
+ * client: new OpenAI(),
285
+ * resolveModel: () => 'gpt-5.4-mini',
286
+ * });
287
+ * const result = await runAgent(machine, { input, executors });
288
+ * ```
289
+ */
290
+ function createOpenAiExecutors(options) {
291
+ const { client, resolveModel = (modelRef) => modelRef } = options;
292
+ const textParams = (request, messages, extra = {}) => {
293
+ const tools = toOpenAiTools(request.tools);
294
+ return {
295
+ model: resolveModel(request.model),
296
+ messages,
297
+ ...callSettings(options, request),
298
+ ...toOpenAiCallSettings(request),
299
+ ...tools.length > 0 ? { tools } : {},
300
+ ...extra
301
+ };
302
+ };
303
+ const envelopeSchema = async (request) => {
304
+ if (getAgentOutputMode(request.outputSchema) !== "structured") return;
305
+ return getJsonSchema(buildEnvelopeSchema(request.outputSchema, { reasoning: request.includeReasoning }));
306
+ };
307
+ const generateText = async (request, info) => {
308
+ const jsonSchema = await envelopeSchema(request);
309
+ const responseFormat = jsonSchema ? { response_format: {
310
+ type: "json_schema",
311
+ json_schema: {
312
+ name: "output",
313
+ schema: jsonSchema,
314
+ strict: false
315
+ }
316
+ } } : {};
317
+ const maxSteps = typeof request.maxSteps === "number" && Number.isFinite(request.maxSteps) ? Math.max(1, Math.floor(request.maxSteps)) : 1;
318
+ const messages = toOpenAiMessages(request);
319
+ const toolChoice = toOpenAiToolChoice(request.toolChoice);
320
+ let response;
321
+ let usage;
322
+ let step = 0;
323
+ for (;;) {
324
+ step++;
325
+ response = await client.chat.completions.create(textParams(request, messages, {
326
+ ...responseFormat,
327
+ ...step === 1 && toolChoice !== void 0 ? { tool_choice: toolChoice } : {}
328
+ }), { signal: info?.signal });
329
+ usage = addAgentCallUsage(usage, toAgentCallUsage(response.usage));
330
+ const message = response.choices[0]?.message;
331
+ const toolCalls = message?.tool_calls ?? [];
332
+ if (toolCalls.length === 0 || step >= maxSteps) break;
333
+ const executors = toolCalls.map((call) => call.type === "function" ? toolExecutor(request.tools, call.function.name) : void 0);
334
+ if (executors.some((execute) => !execute)) break;
335
+ messages.push(message);
336
+ for (const [index, call] of toolCalls.entries()) {
337
+ const input = call.type === "function" && call.function.arguments ? JSON.parse(call.function.arguments) : {};
338
+ let content;
339
+ try {
340
+ content = toToolContent(await executors[index](input));
341
+ } catch (error) {
342
+ content = `Error: ${error instanceof Error ? error.message : String(error)}`;
343
+ }
344
+ messages.push({
345
+ role: "tool",
346
+ tool_call_id: call.id,
347
+ content
348
+ });
349
+ }
350
+ }
351
+ const unwrap = (content) => parseStructuredEnvelope(request, content ? JSON.parse(content) : void 0);
352
+ const choice = response.choices[0];
353
+ const finishReason = toAgentFinishReason(choice?.finish_reason);
354
+ const content = choice?.message.content;
355
+ if (jsonSchema) {
356
+ if (finishReason === "length") throw truncated(request, info, content ?? void 0);
357
+ const parsed = unwrap(content);
358
+ return {
359
+ output: parsed.result,
360
+ ...typeof parsed.reasoning === "string" ? { reasoning: parsed.reasoning } : {},
361
+ ...usageField(usage),
362
+ finishReason,
363
+ raw: response
364
+ };
365
+ }
366
+ return {
367
+ output: content ?? "",
368
+ ...usageField(usage),
369
+ finishReason,
370
+ raw: response
371
+ };
372
+ };
373
+ const streamText = async (request, info) => {
374
+ if (getAgentOutputMode(request.outputSchema) === "structured") throw new Error("createOpenAiExecutors: streamText is text-only — a request declaring a structured output schema must be routed to generateText.");
375
+ if (Object.keys(request.tools ?? {}).length > 0) throw new Error("createOpenAiExecutors: streamText is text-only — a request declaring tools must be routed to generateText.");
376
+ const stream = await client.chat.completions.create({
377
+ model: resolveModel(request.model),
378
+ messages: toOpenAiMessages(request),
379
+ ...callSettings(options, request),
380
+ ...toOpenAiCallSettings(request),
381
+ stream: true,
382
+ stream_options: { include_usage: true }
383
+ }, { signal: info?.signal });
384
+ let text = "";
385
+ let finishReason = "other";
386
+ let usage;
387
+ let last;
388
+ for await (const chunk of stream) {
389
+ last = chunk;
390
+ const choice = chunk.choices[0];
391
+ const delta = choice?.delta.content;
392
+ if (delta) {
393
+ text += delta;
394
+ info?.onChunk?.(delta);
395
+ }
396
+ if (choice?.finish_reason) finishReason = toAgentFinishReason(choice.finish_reason);
397
+ if (chunk.usage) usage = chunk.usage;
398
+ }
399
+ return {
400
+ output: text,
401
+ ...usageField(toAgentCallUsage(usage)),
402
+ finishReason,
403
+ raw: last
404
+ };
405
+ };
406
+ const decide = async (request, info) => {
407
+ const response = await client.chat.completions.create({
408
+ model: resolveModel(request.model),
409
+ messages: toDecisionMessages(request),
410
+ ...callSettings(options, request),
411
+ ...toOpenAiCallSettings(request),
412
+ tools: toOpenAiEventTools(request.events),
413
+ tool_choice: "required"
414
+ }, { signal: info?.signal });
415
+ const toolCall = response.choices[0]?.message.tool_calls?.[0];
416
+ if (!toolCall || toolCall.type !== "function") throw new Error("createOpenAiExecutors: decide — model did not call an event tool.");
417
+ const chosenEvent = request.events.find((event) => event.toolName === toolCall.function.name);
418
+ if (!chosenEvent) throw new Error(`createOpenAiExecutors: decide — model called unknown tool '${toolCall.function.name}'.`);
419
+ const args = toolCall.function.arguments ? JSON.parse(toolCall.function.arguments) : {};
420
+ return {
421
+ event: {
422
+ ...args && typeof args === "object" ? args : {},
423
+ type: chosenEvent.type
424
+ },
425
+ ...usageField(toAgentCallUsage(response.usage)),
426
+ finishReason: toAgentFinishReason(response.choices[0]?.finish_reason),
427
+ raw: response
428
+ };
429
+ };
430
+ return {
431
+ generateText,
432
+ streamText,
433
+ decide
434
+ };
435
+ }
436
+ //#endregion
437
+ export { addAgentCallUsage, createOpenAiExecutors, toAgentCallUsage, toAgentFinishReason, toDecisionMessages, toOpenAiCallSettings, toOpenAiEventTools, toOpenAiMessages, toOpenAiToolChoice, toOpenAiTools };
package/dist/otel.cjs CHANGED
@@ -210,15 +210,12 @@ function createOtelTraceHandler(options) {
210
210
  });
211
211
  });
212
212
  break;
213
- case "machine.transition": {
214
- const attributes = {
213
+ case "machine.transition":
214
+ addRunEvent(event, "agent.transition", {
215
215
  "agent.event_type": event.event.type,
216
216
  "agent.state": toJson(event.snapshot.value) ?? ""
217
- };
218
- if (event.eventId !== void 0) attributes["agent.event_id"] = event.eventId;
219
- addRunEvent(event, "agent.transition", attributes);
217
+ });
220
218
  break;
221
- }
222
219
  case "emit":
223
220
  addRunEvent(event, "agent.emit", { "agent.event_type": event.event.type });
224
221
  break;
package/dist/otel.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { d as AgentTraceEvent } from "./run-agent-Do094mGu.cjs";
1
+ import { o as AgentTraceEvent } from "./run-agent-DWqA8KiI.cjs";
2
2
  import { Attributes, Tracer, TracerProvider } from "@opentelemetry/api";
3
3
 
4
4
  //#region src/otel/index.d.ts
package/dist/otel.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { d as AgentTraceEvent } from "./run-agent-DBNI8ETM.mjs";
1
+ import { o as AgentTraceEvent } from "./run-agent-Ck7Sa3Tb.mjs";
2
2
  import { Attributes, Tracer, TracerProvider } from "@opentelemetry/api";
3
3
 
4
4
  //#region src/otel/index.d.ts
package/dist/otel.mjs CHANGED
@@ -208,15 +208,12 @@ function createOtelTraceHandler(options) {
208
208
  });
209
209
  });
210
210
  break;
211
- case "machine.transition": {
212
- const attributes = {
211
+ case "machine.transition":
212
+ addRunEvent(event, "agent.transition", {
213
213
  "agent.event_type": event.event.type,
214
214
  "agent.state": toJson(event.snapshot.value) ?? ""
215
- };
216
- if (event.eventId !== void 0) attributes["agent.event_id"] = event.eventId;
217
- addRunEvent(event, "agent.transition", attributes);
215
+ });
218
216
  break;
219
- }
220
217
  case "emit":
221
218
  addRunEvent(event, "agent.emit", { "agent.event_type": event.event.type });
222
219
  break;