@smithers-orchestrator/agents 0.24.0 → 0.25.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.
Files changed (57) hide show
  1. package/package.json +15 -5
  2. package/src/AgentLike.ts +5 -0
  3. package/src/AmpAgent.js +15 -5
  4. package/src/AmpAgentOptions.ts +6 -0
  5. package/src/BaseCliAgent/BaseCliAgent.js +205 -11
  6. package/src/BaseCliAgent/createAgentStdoutTextEmitter.js +21 -3
  7. package/src/BaseCliAgent/index.d.ts +467 -0
  8. package/src/ClaudeCodeAgent.js +6 -2
  9. package/src/CodexAgent.js +17 -2
  10. package/src/CodexAgentOptions.ts +11 -0
  11. package/src/GeminiAgent.js +34 -224
  12. package/src/GeminiAgentOptions.ts +4 -9
  13. package/src/OpenCodeAgent.js +2 -12
  14. package/src/OpenCodeAgentOptions.ts +19 -0
  15. package/src/PiAgent.js +63 -5
  16. package/src/cli-capabilities/CliAgentCapabilityAdapterId.ts +0 -1
  17. package/src/cli-capabilities/getCliAgentCapabilityDoctorReport.js +3 -2
  18. package/src/cli-capabilities/getCliAgentCapabilityReport.js +0 -6
  19. package/src/cli-surface/cliAgentSurfaceManifest.js +1 -40
  20. package/src/createElevenLabsTextToSpeechTool.js +128 -0
  21. package/src/createElevenLabsTextToSpeechTool.ts +33 -0
  22. package/src/diagnostics/getDiagnosticStrategy.js +94 -23
  23. package/src/diagnostics/launchDiagnostics.js +7 -4
  24. package/src/document-parsing/DocumentParsingProvider.ts +13 -0
  25. package/src/document-parsing/DocumentParsingResult.ts +13 -0
  26. package/src/document-parsing/DocumentParsingToolset.ts +4 -0
  27. package/src/document-parsing/DocumentParsingToolsetOptions.ts +9 -0
  28. package/src/document-parsing/createDocumentParsingToolset.d.ts +9 -0
  29. package/src/document-parsing/createDocumentParsingToolset.js +416 -0
  30. package/src/http/CreateHttpToolOptions.ts +4 -0
  31. package/src/http/HttpToolAuth.ts +15 -0
  32. package/src/http/HttpToolInput.ts +11 -0
  33. package/src/http/HttpToolOutput.ts +7 -0
  34. package/src/http/createHttpTool.js +136 -0
  35. package/src/image-generation/ImageGenerationProvider.ts +7 -0
  36. package/src/image-generation/ImageGenerationRequest.ts +8 -0
  37. package/src/image-generation/ImageGenerationResult.ts +10 -0
  38. package/src/image-generation/ImageGenerationToolOptions.ts +10 -0
  39. package/src/image-generation/createImageGenerationTool.d.ts +18 -0
  40. package/src/image-generation/createImageGenerationTool.js +92 -0
  41. package/src/index.d.ts +490 -147
  42. package/src/index.js +23 -5
  43. package/src/streamResultToGenerateResult.js +55 -26
  44. package/src/transcription/createTranscriptionTool.js +182 -0
  45. package/src/transcription/createTranscriptionTool.ts +29 -0
  46. package/src/transcription/index.js +1 -0
  47. package/src/transcription/index.ts +6 -0
  48. package/src/web-search/GroundedWebSearchProvider.ts +21 -0
  49. package/src/web-search/GroundedWebSearchToolset.ts +6 -0
  50. package/src/web-search/createBraveSearchProvider.js +53 -0
  51. package/src/web-search/createExaSearchProvider.js +72 -0
  52. package/src/web-search/createGroundedWebSearchToolset.js +110 -0
  53. package/src/web-search/createSerperSearchProvider.js +63 -0
  54. package/src/web-search/createTavilySearchProvider.js +59 -0
  55. package/src/web-search/index.js +5 -0
  56. package/src/zodToOpenAISchema.js +4 -0
  57. package/src/OpenCodeAgent.ts +0 -43
