@ouro.bot/cli 0.1.0-alpha.412 → 0.1.0-alpha.414
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/changelog.json +18 -0
- package/dist/heart/daemon/agent-config-check.js +1 -1
- package/dist/heart/daemon/cli-exec.js +26 -9
- package/dist/heart/daemon/daemon-runtime-sync.js +20 -0
- package/dist/heart/daemon/up-progress.js +12 -1
- package/dist/heart/provider-credentials.js +3 -0
- package/dist/heart/providers/openai-codex.js +25 -11
- package/package.json +1 -1
package/changelog.json
CHANGED
|
@@ -1,6 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_note": "This changelog is maintained as part of the PR/version-bump workflow. Agent-curated, not auto-generated. Agents read this file directly via read_file to understand what changed between versions.",
|
|
3
3
|
"versions": [
|
|
4
|
+
{
|
|
5
|
+
"version": "0.1.0-alpha.414",
|
|
6
|
+
"changes": [
|
|
7
|
+
"`openai-codex` live checks now use the same Responses API request shape as real Codex turns, so `ouro up` and `ouro auth verify` stop reporting false `400 status code (no body)` failures for healthy GPT-5.4 credentials.",
|
|
8
|
+
"`ouro up` now detects daemon roster drift when enabled agent bundles change on disk and restarts the stale daemon, which removes ghost agents like a locally-removed `ouroboros` from the repair queue instead of pretending they still live here.",
|
|
9
|
+
"Bundle skeleton contract coverage now validates whichever local agent bundles actually exist, so one-agent machines can keep `slugger` installed without failing the full test suite just because `ouroboros` was intentionally removed.",
|
|
10
|
+
"Expanded Codex provider runtime coverage to exercise the real turn path, preserved output-item turn state, runtime error classification, and the ping callback bundle, which keeps the global 100% coverage gate green for this repair.",
|
|
11
|
+
"`@ouro.bot/cli` and the `ouro.bot` wrapper are version-synced for the Codex live-check and daemon-roster drift repair release."
|
|
12
|
+
]
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"version": "0.1.0-alpha.413",
|
|
16
|
+
"changes": [
|
|
17
|
+
"`ouro up` now shows real-time per-agent progress during provider checks -- the spinner displays which agent and vault operation is in progress (`slugger: reading vault items...`) instead of a static label with no detail.",
|
|
18
|
+
"Added `onProgress` callback to `refreshProviderCredentialPool` and `updateDetail()` method to `UpProgress` for sub-step spinner detail, threaded through the full `executeUpCommand` -> `checkAgentProviders` -> provider health check chain.",
|
|
19
|
+
"`@ouro.bot/cli` and the `ouro.bot` wrapper are version-synced for the progress threading release."
|
|
20
|
+
]
|
|
21
|
+
},
|
|
4
22
|
{
|
|
5
23
|
"version": "0.1.0-alpha.412",
|
|
6
24
|
"changes": [
|
|
@@ -332,7 +332,7 @@ async function checkAgentConfigWithProviderHealth(agentName, bundlesRoot, deps =
|
|
|
332
332
|
if (stateResult.disabled)
|
|
333
333
|
return { ok: true };
|
|
334
334
|
const ping = deps.pingProvider ?? (await Promise.resolve().then(() => __importStar(require("../provider-ping")))).pingProvider;
|
|
335
|
-
const poolResult = await (0, provider_credentials_1.refreshProviderCredentialPool)(agentName);
|
|
335
|
+
const poolResult = await (0, provider_credentials_1.refreshProviderCredentialPool)(agentName, deps.onProgress ? { onProgress: deps.onProgress } : undefined);
|
|
336
336
|
const pingGroups = new Map();
|
|
337
337
|
const lanes = ["outward", "inner"];
|
|
338
338
|
for (const lane of lanes) {
|
|
@@ -98,13 +98,14 @@ const DEFAULT_DAEMON_STARTUP_POLL_INTERVAL_MS = 500;
|
|
|
98
98
|
const DEFAULT_DAEMON_STARTUP_STABILITY_WINDOW_MS = 1_500;
|
|
99
99
|
const DEFAULT_DAEMON_STARTUP_RETRY_LIMIT = 1;
|
|
100
100
|
const DEFAULT_DAEMON_STARTUP_LOG_LINES = 10;
|
|
101
|
-
async function checkAgentProviders(deps, agentsOverride) {
|
|
101
|
+
async function checkAgentProviders(deps, agentsOverride, onProgress) {
|
|
102
102
|
const agents = agentsOverride ?? await listCliAgents(deps);
|
|
103
103
|
const bundlesRoot = deps.bundlesRoot ?? (0, identity_1.getAgentBundlesRoot)();
|
|
104
104
|
const degraded = [];
|
|
105
105
|
for (const agent of [...new Set(agents)]) {
|
|
106
106
|
try {
|
|
107
|
-
|
|
107
|
+
onProgress?.(`${agent}: checking providers...`);
|
|
108
|
+
const result = await checkAgentProviderHealth(agent, bundlesRoot, deps, onProgress);
|
|
108
109
|
if (result.ok)
|
|
109
110
|
continue;
|
|
110
111
|
const errorReason = result.error ?? "agent provider health check failed";
|
|
@@ -136,9 +137,14 @@ async function checkAgentProviders(deps, agentsOverride) {
|
|
|
136
137
|
}
|
|
137
138
|
return degraded;
|
|
138
139
|
}
|
|
139
|
-
async function checkAgentProviderHealth(agentName, bundlesRoot, deps) {
|
|
140
|
-
|
|
141
|
-
|
|
140
|
+
async function checkAgentProviderHealth(agentName, bundlesRoot, deps, onProgress) {
|
|
141
|
+
const liveDeps = {};
|
|
142
|
+
if (deps.homeDir)
|
|
143
|
+
liveDeps.homeDir = deps.homeDir;
|
|
144
|
+
if (onProgress)
|
|
145
|
+
liveDeps.onProgress = onProgress;
|
|
146
|
+
if (liveDeps.homeDir || liveDeps.onProgress) {
|
|
147
|
+
return (0, agent_config_check_1.checkAgentConfigWithProviderHealth)(agentName, bundlesRoot, liveDeps);
|
|
142
148
|
}
|
|
143
149
|
return (0, agent_config_check_1.checkAgentConfigWithProviderHealth)(agentName, bundlesRoot);
|
|
144
150
|
}
|
|
@@ -151,8 +157,12 @@ async function listCliAgents(deps) {
|
|
|
151
157
|
}
|
|
152
158
|
return [];
|
|
153
159
|
}
|
|
154
|
-
|
|
155
|
-
|
|
160
|
+
function managedAgentsSignature(agentNames) {
|
|
161
|
+
const unique = [...new Set(agentNames.map((agent) => agent.trim()).filter((agent) => agent.length > 0))].sort();
|
|
162
|
+
return unique.length > 0 ? unique.join(",") : "(none)";
|
|
163
|
+
}
|
|
164
|
+
async function checkAlreadyRunningAgentProviders(deps, onProgress) {
|
|
165
|
+
return checkAgentProviders(deps, undefined, onProgress);
|
|
156
166
|
}
|
|
157
167
|
function readinessIssueFromDegraded(entry) {
|
|
158
168
|
return entry.issue ?? (0, readiness_repair_1.genericReadinessIssue)({
|
|
@@ -261,6 +271,9 @@ async function ensureDaemonRunning(deps, options = {}) {
|
|
|
261
271
|
const alive = options.initialAlive ?? await deps.checkSocketAlive(deps.socketPath);
|
|
262
272
|
if (alive) {
|
|
263
273
|
const localRuntime = (0, runtime_metadata_1.getRuntimeMetadata)();
|
|
274
|
+
const localManagedAgents = managedAgentsSignature((0, agent_discovery_1.listEnabledBundleAgents)({
|
|
275
|
+
bundlesRoot: deps.bundlesRoot ?? (0, identity_1.getAgentBundlesRoot)(),
|
|
276
|
+
}));
|
|
264
277
|
let runningRuntimePromise = null;
|
|
265
278
|
const fetchRunningRuntimeMetadata = async () => {
|
|
266
279
|
runningRuntimePromise ??= (async () => {
|
|
@@ -271,6 +284,9 @@ async function ensureDaemonRunning(deps, options = {}) {
|
|
|
271
284
|
lastUpdated: payload?.overview.lastUpdated ?? "unknown",
|
|
272
285
|
repoRoot: payload?.overview.repoRoot ?? "unknown",
|
|
273
286
|
configFingerprint: payload?.overview.configFingerprint ?? "unknown",
|
|
287
|
+
managedAgents: payload && payload.workers.length > 0
|
|
288
|
+
? managedAgentsSignature(payload.workers.map((worker) => worker.agent))
|
|
289
|
+
: "unknown",
|
|
274
290
|
};
|
|
275
291
|
})();
|
|
276
292
|
return runningRuntimePromise;
|
|
@@ -281,6 +297,7 @@ async function ensureDaemonRunning(deps, options = {}) {
|
|
|
281
297
|
localLastUpdated: localRuntime.lastUpdated,
|
|
282
298
|
localRepoRoot: localRuntime.repoRoot,
|
|
283
299
|
localConfigFingerprint: localRuntime.configFingerprint,
|
|
300
|
+
localManagedAgents,
|
|
284
301
|
fetchRunningVersion: async () => (await fetchRunningRuntimeMetadata()).version,
|
|
285
302
|
fetchRunningRuntimeMetadata,
|
|
286
303
|
stopDaemon: async () => {
|
|
@@ -2578,7 +2595,7 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
|
|
|
2578
2595
|
let providerChecksAlreadyRun = false;
|
|
2579
2596
|
if (!daemonAliveBeforeStart) {
|
|
2580
2597
|
progress.startPhase("provider checks");
|
|
2581
|
-
const preflightProviderDegraded = await checkAgentProviders(deps);
|
|
2598
|
+
const preflightProviderDegraded = await checkAgentProviders(deps, undefined, (msg) => progress.updateDetail(msg));
|
|
2582
2599
|
providerChecksAlreadyRun = true;
|
|
2583
2600
|
progress.completePhase("provider checks", providerRepairCountSummary(preflightProviderDegraded.length));
|
|
2584
2601
|
if (preflightProviderDegraded.length > 0) {
|
|
@@ -2616,7 +2633,7 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
|
|
|
2616
2633
|
}, { initialAlive: daemonAliveBeforeStart });
|
|
2617
2634
|
if (!providerChecksAlreadyRun || daemonResult.alreadyRunning) {
|
|
2618
2635
|
progress.startPhase("provider checks");
|
|
2619
|
-
const providerDegraded = await checkAlreadyRunningAgentProviders(deps);
|
|
2636
|
+
const providerDegraded = await checkAlreadyRunningAgentProviders(deps, (msg) => progress.updateDetail(msg));
|
|
2620
2637
|
daemonResult.stability = mergeStartupStability(daemonResult.stability, providerDegraded);
|
|
2621
2638
|
progress.completePhase("provider checks", providerRepairCountSummary(providerDegraded.length));
|
|
2622
2639
|
}
|
|
@@ -32,6 +32,7 @@ function normalizeRuntimeIdentity(value) {
|
|
|
32
32
|
lastUpdated: typeof value.lastUpdated === "string" ? value.lastUpdated : "unknown",
|
|
33
33
|
repoRoot: typeof value.repoRoot === "string" ? value.repoRoot : "unknown",
|
|
34
34
|
configFingerprint: typeof value.configFingerprint === "string" ? value.configFingerprint : "unknown",
|
|
35
|
+
managedAgents: typeof value.managedAgents === "string" ? value.managedAgents : "unknown",
|
|
35
36
|
};
|
|
36
37
|
}
|
|
37
38
|
async function readRunningRuntimeIdentity(deps) {
|
|
@@ -70,6 +71,16 @@ function collectRuntimeDriftReasons(local, running) {
|
|
|
70
71
|
running: running.configFingerprint,
|
|
71
72
|
});
|
|
72
73
|
}
|
|
74
|
+
if (isKnownRuntimeValue(local.managedAgents)
|
|
75
|
+
&& isKnownRuntimeValue(running.managedAgents)
|
|
76
|
+
&& local.managedAgents !== running.managedAgents) {
|
|
77
|
+
reasons.push({
|
|
78
|
+
key: "managedAgents",
|
|
79
|
+
label: "managed agents",
|
|
80
|
+
local: local.managedAgents,
|
|
81
|
+
running: running.managedAgents,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
73
84
|
return reasons;
|
|
74
85
|
}
|
|
75
86
|
function formatRuntimeValue(reason) {
|
|
@@ -87,6 +98,7 @@ async function ensureCurrentDaemonRuntime(deps) {
|
|
|
87
98
|
lastUpdated: deps.localLastUpdated,
|
|
88
99
|
repoRoot: deps.localRepoRoot,
|
|
89
100
|
configFingerprint: deps.localConfigFingerprint,
|
|
101
|
+
managedAgents: deps.localManagedAgents,
|
|
90
102
|
});
|
|
91
103
|
try {
|
|
92
104
|
const runningRuntime = await readRunningRuntimeIdentity(deps);
|
|
@@ -118,10 +130,12 @@ async function ensureCurrentDaemonRuntime(deps) {
|
|
|
118
130
|
localLastUpdated: localRuntime.lastUpdated,
|
|
119
131
|
localRepoRoot: localRuntime.repoRoot,
|
|
120
132
|
localConfigFingerprint: localRuntime.configFingerprint,
|
|
133
|
+
localManagedAgents: localRuntime.managedAgents,
|
|
121
134
|
runningVersion,
|
|
122
135
|
runningLastUpdated: runningRuntime.lastUpdated,
|
|
123
136
|
runningRepoRoot: runningRuntime.repoRoot,
|
|
124
137
|
runningConfigFingerprint: runningRuntime.configFingerprint,
|
|
138
|
+
runningManagedAgents: runningRuntime.managedAgents,
|
|
125
139
|
action: "stale_replace_failed",
|
|
126
140
|
driftKeys: driftReasons.map((entry) => entry.key),
|
|
127
141
|
reason,
|
|
@@ -153,10 +167,12 @@ async function ensureCurrentDaemonRuntime(deps) {
|
|
|
153
167
|
localLastUpdated: localRuntime.lastUpdated,
|
|
154
168
|
localRepoRoot: localRuntime.repoRoot,
|
|
155
169
|
localConfigFingerprint: localRuntime.configFingerprint,
|
|
170
|
+
localManagedAgents: localRuntime.managedAgents,
|
|
156
171
|
runningVersion,
|
|
157
172
|
runningLastUpdated: runningRuntime.lastUpdated,
|
|
158
173
|
runningRepoRoot: runningRuntime.repoRoot,
|
|
159
174
|
runningConfigFingerprint: runningRuntime.configFingerprint,
|
|
175
|
+
runningManagedAgents: runningRuntime.managedAgents,
|
|
160
176
|
action: "stale_restarted",
|
|
161
177
|
driftKeys: driftReasons.map((entry) => entry.key),
|
|
162
178
|
pid: started.pid ?? null,
|
|
@@ -179,10 +195,12 @@ async function ensureCurrentDaemonRuntime(deps) {
|
|
|
179
195
|
localLastUpdated: localRuntime.lastUpdated,
|
|
180
196
|
localRepoRoot: localRuntime.repoRoot,
|
|
181
197
|
localConfigFingerprint: localRuntime.configFingerprint,
|
|
198
|
+
localManagedAgents: localRuntime.managedAgents,
|
|
182
199
|
runningVersion,
|
|
183
200
|
runningLastUpdated: runningRuntime.lastUpdated,
|
|
184
201
|
runningRepoRoot: runningRuntime.repoRoot,
|
|
185
202
|
runningConfigFingerprint: runningRuntime.configFingerprint,
|
|
203
|
+
runningManagedAgents: runningRuntime.managedAgents,
|
|
186
204
|
action: "unknown_version",
|
|
187
205
|
},
|
|
188
206
|
});
|
|
@@ -206,6 +224,7 @@ async function ensureCurrentDaemonRuntime(deps) {
|
|
|
206
224
|
localLastUpdated: localRuntime.lastUpdated,
|
|
207
225
|
localRepoRoot: localRuntime.repoRoot,
|
|
208
226
|
localConfigFingerprint: localRuntime.configFingerprint,
|
|
227
|
+
localManagedAgents: localRuntime.managedAgents,
|
|
209
228
|
action: "status_lookup_failed",
|
|
210
229
|
reason,
|
|
211
230
|
},
|
|
@@ -228,6 +247,7 @@ async function ensureCurrentDaemonRuntime(deps) {
|
|
|
228
247
|
localLastUpdated: localRuntime.lastUpdated,
|
|
229
248
|
localRepoRoot: localRuntime.repoRoot,
|
|
230
249
|
localConfigFingerprint: localRuntime.configFingerprint,
|
|
250
|
+
localManagedAgents: localRuntime.managedAgents,
|
|
231
251
|
action: "already_current",
|
|
232
252
|
},
|
|
233
253
|
});
|
|
@@ -52,6 +52,16 @@ class UpProgress {
|
|
|
52
52
|
return;
|
|
53
53
|
this.write(label);
|
|
54
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* Update the sub-step detail on the current spinner phase. Rendered as
|
|
57
|
+
* "label (Xs) -- detail" in TTY mode. No-op in non-TTY mode or when
|
|
58
|
+
* no phase is active.
|
|
59
|
+
*/
|
|
60
|
+
updateDetail(detail) {
|
|
61
|
+
if (!this.isTTY || !this.currentPhase)
|
|
62
|
+
return;
|
|
63
|
+
this.currentPhase.detail = detail;
|
|
64
|
+
}
|
|
55
65
|
/**
|
|
56
66
|
* Mark the current phase as done. In non-TTY mode, immediately writes
|
|
57
67
|
* a static line. Emits a nerves event for observability.
|
|
@@ -94,7 +104,8 @@ class UpProgress {
|
|
|
94
104
|
const elapsedSec = (elapsed / 1000).toFixed(1);
|
|
95
105
|
const frameIndex = Math.floor(elapsed / 80) % SPINNER_FRAMES.length;
|
|
96
106
|
const spinner = SPINNER_FRAMES[frameIndex];
|
|
97
|
-
|
|
107
|
+
const detailSuffix = this.currentPhase.detail ? ` \u2014 ${this.currentPhase.detail}` : "";
|
|
108
|
+
lines.push(` ${BOLD}${spinner}${RESET} ${this.currentPhase.label} ${DIM}(${elapsedSec}s)${detailSuffix}${RESET}`);
|
|
98
109
|
}
|
|
99
110
|
let output = "";
|
|
100
111
|
if (this.prevLineCount > 0) {
|
|
@@ -224,6 +224,7 @@ function readProviderCredentialPool(agentName) {
|
|
|
224
224
|
async function refreshProviderCredentialPool(agentName, options = {}) {
|
|
225
225
|
try {
|
|
226
226
|
const store = (0, credential_access_1.getCredentialStore)(agentName);
|
|
227
|
+
options.onProgress?.(`reading vault items for ${agentName}...`);
|
|
227
228
|
const items = await store.list();
|
|
228
229
|
const providers = {};
|
|
229
230
|
let updatedAt = new Date(0).toISOString();
|
|
@@ -233,6 +234,7 @@ async function refreshProviderCredentialPool(agentName, options = {}) {
|
|
|
233
234
|
const provider = item.domain.slice(VAULT_ITEM_PREFIX.length);
|
|
234
235
|
if (!isAgentProvider(provider))
|
|
235
236
|
continue;
|
|
237
|
+
options.onProgress?.(`reading ${provider} credentials...`);
|
|
236
238
|
const raw = await store.getRawSecret(item.domain, "password");
|
|
237
239
|
const payload = validateProviderCredentialPayload(JSON.parse(raw), provider);
|
|
238
240
|
const record = recordFromPayload(payload);
|
|
@@ -240,6 +242,7 @@ async function refreshProviderCredentialPool(agentName, options = {}) {
|
|
|
240
242
|
if (record.updatedAt > updatedAt)
|
|
241
243
|
updatedAt = record.updatedAt;
|
|
242
244
|
}
|
|
245
|
+
options.onProgress?.("parsing provider credentials...");
|
|
243
246
|
const pool = {
|
|
244
247
|
schemaVersion: 1,
|
|
245
248
|
updatedAt,
|
|
@@ -20,6 +20,16 @@ const OPENAI_CODEX_AUTH_FAILURE_MARKERS = [
|
|
|
20
20
|
"invalid bearer token",
|
|
21
21
|
];
|
|
22
22
|
const OPENAI_CODEX_BACKEND_BASE_URL = "https://chatgpt.com/backend-api/codex";
|
|
23
|
+
const OPENAI_CODEX_PING_INPUT = [{ role: "user", content: "ping" }];
|
|
24
|
+
const OPENAI_CODEX_PING_CALLBACKS = {
|
|
25
|
+
onModelStart() { },
|
|
26
|
+
onModelStreamStart() { },
|
|
27
|
+
onTextChunk() { },
|
|
28
|
+
onReasoningChunk() { },
|
|
29
|
+
onToolStart() { },
|
|
30
|
+
onToolEnd() { },
|
|
31
|
+
onError() { },
|
|
32
|
+
};
|
|
23
33
|
function getOpenAICodexAgentNameForGuidance() {
|
|
24
34
|
return (0, identity_1.getAgentName)();
|
|
25
35
|
}
|
|
@@ -82,6 +92,18 @@ function getChatGPTAccountIdFromToken(token) {
|
|
|
82
92
|
return "";
|
|
83
93
|
return accountId.trim();
|
|
84
94
|
}
|
|
95
|
+
function createOpenAICodexResponsesParams(input, instructions, model, reasoningEffort) {
|
|
96
|
+
return {
|
|
97
|
+
model,
|
|
98
|
+
input,
|
|
99
|
+
instructions,
|
|
100
|
+
tools: [],
|
|
101
|
+
reasoning: { effort: reasoningEffort, summary: "detailed" },
|
|
102
|
+
stream: true,
|
|
103
|
+
store: false,
|
|
104
|
+
include: ["reasoning.encrypted_content"],
|
|
105
|
+
};
|
|
106
|
+
}
|
|
85
107
|
function createOpenAICodexProviderRuntime(model, codexConfig = (0, config_1.getOpenAICodexConfig)()) {
|
|
86
108
|
(0, runtime_1.emitNervesEvent)({
|
|
87
109
|
component: "engine",
|
|
@@ -137,16 +159,8 @@ function createOpenAICodexProviderRuntime(model, codexConfig = (0, config_1.getO
|
|
|
137
159
|
async streamTurn(request) {
|
|
138
160
|
if (!nativeInput)
|
|
139
161
|
this.resetTurnState(request.messages);
|
|
140
|
-
const params =
|
|
141
|
-
|
|
142
|
-
input: nativeInput,
|
|
143
|
-
instructions: nativeInstructions,
|
|
144
|
-
tools: (0, streaming_1.toResponsesTools)(request.activeTools),
|
|
145
|
-
reasoning: { effort: request.reasoningEffort ?? "medium", summary: "detailed" },
|
|
146
|
-
stream: true,
|
|
147
|
-
store: false,
|
|
148
|
-
include: ["reasoning.encrypted_content"],
|
|
149
|
-
};
|
|
162
|
+
const params = createOpenAICodexResponsesParams(nativeInput, nativeInstructions, this.model, request.reasoningEffort ?? "medium");
|
|
163
|
+
params.tools = (0, streaming_1.toResponsesTools)(request.activeTools);
|
|
150
164
|
if (request.toolChoiceRequired)
|
|
151
165
|
params.tool_choice = "required";
|
|
152
166
|
try {
|
|
@@ -161,7 +175,7 @@ function createOpenAICodexProviderRuntime(model, codexConfig = (0, config_1.getO
|
|
|
161
175
|
},
|
|
162
176
|
/* v8 ignore start -- ping: tested via provider-ping.test.ts @preserve */
|
|
163
177
|
async ping(signal) {
|
|
164
|
-
await this.client
|
|
178
|
+
await (0, streaming_1.streamResponsesApi)(this.client, createOpenAICodexResponsesParams(OPENAI_CODEX_PING_INPUT, "", this.model, "medium"), OPENAI_CODEX_PING_CALLBACKS, signal);
|
|
165
179
|
},
|
|
166
180
|
/* v8 ignore stop */
|
|
167
181
|
/* v8 ignore next 3 -- delegation: classification logic tested via classifyOpenAICodexError @preserve */
|