@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
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
const DEFAULT_CLAUDE_ENDPOINT =
|
|
2
|
-
const ANTHROPIC_VERSION =
|
|
1
|
+
const DEFAULT_CLAUDE_ENDPOINT = "https://api.anthropic.com/v1/messages";
|
|
2
|
+
const ANTHROPIC_VERSION = "2023-06-01";
|
|
3
3
|
function extractPrompt(body) {
|
|
4
4
|
const first = body.input?.[0]?.content?.[0];
|
|
5
|
-
if (first && first.type ===
|
|
6
|
-
return first.text ??
|
|
5
|
+
if (first && first.type === "input_text") {
|
|
6
|
+
return first.text ?? "";
|
|
7
7
|
}
|
|
8
|
-
return
|
|
8
|
+
return "";
|
|
9
9
|
}
|
|
10
10
|
async function callClaude({ apiKey, model, prompt, endpoint, stream = false, }) {
|
|
11
11
|
const url = endpoint?.trim() || DEFAULT_CLAUDE_ENDPOINT;
|
|
@@ -14,34 +14,45 @@ async function callClaude({ apiKey, model, prompt, endpoint, stream = false, })
|
|
|
14
14
|
max_tokens: 2048,
|
|
15
15
|
messages: [
|
|
16
16
|
{
|
|
17
|
-
role:
|
|
17
|
+
role: "user",
|
|
18
18
|
content: prompt,
|
|
19
19
|
},
|
|
20
20
|
],
|
|
21
21
|
stream,
|
|
22
22
|
};
|
|
23
23
|
return fetch(url, {
|
|
24
|
-
method:
|
|
24
|
+
method: "POST",
|
|
25
25
|
headers: {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
"content-type": "application/json",
|
|
27
|
+
"x-api-key": apiKey,
|
|
28
|
+
"anthropic-version": ANTHROPIC_VERSION,
|
|
29
29
|
},
|
|
30
30
|
body: JSON.stringify(payload),
|
|
31
31
|
});
|
|
32
32
|
}
|
|
33
33
|
async function parseClaudeResponse(raw) {
|
|
34
|
-
const
|
|
34
|
+
const body = await raw.text();
|
|
35
|
+
if (!body.trim()) {
|
|
36
|
+
throw new Error(`Claude request failed (${raw.status} ${raw.statusText || "unknown status"}): empty response`);
|
|
37
|
+
}
|
|
38
|
+
let json;
|
|
39
|
+
try {
|
|
40
|
+
json = JSON.parse(body);
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
const snippet = body.slice(0, 160).replace(/\s+/g, " ").trim();
|
|
44
|
+
throw new Error(`Claude request failed (${raw.status} ${raw.statusText || "unknown status"}): invalid JSON response${snippet ? `: ${snippet}` : ""}`, { cause: error });
|
|
45
|
+
}
|
|
35
46
|
if (json.error) {
|
|
36
|
-
throw new Error(json.error.message ||
|
|
47
|
+
throw new Error(json.error.message || "Claude request failed");
|
|
37
48
|
}
|
|
38
|
-
const textParts = json.content?.map((part) => part.text ??
|
|
39
|
-
const outputText = textParts.join(
|
|
49
|
+
const textParts = json.content?.map((part) => part.text ?? "").filter(Boolean) ?? [];
|
|
50
|
+
const outputText = textParts.join("");
|
|
40
51
|
return {
|
|
41
52
|
id: json.id ?? `claude-${Date.now()}`,
|
|
42
|
-
status:
|
|
53
|
+
status: "completed",
|
|
43
54
|
output_text: [outputText],
|
|
44
|
-
output: [{ type:
|
|
55
|
+
output: [{ type: "text", text: outputText }],
|
|
45
56
|
usage: {
|
|
46
57
|
input_tokens: json.usage?.input_tokens ?? 0,
|
|
47
58
|
output_tokens: json.usage?.output_tokens ?? 0,
|
|
@@ -53,11 +64,17 @@ export function createClaudeClient(apiKey, modelName, resolvedModelId, baseUrl)
|
|
|
53
64
|
const modelId = resolveClaudeModelId(resolvedModelId ?? modelName);
|
|
54
65
|
const stream = async (body) => {
|
|
55
66
|
const prompt = extractPrompt(body);
|
|
56
|
-
const resp = await callClaude({
|
|
67
|
+
const resp = await callClaude({
|
|
68
|
+
apiKey,
|
|
69
|
+
model: modelId,
|
|
70
|
+
prompt,
|
|
71
|
+
stream: false,
|
|
72
|
+
endpoint: baseUrl,
|
|
73
|
+
});
|
|
57
74
|
const parsed = await parseClaudeResponse(resp);
|
|
58
75
|
const iterator = async function* () {
|
|
59
76
|
if (parsed.output_text?.[0]) {
|
|
60
|
-
yield { type:
|
|
77
|
+
yield { type: "response.output_text.delta", delta: parsed.output_text[0] };
|
|
61
78
|
}
|
|
62
79
|
return;
|
|
63
80
|
};
|
|
@@ -68,13 +85,19 @@ export function createClaudeClient(apiKey, modelName, resolvedModelId, baseUrl)
|
|
|
68
85
|
};
|
|
69
86
|
const create = async (body) => {
|
|
70
87
|
const prompt = extractPrompt(body);
|
|
71
|
-
const resp = await callClaude({
|
|
88
|
+
const resp = await callClaude({
|
|
89
|
+
apiKey,
|
|
90
|
+
model: modelId,
|
|
91
|
+
prompt,
|
|
92
|
+
stream: false,
|
|
93
|
+
endpoint: baseUrl,
|
|
94
|
+
});
|
|
72
95
|
return parseClaudeResponse(resp);
|
|
73
96
|
};
|
|
74
97
|
const retrieve = async (id) => ({
|
|
75
98
|
id,
|
|
76
|
-
status:
|
|
77
|
-
error: { message:
|
|
99
|
+
status: "error",
|
|
100
|
+
error: { message: "Retrieve by ID not supported for Claude API yet." },
|
|
78
101
|
});
|
|
79
102
|
return {
|
|
80
103
|
responses: {
|
|
@@ -85,11 +108,16 @@ export function createClaudeClient(apiKey, modelName, resolvedModelId, baseUrl)
|
|
|
85
108
|
};
|
|
86
109
|
}
|
|
87
110
|
export function resolveClaudeModelId(modelName) {
|
|
88
|
-
if (modelName ===
|
|
89
|
-
return
|
|
111
|
+
if (modelName === "claude-4.6-sonnet" || modelName === "claude-sonnet-4-6") {
|
|
112
|
+
return "claude-sonnet-4-6";
|
|
113
|
+
}
|
|
114
|
+
if (modelName === "claude-4.5-sonnet" ||
|
|
115
|
+
modelName === "claude-sonnet-4-5" ||
|
|
116
|
+
modelName === "claude-sonnet-4-5-20250929") {
|
|
117
|
+
return "claude-sonnet-4-5";
|
|
90
118
|
}
|
|
91
|
-
if (modelName ===
|
|
92
|
-
return
|
|
119
|
+
if (modelName === "claude-4.1-opus" || modelName === "claude-opus-4-1-20240808") {
|
|
120
|
+
return "claude-opus-4-1";
|
|
93
121
|
}
|
|
94
122
|
return modelName;
|
|
95
123
|
}
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import OpenAI from
|
|
2
|
-
import path from
|
|
3
|
-
import { createRequire } from
|
|
4
|
-
import { createGeminiClient } from
|
|
5
|
-
import { createClaudeClient } from
|
|
6
|
-
import { isOpenRouterBaseUrl } from
|
|
1
|
+
import OpenAI from "openai";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
import { createGeminiClient } from "./gemini.js";
|
|
5
|
+
import { createClaudeClient } from "./claude.js";
|
|
6
|
+
import { isOpenRouterBaseUrl } from "./modelResolver.js";
|
|
7
7
|
/**
|
|
8
8
|
* Known native API base URLs that should still use their dedicated SDKs.
|
|
9
9
|
* Any other custom base URL is treated as an OpenAI-compatible proxy and
|
|
10
10
|
* all models are routed through the chat/completions adapter.
|
|
11
11
|
*/
|
|
12
12
|
const NATIVE_API_HOSTS = [
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
"api.openai.com",
|
|
14
|
+
"api.anthropic.com",
|
|
15
|
+
"generativelanguage.googleapis.com",
|
|
16
|
+
"api.x.ai",
|
|
17
17
|
];
|
|
18
18
|
export function isCustomBaseUrl(baseUrl) {
|
|
19
19
|
if (!baseUrl)
|
|
@@ -27,7 +27,7 @@ export function isCustomBaseUrl(baseUrl) {
|
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
export function buildAzureResponsesBaseUrl(endpoint) {
|
|
30
|
-
return `${endpoint.replace(/\/+$/,
|
|
30
|
+
return `${endpoint.replace(/\/+$/, "")}/openai/v1`;
|
|
31
31
|
}
|
|
32
32
|
export function createDefaultClientFactory() {
|
|
33
33
|
const customFactory = loadCustomClientFactory();
|
|
@@ -40,17 +40,21 @@ export function createDefaultClientFactory() {
|
|
|
40
40
|
// route ALL models through the OpenAI chat/completions adapter instead of native SDKs
|
|
41
41
|
// which would reject the proxy's API key.
|
|
42
42
|
if (!openRouter && !customProxy) {
|
|
43
|
-
if (options?.model?.startsWith(
|
|
43
|
+
if (options?.model?.startsWith("gemini")) {
|
|
44
44
|
// Gemini client uses its own SDK; allow passing the already-resolved id for transparency/logging.
|
|
45
45
|
return createGeminiClient(key, options.model, options.resolvedModelId);
|
|
46
46
|
}
|
|
47
|
-
if (options?.model?.startsWith(
|
|
47
|
+
if (options?.model?.startsWith("claude")) {
|
|
48
48
|
return createClaudeClient(key, options.model, options.resolvedModelId, options.baseUrl);
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
let instance;
|
|
52
|
-
const defaultHeaders = openRouter
|
|
53
|
-
|
|
52
|
+
const defaultHeaders = openRouter
|
|
53
|
+
? buildOpenRouterHeaders()
|
|
54
|
+
: undefined;
|
|
55
|
+
const httpTimeoutMs = typeof options?.httpTimeoutMs === "number" &&
|
|
56
|
+
Number.isFinite(options.httpTimeoutMs) &&
|
|
57
|
+
options.httpTimeoutMs > 0
|
|
54
58
|
? options.httpTimeoutMs
|
|
55
59
|
: 20 * 60 * 1000;
|
|
56
60
|
if (options?.azure?.endpoint) {
|
|
@@ -82,13 +86,15 @@ export function createDefaultClientFactory() {
|
|
|
82
86
|
}
|
|
83
87
|
function buildOpenRouterHeaders() {
|
|
84
88
|
const headers = {};
|
|
85
|
-
const referer = process.env.OPENROUTER_REFERER ??
|
|
86
|
-
|
|
89
|
+
const referer = process.env.OPENROUTER_REFERER ??
|
|
90
|
+
process.env.OPENROUTER_HTTP_REFERER ??
|
|
91
|
+
"https://github.com/steipete/oracle";
|
|
92
|
+
const title = process.env.OPENROUTER_TITLE ?? "Oracle CLI";
|
|
87
93
|
if (referer) {
|
|
88
|
-
headers[
|
|
94
|
+
headers["HTTP-Referer"] = referer;
|
|
89
95
|
}
|
|
90
96
|
if (title) {
|
|
91
|
-
headers[
|
|
97
|
+
headers["X-Title"] = title;
|
|
92
98
|
}
|
|
93
99
|
return headers;
|
|
94
100
|
}
|
|
@@ -97,19 +103,19 @@ function loadCustomClientFactory() {
|
|
|
97
103
|
if (!override) {
|
|
98
104
|
return null;
|
|
99
105
|
}
|
|
100
|
-
if (override ===
|
|
106
|
+
if (override === "INLINE_TEST_FACTORY") {
|
|
101
107
|
return () => ({
|
|
102
108
|
responses: {
|
|
103
|
-
create: async () => ({ id:
|
|
109
|
+
create: async () => ({ id: "inline-test", status: "completed" }),
|
|
104
110
|
stream: async () => ({
|
|
105
111
|
[Symbol.asyncIterator]: () => ({
|
|
106
112
|
async next() {
|
|
107
113
|
return { done: true, value: undefined };
|
|
108
114
|
},
|
|
109
115
|
}),
|
|
110
|
-
finalResponse: async () => ({ id:
|
|
116
|
+
finalResponse: async () => ({ id: "inline-test", status: "completed" }),
|
|
111
117
|
}),
|
|
112
|
-
retrieve: async (id) => ({ id, status:
|
|
118
|
+
retrieve: async (id) => ({ id, status: "completed" }),
|
|
113
119
|
},
|
|
114
120
|
});
|
|
115
121
|
}
|
|
@@ -117,14 +123,14 @@ function loadCustomClientFactory() {
|
|
|
117
123
|
const require = createRequire(import.meta.url);
|
|
118
124
|
const resolved = path.isAbsolute(override) ? override : path.resolve(process.cwd(), override);
|
|
119
125
|
const moduleExports = require(resolved);
|
|
120
|
-
const factory = typeof moduleExports ===
|
|
126
|
+
const factory = typeof moduleExports === "function"
|
|
121
127
|
? moduleExports
|
|
122
|
-
: typeof moduleExports?.default ===
|
|
128
|
+
: typeof moduleExports?.default === "function"
|
|
123
129
|
? moduleExports.default
|
|
124
|
-
: typeof moduleExports?.createClientFactory ===
|
|
130
|
+
: typeof moduleExports?.createClientFactory === "function"
|
|
125
131
|
? moduleExports.createClientFactory
|
|
126
132
|
: null;
|
|
127
|
-
if (typeof factory ===
|
|
133
|
+
if (typeof factory === "function") {
|
|
128
134
|
return factory;
|
|
129
135
|
}
|
|
130
136
|
console.warn(`Custom client factory at ${resolved} did not export a function.`);
|
|
@@ -140,14 +146,17 @@ function buildOpenRouterCompletionClient(instance) {
|
|
|
140
146
|
const adaptRequest = (body) => {
|
|
141
147
|
const messages = [];
|
|
142
148
|
if (body.instructions) {
|
|
143
|
-
messages.push({ role:
|
|
149
|
+
messages.push({ role: "system", content: body.instructions });
|
|
144
150
|
}
|
|
145
151
|
for (const entry of body.input) {
|
|
146
152
|
const textParts = entry.content
|
|
147
|
-
.map((c) => (c.type ===
|
|
153
|
+
.map((c) => (c.type === "input_text" ? c.text : ""))
|
|
148
154
|
.filter((t) => t)
|
|
149
|
-
.join(
|
|
150
|
-
messages.push({
|
|
155
|
+
.join("\n\n");
|
|
156
|
+
messages.push({
|
|
157
|
+
role: entry.role ?? "user",
|
|
158
|
+
content: textParts,
|
|
159
|
+
});
|
|
151
160
|
}
|
|
152
161
|
const base = {
|
|
153
162
|
model: body.model,
|
|
@@ -159,7 +168,7 @@ function buildOpenRouterCompletionClient(instance) {
|
|
|
159
168
|
return { streaming, nonStreaming };
|
|
160
169
|
};
|
|
161
170
|
const adaptResponse = (response) => {
|
|
162
|
-
const text = response.choices?.[0]?.message?.content ??
|
|
171
|
+
const text = response.choices?.[0]?.message?.content ?? "";
|
|
163
172
|
const usage = {
|
|
164
173
|
input_tokens: response.usage?.prompt_tokens ?? 0,
|
|
165
174
|
output_tokens: response.usage?.completion_tokens ?? 0,
|
|
@@ -167,9 +176,9 @@ function buildOpenRouterCompletionClient(instance) {
|
|
|
167
176
|
};
|
|
168
177
|
return {
|
|
169
178
|
id: response.id ?? `openrouter-${Date.now()}`,
|
|
170
|
-
status:
|
|
179
|
+
status: "completed",
|
|
171
180
|
output_text: [text],
|
|
172
|
-
output: [{ type:
|
|
181
|
+
output: [{ type: "text", text }],
|
|
173
182
|
usage,
|
|
174
183
|
};
|
|
175
184
|
};
|
|
@@ -177,15 +186,15 @@ function buildOpenRouterCompletionClient(instance) {
|
|
|
177
186
|
const { streaming } = adaptRequest(body);
|
|
178
187
|
let finalUsage;
|
|
179
188
|
let finalId;
|
|
180
|
-
let aggregated =
|
|
189
|
+
let aggregated = "";
|
|
181
190
|
async function* iterator() {
|
|
182
191
|
const completion = await instance.chat.completions.create(streaming);
|
|
183
192
|
for await (const chunk of completion) {
|
|
184
193
|
finalId = chunk.id ?? finalId;
|
|
185
|
-
const delta = chunk.choices?.[0]?.delta?.content ??
|
|
194
|
+
const delta = chunk.choices?.[0]?.delta?.content ?? "";
|
|
186
195
|
if (delta) {
|
|
187
196
|
aggregated += delta;
|
|
188
|
-
yield { type:
|
|
197
|
+
yield { type: "chunk", delta };
|
|
189
198
|
}
|
|
190
199
|
if (chunk.usage) {
|
|
191
200
|
finalUsage = chunk.usage;
|
|
@@ -200,11 +209,11 @@ function buildOpenRouterCompletionClient(instance) {
|
|
|
200
209
|
async finalResponse() {
|
|
201
210
|
return adaptResponse({
|
|
202
211
|
id: finalId ?? `openrouter-${Date.now()}`,
|
|
203
|
-
choices: [{ message: { role:
|
|
212
|
+
choices: [{ message: { role: "assistant", content: aggregated } }],
|
|
204
213
|
usage: finalUsage ?? { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 },
|
|
205
214
|
created: Math.floor(Date.now() / 1000),
|
|
206
|
-
model:
|
|
207
|
-
object:
|
|
215
|
+
model: "",
|
|
216
|
+
object: "chat.completion",
|
|
208
217
|
});
|
|
209
218
|
},
|
|
210
219
|
};
|
|
@@ -219,7 +228,7 @@ function buildOpenRouterCompletionClient(instance) {
|
|
|
219
228
|
stream,
|
|
220
229
|
create,
|
|
221
230
|
retrieve: async () => {
|
|
222
|
-
throw new Error(
|
|
231
|
+
throw new Error("retrieve is not supported for OpenRouter chat/completions fallback.");
|
|
223
232
|
},
|
|
224
233
|
},
|
|
225
234
|
};
|
|
@@ -1,15 +1,45 @@
|
|
|
1
|
-
import { countTokens as countTokensGpt5 } from
|
|
2
|
-
import { countTokens as countTokensGpt5Pro } from
|
|
3
|
-
import { countTokens as countTokensAnthropicRaw } from
|
|
4
|
-
import { stringifyTokenizerInput } from
|
|
5
|
-
export const DEFAULT_MODEL =
|
|
6
|
-
export const PRO_MODELS = new Set([
|
|
1
|
+
import { countTokens as countTokensGpt5 } from "gpt-tokenizer/model/gpt-5";
|
|
2
|
+
import { countTokens as countTokensGpt5Pro } from "gpt-tokenizer/model/gpt-5-pro";
|
|
3
|
+
import { countTokens as countTokensAnthropicRaw } from "@anthropic-ai/tokenizer";
|
|
4
|
+
import { stringifyTokenizerInput } from "./tokenStringifier.js";
|
|
5
|
+
export const DEFAULT_MODEL = "gpt-5.5-pro";
|
|
6
|
+
export const PRO_MODELS = new Set([
|
|
7
|
+
"gpt-5.5-pro",
|
|
8
|
+
"gpt-5.4-pro",
|
|
9
|
+
"gpt-5.1-pro",
|
|
10
|
+
"gpt-5-pro",
|
|
11
|
+
"gpt-5.2-pro",
|
|
12
|
+
"claude-4.6-sonnet",
|
|
13
|
+
"claude-4.1-opus",
|
|
14
|
+
]);
|
|
7
15
|
const countTokensAnthropic = (input) => countTokensAnthropicRaw(stringifyTokenizerInput(input));
|
|
8
16
|
export const MODEL_CONFIGS = {
|
|
9
|
-
|
|
10
|
-
model:
|
|
11
|
-
|
|
12
|
-
|
|
17
|
+
"gpt-5.5-pro": {
|
|
18
|
+
model: "gpt-5.5-pro",
|
|
19
|
+
provider: "openai",
|
|
20
|
+
tokenizer: countTokensGpt5Pro,
|
|
21
|
+
inputLimit: 1_050_000,
|
|
22
|
+
pricing: {
|
|
23
|
+
inputPerToken: 30 / 1_000_000,
|
|
24
|
+
outputPerToken: 180 / 1_000_000,
|
|
25
|
+
},
|
|
26
|
+
reasoning: { effort: "xhigh" },
|
|
27
|
+
},
|
|
28
|
+
"gpt-5.5": {
|
|
29
|
+
model: "gpt-5.5",
|
|
30
|
+
provider: "openai",
|
|
31
|
+
tokenizer: countTokensGpt5,
|
|
32
|
+
inputLimit: 1_050_000,
|
|
33
|
+
pricing: {
|
|
34
|
+
inputPerToken: 5 / 1_000_000,
|
|
35
|
+
outputPerToken: 30 / 1_000_000,
|
|
36
|
+
},
|
|
37
|
+
reasoning: { effort: "xhigh" },
|
|
38
|
+
},
|
|
39
|
+
"gpt-5.1-pro": {
|
|
40
|
+
model: "gpt-5.1-pro",
|
|
41
|
+
apiModel: "gpt-5.5-pro",
|
|
42
|
+
provider: "openai",
|
|
13
43
|
tokenizer: countTokensGpt5Pro,
|
|
14
44
|
inputLimit: 196000,
|
|
15
45
|
pricing: {
|
|
@@ -18,9 +48,9 @@ export const MODEL_CONFIGS = {
|
|
|
18
48
|
},
|
|
19
49
|
reasoning: null,
|
|
20
50
|
},
|
|
21
|
-
|
|
22
|
-
model:
|
|
23
|
-
provider:
|
|
51
|
+
"gpt-5-pro": {
|
|
52
|
+
model: "gpt-5-pro",
|
|
53
|
+
provider: "openai",
|
|
24
54
|
tokenizer: countTokensGpt5Pro,
|
|
25
55
|
inputLimit: 196000,
|
|
26
56
|
pricing: {
|
|
@@ -29,65 +59,65 @@ export const MODEL_CONFIGS = {
|
|
|
29
59
|
},
|
|
30
60
|
reasoning: null,
|
|
31
61
|
},
|
|
32
|
-
|
|
33
|
-
model:
|
|
34
|
-
provider:
|
|
62
|
+
"gpt-5.1": {
|
|
63
|
+
model: "gpt-5.1",
|
|
64
|
+
provider: "openai",
|
|
35
65
|
tokenizer: countTokensGpt5,
|
|
36
66
|
inputLimit: 196000,
|
|
37
67
|
pricing: {
|
|
38
68
|
inputPerToken: 1.25 / 1_000_000,
|
|
39
69
|
outputPerToken: 10 / 1_000_000,
|
|
40
70
|
},
|
|
41
|
-
reasoning: { effort:
|
|
71
|
+
reasoning: { effort: "high" },
|
|
42
72
|
},
|
|
43
|
-
|
|
44
|
-
model:
|
|
45
|
-
provider:
|
|
73
|
+
"gpt-5.1-codex": {
|
|
74
|
+
model: "gpt-5.1-codex",
|
|
75
|
+
provider: "openai",
|
|
46
76
|
tokenizer: countTokensGpt5,
|
|
47
77
|
inputLimit: 196000,
|
|
48
78
|
pricing: {
|
|
49
79
|
inputPerToken: 1.25 / 1_000_000,
|
|
50
80
|
outputPerToken: 10 / 1_000_000,
|
|
51
81
|
},
|
|
52
|
-
reasoning: { effort:
|
|
82
|
+
reasoning: { effort: "high" },
|
|
53
83
|
},
|
|
54
|
-
|
|
55
|
-
model:
|
|
56
|
-
provider:
|
|
84
|
+
"gpt-5.4": {
|
|
85
|
+
model: "gpt-5.4",
|
|
86
|
+
provider: "openai",
|
|
57
87
|
tokenizer: countTokensGpt5,
|
|
58
88
|
inputLimit: 196000,
|
|
59
89
|
pricing: {
|
|
60
90
|
inputPerToken: 2.5 / 1_000_000,
|
|
61
91
|
outputPerToken: 15 / 1_000_000,
|
|
62
92
|
},
|
|
63
|
-
reasoning: { effort:
|
|
93
|
+
reasoning: { effort: "xhigh" },
|
|
64
94
|
},
|
|
65
|
-
|
|
66
|
-
model:
|
|
67
|
-
provider:
|
|
95
|
+
"gpt-5.4-pro": {
|
|
96
|
+
model: "gpt-5.4-pro",
|
|
97
|
+
provider: "openai",
|
|
68
98
|
tokenizer: countTokensGpt5Pro,
|
|
69
99
|
inputLimit: 196000,
|
|
70
100
|
pricing: {
|
|
71
101
|
inputPerToken: 30 / 1_000_000,
|
|
72
102
|
outputPerToken: 180 / 1_000_000,
|
|
73
103
|
},
|
|
74
|
-
reasoning: { effort:
|
|
104
|
+
reasoning: { effort: "xhigh" },
|
|
75
105
|
},
|
|
76
|
-
|
|
77
|
-
model:
|
|
78
|
-
provider:
|
|
106
|
+
"gpt-5.2": {
|
|
107
|
+
model: "gpt-5.2",
|
|
108
|
+
provider: "openai",
|
|
79
109
|
tokenizer: countTokensGpt5,
|
|
80
110
|
inputLimit: 196000,
|
|
81
111
|
pricing: {
|
|
82
112
|
inputPerToken: 1.75 / 1_000_000,
|
|
83
113
|
outputPerToken: 14 / 1_000_000,
|
|
84
114
|
},
|
|
85
|
-
reasoning: { effort:
|
|
115
|
+
reasoning: { effort: "xhigh" },
|
|
86
116
|
},
|
|
87
|
-
|
|
88
|
-
model:
|
|
89
|
-
apiModel:
|
|
90
|
-
provider:
|
|
117
|
+
"gpt-5.2-instant": {
|
|
118
|
+
model: "gpt-5.2-instant",
|
|
119
|
+
apiModel: "gpt-5.2-chat-latest",
|
|
120
|
+
provider: "openai",
|
|
91
121
|
tokenizer: countTokensGpt5,
|
|
92
122
|
inputLimit: 196000,
|
|
93
123
|
pricing: {
|
|
@@ -96,21 +126,21 @@ export const MODEL_CONFIGS = {
|
|
|
96
126
|
},
|
|
97
127
|
reasoning: null,
|
|
98
128
|
},
|
|
99
|
-
|
|
100
|
-
model:
|
|
101
|
-
apiModel:
|
|
102
|
-
provider:
|
|
129
|
+
"gpt-5.2-pro": {
|
|
130
|
+
model: "gpt-5.2-pro",
|
|
131
|
+
apiModel: "gpt-5.5-pro",
|
|
132
|
+
provider: "openai",
|
|
103
133
|
tokenizer: countTokensGpt5Pro,
|
|
104
134
|
inputLimit: 196000,
|
|
105
135
|
pricing: {
|
|
106
136
|
inputPerToken: 30 / 1_000_000,
|
|
107
137
|
outputPerToken: 180 / 1_000_000,
|
|
108
138
|
},
|
|
109
|
-
reasoning: { effort:
|
|
139
|
+
reasoning: { effort: "xhigh" },
|
|
110
140
|
},
|
|
111
|
-
|
|
112
|
-
model:
|
|
113
|
-
provider:
|
|
141
|
+
"gemini-3.1-pro": {
|
|
142
|
+
model: "gemini-3.1-pro",
|
|
143
|
+
provider: "google",
|
|
114
144
|
tokenizer: countTokensGpt5Pro,
|
|
115
145
|
inputLimit: 200000,
|
|
116
146
|
pricing: {
|
|
@@ -121,9 +151,9 @@ export const MODEL_CONFIGS = {
|
|
|
121
151
|
supportsBackground: false,
|
|
122
152
|
supportsSearch: true,
|
|
123
153
|
},
|
|
124
|
-
|
|
125
|
-
model:
|
|
126
|
-
provider:
|
|
154
|
+
"gemini-3-pro": {
|
|
155
|
+
model: "gemini-3-pro",
|
|
156
|
+
provider: "google",
|
|
127
157
|
tokenizer: countTokensGpt5Pro,
|
|
128
158
|
inputLimit: 200000,
|
|
129
159
|
pricing: {
|
|
@@ -134,10 +164,10 @@ export const MODEL_CONFIGS = {
|
|
|
134
164
|
supportsBackground: false,
|
|
135
165
|
supportsSearch: true,
|
|
136
166
|
},
|
|
137
|
-
|
|
138
|
-
model:
|
|
139
|
-
apiModel:
|
|
140
|
-
provider:
|
|
167
|
+
"claude-4.6-sonnet": {
|
|
168
|
+
model: "claude-4.6-sonnet",
|
|
169
|
+
apiModel: "claude-sonnet-4-6",
|
|
170
|
+
provider: "anthropic",
|
|
141
171
|
tokenizer: countTokensAnthropic,
|
|
142
172
|
inputLimit: 200000,
|
|
143
173
|
pricing: {
|
|
@@ -148,24 +178,24 @@ export const MODEL_CONFIGS = {
|
|
|
148
178
|
supportsBackground: false,
|
|
149
179
|
supportsSearch: false,
|
|
150
180
|
},
|
|
151
|
-
|
|
152
|
-
model:
|
|
153
|
-
apiModel:
|
|
154
|
-
provider:
|
|
181
|
+
"claude-4.1-opus": {
|
|
182
|
+
model: "claude-4.1-opus",
|
|
183
|
+
apiModel: "claude-opus-4-1",
|
|
184
|
+
provider: "anthropic",
|
|
155
185
|
tokenizer: countTokensAnthropic,
|
|
156
186
|
inputLimit: 200000,
|
|
157
187
|
pricing: {
|
|
158
188
|
inputPerToken: 15 / 1_000_000,
|
|
159
189
|
outputPerToken: 75 / 1_000_000,
|
|
160
190
|
},
|
|
161
|
-
reasoning: { effort:
|
|
191
|
+
reasoning: { effort: "high" },
|
|
162
192
|
supportsBackground: false,
|
|
163
193
|
supportsSearch: false,
|
|
164
194
|
},
|
|
165
|
-
|
|
166
|
-
model:
|
|
167
|
-
apiModel:
|
|
168
|
-
provider:
|
|
195
|
+
"grok-4.1": {
|
|
196
|
+
model: "grok-4.1",
|
|
197
|
+
apiModel: "grok-4-1-fast-reasoning",
|
|
198
|
+
provider: "xai",
|
|
169
199
|
tokenizer: countTokensGpt5Pro,
|
|
170
200
|
inputLimit: 2_000_000,
|
|
171
201
|
pricing: {
|
|
@@ -175,11 +205,11 @@ export const MODEL_CONFIGS = {
|
|
|
175
205
|
reasoning: null,
|
|
176
206
|
supportsBackground: false,
|
|
177
207
|
supportsSearch: true,
|
|
178
|
-
searchToolType:
|
|
208
|
+
searchToolType: "web_search",
|
|
179
209
|
},
|
|
180
210
|
};
|
|
181
211
|
export const DEFAULT_SYSTEM_PROMPT = [
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
].join(
|
|
185
|
-
export const TOKENIZER_OPTIONS = { allowedSpecial:
|
|
212
|
+
"You are Oracle, a focused one-shot problem solver.",
|
|
213
|
+
"Emphasize direct answers and cite any files referenced.",
|
|
214
|
+
].join(" ");
|
|
215
|
+
export const TOKENIZER_OPTIONS = { allowedSpecial: "all" };
|