@@ -0,0 +1,467 @@
1
+ import * as ai from 'ai';
2
+ import * as _smithers_orchestrator_errors_SmithersError from '@smithers-orchestrator/errors/SmithersError';
3
+ import { SmithersError as SmithersError$1 } from '@smithers-orchestrator/errors/SmithersError';
4
+ import { Effect } from 'effect';
5
+ import { spawn } from 'node:child_process';
6
+
7
+ type RunCommandResult$2 = {
8
+ stdout: string;
9
+ stderr: string;
10
+ exitCode: number | null;
11
+ /** True when captured stdout exceeded maxOutputBytes and was truncated. */
12
+ stdoutTruncated?: boolean;
13
+ /** True when captured stderr exceeded maxOutputBytes and was truncated. */
14
+ stderrTruncated?: boolean;
15
+ };
16
+
17
+ type PiExtensionUiResponse$2 = {
18
+ type: "extension_ui_response";
19
+ id: string;
20
+ value?: string;
21
+ cancelled?: boolean;
22
+ [key: string]: unknown;
23
+ };
24
+
25
+ type PiExtensionUiRequest$2 = {
26
+ type: "extension_ui_request";
27
+ id: string;
28
+ method: string;
29
+ title?: string;
30
+ placeholder?: string;
31
+ [key: string]: unknown;
32
+ };
33
+
34
+ type CodexConfigOverrides$2 = Record<string, string | number | boolean | object | null> | string[];
35
+
36
+ type NormalizedTokenUsage$2 = {
37
+ inputTokens?: number;
38
+ outputTokens?: number;
39
+ cacheReadTokens?: number;
40
+ cacheWriteTokens?: number;
41
+ reasoningTokens?: number;
42
+ totalTokens?: number;
43
+ };
44
+
45
+ type CliUsageInfo$2 = {
46
+ inputTokens?: number;
47
+ outputTokens?: number;
48
+ cacheReadTokens?: number;
49
+ cacheWriteTokens?: number;
50
+ reasoningTokens?: number;
51
+ };
52
+
53
+ type AgentCliActionKind$2 = "turn" | "command" | "tool" | "file_change" | "web_search" | "todo_list" | "reasoning" | "warning" | "note";
54
+
55
+ type AgentCliActionPhase$1 = "started" | "updated" | "completed";
56
+ type AgentCliEventLevel$1 = "debug" | "info" | "warning" | "error";
57
+ type AgentCliStartedEvent$1 = {
58
+ type: "started";
59
+ engine: string;
60
+ title: string;
61
+ resume?: string;
62
+ detail?: Record<string, unknown>;
63
+ };
64
+ type AgentCliActionEvent$1 = {
65
+ type: "action";
66
+ engine: string;
67
+ phase: AgentCliActionPhase$1;
68
+ entryType?: "thought" | "message";
69
+ action: {
70
+ id: string;
71
+ kind: AgentCliActionKind$2;
72
+ title: string;
73
+ detail?: Record<string, unknown>;
74
+ };
75
+ message?: string;
76
+ ok?: boolean;
77
+ level?: AgentCliEventLevel$1;
78
+ };
79
+ type AgentCliCompletedEvent$1 = {
80
+ type: "completed";
81
+ engine: string;
82
+ ok: boolean;
83
+ answer?: string;
84
+ error?: string;
85
+ resume?: string;
86
+ usage?: Record<string, unknown>;
87
+ };
88
+ type AgentCliEvent$1 = AgentCliStartedEvent$1 | AgentCliActionEvent$1 | AgentCliCompletedEvent$1;
89
+
90
+ type CliOutputInterpreter$2 = {
91
+ onStdoutLine?: (line: string) => AgentCliEvent$1[] | AgentCliEvent$1 | null | undefined;
92
+ onStderrLine?: (line: string) => AgentCliEvent$1[] | AgentCliEvent$1 | null | undefined;
93
+ onExit?: (result: RunCommandResult$2) => AgentCliEvent$1[] | AgentCliEvent$1 | null | undefined;
94
+ };
95
+
96
+ type BaseCliAgentOptions$2 = {
97
+ id?: string;
98
+ model?: string;
99
+ systemPrompt?: string;
100
+ instructions?: string;
101
+ cwd?: string;
102
+ env?: Record<string, string>;
103
+ yolo?: boolean;
104
+ timeoutMs?: number;
105
+ idleTimeoutMs?: number;
106
+ maxOutputBytes?: number;
107
+ extraArgs?: string[];
108
+ };
109
+
110
+ /**
111
+ * Loosely-typed generation options. The AI SDK passes a dynamic shape here
112
+ * (GenerateTextOptions / StreamTextOptions and provider-specific extensions)
113
+ * so we keep this permissive but avoid raw `any`.
114
+ */
115
+ type AgentGenerateOptions$2 = {
116
+ prompt?: unknown;
117
+ messages?: unknown;
118
+ timeout?: unknown;
119
+ abortSignal?: AbortSignal;
120
+ rootDir?: string;
121
+ resumeSession?: string;
122
+ maxOutputBytes?: number;
123
+ onStdout?: (text: string) => void;
124
+ onStderr?: (text: string) => void;
125
+ onEvent?: (event: AgentCliEvent$1) => unknown;
126
+ retry?: unknown;
127
+ isRetry?: unknown;
128
+ retryAttempt?: unknown;
129
+ schemaRetry?: unknown;
130
+ /**
131
+ * Run context for the task this agent invocation belongs to. Surfaced to the
132
+ * spawned agent process (and its subprocesses) as SMITHERS_RUN_ID / NODE_ID /
133
+ * ITERATION / ATTEMPT so the agent can address its own run — e.g. to raise a
134
+ * blocking `smithers ask-human` request.
135
+ */
136
+ taskContext?: {
137
+ runId?: string;
138
+ nodeId?: string;
139
+ iteration?: number;
140
+ attempt?: number;
141
+ };
142
+ [key: string]: unknown;
143
+ };
144
+
145
+ /**
146
+ * @typedef {number | { totalMs?: number; idleMs?: number; } | undefined} TimeoutInput
147
+ */
148
+ /**
149
+ * @param {TimeoutInput} timeout
150
+ * @param {{ totalMs?: number; idleMs?: number }} [fallback]
151
+ * @returns {{ totalMs?: number; idleMs?: number }}
152
+ */
153
+ declare function resolveTimeouts(timeout: TimeoutInput, fallback?: {
154
+ totalMs?: number;
155
+ idleMs?: number;
156
+ }): {
157
+ totalMs?: number;
158
+ idleMs?: number;
159
+ };
160
+ type TimeoutInput = number | {
161
+ totalMs?: number;
162
+ idleMs?: number;
163
+ } | undefined;
164
+
165
+ /**
166
+ * @param {Array<string | undefined>} parts
167
+ * @returns {string | undefined}
168
+ */
169
+ declare function combineNonEmpty(parts: Array<string | undefined>): string | undefined;
170
+
171
+ /**
172
+ * @param {unknown} options
173
+ * @returns {PromptParts}
174
+ */
175
+ declare function extractPrompt(options: unknown): PromptParts;
176
+ type PromptParts = {
177
+ prompt: string;
178
+ systemFromMessages?: string;
179
+ };
180
+
181
+ /**
182
+ * @param {string} text
183
+ * @returns {unknown | undefined}
184
+ */
185
+ declare function tryParseJson(text: string): unknown | undefined;
186
+
187
+ /**
188
+ * @param {unknown} value
189
+ * @returns {string | undefined}
190
+ */
191
+ declare function extractTextFromJsonValue(value: unknown): string | undefined;
192
+
193
+ /**
194
+ * @param {unknown} usage
195
+ * @returns {NormalizedTokenUsage | null}
196
+ */
197
+ declare function normalizeTokenUsage(usage: unknown): NormalizedTokenUsage$1 | null;
198
+ type NormalizedTokenUsage$1 = NormalizedTokenUsage$2;
199
+
200
+ /**
201
+ * @param {AgentStdoutTextEmitterOptions} options
202
+ * @returns {AgentStdoutTextEmitter}
203
+ */
204
+ declare function createAgentStdoutTextEmitter(options: AgentStdoutTextEmitterOptions): AgentStdoutTextEmitter;
205
+ type AgentStdoutTextEmitter = {
206
+ push: (chunk: string) => void;
207
+ flush: (finalText?: string) => void;
208
+ };
209
+ type AgentStdoutTextEmitterOptions = {
210
+ outputFormat?: string;
211
+ onText?: (text: string) => void;
212
+ };
213
+
214
+ /**
215
+ * @param {string} text
216
+ * @param {number} [maxBytes]
217
+ * @returns {string}
218
+ */
219
+ declare function truncateToBytes(text: string, maxBytes?: number): string;
220
+
221
+ /** @typedef {import("ai").GenerateTextResult} GenerateTextResult */
222
+ /** @typedef {import("ai").LanguageModelUsage} LanguageModelUsage */
223
+ /**
224
+ * @param {string} text
225
+ * @param {unknown} output
226
+ * @param {string} modelId
227
+ * @param {LanguageModelUsage} [usage]
228
+ * @returns {GenerateTextResult<Record<string, never>, unknown>}
229
+ */
230
+ declare function buildGenerateResult(text: string, output: unknown, modelId: string, usage?: LanguageModelUsage): GenerateTextResult$1<Record<string, never>, unknown>;
231
+ type GenerateTextResult$1 = ai.GenerateTextResult<any, any>;
232
+ type LanguageModelUsage = ai.LanguageModelUsage;
233
+
234
+ /**
235
+ * @typedef {{ cwd: string; env: Record<string, string>; input?: string; timeoutMs?: number; idleTimeoutMs?: number; signal?: AbortSignal; maxOutputBytes?: number; truncateKeep?: "head" | "tail"; onStdout?: (chunk: string) => void; onStderr?: (chunk: string) => void; }} RunCommandOptions
236
+ */
237
+ /** @typedef {import("./RunCommandResult.ts").RunCommandResult} RunCommandResult */
238
+ /** @typedef {import("@smithers-orchestrator/errors/SmithersError").SmithersError} SmithersError */
239
+ /**
240
+ * @param {string} command
241
+ * @param {string[]} args
242
+ * @param {RunCommandOptions} options
243
+ * @returns {Effect.Effect<RunCommandResult, SmithersError>}
244
+ */
245
+ declare function runCommandEffect(command: string, args: string[], options: RunCommandOptions): Effect.Effect<RunCommandResult$1, SmithersError>;
246
+ type RunCommandOptions = {
247
+ cwd: string;
248
+ env: Record<string, string>;
249
+ input?: string;
250
+ timeoutMs?: number;
251
+ idleTimeoutMs?: number;
252
+ signal?: AbortSignal;
253
+ maxOutputBytes?: number;
254
+ truncateKeep?: "head" | "tail";
255
+ onStdout?: (chunk: string) => void;
256
+ onStderr?: (chunk: string) => void;
257
+ };
258
+ type RunCommandResult$1 = RunCommandResult$2;
259
+ type SmithersError = _smithers_orchestrator_errors_SmithersError.SmithersError;
260
+
261
+ /**
262
+ * @param {string} command
263
+ * @param {string[]} args
264
+ * @param {RunRpcCommandOptions} options
265
+ * @returns {Effect.Effect<{ text: string; output: unknown; stderr: string; exitCode: number | null; usage?: any; }, SmithersError>}
266
+ */
267
+ declare function runRpcCommandEffect(command: string, args: string[], options: RunRpcCommandOptions): Effect.Effect<{
268
+ text: string;
269
+ output: unknown;
270
+ stderr: string;
271
+ exitCode: number | null;
272
+ usage?: any;
273
+ }, SmithersError$1>;
274
+ type PiExtensionUiResponse$1 = PiExtensionUiResponse$2;
275
+ type PiExtensionUiRequest$1 = PiExtensionUiRequest$2;
276
+ type RunRpcCommandOptions = {
277
+ cwd: string;
278
+ env: Record<string, string>;
279
+ prompt: string;
280
+ timeoutMs?: number;
281
+ idleTimeoutMs?: number;
282
+ signal?: AbortSignal;
283
+ maxOutputBytes?: number;
284
+ onStdout?: (chunk: string) => void;
285
+ onStderr?: (chunk: string) => void;
286
+ onJsonEvent?: (event: Record<string, unknown>) => Promise<void> | void;
287
+ onExtensionUiRequest?: (request: PiExtensionUiRequest$1) => Promise<PiExtensionUiResponse$1 | null> | PiExtensionUiResponse$1 | null;
288
+ spawnFn?: typeof spawn;
289
+ };
290
+
291
+ /**
292
+ * @param {string[]} args
293
+ * @param {string} flag
294
+ * @param {string | number | boolean} [value]
295
+ */
296
+ declare function pushFlag(args: string[], flag: string, value?: string | number | boolean): void;
297
+
298
+ /**
299
+ * @param {string[]} args
300
+ * @param {string} flag
301
+ * @param {string[]} [values]
302
+ */
303
+ declare function pushList(args: string[], flag: string, values?: string[]): void;
304
+
305
+ /** @typedef {import("./CodexConfigOverrides.ts").CodexConfigOverrides} CodexConfigOverrides */
306
+ /**
307
+ * @param {CodexConfigOverrides} [config]
308
+ * @returns {string[]}
309
+ */
310
+ declare function normalizeCodexConfig(config?: CodexConfigOverrides$1): string[];
311
+ type CodexConfigOverrides$1 = CodexConfigOverrides$2;
312
+
313
+ /** @typedef {import("./AgentCliEvent.ts").AgentCliEvent} AgentCliEvent */
314
+ /** @typedef {import("./AgentGenerateOptions.ts").AgentGenerateOptions} AgentGenerateOptions */
315
+ /** @typedef {import("./BaseCliAgentOptions.ts").BaseCliAgentOptions} BaseCliAgentOptions */
316
+ /** @typedef {import("./CliOutputInterpreter.ts").CliOutputInterpreter} CliOutputInterpreter */
317
+ /** @typedef {import("./CliUsageInfo.ts").CliUsageInfo} CliUsageInfo */
318
+ /** @typedef {import("ai").GenerateTextResult} GenerateTextResult */
319
+ /** @typedef {import("ai").StreamTextResult} StreamTextResult */
320
+ /** @typedef {import("ai").LanguageModelUsage} LanguageModelUsage */
321
+ /**
322
+ * @typedef {"generate" | "stream"} AgentInvocationOperation
323
+ */
324
+ /**
325
+ * @typedef {Record<string, string | undefined>} AgentInvocationTags
326
+ */
327
+ /**
328
+ * @typedef {{
329
+ * inputTokens?: number;
330
+ * outputTokens?: number;
331
+ * cacheReadTokens?: number;
332
+ * cacheWriteTokens?: number;
333
+ * reasoningTokens?: number;
334
+ * totalTokens?: number;
335
+ * }} AgentTokenTotals
336
+ */
337
+ /**
338
+ * @template A
339
+ * @param {Effect.Effect<A, SmithersError, never>} effect
340
+ * @returns {Promise<A>}
341
+ */
342
+ declare function runAgentPromise<A>(effect: Effect.Effect<A, SmithersError$1, never>): Promise<A>;
343
+ /**
344
+ * @param {string} raw
345
+ * @returns {CliUsageInfo | undefined}
346
+ */
347
+ declare function extractUsageFromOutput(raw: string): CliUsageInfo$1 | undefined;
348
+ declare class BaseCliAgent {
349
+ /**
350
+ * @param {BaseCliAgentOptions} opts
351
+ */
352
+ constructor(opts: BaseCliAgentOptions$1);
353
+ version: string;
354
+ tools: {};
355
+ capabilities: any;
356
+ id: string;
357
+ model: string | undefined;
358
+ systemPrompt: string | undefined;
359
+ cwd: string | undefined;
360
+ env: Record<string, string> | undefined;
361
+ yolo: boolean;
362
+ timeoutMs: number | undefined;
363
+ idleTimeoutMs: number | undefined;
364
+ maxOutputBytes: number | undefined;
365
+ extraArgs: string[] | undefined;
366
+ /**
367
+ * @param {AgentGenerateOptions | undefined} options
368
+ * @param {AgentInvocationOperation} operation
369
+ * @returns {Effect.Effect<GenerateTextResult<Record<string, never>, unknown>, SmithersError>}
370
+ */
371
+ runGenerateEffect(options: AgentGenerateOptions$1 | undefined, operation: AgentInvocationOperation): Effect.Effect<GenerateTextResult<Record<string, never>, unknown>, SmithersError$1>;
372
+ /**
373
+ * @param {AgentGenerateOptions} [options]
374
+ * @returns {Promise<void>}
375
+ */
376
+ preflight(options?: AgentGenerateOptions$1): Promise<void>;
377
+ /**
378
+ * @param {AgentGenerateOptions} [options]
379
+ * @returns {Promise<GenerateTextResult<Record<string, never>, unknown>>}
380
+ */
381
+ generate(options?: AgentGenerateOptions$1): Promise<GenerateTextResult<Record<string, never>, unknown>>;
382
+ /**
383
+ * @param {AgentGenerateOptions} [options]
384
+ * @returns {Promise<StreamTextResult<Record<string, never>, unknown>>}
385
+ */
386
+ stream(options?: AgentGenerateOptions$1): Promise<StreamTextResult<Record<string, never>, unknown>>;
387
+ /**
388
+ * @returns {CliOutputInterpreter | undefined}
389
+ */
390
+ createOutputInterpreter(): CliOutputInterpreter$1 | undefined;
391
+ /**
392
+ * @returns {{ provider?: string; model?: string } | undefined}
393
+ */
394
+ diagnosticHints(): {
395
+ provider?: string;
396
+ model?: string;
397
+ } | undefined;
398
+ }
399
+ type AgentGenerateOptions$1 = AgentGenerateOptions$2;
400
+ type BaseCliAgentOptions$1 = BaseCliAgentOptions$2;
401
+ type CliOutputInterpreter$1 = CliOutputInterpreter$2;
402
+ type CliUsageInfo$1 = CliUsageInfo$2;
403
+ type GenerateTextResult = ai.GenerateTextResult<any, any>;
404
+ type StreamTextResult = ai.StreamTextResult<any, any>;
405
+ type AgentInvocationOperation = "generate" | "stream";
406
+
407
+ /** @typedef {import("./AgentCliActionKind.ts").AgentCliActionKind} AgentCliActionKind */
408
+ /**
409
+ * @param {unknown} value
410
+ * @returns {value is Record<string, unknown>}
411
+ */
412
+ declare function isRecord(value: unknown): value is Record<string, unknown>;
413
+ /**
414
+ * @param {unknown} value
415
+ * @returns {string | undefined}
416
+ */
417
+ declare function asString(value: unknown): string | undefined;
418
+ /**
419
+ * @param {unknown} value
420
+ * @returns {number | undefined}
421
+ */
422
+ declare function asNumber(value: unknown): number | undefined;
423
+ /**
424
+ * @param {string} value
425
+ * @returns {string}
426
+ */
427
+ declare function truncate(value: string, maxLength?: number): string;
428
+ /**
429
+ * @param {string | undefined} name
430
+ * @param {ReadonlyArray<readonly [string[], AgentCliActionKind]>} [extraRules]
431
+ * @returns {AgentCliActionKind}
432
+ */
433
+ declare function toolKindFromName(name: string | undefined, extraRules?: ReadonlyArray<readonly [string[], AgentCliActionKind$1]>): AgentCliActionKind$1;
434
+ /**
435
+ * @param {string} value
436
+ * @returns {boolean}
437
+ */
438
+ declare function isLikelyRuntimeMetadata(value: string): boolean;
439
+ /**
440
+ * @param {string} line
441
+ * @returns {boolean}
442
+ */
443
+ declare function shouldSurfaceUnparsedStdout(line: string): boolean;
444
+ /**
445
+ * @returns {(prefix: string) => string}
446
+ */
447
+ declare function createSyntheticIdGenerator(): (prefix: string) => string;
448
+ type AgentCliActionKind$1 = AgentCliActionKind$2;
449
+
450
+ type AgentCliActionEvent = AgentCliActionEvent$1;
451
+ type AgentCliActionKind = AgentCliActionKind$2;
452
+ type AgentCliActionPhase = AgentCliActionPhase$1;
453
+ type AgentCliCompletedEvent = AgentCliCompletedEvent$1;
454
+ type AgentCliEvent = AgentCliEvent$1;
455
+ type AgentCliEventLevel = AgentCliEventLevel$1;
456
+ type AgentCliStartedEvent = AgentCliStartedEvent$1;
457
+ type AgentGenerateOptions = AgentGenerateOptions$2;
458
+ type BaseCliAgentOptions = BaseCliAgentOptions$2;
459
+ type CliOutputInterpreter = CliOutputInterpreter$2;
460
+ type CliUsageInfo = CliUsageInfo$2;
461
+ type NormalizedTokenUsage = NormalizedTokenUsage$2;
462
+ type CodexConfigOverrides = CodexConfigOverrides$2;
463
+ type PiExtensionUiRequest = PiExtensionUiRequest$2;
464
+ type PiExtensionUiResponse = PiExtensionUiResponse$2;
465
+ type RunCommandResult = RunCommandResult$2;
466
+
467
+ export { type AgentCliActionEvent, type AgentCliActionKind, type AgentCliActionPhase, type AgentCliCompletedEvent, type AgentCliEvent, type AgentCliEventLevel, type AgentCliStartedEvent, type AgentGenerateOptions, BaseCliAgent, type BaseCliAgentOptions, type CliOutputInterpreter, type CliUsageInfo, type CodexConfigOverrides, type NormalizedTokenUsage, type PiExtensionUiRequest, type PiExtensionUiResponse, type RunCommandResult, asNumber, asString, buildGenerateResult, combineNonEmpty, createAgentStdoutTextEmitter, createSyntheticIdGenerator, extractPrompt, extractTextFromJsonValue, extractUsageFromOutput, isLikelyRuntimeMetadata, isRecord, normalizeCodexConfig, normalizeTokenUsage, pushFlag, pushList, resolveTimeouts, runAgentPromise, runCommandEffect, runRpcCommandEffect, shouldSurfaceUnparsedStdout, toolKindFromName, truncate, truncateToBytes, tryParseJson };
@@ -56,6 +56,7 @@ export function createClaudeCodeCapabilityRegistry(opts = {}) {
56
56
  };
