@steipete/oracle 0.9.0 → 0.11.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 +107 -49
- package/dist/bin/oracle-cli.js +551 -410
- 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/archiveConversation.js +224 -0
- package/dist/src/browser/actions/assistantResponse.js +175 -101
- package/dist/src/browser/actions/attachmentDataTransfer.js +49 -47
- package/dist/src/browser/actions/attachments.js +246 -150
- package/dist/src/browser/actions/deepResearch.js +662 -0
- package/dist/src/browser/actions/domEvents.js +2 -2
- package/dist/src/browser/actions/modelSelection.js +342 -119
- package/dist/src/browser/actions/navigation.js +183 -137
- package/dist/src/browser/actions/projectSources.js +491 -0
- package/dist/src/browser/actions/promptComposer.js +152 -91
- package/dist/src/browser/actions/remoteFileTransfer.js +10 -10
- package/dist/src/browser/actions/thinkingStatus.js +391 -0
- package/dist/src/browser/actions/thinkingTime.js +207 -110
- package/dist/src/browser/artifacts.js +150 -0
- package/dist/src/browser/attachRunning.js +31 -0
- package/dist/src/browser/chatgptImages.js +315 -0
- package/dist/src/browser/chromeLifecycle.js +276 -63
- package/dist/src/browser/config.js +59 -16
- package/dist/src/browser/constants.js +25 -12
- package/dist/src/browser/controlPlan.js +81 -0
- package/dist/src/browser/cookies.js +19 -19
- package/dist/src/browser/detect.js +250 -77
- package/dist/src/browser/domDebug.js +50 -1
- package/dist/src/browser/index.js +1559 -692
- package/dist/src/browser/liveTabs.js +434 -0
- 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 +127 -42
- package/dist/src/browser/projectSourcesRunner.js +366 -0
- package/dist/src/browser/prompt.js +72 -42
- package/dist/src/browser/promptSummary.js +5 -5
- package/dist/src/browser/providerDomFlow.js +1 -1
- package/dist/src/browser/providers/chatgptDomProvider.js +9 -9
- package/dist/src/browser/providers/geminiDeepThinkDomProvider.js +51 -42
- package/dist/src/browser/providers/index.js +2 -2
- package/dist/src/browser/reattach.js +178 -73
- package/dist/src/browser/reattachHelpers.js +32 -27
- package/dist/src/browser/sessionRunner.js +89 -25
- package/dist/src/browser/tabLeaseRegistry.js +182 -0
- package/dist/src/browser/utils.js +9 -9
- package/dist/src/browserMode.js +1 -1
- package/dist/src/cli/bridge/claudeConfig.js +24 -20
- 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 +102 -48
- package/dist/src/cli/browserDefaults.js +51 -26
- package/dist/src/cli/browserTabs.js +228 -0
- package/dist/src/cli/bundleWarnings.js +1 -1
- package/dist/src/cli/clipboard.js +11 -2
- package/dist/src/cli/detach.js +2 -2
- package/dist/src/cli/dryRun.js +62 -26
- package/dist/src/cli/duplicatePromptGuard.js +12 -4
- package/dist/src/cli/engine.js +9 -9
- package/dist/src/cli/errorUtils.js +1 -1
- package/dist/src/cli/fileSize.js +3 -3
- 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 +7 -7
- package/dist/src/cli/markdownRenderer.js +15 -15
- package/dist/src/cli/notifier.js +77 -67
- package/dist/src/cli/options.js +131 -106
- package/dist/src/cli/oscUtils.js +1 -1
- package/dist/src/cli/projectSources.js +116 -0
- 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 +32 -28
- package/dist/src/cli/sessionCommand.js +82 -21
- package/dist/src/cli/sessionDisplay.js +213 -87
- package/dist/src/cli/sessionLineage.js +6 -2
- package/dist/src/cli/sessionRunner.js +149 -95
- package/dist/src/cli/sessionTable.js +26 -23
- package/dist/src/cli/stdin.js +22 -0
- package/dist/src/cli/tagline.js +121 -124
- package/dist/src/cli/tui/index.js +139 -128
- package/dist/src/cli/writeOutputPath.js +5 -5
- package/dist/src/config.js +7 -7
- package/dist/src/gemini-web/browserSessionManager.js +19 -15
- package/dist/src/gemini-web/client.js +76 -70
- package/dist/src/gemini-web/executionMode.js +6 -8
- package/dist/src/gemini-web/executor.js +98 -93
- package/dist/src/gemini-web/index.js +1 -1
- package/dist/src/mcp/consultPresets.js +19 -0
- package/dist/src/mcp/server.js +18 -12
- package/dist/src/mcp/tools/consult.js +246 -67
- package/dist/src/mcp/tools/projectSources.js +123 -0
- package/dist/src/mcp/tools/sessionResources.js +12 -12
- package/dist/src/mcp/tools/sessions.js +26 -17
- package/dist/src/mcp/types.js +12 -5
- package/dist/src/mcp/utils.js +21 -8
- package/dist/src/oracle/background.js +15 -15
- package/dist/src/oracle/claude.js +53 -25
- package/dist/src/oracle/client.js +50 -41
- package/dist/src/oracle/config.js +96 -66
- package/dist/src/oracle/errors.js +38 -38
- package/dist/src/oracle/files.js +55 -46
- package/dist/src/oracle/finishLine.js +10 -8
- package/dist/src/oracle/format.js +3 -3
- package/dist/src/oracle/gemini.js +37 -33
- 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 +16 -13
- package/dist/src/oracle/run.js +160 -135
- 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/projectSources/plan.js +27 -0
- package/dist/src/projectSources/url.js +23 -0
- 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 +78 -75
- package/dist/src/sessionStore.js +3 -3
- package/dist/src/version.js +10 -10
- package/dist/vendor/oracle-notifier/README.md +2 -0
- package/package.json +67 -62
- 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 → src/projectSources}/types.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, isCustomBaseUrl } 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,76 @@ 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
|
-
:
|
|
127
|
-
|
|
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";
|
|
130
|
+
const browserModeHint = options.model.startsWith("gpt")
|
|
131
|
+
? ' If you have a ChatGPT Pro subscription, retry with --engine browser (or MCP engine:"browser" / preset:"chatgpt-pro-heavy"); browser mode uses your signed-in ChatGPT session instead of an API key.'
|
|
132
|
+
: "";
|
|
133
|
+
throw new PromptValidationError(`Missing ${envVar}. Set it via the environment or a .env file.${browserModeHint}`, {
|
|
128
134
|
env: envVar,
|
|
129
135
|
});
|
|
130
136
|
}
|
|
131
137
|
const envVar = apiKeyResult.source;
|
|
132
|
-
const minPromptLength = Number.parseInt(process.env.ORACLE_MIN_PROMPT_CHARS ??
|
|
138
|
+
const minPromptLength = Number.parseInt(process.env.ORACLE_MIN_PROMPT_CHARS ?? "10", 10);
|
|
133
139
|
const promptLength = options.prompt?.trim().length ?? 0;
|
|
134
140
|
// Enforce the short-prompt guardrail on pro-tier models because they're costly; cheaper models can run short prompts without blocking.
|
|
135
141
|
const isProTierModel = isProModel(options.model);
|
|
136
142
|
if (isProTierModel && !Number.isNaN(minPromptLength) && promptLength < minPromptLength) {
|
|
137
143
|
throw new PromptValidationError(`Prompt is too short (<${minPromptLength} chars). This was likely accidental; please provide more detail.`, { minPromptLength, promptLength });
|
|
138
144
|
}
|
|
139
|
-
const resolverOpenRouterApiKey = openRouterFallback || isOpenRouterBaseUrl(baseUrl) ? openRouterApiKey ?? apiKey : undefined;
|
|
145
|
+
const resolverOpenRouterApiKey = openRouterFallback || isOpenRouterBaseUrl(baseUrl) ? (openRouterApiKey ?? apiKey) : undefined;
|
|
140
146
|
const modelConfig = await resolveModelConfig(options.model, {
|
|
141
147
|
baseUrl,
|
|
142
148
|
openRouterApiKey: resolverOpenRouterApiKey,
|
|
143
149
|
});
|
|
144
150
|
const isLongRunningModel = isProTierModel;
|
|
145
151
|
const supportsBackground = modelConfig.supportsBackground !== false;
|
|
146
|
-
const useBackground = supportsBackground ? options.background ?? isLongRunningModel : false;
|
|
152
|
+
const useBackground = supportsBackground ? (options.background ?? isLongRunningModel) : false;
|
|
147
153
|
const inputTokenBudget = options.maxInput ?? modelConfig.inputLimit;
|
|
148
|
-
const files = await readFiles(options.file ?? [], {
|
|
154
|
+
const files = await readFiles(options.file ?? [], {
|
|
155
|
+
cwd,
|
|
156
|
+
fsModule,
|
|
157
|
+
maxFileSizeBytes: options.maxFileSizeBytes,
|
|
158
|
+
});
|
|
149
159
|
const searchEnabled = options.search !== false;
|
|
150
160
|
logVerbose(`cwd: ${cwd}`);
|
|
151
161
|
let pendingNoFilesTip = null;
|
|
@@ -154,21 +164,21 @@ export async function runOracle(options, deps = {}) {
|
|
|
154
164
|
const displayPaths = files
|
|
155
165
|
.map((file) => path.relative(cwd, file.path) || file.path)
|
|
156
166
|
.slice(0, 10)
|
|
157
|
-
.join(
|
|
158
|
-
const extra = files.length > 10 ? ` (+${files.length - 10} more)` :
|
|
167
|
+
.join(", ");
|
|
168
|
+
const extra = files.length > 10 ? ` (+${files.length - 10} more)` : "";
|
|
159
169
|
logVerbose(`Attached files (${files.length}): ${displayPaths}${extra}`);
|
|
160
170
|
}
|
|
161
171
|
else {
|
|
162
|
-
logVerbose(
|
|
172
|
+
logVerbose("No files attached.");
|
|
163
173
|
if (!isPreview) {
|
|
164
174
|
pendingNoFilesTip =
|
|
165
|
-
|
|
175
|
+
"Tip: no files attached — Oracle works best with project context. Add files via --file path/to/code or docs.";
|
|
166
176
|
}
|
|
167
177
|
}
|
|
168
178
|
const shortPrompt = (options.prompt?.trim().length ?? 0) < 80;
|
|
169
179
|
if (!isPreview && shortPrompt) {
|
|
170
180
|
pendingShortPromptTip =
|
|
171
|
-
|
|
181
|
+
"Tip: brief prompts often yield generic answers — aim for 6–30 sentences and attach key files.";
|
|
172
182
|
}
|
|
173
183
|
const fileTokenInfo = getFileTokenStats(files, {
|
|
174
184
|
cwd,
|
|
@@ -183,7 +193,7 @@ export async function runOracle(options, deps = {}) {
|
|
|
183
193
|
const fileCount = files.length;
|
|
184
194
|
const richTty = allowStdout && process.stdout.isTTY && chalk.level > 0;
|
|
185
195
|
const renderPlain = Boolean(options.renderPlain);
|
|
186
|
-
const timeoutSeconds = options.timeoutSeconds === undefined || options.timeoutSeconds ===
|
|
196
|
+
const timeoutSeconds = options.timeoutSeconds === undefined || options.timeoutSeconds === "auto"
|
|
187
197
|
? isLongRunningModel
|
|
188
198
|
? DEFAULT_TIMEOUT_PRO_MS / 1000
|
|
189
199
|
: DEFAULT_TIMEOUT_NON_PRO_MS / 1000
|
|
@@ -194,7 +204,7 @@ export async function runOracle(options, deps = {}) {
|
|
|
194
204
|
const effectiveModelId = options.effectiveModelId ??
|
|
195
205
|
(azureDeploymentName
|
|
196
206
|
? azureDeploymentName
|
|
197
|
-
: options.model.startsWith(
|
|
207
|
+
: options.model.startsWith("gemini")
|
|
198
208
|
? resolveGeminiModelId(options.model)
|
|
199
209
|
: (modelConfig.apiModel ?? modelConfig.model));
|
|
200
210
|
if (!isPreview && options.previousResponseId) {
|
|
@@ -213,30 +223,31 @@ export async function runOracle(options, deps = {}) {
|
|
|
213
223
|
});
|
|
214
224
|
requestBody.model = effectiveModelId;
|
|
215
225
|
const estimatedInputTokens = estimateRequestTokens(requestBody, modelConfig);
|
|
216
|
-
const tokenLabel = formatTokenEstimate(estimatedInputTokens, (text) =>
|
|
226
|
+
const tokenLabel = formatTokenEstimate(estimatedInputTokens, (text) => richTty ? chalk.green(text) : text);
|
|
217
227
|
const fileLabel = richTty ? chalk.magenta(fileCount.toString()) : fileCount.toString();
|
|
218
|
-
const filesPhrase = fileCount === 0 ?
|
|
228
|
+
const filesPhrase = fileCount === 0 ? "no files" : `${fileLabel} files`;
|
|
219
229
|
const headerModelLabelBase = richTty ? chalk.cyan(modelConfig.model) : modelConfig.model;
|
|
220
230
|
const headerModelSuffix = effectiveModelId !== modelConfig.model
|
|
221
231
|
? richTty
|
|
222
232
|
? chalk.gray(` (API: ${effectiveModelId})`)
|
|
223
233
|
: ` (API: ${effectiveModelId})`
|
|
224
|
-
:
|
|
234
|
+
: "";
|
|
225
235
|
const headerLine = `Calling ${headerModelLabelBase}${headerModelSuffix} — ${tokenLabel} tokens, ${filesPhrase}.`;
|
|
226
|
-
const shouldReportFiles = (options.filesReport || fileTokenInfo.totalTokens > inputTokenBudget) &&
|
|
236
|
+
const shouldReportFiles = (options.filesReport || fileTokenInfo.totalTokens > inputTokenBudget) &&
|
|
237
|
+
fileTokenInfo.stats.length > 0;
|
|
227
238
|
if (!isPreview) {
|
|
228
239
|
if (!options.suppressHeader) {
|
|
229
240
|
log(headerLine);
|
|
230
241
|
}
|
|
231
242
|
const maskedKey = maskApiKey(apiKey);
|
|
232
243
|
if (maskedKey && options.verbose) {
|
|
233
|
-
const resolvedSuffix = effectiveModelId !== modelConfig.model ? ` (API: ${effectiveModelId})` :
|
|
244
|
+
const resolvedSuffix = effectiveModelId !== modelConfig.model ? ` (API: ${effectiveModelId})` : "";
|
|
234
245
|
log(dim(`Using ${envVar}=${maskedKey} for model ${modelConfig.model}${resolvedSuffix}`));
|
|
235
246
|
}
|
|
236
247
|
if (!options.suppressHeader &&
|
|
237
|
-
(modelConfig.model ===
|
|
238
|
-
effectiveModelId ===
|
|
239
|
-
log(dim(`Note: \`${modelConfig.model}\` is a stable CLI alias; OpenAI API uses \`gpt-5.
|
|
248
|
+
(modelConfig.model === "gpt-5.1-pro" || modelConfig.model === "gpt-5.2-pro") &&
|
|
249
|
+
effectiveModelId === "gpt-5.5-pro") {
|
|
250
|
+
log(dim(`Note: \`${modelConfig.model}\` is a stable CLI alias; OpenAI API uses \`gpt-5.5-pro\`.`));
|
|
240
251
|
}
|
|
241
252
|
if (baseUrl) {
|
|
242
253
|
log(dim(`Base URL: ${formatBaseUrlForLog(baseUrl)}`));
|
|
@@ -245,7 +256,7 @@ export async function runOracle(options, deps = {}) {
|
|
|
245
256
|
log(dim(`Resolved model: ${modelConfig.model} → ${effectiveModelId}`));
|
|
246
257
|
}
|
|
247
258
|
if (options.background && !supportsBackground) {
|
|
248
|
-
log(dim(
|
|
259
|
+
log(dim("Background runs are not supported for this model; streaming in foreground instead."));
|
|
249
260
|
}
|
|
250
261
|
if (!options.suppressTips) {
|
|
251
262
|
if (pendingNoFilesTip) {
|
|
@@ -256,10 +267,10 @@ export async function runOracle(options, deps = {}) {
|
|
|
256
267
|
}
|
|
257
268
|
}
|
|
258
269
|
if (isLongRunningModel) {
|
|
259
|
-
log(dim(
|
|
270
|
+
log(dim("This model can take up to 60 minutes (usually replies much faster)."));
|
|
260
271
|
}
|
|
261
272
|
if (options.verbose || isLongRunningModel) {
|
|
262
|
-
log(dim(
|
|
273
|
+
log(dim("Press Ctrl+C to cancel."));
|
|
263
274
|
}
|
|
264
275
|
}
|
|
265
276
|
if (shouldReportFiles) {
|
|
@@ -270,19 +281,19 @@ export async function runOracle(options, deps = {}) {
|
|
|
270
281
|
}
|
|
271
282
|
logVerbose(`Estimated tokens (request body): ${estimatedInputTokens.toLocaleString()}`);
|
|
272
283
|
if (isPreview && previewMode) {
|
|
273
|
-
if (previewMode ===
|
|
274
|
-
log(
|
|
284
|
+
if (previewMode === "json" || previewMode === "full") {
|
|
285
|
+
log("Request JSON");
|
|
275
286
|
log(JSON.stringify(requestBody, null, 2));
|
|
276
|
-
log(
|
|
287
|
+
log("");
|
|
277
288
|
}
|
|
278
|
-
if (previewMode ===
|
|
279
|
-
log(
|
|
289
|
+
if (previewMode === "full") {
|
|
290
|
+
log("Assembled Prompt");
|
|
280
291
|
log(promptWithFiles);
|
|
281
|
-
log(
|
|
292
|
+
log("");
|
|
282
293
|
}
|
|
283
294
|
log(`Estimated input tokens: ${estimatedInputTokens.toLocaleString()} / ${inputTokenBudget.toLocaleString()} (model: ${modelConfig.model})`);
|
|
284
295
|
return {
|
|
285
|
-
mode:
|
|
296
|
+
mode: "preview",
|
|
286
297
|
previewMode,
|
|
287
298
|
requestBody,
|
|
288
299
|
estimatedInputTokens,
|
|
@@ -290,31 +301,31 @@ export async function runOracle(options, deps = {}) {
|
|
|
290
301
|
};
|
|
291
302
|
}
|
|
292
303
|
const proxyCompatibleBaseUrl = baseUrl && (isOpenRouterBaseUrl(baseUrl) || isCustomBaseUrl(baseUrl)) ? baseUrl : undefined;
|
|
293
|
-
const apiEndpoint = modelConfig.model.startsWith(
|
|
304
|
+
const apiEndpoint = modelConfig.model.startsWith("gemini")
|
|
294
305
|
? proxyCompatibleBaseUrl
|
|
295
306
|
: proxyCompatibleBaseUrl
|
|
296
307
|
? proxyCompatibleBaseUrl
|
|
297
|
-
: modelConfig.model.startsWith(
|
|
298
|
-
? process.env.ANTHROPIC_BASE_URL ?? baseUrl
|
|
308
|
+
: modelConfig.model.startsWith("claude")
|
|
309
|
+
? (process.env.ANTHROPIC_BASE_URL ?? baseUrl)
|
|
299
310
|
: baseUrl;
|
|
300
311
|
const clientInstance = client ??
|
|
301
312
|
clientFactory(apiKey, {
|
|
302
313
|
baseUrl: apiEndpoint,
|
|
303
314
|
azure: options.azure,
|
|
304
315
|
model: options.model,
|
|
305
|
-
resolvedModelId: modelConfig.model.startsWith(
|
|
316
|
+
resolvedModelId: modelConfig.model.startsWith("claude")
|
|
306
317
|
? resolveClaudeModelId(effectiveModelId)
|
|
307
|
-
: modelConfig.model.startsWith(
|
|
318
|
+
: modelConfig.model.startsWith("gemini")
|
|
308
319
|
? resolveGeminiModelId(effectiveModelId)
|
|
309
320
|
: effectiveModelId,
|
|
310
321
|
httpTimeoutMs: options.httpTimeoutMs,
|
|
311
322
|
});
|
|
312
|
-
logVerbose(
|
|
323
|
+
logVerbose("Dispatching request to API...");
|
|
313
324
|
if (options.verbose) {
|
|
314
|
-
log(
|
|
325
|
+
log(""); // ensure verbose section is separated from Answer stream
|
|
315
326
|
}
|
|
316
327
|
const stopOscProgress = startOscProgress({
|
|
317
|
-
label: useBackground ?
|
|
328
|
+
label: useBackground ? "Waiting for API (background)" : "Waiting for API",
|
|
318
329
|
targetMs: useBackground ? timeoutMs : Math.min(timeoutMs, 10 * 60_000),
|
|
319
330
|
indeterminate: true,
|
|
320
331
|
write: sinkWrite,
|
|
@@ -328,16 +339,16 @@ export async function runOracle(options, deps = {}) {
|
|
|
328
339
|
const timeoutExceeded = () => now() - runStart >= timeoutMs;
|
|
329
340
|
const throwIfTimedOut = () => {
|
|
330
341
|
if (timeoutExceeded()) {
|
|
331
|
-
throw new OracleTransportError(
|
|
342
|
+
throw new OracleTransportError("client-timeout", `Timed out waiting for API response after ${formatElapsed(timeoutMs)}.`);
|
|
332
343
|
}
|
|
333
344
|
};
|
|
334
345
|
const ensureAnswerHeader = () => {
|
|
335
346
|
if (options.silent || answerHeaderPrinted)
|
|
336
347
|
return;
|
|
337
348
|
// Always add a separating newline for readability; optionally include the label depending on caller needs.
|
|
338
|
-
log(
|
|
349
|
+
log("");
|
|
339
350
|
if (allowAnswerHeader) {
|
|
340
|
-
log(chalk.bold(
|
|
351
|
+
log(chalk.bold("Answer:"));
|
|
341
352
|
}
|
|
342
353
|
answerHeaderPrinted = true;
|
|
343
354
|
};
|
|
@@ -405,20 +416,20 @@ export async function runOracle(options, deps = {}) {
|
|
|
405
416
|
isTty && !renderPlain
|
|
406
417
|
? createMarkdownStreamer({
|
|
407
418
|
render: renderMarkdownAnsi,
|
|
408
|
-
spacing:
|
|
409
|
-
mode:
|
|
419
|
+
spacing: "single",
|
|
420
|
+
mode: "hybrid",
|
|
410
421
|
})
|
|
411
422
|
: null;
|
|
412
423
|
for await (const event of stream) {
|
|
413
424
|
throwIfTimedOut();
|
|
414
|
-
const isTextDelta = event.type ===
|
|
425
|
+
const isTextDelta = event.type === "chunk" || event.type === "response.output_text.delta";
|
|
415
426
|
if (!isTextDelta)
|
|
416
427
|
continue;
|
|
417
428
|
stopOscProgress();
|
|
418
429
|
stopHeartbeatNow();
|
|
419
430
|
sawTextDelta = true;
|
|
420
431
|
ensureAnswerHeader();
|
|
421
|
-
if (options.silent || typeof event.delta !==
|
|
432
|
+
if (options.silent || typeof event.delta !== "string")
|
|
422
433
|
continue;
|
|
423
434
|
// Always keep the log/bookkeeping sink up to date.
|
|
424
435
|
sinkWrite(event.delta);
|
|
@@ -458,41 +469,41 @@ export async function runOracle(options, deps = {}) {
|
|
|
458
469
|
stopOscProgress();
|
|
459
470
|
}
|
|
460
471
|
if (!response) {
|
|
461
|
-
throw new Error(
|
|
472
|
+
throw new Error("API did not return a response.");
|
|
462
473
|
}
|
|
463
474
|
// We only add spacing when streamed text was printed.
|
|
464
475
|
if (sawTextDelta && !options.silent) {
|
|
465
476
|
if (renderPlain) {
|
|
466
477
|
// Plain streaming already wrote chunks; ensure clean separation.
|
|
467
|
-
stdoutWrite(
|
|
478
|
+
stdoutWrite("\n");
|
|
468
479
|
}
|
|
469
480
|
else {
|
|
470
481
|
// Separate streamed output from logs.
|
|
471
|
-
log(
|
|
482
|
+
log("");
|
|
472
483
|
}
|
|
473
484
|
}
|
|
474
|
-
logVerbose(`Response status: ${response.status ??
|
|
475
|
-
if (response.status && response.status !==
|
|
485
|
+
logVerbose(`Response status: ${response.status ?? "completed"}`);
|
|
486
|
+
if (response.status && response.status !== "completed") {
|
|
476
487
|
// API can reply `in_progress` even after the stream closes; give it a brief grace poll.
|
|
477
|
-
if (response.id && response.status ===
|
|
488
|
+
if (response.id && response.status === "in_progress") {
|
|
478
489
|
const polishingStart = now();
|
|
479
490
|
const pollIntervalMs = 2_000;
|
|
480
491
|
const maxWaitMs = 180_000;
|
|
481
|
-
log(chalk.dim(
|
|
492
|
+
log(chalk.dim("Response still in_progress; polling until completion..."));
|
|
482
493
|
// Short polling loop — we don't want to hang forever, just catch late finalization.
|
|
483
494
|
while (now() - polishingStart < maxWaitMs) {
|
|
484
495
|
throwIfTimedOut();
|
|
485
496
|
await wait(pollIntervalMs);
|
|
486
497
|
const refreshed = await clientInstance.responses.retrieve(response.id);
|
|
487
|
-
if (refreshed.status ===
|
|
498
|
+
if (refreshed.status === "completed") {
|
|
488
499
|
response = refreshed;
|
|
489
500
|
break;
|
|
490
501
|
}
|
|
491
502
|
}
|
|
492
503
|
}
|
|
493
|
-
if (response.status !==
|
|
504
|
+
if (response.status !== "completed") {
|
|
494
505
|
const detail = response.error?.message || response.incomplete_details?.reason || response.status;
|
|
495
|
-
log(chalk.yellow(`API ended the run early (status=${response.status}${response.incomplete_details?.reason ? `, reason=${response.incomplete_details.reason}` :
|
|
506
|
+
log(chalk.yellow(`API ended the run early (status=${response.status}${response.incomplete_details?.reason ? `, reason=${response.incomplete_details.reason}` : ""}).`));
|
|
496
507
|
throw new OracleResponseError(`Response did not complete: ${detail}`, response);
|
|
497
508
|
}
|
|
498
509
|
}
|
|
@@ -509,16 +520,16 @@ export async function runOracle(options, deps = {}) {
|
|
|
509
520
|
? renderPlain || !richTty
|
|
510
521
|
? answerText
|
|
511
522
|
: renderMarkdownAnsi(answerText)
|
|
512
|
-
: chalk.dim(
|
|
523
|
+
: chalk.dim("(no text output)");
|
|
513
524
|
sinkWrite(printable);
|
|
514
|
-
if (!printable.endsWith(
|
|
515
|
-
sinkWrite(
|
|
525
|
+
if (!printable.endsWith("\n")) {
|
|
526
|
+
sinkWrite("\n");
|
|
516
527
|
}
|
|
517
528
|
stdoutWrite(printable);
|
|
518
|
-
if (!printable.endsWith(
|
|
519
|
-
stdoutWrite(
|
|
529
|
+
if (!printable.endsWith("\n")) {
|
|
530
|
+
stdoutWrite("\n");
|
|
520
531
|
}
|
|
521
|
-
log(
|
|
532
|
+
log("");
|
|
522
533
|
}
|
|
523
534
|
}
|
|
524
535
|
const usage = response.usage ?? {};
|
|
@@ -530,17 +541,21 @@ export async function runOracle(options, deps = {}) {
|
|
|
530
541
|
const cost = pricing
|
|
531
542
|
? estimateUsdCost({
|
|
532
543
|
usage: { inputTokens, outputTokens, reasoningTokens, totalTokens },
|
|
533
|
-
pricing: {
|
|
544
|
+
pricing: {
|
|
545
|
+
inputUsdPerToken: pricing.inputPerToken,
|
|
546
|
+
outputUsdPerToken: pricing.outputPerToken,
|
|
547
|
+
},
|
|
534
548
|
})?.totalUsd
|
|
535
549
|
: undefined;
|
|
536
550
|
const effortLabel = modelConfig.reasoning?.effort;
|
|
537
551
|
const modelLabel = effortLabel ? `${modelConfig.model}[${effortLabel}]` : modelConfig.model;
|
|
538
|
-
const sessionIdContainsModel = typeof options.sessionId ===
|
|
552
|
+
const sessionIdContainsModel = typeof options.sessionId === "string" &&
|
|
553
|
+
options.sessionId.toLowerCase().includes(modelConfig.model.toLowerCase());
|
|
539
554
|
const tokensDisplay = [inputTokens, outputTokens, reasoningTokens, totalTokens]
|
|
540
555
|
.map((value, index) => formatTokenValue(value, usage, index))
|
|
541
|
-
.join(
|
|
556
|
+
.join("/");
|
|
542
557
|
const tokensPart = (() => {
|
|
543
|
-
const parts = tokensDisplay.split(
|
|
558
|
+
const parts = tokensDisplay.split("/");
|
|
544
559
|
if (parts.length !== 4)
|
|
545
560
|
return tokensDisplay;
|
|
546
561
|
return `↑${parts[0]} ↓${parts[1]} ↻${parts[2]} Δ${parts[3]}`;
|
|
@@ -553,7 +568,11 @@ export async function runOracle(options, deps = {}) {
|
|
|
553
568
|
if (actualInput === undefined)
|
|
554
569
|
return null;
|
|
555
570
|
const delta = actualInput - estimatedInputTokens;
|
|
556
|
-
const deltaText = delta === 0
|
|
571
|
+
const deltaText = delta === 0
|
|
572
|
+
? ""
|
|
573
|
+
: delta > 0
|
|
574
|
+
? ` (+${delta.toLocaleString()})`
|
|
575
|
+
: ` (${delta.toLocaleString()})`;
|
|
557
576
|
return `est→actual=${estimatedInputTokens.toLocaleString()}→${actualInput.toLocaleString()}${deltaText}`;
|
|
558
577
|
})();
|
|
559
578
|
const { line1, line2 } = formatFinishLine({
|
|
@@ -564,43 +583,49 @@ export async function runOracle(options, deps = {}) {
|
|
|
564
583
|
summaryExtraParts: options.sessionId ? [`sid=${options.sessionId}`] : null,
|
|
565
584
|
detailParts: [
|
|
566
585
|
estActualPart,
|
|
567
|
-
!searchEnabled ?
|
|
586
|
+
!searchEnabled ? "search=off" : null,
|
|
568
587
|
files.length > 0 ? `files=${files.length}` : null,
|
|
569
588
|
],
|
|
570
589
|
});
|
|
571
590
|
if (!options.silent) {
|
|
572
|
-
log(
|
|
591
|
+
log("");
|
|
573
592
|
}
|
|
574
593
|
log(chalk.blue(line1));
|
|
575
594
|
if (line2) {
|
|
576
595
|
log(dim(line2));
|
|
577
596
|
}
|
|
578
597
|
return {
|
|
579
|
-
mode:
|
|
598
|
+
mode: "live",
|
|
580
599
|
response,
|
|
581
|
-
usage: {
|
|
600
|
+
usage: {
|
|
601
|
+
inputTokens,
|
|
602
|
+
outputTokens,
|
|
603
|
+
reasoningTokens,
|
|
604
|
+
totalTokens,
|
|
605
|
+
...(cost != null ? { cost } : {}),
|
|
606
|
+
},
|
|
582
607
|
elapsedMs,
|
|
583
608
|
};
|
|
584
609
|
}
|
|
585
610
|
export function extractTextOutput(response) {
|
|
586
611
|
if (Array.isArray(response.output_text) && response.output_text.length > 0) {
|
|
587
|
-
return response.output_text.join(
|
|
612
|
+
return response.output_text.join("\n");
|
|
588
613
|
}
|
|
589
614
|
if (Array.isArray(response.output)) {
|
|
590
615
|
const segments = [];
|
|
591
616
|
for (const item of response.output) {
|
|
592
617
|
if (Array.isArray(item.content)) {
|
|
593
618
|
for (const chunk of item.content) {
|
|
594
|
-
if (chunk && (chunk.type ===
|
|
619
|
+
if (chunk && (chunk.type === "output_text" || chunk.type === "text") && chunk.text) {
|
|
595
620
|
segments.push(chunk.text);
|
|
596
621
|
}
|
|
597
622
|
}
|
|
598
623
|
}
|
|
599
|
-
else if (typeof item.text ===
|
|
624
|
+
else if (typeof item.text === "string") {
|
|
600
625
|
segments.push(item.text);
|
|
601
626
|
}
|
|
602
627
|
}
|
|
603
|
-
return segments.join(
|
|
628
|
+
return segments.join("\n");
|
|
604
629
|
}
|
|
605
|
-
return
|
|
630
|
+
return "";
|
|
606
631
|
}
|
|
@@ -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();
|