@steipete/oracle 0.8.6 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +130 -45
  3. package/dist/bin/oracle-cli.js +613 -379
  4. package/dist/bin/oracle-mcp.js +2 -2
  5. package/dist/bin/oracle.js +165 -279
  6. package/dist/scripts/agent-send.js +31 -31
  7. package/dist/scripts/check.js +6 -6
  8. package/dist/scripts/debug/extract-chatgpt-response.js +10 -10
  9. package/dist/scripts/docs-list.js +30 -30
  10. package/dist/scripts/git-policy.js +25 -23
  11. package/dist/scripts/run-cli.js +8 -8
  12. package/dist/scripts/runner.js +203 -195
  13. package/dist/scripts/test-browser.js +21 -18
  14. package/dist/scripts/test-remote-chrome.js +20 -20
  15. package/dist/src/bridge/connection.js +18 -18
  16. package/dist/src/bridge/userConfigFile.js +7 -7
  17. package/dist/src/browser/actions/assistantResponse.js +149 -101
  18. package/dist/src/browser/actions/attachmentDataTransfer.js +49 -47
  19. package/dist/src/browser/actions/attachments.js +246 -150
  20. package/dist/src/browser/actions/domEvents.js +2 -2
  21. package/dist/src/browser/actions/modelSelection.js +314 -104
  22. package/dist/src/browser/actions/navigation.js +161 -136
  23. package/dist/src/browser/actions/promptComposer.js +100 -64
  24. package/dist/src/browser/actions/remoteFileTransfer.js +10 -10
  25. package/dist/src/browser/actions/thinkingTime.js +207 -110
  26. package/dist/src/browser/chromeLifecycle.js +62 -60
  27. package/dist/src/browser/config.js +34 -15
  28. package/dist/src/browser/constants.js +17 -12
  29. package/dist/src/browser/cookies.js +19 -19
  30. package/dist/src/browser/detect.js +62 -62
  31. package/dist/src/browser/domDebug.js +1 -1
  32. package/dist/src/browser/index.js +452 -303
  33. package/dist/src/browser/modelStrategy.js +1 -1
  34. package/dist/src/browser/pageActions.js +5 -5
  35. package/dist/src/browser/policies.js +16 -13
  36. package/dist/src/browser/profileState.js +44 -39
  37. package/dist/src/browser/prompt.js +72 -42
  38. package/dist/src/browser/promptSummary.js +5 -5
  39. package/dist/src/browser/providerDomFlow.js +17 -0
  40. package/dist/src/browser/providers/chatgptDomProvider.js +49 -0
  41. package/dist/src/browser/providers/geminiDeepThinkDomProvider.js +254 -0
  42. package/dist/src/browser/providers/index.js +2 -0
  43. package/dist/src/browser/reattach.js +67 -34
  44. package/dist/src/browser/reattachHelpers.js +31 -26
  45. package/dist/src/browser/sessionRunner.js +37 -25
  46. package/dist/src/browser/utils.js +9 -9
  47. package/dist/src/browserMode.js +1 -1
  48. package/dist/src/cli/bridge/claudeConfig.js +16 -16
  49. package/dist/src/cli/bridge/client.js +28 -20
  50. package/dist/src/cli/bridge/codexConfig.js +16 -16
  51. package/dist/src/cli/bridge/doctor.js +47 -39
  52. package/dist/src/cli/bridge/host.js +58 -56
  53. package/dist/src/cli/browserConfig.js +65 -45
  54. package/dist/src/cli/browserDefaults.js +27 -26
  55. package/dist/src/cli/bundleWarnings.js +1 -1
  56. package/dist/src/cli/clipboard.js +11 -2
  57. package/dist/src/cli/detach.js +7 -4
  58. package/dist/src/cli/dryRun.js +29 -25
  59. package/dist/src/cli/duplicatePromptGuard.js +3 -3
  60. package/dist/src/cli/engine.js +9 -9
  61. package/dist/src/cli/errorUtils.js +1 -1
  62. package/dist/src/cli/fileSize.js +11 -0
  63. package/dist/src/cli/format.js +2 -2
  64. package/dist/src/cli/help.js +28 -28
  65. package/dist/src/cli/hiddenAliases.js +3 -3
  66. package/dist/src/cli/markdownBundle.js +12 -8
  67. package/dist/src/cli/markdownRenderer.js +15 -15
  68. package/dist/src/cli/notifier.js +77 -67
  69. package/dist/src/cli/options.js +145 -87
  70. package/dist/src/cli/oscUtils.js +1 -1
  71. package/dist/src/cli/promptRequirement.js +2 -2
  72. package/dist/src/cli/renderOutput.js +1 -1
  73. package/dist/src/cli/rootAlias.js +1 -1
  74. package/dist/src/cli/runOptions.js +37 -25
  75. package/dist/src/cli/sessionCommand.js +31 -21
  76. package/dist/src/cli/sessionDisplay.js +182 -79
  77. package/dist/src/cli/sessionLineage.js +60 -0
  78. package/dist/src/cli/sessionRunner.js +118 -90
  79. package/dist/src/cli/sessionTable.js +28 -24
  80. package/dist/src/cli/stdin.js +22 -0
  81. package/dist/src/cli/tagline.js +121 -124
  82. package/dist/src/cli/tui/index.js +140 -127
  83. package/dist/src/cli/writeOutputPath.js +5 -5
  84. package/dist/src/config.js +7 -7
  85. package/dist/src/gemini-web/browserSessionManager.js +80 -0
  86. package/dist/src/gemini-web/client.js +81 -64
  87. package/dist/src/gemini-web/executionMode.js +16 -0
  88. package/dist/src/gemini-web/executor.js +327 -169
  89. package/dist/src/gemini-web/index.js +1 -1
  90. package/dist/src/mcp/server.js +16 -12
  91. package/dist/src/mcp/tools/consult.js +81 -64
  92. package/dist/src/mcp/tools/sessionResources.js +12 -12
  93. package/dist/src/mcp/tools/sessions.js +26 -17
  94. package/dist/src/mcp/types.js +5 -5
  95. package/dist/src/mcp/utils.js +15 -7
  96. package/dist/src/oracle/background.js +15 -15
  97. package/dist/src/oracle/claude.js +53 -25
  98. package/dist/src/oracle/client.js +84 -46
  99. package/dist/src/oracle/config.js +124 -58
  100. package/dist/src/oracle/errors.js +38 -38
  101. package/dist/src/oracle/files.js +69 -45
  102. package/dist/src/oracle/finishLine.js +10 -8
  103. package/dist/src/oracle/format.js +3 -3
  104. package/dist/src/oracle/gemini.js +37 -30
  105. package/dist/src/oracle/logging.js +7 -7
  106. package/dist/src/oracle/markdown.js +28 -28
  107. package/dist/src/oracle/modelResolver.js +16 -16
  108. package/dist/src/oracle/multiModelRunner.js +12 -12
  109. package/dist/src/oracle/oscProgress.js +8 -8
  110. package/dist/src/oracle/promptAssembly.js +6 -3
  111. package/dist/src/oracle/request.js +23 -15
  112. package/dist/src/oracle/run.js +172 -140
  113. package/dist/src/oracle/runUtils.js +8 -5
  114. package/dist/src/oracle/tokenEstimate.js +6 -6
  115. package/dist/src/oracle/tokenStats.js +5 -5
  116. package/dist/src/oracle/tokenStringifier.js +5 -5
  117. package/dist/src/oracle.js +12 -12
  118. package/dist/src/oracleHome.js +3 -3
  119. package/dist/src/remote/client.js +25 -25
  120. package/dist/src/remote/health.js +20 -20
  121. package/dist/src/remote/remoteServiceConfig.js +9 -9
  122. package/dist/src/remote/server.js +129 -118
  123. package/dist/src/sessionManager.js +81 -75
  124. package/dist/src/sessionStore.js +3 -3
  125. package/dist/src/version.js +10 -10
  126. package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/CodeResources +0 -0
  127. package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/MacOS/OracleNotifier +0 -0
  128. package/dist/vendor/oracle-notifier/README.md +2 -0
  129. package/package.json +69 -65
  130. package/vendor/oracle-notifier/OracleNotifier.app/Contents/CodeResources +0 -0
  131. package/vendor/oracle-notifier/OracleNotifier.app/Contents/MacOS/OracleNotifier +0 -0
  132. package/vendor/oracle-notifier/README.md +2 -0
  133. package/dist/markdansi/types/index.js +0 -4
  134. package/dist/oracle/bin/oracle-cli.js +0 -472
  135. package/dist/oracle/src/browser/actions/assistantResponse.js +0 -471
  136. package/dist/oracle/src/browser/actions/attachments.js +0 -82
  137. package/dist/oracle/src/browser/actions/modelSelection.js +0 -190
  138. package/dist/oracle/src/browser/actions/navigation.js +0 -75
  139. package/dist/oracle/src/browser/actions/promptComposer.js +0 -167
  140. package/dist/oracle/src/browser/chromeLifecycle.js +0 -104
  141. package/dist/oracle/src/browser/config.js +0 -33
  142. package/dist/oracle/src/browser/constants.js +0 -40
  143. package/dist/oracle/src/browser/cookies.js +0 -210
  144. package/dist/oracle/src/browser/domDebug.js +0 -36
  145. package/dist/oracle/src/browser/index.js +0 -331
  146. package/dist/oracle/src/browser/pageActions.js +0 -5
  147. package/dist/oracle/src/browser/prompt.js +0 -88
  148. package/dist/oracle/src/browser/promptSummary.js +0 -20
  149. package/dist/oracle/src/browser/sessionRunner.js +0 -80
  150. package/dist/oracle/src/browser/utils.js +0 -62
  151. package/dist/oracle/src/browserMode.js +0 -1
  152. package/dist/oracle/src/cli/browserConfig.js +0 -44
  153. package/dist/oracle/src/cli/dryRun.js +0 -59
  154. package/dist/oracle/src/cli/engine.js +0 -17
  155. package/dist/oracle/src/cli/errorUtils.js +0 -9
  156. package/dist/oracle/src/cli/help.js +0 -70
  157. package/dist/oracle/src/cli/markdownRenderer.js +0 -15
  158. package/dist/oracle/src/cli/options.js +0 -103
  159. package/dist/oracle/src/cli/promptRequirement.js +0 -14
  160. package/dist/oracle/src/cli/rootAlias.js +0 -30
  161. package/dist/oracle/src/cli/sessionCommand.js +0 -77
  162. package/dist/oracle/src/cli/sessionDisplay.js +0 -270
  163. package/dist/oracle/src/cli/sessionRunner.js +0 -94
  164. package/dist/oracle/src/heartbeat.js +0 -43
  165. package/dist/oracle/src/oracle/client.js +0 -48
  166. package/dist/oracle/src/oracle/config.js +0 -29
  167. package/dist/oracle/src/oracle/errors.js +0 -101
  168. package/dist/oracle/src/oracle/files.js +0 -220
  169. package/dist/oracle/src/oracle/format.js +0 -33
  170. package/dist/oracle/src/oracle/fsAdapter.js +0 -7
  171. package/dist/oracle/src/oracle/oscProgress.js +0 -60
  172. package/dist/oracle/src/oracle/request.js +0 -48
  173. package/dist/oracle/src/oracle/run.js +0 -444
  174. package/dist/oracle/src/oracle/tokenStats.js +0 -39
  175. package/dist/oracle/src/oracle/types.js +0 -1
  176. package/dist/oracle/src/oracle.js +0 -9
  177. package/dist/oracle/src/sessionManager.js +0 -205
  178. package/dist/oracle/src/version.js +0 -39
  179. package/dist/scripts/chrome/browser-tools.js +0 -295
  180. package/dist/src/browser/profileSync.js +0 -141
  181. /package/dist/{oracle/src/browser/types.js → src/gemini-web/executionClients.js} +0 -0