57
57
  }
58
58
  const TOOL_OUTPUT_MAX_CHARS = 500;
59
+ let didWarnAnthropicApiKeyUnset = false;
59
60
  /**
60
61
  * @param {string} toolName
61
62
  * @param {string | undefined} rawOutput
@@ -105,8 +106,11 @@ export class ClaudeCodeAgent extends BaseCliAgent {
105
106
  if (process.env.CLAUDECODE)
106
107
  parentEnvOverrides.CLAUDECODE = "";
107
108
  if (process.env.ANTHROPIC_API_KEY && !opts.apiKey) {
108
- logWarning("ClaudeCodeAgent: unsetting ANTHROPIC_API_KEY so Claude Code uses your subscription. " +
109
- "To use API billing instead, pass `apiKey` to ClaudeCodeAgent or use ToolLoopAgent from 'ai' with anthropic() provider.", {}, "agent.init");
109
+ if (!didWarnAnthropicApiKeyUnset) {
110
+ didWarnAnthropicApiKeyUnset = true;
111
+ logWarning("ClaudeCodeAgent: unsetting ANTHROPIC_API_KEY so Claude Code uses your subscription. " +
112
+ "To use API billing instead, pass `apiKey` to ClaudeCodeAgent or use ToolLoopAgent from 'ai' with anthropic() provider.", {}, "agent.init");
113
+ }
110
114
  parentEnvOverrides.ANTHROPIC_API_KEY = "";
111
115
  }
112
116
  if (Object.keys(parentEnvOverrides).length > 0) {
package/src/CodexAgent.js CHANGED
@@ -4,6 +4,7 @@ import { join } from "node:path";
4
4
  import { randomUUID } from "node:crypto";
5
5
  import { BaseCliAgent, normalizeCodexConfig, pushFlag, pushList, isRecord, asString, asNumber, truncate, shouldSurfaceUnparsedStdout, createSyntheticIdGenerator, } from "./BaseCliAgent/index.js";
6
6
  import { normalizeCapabilityStringList, } from "./capability-registry/index.js";
7
+ import { assertZodV4 } from "@smithers-orchestrator/errors/assertZodV4";
7
8
  import { sanitizeForOpenAI } from "./sanitizeForOpenAI.js";
8
9
  /** @typedef {import("./BaseCliAgent/BaseCliAgentOptions.ts").BaseCliAgentOptions} BaseCliAgentOptions */
