neoctl 0.2.53 → 0.2.54

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 (76) hide show
  1. package/README.md +0 -1
  2. package/dist/agents/agent-tool.js +4 -4
  3. package/dist/agents/agent-tool.js.map +1 -1
  4. package/dist/core/query-engine.d.ts +10 -0
  5. package/dist/core/query-engine.js +72 -16
  6. package/dist/core/query-engine.js.map +1 -1
  7. package/dist/core/query.d.ts +1 -0
  8. package/dist/core/query.js +5 -0
  9. package/dist/core/query.js.map +1 -1
  10. package/dist/core/run-agent.js +45 -36
  11. package/dist/core/run-agent.js.map +1 -1
  12. package/dist/index.d.ts +2 -0
  13. package/dist/index.js +2 -0
  14. package/dist/index.js.map +1 -1
  15. package/dist/model/agent-settings-boundary.d.ts +11 -0
  16. package/dist/model/agent-settings-boundary.js +52 -0
  17. package/dist/model/agent-settings-boundary.js.map +1 -0
  18. package/dist/model/anthropic-adapter.d.ts +24 -0
  19. package/dist/model/anthropic-adapter.js +106 -0
  20. package/dist/model/anthropic-adapter.js.map +1 -0
  21. package/dist/model/anthropic-mapper.d.ts +14 -0
  22. package/dist/model/anthropic-mapper.js +340 -0
  23. package/dist/model/anthropic-mapper.js.map +1 -0
  24. package/dist/model/composition-runtime.d.ts +31 -0
  25. package/dist/model/composition-runtime.js +31 -0
  26. package/dist/model/composition-runtime.js.map +1 -0
  27. package/dist/model/config.d.ts +10 -3
  28. package/dist/model/config.js +26 -10
  29. package/dist/model/config.js.map +1 -1
  30. package/dist/model/context-window.d.ts +16 -1
  31. package/dist/model/context-window.js +171 -5
  32. package/dist/model/context-window.js.map +1 -1
  33. package/dist/model/env.js +0 -1
  34. package/dist/model/env.js.map +1 -1
  35. package/dist/model/model-gateway.d.ts +3 -1
  36. package/dist/model/model-gateway.js +4 -0
  37. package/dist/model/model-gateway.js.map +1 -1
  38. package/dist/model/model-metadata.json +9 -3
  39. package/dist/model/openai-adapter.js +2 -1
  40. package/dist/model/openai-adapter.js.map +1 -1
  41. package/dist/model/provider-factory.js +28 -1
  42. package/dist/model/provider-factory.js.map +1 -1
  43. package/dist/model/settings-files.d.ts +3 -0
  44. package/dist/model/settings-files.js +43 -0
  45. package/dist/model/settings-files.js.map +1 -0
  46. package/dist/model/settings-store.d.ts +84 -0
  47. package/dist/model/settings-store.js +191 -0
  48. package/dist/model/settings-store.js.map +1 -0
  49. package/dist/model/settings-sync.d.ts +23 -0
  50. package/dist/model/settings-sync.js +68 -0
  51. package/dist/model/settings-sync.js.map +1 -0
  52. package/dist/repl/commands.d.ts +1 -1
  53. package/dist/repl/commands.js +3 -2
  54. package/dist/repl/commands.js.map +1 -1
  55. package/dist/repl/index.js +50 -22
  56. package/dist/repl/index.js.map +1 -1
  57. package/dist/session/session-store.d.ts +2 -0
  58. package/dist/session/session-store.js +7 -1
  59. package/dist/session/session-store.js.map +1 -1
  60. package/dist/tasks/task-persistence.js +2 -1
  61. package/dist/tasks/task-persistence.js.map +1 -1
  62. package/dist/tools/builtins/image-generation-tool.js +2 -0
  63. package/dist/tools/builtins/image-generation-tool.js.map +1 -1
  64. package/dist/tools/builtins/search-providers.js +2 -0
  65. package/dist/tools/builtins/search-providers.js.map +1 -1
  66. package/dist/tools/builtins/search-tool.d.ts +2 -0
  67. package/dist/tools/builtins/search-tool.js +2 -2
  68. package/dist/tools/builtins/search-tool.js.map +1 -1
  69. package/dist/web/index.d.ts +9 -3
  70. package/dist/web/index.js +121 -29
  71. package/dist/web/index.js.map +1 -1
  72. package/dist/web/model-settings-api.d.ts +9 -0
  73. package/dist/web/model-settings-api.js +221 -0
  74. package/dist/web/model-settings-api.js.map +1 -0
  75. package/package.json +2 -2
  76. package/scripts/initialize-model-metadata.mjs +6 -0
