@steipete/oracle 0.8.6 → 0.10.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/LICENSE +1 -1
- package/README.md +130 -45
- package/dist/bin/oracle-cli.js +613 -379
- package/dist/bin/oracle-mcp.js +2 -2
- package/dist/bin/oracle.js +165 -279
- package/dist/scripts/agent-send.js +31 -31
- package/dist/scripts/check.js +6 -6
- package/dist/scripts/debug/extract-chatgpt-response.js +10 -10
- package/dist/scripts/docs-list.js +30 -30
- package/dist/scripts/git-policy.js +25 -23
- package/dist/scripts/run-cli.js +8 -8
- package/dist/scripts/runner.js +203 -195
- package/dist/scripts/test-browser.js +21 -18
- package/dist/scripts/test-remote-chrome.js +20 -20
- package/dist/src/bridge/connection.js +18 -18
- package/dist/src/bridge/userConfigFile.js +7 -7
- package/dist/src/browser/actions/assistantResponse.js +149 -101
- package/dist/src/browser/actions/attachmentDataTransfer.js +49 -47
- package/dist/src/browser/actions/attachments.js +246 -150
- package/dist/src/browser/actions/domEvents.js +2 -2
- package/dist/src/browser/actions/modelSelection.js +314 -104
- package/dist/src/browser/actions/navigation.js +161 -136
- package/dist/src/browser/actions/promptComposer.js +100 -64
- package/dist/src/browser/actions/remoteFileTransfer.js +10 -10
- package/dist/src/browser/actions/thinkingTime.js +207 -110
- package/dist/src/browser/chromeLifecycle.js +62 -60
- package/dist/src/browser/config.js +34 -15
- package/dist/src/browser/constants.js +17 -12
- package/dist/src/browser/cookies.js +19 -19
- package/dist/src/browser/detect.js +62 -62
- package/dist/src/browser/domDebug.js +1 -1
- package/dist/src/browser/index.js +452 -303
- package/dist/src/browser/modelStrategy.js +1 -1
- package/dist/src/browser/pageActions.js +5 -5
- package/dist/src/browser/policies.js +16 -13
- package/dist/src/browser/profileState.js +44 -39
- package/dist/src/browser/prompt.js +72 -42
- package/dist/src/browser/promptSummary.js +5 -5
- package/dist/src/browser/providerDomFlow.js +17 -0
- package/dist/src/browser/providers/chatgptDomProvider.js +49 -0
- package/dist/src/browser/providers/geminiDeepThinkDomProvider.js +254 -0
- package/dist/src/browser/providers/index.js +2 -0
- package/dist/src/browser/reattach.js +67 -34
- package/dist/src/browser/reattachHelpers.js +31 -26
- package/dist/src/browser/sessionRunner.js +37 -25
- package/dist/src/browser/utils.js +9 -9
- package/dist/src/browserMode.js +1 -1
- package/dist/src/cli/bridge/claudeConfig.js +16 -16
- package/dist/src/cli/bridge/client.js +28 -20
- package/dist/src/cli/bridge/codexConfig.js +16 -16
- package/dist/src/cli/bridge/doctor.js +47 -39
- package/dist/src/cli/bridge/host.js +58 -56
- package/dist/src/cli/browserConfig.js +65 -45
- package/dist/src/cli/browserDefaults.js +27 -26
- package/dist/src/cli/bundleWarnings.js +1 -1
- package/dist/src/cli/clipboard.js +11 -2
- package/dist/src/cli/detach.js +7 -4
- package/dist/src/cli/dryRun.js +29 -25
- package/dist/src/cli/duplicatePromptGuard.js +3 -3
- package/dist/src/cli/engine.js +9 -9
- package/dist/src/cli/errorUtils.js +1 -1
- package/dist/src/cli/fileSize.js +11 -0
- package/dist/src/cli/format.js +2 -2
- package/dist/src/cli/help.js +28 -28
- package/dist/src/cli/hiddenAliases.js +3 -3
- package/dist/src/cli/markdownBundle.js +12 -8
- package/dist/src/cli/markdownRenderer.js +15 -15
- package/dist/src/cli/notifier.js +77 -67
- package/dist/src/cli/options.js +145 -87
- package/dist/src/cli/oscUtils.js +1 -1
- package/dist/src/cli/promptRequirement.js +2 -2
- package/dist/src/cli/renderOutput.js +1 -1
- package/dist/src/cli/rootAlias.js +1 -1
- package/dist/src/cli/runOptions.js +37 -25
- package/dist/src/cli/sessionCommand.js +31 -21
- package/dist/src/cli/sessionDisplay.js +182 -79
- package/dist/src/cli/sessionLineage.js +60 -0
- package/dist/src/cli/sessionRunner.js +118 -90
- package/dist/src/cli/sessionTable.js +28 -24
- package/dist/src/cli/stdin.js +22 -0
- package/dist/src/cli/tagline.js +121 -124
- package/dist/src/cli/tui/index.js +140 -127
- package/dist/src/cli/writeOutputPath.js +5 -5
- package/dist/src/config.js +7 -7
- package/dist/src/gemini-web/browserSessionManager.js +80 -0
- package/dist/src/gemini-web/client.js +81 -64
- package/dist/src/gemini-web/executionMode.js +16 -0
- package/dist/src/gemini-web/executor.js +327 -169
- package/dist/src/gemini-web/index.js +1 -1
- package/dist/src/mcp/server.js +16 -12
- package/dist/src/mcp/tools/consult.js +81 -64
- package/dist/src/mcp/tools/sessionResources.js +12 -12
- package/dist/src/mcp/tools/sessions.js +26 -17
- package/dist/src/mcp/types.js +5 -5
- package/dist/src/mcp/utils.js +15 -7
- package/dist/src/oracle/background.js +15 -15
- package/dist/src/oracle/claude.js +53 -25
- package/dist/src/oracle/client.js +84 -46
- package/dist/src/oracle/config.js +124 -58
- package/dist/src/oracle/errors.js +38 -38
- package/dist/src/oracle/files.js +69 -45
- package/dist/src/oracle/finishLine.js +10 -8
- package/dist/src/oracle/format.js +3 -3
- package/dist/src/oracle/gemini.js +37 -30
- package/dist/src/oracle/logging.js +7 -7
- package/dist/src/oracle/markdown.js +28 -28
- package/dist/src/oracle/modelResolver.js +16 -16
- package/dist/src/oracle/multiModelRunner.js +12 -12
- package/dist/src/oracle/oscProgress.js +8 -8
- package/dist/src/oracle/promptAssembly.js +6 -3
- package/dist/src/oracle/request.js +23 -15
- package/dist/src/oracle/run.js +172 -140
- package/dist/src/oracle/runUtils.js +8 -5
- package/dist/src/oracle/tokenEstimate.js +6 -6
- package/dist/src/oracle/tokenStats.js +5 -5
- package/dist/src/oracle/tokenStringifier.js +5 -5
- package/dist/src/oracle.js +12 -12
- package/dist/src/oracleHome.js +3 -3
- package/dist/src/remote/client.js +25 -25
- package/dist/src/remote/health.js +20 -20
- package/dist/src/remote/remoteServiceConfig.js +9 -9
- package/dist/src/remote/server.js +129 -118
- package/dist/src/sessionManager.js +81 -75
- package/dist/src/sessionStore.js +3 -3
- package/dist/src/version.js +10 -10
- package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/CodeResources +0 -0
- package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/MacOS/OracleNotifier +0 -0
- package/dist/vendor/oracle-notifier/README.md +2 -0
- package/package.json +69 -65
- package/vendor/oracle-notifier/OracleNotifier.app/Contents/CodeResources +0 -0
- package/vendor/oracle-notifier/OracleNotifier.app/Contents/MacOS/OracleNotifier +0 -0
- package/vendor/oracle-notifier/README.md +2 -0
- package/dist/markdansi/types/index.js +0 -4
- package/dist/oracle/bin/oracle-cli.js +0 -472
- package/dist/oracle/src/browser/actions/assistantResponse.js +0 -471
- package/dist/oracle/src/browser/actions/attachments.js +0 -82
- package/dist/oracle/src/browser/actions/modelSelection.js +0 -190
- package/dist/oracle/src/browser/actions/navigation.js +0 -75
- package/dist/oracle/src/browser/actions/promptComposer.js +0 -167
- package/dist/oracle/src/browser/chromeLifecycle.js +0 -104
- package/dist/oracle/src/browser/config.js +0 -33
- package/dist/oracle/src/browser/constants.js +0 -40
- package/dist/oracle/src/browser/cookies.js +0 -210
- package/dist/oracle/src/browser/domDebug.js +0 -36
- package/dist/oracle/src/browser/index.js +0 -331
- package/dist/oracle/src/browser/pageActions.js +0 -5
- package/dist/oracle/src/browser/prompt.js +0 -88
- package/dist/oracle/src/browser/promptSummary.js +0 -20
- package/dist/oracle/src/browser/sessionRunner.js +0 -80
- package/dist/oracle/src/browser/utils.js +0 -62
- package/dist/oracle/src/browserMode.js +0 -1
- package/dist/oracle/src/cli/browserConfig.js +0 -44
- package/dist/oracle/src/cli/dryRun.js +0 -59
- package/dist/oracle/src/cli/engine.js +0 -17
- package/dist/oracle/src/cli/errorUtils.js +0 -9
- package/dist/oracle/src/cli/help.js +0 -70
- package/dist/oracle/src/cli/markdownRenderer.js +0 -15
- package/dist/oracle/src/cli/options.js +0 -103
- package/dist/oracle/src/cli/promptRequirement.js +0 -14
- package/dist/oracle/src/cli/rootAlias.js +0 -30
- package/dist/oracle/src/cli/sessionCommand.js +0 -77
- package/dist/oracle/src/cli/sessionDisplay.js +0 -270
- package/dist/oracle/src/cli/sessionRunner.js +0 -94
- package/dist/oracle/src/heartbeat.js +0 -43
- package/dist/oracle/src/oracle/client.js +0 -48
- package/dist/oracle/src/oracle/config.js +0 -29
- package/dist/oracle/src/oracle/errors.js +0 -101
- package/dist/oracle/src/oracle/files.js +0 -220
- package/dist/oracle/src/oracle/format.js +0 -33
- package/dist/oracle/src/oracle/fsAdapter.js +0 -7
- package/dist/oracle/src/oracle/oscProgress.js +0 -60
- package/dist/oracle/src/oracle/request.js +0 -48
- package/dist/oracle/src/oracle/run.js +0 -444
- package/dist/oracle/src/oracle/tokenStats.js +0 -39
- package/dist/oracle/src/oracle/types.js +0 -1
- package/dist/oracle/src/oracle.js +0 -9
- package/dist/oracle/src/sessionManager.js +0 -205
- package/dist/oracle/src/version.js +0 -39
- package/dist/scripts/chrome/browser-tools.js +0 -295
- package/dist/src/browser/profileSync.js +0 -141
- /package/dist/{oracle/src/browser/types.js → src/gemini-web/executionClients.js} +0 -0
package/dist/src/oracle/run.js
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
import chalk from
|
|
2
|
-
import kleur from
|
|
3
|
-
import fs from
|
|
4
|
-
import path from
|
|
5
|
-
import process from
|
|
6
|
-
import { performance } from
|
|
7
|
-
import { DEFAULT_SYSTEM_PROMPT, MODEL_CONFIGS, TOKENIZER_OPTIONS } from
|
|
8
|
-
import { readFiles } from
|
|
9
|
-
import { buildPrompt, buildRequestBody } from
|
|
10
|
-
import { estimateRequestTokens } from
|
|
11
|
-
import { formatElapsed } from
|
|
12
|
-
import { formatFinishLine } from
|
|
13
|
-
import { getFileTokenStats, printFileTokenStats } from
|
|
14
|
-
import { OracleResponseError, OracleTransportError, PromptValidationError, describeTransportError, toTransportError, } from
|
|
15
|
-
import { createDefaultClientFactory } from
|
|
16
|
-
import { formatBaseUrlForLog, maskApiKey } from
|
|
17
|
-
import { startHeartbeat } from
|
|
18
|
-
import { startOscProgress } from
|
|
19
|
-
import { createFsAdapter } from
|
|
20
|
-
import { resolveGeminiModelId } from
|
|
21
|
-
import { resolveClaudeModelId } from
|
|
22
|
-
import { renderMarkdownAnsi } from
|
|
23
|
-
import { createMarkdownStreamer } from
|
|
24
|
-
import { executeBackgroundResponse } from
|
|
25
|
-
import { formatTokenEstimate, formatTokenValue, resolvePreviewMode } from
|
|
26
|
-
import { estimateUsdCost } from
|
|
27
|
-
import { defaultOpenRouterBaseUrl, isKnownModel, isOpenRouterBaseUrl, isProModel, resolveModelConfig, normalizeOpenRouterBaseUrl, } from
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import kleur from "kleur";
|
|
3
|
+
import fs from "node:fs/promises";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import process from "node:process";
|
|
6
|
+
import { performance } from "node:perf_hooks";
|
|
7
|
+
import { DEFAULT_SYSTEM_PROMPT, MODEL_CONFIGS, TOKENIZER_OPTIONS } from "./config.js";
|
|
8
|
+
import { readFiles } from "./files.js";
|
|
9
|
+
import { buildPrompt, buildRequestBody } from "./request.js";
|
|
10
|
+
import { estimateRequestTokens } from "./tokenEstimate.js";
|
|
11
|
+
import { formatElapsed } from "./format.js";
|
|
12
|
+
import { formatFinishLine } from "./finishLine.js";
|
|
13
|
+
import { getFileTokenStats, printFileTokenStats } from "./tokenStats.js";
|
|
14
|
+
import { OracleResponseError, OracleTransportError, PromptValidationError, describeTransportError, toTransportError, } from "./errors.js";
|
|
15
|
+
import { createDefaultClientFactory, isCustomBaseUrl } from "./client.js";
|
|
16
|
+
import { formatBaseUrlForLog, maskApiKey } from "./logging.js";
|
|
17
|
+
import { startHeartbeat } from "../heartbeat.js";
|
|
18
|
+
import { startOscProgress } from "./oscProgress.js";
|
|
19
|
+
import { createFsAdapter } from "./fsAdapter.js";
|
|
20
|
+
import { resolveGeminiModelId } from "./gemini.js";
|
|
21
|
+
import { resolveClaudeModelId } from "./claude.js";
|
|
22
|
+
import { renderMarkdownAnsi } from "../cli/markdownRenderer.js";
|
|
23
|
+
import { createMarkdownStreamer } from "markdansi";
|
|
24
|
+
import { executeBackgroundResponse } from "./background.js";
|
|
25
|
+
import { formatTokenEstimate, formatTokenValue, resolvePreviewMode } from "./runUtils.js";
|
|
26
|
+
import { estimateUsdCost } from "tokentally";
|
|
27
|
+
import { defaultOpenRouterBaseUrl, isKnownModel, isOpenRouterBaseUrl, isProModel, resolveModelConfig, normalizeOpenRouterBaseUrl, } from "./modelResolver.js";
|
|
28
28
|
const isStdoutTty = process.stdout.isTTY && chalk.level > 0;
|
|
29
29
|
const dim = (text) => (isStdoutTty ? kleur.dim(text) : text);
|
|
30
30
|
// Default timeout for non-pro API runs (fast models) — give them up to 120s.
|
|
@@ -36,14 +36,14 @@ const defaultWait = (ms) => new Promise((resolve) => {
|
|
|
36
36
|
export async function runOracle(options, deps = {}) {
|
|
37
37
|
const { apiKey: optionsApiKey = options.apiKey, cwd = process.cwd(), fs: fsModule = createFsAdapter(fs), log = console.log, write: sinkWrite = (_text) => true, allowStdout = true, stdoutWrite: stdoutWriteDep, now = () => performance.now(), clientFactory = createDefaultClientFactory(), client, wait = defaultWait, } = deps;
|
|
38
38
|
const stdoutWrite = allowStdout
|
|
39
|
-
? stdoutWriteDep ?? process.stdout.write.bind(process.stdout)
|
|
39
|
+
? (stdoutWriteDep ?? process.stdout.write.bind(process.stdout))
|
|
40
40
|
: () => true;
|
|
41
41
|
const isTty = allowStdout && isStdoutTty;
|
|
42
|
-
const resolvedXaiBaseUrl = process.env.XAI_BASE_URL?.trim() ||
|
|
42
|
+
const resolvedXaiBaseUrl = process.env.XAI_BASE_URL?.trim() || "https://api.x.ai/v1";
|
|
43
43
|
const openRouterApiKey = process.env.OPENROUTER_API_KEY?.trim();
|
|
44
44
|
const defaultOpenRouterBase = defaultOpenRouterBaseUrl();
|
|
45
45
|
const knownModelConfig = isKnownModel(options.model) ? MODEL_CONFIGS[options.model] : undefined;
|
|
46
|
-
const provider = knownModelConfig?.provider ??
|
|
46
|
+
const provider = knownModelConfig?.provider ?? "other";
|
|
47
47
|
const hasOpenAIKey = Boolean(optionsApiKey) ||
|
|
48
48
|
Boolean(process.env.OPENAI_API_KEY) ||
|
|
49
49
|
Boolean(process.env.AZURE_OPENAI_API_KEY && options.azure?.endpoint);
|
|
@@ -52,21 +52,21 @@ export async function runOracle(options, deps = {}) {
|
|
|
52
52
|
const hasXaiKey = Boolean(optionsApiKey) || Boolean(process.env.XAI_API_KEY);
|
|
53
53
|
let baseUrl = options.baseUrl?.trim();
|
|
54
54
|
if (!baseUrl) {
|
|
55
|
-
if (options.model.startsWith(
|
|
55
|
+
if (options.model.startsWith("grok")) {
|
|
56
56
|
baseUrl = resolvedXaiBaseUrl;
|
|
57
57
|
}
|
|
58
|
-
else if (provider ===
|
|
58
|
+
else if (provider === "anthropic") {
|
|
59
59
|
baseUrl = process.env.ANTHROPIC_BASE_URL?.trim();
|
|
60
60
|
}
|
|
61
61
|
else {
|
|
62
62
|
baseUrl = process.env.OPENAI_BASE_URL?.trim();
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
|
-
const providerKeyMissing = (provider ===
|
|
66
|
-
(provider ===
|
|
67
|
-
(provider ===
|
|
68
|
-
(provider ===
|
|
69
|
-
provider ===
|
|
65
|
+
const providerKeyMissing = (provider === "openai" && !hasOpenAIKey) ||
|
|
66
|
+
(provider === "anthropic" && !hasAnthropicKey) ||
|
|
67
|
+
(provider === "google" && !hasGeminiKey) ||
|
|
68
|
+
(provider === "xai" && !hasXaiKey) ||
|
|
69
|
+
provider === "other";
|
|
70
70
|
const openRouterFallback = providerKeyMissing && Boolean(openRouterApiKey);
|
|
71
71
|
if (!baseUrl || openRouterFallback) {
|
|
72
72
|
if (openRouterFallback) {
|
|
@@ -86,66 +86,73 @@ export async function runOracle(options, deps = {}) {
|
|
|
86
86
|
const isAzureOpenAI = Boolean(options.azure?.endpoint);
|
|
87
87
|
const getApiKeyForModel = (model) => {
|
|
88
88
|
if (isOpenRouterBaseUrl(baseUrl) || openRouterFallback) {
|
|
89
|
-
return { key: optionsApiKey ?? openRouterApiKey, source:
|
|
89
|
+
return { key: optionsApiKey ?? openRouterApiKey, source: "OPENROUTER_API_KEY" };
|
|
90
90
|
}
|
|
91
|
-
if (typeof model ===
|
|
91
|
+
if (typeof model === "string" && model.startsWith("gpt")) {
|
|
92
92
|
if (optionsApiKey)
|
|
93
|
-
return { key: optionsApiKey, source:
|
|
93
|
+
return { key: optionsApiKey, source: "apiKey option" };
|
|
94
94
|
if (isAzureOpenAI) {
|
|
95
95
|
const key = process.env.AZURE_OPENAI_API_KEY ?? process.env.OPENAI_API_KEY;
|
|
96
|
-
return { key, source:
|
|
96
|
+
return { key, source: "AZURE_OPENAI_API_KEY|OPENAI_API_KEY" };
|
|
97
97
|
}
|
|
98
|
-
return { key: process.env.OPENAI_API_KEY, source:
|
|
98
|
+
return { key: process.env.OPENAI_API_KEY, source: "OPENAI_API_KEY" };
|
|
99
99
|
}
|
|
100
|
-
if (typeof model ===
|
|
101
|
-
return { key: optionsApiKey ?? process.env.GEMINI_API_KEY, source:
|
|
100
|
+
if (typeof model === "string" && model.startsWith("gemini")) {
|
|
101
|
+
return { key: optionsApiKey ?? process.env.GEMINI_API_KEY, source: "GEMINI_API_KEY" };
|
|
102
102
|
}
|
|
103
|
-
if (typeof model ===
|
|
104
|
-
return { key: optionsApiKey ?? process.env.ANTHROPIC_API_KEY, source:
|
|
103
|
+
if (typeof model === "string" && model.startsWith("claude")) {
|
|
104
|
+
return { key: optionsApiKey ?? process.env.ANTHROPIC_API_KEY, source: "ANTHROPIC_API_KEY" };
|
|
105
105
|
}
|
|
106
|
-
if (typeof model ===
|
|
107
|
-
return { key: optionsApiKey ?? process.env.XAI_API_KEY, source:
|
|
106
|
+
if (typeof model === "string" && model.startsWith("grok")) {
|
|
107
|
+
return { key: optionsApiKey ?? process.env.XAI_API_KEY, source: "XAI_API_KEY" };
|
|
108
108
|
}
|
|
109
|
-
return {
|
|
109
|
+
return {
|
|
110
|
+
key: optionsApiKey ?? openRouterApiKey,
|
|
111
|
+
source: optionsApiKey ? "apiKey option" : "OPENROUTER_API_KEY",
|
|
112
|
+
};
|
|
110
113
|
};
|
|
111
114
|
const apiKeyResult = getApiKeyForModel(options.model);
|
|
112
115
|
const apiKey = apiKeyResult.key;
|
|
113
116
|
if (!apiKey) {
|
|
114
117
|
const envVar = isOpenRouterBaseUrl(baseUrl) || openRouterFallback
|
|
115
|
-
?
|
|
116
|
-
: options.model.startsWith(
|
|
118
|
+
? "OPENROUTER_API_KEY"
|
|
119
|
+
: options.model.startsWith("gpt")
|
|
117
120
|
? isAzureOpenAI
|
|
118
|
-
?
|
|
119
|
-
:
|
|
120
|
-
: options.model.startsWith(
|
|
121
|
-
?
|
|
122
|
-
: options.model.startsWith(
|
|
123
|
-
?
|
|
124
|
-
: options.model.startsWith(
|
|
125
|
-
?
|
|
126
|
-
:
|
|
121
|
+
? "AZURE_OPENAI_API_KEY (or OPENAI_API_KEY)"
|
|
122
|
+
: "OPENAI_API_KEY"
|
|
123
|
+
: options.model.startsWith("gemini")
|
|
124
|
+
? "GEMINI_API_KEY"
|
|
125
|
+
: options.model.startsWith("claude")
|
|
126
|
+
? "ANTHROPIC_API_KEY"
|
|
127
|
+
: options.model.startsWith("grok")
|
|
128
|
+
? "XAI_API_KEY"
|
|
129
|
+
: "OPENROUTER_API_KEY";
|
|
127
130
|
throw new PromptValidationError(`Missing ${envVar}. Set it via the environment or a .env file.`, {
|
|
128
131
|
env: envVar,
|
|
129
132
|
});
|
|
130
133
|
}
|
|
131
134
|
const envVar = apiKeyResult.source;
|
|
132
|
-
const minPromptLength = Number.parseInt(process.env.ORACLE_MIN_PROMPT_CHARS ??
|
|
135
|
+
const minPromptLength = Number.parseInt(process.env.ORACLE_MIN_PROMPT_CHARS ?? "10", 10);
|
|
133
136
|
const promptLength = options.prompt?.trim().length ?? 0;
|
|
134
137
|
// Enforce the short-prompt guardrail on pro-tier models because they're costly; cheaper models can run short prompts without blocking.
|
|
135
138
|
const isProTierModel = isProModel(options.model);
|
|
136
139
|
if (isProTierModel && !Number.isNaN(minPromptLength) && promptLength < minPromptLength) {
|
|
137
140
|
throw new PromptValidationError(`Prompt is too short (<${minPromptLength} chars). This was likely accidental; please provide more detail.`, { minPromptLength, promptLength });
|
|
138
141
|
}
|
|
139
|
-
const resolverOpenRouterApiKey = openRouterFallback || isOpenRouterBaseUrl(baseUrl) ? openRouterApiKey ?? apiKey : undefined;
|
|
142
|
+
const resolverOpenRouterApiKey = openRouterFallback || isOpenRouterBaseUrl(baseUrl) ? (openRouterApiKey ?? apiKey) : undefined;
|
|
140
143
|
const modelConfig = await resolveModelConfig(options.model, {
|
|
141
144
|
baseUrl,
|
|
142
145
|
openRouterApiKey: resolverOpenRouterApiKey,
|
|
143
146
|
});
|
|
144
147
|
const isLongRunningModel = isProTierModel;
|
|
145
148
|
const supportsBackground = modelConfig.supportsBackground !== false;
|
|
146
|
-
const useBackground = supportsBackground ? options.background ?? isLongRunningModel : false;
|
|
149
|
+
const useBackground = supportsBackground ? (options.background ?? isLongRunningModel) : false;
|
|
147
150
|
const inputTokenBudget = options.maxInput ?? modelConfig.inputLimit;
|
|
148
|
-
const files = await readFiles(options.file ?? [], {
|
|
151
|
+
const files = await readFiles(options.file ?? [], {
|
|
152
|
+
cwd,
|
|
153
|
+
fsModule,
|
|
154
|
+
maxFileSizeBytes: options.maxFileSizeBytes,
|
|
155
|
+
});
|
|
149
156
|
const searchEnabled = options.search !== false;
|
|
150
157
|
logVerbose(`cwd: ${cwd}`);
|
|
151
158
|
let pendingNoFilesTip = null;
|
|
@@ -154,21 +161,21 @@ export async function runOracle(options, deps = {}) {
|
|
|
154
161
|
const displayPaths = files
|
|
155
162
|
.map((file) => path.relative(cwd, file.path) || file.path)
|
|
156
163
|
.slice(0, 10)
|
|
157
|
-
.join(
|
|
158
|
-
const extra = files.length > 10 ? ` (+${files.length - 10} more)` :
|
|
164
|
+
.join(", ");
|
|
165
|
+
const extra = files.length > 10 ? ` (+${files.length - 10} more)` : "";
|
|
159
166
|
logVerbose(`Attached files (${files.length}): ${displayPaths}${extra}`);
|
|
160
167
|
}
|
|
161
168
|
else {
|
|
162
|
-
logVerbose(
|
|
169
|
+
logVerbose("No files attached.");
|
|
163
170
|
if (!isPreview) {
|
|
164
171
|
pendingNoFilesTip =
|
|
165
|
-
|
|
172
|
+
"Tip: no files attached — Oracle works best with project context. Add files via --file path/to/code or docs.";
|
|
166
173
|
}
|
|
167
174
|
}
|
|
168
175
|
const shortPrompt = (options.prompt?.trim().length ?? 0) < 80;
|
|
169
176
|
if (!isPreview && shortPrompt) {
|
|
170
177
|
pendingShortPromptTip =
|
|
171
|
-
|
|
178
|
+
"Tip: brief prompts often yield generic answers — aim for 6–30 sentences and attach key files.";
|
|
172
179
|
}
|
|
173
180
|
const fileTokenInfo = getFileTokenStats(files, {
|
|
174
181
|
cwd,
|
|
@@ -183,17 +190,23 @@ export async function runOracle(options, deps = {}) {
|
|
|
183
190
|
const fileCount = files.length;
|
|
184
191
|
const richTty = allowStdout && process.stdout.isTTY && chalk.level > 0;
|
|
185
192
|
const renderPlain = Boolean(options.renderPlain);
|
|
186
|
-
const timeoutSeconds = options.timeoutSeconds === undefined || options.timeoutSeconds ===
|
|
193
|
+
const timeoutSeconds = options.timeoutSeconds === undefined || options.timeoutSeconds === "auto"
|
|
187
194
|
? isLongRunningModel
|
|
188
195
|
? DEFAULT_TIMEOUT_PRO_MS / 1000
|
|
189
196
|
: DEFAULT_TIMEOUT_NON_PRO_MS / 1000
|
|
190
197
|
: options.timeoutSeconds;
|
|
191
198
|
const timeoutMs = timeoutSeconds * 1000;
|
|
199
|
+
const azureDeploymentName = isAzureOpenAI ? options.azure?.deployment?.trim() : undefined;
|
|
192
200
|
// Track the concrete model id we dispatch to (especially for Gemini preview aliases)
|
|
193
201
|
const effectiveModelId = options.effectiveModelId ??
|
|
194
|
-
(
|
|
195
|
-
?
|
|
196
|
-
:
|
|
202
|
+
(azureDeploymentName
|
|
203
|
+
? azureDeploymentName
|
|
204
|
+
: options.model.startsWith("gemini")
|
|
205
|
+
? resolveGeminiModelId(options.model)
|
|
206
|
+
: (modelConfig.apiModel ?? modelConfig.model));
|
|
207
|
+
if (!isPreview && options.previousResponseId) {
|
|
208
|
+
log(dim(`Continuing from response ${options.previousResponseId}`));
|
|
209
|
+
}
|
|
197
210
|
const requestBody = buildRequestBody({
|
|
198
211
|
modelConfig,
|
|
199
212
|
systemPrompt,
|
|
@@ -201,33 +214,37 @@ export async function runOracle(options, deps = {}) {
|
|
|
201
214
|
searchEnabled,
|
|
202
215
|
maxOutputTokens: options.maxOutput,
|
|
203
216
|
background: useBackground,
|
|
204
|
-
|
|
217
|
+
// Storing makes follow-ups possible (Responses API chaining relies on stored response state).
|
|
218
|
+
storeResponse: useBackground || Boolean(options.previousResponseId),
|
|
219
|
+
previousResponseId: options.previousResponseId,
|
|
205
220
|
});
|
|
221
|
+
requestBody.model = effectiveModelId;
|
|
206
222
|
const estimatedInputTokens = estimateRequestTokens(requestBody, modelConfig);
|
|
207
|
-
const tokenLabel = formatTokenEstimate(estimatedInputTokens, (text) =>
|
|
223
|
+
const tokenLabel = formatTokenEstimate(estimatedInputTokens, (text) => richTty ? chalk.green(text) : text);
|
|
208
224
|
const fileLabel = richTty ? chalk.magenta(fileCount.toString()) : fileCount.toString();
|
|
209
|
-
const filesPhrase = fileCount === 0 ?
|
|
225
|
+
const filesPhrase = fileCount === 0 ? "no files" : `${fileLabel} files`;
|
|
210
226
|
const headerModelLabelBase = richTty ? chalk.cyan(modelConfig.model) : modelConfig.model;
|
|
211
227
|
const headerModelSuffix = effectiveModelId !== modelConfig.model
|
|
212
228
|
? richTty
|
|
213
229
|
? chalk.gray(` (API: ${effectiveModelId})`)
|
|
214
230
|
: ` (API: ${effectiveModelId})`
|
|
215
|
-
:
|
|
231
|
+
: "";
|
|
216
232
|
const headerLine = `Calling ${headerModelLabelBase}${headerModelSuffix} — ${tokenLabel} tokens, ${filesPhrase}.`;
|
|
217
|
-
const shouldReportFiles = (options.filesReport || fileTokenInfo.totalTokens > inputTokenBudget) &&
|
|
233
|
+
const shouldReportFiles = (options.filesReport || fileTokenInfo.totalTokens > inputTokenBudget) &&
|
|
234
|
+
fileTokenInfo.stats.length > 0;
|
|
218
235
|
if (!isPreview) {
|
|
219
236
|
if (!options.suppressHeader) {
|
|
220
237
|
log(headerLine);
|
|
221
238
|
}
|
|
222
239
|
const maskedKey = maskApiKey(apiKey);
|
|
223
240
|
if (maskedKey && options.verbose) {
|
|
224
|
-
const resolvedSuffix = effectiveModelId !== modelConfig.model ? ` (API: ${effectiveModelId})` :
|
|
241
|
+
const resolvedSuffix = effectiveModelId !== modelConfig.model ? ` (API: ${effectiveModelId})` : "";
|
|
225
242
|
log(dim(`Using ${envVar}=${maskedKey} for model ${modelConfig.model}${resolvedSuffix}`));
|
|
226
243
|
}
|
|
227
244
|
if (!options.suppressHeader &&
|
|
228
|
-
modelConfig.model ===
|
|
229
|
-
effectiveModelId ===
|
|
230
|
-
log(dim(
|
|
245
|
+
(modelConfig.model === "gpt-5.1-pro" || modelConfig.model === "gpt-5.2-pro") &&
|
|
246
|
+
effectiveModelId === "gpt-5.5-pro") {
|
|
247
|
+
log(dim(`Note: \`${modelConfig.model}\` is a stable CLI alias; OpenAI API uses \`gpt-5.5-pro\`.`));
|
|
231
248
|
}
|
|
232
249
|
if (baseUrl) {
|
|
233
250
|
log(dim(`Base URL: ${formatBaseUrlForLog(baseUrl)}`));
|
|
@@ -236,7 +253,7 @@ export async function runOracle(options, deps = {}) {
|
|
|
236
253
|
log(dim(`Resolved model: ${modelConfig.model} → ${effectiveModelId}`));
|
|
237
254
|
}
|
|
238
255
|
if (options.background && !supportsBackground) {
|
|
239
|
-
log(dim(
|
|
256
|
+
log(dim("Background runs are not supported for this model; streaming in foreground instead."));
|
|
240
257
|
}
|
|
241
258
|
if (!options.suppressTips) {
|
|
242
259
|
if (pendingNoFilesTip) {
|
|
@@ -247,10 +264,10 @@ export async function runOracle(options, deps = {}) {
|
|
|
247
264
|
}
|
|
248
265
|
}
|
|
249
266
|
if (isLongRunningModel) {
|
|
250
|
-
log(dim(
|
|
267
|
+
log(dim("This model can take up to 60 minutes (usually replies much faster)."));
|
|
251
268
|
}
|
|
252
269
|
if (options.verbose || isLongRunningModel) {
|
|
253
|
-
log(dim(
|
|
270
|
+
log(dim("Press Ctrl+C to cancel."));
|
|
254
271
|
}
|
|
255
272
|
}
|
|
256
273
|
if (shouldReportFiles) {
|
|
@@ -261,50 +278,51 @@ export async function runOracle(options, deps = {}) {
|
|
|
261
278
|
}
|
|
262
279
|
logVerbose(`Estimated tokens (request body): ${estimatedInputTokens.toLocaleString()}`);
|
|
263
280
|
if (isPreview && previewMode) {
|
|
264
|
-
if (previewMode ===
|
|
265
|
-
log(
|
|
281
|
+
if (previewMode === "json" || previewMode === "full") {
|
|
282
|
+
log("Request JSON");
|
|
266
283
|
log(JSON.stringify(requestBody, null, 2));
|
|
267
|
-
log(
|
|
284
|
+
log("");
|
|
268
285
|
}
|
|
269
|
-
if (previewMode ===
|
|
270
|
-
log(
|
|
286
|
+
if (previewMode === "full") {
|
|
287
|
+
log("Assembled Prompt");
|
|
271
288
|
log(promptWithFiles);
|
|
272
|
-
log(
|
|
289
|
+
log("");
|
|
273
290
|
}
|
|
274
291
|
log(`Estimated input tokens: ${estimatedInputTokens.toLocaleString()} / ${inputTokenBudget.toLocaleString()} (model: ${modelConfig.model})`);
|
|
275
292
|
return {
|
|
276
|
-
mode:
|
|
293
|
+
mode: "preview",
|
|
277
294
|
previewMode,
|
|
278
295
|
requestBody,
|
|
279
296
|
estimatedInputTokens,
|
|
280
297
|
inputTokenBudget,
|
|
281
298
|
};
|
|
282
299
|
}
|
|
283
|
-
const
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
300
|
+
const proxyCompatibleBaseUrl = baseUrl && (isOpenRouterBaseUrl(baseUrl) || isCustomBaseUrl(baseUrl)) ? baseUrl : undefined;
|
|
301
|
+
const apiEndpoint = modelConfig.model.startsWith("gemini")
|
|
302
|
+
? proxyCompatibleBaseUrl
|
|
303
|
+
: proxyCompatibleBaseUrl
|
|
304
|
+
? proxyCompatibleBaseUrl
|
|
305
|
+
: modelConfig.model.startsWith("claude")
|
|
306
|
+
? (process.env.ANTHROPIC_BASE_URL ?? baseUrl)
|
|
289
307
|
: baseUrl;
|
|
290
308
|
const clientInstance = client ??
|
|
291
309
|
clientFactory(apiKey, {
|
|
292
310
|
baseUrl: apiEndpoint,
|
|
293
311
|
azure: options.azure,
|
|
294
312
|
model: options.model,
|
|
295
|
-
resolvedModelId: modelConfig.model.startsWith(
|
|
313
|
+
resolvedModelId: modelConfig.model.startsWith("claude")
|
|
296
314
|
? resolveClaudeModelId(effectiveModelId)
|
|
297
|
-
: modelConfig.model.startsWith(
|
|
315
|
+
: modelConfig.model.startsWith("gemini")
|
|
298
316
|
? resolveGeminiModelId(effectiveModelId)
|
|
299
317
|
: effectiveModelId,
|
|
300
318
|
httpTimeoutMs: options.httpTimeoutMs,
|
|
301
319
|
});
|
|
302
|
-
logVerbose(
|
|
320
|
+
logVerbose("Dispatching request to API...");
|
|
303
321
|
if (options.verbose) {
|
|
304
|
-
log(
|
|
322
|
+
log(""); // ensure verbose section is separated from Answer stream
|
|
305
323
|
}
|
|
306
324
|
const stopOscProgress = startOscProgress({
|
|
307
|
-
label: useBackground ?
|
|
325
|
+
label: useBackground ? "Waiting for API (background)" : "Waiting for API",
|
|
308
326
|
targetMs: useBackground ? timeoutMs : Math.min(timeoutMs, 10 * 60_000),
|
|
309
327
|
indeterminate: true,
|
|
310
328
|
write: sinkWrite,
|
|
@@ -318,16 +336,16 @@ export async function runOracle(options, deps = {}) {
|
|
|
318
336
|
const timeoutExceeded = () => now() - runStart >= timeoutMs;
|
|
319
337
|
const throwIfTimedOut = () => {
|
|
320
338
|
if (timeoutExceeded()) {
|
|
321
|
-
throw new OracleTransportError(
|
|
339
|
+
throw new OracleTransportError("client-timeout", `Timed out waiting for API response after ${formatElapsed(timeoutMs)}.`);
|
|
322
340
|
}
|
|
323
341
|
};
|
|
324
342
|
const ensureAnswerHeader = () => {
|
|
325
343
|
if (options.silent || answerHeaderPrinted)
|
|
326
344
|
return;
|
|
327
345
|
// Always add a separating newline for readability; optionally include the label depending on caller needs.
|
|
328
|
-
log(
|
|
346
|
+
log("");
|
|
329
347
|
if (allowAnswerHeader) {
|
|
330
|
-
log(chalk.bold(
|
|
348
|
+
log(chalk.bold("Answer:"));
|
|
331
349
|
}
|
|
332
350
|
answerHeaderPrinted = true;
|
|
333
351
|
};
|
|
@@ -395,20 +413,20 @@ export async function runOracle(options, deps = {}) {
|
|
|
395
413
|
isTty && !renderPlain
|
|
396
414
|
? createMarkdownStreamer({
|
|
397
415
|
render: renderMarkdownAnsi,
|
|
398
|
-
spacing:
|
|
399
|
-
mode:
|
|
416
|
+
spacing: "single",
|
|
417
|
+
mode: "hybrid",
|
|
400
418
|
})
|
|
401
419
|
: null;
|
|
402
420
|
for await (const event of stream) {
|
|
403
421
|
throwIfTimedOut();
|
|
404
|
-
const isTextDelta = event.type ===
|
|
422
|
+
const isTextDelta = event.type === "chunk" || event.type === "response.output_text.delta";
|
|
405
423
|
if (!isTextDelta)
|
|
406
424
|
continue;
|
|
407
425
|
stopOscProgress();
|
|
408
426
|
stopHeartbeatNow();
|
|
409
427
|
sawTextDelta = true;
|
|
410
428
|
ensureAnswerHeader();
|
|
411
|
-
if (options.silent || typeof event.delta !==
|
|
429
|
+
if (options.silent || typeof event.delta !== "string")
|
|
412
430
|
continue;
|
|
413
431
|
// Always keep the log/bookkeeping sink up to date.
|
|
414
432
|
sinkWrite(event.delta);
|
|
@@ -448,41 +466,41 @@ export async function runOracle(options, deps = {}) {
|
|
|
448
466
|
stopOscProgress();
|
|
449
467
|
}
|
|
450
468
|
if (!response) {
|
|
451
|
-
throw new Error(
|
|
469
|
+
throw new Error("API did not return a response.");
|
|
452
470
|
}
|
|
453
471
|
// We only add spacing when streamed text was printed.
|
|
454
472
|
if (sawTextDelta && !options.silent) {
|
|
455
473
|
if (renderPlain) {
|
|
456
474
|
// Plain streaming already wrote chunks; ensure clean separation.
|
|
457
|
-
stdoutWrite(
|
|
475
|
+
stdoutWrite("\n");
|
|
458
476
|
}
|
|
459
477
|
else {
|
|
460
478
|
// Separate streamed output from logs.
|
|
461
|
-
log(
|
|
479
|
+
log("");
|
|
462
480
|
}
|
|
463
481
|
}
|
|
464
|
-
logVerbose(`Response status: ${response.status ??
|
|
465
|
-
if (response.status && response.status !==
|
|
482
|
+
logVerbose(`Response status: ${response.status ?? "completed"}`);
|
|
483
|
+
if (response.status && response.status !== "completed") {
|
|
466
484
|
// API can reply `in_progress` even after the stream closes; give it a brief grace poll.
|
|
467
|
-
if (response.id && response.status ===
|
|
485
|
+
if (response.id && response.status === "in_progress") {
|
|
468
486
|
const polishingStart = now();
|
|
469
487
|
const pollIntervalMs = 2_000;
|
|
470
488
|
const maxWaitMs = 180_000;
|
|
471
|
-
log(chalk.dim(
|
|
489
|
+
log(chalk.dim("Response still in_progress; polling until completion..."));
|
|
472
490
|
// Short polling loop — we don't want to hang forever, just catch late finalization.
|
|
473
491
|
while (now() - polishingStart < maxWaitMs) {
|
|
474
492
|
throwIfTimedOut();
|
|
475
493
|
await wait(pollIntervalMs);
|
|
476
494
|
const refreshed = await clientInstance.responses.retrieve(response.id);
|
|
477
|
-
if (refreshed.status ===
|
|
495
|
+
if (refreshed.status === "completed") {
|
|
478
496
|
response = refreshed;
|
|
479
497
|
break;
|
|
480
498
|
}
|
|
481
499
|
}
|
|
482
500
|
}
|
|
483
|
-
if (response.status !==
|
|
501
|
+
if (response.status !== "completed") {
|
|
484
502
|
const detail = response.error?.message || response.incomplete_details?.reason || response.status;
|
|
485
|
-
log(chalk.yellow(`API ended the run early (status=${response.status}${response.incomplete_details?.reason ? `, reason=${response.incomplete_details.reason}` :
|
|
503
|
+
log(chalk.yellow(`API ended the run early (status=${response.status}${response.incomplete_details?.reason ? `, reason=${response.incomplete_details.reason}` : ""}).`));
|
|
486
504
|
throw new OracleResponseError(`Response did not complete: ${detail}`, response);
|
|
487
505
|
}
|
|
488
506
|
}
|
|
@@ -499,16 +517,16 @@ export async function runOracle(options, deps = {}) {
|
|
|
499
517
|
? renderPlain || !richTty
|
|
500
518
|
? answerText
|
|
501
519
|
: renderMarkdownAnsi(answerText)
|
|
502
|
-
: chalk.dim(
|
|
520
|
+
: chalk.dim("(no text output)");
|
|
503
521
|
sinkWrite(printable);
|
|
504
|
-
if (!printable.endsWith(
|
|
505
|
-
sinkWrite(
|
|
522
|
+
if (!printable.endsWith("\n")) {
|
|
523
|
+
sinkWrite("\n");
|
|
506
524
|
}
|
|
507
525
|
stdoutWrite(printable);
|
|
508
|
-
if (!printable.endsWith(
|
|
509
|
-
stdoutWrite(
|
|
526
|
+
if (!printable.endsWith("\n")) {
|
|
527
|
+
stdoutWrite("\n");
|
|
510
528
|
}
|
|
511
|
-
log(
|
|
529
|
+
log("");
|
|
512
530
|
}
|
|
513
531
|
}
|
|
514
532
|
const usage = response.usage ?? {};
|
|
@@ -520,17 +538,21 @@ export async function runOracle(options, deps = {}) {
|
|
|
520
538
|
const cost = pricing
|
|
521
539
|
? estimateUsdCost({
|
|
522
540
|
usage: { inputTokens, outputTokens, reasoningTokens, totalTokens },
|
|
523
|
-
pricing: {
|
|
541
|
+
pricing: {
|
|
542
|
+
inputUsdPerToken: pricing.inputPerToken,
|
|
543
|
+
outputUsdPerToken: pricing.outputPerToken,
|
|
544
|
+
},
|
|
524
545
|
})?.totalUsd
|
|
525
546
|
: undefined;
|
|
526
547
|
const effortLabel = modelConfig.reasoning?.effort;
|
|
527
548
|
const modelLabel = effortLabel ? `${modelConfig.model}[${effortLabel}]` : modelConfig.model;
|
|
528
|
-
const sessionIdContainsModel = typeof options.sessionId ===
|
|
549
|
+
const sessionIdContainsModel = typeof options.sessionId === "string" &&
|
|
550
|
+
options.sessionId.toLowerCase().includes(modelConfig.model.toLowerCase());
|
|
529
551
|
const tokensDisplay = [inputTokens, outputTokens, reasoningTokens, totalTokens]
|
|
530
552
|
.map((value, index) => formatTokenValue(value, usage, index))
|
|
531
|
-
.join(
|
|
553
|
+
.join("/");
|
|
532
554
|
const tokensPart = (() => {
|
|
533
|
-
const parts = tokensDisplay.split(
|
|
555
|
+
const parts = tokensDisplay.split("/");
|
|
534
556
|
if (parts.length !== 4)
|
|
535
557
|
return tokensDisplay;
|
|
536
558
|
return `↑${parts[0]} ↓${parts[1]} ↻${parts[2]} Δ${parts[3]}`;
|
|
@@ -543,7 +565,11 @@ export async function runOracle(options, deps = {}) {
|
|
|
543
565
|
if (actualInput === undefined)
|
|
544
566
|
return null;
|
|
545
567
|
const delta = actualInput - estimatedInputTokens;
|
|
546
|
-
const deltaText = delta === 0
|
|
568
|
+
const deltaText = delta === 0
|
|
569
|
+
? ""
|
|
570
|
+
: delta > 0
|
|
571
|
+
? ` (+${delta.toLocaleString()})`
|
|
572
|
+
: ` (${delta.toLocaleString()})`;
|
|
547
573
|
return `est→actual=${estimatedInputTokens.toLocaleString()}→${actualInput.toLocaleString()}${deltaText}`;
|
|
548
574
|
})();
|
|
549
575
|
const { line1, line2 } = formatFinishLine({
|
|
@@ -554,43 +580,49 @@ export async function runOracle(options, deps = {}) {
|
|
|
554
580
|
summaryExtraParts: options.sessionId ? [`sid=${options.sessionId}`] : null,
|
|
555
581
|
detailParts: [
|
|
556
582
|
estActualPart,
|
|
557
|
-
!searchEnabled ?
|
|
583
|
+
!searchEnabled ? "search=off" : null,
|
|
558
584
|
files.length > 0 ? `files=${files.length}` : null,
|
|
559
585
|
],
|
|
560
586
|
});
|
|
561
587
|
if (!options.silent) {
|
|
562
|
-
log(
|
|
588
|
+
log("");
|
|
563
589
|
}
|
|
564
590
|
log(chalk.blue(line1));
|
|
565
591
|
if (line2) {
|
|
566
592
|
log(dim(line2));
|
|
567
593
|
}
|
|
568
594
|
return {
|
|
569
|
-
mode:
|
|
595
|
+
mode: "live",
|
|
570
596
|
response,
|
|
571
|
-
usage: {
|
|
597
|
+
usage: {
|
|
598
|
+
inputTokens,
|
|
599
|
+
outputTokens,
|
|
600
|
+
reasoningTokens,
|
|
601
|
+
totalTokens,
|
|
602
|
+
...(cost != null ? { cost } : {}),
|
|
603
|
+
},
|
|
572
604
|
elapsedMs,
|
|
573
605
|
};
|
|
574
606
|
}
|
|
575
607
|
export function extractTextOutput(response) {
|
|
576
608
|
if (Array.isArray(response.output_text) && response.output_text.length > 0) {
|
|
577
|
-
return response.output_text.join(
|
|
609
|
+
return response.output_text.join("\n");
|
|
578
610
|
}
|
|
579
611
|
if (Array.isArray(response.output)) {
|
|
580
612
|
const segments = [];
|
|
581
613
|
for (const item of response.output) {
|
|
582
614
|
if (Array.isArray(item.content)) {
|
|
583
615
|
for (const chunk of item.content) {
|
|
584
|
-
if (chunk && (chunk.type ===
|
|
616
|
+
if (chunk && (chunk.type === "output_text" || chunk.type === "text") && chunk.text) {
|
|
585
617
|
segments.push(chunk.text);
|
|
586
618
|
}
|
|
587
619
|
}
|
|
588
620
|
}
|
|
589
|
-
else if (typeof item.text ===
|
|
621
|
+
else if (typeof item.text === "string") {
|
|
590
622
|
segments.push(item.text);
|
|
591
623
|
}
|
|
592
624
|
}
|
|
593
|
-
return segments.join(
|
|
625
|
+
return segments.join("\n");
|
|
594
626
|
}
|
|
595
|
-
return
|
|
627
|
+
return "";
|
|
596
628
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
export function resolvePreviewMode(value) {
|
|
2
|
-
const allowed = new Set([
|
|
3
|
-
if (typeof value ===
|
|
4
|
-
return allowed.has(value) ? value :
|
|
2
|
+
const allowed = new Set(["summary", "json", "full"]);
|
|
3
|
+
if (typeof value === "string" && value.length > 0) {
|
|
4
|
+
return allowed.has(value) ? value : "summary";
|
|
5
5
|
}
|
|
6
6
|
if (value) {
|
|
7
|
-
return
|
|
7
|
+
return "summary";
|
|
8
8
|
}
|
|
9
9
|
return undefined;
|
|
10
10
|
}
|
|
@@ -13,7 +13,10 @@ export function resolvePreviewMode(value) {
|
|
|
13
13
|
*/
|
|
14
14
|
export function formatTokenCount(value) {
|
|
15
15
|
if (Math.abs(value) >= 1000) {
|
|
16
|
-
const abbreviated = (value / 1000)
|
|
16
|
+
const abbreviated = (value / 1000)
|
|
17
|
+
.toFixed(2)
|
|
18
|
+
.replace(/\.0+$/, "")
|
|
19
|
+
.replace(/\.([1-9]*)0$/, ".$1");
|
|
17
20
|
return `${abbreviated}k`;
|
|
18
21
|
}
|
|
19
22
|
return value.toLocaleString();
|