@smithers-orchestrator/agents 0.24.2 → 0.25.1
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/package.json +15 -5
- package/src/AgentLike.ts +5 -0
- package/src/AmpAgent.js +15 -5
- package/src/AmpAgentOptions.ts +6 -0
- package/src/BaseCliAgent/BaseCliAgent.js +198 -10
- package/src/BaseCliAgent/createAgentStdoutTextEmitter.js +21 -3
- package/src/BaseCliAgent/index.d.ts +467 -0
- package/src/ClaudeCodeAgent.js +6 -2
- package/src/CodexAgent.js +4 -0
- package/src/GeminiAgent.js +34 -224
- package/src/GeminiAgentOptions.ts +4 -9
- package/src/OpenCodeAgent.js +2 -12
- package/src/OpenCodeAgentOptions.ts +19 -0
- package/src/PiAgent.js +4 -0
- package/src/cli-capabilities/CliAgentCapabilityAdapterId.ts +0 -1
- package/src/cli-capabilities/getCliAgentCapabilityDoctorReport.js +3 -2
- package/src/cli-capabilities/getCliAgentCapabilityReport.js +0 -6
- package/src/cli-surface/cliAgentSurfaceManifest.js +1 -40
- package/src/createElevenLabsTextToSpeechTool.js +128 -0
- package/src/createElevenLabsTextToSpeechTool.ts +33 -0
- package/src/diagnostics/getDiagnosticStrategy.js +163 -35
- package/src/document-parsing/DocumentParsingProvider.ts +13 -0
- package/src/document-parsing/DocumentParsingResult.ts +13 -0
- package/src/document-parsing/DocumentParsingToolset.ts +4 -0
- package/src/document-parsing/DocumentParsingToolsetOptions.ts +9 -0
- package/src/document-parsing/createDocumentParsingToolset.d.ts +9 -0
- package/src/document-parsing/createDocumentParsingToolset.js +416 -0
- package/src/http/CreateHttpToolOptions.ts +4 -0
- package/src/http/HttpToolAuth.ts +15 -0
- package/src/http/HttpToolInput.ts +11 -0
- package/src/http/HttpToolOutput.ts +7 -0
- package/src/http/createHttpTool.js +136 -0
- package/src/image-generation/ImageGenerationProvider.ts +7 -0
- package/src/image-generation/ImageGenerationRequest.ts +8 -0
- package/src/image-generation/ImageGenerationResult.ts +10 -0
- package/src/image-generation/ImageGenerationToolOptions.ts +10 -0
- package/src/image-generation/createImageGenerationTool.d.ts +18 -0
- package/src/image-generation/createImageGenerationTool.js +92 -0
- package/src/index.d.ts +490 -147
- package/src/index.js +23 -5
- package/src/streamResultToGenerateResult.js +55 -26
- package/src/transcription/createTranscriptionTool.js +182 -0
- package/src/transcription/createTranscriptionTool.ts +29 -0
- package/src/transcription/index.js +1 -0
- package/src/transcription/index.ts +6 -0
- package/src/web-search/GroundedWebSearchProvider.ts +21 -0
- package/src/web-search/GroundedWebSearchToolset.ts +6 -0
- package/src/web-search/createBraveSearchProvider.js +53 -0
- package/src/web-search/createExaSearchProvider.js +72 -0
- package/src/web-search/createGroundedWebSearchToolset.js +110 -0
- package/src/web-search/createSerperSearchProvider.js +63 -0
- package/src/web-search/createTavilySearchProvider.js +59 -0
- package/src/web-search/index.js +5 -0
- package/src/zodToOpenAISchema.js +4 -0
- package/src/OpenCodeAgent.ts +0 -43
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
2
5
|
/** @typedef {import("./DiagnosticCheck.ts").DiagnosticCheck} DiagnosticCheck */
|
|
3
6
|
/** @typedef {import("./DiagnosticCheckId.ts").DiagnosticCheckId} DiagnosticCheckId */
|
|
4
7
|
/** @typedef {import("./DiagnosticContext.ts").DiagnosticContext} DiagnosticContext */
|
|
@@ -190,24 +193,121 @@ const claudeStrategy = {
|
|
|
190
193
|
// ---------------------------------------------------------------------------
|
|
191
194
|
// Codex strategy
|
|
192
195
|
// ---------------------------------------------------------------------------
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
+
/**
|
|
197
|
+
* Resolve the OpenAI models endpoint, honoring OPENAI_BASE_URL (Azure, proxies,
|
|
198
|
+
* OpenAI-compatible gateways, and hermetic test fixtures) the same way the
|
|
199
|
+
* OpenAI SDK and codex do. Defaults to the public API, so existing behavior is
|
|
200
|
+
* unchanged when the variable is unset.
|
|
201
|
+
* @param {Record<string, string | undefined>} env
|
|
202
|
+
*/
|
|
203
|
+
function openaiModelsUrl(env) {
|
|
204
|
+
const base = (env.OPENAI_BASE_URL ?? "https://api.openai.com/v1").replace(/\/+$/, "");
|
|
205
|
+
return `${base}/models`;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Resolve the Codex CLI config directory the same way the `codex` binary does:
|
|
209
|
+
* an explicit `CODEX_HOME` wins, otherwise `~/.codex` (honoring `$HOME`).
|
|
210
|
+
* @param {Record<string, string | undefined>} env
|
|
211
|
+
* @returns {string}
|
|
212
|
+
*/
|
|
213
|
+
function resolveCodexHome(env) {
|
|
214
|
+
const explicit = env.CODEX_HOME?.trim();
|
|
215
|
+
if (explicit) {
|
|
216
|
+
return explicit;
|
|
217
|
+
}
|
|
218
|
+
return join(env.HOME?.trim() || homedir(), ".codex");
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* @typedef {{ apiKey: string; keySource: string } | { subscription: true } | { missing: true }} OpenAiCredentials
|
|
222
|
+
*/
|
|
223
|
+
/**
|
|
224
|
+
* Read OpenAI credentials from `<CODEX_HOME>/auth.json`, the file `codex login`
|
|
225
|
+
* writes. Mirrors the codex binary's own auth resolution: a stored API key, or
|
|
226
|
+
* ChatGPT subscription tokens. Returns null when no usable credentials are
|
|
227
|
+
* present (file missing, unreadable, malformed, or empty).
|
|
228
|
+
* @param {Record<string, string | undefined>} env
|
|
229
|
+
* @returns {{ kind: "apiKey"; apiKey: string } | { kind: "subscription" } | null}
|
|
230
|
+
*/
|
|
231
|
+
function readCodexCliAuth(env) {
|
|
232
|
+
try {
|
|
233
|
+
const raw = readFileSync(join(resolveCodexHome(env), "auth.json"), "utf8");
|
|
234
|
+
const parsed = JSON.parse(raw);
|
|
235
|
+
if (typeof parsed?.OPENAI_API_KEY === "string" && parsed.OPENAI_API_KEY.trim()) {
|
|
236
|
+
return { kind: "apiKey", apiKey: parsed.OPENAI_API_KEY.trim() };
|
|
237
|
+
}
|
|
238
|
+
if (typeof parsed?.tokens?.access_token === "string" && parsed.tokens.access_token.trim()) {
|
|
239
|
+
return { kind: "subscription" };
|
|
240
|
+
}
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
catch {
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Resolve the OpenAI credentials a codex/pi invocation will actually use. An env
|
|
249
|
+
* `OPENAI_API_KEY` always wins. When it is absent and `codexCliAuth` is set, fall
|
|
250
|
+
* back to `<CODEX_HOME>/auth.json` (subscription tokens or a stored API key) the
|
|
251
|
+
* same way the codex binary does (#448). pi leaves `codexCliAuth` off — it reads
|
|
252
|
+
* the env var (or `--api-key`), not codex's auth.json.
|
|
253
|
+
* @param {Record<string, string | undefined>} env
|
|
254
|
+
* @param {boolean} codexCliAuth
|
|
255
|
+
* @returns {OpenAiCredentials}
|
|
256
|
+
*/
|
|
257
|
+
function resolveOpenAiCredentials(env, codexCliAuth) {
|
|
258
|
+
const envKey = env.OPENAI_API_KEY;
|
|
259
|
+
if (envKey) {
|
|
260
|
+
return { apiKey: envKey, keySource: "OPENAI_API_KEY" };
|
|
261
|
+
}
|
|
262
|
+
if (codexCliAuth) {
|
|
263
|
+
const auth = readCodexCliAuth(env);
|
|
264
|
+
if (auth?.kind === "apiKey") {
|
|
265
|
+
return { apiKey: auth.apiKey, keySource: "Codex CLI auth.json API key" };
|
|
266
|
+
}
|
|
267
|
+
if (auth?.kind === "subscription") {
|
|
268
|
+
return { subscription: true };
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return { missing: true };
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* OpenAI API-key validity check via GET /v1/models (free, no tokens).
|
|
275
|
+
*
|
|
276
|
+
* Validates whatever concrete key the invocation will use — env `OPENAI_API_KEY`
|
|
277
|
+
* or, when `codexCliAuth` is set, a key stored in `<CODEX_HOME>/auth.json`. A
|
|
278
|
+
* stored key can be invalid/exhausted just like an env key, so it is probed, not
|
|
279
|
+
* trusted on presence. ChatGPT subscription tokens can't be probed cheaply, so
|
|
280
|
+
* (like the Claude subscription check) their presence passes (#448).
|
|
281
|
+
* @param {{ codexCliAuth: boolean }} options
|
|
282
|
+
* @returns {DiagnosticCheckDef}
|
|
283
|
+
*/
|
|
284
|
+
function openaiApiKeyCheck({ codexCliAuth }) {
|
|
285
|
+
return {
|
|
196
286
|
id: "api_key_valid",
|
|
197
287
|
run: async (ctx) => {
|
|
198
288
|
const start = performance.now();
|
|
199
|
-
const
|
|
200
|
-
if (
|
|
289
|
+
const creds = resolveOpenAiCredentials(ctx.env, codexCliAuth);
|
|
290
|
+
if ("subscription" in creds) {
|
|
291
|
+
return {
|
|
292
|
+
id: "api_key_valid",
|
|
293
|
+
status: "pass",
|
|
294
|
+
message: "No OPENAI_API_KEY set — using Codex CLI subscription auth (CODEX_HOME/auth.json)",
|
|
295
|
+
durationMs: performance.now() - start,
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
if ("missing" in creds) {
|
|
201
299
|
return {
|
|
202
300
|
id: "api_key_valid",
|
|
203
301
|
status: "fail",
|
|
204
|
-
message:
|
|
302
|
+
message: codexCliAuth
|
|
303
|
+
? "OPENAI_API_KEY not set and no Codex CLI auth found — run `codex login` or set OPENAI_API_KEY"
|
|
304
|
+
: "OPENAI_API_KEY not set",
|
|
205
305
|
durationMs: performance.now() - start,
|
|
206
306
|
};
|
|
207
307
|
}
|
|
208
308
|
try {
|
|
209
|
-
const res = await fetch(
|
|
210
|
-
headers: { Authorization: `Bearer ${apiKey}` },
|
|
309
|
+
const res = await fetch(openaiModelsUrl(ctx.env), {
|
|
310
|
+
headers: { Authorization: `Bearer ${creds.apiKey}` },
|
|
211
311
|
signal: AbortSignal.timeout(4_000),
|
|
212
312
|
});
|
|
213
313
|
const elapsed = performance.now() - start;
|
|
@@ -215,7 +315,7 @@ const codexApiKeyAndRateLimitCheck = [
|
|
|
215
315
|
return {
|
|
216
316
|
id: "api_key_valid",
|
|
217
317
|
status: "fail",
|
|
218
|
-
message:
|
|
318
|
+
message: `${creds.keySource} is invalid (401 Unauthorized)`,
|
|
219
319
|
durationMs: elapsed,
|
|
220
320
|
};
|
|
221
321
|
}
|
|
@@ -223,14 +323,14 @@ const codexApiKeyAndRateLimitCheck = [
|
|
|
223
323
|
return {
|
|
224
324
|
id: "api_key_valid",
|
|
225
325
|
status: "fail",
|
|
226
|
-
message:
|
|
326
|
+
message: `${creds.keySource} lacks permission (403 Forbidden)`,
|
|
227
327
|
durationMs: elapsed,
|
|
228
328
|
};
|
|
229
329
|
}
|
|
230
330
|
return {
|
|
231
331
|
id: "api_key_valid",
|
|
232
332
|
status: "pass",
|
|
233
|
-
message:
|
|
333
|
+
message: `${creds.keySource} is valid`,
|
|
234
334
|
durationMs: elapsed,
|
|
235
335
|
};
|
|
236
336
|
}
|
|
@@ -243,23 +343,34 @@ const codexApiKeyAndRateLimitCheck = [
|
|
|
243
343
|
};
|
|
244
344
|
}
|
|
245
345
|
},
|
|
246
|
-
}
|
|
247
|
-
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Rate-limit probe via GET /v1/models (free, no tokens). Probes the same key the
|
|
350
|
+
* api-key check resolves (env or Codex CLI auth.json), so a stored key's quota is
|
|
351
|
+
* checked too; subscription/no-key resolve to a non-blocking skip.
|
|
352
|
+
* @param {{ codexCliAuth: boolean }} options
|
|
353
|
+
* @returns {DiagnosticCheckDef}
|
|
354
|
+
*/
|
|
355
|
+
function openaiRateLimitCheck({ codexCliAuth }) {
|
|
356
|
+
return {
|
|
248
357
|
id: "rate_limit_status",
|
|
249
358
|
run: async (ctx) => {
|
|
250
359
|
const start = performance.now();
|
|
251
|
-
const
|
|
252
|
-
if (!apiKey) {
|
|
360
|
+
const creds = resolveOpenAiCredentials(ctx.env, codexCliAuth);
|
|
361
|
+
if (!("apiKey" in creds)) {
|
|
253
362
|
return {
|
|
254
363
|
id: "rate_limit_status",
|
|
255
364
|
status: "skip",
|
|
256
|
-
message: "
|
|
365
|
+
message: "subscription" in creds
|
|
366
|
+
? "Subscription mode — cannot probe rate limits via API"
|
|
367
|
+
: "No API key — cannot check rate limits",
|
|
257
368
|
durationMs: 0,
|
|
258
369
|
};
|
|
259
370
|
}
|
|
260
371
|
try {
|
|
261
|
-
const res = await fetch(
|
|
262
|
-
headers: { Authorization: `Bearer ${apiKey}` },
|
|
372
|
+
const res = await fetch(openaiModelsUrl(ctx.env), {
|
|
373
|
+
headers: { Authorization: `Bearer ${creds.apiKey}` },
|
|
263
374
|
signal: AbortSignal.timeout(4_000),
|
|
264
375
|
});
|
|
265
376
|
const elapsed = performance.now() - start;
|
|
@@ -313,7 +424,13 @@ const codexApiKeyAndRateLimitCheck = [
|
|
|
313
424
|
};
|
|
314
425
|
}
|
|
315
426
|
},
|
|
316
|
-
}
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
// Codex resolves auth from `<CODEX_HOME>/auth.json` (subscription tokens or a
|
|
430
|
+
// stored API key) when OPENAI_API_KEY is absent, so its checks honor that.
|
|
431
|
+
const codexApiKeyAndRateLimitCheck = [
|
|
432
|
+
openaiApiKeyCheck({ codexCliAuth: true }),
|
|
433
|
+
openaiRateLimitCheck({ codexCliAuth: true }),
|
|
317
434
|
];
|
|
318
435
|
const codexStrategy = {
|
|
319
436
|
agentId: "codex",
|
|
@@ -432,15 +549,6 @@ const googleRateLimitCheck = {
|
|
|
432
549
|
}
|
|
433
550
|
},
|
|
434
551
|
};
|
|
435
|
-
const geminiStrategy = {
|
|
436
|
-
agentId: "gemini",
|
|
437
|
-
command: "gemini",
|
|
438
|
-
checks: [
|
|
439
|
-
checkCliInstalled("gemini", "Gemini CLI"),
|
|
440
|
-
googleAuthCheck,
|
|
441
|
-
googleRateLimitCheck,
|
|
442
|
-
],
|
|
443
|
-
};
|
|
444
552
|
const antigravityAuthSkip = {
|
|
445
553
|
id: "api_key_valid",
|
|
446
554
|
run: async () => {
|
|
@@ -473,12 +581,11 @@ const antigravityStrategy = {
|
|
|
473
581
|
],
|
|
474
582
|
};
|
|
475
583
|
// ---------------------------------------------------------------------------
|
|
476
|
-
// Pi strategy
|
|
584
|
+
// Pi strategy helpers — dispatch checks based on which provider pi is using
|
|
477
585
|
// ---------------------------------------------------------------------------
|
|
478
586
|
/**
|
|
479
587
|
* Resolve the effective pi provider family from an explicit `--provider`, a
|
|
480
|
-
* `provider/model` prefix, or a bare model id's well-known prefix.
|
|
481
|
-
* when undeterminable so callers fall back to pi's default (google) (#284).
|
|
588
|
+
* `provider/model` prefix, or a bare model id's well-known prefix.
|
|
482
589
|
* @param {DiagnosticHints | undefined} hints
|
|
483
590
|
* @returns {string}
|
|
484
591
|
*/
|
|
@@ -514,12 +621,31 @@ function resolvePiProvider(hints) {
|
|
|
514
621
|
function piProviderChecks(hints) {
|
|
515
622
|
const raw = resolvePiProvider(hints);
|
|
516
623
|
if (raw === "openai" || raw === "openai-codex" || raw === "azure" || raw === "azure-openai") {
|
|
517
|
-
|
|
624
|
+
// pi reads OPENAI_API_KEY from the env (or --api-key), not codex's
|
|
625
|
+
// auth.json, so it still requires the key — no Codex CLI auth fallback.
|
|
626
|
+
return [
|
|
627
|
+
openaiApiKeyCheck({ codexCliAuth: false }),
|
|
628
|
+
openaiRateLimitCheck({ codexCliAuth: false }),
|
|
629
|
+
];
|
|
518
630
|
}
|
|
519
631
|
if (raw === "anthropic" || raw === "claude") {
|
|
520
632
|
return [claudeApiKeyCheck, claudeRateLimitCheck];
|
|
521
633
|
}
|
|
522
|
-
|
|
634
|
+
if (raw === "google" || raw === "gemini") {
|
|
635
|
+
return [googleAuthCheck, googleRateLimitCheck];
|
|
636
|
+
}
|
|
637
|
+
// Unknown provider — skip preflight, pi handles its own auth
|
|
638
|
+
return [
|
|
639
|
+
{
|
|
640
|
+
id: "api_key_valid",
|
|
641
|
+
run: async () => ({
|
|
642
|
+
id: "api_key_valid",
|
|
643
|
+
status: "skip",
|
|
644
|
+
message: `Pi provider "${raw || "unset"}" — passing auth to pi`,
|
|
645
|
+
durationMs: 0,
|
|
646
|
+
}),
|
|
647
|
+
},
|
|
648
|
+
];
|
|
523
649
|
}
|
|
524
650
|
/**
|
|
525
651
|
* pi accepts credentials via the `--api-key` option instead of an environment
|
|
@@ -542,7 +668,10 @@ export function diagnosticApiKeyEnv(command, hints) {
|
|
|
542
668
|
if (raw === "anthropic" || raw === "claude") {
|
|
543
669
|
return { ANTHROPIC_API_KEY: hints.apiKey };
|
|
544
670
|
}
|
|
545
|
-
|
|
671
|
+
if (raw === "google" || raw === "gemini") {
|
|
672
|
+
return { GOOGLE_API_KEY: hints.apiKey };
|
|
673
|
+
}
|
|
674
|
+
return undefined;
|
|
546
675
|
}
|
|
547
676
|
// ---------------------------------------------------------------------------
|
|
548
677
|
// Amp strategy
|
|
@@ -586,7 +715,6 @@ const strategies = {
|
|
|
586
715
|
codex: codexStrategy,
|
|
587
716
|
antigravity: antigravityStrategy,
|
|
588
717
|
agy: antigravityStrategy,
|
|
589
|
-
gemini: geminiStrategy,
|
|
590
718
|
amp: ampStrategy,
|
|
591
719
|
};
|
|
592
720
|
/**
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { DocumentParsingResult } from "./DocumentParsingResult.ts";
|
|
2
|
+
|
|
3
|
+
export type DocumentParsingProvider = {
|
|
4
|
+
name: "firecrawl" | "mistral-ocr" | "llamaparse" | string;
|
|
5
|
+
parseDocument: (input: {
|
|
6
|
+
source:
|
|
7
|
+
| { type: "url"; url: string }
|
|
8
|
+
| { type: "base64"; data: string; mimeType?: string; filename?: string }
|
|
9
|
+
| { type: "text"; text: string; filename?: string };
|
|
10
|
+
outputFormat?: "text" | "markdown" | "json";
|
|
11
|
+
instructions?: string;
|
|
12
|
+
}) => Promise<DocumentParsingResult>;
|
|
13
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type DocumentParsingResult = {
|
|
2
|
+
provider: "firecrawl" | "mistral-ocr" | "llamaparse" | string;
|
|
3
|
+
text: string;
|
|
4
|
+
markdown?: string;
|
|
5
|
+
pages?: Array<{
|
|
6
|
+
index: number;
|
|
7
|
+
text?: string;
|
|
8
|
+
markdown?: string;
|
|
9
|
+
images?: unknown[];
|
|
10
|
+
}>;
|
|
11
|
+
metadata?: Record<string, unknown>;
|
|
12
|
+
raw?: unknown;
|
|
13
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { DocumentParsingProvider } from "./DocumentParsingProvider.ts";
|
|
2
|
+
|
|
3
|
+
export type DocumentParsingToolsetOptions = {
|
|
4
|
+
provider?: "firecrawl" | "mistral-ocr" | "llamaparse" | DocumentParsingProvider;
|
|
5
|
+
apiKey?: string;
|
|
6
|
+
baseUrl?: string;
|
|
7
|
+
toolName?: string;
|
|
8
|
+
fetch?: typeof fetch;
|
|
9
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { DocumentParsingToolset } from "./DocumentParsingToolset.js";
|
|
2
|
+
import type { DocumentParsingToolsetOptions } from "./DocumentParsingToolsetOptions.js";
|
|
3
|
+
|
|
4
|
+
export type { DocumentParsingProvider } from "./DocumentParsingProvider.js";
|
|
5
|
+
export type { DocumentParsingResult } from "./DocumentParsingResult.js";
|
|
6
|
+
export type { DocumentParsingToolset } from "./DocumentParsingToolset.js";
|
|
7
|
+
export type { DocumentParsingToolsetOptions } from "./DocumentParsingToolsetOptions.js";
|
|
8
|
+
|
|
9
|
+
export declare function createDocumentParsingToolset(options?: DocumentParsingToolsetOptions): DocumentParsingToolset;
|