@@ -1,11 +1,11 @@
1
- const DEFAULT_CLAUDE_ENDPOINT = 'https://api.anthropic.com/v1/messages';
2
- const ANTHROPIC_VERSION = '2023-06-01';
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 === 'input_text') {
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: 'user',
17
+ role: "user",
18
18
  content: prompt,
19
19
  },
20
20
  ],
21
21
  stream,
22
22
  };
23
23
  return fetch(url, {
24
- method: 'POST',
24
+ method: "POST",
25
25
  headers: {
26
- 'content-type': 'application/json',
27
- 'x-api-key': apiKey,
28
- 'anthropic-version': ANTHROPIC_VERSION,
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 json = (await raw.json());
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 || 'Claude request failed');
47
+ throw new Error(json.error.message || "Claude request failed");
37
48
  }
38
- const textParts = json.content?.map((part) => part.text ?? '').filter(Boolean) ?? [];
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: 'completed',
53
+ status: "completed",
43
54
  output_text: [outputText],
44
- output: [{ type: 'text', text: outputText }],
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({ apiKey, model: modelId, prompt, stream: false, endpoint: baseUrl });
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: 'response.output_text.delta', delta: parsed.output_text[0] };
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({ apiKey, model: modelId, prompt, stream: false, endpoint: baseUrl });
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: 'error',
77
- error: { message: 'Retrieve by ID not supported for Claude API yet.' },
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 === 'claude-4.5-sonnet' || modelName === 'claude-sonnet-4-5-20241022') {
89
- return 'claude-sonnet-4-5';
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 === 'claude-4.1-opus' || modelName === 'claude-opus-4-1-20240808') {
92
- return 'claude-opus-4-1';
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,34 +1,67 @@
1
- import OpenAI, { AzureOpenAI } 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';
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
+ /**
8
+ * Known native API base URLs that should still use their dedicated SDKs.
9
+ * Any other custom base URL is treated as an OpenAI-compatible proxy and
10
+ * all models are routed through the chat/completions adapter.
11
+ */
12
+ const NATIVE_API_HOSTS = [
13
+ "api.openai.com",
14
+ "api.anthropic.com",
15
+ "generativelanguage.googleapis.com",
16
+ "api.x.ai",
17
+ ];
18
+ export function isCustomBaseUrl(baseUrl) {
19
+ if (!baseUrl)
20
+ return false;
21
+ try {
22
+ const url = new URL(baseUrl);
23
+ return !NATIVE_API_HOSTS.some((host) => url.hostname === host || url.hostname.endsWith(`.${host}`));
24
+ }
25
+ catch {
26
+ return false;
27
+ }
28
+ }
29
+ export function buildAzureResponsesBaseUrl(endpoint) {
30
+ return `${endpoint.replace(/\/+$/, "")}/openai/v1`;
31
+ }
7
32
  export function createDefaultClientFactory() {
8
33
  const customFactory = loadCustomClientFactory();
9
34
  if (customFactory)
10
35
  return customFactory;
11
36
  return (key, options) => {
12
- if (options?.model?.startsWith('gemini')) {
13
- // Gemini client uses its own SDK; allow passing the already-resolved id for transparency/logging.
14
- return createGeminiClient(key, options.model, options.resolvedModelId);
15
- }
16
- if (options?.model?.startsWith('claude')) {
17
- return createClaudeClient(key, options.model, options.resolvedModelId, options.baseUrl);
37
+ const openRouter = isOpenRouterBaseUrl(options?.baseUrl);
38
+ const customProxy = isCustomBaseUrl(options?.baseUrl);
39
+ // When using any custom/proxy base URL (OpenRouter, LiteLLM, vLLM, Together, etc.),
40
+ // route ALL models through the OpenAI chat/completions adapter instead of native SDKs
41
+ // which would reject the proxy's API key.
42
+ if (!openRouter && !customProxy) {
43
+ if (options?.model?.startsWith("gemini")) {
44
+ // Gemini client uses its own SDK; allow passing the already-resolved id for transparency/logging.
45
+ return createGeminiClient(key, options.model, options.resolvedModelId);
46
+ }
47
+ if (options?.model?.startsWith("claude")) {
48
+ return createClaudeClient(key, options.model, options.resolvedModelId, options.baseUrl);
49
+ }
18
50
  }
19
51
  let instance;
20
- const openRouter = isOpenRouterBaseUrl(options?.baseUrl);
21
- const defaultHeaders = openRouter ? buildOpenRouterHeaders() : undefined;
22
- const httpTimeoutMs = typeof options?.httpTimeoutMs === 'number' && Number.isFinite(options.httpTimeoutMs) && options.httpTimeoutMs > 0
52
+ const defaultHeaders = openRouter
53
+ ? buildOpenRouterHeaders()
54
+ : undefined;
55
+ const httpTimeoutMs = typeof options?.httpTimeoutMs === "number" &&
56
+ Number.isFinite(options.httpTimeoutMs) &&
57
+ options.httpTimeoutMs > 0
23
58
  ? options.httpTimeoutMs
24
59
  : 20 * 60 * 1000;
25
60
  if (options?.azure?.endpoint) {
26
- instance = new AzureOpenAI({
61
+ instance = new OpenAI({
27
62
  apiKey: key,
28
- endpoint: options.azure.endpoint,
29
- apiVersion: options.azure.apiVersion,
30
- deployment: options.azure.deployment,
31
63
  timeout: httpTimeoutMs,
64
+ baseURL: buildAzureResponsesBaseUrl(options.azure.endpoint),
32
65
  });
33
66
  }
34
67
  else {
@@ -39,7 +72,7 @@ export function createDefaultClientFactory() {
39
72
  defaultHeaders,
40
73
  });
41
74
  }
42
- if (openRouter) {
75
+ if (openRouter || customProxy) {
43
76
  return buildOpenRouterCompletionClient(instance);
44
77
  }
45
78
  return {
@@ -53,13 +86,15 @@ export function createDefaultClientFactory() {
53
86
  }
54
87
  function buildOpenRouterHeaders() {
55
88
  const headers = {};
56
- const referer = process.env.OPENROUTER_REFERER ?? process.env.OPENROUTER_HTTP_REFERER ?? 'https://github.com/steipete/oracle';
57
- const title = process.env.OPENROUTER_TITLE ?? 'Oracle CLI';
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";
58
93
  if (referer) {
59
- headers['HTTP-Referer'] = referer;
94
+ headers["HTTP-Referer"] = referer;
60
95
  }
61
96
  if (title) {
62
- headers['X-Title'] = title;
97
+ headers["X-Title"] = title;
63
98
  }
64
99
  return headers;
65
100
  }
@@ -68,19 +103,19 @@ function loadCustomClientFactory() {
68
103
  if (!override) {
69
104
  return null;
70
105
  }
71
- if (override === 'INLINE_TEST_FACTORY') {
106
+ if (override === "INLINE_TEST_FACTORY") {
72
107
  return () => ({
73
108
  responses: {
74
- create: async () => ({ id: 'inline-test', status: 'completed' }),
109
+ create: async () => ({ id: "inline-test", status: "completed" }),
75
110
  stream: async () => ({
76
111
  [Symbol.asyncIterator]: () => ({
77
112
  async next() {
78
113
  return { done: true, value: undefined };
79
114
  },
80
115
  }),
81
- finalResponse: async () => ({ id: 'inline-test', status: 'completed' }),
116
+ finalResponse: async () => ({ id: "inline-test", status: "completed" }),
82
117
  }),
83
- retrieve: async (id) => ({ id, status: 'completed' }),
118
+ retrieve: async (id) => ({ id, status: "completed" }),
84
119
  },
85
120
  });
86
121
  }
@@ -88,14 +123,14 @@ function loadCustomClientFactory() {
88
123
  const require = createRequire(import.meta.url);
89
124
  const resolved = path.isAbsolute(override) ? override : path.resolve(process.cwd(), override);
90
125
  const moduleExports = require(resolved);
91
- const factory = typeof moduleExports === 'function'
126
+ const factory = typeof moduleExports === "function"
92
127
  ? moduleExports
93
- : typeof moduleExports?.default === 'function'
128
+ : typeof moduleExports?.default === "function"
94
129
  ? moduleExports.default
95
- : typeof moduleExports?.createClientFactory === 'function'
130
+ : typeof moduleExports?.createClientFactory === "function"
96
131
  ? moduleExports.createClientFactory
97
132
  : null;
98
- if (typeof factory === 'function') {
133
+ if (typeof factory === "function") {
99
134
  return factory;
100
135
  }
101
136
  console.warn(`Custom client factory at ${resolved} did not export a function.`);
@@ -111,14 +146,17 @@ function buildOpenRouterCompletionClient(instance) {
111
146
  const adaptRequest = (body) => {
112
147
  const messages = [];
113
148
  if (body.instructions) {
114
- messages.push({ role: 'system', content: body.instructions });
149
+ messages.push({ role: "system", content: body.instructions });
115
150
  }
116
151
  for (const entry of body.input) {
117
152
  const textParts = entry.content
118
- .map((c) => (c.type === 'input_text' ? c.text : ''))
153
+ .map((c) => (c.type === "input_text" ? c.text : ""))
119
154
  .filter((t) => t)
120
- .join('\n\n');
121
- messages.push({ role: entry.role ?? 'user', content: textParts });
155
+ .join("\n\n");
156
+ messages.push({
157
+ role: entry.role ?? "user",
158
+ content: textParts,
159
+ });
122
160
  }
123
161
  const base = {
124
162
  model: body.model,
@@ -130,7 +168,7 @@ function buildOpenRouterCompletionClient(instance) {
130
168
  return { streaming, nonStreaming };
131
169
  };
132
170
  const adaptResponse = (response) => {
133
- const text = response.choices?.[0]?.message?.content ?? '';
171
+ const text = response.choices?.[0]?.message?.content ?? "";
134
172
  const usage = {
135
173
  input_tokens: response.usage?.prompt_tokens ?? 0,
136
174
  output_tokens: response.usage?.completion_tokens ?? 0,
@@ -138,9 +176,9 @@ function buildOpenRouterCompletionClient(instance) {
138
176
  };
139
177
  return {
140
178
  id: response.id ?? `openrouter-${Date.now()}`,
141
- status: 'completed',
179
+ status: "completed",
142
180
  output_text: [text],
143
- output: [{ type: 'text', text }],
181
+ output: [{ type: "text", text }],
144
182
  usage,
145
183
  };
146
184
  };
@@ -148,15 +186,15 @@ function buildOpenRouterCompletionClient(instance) {
148
186
  const { streaming } = adaptRequest(body);
149
187
  let finalUsage;
150
188
  let finalId;
151
- let aggregated = '';
189
+ let aggregated = "";
152
190
  async function* iterator() {
153
191
  const completion = await instance.chat.completions.create(streaming);
154
192
  for await (const chunk of completion) {
155
193
  finalId = chunk.id ?? finalId;
156
- const delta = chunk.choices?.[0]?.delta?.content ?? '';
194
+ const delta = chunk.choices?.[0]?.delta?.content ?? "";
157
195
  if (delta) {
158
196
  aggregated += delta;
159
- yield { type: 'chunk', delta };
197
+ yield { type: "chunk", delta };
160
198
  }
161
199
  if (chunk.usage) {
162
200
  finalUsage = chunk.usage;
@@ -171,11 +209,11 @@ function buildOpenRouterCompletionClient(instance) {
171
209
  async finalResponse() {
172
210
  return adaptResponse({
173
211
  id: finalId ?? `openrouter-${Date.now()}`,
174
- choices: [{ message: { role: 'assistant', content: aggregated } }],
212
+ choices: [{ message: { role: "assistant", content: aggregated } }],
175
213
  usage: finalUsage ?? { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 },
176
214
  created: Math.floor(Date.now() / 1000),
177
- model: '',
178
- object: 'chat.completion',
215
+ model: "",
216
+ object: "chat.completion",
179
217
  });
180
218
  },
181
219
  };
@@ -190,7 +228,7 @@ function buildOpenRouterCompletionClient(instance) {
190
228
  stream,
191
229
  create,
192
230
  retrieve: async () => {
193
- throw new Error('retrieve is not supported for OpenRouter chat/completions fallback.');
231
+ throw new Error("retrieve is not supported for OpenRouter chat/completions fallback.");
194
232
  },
195
233
  },
196
234
  };
@@ -1,26 +1,56 @@
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.2-pro';
6
- export const PRO_MODELS = new Set(['gpt-5.1-pro', 'gpt-5-pro', 'gpt-5.2-pro', 'claude-4.5-sonnet', 'claude-4.1-opus']);
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
- 'gpt-5.1-pro': {
10
- model: 'gpt-5.1-pro',
11
- apiModel: 'gpt-5.2-pro',
12
- provider: 'openai',
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: {
16
- inputPerToken: 21 / 1_000_000,
17
- outputPerToken: 168 / 1_000_000,
46
+ inputPerToken: 30 / 1_000_000,
47
+ outputPerToken: 180 / 1_000_000,
18
48
  },
19
49
  reasoning: null,
20
50
  },
21
- 'gpt-5-pro': {
22
- model: 'gpt-5-pro',
23
- provider: 'openai',
51
+ "gpt-5-pro": {
52
+ model: "gpt-5-pro",
53
+ provider: "openai",
24
54
  tokenizer: countTokensGpt5Pro,
25
55
  inputLimit: 196000,
26
56
  pricing: {
@@ -29,43 +59,65 @@ export const MODEL_CONFIGS = {
29
59
  },
30
60
  reasoning: null,
31
61
  },
32
- 'gpt-5.1': {
33
- model: 'gpt-5.1',
34
- provider: 'openai',
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: 'high' },
71
+ reasoning: { effort: "high" },
42
72
  },
43
- 'gpt-5.1-codex': {
44
- model: 'gpt-5.1-codex',
45
- provider: 'openai',
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: 'high' },
82
+ reasoning: { effort: "high" },
53
83
  },
54
- 'gpt-5.2': {
55
- model: 'gpt-5.2',
56
- provider: 'openai',
84
+ "gpt-5.4": {
85
+ model: "gpt-5.4",
86
+ provider: "openai",
87
+ tokenizer: countTokensGpt5,
88
+ inputLimit: 196000,
89
+ pricing: {
90
+ inputPerToken: 2.5 / 1_000_000,
91
+ outputPerToken: 15 / 1_000_000,
92
+ },
93
+ reasoning: { effort: "xhigh" },
94
+ },
95
+ "gpt-5.4-pro": {
96
+ model: "gpt-5.4-pro",
97
+ provider: "openai",
98
+ tokenizer: countTokensGpt5Pro,
99
+ inputLimit: 196000,
100
+ pricing: {
101
+ inputPerToken: 30 / 1_000_000,
102
+ outputPerToken: 180 / 1_000_000,
103
+ },
104
+ reasoning: { effort: "xhigh" },
105
+ },
106
+ "gpt-5.2": {
107
+ model: "gpt-5.2",
108
+ provider: "openai",
57
109
  tokenizer: countTokensGpt5,
58
110
  inputLimit: 196000,
59
111
  pricing: {
60
112
  inputPerToken: 1.75 / 1_000_000,
61
113
  outputPerToken: 14 / 1_000_000,
62
114
  },
63
- reasoning: { effort: 'xhigh' },
115
+ reasoning: { effort: "xhigh" },
64
116
  },
65
- 'gpt-5.2-instant': {
66
- model: 'gpt-5.2-instant',
67
- apiModel: 'gpt-5.2-chat-latest',
68
- provider: 'openai',
117
+ "gpt-5.2-instant": {
118
+ model: "gpt-5.2-instant",
119
+ apiModel: "gpt-5.2-chat-latest",
120
+ provider: "openai",
69
121
  tokenizer: countTokensGpt5,
70
122
  inputLimit: 196000,
71
123
  pricing: {
@@ -74,20 +126,34 @@ export const MODEL_CONFIGS = {
74
126
  },
75
127
  reasoning: null,
76
128
  },
77
- 'gpt-5.2-pro': {
78
- model: 'gpt-5.2-pro',
79
- provider: 'openai',
129
+ "gpt-5.2-pro": {
130
+ model: "gpt-5.2-pro",
131
+ apiModel: "gpt-5.5-pro",
132
+ provider: "openai",
80
133
  tokenizer: countTokensGpt5Pro,
81
134
  inputLimit: 196000,
82
135
  pricing: {
83
- inputPerToken: 21 / 1_000_000,
84
- outputPerToken: 168 / 1_000_000,
136
+ inputPerToken: 30 / 1_000_000,
137
+ outputPerToken: 180 / 1_000_000,
138
+ },
139
+ reasoning: { effort: "xhigh" },
140
+ },
141
+ "gemini-3.1-pro": {
142
+ model: "gemini-3.1-pro",
143
+ provider: "google",
144
+ tokenizer: countTokensGpt5Pro,
145
+ inputLimit: 200000,
146
+ pricing: {
147
+ inputPerToken: 2 / 1_000_000,
148
+ outputPerToken: 12 / 1_000_000,
85
149
  },
86
- reasoning: { effort: 'xhigh' },
150
+ reasoning: null,
151
+ supportsBackground: false,
152
+ supportsSearch: true,
87
153
  },
88
- 'gemini-3-pro': {
89
- model: 'gemini-3-pro',
90
- provider: 'google',
154
+ "gemini-3-pro": {
155
+ model: "gemini-3-pro",
156
+ provider: "google",
91
157
  tokenizer: countTokensGpt5Pro,
92
158
  inputLimit: 200000,
93
159
  pricing: {
@@ -98,10 +164,10 @@ export const MODEL_CONFIGS = {
98
164
  supportsBackground: false,
99
165
  supportsSearch: true,
100
166
  },
101
- 'claude-4.5-sonnet': {
102
- model: 'claude-4.5-sonnet',
103
- apiModel: 'claude-sonnet-4-5',
104
- provider: 'anthropic',
167
+ "claude-4.6-sonnet": {
168
+ model: "claude-4.6-sonnet",
169
+ apiModel: "claude-sonnet-4-6",
170
+ provider: "anthropic",
105
171
  tokenizer: countTokensAnthropic,
106
172
  inputLimit: 200000,
107
173
  pricing: {
@@ -112,24 +178,24 @@ export const MODEL_CONFIGS = {
112
178
  supportsBackground: false,
113
179
  supportsSearch: false,
114
180
  },
115
- 'claude-4.1-opus': {
116
- model: 'claude-4.1-opus',
117
- apiModel: 'claude-opus-4-1',
118
- provider: 'anthropic',
181
+ "claude-4.1-opus": {
182
+ model: "claude-4.1-opus",
183
+ apiModel: "claude-opus-4-1",
184
+ provider: "anthropic",
119
185
  tokenizer: countTokensAnthropic,
120
186
  inputLimit: 200000,
121
187
  pricing: {
122
188
  inputPerToken: 15 / 1_000_000,
123
189
  outputPerToken: 75 / 1_000_000,
124
190
  },
125
- reasoning: { effort: 'high' },
191
+ reasoning: { effort: "high" },
126
192
  supportsBackground: false,
127
193
  supportsSearch: false,
128
194
  },
129
- 'grok-4.1': {
130
- model: 'grok-4.1',
131
- apiModel: 'grok-4-1-fast-reasoning',
132
- provider: 'xai',
195
+ "grok-4.1": {
196
+ model: "grok-4.1",
197
+ apiModel: "grok-4-1-fast-reasoning",
198
+ provider: "xai",
133
199
  tokenizer: countTokensGpt5Pro,
134
200
  inputLimit: 2_000_000,
135
201
  pricing: {
@@ -139,11 +205,11 @@ export const MODEL_CONFIGS = {
139
205
  reasoning: null,
140
206
  supportsBackground: false,
141
207
  supportsSearch: true,
142
- searchToolType: 'web_search',
208
+ searchToolType: "web_search",
143
209
  },
144
210
  };
145
211
  export const DEFAULT_SYSTEM_PROMPT = [
146
- 'You are Oracle, a focused one-shot problem solver.',
147
- 'Emphasize direct answers and cite any files referenced.',
148
- ].join(' ');
149
- export const TOKENIZER_OPTIONS = { allowedSpecial: 'all' };
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" };