@steipete/oracle 0.9.0 → 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 +61 -48
- package/dist/bin/oracle-cli.js +455 -402
- 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 +275 -117
- package/dist/src/browser/actions/navigation.js +161 -137
- 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 +390 -295
- 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 +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 +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 +62 -48
- 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 +2 -2
- 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 +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 +127 -106
- 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 +32 -28
- package/dist/src/cli/sessionCommand.js +31 -21
- package/dist/src/cli/sessionDisplay.js +95 -81
- package/dist/src/cli/sessionLineage.js +6 -2
- package/dist/src/cli/sessionRunner.js +103 -93
- 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/server.js +16 -12
- package/dist/src/mcp/tools/consult.js +51 -47
- 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 +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 +156 -134
- 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 +77 -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 +66 -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/types.js +0 -1
- 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/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,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,7 +190,7 @@ 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
|
|
@@ -194,7 +201,7 @@ export async function runOracle(options, deps = {}) {
|
|
|
194
201
|
const effectiveModelId = options.effectiveModelId ??
|
|
195
202
|
(azureDeploymentName
|
|
196
203
|
? azureDeploymentName
|
|
197
|
-
: options.model.startsWith(
|
|
204
|
+
: options.model.startsWith("gemini")
|
|
198
205
|
? resolveGeminiModelId(options.model)
|
|
199
206
|
: (modelConfig.apiModel ?? modelConfig.model));
|
|
200
207
|
if (!isPreview && options.previousResponseId) {
|
|
@@ -213,30 +220,31 @@ export async function runOracle(options, deps = {}) {
|
|
|
213
220
|
});
|
|
214
221
|
requestBody.model = effectiveModelId;
|
|
215
222
|
const estimatedInputTokens = estimateRequestTokens(requestBody, modelConfig);
|
|
216
|
-
const tokenLabel = formatTokenEstimate(estimatedInputTokens, (text) =>
|
|
223
|
+
const tokenLabel = formatTokenEstimate(estimatedInputTokens, (text) => richTty ? chalk.green(text) : text);
|
|
217
224
|
const fileLabel = richTty ? chalk.magenta(fileCount.toString()) : fileCount.toString();
|
|
218
|
-
const filesPhrase = fileCount === 0 ?
|
|
225
|
+
const filesPhrase = fileCount === 0 ? "no files" : `${fileLabel} files`;
|
|
219
226
|
const headerModelLabelBase = richTty ? chalk.cyan(modelConfig.model) : modelConfig.model;
|
|
220
227
|
const headerModelSuffix = effectiveModelId !== modelConfig.model
|
|
221
228
|
? richTty
|
|
222
229
|
? chalk.gray(` (API: ${effectiveModelId})`)
|
|
223
230
|
: ` (API: ${effectiveModelId})`
|
|
224
|
-
:
|
|
231
|
+
: "";
|
|
225
232
|
const headerLine = `Calling ${headerModelLabelBase}${headerModelSuffix} — ${tokenLabel} tokens, ${filesPhrase}.`;
|
|
226
|
-
const shouldReportFiles = (options.filesReport || fileTokenInfo.totalTokens > inputTokenBudget) &&
|
|
233
|
+
const shouldReportFiles = (options.filesReport || fileTokenInfo.totalTokens > inputTokenBudget) &&
|
|
234
|
+
fileTokenInfo.stats.length > 0;
|
|
227
235
|
if (!isPreview) {
|
|
228
236
|
if (!options.suppressHeader) {
|
|
229
237
|
log(headerLine);
|
|
230
238
|
}
|
|
231
239
|
const maskedKey = maskApiKey(apiKey);
|
|
232
240
|
if (maskedKey && options.verbose) {
|
|
233
|
-
const resolvedSuffix = effectiveModelId !== modelConfig.model ? ` (API: ${effectiveModelId})` :
|
|
241
|
+
const resolvedSuffix = effectiveModelId !== modelConfig.model ? ` (API: ${effectiveModelId})` : "";
|
|
234
242
|
log(dim(`Using ${envVar}=${maskedKey} for model ${modelConfig.model}${resolvedSuffix}`));
|
|
235
243
|
}
|
|
236
244
|
if (!options.suppressHeader &&
|
|
237
|
-
(modelConfig.model ===
|
|
238
|
-
effectiveModelId ===
|
|
239
|
-
log(dim(`Note: \`${modelConfig.model}\` is a stable CLI alias; OpenAI API uses \`gpt-5.
|
|
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\`.`));
|
|
240
248
|
}
|
|
241
249
|
if (baseUrl) {
|
|
242
250
|
log(dim(`Base URL: ${formatBaseUrlForLog(baseUrl)}`));
|
|
@@ -245,7 +253,7 @@ export async function runOracle(options, deps = {}) {
|
|
|
245
253
|
log(dim(`Resolved model: ${modelConfig.model} → ${effectiveModelId}`));
|
|
246
254
|
}
|
|
247
255
|
if (options.background && !supportsBackground) {
|
|
248
|
-
log(dim(
|
|
256
|
+
log(dim("Background runs are not supported for this model; streaming in foreground instead."));
|
|
249
257
|
}
|
|
250
258
|
if (!options.suppressTips) {
|
|
251
259
|
if (pendingNoFilesTip) {
|
|
@@ -256,10 +264,10 @@ export async function runOracle(options, deps = {}) {
|
|
|
256
264
|
}
|
|
257
265
|
}
|
|
258
266
|
if (isLongRunningModel) {
|
|
259
|
-
log(dim(
|
|
267
|
+
log(dim("This model can take up to 60 minutes (usually replies much faster)."));
|
|
260
268
|
}
|
|
261
269
|
if (options.verbose || isLongRunningModel) {
|
|
262
|
-
log(dim(
|
|
270
|
+
log(dim("Press Ctrl+C to cancel."));
|
|
263
271
|
}
|
|
264
272
|
}
|
|
265
273
|
if (shouldReportFiles) {
|
|
@@ -270,19 +278,19 @@ export async function runOracle(options, deps = {}) {
|
|
|
270
278
|
}
|
|
271
279
|
logVerbose(`Estimated tokens (request body): ${estimatedInputTokens.toLocaleString()}`);
|
|
272
280
|
if (isPreview && previewMode) {
|
|
273
|
-
if (previewMode ===
|
|
274
|
-
log(
|
|
281
|
+
if (previewMode === "json" || previewMode === "full") {
|
|
282
|
+
log("Request JSON");
|
|
275
283
|
log(JSON.stringify(requestBody, null, 2));
|
|
276
|
-
log(
|
|
284
|
+
log("");
|
|
277
285
|
}
|
|
278
|
-
if (previewMode ===
|
|
279
|
-
log(
|
|
286
|
+
if (previewMode === "full") {
|
|
287
|
+
log("Assembled Prompt");
|
|
280
288
|
log(promptWithFiles);
|
|
281
|
-
log(
|
|
289
|
+
log("");
|
|
282
290
|
}
|
|
283
291
|
log(`Estimated input tokens: ${estimatedInputTokens.toLocaleString()} / ${inputTokenBudget.toLocaleString()} (model: ${modelConfig.model})`);
|
|
284
292
|
return {
|
|
285
|
-
mode:
|
|
293
|
+
mode: "preview",
|
|
286
294
|
previewMode,
|
|
287
295
|
requestBody,
|
|
288
296
|
estimatedInputTokens,
|
|
@@ -290,31 +298,31 @@ export async function runOracle(options, deps = {}) {
|
|
|
290
298
|
};
|
|
291
299
|
}
|
|
292
300
|
const proxyCompatibleBaseUrl = baseUrl && (isOpenRouterBaseUrl(baseUrl) || isCustomBaseUrl(baseUrl)) ? baseUrl : undefined;
|
|
293
|
-
const apiEndpoint = modelConfig.model.startsWith(
|
|
301
|
+
const apiEndpoint = modelConfig.model.startsWith("gemini")
|
|
294
302
|
? proxyCompatibleBaseUrl
|
|
295
303
|
: proxyCompatibleBaseUrl
|
|
296
304
|
? proxyCompatibleBaseUrl
|
|
297
|
-
: modelConfig.model.startsWith(
|
|
298
|
-
? process.env.ANTHROPIC_BASE_URL ?? baseUrl
|
|
305
|
+
: modelConfig.model.startsWith("claude")
|
|
306
|
+
? (process.env.ANTHROPIC_BASE_URL ?? baseUrl)
|
|
299
307
|
: baseUrl;
|
|
300
308
|
const clientInstance = client ??
|
|
301
309
|
clientFactory(apiKey, {
|
|
302
310
|
baseUrl: apiEndpoint,
|
|
303
311
|
azure: options.azure,
|
|
304
312
|
model: options.model,
|
|
305
|
-
resolvedModelId: modelConfig.model.startsWith(
|
|
313
|
+
resolvedModelId: modelConfig.model.startsWith("claude")
|
|
306
314
|
? resolveClaudeModelId(effectiveModelId)
|
|
307
|
-
: modelConfig.model.startsWith(
|
|
315
|
+
: modelConfig.model.startsWith("gemini")
|
|
308
316
|
? resolveGeminiModelId(effectiveModelId)
|
|
309
317
|
: effectiveModelId,
|
|
310
318
|
httpTimeoutMs: options.httpTimeoutMs,
|
|
311
319
|
});
|
|
312
|
-
logVerbose(
|
|
320
|
+
logVerbose("Dispatching request to API...");
|
|
313
321
|
if (options.verbose) {
|
|
314
|
-
log(
|
|
322
|
+
log(""); // ensure verbose section is separated from Answer stream
|
|
315
323
|
}
|
|
316
324
|
const stopOscProgress = startOscProgress({
|
|
317
|
-
label: useBackground ?
|
|
325
|
+
label: useBackground ? "Waiting for API (background)" : "Waiting for API",
|
|
318
326
|
targetMs: useBackground ? timeoutMs : Math.min(timeoutMs, 10 * 60_000),
|
|
319
327
|
indeterminate: true,
|
|
320
328
|
write: sinkWrite,
|
|
@@ -328,16 +336,16 @@ export async function runOracle(options, deps = {}) {
|
|
|
328
336
|
const timeoutExceeded = () => now() - runStart >= timeoutMs;
|
|
329
337
|
const throwIfTimedOut = () => {
|
|
330
338
|
if (timeoutExceeded()) {
|
|
331
|
-
throw new OracleTransportError(
|
|
339
|
+
throw new OracleTransportError("client-timeout", `Timed out waiting for API response after ${formatElapsed(timeoutMs)}.`);
|
|
332
340
|
}
|
|
333
341
|
};
|
|
334
342
|
const ensureAnswerHeader = () => {
|
|
335
343
|
if (options.silent || answerHeaderPrinted)
|
|
336
344
|
return;
|
|
337
345
|
// Always add a separating newline for readability; optionally include the label depending on caller needs.
|
|
338
|
-
log(
|
|
346
|
+
log("");
|
|
339
347
|
if (allowAnswerHeader) {
|
|
340
|
-
log(chalk.bold(
|
|
348
|
+
log(chalk.bold("Answer:"));
|
|
341
349
|
}
|
|
342
350
|
answerHeaderPrinted = true;
|
|
343
351
|
};
|
|
@@ -405,20 +413,20 @@ export async function runOracle(options, deps = {}) {
|
|
|
405
413
|
isTty && !renderPlain
|
|
406
414
|
? createMarkdownStreamer({
|
|
407
415
|
render: renderMarkdownAnsi,
|
|
408
|
-
spacing:
|
|
409
|
-
mode:
|
|
416
|
+
spacing: "single",
|
|
417
|
+
mode: "hybrid",
|
|
410
418
|
})
|
|
411
419
|
: null;
|
|
412
420
|
for await (const event of stream) {
|
|
413
421
|
throwIfTimedOut();
|
|
414
|
-
const isTextDelta = event.type ===
|
|
422
|
+
const isTextDelta = event.type === "chunk" || event.type === "response.output_text.delta";
|
|
415
423
|
if (!isTextDelta)
|
|
416
424
|
continue;
|
|
417
425
|
stopOscProgress();
|
|
418
426
|
stopHeartbeatNow();
|
|
419
427
|
sawTextDelta = true;
|
|
420
428
|
ensureAnswerHeader();
|
|
421
|
-
if (options.silent || typeof event.delta !==
|
|
429
|
+
if (options.silent || typeof event.delta !== "string")
|
|
422
430
|
continue;
|
|
423
431
|
// Always keep the log/bookkeeping sink up to date.
|
|
424
432
|
sinkWrite(event.delta);
|
|
@@ -458,41 +466,41 @@ export async function runOracle(options, deps = {}) {
|
|
|
458
466
|
stopOscProgress();
|
|
459
467
|
}
|
|
460
468
|
if (!response) {
|
|
461
|
-
throw new Error(
|
|
469
|
+
throw new Error("API did not return a response.");
|
|
462
470
|
}
|
|
463
471
|
// We only add spacing when streamed text was printed.
|
|
464
472
|
if (sawTextDelta && !options.silent) {
|
|
465
473
|
if (renderPlain) {
|
|
466
474
|
// Plain streaming already wrote chunks; ensure clean separation.
|
|
467
|
-
stdoutWrite(
|
|
475
|
+
stdoutWrite("\n");
|
|
468
476
|
}
|
|
469
477
|
else {
|
|
470
478
|
// Separate streamed output from logs.
|
|
471
|
-
log(
|
|
479
|
+
log("");
|
|
472
480
|
}
|
|
473
481
|
}
|
|
474
|
-
logVerbose(`Response status: ${response.status ??
|
|
475
|
-
if (response.status && response.status !==
|
|
482
|
+
logVerbose(`Response status: ${response.status ?? "completed"}`);
|
|
483
|
+
if (response.status && response.status !== "completed") {
|
|
476
484
|
// API can reply `in_progress` even after the stream closes; give it a brief grace poll.
|
|
477
|
-
if (response.id && response.status ===
|
|
485
|
+
if (response.id && response.status === "in_progress") {
|
|
478
486
|
const polishingStart = now();
|
|
479
487
|
const pollIntervalMs = 2_000;
|
|
480
488
|
const maxWaitMs = 180_000;
|
|
481
|
-
log(chalk.dim(
|
|
489
|
+
log(chalk.dim("Response still in_progress; polling until completion..."));
|
|
482
490
|
// Short polling loop — we don't want to hang forever, just catch late finalization.
|
|
483
491
|
while (now() - polishingStart < maxWaitMs) {
|
|
484
492
|
throwIfTimedOut();
|
|
485
493
|
await wait(pollIntervalMs);
|
|
486
494
|
const refreshed = await clientInstance.responses.retrieve(response.id);
|
|
487
|
-
if (refreshed.status ===
|
|
495
|
+
if (refreshed.status === "completed") {
|
|
488
496
|
response = refreshed;
|
|
489
497
|
break;
|
|
490
498
|
}
|
|
491
499
|
}
|
|
492
500
|
}
|
|
493
|
-
if (response.status !==
|
|
501
|
+
if (response.status !== "completed") {
|
|
494
502
|
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}` :
|
|
503
|
+
log(chalk.yellow(`API ended the run early (status=${response.status}${response.incomplete_details?.reason ? `, reason=${response.incomplete_details.reason}` : ""}).`));
|
|
496
504
|
throw new OracleResponseError(`Response did not complete: ${detail}`, response);
|
|
497
505
|
}
|
|
498
506
|
}
|
|
@@ -509,16 +517,16 @@ export async function runOracle(options, deps = {}) {
|
|
|
509
517
|
? renderPlain || !richTty
|
|
510
518
|
? answerText
|
|
511
519
|
: renderMarkdownAnsi(answerText)
|
|
512
|
-
: chalk.dim(
|
|
520
|
+
: chalk.dim("(no text output)");
|
|
513
521
|
sinkWrite(printable);
|
|
514
|
-
if (!printable.endsWith(
|
|
515
|
-
sinkWrite(
|
|
522
|
+
if (!printable.endsWith("\n")) {
|
|
523
|
+
sinkWrite("\n");
|
|
516
524
|
}
|
|
517
525
|
stdoutWrite(printable);
|
|
518
|
-
if (!printable.endsWith(
|
|
519
|
-
stdoutWrite(
|
|
526
|
+
if (!printable.endsWith("\n")) {
|
|
527
|
+
stdoutWrite("\n");
|
|
520
528
|
}
|
|
521
|
-
log(
|
|
529
|
+
log("");
|
|
522
530
|
}
|
|
523
531
|
}
|
|
524
532
|
const usage = response.usage ?? {};
|
|
@@ -530,17 +538,21 @@ export async function runOracle(options, deps = {}) {
|
|
|
530
538
|
const cost = pricing
|
|
531
539
|
? estimateUsdCost({
|
|
532
540
|
usage: { inputTokens, outputTokens, reasoningTokens, totalTokens },
|
|
533
|
-
pricing: {
|
|
541
|
+
pricing: {
|
|
542
|
+
inputUsdPerToken: pricing.inputPerToken,
|
|
543
|
+
outputUsdPerToken: pricing.outputPerToken,
|
|
544
|
+
},
|
|
534
545
|
})?.totalUsd
|
|
535
546
|
: undefined;
|
|
536
547
|
const effortLabel = modelConfig.reasoning?.effort;
|
|
537
548
|
const modelLabel = effortLabel ? `${modelConfig.model}[${effortLabel}]` : modelConfig.model;
|
|
538
|
-
const sessionIdContainsModel = typeof options.sessionId ===
|
|
549
|
+
const sessionIdContainsModel = typeof options.sessionId === "string" &&
|
|
550
|
+
options.sessionId.toLowerCase().includes(modelConfig.model.toLowerCase());
|
|
539
551
|
const tokensDisplay = [inputTokens, outputTokens, reasoningTokens, totalTokens]
|
|
540
552
|
.map((value, index) => formatTokenValue(value, usage, index))
|
|
541
|
-
.join(
|
|
553
|
+
.join("/");
|
|
542
554
|
const tokensPart = (() => {
|
|
543
|
-
const parts = tokensDisplay.split(
|
|
555
|
+
const parts = tokensDisplay.split("/");
|
|
544
556
|
if (parts.length !== 4)
|
|
545
557
|
return tokensDisplay;
|
|
546
558
|
return `↑${parts[0]} ↓${parts[1]} ↻${parts[2]} Δ${parts[3]}`;
|
|
@@ -553,7 +565,11 @@ export async function runOracle(options, deps = {}) {
|
|
|
553
565
|
if (actualInput === undefined)
|
|
554
566
|
return null;
|
|
555
567
|
const delta = actualInput - estimatedInputTokens;
|
|
556
|
-
const deltaText = delta === 0
|
|
568
|
+
const deltaText = delta === 0
|
|
569
|
+
? ""
|
|
570
|
+
: delta > 0
|
|
571
|
+
? ` (+${delta.toLocaleString()})`
|
|
572
|
+
: ` (${delta.toLocaleString()})`;
|
|
557
573
|
return `est→actual=${estimatedInputTokens.toLocaleString()}→${actualInput.toLocaleString()}${deltaText}`;
|
|
558
574
|
})();
|
|
559
575
|
const { line1, line2 } = formatFinishLine({
|
|
@@ -564,43 +580,49 @@ export async function runOracle(options, deps = {}) {
|
|
|
564
580
|
summaryExtraParts: options.sessionId ? [`sid=${options.sessionId}`] : null,
|
|
565
581
|
detailParts: [
|
|
566
582
|
estActualPart,
|
|
567
|
-
!searchEnabled ?
|
|
583
|
+
!searchEnabled ? "search=off" : null,
|
|
568
584
|
files.length > 0 ? `files=${files.length}` : null,
|
|
569
585
|
],
|
|
570
586
|
});
|
|
571
587
|
if (!options.silent) {
|
|
572
|
-
log(
|
|
588
|
+
log("");
|
|
573
589
|
}
|
|
574
590
|
log(chalk.blue(line1));
|
|
575
591
|
if (line2) {
|
|
576
592
|
log(dim(line2));
|
|
577
593
|
}
|
|
578
594
|
return {
|
|
579
|
-
mode:
|
|
595
|
+
mode: "live",
|
|
580
596
|
response,
|
|
581
|
-
usage: {
|
|
597
|
+
usage: {
|
|
598
|
+
inputTokens,
|
|
599
|
+
outputTokens,
|
|
600
|
+
reasoningTokens,
|
|
601
|
+
totalTokens,
|
|
602
|
+
...(cost != null ? { cost } : {}),
|
|
603
|
+
},
|
|
582
604
|
elapsedMs,
|
|
583
605
|
};
|
|
584
606
|
}
|
|
585
607
|
export function extractTextOutput(response) {
|
|
586
608
|
if (Array.isArray(response.output_text) && response.output_text.length > 0) {
|
|
587
|
-
return response.output_text.join(
|
|
609
|
+
return response.output_text.join("\n");
|
|
588
610
|
}
|
|
589
611
|
if (Array.isArray(response.output)) {
|
|
590
612
|
const segments = [];
|
|
591
613
|
for (const item of response.output) {
|
|
592
614
|
if (Array.isArray(item.content)) {
|
|
593
615
|
for (const chunk of item.content) {
|
|
594
|
-
if (chunk && (chunk.type ===
|
|
616
|
+
if (chunk && (chunk.type === "output_text" || chunk.type === "text") && chunk.text) {
|
|
595
617
|
segments.push(chunk.text);
|
|
596
618
|
}
|
|
597
619
|
}
|
|
598
620
|
}
|
|
599
|
-
else if (typeof item.text ===
|
|
621
|
+
else if (typeof item.text === "string") {
|
|
600
622
|
segments.push(item.text);
|
|
601
623
|
}
|
|
602
624
|
}
|
|
603
|
-
return segments.join(
|
|
625
|
+
return segments.join("\n");
|
|
604
626
|
}
|
|
605
|
-
return
|
|
627
|
+
return "";
|
|
606
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();
|