9
10
  /** @typedef {import("./BaseCliAgent/CodexConfigOverrides.ts").CodexConfigOverrides} CodexConfigOverrides */
@@ -59,6 +60,15 @@ export class CodexAgent extends BaseCliAgent {
59
60
  super(opts);
60
61
  this.opts = opts;
61
62
  this.capabilities = createCodexCapabilityRegistry(opts);
63
+ // Native structured output (`codex exec --output-schema`) constrains the
64
+ // model to emit only final JSON and makes it refuse tool calls ("tool calls
65
+ // are constrained by a JSON response schema"), which breaks any agentic task
66
+ // (read/edit/run). It is therefore OPT-IN: by default Codex is treated like
67
+ // the other CLI engines (supportsNativeStructuredOutput=false), so the engine
68
+ // prompt-injects the schema and extracts JSON from the agent's final text,
69
+ // leaving tool use intact. Set nativeStructuredOutput:true for pure, tool-free
70
+ // extraction tasks that want strict schema enforcement.
71
+ this.supportsNativeStructuredOutput = opts.nativeStructuredOutput === true;
62
72
  }
63
73
  /**
64
74
  * @returns {CliOutputInterpreter}
@@ -548,11 +558,16 @@ export class CodexAgent extends BaseCliAgent {
548
558
  // turn.completed with token usage for metrics. extractUsageFromOutput
549
559
  // in BaseCliAgent will parse these automatically.
550
560
  args.push("--json");
551
- // Auto-wire output schema from task context if not explicitly set.
561
+ // Auto-wire output schema from task context if not explicitly set — only when
562
+ // native structured output is opted in. Otherwise the engine handles the schema
563
+ // via prompt-injection and Codex keeps full tool access (see constructor note).
552
564
  // Skip when resuming — `codex exec resume` does not accept --output-schema.
553
565
  let schemaCleanupFile = null;
554
- if (!resumeSession && !this.opts.outputSchema && params.options?.outputSchema) {
566
+ if (!resumeSession && this.opts.nativeStructuredOutput === true && !this.opts.outputSchema && params.options?.outputSchema) {
555
567
  const schema = params.options.outputSchema;
568
+ // z.toJSONSchema() reads Zod v4 internals; a v3 schema throws a cryptic
569
+ // `schema._zod.def` TypeError. Surface a clear, actionable error instead.
570
+ assertZodV4(schema);
556
571
  const { z } = await import("zod");
557
572
  let jsonSchema = z.toJSONSchema(schema);
558
573
  // Sanitize for OpenAI structured output compatibility
@@ -17,6 +17,17 @@ export type CodexAgentOptions = BaseCliAgentOptions & {
17
17
  skipGitRepoCheck?: boolean;
18
18
  addDir?: string[];
19
19
  outputSchema?: string;
20
+ /**
21
+ * Opt in to Codex's native structured output (`codex exec --output-schema`).
22
+ *
23
+ * Defaults to `false`. Native structured output makes the model emit only the
24
+ * final JSON and refuse tool calls, so it BREAKS agentic tasks (read/edit/run) —
25
+ * Codex returns `blocked` with no changes. Left off, Smithers treats Codex like
26
+ * the other CLI engines: it prompt-injects the schema and extracts JSON from the
27
+ * agent's final message, so tool use stays intact. Enable only for pure, tool-free
28
+ * extraction tasks that need strict schema enforcement.
29
+ */
30
+ nativeStructuredOutput?: boolean;
20
31
  color?: "always" | "never" | "auto";
21
32
  json?: boolean;
22
33
  outputLastMessage?: string;