ai-sdk-provider-codex-cli 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +57 -0
- package/dist/index.cjs +104 -7
- package/dist/index.d.cts +31 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.js +104 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,6 +15,7 @@ A community provider for Vercel AI SDK v5 that uses OpenAI’s Codex CLI (non‑
|
|
|
15
15
|
- Works with `generateText`, `streamText`, and `generateObject` (native JSON Schema support via `--output-schema`)
|
|
16
16
|
- Uses ChatGPT OAuth from `codex login` (tokens in `~/.codex/auth.json`) or `OPENAI_API_KEY`
|
|
17
17
|
- Node-only (spawns a local process); supports CI and local dev
|
|
18
|
+
- **v0.5.0**: Adds comprehensive logging system with verbose mode and custom logger support
|
|
18
19
|
- **v0.3.0**: Adds comprehensive tool streaming support for monitoring autonomous tool execution
|
|
19
20
|
- **v0.2.0 Breaking Changes**: Switched to `--experimental-json` and native schema enforcement (see [CHANGELOG](CHANGELOG.md))
|
|
20
21
|
|
|
@@ -96,6 +97,7 @@ console.log(object);
|
|
|
96
97
|
|
|
97
98
|
- AI SDK v5 compatible (LanguageModelV2)
|
|
98
99
|
- Streaming and non‑streaming
|
|
100
|
+
- **Configurable logging** (v0.5.0+) - Verbose mode, custom loggers, or silent operation
|
|
99
101
|
- **Tool streaming support** (v0.3.0+) - Monitor autonomous tool execution in real-time
|
|
100
102
|
- **Native JSON Schema support** via `--output-schema` (API-enforced with `strict: true`)
|
|
101
103
|
- JSON object generation with Zod schemas (100-200 fewer tokens per request vs prompt engineering)
|
|
@@ -135,6 +137,58 @@ for await (const part of result.fullStream) {
|
|
|
135
137
|
|
|
136
138
|
**Limitation:** Real-time output streaming (`output-delta` events) not yet available. Tool outputs delivered in final `tool-result` event. See `examples/streaming-tool-calls.mjs` and `examples/streaming-multiple-tools.mjs` for usage patterns.
|
|
137
139
|
|
|
140
|
+
### Logging Configuration (v0.5.0+)
|
|
141
|
+
|
|
142
|
+
Control logging verbosity and integrate with your observability stack:
|
|
143
|
+
|
|
144
|
+
```js
|
|
145
|
+
import { codexCli } from 'ai-sdk-provider-codex-cli';
|
|
146
|
+
|
|
147
|
+
// Default: warn/error only (clean production output)
|
|
148
|
+
const model = codexCli('gpt-5-codex', {
|
|
149
|
+
allowNpx: true,
|
|
150
|
+
skipGitRepoCheck: true,
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// Verbose mode: enable debug/info logs for troubleshooting
|
|
154
|
+
const verboseModel = codexCli('gpt-5-codex', {
|
|
155
|
+
allowNpx: true,
|
|
156
|
+
skipGitRepoCheck: true,
|
|
157
|
+
verbose: true, // Shows all log levels
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// Custom logger: integrate with Winston, Pino, Datadog, etc.
|
|
161
|
+
const customModel = codexCli('gpt-5-codex', {
|
|
162
|
+
allowNpx: true,
|
|
163
|
+
skipGitRepoCheck: true,
|
|
164
|
+
verbose: true,
|
|
165
|
+
logger: {
|
|
166
|
+
debug: (msg) => myLogger.debug('Codex:', msg),
|
|
167
|
+
info: (msg) => myLogger.info('Codex:', msg),
|
|
168
|
+
warn: (msg) => myLogger.warn('Codex:', msg),
|
|
169
|
+
error: (msg) => myLogger.error('Codex:', msg),
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// Silent: disable all logging
|
|
174
|
+
const silentModel = codexCli('gpt-5-codex', {
|
|
175
|
+
allowNpx: true,
|
|
176
|
+
skipGitRepoCheck: true,
|
|
177
|
+
logger: false, // No logs at all
|
|
178
|
+
});
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
**Log Levels:**
|
|
182
|
+
|
|
183
|
+
- `debug`: Detailed execution traces (verbose mode only)
|
|
184
|
+
- `info`: General execution flow (verbose mode only)
|
|
185
|
+
- `warn`: Warnings and misconfigurations (always shown)
|
|
186
|
+
- `error`: Errors and failures (always shown)
|
|
187
|
+
|
|
188
|
+
**Default Logger:** Adds level tags `[DEBUG]`, `[INFO]`, `[WARN]`, `[ERROR]` to console output. Use a custom logger or `logger: false` if you need different formatting.
|
|
189
|
+
|
|
190
|
+
See `examples/logging-*.mjs` for complete examples and [docs/ai-sdk-v5/guide.md](docs/ai-sdk-v5/guide.md) for detailed configuration.
|
|
191
|
+
|
|
138
192
|
### Text Streaming behavior
|
|
139
193
|
|
|
140
194
|
**Status:** Incremental streaming not currently supported with `--experimental-json` format (expected in future Codex CLI releases)
|
|
@@ -176,6 +230,9 @@ When OpenAI adds streaming support, this provider will be updated to handle thos
|
|
|
176
230
|
- `skipGitRepoCheck`: enable by default for CI/non‑repo contexts
|
|
177
231
|
- `color`: `always` | `never` | `auto`
|
|
178
232
|
- `outputLastMessageFile`: by default the provider sets a temp path and reads it to capture final text reliably
|
|
233
|
+
- Logging (v0.5.0+):
|
|
234
|
+
- `verbose`: Enable debug/info logs (default: `false` for clean output)
|
|
235
|
+
- `logger`: Custom logger object or `false` to disable all logging
|
|
179
236
|
|
|
180
237
|
See [docs/ai-sdk-v5/configuration.md](docs/ai-sdk-v5/configuration.md) for the full list and examples.
|
|
181
238
|
|
package/dist/index.cjs
CHANGED
|
@@ -15,20 +15,59 @@ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentS
|
|
|
15
15
|
|
|
16
16
|
// src/logger.ts
|
|
17
17
|
var defaultLogger = {
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
debug: (message) => console.debug(`[DEBUG] ${message}`),
|
|
19
|
+
info: (message) => console.info(`[INFO] ${message}`),
|
|
20
|
+
warn: (message) => console.warn(`[WARN] ${message}`),
|
|
21
|
+
error: (message) => console.error(`[ERROR] ${message}`)
|
|
20
22
|
};
|
|
21
23
|
var noopLogger = {
|
|
24
|
+
debug: () => {
|
|
25
|
+
},
|
|
26
|
+
info: () => {
|
|
27
|
+
},
|
|
22
28
|
warn: () => {
|
|
23
29
|
},
|
|
24
30
|
error: () => {
|
|
25
31
|
}
|
|
26
32
|
};
|
|
27
33
|
function getLogger(logger) {
|
|
28
|
-
if (logger === false)
|
|
29
|
-
|
|
34
|
+
if (logger === false) {
|
|
35
|
+
return noopLogger;
|
|
36
|
+
}
|
|
37
|
+
if (logger === void 0) {
|
|
38
|
+
return defaultLogger;
|
|
39
|
+
}
|
|
30
40
|
return logger;
|
|
31
41
|
}
|
|
42
|
+
function createVerboseLogger(logger, verbose = false) {
|
|
43
|
+
if (verbose) {
|
|
44
|
+
return logger;
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
debug: () => {
|
|
48
|
+
},
|
|
49
|
+
// No-op when not verbose
|
|
50
|
+
info: () => {
|
|
51
|
+
},
|
|
52
|
+
// No-op when not verbose
|
|
53
|
+
warn: logger.warn.bind(logger),
|
|
54
|
+
error: logger.error.bind(logger)
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
var loggerFunctionSchema = zod.z.object({
|
|
58
|
+
debug: zod.z.any().refine((val) => typeof val === "function", {
|
|
59
|
+
message: "debug must be a function"
|
|
60
|
+
}),
|
|
61
|
+
info: zod.z.any().refine((val) => typeof val === "function", {
|
|
62
|
+
message: "info must be a function"
|
|
63
|
+
}),
|
|
64
|
+
warn: zod.z.any().refine((val) => typeof val === "function", {
|
|
65
|
+
message: "warn must be a function"
|
|
66
|
+
}),
|
|
67
|
+
error: zod.z.any().refine((val) => typeof val === "function", {
|
|
68
|
+
message: "error must be a function"
|
|
69
|
+
})
|
|
70
|
+
});
|
|
32
71
|
var settingsSchema = zod.z.object({
|
|
33
72
|
codexPath: zod.z.string().optional(),
|
|
34
73
|
cwd: zod.z.string().optional(),
|
|
@@ -41,7 +80,7 @@ var settingsSchema = zod.z.object({
|
|
|
41
80
|
allowNpx: zod.z.boolean().optional(),
|
|
42
81
|
env: zod.z.record(zod.z.string(), zod.z.string()).optional(),
|
|
43
82
|
verbose: zod.z.boolean().optional(),
|
|
44
|
-
logger: zod.z.
|
|
83
|
+
logger: zod.z.union([zod.z.literal(false), loggerFunctionSchema]).optional(),
|
|
45
84
|
// NEW: Reasoning & Verbosity
|
|
46
85
|
reasoningEffort: zod.z.enum(["minimal", "low", "medium", "high"]).optional(),
|
|
47
86
|
// Note: API rejects 'concise' and 'none' despite error messages claiming they're valid
|
|
@@ -233,7 +272,8 @@ var CodexCliLanguageModel = class {
|
|
|
233
272
|
constructor(options) {
|
|
234
273
|
this.modelId = options.id;
|
|
235
274
|
this.settings = options.settings ?? {};
|
|
236
|
-
|
|
275
|
+
const baseLogger = getLogger(this.settings.logger);
|
|
276
|
+
this.logger = createVerboseLogger(baseLogger, this.settings.verbose ?? false);
|
|
237
277
|
if (!this.modelId || this.modelId.trim() === "") {
|
|
238
278
|
throw new provider.NoSuchModelError({ modelId: this.modelId, modelType: "languageModel" });
|
|
239
279
|
}
|
|
@@ -625,12 +665,16 @@ var CodexCliLanguageModel = class {
|
|
|
625
665
|
});
|
|
626
666
|
}
|
|
627
667
|
async doGenerate(options) {
|
|
668
|
+
this.logger.debug(`[codex-cli] Starting doGenerate request with model: ${this.modelId}`);
|
|
628
669
|
const { promptText, warnings: mappingWarnings } = mapMessagesToPrompt(options.prompt);
|
|
629
670
|
const promptExcerpt = promptText.slice(0, 200);
|
|
630
671
|
const warnings = [
|
|
631
672
|
...this.mapWarnings(options),
|
|
632
673
|
...mappingWarnings?.map((m) => ({ type: "other", message: m })) || []
|
|
633
674
|
];
|
|
675
|
+
this.logger.debug(
|
|
676
|
+
`[codex-cli] Converted ${options.prompt.length} messages, response format: ${options.responseFormat?.type ?? "none"}`
|
|
677
|
+
);
|
|
634
678
|
const providerOptions = await providerUtils.parseProviderOptions({
|
|
635
679
|
provider: this.provider,
|
|
636
680
|
providerOptions: options.providerOptions,
|
|
@@ -643,9 +687,13 @@ var CodexCliLanguageModel = class {
|
|
|
643
687
|
responseFormat,
|
|
644
688
|
effectiveSettings
|
|
645
689
|
);
|
|
690
|
+
this.logger.debug(
|
|
691
|
+
`[codex-cli] Executing Codex CLI: ${cmd} with ${args.length} arguments, cwd: ${cwd ?? "default"}`
|
|
692
|
+
);
|
|
646
693
|
let text = "";
|
|
647
694
|
const usage = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
|
|
648
695
|
const finishReason = "stop";
|
|
696
|
+
const startTime = Date.now();
|
|
649
697
|
const child = child_process.spawn(cmd, args, { env, cwd, stdio: ["ignore", "pipe", "pipe"] });
|
|
650
698
|
let onAbort;
|
|
651
699
|
if (options.abortSignal) {
|
|
@@ -667,11 +715,14 @@ var CodexCliLanguageModel = class {
|
|
|
667
715
|
for (const line of lines) {
|
|
668
716
|
const event = this.parseExperimentalJsonEvent(line);
|
|
669
717
|
if (!event) continue;
|
|
718
|
+
this.logger.debug(`[codex-cli] Received event type: ${event.type ?? "unknown"}`);
|
|
670
719
|
if (event.type === "thread.started" && typeof event.thread_id === "string") {
|
|
671
720
|
this.sessionId = event.thread_id;
|
|
721
|
+
this.logger.debug(`[codex-cli] Session started: ${this.sessionId}`);
|
|
672
722
|
}
|
|
673
723
|
if (event.type === "session.created" && typeof event.session_id === "string") {
|
|
674
724
|
this.sessionId = event.session_id;
|
|
725
|
+
this.logger.debug(`[codex-cli] Session created: ${this.sessionId}`);
|
|
675
726
|
}
|
|
676
727
|
if (event.type === "turn.completed") {
|
|
677
728
|
const usageEvent = this.extractUsage(event);
|
|
@@ -687,15 +738,21 @@ var CodexCliLanguageModel = class {
|
|
|
687
738
|
if (event.type === "turn.failed") {
|
|
688
739
|
const errorText = event.error && typeof event.error.message === "string" && event.error.message || (typeof event.message === "string" ? event.message : void 0);
|
|
689
740
|
turnFailureMessage = errorText ?? turnFailureMessage ?? "Codex turn failed";
|
|
741
|
+
this.logger.error(`[codex-cli] Turn failed: ${turnFailureMessage}`);
|
|
690
742
|
}
|
|
691
743
|
if (event.type === "error") {
|
|
692
744
|
const errorText = typeof event.message === "string" ? event.message : void 0;
|
|
693
745
|
turnFailureMessage = errorText ?? turnFailureMessage ?? "Codex error";
|
|
746
|
+
this.logger.error(`[codex-cli] Error event: ${turnFailureMessage}`);
|
|
694
747
|
}
|
|
695
748
|
}
|
|
696
749
|
});
|
|
697
|
-
child.on("error", (e) =>
|
|
750
|
+
child.on("error", (e) => {
|
|
751
|
+
this.logger.error(`[codex-cli] Spawn error: ${String(e)}`);
|
|
752
|
+
reject(this.handleSpawnError(e, promptExcerpt));
|
|
753
|
+
});
|
|
698
754
|
child.on("close", (code) => {
|
|
755
|
+
const duration = Date.now() - startTime;
|
|
699
756
|
if (code === 0) {
|
|
700
757
|
if (turnFailureMessage) {
|
|
701
758
|
reject(
|
|
@@ -707,8 +764,15 @@ var CodexCliLanguageModel = class {
|
|
|
707
764
|
);
|
|
708
765
|
return;
|
|
709
766
|
}
|
|
767
|
+
this.logger.info(
|
|
768
|
+
`[codex-cli] Request completed - Session: ${this.sessionId ?? "N/A"}, Duration: ${duration}ms, Tokens: ${usage.totalTokens}`
|
|
769
|
+
);
|
|
770
|
+
this.logger.debug(
|
|
771
|
+
`[codex-cli] Token usage - Input: ${usage.inputTokens}, Output: ${usage.outputTokens}, Total: ${usage.totalTokens}`
|
|
772
|
+
);
|
|
710
773
|
resolve();
|
|
711
774
|
} else {
|
|
775
|
+
this.logger.error(`[codex-cli] Process exited with code ${code} after ${duration}ms`);
|
|
712
776
|
reject(
|
|
713
777
|
createAPICallError({
|
|
714
778
|
message: `Codex CLI exited with code ${code}`,
|
|
@@ -757,12 +821,16 @@ var CodexCliLanguageModel = class {
|
|
|
757
821
|
};
|
|
758
822
|
}
|
|
759
823
|
async doStream(options) {
|
|
824
|
+
this.logger.debug(`[codex-cli] Starting doStream request with model: ${this.modelId}`);
|
|
760
825
|
const { promptText, warnings: mappingWarnings } = mapMessagesToPrompt(options.prompt);
|
|
761
826
|
const promptExcerpt = promptText.slice(0, 200);
|
|
762
827
|
const warnings = [
|
|
763
828
|
...this.mapWarnings(options),
|
|
764
829
|
...mappingWarnings?.map((m) => ({ type: "other", message: m })) || []
|
|
765
830
|
];
|
|
831
|
+
this.logger.debug(
|
|
832
|
+
`[codex-cli] Converted ${options.prompt.length} messages for streaming, response format: ${options.responseFormat?.type ?? "none"}`
|
|
833
|
+
);
|
|
766
834
|
const providerOptions = await providerUtils.parseProviderOptions({
|
|
767
835
|
provider: this.provider,
|
|
768
836
|
providerOptions: options.providerOptions,
|
|
@@ -775,8 +843,12 @@ var CodexCliLanguageModel = class {
|
|
|
775
843
|
responseFormat,
|
|
776
844
|
effectiveSettings
|
|
777
845
|
);
|
|
846
|
+
this.logger.debug(
|
|
847
|
+
`[codex-cli] Executing Codex CLI for streaming: ${cmd} with ${args.length} arguments`
|
|
848
|
+
);
|
|
778
849
|
const stream = new ReadableStream({
|
|
779
850
|
start: (controller) => {
|
|
851
|
+
const startTime = Date.now();
|
|
780
852
|
const child = child_process.spawn(cmd, args, { env, cwd, stdio: ["ignore", "pipe", "pipe"] });
|
|
781
853
|
controller.enqueue({ type: "stream-start", warnings });
|
|
782
854
|
let stderr = "";
|
|
@@ -799,12 +871,18 @@ var CodexCliLanguageModel = class {
|
|
|
799
871
|
if (!item) return;
|
|
800
872
|
if (event.type === "item.completed" && this.getItemType(item) === "assistant_message" && typeof item.text === "string") {
|
|
801
873
|
accumulatedText = item.text;
|
|
874
|
+
this.logger.debug(
|
|
875
|
+
`[codex-cli] Received assistant message, length: ${item.text.length}`
|
|
876
|
+
);
|
|
802
877
|
return;
|
|
803
878
|
}
|
|
804
879
|
const toolName = this.getToolName(item);
|
|
805
880
|
if (!toolName) {
|
|
806
881
|
return;
|
|
807
882
|
}
|
|
883
|
+
this.logger.debug(
|
|
884
|
+
`[codex-cli] Tool detected: ${toolName}, item type: ${this.getItemType(item)}`
|
|
885
|
+
);
|
|
808
886
|
const mapKey = typeof item.id === "string" && item.id.length > 0 ? item.id : crypto.randomUUID();
|
|
809
887
|
let toolState = activeTools.get(mapKey);
|
|
810
888
|
const latestInput = this.buildToolInputPayload(item);
|
|
@@ -823,6 +901,7 @@ var CodexCliLanguageModel = class {
|
|
|
823
901
|
}
|
|
824
902
|
}
|
|
825
903
|
if (!toolState.hasEmittedCall) {
|
|
904
|
+
this.logger.debug(`[codex-cli] Emitting tool invocation: ${toolState.toolName}`);
|
|
826
905
|
this.emitToolInvocation(
|
|
827
906
|
controller,
|
|
828
907
|
toolState.toolCallId,
|
|
@@ -833,6 +912,7 @@ var CodexCliLanguageModel = class {
|
|
|
833
912
|
}
|
|
834
913
|
if (event.type === "item.completed") {
|
|
835
914
|
const { result, metadata } = this.buildToolResultPayload(item);
|
|
915
|
+
this.logger.debug(`[codex-cli] Tool completed: ${toolState.toolName}`);
|
|
836
916
|
this.emitToolResult(
|
|
837
917
|
controller,
|
|
838
918
|
toolState.toolCallId,
|
|
@@ -856,7 +936,11 @@ var CodexCliLanguageModel = class {
|
|
|
856
936
|
options.abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
857
937
|
}
|
|
858
938
|
const finishStream = (code) => {
|
|
939
|
+
const duration = Date.now() - startTime;
|
|
859
940
|
if (code !== 0) {
|
|
941
|
+
this.logger.error(
|
|
942
|
+
`[codex-cli] Stream process exited with code ${code} after ${duration}ms`
|
|
943
|
+
);
|
|
860
944
|
controller.error(
|
|
861
945
|
createAPICallError({
|
|
862
946
|
message: `Codex CLI exited with code ${code}`,
|
|
@@ -868,6 +952,7 @@ var CodexCliLanguageModel = class {
|
|
|
868
952
|
return;
|
|
869
953
|
}
|
|
870
954
|
if (turnFailureMessage) {
|
|
955
|
+
this.logger.error(`[codex-cli] Stream failed: ${turnFailureMessage}`);
|
|
871
956
|
controller.error(
|
|
872
957
|
createAPICallError({
|
|
873
958
|
message: turnFailureMessage,
|
|
@@ -896,6 +981,12 @@ var CodexCliLanguageModel = class {
|
|
|
896
981
|
controller.enqueue({ type: "text-end", id: textId });
|
|
897
982
|
}
|
|
898
983
|
const usageSummary = lastUsage ?? { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
|
|
984
|
+
this.logger.info(
|
|
985
|
+
`[codex-cli] Stream completed - Session: ${this.sessionId ?? "N/A"}, Duration: ${duration}ms, Tokens: ${usageSummary.totalTokens}`
|
|
986
|
+
);
|
|
987
|
+
this.logger.debug(
|
|
988
|
+
`[codex-cli] Token usage - Input: ${usageSummary.inputTokens}, Output: ${usageSummary.outputTokens}, Total: ${usageSummary.totalTokens}`
|
|
989
|
+
);
|
|
899
990
|
controller.enqueue({
|
|
900
991
|
type: "finish",
|
|
901
992
|
finishReason: "stop",
|
|
@@ -910,8 +1001,10 @@ var CodexCliLanguageModel = class {
|
|
|
910
1001
|
for (const line of lines) {
|
|
911
1002
|
const event = this.parseExperimentalJsonEvent(line);
|
|
912
1003
|
if (!event) continue;
|
|
1004
|
+
this.logger.debug(`[codex-cli] Stream event: ${event.type ?? "unknown"}`);
|
|
913
1005
|
if (event.type === "thread.started" && typeof event.thread_id === "string") {
|
|
914
1006
|
this.sessionId = event.thread_id;
|
|
1007
|
+
this.logger.debug(`[codex-cli] Stream session started: ${this.sessionId}`);
|
|
915
1008
|
if (!responseMetadataSent) {
|
|
916
1009
|
responseMetadataSent = true;
|
|
917
1010
|
sendMetadata();
|
|
@@ -920,6 +1013,7 @@ var CodexCliLanguageModel = class {
|
|
|
920
1013
|
}
|
|
921
1014
|
if (event.type === "session.created" && typeof event.session_id === "string") {
|
|
922
1015
|
this.sessionId = event.session_id;
|
|
1016
|
+
this.logger.debug(`[codex-cli] Stream session created: ${this.sessionId}`);
|
|
923
1017
|
if (!responseMetadataSent) {
|
|
924
1018
|
responseMetadataSent = true;
|
|
925
1019
|
sendMetadata();
|
|
@@ -936,6 +1030,7 @@ var CodexCliLanguageModel = class {
|
|
|
936
1030
|
if (event.type === "turn.failed") {
|
|
937
1031
|
const errorText = event.error && typeof event.error.message === "string" && event.error.message || (typeof event.message === "string" ? event.message : void 0);
|
|
938
1032
|
turnFailureMessage = errorText ?? turnFailureMessage ?? "Codex turn failed";
|
|
1033
|
+
this.logger.error(`[codex-cli] Stream turn failed: ${turnFailureMessage}`);
|
|
939
1034
|
sendMetadata({ error: turnFailureMessage });
|
|
940
1035
|
continue;
|
|
941
1036
|
}
|
|
@@ -943,6 +1038,7 @@ var CodexCliLanguageModel = class {
|
|
|
943
1038
|
const errorText = typeof event.message === "string" ? event.message : void 0;
|
|
944
1039
|
const effective = errorText ?? "Codex error";
|
|
945
1040
|
turnFailureMessage = turnFailureMessage ?? effective;
|
|
1041
|
+
this.logger.error(`[codex-cli] Stream error event: ${effective}`);
|
|
946
1042
|
sendMetadata({ error: effective });
|
|
947
1043
|
continue;
|
|
948
1044
|
}
|
|
@@ -960,6 +1056,7 @@ var CodexCliLanguageModel = class {
|
|
|
960
1056
|
}
|
|
961
1057
|
};
|
|
962
1058
|
child.on("error", (e) => {
|
|
1059
|
+
this.logger.error(`[codex-cli] Stream spawn error: ${String(e)}`);
|
|
963
1060
|
if (options.abortSignal) options.abortSignal.removeEventListener("abort", onAbort);
|
|
964
1061
|
cleanupSchema();
|
|
965
1062
|
controller.error(this.handleSpawnError(e, promptExcerpt));
|
package/dist/index.d.cts
CHANGED
|
@@ -1,7 +1,38 @@
|
|
|
1
1
|
import { ProviderV2, LanguageModelV2 } from '@ai-sdk/provider';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Logger interface for custom logging.
|
|
5
|
+
* Allows consumers to provide their own logging implementation
|
|
6
|
+
* or disable logging entirely.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* const customLogger: Logger = {
|
|
11
|
+
* debug: (message) => myLoggingService.debug(message),
|
|
12
|
+
* info: (message) => myLoggingService.info(message),
|
|
13
|
+
* warn: (message) => myLoggingService.warn(message),
|
|
14
|
+
* error: (message) => myLoggingService.error(message),
|
|
15
|
+
* };
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
3
18
|
interface Logger {
|
|
19
|
+
/**
|
|
20
|
+
* Log a debug message. Only logged when verbose mode is enabled.
|
|
21
|
+
* Used for detailed execution tracing and troubleshooting.
|
|
22
|
+
*/
|
|
23
|
+
debug: (message: string) => void;
|
|
24
|
+
/**
|
|
25
|
+
* Log an informational message. Only logged when verbose mode is enabled.
|
|
26
|
+
* Used for general execution flow information.
|
|
27
|
+
*/
|
|
28
|
+
info: (message: string) => void;
|
|
29
|
+
/**
|
|
30
|
+
* Log a warning message.
|
|
31
|
+
*/
|
|
4
32
|
warn: (message: string) => void;
|
|
33
|
+
/**
|
|
34
|
+
* Log an error message.
|
|
35
|
+
*/
|
|
5
36
|
error: (message: string) => void;
|
|
6
37
|
}
|
|
7
38
|
type ApprovalMode = 'untrusted' | 'on-failure' | 'on-request' | 'never';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,38 @@
|
|
|
1
1
|
import { ProviderV2, LanguageModelV2 } from '@ai-sdk/provider';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Logger interface for custom logging.
|
|
5
|
+
* Allows consumers to provide their own logging implementation
|
|
6
|
+
* or disable logging entirely.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* const customLogger: Logger = {
|
|
11
|
+
* debug: (message) => myLoggingService.debug(message),
|
|
12
|
+
* info: (message) => myLoggingService.info(message),
|
|
13
|
+
* warn: (message) => myLoggingService.warn(message),
|
|
14
|
+
* error: (message) => myLoggingService.error(message),
|
|
15
|
+
* };
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
3
18
|
interface Logger {
|
|
19
|
+
/**
|
|
20
|
+
* Log a debug message. Only logged when verbose mode is enabled.
|
|
21
|
+
* Used for detailed execution tracing and troubleshooting.
|
|
22
|
+
*/
|
|
23
|
+
debug: (message: string) => void;
|
|
24
|
+
/**
|
|
25
|
+
* Log an informational message. Only logged when verbose mode is enabled.
|
|
26
|
+
* Used for general execution flow information.
|
|
27
|
+
*/
|
|
28
|
+
info: (message: string) => void;
|
|
29
|
+
/**
|
|
30
|
+
* Log a warning message.
|
|
31
|
+
*/
|
|
4
32
|
warn: (message: string) => void;
|
|
33
|
+
/**
|
|
34
|
+
* Log an error message.
|
|
35
|
+
*/
|
|
5
36
|
error: (message: string) => void;
|
|
6
37
|
}
|
|
7
38
|
type ApprovalMode = 'untrusted' | 'on-failure' | 'on-request' | 'never';
|
package/dist/index.js
CHANGED
|
@@ -12,20 +12,59 @@ import { parseProviderOptions, generateId } from '@ai-sdk/provider-utils';
|
|
|
12
12
|
|
|
13
13
|
// src/logger.ts
|
|
14
14
|
var defaultLogger = {
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
debug: (message) => console.debug(`[DEBUG] ${message}`),
|
|
16
|
+
info: (message) => console.info(`[INFO] ${message}`),
|
|
17
|
+
warn: (message) => console.warn(`[WARN] ${message}`),
|
|
18
|
+
error: (message) => console.error(`[ERROR] ${message}`)
|
|
17
19
|
};
|
|
18
20
|
var noopLogger = {
|
|
21
|
+
debug: () => {
|
|
22
|
+
},
|
|
23
|
+
info: () => {
|
|
24
|
+
},
|
|
19
25
|
warn: () => {
|
|
20
26
|
},
|
|
21
27
|
error: () => {
|
|
22
28
|
}
|
|
23
29
|
};
|
|
24
30
|
function getLogger(logger) {
|
|
25
|
-
if (logger === false)
|
|
26
|
-
|
|
31
|
+
if (logger === false) {
|
|
32
|
+
return noopLogger;
|
|
33
|
+
}
|
|
34
|
+
if (logger === void 0) {
|
|
35
|
+
return defaultLogger;
|
|
36
|
+
}
|
|
27
37
|
return logger;
|
|
28
38
|
}
|
|
39
|
+
function createVerboseLogger(logger, verbose = false) {
|
|
40
|
+
if (verbose) {
|
|
41
|
+
return logger;
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
debug: () => {
|
|
45
|
+
},
|
|
46
|
+
// No-op when not verbose
|
|
47
|
+
info: () => {
|
|
48
|
+
},
|
|
49
|
+
// No-op when not verbose
|
|
50
|
+
warn: logger.warn.bind(logger),
|
|
51
|
+
error: logger.error.bind(logger)
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
var loggerFunctionSchema = z.object({
|
|
55
|
+
debug: z.any().refine((val) => typeof val === "function", {
|
|
56
|
+
message: "debug must be a function"
|
|
57
|
+
}),
|
|
58
|
+
info: z.any().refine((val) => typeof val === "function", {
|
|
59
|
+
message: "info must be a function"
|
|
60
|
+
}),
|
|
61
|
+
warn: z.any().refine((val) => typeof val === "function", {
|
|
62
|
+
message: "warn must be a function"
|
|
63
|
+
}),
|
|
64
|
+
error: z.any().refine((val) => typeof val === "function", {
|
|
65
|
+
message: "error must be a function"
|
|
66
|
+
})
|
|
67
|
+
});
|
|
29
68
|
var settingsSchema = z.object({
|
|
30
69
|
codexPath: z.string().optional(),
|
|
31
70
|
cwd: z.string().optional(),
|
|
@@ -38,7 +77,7 @@ var settingsSchema = z.object({
|
|
|
38
77
|
allowNpx: z.boolean().optional(),
|
|
39
78
|
env: z.record(z.string(), z.string()).optional(),
|
|
40
79
|
verbose: z.boolean().optional(),
|
|
41
|
-
logger: z.
|
|
80
|
+
logger: z.union([z.literal(false), loggerFunctionSchema]).optional(),
|
|
42
81
|
// NEW: Reasoning & Verbosity
|
|
43
82
|
reasoningEffort: z.enum(["minimal", "low", "medium", "high"]).optional(),
|
|
44
83
|
// Note: API rejects 'concise' and 'none' despite error messages claiming they're valid
|
|
@@ -230,7 +269,8 @@ var CodexCliLanguageModel = class {
|
|
|
230
269
|
constructor(options) {
|
|
231
270
|
this.modelId = options.id;
|
|
232
271
|
this.settings = options.settings ?? {};
|
|
233
|
-
|
|
272
|
+
const baseLogger = getLogger(this.settings.logger);
|
|
273
|
+
this.logger = createVerboseLogger(baseLogger, this.settings.verbose ?? false);
|
|
234
274
|
if (!this.modelId || this.modelId.trim() === "") {
|
|
235
275
|
throw new NoSuchModelError({ modelId: this.modelId, modelType: "languageModel" });
|
|
236
276
|
}
|
|
@@ -622,12 +662,16 @@ var CodexCliLanguageModel = class {
|
|
|
622
662
|
});
|
|
623
663
|
}
|
|
624
664
|
async doGenerate(options) {
|
|
665
|
+
this.logger.debug(`[codex-cli] Starting doGenerate request with model: ${this.modelId}`);
|
|
625
666
|
const { promptText, warnings: mappingWarnings } = mapMessagesToPrompt(options.prompt);
|
|
626
667
|
const promptExcerpt = promptText.slice(0, 200);
|
|
627
668
|
const warnings = [
|
|
628
669
|
...this.mapWarnings(options),
|
|
629
670
|
...mappingWarnings?.map((m) => ({ type: "other", message: m })) || []
|
|
630
671
|
];
|
|
672
|
+
this.logger.debug(
|
|
673
|
+
`[codex-cli] Converted ${options.prompt.length} messages, response format: ${options.responseFormat?.type ?? "none"}`
|
|
674
|
+
);
|
|
631
675
|
const providerOptions = await parseProviderOptions({
|
|
632
676
|
provider: this.provider,
|
|
633
677
|
providerOptions: options.providerOptions,
|
|
@@ -640,9 +684,13 @@ var CodexCliLanguageModel = class {
|
|
|
640
684
|
responseFormat,
|
|
641
685
|
effectiveSettings
|
|
642
686
|
);
|
|
687
|
+
this.logger.debug(
|
|
688
|
+
`[codex-cli] Executing Codex CLI: ${cmd} with ${args.length} arguments, cwd: ${cwd ?? "default"}`
|
|
689
|
+
);
|
|
643
690
|
let text = "";
|
|
644
691
|
const usage = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
|
|
645
692
|
const finishReason = "stop";
|
|
693
|
+
const startTime = Date.now();
|
|
646
694
|
const child = spawn(cmd, args, { env, cwd, stdio: ["ignore", "pipe", "pipe"] });
|
|
647
695
|
let onAbort;
|
|
648
696
|
if (options.abortSignal) {
|
|
@@ -664,11 +712,14 @@ var CodexCliLanguageModel = class {
|
|
|
664
712
|
for (const line of lines) {
|
|
665
713
|
const event = this.parseExperimentalJsonEvent(line);
|
|
666
714
|
if (!event) continue;
|
|
715
|
+
this.logger.debug(`[codex-cli] Received event type: ${event.type ?? "unknown"}`);
|
|
667
716
|
if (event.type === "thread.started" && typeof event.thread_id === "string") {
|
|
668
717
|
this.sessionId = event.thread_id;
|
|
718
|
+
this.logger.debug(`[codex-cli] Session started: ${this.sessionId}`);
|
|
669
719
|
}
|
|
670
720
|
if (event.type === "session.created" && typeof event.session_id === "string") {
|
|
671
721
|
this.sessionId = event.session_id;
|
|
722
|
+
this.logger.debug(`[codex-cli] Session created: ${this.sessionId}`);
|
|
672
723
|
}
|
|
673
724
|
if (event.type === "turn.completed") {
|
|
674
725
|
const usageEvent = this.extractUsage(event);
|
|
@@ -684,15 +735,21 @@ var CodexCliLanguageModel = class {
|
|
|
684
735
|
if (event.type === "turn.failed") {
|
|
685
736
|
const errorText = event.error && typeof event.error.message === "string" && event.error.message || (typeof event.message === "string" ? event.message : void 0);
|
|
686
737
|
turnFailureMessage = errorText ?? turnFailureMessage ?? "Codex turn failed";
|
|
738
|
+
this.logger.error(`[codex-cli] Turn failed: ${turnFailureMessage}`);
|
|
687
739
|
}
|
|
688
740
|
if (event.type === "error") {
|
|
689
741
|
const errorText = typeof event.message === "string" ? event.message : void 0;
|
|
690
742
|
turnFailureMessage = errorText ?? turnFailureMessage ?? "Codex error";
|
|
743
|
+
this.logger.error(`[codex-cli] Error event: ${turnFailureMessage}`);
|
|
691
744
|
}
|
|
692
745
|
}
|
|
693
746
|
});
|
|
694
|
-
child.on("error", (e) =>
|
|
747
|
+
child.on("error", (e) => {
|
|
748
|
+
this.logger.error(`[codex-cli] Spawn error: ${String(e)}`);
|
|
749
|
+
reject(this.handleSpawnError(e, promptExcerpt));
|
|
750
|
+
});
|
|
695
751
|
child.on("close", (code) => {
|
|
752
|
+
const duration = Date.now() - startTime;
|
|
696
753
|
if (code === 0) {
|
|
697
754
|
if (turnFailureMessage) {
|
|
698
755
|
reject(
|
|
@@ -704,8 +761,15 @@ var CodexCliLanguageModel = class {
|
|
|
704
761
|
);
|
|
705
762
|
return;
|
|
706
763
|
}
|
|
764
|
+
this.logger.info(
|
|
765
|
+
`[codex-cli] Request completed - Session: ${this.sessionId ?? "N/A"}, Duration: ${duration}ms, Tokens: ${usage.totalTokens}`
|
|
766
|
+
);
|
|
767
|
+
this.logger.debug(
|
|
768
|
+
`[codex-cli] Token usage - Input: ${usage.inputTokens}, Output: ${usage.outputTokens}, Total: ${usage.totalTokens}`
|
|
769
|
+
);
|
|
707
770
|
resolve();
|
|
708
771
|
} else {
|
|
772
|
+
this.logger.error(`[codex-cli] Process exited with code ${code} after ${duration}ms`);
|
|
709
773
|
reject(
|
|
710
774
|
createAPICallError({
|
|
711
775
|
message: `Codex CLI exited with code ${code}`,
|
|
@@ -754,12 +818,16 @@ var CodexCliLanguageModel = class {
|
|
|
754
818
|
};
|
|
755
819
|
}
|
|
756
820
|
async doStream(options) {
|
|
821
|
+
this.logger.debug(`[codex-cli] Starting doStream request with model: ${this.modelId}`);
|
|
757
822
|
const { promptText, warnings: mappingWarnings } = mapMessagesToPrompt(options.prompt);
|
|
758
823
|
const promptExcerpt = promptText.slice(0, 200);
|
|
759
824
|
const warnings = [
|
|
760
825
|
...this.mapWarnings(options),
|
|
761
826
|
...mappingWarnings?.map((m) => ({ type: "other", message: m })) || []
|
|
762
827
|
];
|
|
828
|
+
this.logger.debug(
|
|
829
|
+
`[codex-cli] Converted ${options.prompt.length} messages for streaming, response format: ${options.responseFormat?.type ?? "none"}`
|
|
830
|
+
);
|
|
763
831
|
const providerOptions = await parseProviderOptions({
|
|
764
832
|
provider: this.provider,
|
|
765
833
|
providerOptions: options.providerOptions,
|
|
@@ -772,8 +840,12 @@ var CodexCliLanguageModel = class {
|
|
|
772
840
|
responseFormat,
|
|
773
841
|
effectiveSettings
|
|
774
842
|
);
|
|
843
|
+
this.logger.debug(
|
|
844
|
+
`[codex-cli] Executing Codex CLI for streaming: ${cmd} with ${args.length} arguments`
|
|
845
|
+
);
|
|
775
846
|
const stream = new ReadableStream({
|
|
776
847
|
start: (controller) => {
|
|
848
|
+
const startTime = Date.now();
|
|
777
849
|
const child = spawn(cmd, args, { env, cwd, stdio: ["ignore", "pipe", "pipe"] });
|
|
778
850
|
controller.enqueue({ type: "stream-start", warnings });
|
|
779
851
|
let stderr = "";
|
|
@@ -796,12 +868,18 @@ var CodexCliLanguageModel = class {
|
|
|
796
868
|
if (!item) return;
|
|
797
869
|
if (event.type === "item.completed" && this.getItemType(item) === "assistant_message" && typeof item.text === "string") {
|
|
798
870
|
accumulatedText = item.text;
|
|
871
|
+
this.logger.debug(
|
|
872
|
+
`[codex-cli] Received assistant message, length: ${item.text.length}`
|
|
873
|
+
);
|
|
799
874
|
return;
|
|
800
875
|
}
|
|
801
876
|
const toolName = this.getToolName(item);
|
|
802
877
|
if (!toolName) {
|
|
803
878
|
return;
|
|
804
879
|
}
|
|
880
|
+
this.logger.debug(
|
|
881
|
+
`[codex-cli] Tool detected: ${toolName}, item type: ${this.getItemType(item)}`
|
|
882
|
+
);
|
|
805
883
|
const mapKey = typeof item.id === "string" && item.id.length > 0 ? item.id : randomUUID();
|
|
806
884
|
let toolState = activeTools.get(mapKey);
|
|
807
885
|
const latestInput = this.buildToolInputPayload(item);
|
|
@@ -820,6 +898,7 @@ var CodexCliLanguageModel = class {
|
|
|
820
898
|
}
|
|
821
899
|
}
|
|
822
900
|
if (!toolState.hasEmittedCall) {
|
|
901
|
+
this.logger.debug(`[codex-cli] Emitting tool invocation: ${toolState.toolName}`);
|
|
823
902
|
this.emitToolInvocation(
|
|
824
903
|
controller,
|
|
825
904
|
toolState.toolCallId,
|
|
@@ -830,6 +909,7 @@ var CodexCliLanguageModel = class {
|
|
|
830
909
|
}
|
|
831
910
|
if (event.type === "item.completed") {
|
|
832
911
|
const { result, metadata } = this.buildToolResultPayload(item);
|
|
912
|
+
this.logger.debug(`[codex-cli] Tool completed: ${toolState.toolName}`);
|
|
833
913
|
this.emitToolResult(
|
|
834
914
|
controller,
|
|
835
915
|
toolState.toolCallId,
|
|
@@ -853,7 +933,11 @@ var CodexCliLanguageModel = class {
|
|
|
853
933
|
options.abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
854
934
|
}
|
|
855
935
|
const finishStream = (code) => {
|
|
936
|
+
const duration = Date.now() - startTime;
|
|
856
937
|
if (code !== 0) {
|
|
938
|
+
this.logger.error(
|
|
939
|
+
`[codex-cli] Stream process exited with code ${code} after ${duration}ms`
|
|
940
|
+
);
|
|
857
941
|
controller.error(
|
|
858
942
|
createAPICallError({
|
|
859
943
|
message: `Codex CLI exited with code ${code}`,
|
|
@@ -865,6 +949,7 @@ var CodexCliLanguageModel = class {
|
|
|
865
949
|
return;
|
|
866
950
|
}
|
|
867
951
|
if (turnFailureMessage) {
|
|
952
|
+
this.logger.error(`[codex-cli] Stream failed: ${turnFailureMessage}`);
|
|
868
953
|
controller.error(
|
|
869
954
|
createAPICallError({
|
|
870
955
|
message: turnFailureMessage,
|
|
@@ -893,6 +978,12 @@ var CodexCliLanguageModel = class {
|
|
|
893
978
|
controller.enqueue({ type: "text-end", id: textId });
|
|
894
979
|
}
|
|
895
980
|
const usageSummary = lastUsage ?? { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
|
|
981
|
+
this.logger.info(
|
|
982
|
+
`[codex-cli] Stream completed - Session: ${this.sessionId ?? "N/A"}, Duration: ${duration}ms, Tokens: ${usageSummary.totalTokens}`
|
|
983
|
+
);
|
|
984
|
+
this.logger.debug(
|
|
985
|
+
`[codex-cli] Token usage - Input: ${usageSummary.inputTokens}, Output: ${usageSummary.outputTokens}, Total: ${usageSummary.totalTokens}`
|
|
986
|
+
);
|
|
896
987
|
controller.enqueue({
|
|
897
988
|
type: "finish",
|
|
898
989
|
finishReason: "stop",
|
|
@@ -907,8 +998,10 @@ var CodexCliLanguageModel = class {
|
|
|
907
998
|
for (const line of lines) {
|
|
908
999
|
const event = this.parseExperimentalJsonEvent(line);
|
|
909
1000
|
if (!event) continue;
|
|
1001
|
+
this.logger.debug(`[codex-cli] Stream event: ${event.type ?? "unknown"}`);
|
|
910
1002
|
if (event.type === "thread.started" && typeof event.thread_id === "string") {
|
|
911
1003
|
this.sessionId = event.thread_id;
|
|
1004
|
+
this.logger.debug(`[codex-cli] Stream session started: ${this.sessionId}`);
|
|
912
1005
|
if (!responseMetadataSent) {
|
|
913
1006
|
responseMetadataSent = true;
|
|
914
1007
|
sendMetadata();
|
|
@@ -917,6 +1010,7 @@ var CodexCliLanguageModel = class {
|
|
|
917
1010
|
}
|
|
918
1011
|
if (event.type === "session.created" && typeof event.session_id === "string") {
|
|
919
1012
|
this.sessionId = event.session_id;
|
|
1013
|
+
this.logger.debug(`[codex-cli] Stream session created: ${this.sessionId}`);
|
|
920
1014
|
if (!responseMetadataSent) {
|
|
921
1015
|
responseMetadataSent = true;
|
|
922
1016
|
sendMetadata();
|
|
@@ -933,6 +1027,7 @@ var CodexCliLanguageModel = class {
|
|
|
933
1027
|
if (event.type === "turn.failed") {
|
|
934
1028
|
const errorText = event.error && typeof event.error.message === "string" && event.error.message || (typeof event.message === "string" ? event.message : void 0);
|
|
935
1029
|
turnFailureMessage = errorText ?? turnFailureMessage ?? "Codex turn failed";
|
|
1030
|
+
this.logger.error(`[codex-cli] Stream turn failed: ${turnFailureMessage}`);
|
|
936
1031
|
sendMetadata({ error: turnFailureMessage });
|
|
937
1032
|
continue;
|
|
938
1033
|
}
|
|
@@ -940,6 +1035,7 @@ var CodexCliLanguageModel = class {
|
|
|
940
1035
|
const errorText = typeof event.message === "string" ? event.message : void 0;
|
|
941
1036
|
const effective = errorText ?? "Codex error";
|
|
942
1037
|
turnFailureMessage = turnFailureMessage ?? effective;
|
|
1038
|
+
this.logger.error(`[codex-cli] Stream error event: ${effective}`);
|
|
943
1039
|
sendMetadata({ error: effective });
|
|
944
1040
|
continue;
|
|
945
1041
|
}
|
|
@@ -957,6 +1053,7 @@ var CodexCliLanguageModel = class {
|
|
|
957
1053
|
}
|
|
958
1054
|
};
|
|
959
1055
|
child.on("error", (e) => {
|
|
1056
|
+
this.logger.error(`[codex-cli] Stream spawn error: ${String(e)}`);
|
|
960
1057
|
if (options.abortSignal) options.abortSignal.removeEventListener("abort", onAbort);
|
|
961
1058
|
cleanupSchema();
|
|
962
1059
|
controller.error(this.handleSpawnError(e, promptExcerpt));
|