@@ -11,7 +11,7 @@ import { QueryEngine } from "../core/query-engine.js";
11
11
  import { type AppPromptStore } from "../app/app-prompt.js";
12
12
  import { type ModelProviderName } from "../model/config.js";
13
13
  import { CommunicationLogger, LoggingModelGateway } from "../model/communication-logger.js";
14
- import type { ModelUsage, ReasoningConfig, ReasoningEffort } from "../model/model-gateway.js";
14
+ import type { ModelUsage, ReasoningConfig } from "../model/model-gateway.js";
15
15
  import { ToolRegistry } from "../tools/registry.js";
16
16
  import type { Tool, ToolUseContext } from "../tools/tool.js";
17
17
  import { type ToolPresentation } from "../tools/tool-catalog.js";
@@ -451,6 +451,7 @@ export declare class WebRepl {
451
451
  isDemo(engine?: QueryEngine): boolean;
452
452
  startDemo(expectedSessionId: string): Promise<WebActionResult>;
453
453
  private recordDemoInput;
454
+ private readonly settingsListener;
454
455
  constructor(runtime: WebRuntime, demoOptions?: {
455
456
  wait?: DemoWait;
456
457
  });
@@ -726,6 +727,7 @@ export declare class WebRepl {
726
727
  modelSettings: {
727
728
  model: string | undefined;
728
729
  reasoning: ReasoningConfig | null | undefined;
730
+ imageInput: boolean | undefined;
729
731
  };
730
732
  pendingSessionSettings: import("../core/query.js").SessionSettingsPatch & {
731
733
  sessionPrompt?: import("../core/session-settings-prompt.js").SessionPromptState | null;
@@ -735,12 +737,16 @@ export declare class WebRepl {
735
737
  catalog: {
736
738
  commands: import("../repl/commands.js").ReplCommandDefinition[];
737
739
  modelIds: string[];
740
+ modelProtocol: "openai" | "anthropic" | undefined;
738
741
  modelReasoning: {
739
- [k: string]: ReasoningEffort[];
742
+ [k: string]: string[];
743
+ };
744
+ modelReasoningEnabled: {
745
+ [k: string]: boolean;
740
746
  };
741
747
  reasoning: string[];
742
748
  envPath: string;
743
- } | undefined;
749
+ };
744
750
  interactive: {
745
751
  sessions: boolean;
746
752
  login: LoginFormPayload;
package/dist/web/index.js CHANGED
@@ -1,4 +1,11 @@
1
1
  #!/usr/bin/env node
2
+ import { trackSettingsGateway } from "../model/settings-sync.js";
3
+ import { observeModelSettings } from "../model/settings-sync.js";
4
+ import { modelProtocols } from "../model/context-window.js";
5
+ import { isReasoningEffort } from "../model/model-gateway.js";
6
+ import { ModelSettingsStore, compositionModelConfig } from "../model/settings-store.js";
7
+ import { registerCompositionTools } from "../model/composition-runtime.js";
8
+ import { handleModelSettingsRequest } from "./model-settings-api.js";
2
9
  import { paginateSessions, sessionPageOptions } from "../session/session-pagination.js";
3
10
  import { applyToolInputChanges } from "../model/tool-input-stream.js";
4
11
  import { toolActivity } from "./tool-activity.js";
@@ -29,7 +36,7 @@ import { loadDefaultDotEnvFiles } from "../model/env.js";
29
36
  import { readModelProviderConfig } from "../model/config.js";
30
37
  import { findModelMetadata, loadModelCatalog, reasoningEffortsForModel, resolveContextWindowTokens } from "../model/context-window.js";
31
38
  import { CommunicationLogger, LoggingModelGateway } from "../model/communication-logger.js";
32
- import { createModelGatewayFromConfig, createModelGatewayFromProcessEnv } from "../model/provider-factory.js";
39
+ import { createModelGatewayFromConfig } from "../model/provider-factory.js";
33
40
  import { ToolRegistry, isToolEnabledByDefault } from "../tools/registry.js";
34
41
  import { builtinToolPresentation, toolPresentationForSource } from "../tools/tool-catalog.js";
35
42
  import { editTool, writeTool } from "../tools/builtins/edit-tool.js";
@@ -147,9 +154,13 @@ function parseWebArgs(argv) {
147
154
  }
148
155
  export async function createWebRuntime(options = {}) {
149
156
  const envLoad = loadDefaultDotEnvFiles({ override: true });
150
- const modelConfig = readModelProviderConfig(process.env);
157
+ loadModelCatalog();
158
+ const settingsStore = new ModelSettingsStore();
159
+ const appliedSettings = settingsStore.read()?.applied;
160
+ const settingsRevisions = new WeakMap();
161
+ const modelConfig = appliedSettings ? compositionModelConfig(appliedSettings.composition, appliedSettings.connections) : readModelProviderConfig(process.env);
151
162
  const communicationLogger = new CommunicationLogger();
152
- const modelGateway = new LoggingModelGateway(createModelGatewayFromProcessEnv(process.env), communicationLogger);
163
+ const modelGateway = trackSettingsGateway(new LoggingModelGateway(createModelGatewayFromConfig(modelConfig), communicationLogger), appliedSettings?.revision);
153
164
  const taskStore = new TaskStore();
154
165
  const pluginCatalog = normalizeWebRuntimePlugins(options.externalPlugins);
155
166
  const pluginOverrides = normalizePluginOverrides(options.sessionPluginOverrides, pluginCatalog);
@@ -179,6 +190,13 @@ export async function createWebRuntime(options = {}) {
179
190
  registerTool(createLoadImageTool(), { source: "builtin" });
180
191
  if (modelConfig?.provider === "openai")
181
192
  registerTool(createOpenAIImageGenerationTool(), { source: "builtin" });
193
+ if (appliedSettings)
194
+ registerCompositionTools(tools, appliedSettings.composition, appliedSettings.connections);
195
+ for (const name of ["web_search", "image_create"]) {
196
+ const tool = tools.get(name);
197
+ if (tool)
198
+ catalogTool(tool, { source: "builtin" });
199
+ }
182
200
  registerTool(planTool, { source: "builtin" });
183
201
  const frameworkFeedback = new FrameworkFeedbackStore(options.frameworkFeedbackDir);
184
202
  registerTool(createFrameworkFeedbackTool(frameworkFeedback), { source: "builtin" });
@@ -217,6 +235,41 @@ export async function createWebRuntime(options = {}) {
217
235
  tools,
218
236
  contextManagerFactory: (cwd) => new DefaultContextManager({ cwd }),
219
237
  acquireTurnResources: (engine) => acquireWebPluginTurn(runtime, true, engine),
238
+ beforeQuery: (engine) => {
239
+ const applied = settingsStore.read()?.applied;
240
+ const captureTools = () => {
241
+ const snapshot = tools.clone();
242
+ compositionToolSnapshots.set(engine, snapshot);
243
+ const session = engine.snapshot().session?.sessionId;
244
+ if (session)
245
+ sessionCompositionSnapshots.set(session, new WeakRef(engine));
246
+ };
247
+ if (!applied) {
248
+ if (!compositionToolSnapshots.has(engine))
249
+ captureTools();
250
+ return;
251
+ }
252
+ const revision = applied.revision;
253
+ if (settingsRevisions.get(engine) === revision)
254
+ return;
255
+ const config = compositionModelConfig(applied.composition, applied.connections);
256
+ if (!config)
257
+ return;
258
+ const gateway = trackSettingsGateway(new LoggingModelGateway(createModelGatewayFromConfig(config), communicationLogger), revision);
259
+ engine.setModelProvider({ modelGateway: gateway, model: config.model, reasoning: config.defaultReasoning });
260
+ effectiveModelProviders.set(engine, config.provider);
261
+ registerCompositionTools(tools, applied.composition, applied.connections);
262
+ for (const name of ["web_search", "image_create"]) {
263
+ const tool = tools.get(name);
264
+ if (tool)
265
+ catalogTool(tool, { source: "builtin" });
266
+ }
267
+ runtime.modelGateway = gateway;
268
+ runtime.agentRuntime.modelGateway = gateway;
269
+ runtime.defaultReasoning = config.defaultReasoning;
270
+ settingsRevisions.set(engine, revision);
271
+ captureTools();
272
+ },
220
273
  refreshTools: (engine) => {
221
274
  if (runtime)
222
275
  refreshRuntimePlugins(runtime, engine);
@@ -353,6 +406,8 @@ function resolveRuntimePluginOverrides(runtime, engine, sessionId = engine.snaps
353
406
  return support.overrides;
354
407
  }
355
408
  /** Children acquire independent tools/prompts; they never refresh the parent's in-flight context. */
409
+ const compositionToolSnapshots = new WeakMap();
410
+ const sessionCompositionSnapshots = new Map();
356
411
  function acquireWebPluginTurn(runtime, main, engine = runtime.engine, sessionId) {
357
412
  if (main) {
358
413
  refreshRuntimePlugins(runtime, engine);
@@ -364,6 +419,22 @@ function acquireWebPluginTurn(runtime, main, engine = runtime.engine, sessionId)
364
419
  try {
365
420
  const catalog = normalizeWebRuntimePlugins(snapshot?.plugins ?? support?.resolveCatalog?.() ?? support?.catalog);
366
421
  const tools = buildPluginRegistry(runtime, catalog, overrides);
422
+ const owner = sessionId ? sessionCompositionSnapshots.get(sessionId)?.deref() : engine;
423
+ const frozen = owner ? compositionToolSnapshots.get(owner)?.clone() : undefined;
424
+ if (sessionId && !owner)
425
+ sessionCompositionSnapshots.delete(sessionId);
426
+ if (frozen)
427
+ for (const name of ["web_search", "image_create"]) {
428
+ const enabled = tools.isEnabled(name) ?? frozen.isEnabled(name);
429
+ frozen.setEnabled(name, true);
430
+ const tool = frozen.get(name);
431
+ tools.unregister(name);
432
+ if (tool) {
433
+ tools.register(tool);
434
+ if (enabled === false)
435
+ tools.setEnabled(name, false);
436
+ }
437
+ }
367
438
  return { tools, release: snapshot?.release ?? (() => { }),
368
439
  ...(!main ? { promptSections: [...(support?.basePromptSections ?? []), ...activeWebRuntimePlugins(catalog, overrides).flatMap(plugin => plugin.promptSections ?? [])] } : {}) };
369
440
  }
@@ -747,9 +818,17 @@ export class WebRepl {
747
818
  this.demoMessageQueues.delete(engine);
748
819
  }
749
820
  }
821
+ settingsListener = () => {
822
+ if (this.deletingSession)
823
+ return;
824
+ this.runtime.engine.refreshModelConfigurationIfIdle();
825
+ for (const subscriber of this.subscribers)
826
+ this.send(subscriber, "sync", this.snapshot());
827
+ };
750
828
  constructor(runtime, demoOptions = {}) {
751
829
  this.runtime = runtime;
752
830
  this.demoOptions = demoOptions;
831
+ observeModelSettings(this.settingsListener);
753
832
  const lineId = { current: 0 };
754
833
  this.lines = initialLines(runtime, lineId);
755
834
  this.lineId = lineId.current;
@@ -830,6 +909,7 @@ export class WebRepl {
830
909
  }
831
910
  snapshot(includeCatalog = false) {
832
911
  const backgroundTasks = this.backgroundTasks();
912
+ this.runtime.engine.refreshModelConfigurationIfIdle?.();
833
913
  const engineSnapshot = this.runtime.engine.snapshot();
834
914
  const pendingSessionSettings = this.runtime.engine.getPendingSessionSettings?.() ?? {};
835
915
  // Include retained reasoning alongside a queued model. Explicit undefined is
@@ -855,11 +935,11 @@ export class WebRepl {
855
935
  backgroundSessionRunCount: this.backgroundSessionRuns.size,
856
936
  runningSessionIds: this.runningSessionIds(),
857
937
  session: engineSnapshot.session,
858
- modelSettings: { model: engineSnapshot.model, reasoning: engineSnapshot.reasoning },
938
+ modelSettings: { model: engineSnapshot.model, reasoning: engineSnapshot.reasoning, imageInput: findModelMetadata(engineSnapshot.model || "")?.imageInput },
859
939
  pendingSessionSettings,
860
940
  fastMode: this.runtime.engine.isFastMode(),
861
941
  appPrompt: this.runtime.engine.getAppPrompt(),
862
- catalog: includeCatalog ? webCatalog(this.runtime) : undefined,
942
+ catalog: webCatalog(this.runtime),
863
943
  interactive: includeCatalog ? webInteractiveCatalog(this.runtime) : undefined,
864
944
  };
865
945
  }
@@ -1457,11 +1537,15 @@ export class WebRepl {
1457
1537
  if (typeof model !== "string" || !model.trim() || model.length > 256 || /[\s\x00-\x1f]/u.test(model.trim())) {
1458
1538
  return actionFailure("MODEL_INVALID", "invalid model");
1459
1539
  }
1460
- if (reasoning !== undefined && (typeof reasoning !== "string" || !["none", "minimal", "low", "medium", "high", "xhigh", "max", "default", "off"].includes(reasoning))) {
1540
+ if (reasoning !== undefined && (typeof reasoning !== "string" || !(reasoning === "default" || reasoning === "off" || isReasoningEffort(reasoning)))) {
1461
1541
  return actionFailure("MODEL_INVALID", "invalid reasoning effort");
1462
1542
  }
1463
1543
  const engine = this.runtime.engine;
1464
1544
  const nextModel = model.trim();
1545
+ const provider = effectiveModelProviders.get(engine) ?? readModelProviderConfig()?.provider;
1546
+ const metadata = (engine.getActiveModelCatalog?.() ?? loadModelCatalog()).models.find(m => m.modelIds.includes(nextModel));
1547
+ if (metadata && provider && !modelProtocols(metadata).includes(provider))
1548
+ return actionFailure("MODEL_INVALID", "模型不支持当前主连接接口");
1465
1549
  const current = { ...engine.getModelSettings(), ...engine.getPendingSessionSettings() };
1466
1550
  const argument = reasoning;
1467
1551
  const validationError = validateModelReasoningArgument(nextModel, argument);
@@ -1567,9 +1651,10 @@ export class WebRepl {
1567
1651
  async saveLogin(providerValue, values) {
1568
1652
  const provider = parseLoginProvider(providerValue);
1569
1653
  if (!provider)
1570
- return actionFailure("LOGIN_INVALID", "provider must be openai");
1654
+ return actionFailure("LOGIN_INVALID", "provider must be openai or anthropic");
1571
1655
  if (dockerEnabled() && !values.apiKey?.trim()) {
1572
- values = { ...values, apiKey: parseEnvFileSafe(this.runtime.envPath).OPENAI_API_KEY || process.env.OPENAI_API_KEY || "" };
1656
+ const key = provider === "anthropic" ? "ANTHROPIC_API_KEY" : "OPENAI_API_KEY";
1657
+ values = { ...values, apiKey: parseEnvFileSafe(this.runtime.envPath)[key] || process.env[key] || "" };
1573
1658
  }
1574
1659
  const payload = { ...createLoginFormPayload(this.runtime.envPath, provider), provider, values };
1575
1660
  const validationError = validateLoginFormPayload(payload);
@@ -2963,6 +3048,8 @@ export async function handleWebRequest(req, res, router) {
2963
3048
  }
2964
3049
  if (req.method === "POST" && url.pathname === "/api/sessions/new")
2965
3050
  return sendJson(res, await repl.newSession());
3051
+ if (await handleModelSettingsRequest(req, res, url))
3052
+ return;
2966
3053
  if (req.method === "GET" && url.pathname === "/api/login")
2967
3054
  return sendJson(res, repl.loginForm(url.searchParams.get("provider") ?? undefined));
2968
3055
  if (req.method === "POST" && url.pathname === "/api/login") {
@@ -3083,12 +3170,18 @@ function sanitizeWebAttachments(value) {
3083
3170
  return [];
3084
3171
  });
3085
3172
  }
3173
+ const effectiveModelProviders = new WeakMap();
3086
3174
  function webCatalog(runtime) {
3087
- const modelIds = [...new Set(loadModelCatalog().models.flatMap((model) => model.modelIds.length ? model.modelIds : [model.id]))].sort((left, right) => left.localeCompare(right));
3175
+ const catalog = runtime.engine.getActiveModelCatalog?.() ?? loadModelCatalog();
3176
+ const provider = effectiveModelProviders.get(runtime.engine) ?? readModelProviderConfig()?.provider;
3177
+ const models = catalog.models.filter(model => !provider || modelProtocols(model).includes(provider));
3178
+ const modelIds = [...new Set(models.flatMap(model => model.modelIds.length ? model.modelIds : [model.id]))].sort((left, right) => left.localeCompare(right, "en", { numeric: true }));
3088
3179
  return {
3089
3180
  commands: replCommandDefinitions,
3090
3181
  modelIds,
3091
- modelReasoning: Object.fromEntries(modelIds.map(model => [model, reasoningEffortsForModel(model) ?? []])),
3182
+ modelProtocol: provider,
3183
+ modelReasoning: Object.fromEntries(modelIds.map(model => [model, reasoningEffortsForModel(model, catalog) ?? []])),
3184
+ modelReasoningEnabled: Object.fromEntries(models.flatMap(m => m.modelIds.map(id => [id, m.reasoning !== false]))),
3092
3185
  reasoning: ["none", "minimal", "low", "medium", "high", "xhigh", "max", "default", "off"],
3093
3186
  envPath: runtime.envPath,
3094
3187
  };
@@ -3448,28 +3541,32 @@ async function persistModelCommandSettings(runtime, command, reasoningUpdate) {
3448
3541
  return { providerChanged: targetProvider !== currentProvider };
3449
3542
  }
3450
3543
  function currentModelProvider() {
3451
- return "openai";
3544
+ return parseLoginProvider(process.env.MODEL_PROVIDER) ?? "openai";
3452
3545
  }
3453
3546
  function parseLoginProvider(value) {
3454
- if (value === "openai")
3547
+ if (value === "openai" || value === "anthropic")
3455
3548
  return value;
3456
3549
  return undefined;
3457
3550
  }
3458
- const LOGIN_PROVIDERS = ["openai"];
3551
+ const LOGIN_PROVIDERS = ["openai", "anthropic"];
3459
3552
  const SHARED_LOGIN_FIELDS = [
3460
3553
  { key: "reasoningEffort", label: "Reasoning effort", envKey: "MODEL_REASONING_EFFORT", scope: "shared", options: ["", "off", "none", "minimal", "low", "medium", "high", "xhigh", "max"] },
3461
- { key: "reasoningSummary", label: "Reasoning summary", envKey: "MODEL_REASONING_SUMMARY", scope: "shared", options: ["", "auto", "concise", "detailed"] },
3462
- { key: "maxOutputTokens", label: "Max output tokens", envKey: "MODEL_MAX_OUTPUT_TOKENS", scope: "shared" },
3554
+ { key: "maxOutputTokens", label: "Max output tokens", envKey: "MODEL_MAX_OUTPUT_TOKENS", scope: "shared", placeholder: "Leave empty for server default" },
3463
3555
  { key: "timeoutMs", label: "Timeout ms", envKey: "MODEL_TIMEOUT_MS", scope: "shared", placeholder: "120000" },
3464
3556
  { key: "streamIdleTimeoutMs", label: "Stream idle timeout ms", envKey: "MODEL_STREAM_IDLE_TIMEOUT_MS", scope: "shared", placeholder: "120000" },
3465
3557
  { key: "maxRetries", label: "Max retries", envKey: "MODEL_MAX_RETRIES", scope: "shared", placeholder: "2" },
3466
3558
  ];
3467
3559
  const LOGIN_FIELD_DEFINITIONS = {
3560
+ anthropic: [
3561
+ { key: "apiKey", label: "API / CPA key", envKey: "ANTHROPIC_API_KEY", scope: "provider", required: true, secret: true },
3562
+ { key: "baseUrl", label: "Base URL", envKey: "ANTHROPIC_BASE_URL", scope: "provider", placeholder: "https://api.anthropic.com" },
3563
+ { key: "model", label: "Model", envKey: "ANTHROPIC_MODEL", scope: "provider", required: true, placeholder: "claude-sonnet-4-6" },
3564
+ ...SHARED_LOGIN_FIELDS.map(field => field.key === "reasoningEffort" ? { ...field, options: ["", "off", "none", "low", "medium", "high", "xhigh", "max"] } : field.key === "maxOutputTokens" ? { ...field, placeholder: "8192" } : field),
3565
+ ],
3468
3566
  openai: [
3469
3567
  { key: "apiKey", label: "API key", envKey: "OPENAI_API_KEY", scope: "provider", required: true, secret: true, placeholder: "sk-..." },
3470
3568
  { key: "baseUrl", label: "Base URL", envKey: "OPENAI_BASE_URL", scope: "provider", placeholder: "https://api.openai.com" },
3471
3569
  { key: "model", label: "Model", envKey: "OPENAI_MODEL", scope: "provider", required: true, placeholder: "gpt-5.6" },
3472
- { key: "endpoint", label: "Endpoint", envKey: "OPENAI_ENDPOINT", scope: "provider", placeholder: "auto", options: ["auto", "responses", "chat"] },
3473
3570
  ...SHARED_LOGIN_FIELDS,
3474
3571
  ],
3475
3572
  };
@@ -3496,18 +3593,16 @@ function loginValuesForProvider(provider, env) {
3496
3593
  values.baseUrl = defaultBaseUrlForLoginProvider(provider);
3497
3594
  if (!values.model)
3498
3595
  values.model = defaultModelForLoginProvider(provider);
3499
- if (provider === "openai" && !values.endpoint)
3500
- values.endpoint = "auto";
3501
3596
  return values;
3502
3597
  }
3503
- function guessLoginProvider(_env) {
3504
- return currentModelProvider();
3598
+ function guessLoginProvider(env) {
3599
+ return env.ANTHROPIC_API_KEY && !env.OPENAI_API_KEY ? "anthropic" : currentModelProvider();
3505
3600
  }
3506
- function defaultBaseUrlForLoginProvider(_provider) {
3507
- return "https://api.openai.com";
3601
+ function defaultBaseUrlForLoginProvider(provider) {
3602
+ return provider === "anthropic" ? "https://api.anthropic.com" : "https://api.openai.com";
3508
3603
  }
3509
- function defaultModelForLoginProvider(_provider) {
3510
- return "gpt-5.6";
3604
+ function defaultModelForLoginProvider(provider) {
3605
+ return provider === "anthropic" ? "claude-sonnet-4-6" : "gpt-5.6";
3511
3606
  }
3512
3607
  function validateLoginFormPayload(payload) {
3513
3608
  for (const field of LOGIN_FIELD_DEFINITIONS[payload.provider]) {
@@ -3522,9 +3617,6 @@ function validateLoginFormPayload(payload) {
3522
3617
  if (value && !Number.isFinite(Number(value)))
3523
3618
  return `${fieldKey} must be a number.`;
3524
3619
  }
3525
- if (payload.provider === "openai" && payload.values.endpoint === "chat" && payload.values.reasoningSummary?.trim()) {
3526
- return "Reasoning summary requires the Responses API endpoint.";
3527
- }
3528
3620
  return undefined;
3529
3621
  }
3530
3622
  async function saveLoginPayloadToEnv(payload) {
@@ -3559,8 +3651,8 @@ function stripEnvQuotes(value) {
3559
3651
  return value.slice(1, -1);
3560
3652
  return value;
3561
3653
  }
3562
- function modelEnvKeyForProvider(_provider) {
3563
- return "OPENAI_MODEL";
3654
+ function modelEnvKeyForProvider(provider) {
3655
+ return provider === "anthropic" ? "ANTHROPIC_MODEL" : "OPENAI_MODEL";
3564
3656
  }
3565
3657
  function envValueForReasoning(reasoning) {
3566
3658
  if (reasoning === null)