@charzhu/openjaw-agent 0.2.4 → 0.2.6

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/dist/main.js CHANGED
@@ -123,6 +123,7 @@ function loadAgentConfig() {
123
123
  temperature: parsedLlm?.temperature ?? DEFAULT_CONFIG.llm.temperature,
124
124
  computer_use: parsedLlm?.computer_use ?? DEFAULT_CONFIG.llm.computer_use,
125
125
  use_responses_api: parsedLlm?.use_responses_api ?? DEFAULT_CONFIG.llm.use_responses_api,
126
+ model_reasoning_effort: parsedLlm?.model_reasoning_effort,
126
127
  openai_tool_mode: parsedLlm?.openai_tool_mode ?? DEFAULT_CONFIG.llm.openai_tool_mode,
127
128
  openai_max_tools: parsedLlm?.openai_max_tools ?? DEFAULT_CONFIG.llm.openai_max_tools,
128
129
  openai_mcp_max_tools: parsedLlm?.openai_mcp_max_tools,
@@ -1973,6 +1974,55 @@ var init_provider_auth = __esm({
1973
1974
  }
1974
1975
  });
1975
1976
 
1977
+ // src/providers/types.ts
1978
+ function isReasoningEffort(value) {
1979
+ return REASONING_EFFORTS.includes(value);
1980
+ }
1981
+ function reasoningEffortLabel(effort) {
1982
+ switch (effort) {
1983
+ case "none":
1984
+ return "None";
1985
+ case "minimal":
1986
+ return "Minimal";
1987
+ case "low":
1988
+ return "Low";
1989
+ case "medium":
1990
+ return "Medium";
1991
+ case "high":
1992
+ return "High";
1993
+ case "xhigh":
1994
+ return "Extra high";
1995
+ }
1996
+ }
1997
+ function normalizeReasoningEfforts(values) {
1998
+ if (!Array.isArray(values)) return [];
1999
+ const efforts = [];
2000
+ for (const item of values) {
2001
+ if (typeof item !== "string" || !isReasoningEffort(item)) continue;
2002
+ if (!efforts.includes(item)) efforts.push(item);
2003
+ }
2004
+ return efforts;
2005
+ }
2006
+ function defaultReasoningEffort(efforts) {
2007
+ if (efforts.length === 0) return void 0;
2008
+ return efforts.includes("medium") ? "medium" : efforts[0];
2009
+ }
2010
+ function orderedReasoningEfforts(efforts) {
2011
+ return REASONING_EFFORTS.filter((effort) => efforts.includes(effort));
2012
+ }
2013
+ var REASONING_EFFORTS;
2014
+ var init_types = __esm({
2015
+ "src/providers/types.ts"() {
2016
+ "use strict";
2017
+ REASONING_EFFORTS = ["none", "minimal", "low", "medium", "high", "xhigh"];
2018
+ __name(isReasoningEffort, "isReasoningEffort");
2019
+ __name(reasoningEffortLabel, "reasoningEffortLabel");
2020
+ __name(normalizeReasoningEfforts, "normalizeReasoningEfforts");
2021
+ __name(defaultReasoningEffort, "defaultReasoningEffort");
2022
+ __name(orderedReasoningEfforts, "orderedReasoningEfforts");
2023
+ }
2024
+ });
2025
+
1976
2026
  // src/providers/copilot.ts
1977
2027
  function normalizeCopilotEnterpriseDomain(value) {
1978
2028
  return value.replace(/^https?:\/\//, "").replace(/\/+$/, "");
@@ -2063,20 +2113,67 @@ function safeJsonParse(value) {
2063
2113
  return {};
2064
2114
  }
2065
2115
  }
2066
- function parseModels(body) {
2116
+ function valueAtPath(value, path3) {
2117
+ let current = value;
2118
+ for (const key of path3) {
2119
+ if (!current || typeof current !== "object" || Array.isArray(current)) return void 0;
2120
+ current = current[key];
2121
+ }
2122
+ return current;
2123
+ }
2124
+ function firstPositiveIntegerAtAny(value, paths2) {
2125
+ for (const path3 of paths2) {
2126
+ const candidate = valueAtPath(value, path3);
2127
+ if (typeof candidate !== "number" || !Number.isFinite(candidate) || candidate <= 0) continue;
2128
+ return Math.trunc(candidate);
2129
+ }
2130
+ return void 0;
2131
+ }
2132
+ function readCopilotContextWindow(capabilities) {
2133
+ return firstPositiveIntegerAtAny(capabilities, [
2134
+ ["limits", "max_context_window_tokens"],
2135
+ ["limits", "maxContextWindowTokens"],
2136
+ ["limits", "max_prompt_tokens"],
2137
+ ["max_context_window_tokens"],
2138
+ ["maxContextWindowTokens"],
2139
+ ["context_window"],
2140
+ ["contextWindow"]
2141
+ ]);
2142
+ }
2143
+ function readCopilotOutputTokens(capabilities) {
2144
+ return firstPositiveIntegerAtAny(capabilities, [
2145
+ ["limits", "max_output_tokens"],
2146
+ ["limits", "maxOutputTokens"],
2147
+ ["max_output_tokens"],
2148
+ ["maxOutputTokens"]
2149
+ ]);
2150
+ }
2151
+ function readCopilotReasoningEfforts(capabilities) {
2152
+ return normalizeReasoningEfforts(valueAtPath(capabilities, ["supports", "reasoning_effort"]));
2153
+ }
2154
+ function buildReasoning(effort, modelInfo) {
2155
+ const supported = modelInfo?.supportedReasoningEfforts?.map((option) => option.effort) ?? [];
2156
+ const selected = effort && supported.includes(effort) ? effort : void 0;
2157
+ const effective = selected ?? modelInfo?.defaultReasoningEffort;
2158
+ return effective ? { effort: effective } : void 0;
2159
+ }
2160
+ function parseCopilotModels(body) {
2067
2161
  const items = Array.isArray(body.data) ? body.data : [];
2068
2162
  const models = [];
2069
2163
  for (const item of items) {
2070
2164
  if (!item.id || item.model_picker_enabled === false || item.policy?.state === "disabled") continue;
2071
2165
  const visionMedia = item.capabilities?.limits?.vision?.supported_media_types ?? [];
2166
+ const supportedReasoning = readCopilotReasoningEfforts(item.capabilities);
2072
2167
  models.push({
2073
2168
  id: item.id,
2074
2169
  name: item.name,
2075
2170
  supportedEndpoints: item.supported_endpoints ?? [],
2076
2171
  supportsVision: item.capabilities?.supports?.vision === true || visionMedia.some((media) => media.startsWith("image/")),
2077
2172
  supportsToolCalls: item.capabilities?.supports?.tool_calls !== false,
2078
- contextWindow: item.capabilities?.limits?.max_context_window_tokens,
2079
- outputTokens: item.capabilities?.limits?.max_output_tokens
2173
+ contextWindow: readCopilotContextWindow(item.capabilities),
2174
+ outputTokens: readCopilotOutputTokens(item.capabilities),
2175
+ supportedReasoningEfforts: supportedReasoning.map((effort) => ({ effort, description: effort })),
2176
+ defaultReasoningEffort: defaultReasoningEffort(supportedReasoning)
2080
2177
  });
2081
2178
  }
2082
2179
  return models;
@@ -2086,6 +2183,7 @@ var init_copilot = __esm({
2086
2183
  "src/providers/copilot.ts"() {
2087
2184
  "use strict";
2088
2185
  init_provider_auth();
2186
+ init_types();
2089
2187
  COPILOT_PROVIDER = "github-copilot";
2090
2188
  USER_AGENT = "openjaw-agent/0.1.0";
2091
2189
  OPENAI_TOOL_NAME2 = /^[A-Za-z0-9_-]{1,64}$/;
@@ -2100,7 +2198,13 @@ var init_copilot = __esm({
2100
2198
  __name(hasImageContent, "hasImageContent");
2101
2199
  __name(openAIUserContent, "openAIUserContent");
2102
2200
  __name(safeJsonParse, "safeJsonParse");
2103
- __name(parseModels, "parseModels");
2201
+ __name(valueAtPath, "valueAtPath");
2202
+ __name(firstPositiveIntegerAtAny, "firstPositiveIntegerAtAny");
2203
+ __name(readCopilotContextWindow, "readCopilotContextWindow");
2204
+ __name(readCopilotOutputTokens, "readCopilotOutputTokens");
2205
+ __name(readCopilotReasoningEfforts, "readCopilotReasoningEfforts");
2206
+ __name(buildReasoning, "buildReasoning");
2207
+ __name(parseCopilotModels, "parseCopilotModels");
2104
2208
  CopilotProvider = class {
2105
2209
  static {
2106
2210
  __name(this, "CopilotProvider");
@@ -2113,7 +2217,14 @@ var init_copilot = __esm({
2113
2217
  }
2114
2218
  async listModels() {
2115
2219
  const models = await this.fetchModelInfo();
2116
- return models.map((model) => ({ id: model.id, name: model.name ?? model.id }));
2220
+ return models.map((model) => ({
2221
+ id: model.id,
2222
+ name: model.name ?? model.id,
2223
+ contextWindow: model.contextWindow,
2224
+ outputTokens: model.outputTokens,
2225
+ supportedReasoningEfforts: model.supportedReasoningEfforts,
2226
+ defaultReasoningEffort: model.defaultReasoningEffort
2227
+ }));
2117
2228
  }
2118
2229
  async chat(options) {
2119
2230
  const modelInfo = await this.resolveModelInfo(this.config.model);
@@ -2121,7 +2232,7 @@ var init_copilot = __esm({
2121
2232
  return this.chatAnthropicMessages(options);
2122
2233
  }
2123
2234
  if (this.shouldRouteToResponses(modelInfo)) {
2124
- return this.chatResponses(options);
2235
+ return this.chatResponses(options, modelInfo);
2125
2236
  }
2126
2237
  return this.chatCompletions(options);
2127
2238
  }
@@ -2178,7 +2289,7 @@ var init_copilot = __esm({
2178
2289
  throw new Error(`GitHub Copilot models error: ${res.status}${detail ? ` ${detail.slice(0, 160)}` : ""}`);
2179
2290
  }
2180
2291
  const parsed = await res.json();
2181
- const models = parseModels(parsed);
2292
+ const models = parseCopilotModels(parsed);
2182
2293
  this.modelCache = new Map(models.map((model) => [model.id, model]));
2183
2294
  return models;
2184
2295
  }
@@ -2294,12 +2405,14 @@ var init_copilot = __esm({
2294
2405
  }
2295
2406
  return input;
2296
2407
  }
2297
- async chatResponses(options) {
2408
+ async chatResponses(options, modelInfo) {
2409
+ const reasoning = buildReasoning(options.reasoningEffort, modelInfo);
2298
2410
  const requestBody = {
2299
2411
  model: this.config.model,
2300
2412
  input: this.buildResponsesInput(options),
2301
2413
  instructions: Array.isArray(options.systemPrompt) ? options.systemPrompt.map((block) => block.text).join("\n\n") : options.systemPrompt,
2302
- tools: options.tools.length > 0 ? options.tools.map(toResponsesTool) : void 0
2414
+ tools: options.tools.length > 0 ? options.tools.map(toResponsesTool) : void 0,
2415
+ ...reasoning && { reasoning, include: ["reasoning.encrypted_content"] }
2303
2416
  };
2304
2417
  const res = await fetch(`${this.baseUrl()}/responses`, {
2305
2418
  method: "POST",
@@ -2691,7 +2804,16 @@ var init_cost_tracker = __esm({
2691
2804
  });
2692
2805
 
2693
2806
  // src/context-manager.ts
2694
- function getContextWindow(model) {
2807
+ function contextKey(provider, model) {
2808
+ return `${provider}\0${model}`;
2809
+ }
2810
+ function normalizeContextWindow(value) {
2811
+ if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) return void 0;
2812
+ return Math.trunc(value);
2813
+ }
2814
+ function getContextWindow(model, metadata) {
2815
+ const liveContextWindow = normalizeContextWindow(metadata?.contextWindow);
2816
+ if (liveContextWindow !== void 0) return liveContextWindow;
2695
2817
  if (CONTEXT_WINDOWS[model]) return CONTEXT_WINDOWS[model];
2696
2818
  for (const [key, size] of Object.entries(CONTEXT_WINDOWS)) {
2697
2819
  if (model.startsWith(key)) return size;
@@ -2716,12 +2838,45 @@ var init_context_manager = __esm({
2716
2838
  "gpt-4o": 128e3
2717
2839
  };
2718
2840
  DEFAULT_CONTEXT_WINDOW = 2e5;
2841
+ __name(contextKey, "contextKey");
2842
+ __name(normalizeContextWindow, "normalizeContextWindow");
2719
2843
  __name(getContextWindow, "getContextWindow");
2720
2844
  ContextManager = class {
2721
2845
  static {
2722
2846
  __name(this, "ContextManager");
2723
2847
  }
2724
2848
  lastTotalTokens = 0;
2849
+ activeProvider = null;
2850
+ liveModelContextWindows = /* @__PURE__ */ new Map();
2851
+ setActiveProvider(provider) {
2852
+ this.activeProvider = provider;
2853
+ }
2854
+ setProviderModelMetadata(provider, models) {
2855
+ const prefix = `${provider}\0`;
2856
+ for (const key of [...this.liveModelContextWindows.keys()]) {
2857
+ if (key.startsWith(prefix)) this.liveModelContextWindows.delete(key);
2858
+ }
2859
+ for (const model of models ?? []) {
2860
+ const contextWindow = normalizeContextWindow(model.contextWindow);
2861
+ if (contextWindow !== void 0) {
2862
+ this.liveModelContextWindows.set(contextKey(provider, model.id), contextWindow);
2863
+ }
2864
+ }
2865
+ }
2866
+ clearProviderModelMetadata(provider) {
2867
+ if (!provider) {
2868
+ this.liveModelContextWindows.clear();
2869
+ return;
2870
+ }
2871
+ const prefix = `${provider}\0`;
2872
+ for (const key of [...this.liveModelContextWindows.keys()]) {
2873
+ if (key.startsWith(prefix)) this.liveModelContextWindows.delete(key);
2874
+ }
2875
+ }
2876
+ getContextWindow(model) {
2877
+ const liveContextWindow = this.activeProvider ? this.liveModelContextWindows.get(contextKey(this.activeProvider, model)) : void 0;
2878
+ return getContextWindow(model, { contextWindow: liveContextWindow });
2879
+ }
2725
2880
  /** Update from actual API response usage */
2726
2881
  updateFromUsage(usage2) {
2727
2882
  this.lastTotalTokens = usage2.inputTokens + usage2.outputTokens + usage2.cacheReadTokens + usage2.cacheCreationTokens;
@@ -2733,7 +2888,7 @@ var init_context_manager = __esm({
2733
2888
  /** Get warning level based on model context window */
2734
2889
  getWarningLevel(model, newContentChars = 0) {
2735
2890
  const estimated = this.getEstimatedTokens(newContentChars);
2736
- const limit = getContextWindow(model);
2891
+ const limit = this.getContextWindow(model);
2737
2892
  const ratio = estimated / limit;
2738
2893
  if (ratio > 0.95) return "critical";
2739
2894
  if (ratio > 0.85) return "warning";
@@ -2741,12 +2896,12 @@ var init_context_manager = __esm({
2741
2896
  }
2742
2897
  /** Get context usage as percentage */
2743
2898
  getUsagePercent(model) {
2744
- const limit = getContextWindow(model);
2899
+ const limit = this.getContextWindow(model);
2745
2900
  return limit > 0 ? Math.round(this.lastTotalTokens / limit * 100) : 0;
2746
2901
  }
2747
2902
  /** Format context display string */
2748
2903
  formatContext(model) {
2749
- const limit = getContextWindow(model);
2904
+ const limit = this.getContextWindow(model);
2750
2905
  const fmt2 = /* @__PURE__ */ __name((n) => n >= 1e3 ? `${(n / 1e3).toFixed(0)}K` : String(n), "fmt");
2751
2906
  const pct = this.getUsagePercent(model);
2752
2907
  return `${fmt2(this.lastTotalTokens)}/${fmt2(limit)} (${pct}%)`;
@@ -3096,7 +3251,6 @@ var init_context_compressor = __esm({
3096
3251
  "src/context-compressor.ts"() {
3097
3252
  "use strict";
3098
3253
  init_providers();
3099
- init_context_manager();
3100
3254
  DEFAULT_COMPRESSION_THRESHOLD = 0.7;
3101
3255
  PROTECTED_HEAD = 3;
3102
3256
  PROTECTED_TAIL_TOKENS = 2e4;
@@ -3188,7 +3342,7 @@ Rules:
3188
3342
  shouldCompress(threshold) {
3189
3343
  const t = threshold ?? this.config.llm.compression_threshold ?? DEFAULT_COMPRESSION_THRESHOLD;
3190
3344
  const model = this.config.llm.model;
3191
- const limit = getContextWindow(model);
3345
+ const limit = this.contextManager.getContextWindow(model);
3192
3346
  const current = this.contextManager.getEstimatedTokens();
3193
3347
  const ratio = current / limit;
3194
3348
  if (ratio < t) return false;
@@ -3281,7 +3435,7 @@ ${summary}
3281
3435
  async _summarize(middle, _systemPrompt) {
3282
3436
  const auxProvider = this._getAuxProvider();
3283
3437
  const middleText = buildMiddleText(middle);
3284
- const contextWindow = getContextWindow(this.config.llm.model);
3438
+ const contextWindow = this.contextManager.getContextWindow(this.config.llm.model);
3285
3439
  const targetTokens = Math.max(
3286
3440
  SUMMARY_MIN_TOKENS,
3287
3441
  Math.min(SUMMARY_MAX_TOKENS, Math.floor(contextWindow * SUMMARY_PCT))
@@ -4129,6 +4283,7 @@ var init_agent_loop = __esm({
4129
4283
  cacheMonitor;
4130
4284
  telemetry;
4131
4285
  contextCompressor;
4286
+ liveModelMetadata = /* @__PURE__ */ new Map();
4132
4287
  _compactedOnResume = false;
4133
4288
  _toolRoundsInRun = 0;
4134
4289
  _skillSuggested = false;
@@ -4146,6 +4301,7 @@ var init_agent_loop = __esm({
4146
4301
  this.provider = createProvider(config);
4147
4302
  this.costTracker = new CostTracker();
4148
4303
  this.contextManager = new ContextManager();
4304
+ this.contextManager.setActiveProvider(config.llm.provider);
4149
4305
  this.cacheMonitor = new CacheMonitor();
4150
4306
  this.telemetry = new Telemetry("pending");
4151
4307
  if (resumeSessionId) {
@@ -4162,6 +4318,7 @@ var init_agent_loop = __esm({
4162
4318
  this.telemetry = new Telemetry(this.session.id);
4163
4319
  this.telemetry.recordSessionStart(config.llm.model, config.llm.provider);
4164
4320
  this.contextCompressor = new ContextCompressor(config, this.contextManager, this.telemetry);
4321
+ void this.refreshActiveModelMetadata();
4165
4322
  }
4166
4323
  get tools() {
4167
4324
  return this.toolRegistry;
@@ -4191,6 +4348,9 @@ var init_agent_loop = __esm({
4191
4348
  }
4192
4349
  return this.config.llm.provider;
4193
4350
  }
4351
+ get reasoningEffort() {
4352
+ return this.config.llm.model_reasoning_effort;
4353
+ }
4194
4354
  /**
4195
4355
  * Provide the user's response to an ask_user question.
4196
4356
  * Called by Chat.tsx or bridges when the user answers.
@@ -4208,7 +4368,7 @@ var init_agent_loop = __esm({
4208
4368
  return this._waitingForAskUserFlag;
4209
4369
  }
4210
4370
  /** Switch provider and/or model at runtime */
4211
- switchModel(provider, model) {
4371
+ switchModel(provider, model, reasoningEffort) {
4212
4372
  let baseUrl = this.config.llm.base_url;
4213
4373
  if (baseUrl) {
4214
4374
  if (provider === "openai" && baseUrl.includes("/api/anthropic")) {
@@ -4223,14 +4383,26 @@ var init_agent_loop = __esm({
4223
4383
  }
4224
4384
  this.config = {
4225
4385
  ...this.config,
4226
- llm: { ...this.config.llm, provider, model, base_url: baseUrl }
4386
+ llm: { ...this.config.llm, provider, model, base_url: baseUrl, model_reasoning_effort: reasoningEffort }
4227
4387
  };
4228
4388
  this.provider = createProvider(this.config);
4389
+ this.contextManager.setActiveProvider(provider);
4229
4390
  this._toolExposureState = createToolExposureState();
4230
4391
  this.session.provider = provider;
4231
4392
  this.session.model = model;
4232
4393
  saveSession(this.session);
4233
4394
  saveAgentConfig(this.config);
4395
+ void this.refreshActiveModelMetadata();
4396
+ }
4397
+ updateReasoningEffort(reasoningEffort) {
4398
+ this.config = {
4399
+ ...this.config,
4400
+ llm: { ...this.config.llm, model_reasoning_effort: reasoningEffort }
4401
+ };
4402
+ this.session.provider = this.config.llm.provider;
4403
+ this.session.model = this.config.llm.model;
4404
+ saveSession(this.session);
4405
+ saveAgentConfig(this.config);
4234
4406
  }
4235
4407
  /** Apply provider config changes that are not expressible as a model-only switch. */
4236
4408
  updateProviderConfig(updates) {
@@ -4239,15 +4411,53 @@ var init_agent_loop = __esm({
4239
4411
  llm: { ...this.config.llm, ...updates }
4240
4412
  };
4241
4413
  this.provider = createProvider(this.config);
4414
+ this.contextManager.setActiveProvider(this.config.llm.provider);
4242
4415
  this._toolExposureState = createToolExposureState();
4243
4416
  this.session.provider = this.config.llm.provider;
4244
4417
  this.session.model = this.config.llm.model;
4245
4418
  saveSession(this.session);
4246
4419
  saveAgentConfig(this.config);
4420
+ void this.refreshActiveModelMetadata();
4247
4421
  }
4248
4422
  /** List available models from the current provider's API */
4249
4423
  async listModels() {
4250
- return this.provider.listModels?.() ?? null;
4424
+ const models = await this.provider.listModels?.() ?? null;
4425
+ if (models) this.setProviderModelMetadata(this.config.llm.provider, models);
4426
+ return models;
4427
+ }
4428
+ setProviderModelMetadata(provider, models) {
4429
+ const prefix = `${provider}\0`;
4430
+ for (const key of [...this.liveModelMetadata.keys()]) {
4431
+ if (key.startsWith(prefix)) this.liveModelMetadata.delete(key);
4432
+ }
4433
+ for (const model of models ?? []) {
4434
+ this.liveModelMetadata.set(`${provider}\0${model.id}`, model);
4435
+ }
4436
+ this.contextManager.setProviderModelMetadata(provider, models);
4437
+ }
4438
+ getActiveModelMetadata(model = this.config.llm.model) {
4439
+ return this.getModelMetadata(this.config.llm.provider, model);
4440
+ }
4441
+ getModelMetadata(provider, model) {
4442
+ return this.liveModelMetadata.get(`${provider}\0${model}`);
4443
+ }
4444
+ async refreshActiveModelMetadata() {
4445
+ const providerName = this.config.llm.provider;
4446
+ const provider = this.provider;
4447
+ this.contextManager.setActiveProvider(providerName);
4448
+ if (providerName !== "github-copilot") return;
4449
+ try {
4450
+ const models = await provider.listModels?.() ?? null;
4451
+ if (models) {
4452
+ this.setProviderModelMetadata(providerName, models);
4453
+ } else {
4454
+ this.setProviderModelMetadata(providerName, null);
4455
+ this.contextManager.clearProviderModelMetadata(providerName);
4456
+ }
4457
+ } catch {
4458
+ this.setProviderModelMetadata(providerName, null);
4459
+ this.contextManager.clearProviderModelMetadata(providerName);
4460
+ }
4251
4461
  }
4252
4462
  async listAllModels() {
4253
4463
  const { STATIC_MODELS: STATIC_MODELS2 } = await Promise.resolve().then(() => (init_models_static(), models_static_exports));
@@ -4278,13 +4488,16 @@ var init_agent_loop = __esm({
4278
4488
  const seen = /* @__PURE__ */ new Set();
4279
4489
  if (live && live.length > 0) {
4280
4490
  sources[currentProvider] = { source: "live" };
4491
+ this.setProviderModelMetadata(currentProvider, live);
4281
4492
  for (const m of live) {
4282
4493
  if (seen.has(m.id)) continue;
4283
4494
  seen.add(m.id);
4284
- models.push({ provider: currentProvider, id: m.id, name: m.name, source: "live" });
4495
+ models.push({ provider: currentProvider, id: m.id, name: m.name, contextWindow: m.contextWindow, outputTokens: m.outputTokens, source: "live" });
4285
4496
  }
4286
4497
  } else {
4287
4498
  sources[currentProvider] = { source: "static", error: lastError };
4499
+ this.setProviderModelMetadata(currentProvider, null);
4500
+ this.contextManager.clearProviderModelMetadata(currentProvider);
4288
4501
  for (const m of STATIC_MODELS2[currentProvider]) {
4289
4502
  if (seen.has(m.id)) continue;
4290
4503
  seen.add(m.id);
@@ -4328,13 +4541,16 @@ var init_agent_loop = __esm({
4328
4541
  const seen = new Set(models.filter((m) => m.provider === provider && !m.group).map((m) => m.id));
4329
4542
  if (live && live.length > 0) {
4330
4543
  sources[provider] = { source: "live" };
4544
+ this.setProviderModelMetadata(provider, live);
4331
4545
  for (const m of live) {
4332
4546
  if (seen.has(m.id)) continue;
4333
4547
  seen.add(m.id);
4334
- models.push({ provider, id: m.id, name: m.name, source: "live" });
4548
+ models.push({ provider, id: m.id, name: m.name, contextWindow: m.contextWindow, outputTokens: m.outputTokens, source: "live" });
4335
4549
  }
4336
4550
  } else {
4337
4551
  sources[provider] = { source: "static", error: lastError };
4552
+ this.setProviderModelMetadata(provider, null);
4553
+ this.contextManager.clearProviderModelMetadata(provider);
4338
4554
  for (const m of STATIC_MODELS2[provider]) {
4339
4555
  if (seen.has(m.id)) continue;
4340
4556
  seen.add(m.id);
@@ -4592,6 +4808,7 @@ ${summary}
4592
4808
  messages,
4593
4809
  tools,
4594
4810
  signal,
4811
+ reasoningEffort: this.config.llm.model_reasoning_effort,
4595
4812
  debug: {
4596
4813
  toolMode: exposure.mode,
4597
4814
  fullToolCount: exposure.fullToolCount,
@@ -19486,7 +19703,8 @@ var init_user = __esm({
19486
19703
  var memory_exports = {};
19487
19704
  __export(memory_exports, {
19488
19705
  getMemorySection: () => getMemorySection,
19489
- setMemoryPrefetchQuery: () => setMemoryPrefetchQuery
19706
+ setMemoryPrefetchQuery: () => setMemoryPrefetchQuery,
19707
+ shouldSkipPrefetch: () => shouldSkipPrefetch
19490
19708
  });
19491
19709
  import { join as join21 } from "node:path";
19492
19710
  import { homedir as homedir11 } from "node:os";
@@ -19496,8 +19714,48 @@ function setMemoryPrefetchQuery(query) {
19496
19714
  function escapeLike2(token) {
19497
19715
  return `%${token.replace(/[\\%_]/g, "\\$&")}%`;
19498
19716
  }
19717
+ function shouldSkipPrefetch(query) {
19718
+ const trimmed = query.trim();
19719
+ if (trimmed.length === 0) return true;
19720
+ if (/^[\d\s+\-*/().%^=]+$/.test(trimmed)) return true;
19721
+ if (/^[a-z][a-z0-9_-]*( +-{0,2}[a-z0-9_./-]+){0,3}$/.test(trimmed) && trimmed.length <= 32 && !/\s+(my|i|me|the|a|an|alice|bob|carol|dave)\b/i.test(trimmed)) {
19722
+ return true;
19723
+ }
19724
+ const codeMarkers = [
19725
+ /\bfunction\s+\w+\s*\(/,
19726
+ // function foo(
19727
+ /=>/,
19728
+ // arrow fn
19729
+ /\bimport\s+[\w*{]/,
19730
+ // import x
19731
+ /\bfrom\s+['"][\w./-]+['"]/,
19732
+ // from "x"
19733
+ /\bclass\s+\w+/,
19734
+ // class Foo
19735
+ /\bdef\s+\w+\s*\(/,
19736
+ // def foo(
19737
+ /\bconst\s+\w+\s*=/,
19738
+ // const x =
19739
+ /\blet\s+\w+\s*=/,
19740
+ // let x =
19741
+ /\bvar\s+\w+\s*=/,
19742
+ // var x =
19743
+ /\binterface\s+\w+/,
19744
+ // interface Foo
19745
+ /\btype\s+\w+\s*=/
19746
+ // type Foo =
19747
+ ];
19748
+ if (codeMarkers.some((re) => re.test(trimmed))) return true;
19749
+ const hasFileExt = /\b[\w.-]+\.(ts|tsx|js|jsx|mjs|cjs|py|rb|go|rs|java|c|cpp|h|md|json|yaml|yml|toml|sh|ps1|css|scss|html|xml|sql)\b/i.test(trimmed);
19750
+ if (hasFileExt) {
19751
+ const personalSignal = /\b(my|wife|husband|manager|boss|team|alice|bob|carol|dave|remember|usual|standard|default|reply|template|email|outlook|teams|workiq|msvacation|icm|servicenow)\b/i.test(trimmed);
19752
+ if (!personalSignal) return true;
19753
+ }
19754
+ return false;
19755
+ }
19499
19756
  function getMemorySection() {
19500
19757
  if (!currentQuery) return null;
19758
+ if (shouldSkipPrefetch(currentQuery)) return null;
19501
19759
  try {
19502
19760
  const { DatabaseSync: DatabaseSync2 } = __require("node:sqlite");
19503
19761
  const dbPath = join21(homedir11(), ".openjaw", "memory.db");
@@ -19560,8 +19818,8 @@ var MAX_PREFETCH_RESULTS, MAX_PREFETCH_CHARS, MAX_LIKE_TERMS2, STOPWORDS2, curre
19560
19818
  var init_memory2 = __esm({
19561
19819
  "src/prompts/memory.ts"() {
19562
19820
  "use strict";
19563
- MAX_PREFETCH_RESULTS = 10;
19564
- MAX_PREFETCH_CHARS = 4e3;
19821
+ MAX_PREFETCH_RESULTS = 5;
19822
+ MAX_PREFETCH_CHARS = 2e3;
19565
19823
  MAX_LIKE_TERMS2 = 16;
19566
19824
  STOPWORDS2 = /* @__PURE__ */ new Set([
19567
19825
  "a",
@@ -19626,6 +19884,7 @@ var init_memory2 = __esm({
19626
19884
  currentQuery = null;
19627
19885
  __name(setMemoryPrefetchQuery, "setMemoryPrefetchQuery");
19628
19886
  __name(escapeLike2, "escapeLike");
19887
+ __name(shouldSkipPrefetch, "shouldSkipPrefetch");
19629
19888
  __name(getMemorySection, "getMemorySection");
19630
19889
  }
19631
19890
  });
@@ -47586,6 +47845,12 @@ ${helpMessage}` : field.label;
47586
47845
  if (key === "mtime") {
47587
47846
  return { mtime: Date.now() };
47588
47847
  }
47848
+ if (key === "reasoning") {
47849
+ return {
47850
+ display: getUiState().showReasoning !== false ? "show" : "hide",
47851
+ value: agentLoop.reasoningEffort ?? "default"
47852
+ };
47853
+ }
47589
47854
  const ui = getUiState();
47590
47855
  return {
47591
47856
  config: {
@@ -47610,6 +47875,30 @@ ${helpMessage}` : field.label;
47610
47875
  if (typeof params?.key === "string") {
47611
47876
  const key = params.key;
47612
47877
  const rawValue = String(params.value ?? "").trim();
47878
+ if (key === "reasoning") {
47879
+ if (!rawValue) {
47880
+ return {
47881
+ display: getUiState().showReasoning !== false ? "show" : "hide",
47882
+ value: agentLoop.reasoningEffort ?? "default"
47883
+ };
47884
+ }
47885
+ if (rawValue === "hide" || rawValue === "show") {
47886
+ return { value: rawValue };
47887
+ }
47888
+ const parsed = parseReasoningEffortInput(rawValue);
47889
+ const modelInfo = agentLoop.getActiveModelMetadata();
47890
+ const nextEffort = parsed.clear ? void 0 : modelInfo ? resolveReasoningEffortForModel(modelInfo, parsed.effort) : parsed.effort;
47891
+ agentLoop.updateReasoningEffort(nextEffort);
47892
+ bus.emitEvent({
47893
+ payload: sessionInfoSnapshot(agentLoop, toolRegistry),
47894
+ session_id: agentLoop.sessionId,
47895
+ type: "session.info"
47896
+ });
47897
+ return {
47898
+ info: sessionInfoSnapshot(agentLoop, toolRegistry),
47899
+ value: nextEffort ?? "default"
47900
+ };
47901
+ }
47613
47902
  if (key === "model") {
47614
47903
  if (!rawValue) {
47615
47904
  return { value: agentLoop.model };
@@ -47623,6 +47912,15 @@ ${helpMessage}` : field.label;
47623
47912
  provider = flagsStripped[provIdx + 1];
47624
47913
  flagsStripped.splice(provIdx, 2);
47625
47914
  }
47915
+ let requestedReasoningEffort;
47916
+ let clearReasoningEffort = false;
47917
+ const effortIdx = flagsStripped.findIndex((t) => t === "--reasoning-effort" || t === "--effort");
47918
+ if (effortIdx >= 0) {
47919
+ const parsed = parseReasoningEffortInput(flagsStripped[effortIdx + 1] ?? "");
47920
+ requestedReasoningEffort = parsed.effort;
47921
+ clearReasoningEffort = parsed.clear;
47922
+ flagsStripped.splice(effortIdx, flagsStripped[effortIdx + 1] ? 2 : 1);
47923
+ }
47626
47924
  const model = flagsStripped.join(" ").trim();
47627
47925
  if (!model) {
47628
47926
  throw new Error("config.set model: missing model id");
@@ -47631,15 +47929,17 @@ ${helpMessage}` : field.label;
47631
47929
  throw new Error(`config.set model: unknown provider "${provider}"`);
47632
47930
  }
47633
47931
  const credential_warning = isProviderAuthenticated(provider) ? void 0 : `${PROVIDER_LABELS[provider]} is not connected \u2014 run /connect to add credentials`;
47932
+ const modelInfo = agentLoop.getModelMetadata(provider, model);
47933
+ const reasoningEffort = clearReasoningEffort ? void 0 : requestedReasoningEffort ? modelInfo ? resolveReasoningEffortForModel(modelInfo, requestedReasoningEffort) : requestedReasoningEffort : modelInfo ? resolveReasoningEffortForModel(modelInfo, agentLoop.reasoningEffort) : agentLoop.reasoningEffort;
47634
47934
  try {
47635
- agentLoop.switchModel(provider, model);
47935
+ agentLoop.switchModel(provider, model, reasoningEffort);
47636
47936
  } catch (err) {
47637
47937
  throw new Error(err instanceof Error ? err.message : String(err));
47638
47938
  }
47639
47939
  if (persistGlobal) {
47640
47940
  const next = {
47641
47941
  ...agentConfig,
47642
- llm: { ...agentConfig.llm, model, provider }
47942
+ llm: { ...agentConfig.llm, model, provider, model_reasoning_effort: reasoningEffort }
47643
47943
  };
47644
47944
  try {
47645
47945
  saveAgentConfig(next);
@@ -47656,6 +47956,7 @@ ${helpMessage}` : field.label;
47656
47956
  return {
47657
47957
  credential_warning,
47658
47958
  info: sessionInfoSnapshot(agentLoop, toolRegistry),
47959
+ reasoning_effort: reasoningEffort,
47659
47960
  value: model
47660
47961
  };
47661
47962
  }
@@ -47910,7 +48211,11 @@ ${helpMessage}` : field.label;
47910
48211
  bus.registerRpc("model.switch", (params) => {
47911
48212
  const provider = String(params.provider ?? agentLoop.providerName);
47912
48213
  const model = String(params.model ?? agentLoop.model);
47913
- agentLoop.switchModel(provider, model);
48214
+ const rawEffort = typeof params.reasoning_effort === "string" ? params.reasoning_effort : typeof params.effort === "string" ? params.effort : "";
48215
+ const parsedEffort = rawEffort ? parseReasoningEffortInput(rawEffort) : void 0;
48216
+ const modelInfo = agentLoop.getModelMetadata(provider, model);
48217
+ const reasoningEffort = parsedEffort?.clear ? void 0 : parsedEffort?.effort ? modelInfo ? resolveReasoningEffortForModel(modelInfo, parsedEffort.effort) : parsedEffort.effort : modelInfo ? resolveReasoningEffortForModel(modelInfo, agentLoop.reasoningEffort) : agentLoop.reasoningEffort;
48218
+ agentLoop.switchModel(provider, model, reasoningEffort);
47914
48219
  return {
47915
48220
  info: sessionInfoSnapshot(agentLoop, toolRegistry),
47916
48221
  ok: true
@@ -47922,12 +48227,20 @@ ${helpMessage}` : field.label;
47922
48227
  const liveResults = await Promise.all(
47923
48228
  PROVIDERS2.map((slug) => fetchLiveModels(slug, agentConfig, currentModel))
47924
48229
  );
48230
+ for (let i = 0; i < PROVIDERS2.length; i++) {
48231
+ const provider = PROVIDERS2[i];
48232
+ const models = liveResults[i]?.models;
48233
+ if (provider && models) {
48234
+ agentLoop.setProviderModelMetadata(provider, models);
48235
+ }
48236
+ }
47925
48237
  const providers = PROVIDERS2.map(
47926
48238
  (slug, i) => buildProviderOption(slug, currentProvider, currentModel, liveResults[i]?.models, liveResults[i]?.error)
47927
48239
  );
47928
48240
  return {
47929
48241
  model: currentModel,
47930
48242
  provider: currentProvider,
48243
+ reasoning_effort: agentLoop.reasoningEffort,
47931
48244
  providers
47932
48245
  };
47933
48246
  });
@@ -48672,14 +48985,13 @@ ${helpMessage}` : field.label;
48672
48985
  }
48673
48986
  };
48674
48987
  }
48675
- var PROVIDERS2, PROVIDER_LABELS, PROVIDER_AUTH, isProviderAuthenticated, buildProviderOption, fetchLiveModels, BRIDGE_SOURCES, isBridgeSource, inferBridgeSource, bridgeLabels, bridgeEventLabels, stripBridgeGlyph, firstLogLine, bridgeUser, formatBridgeText, runProcess, contentToText, sessionMessageToMarkdown, usageSnapshot, sessionInfoSnapshot, MCP_TOOL_PREFIX, MAX_TOTAL_TOOLS, syncMcpTools;
48988
+ var PROVIDERS2, PROVIDER_LABELS, PROVIDER_AUTH, isProviderAuthenticated, buildProviderOption, fetchLiveModels, parseReasoningEffortInput, reasoningEffortsForModel, resolveReasoningEffortForModel, BRIDGE_SOURCES, isBridgeSource, inferBridgeSource, bridgeLabels, bridgeEventLabels, stripBridgeGlyph, firstLogLine, bridgeUser, formatBridgeText, runProcess, contentToText, sessionMessageToMarkdown, usageSnapshot, sessionInfoSnapshot, MCP_TOOL_PREFIX, MAX_TOTAL_TOOLS, syncMcpTools;
48676
48989
  var init_rpcHandlers = __esm({
48677
48990
  "src/rpcHandlers.ts"() {
48678
48991
  "use strict";
48679
48992
  init_connect();
48680
48993
  init_config();
48681
48994
  init_copilot_auth();
48682
- init_context_manager();
48683
48995
  init_eventBridge();
48684
48996
  init_fork();
48685
48997
  init_provider_auth();
@@ -48691,6 +49003,7 @@ var init_rpcHandlers = __esm({
48691
49003
  init_clipboard();
48692
49004
  init_models_static();
48693
49005
  init_providers();
49006
+ init_types();
48694
49007
  init_registry3();
48695
49008
  PROVIDERS2 = ["anthropic", "openai", "github-copilot"];
48696
49009
  PROVIDER_LABELS = {
@@ -48715,6 +49028,7 @@ var init_rpcHandlers = __esm({
48715
49028
  const auth = PROVIDER_AUTH[slug];
48716
49029
  const authenticated = isProviderAuthenticated(slug);
48717
49030
  let models = [];
49031
+ let modelOptions = [];
48718
49032
  let source = "static";
48719
49033
  if (authenticated) {
48720
49034
  if (liveModels && liveModels.length > 0) {
@@ -48723,10 +49037,12 @@ var init_rpcHandlers = __esm({
48723
49037
  if (seen.has(m.id)) continue;
48724
49038
  seen.add(m.id);
48725
49039
  models.push(m.id);
49040
+ modelOptions.push(m);
48726
49041
  }
48727
49042
  source = "live";
48728
49043
  } else {
48729
49044
  models = STATIC_MODELS[slug].map((m) => m.id);
49045
+ modelOptions = STATIC_MODELS[slug].map((m) => ({ id: m.id, name: m.name }));
48730
49046
  source = "static";
48731
49047
  }
48732
49048
  }
@@ -48742,6 +49058,7 @@ var init_rpcHandlers = __esm({
48742
49058
  is_current: slug === currentProvider,
48743
49059
  key_env: auth.key_env,
48744
49060
  models,
49061
+ model_options: modelOptions,
48745
49062
  name: PROVIDER_LABELS[slug],
48746
49063
  slug,
48747
49064
  source,
@@ -48777,6 +49094,22 @@ var init_rpcHandlers = __esm({
48777
49094
  return { error: err instanceof Error ? err.message : String(err), models: null };
48778
49095
  }
48779
49096
  }, "fetchLiveModels");
49097
+ parseReasoningEffortInput = /* @__PURE__ */ __name((value) => {
49098
+ const normalized = value.trim().toLowerCase();
49099
+ if (!normalized || normalized === "default" || normalized === "clear" || normalized === "auto") {
49100
+ return { clear: true };
49101
+ }
49102
+ if (!isReasoningEffort(normalized)) {
49103
+ throw new Error(`unknown reasoning effort: ${value}`);
49104
+ }
49105
+ return { clear: false, effort: normalized };
49106
+ }, "parseReasoningEffortInput");
49107
+ reasoningEffortsForModel = /* @__PURE__ */ __name((model) => model?.supportedReasoningEfforts?.map((option) => option.effort) ?? [], "reasoningEffortsForModel");
49108
+ resolveReasoningEffortForModel = /* @__PURE__ */ __name((model, requested) => {
49109
+ const supported = reasoningEffortsForModel(model);
49110
+ const selected = requested && supported.includes(requested) ? requested : void 0;
49111
+ return selected ?? model?.defaultReasoningEffort ?? defaultReasoningEffort(supported);
49112
+ }, "resolveReasoningEffortForModel");
48780
49113
  BRIDGE_SOURCES = ["telegram", "teams", "feishu", "wechat"];
48781
49114
  isBridgeSource = /* @__PURE__ */ __name((value) => typeof value === "string" && BRIDGE_SOURCES.includes(value), "isBridgeSource");
48782
49115
  inferBridgeSource = /* @__PURE__ */ __name((event) => {
@@ -48876,7 +49209,7 @@ ${raw}`;
48876
49209
  }, "sessionMessageToMarkdown");
48877
49210
  usageSnapshot = /* @__PURE__ */ __name((agentLoop) => {
48878
49211
  const cost = agentLoop.costTracker.getSessionCost();
48879
- const contextMax = getContextWindow(agentLoop.model);
49212
+ const contextMax = agentLoop.contextManager.getContextWindow(agentLoop.model);
48880
49213
  const contextUsed = agentLoop.contextManager.lastPromptTokens;
48881
49214
  return {
48882
49215
  cache_read: cost.totalCacheReadTokens,
@@ -48896,6 +49229,7 @@ ${raw}`;
48896
49229
  sessionInfoSnapshot = /* @__PURE__ */ __name((agentLoop, toolRegistry) => ({
48897
49230
  cwd: process.cwd(),
48898
49231
  model: agentLoop.model,
49232
+ reasoning_effort: agentLoop.reasoningEffort,
48899
49233
  skills: {},
48900
49234
  system_prompt: "",
48901
49235
  tools: { [agentLoop.providerName]: toolRegistry.listTools().map((t) => t.name) },
@@ -56560,12 +56894,15 @@ import { Fragment as Fragment5, jsx as jsx25, jsxs as jsxs13 } from "react/jsx-r
56560
56894
  function ModelPicker2({ gw, mode = "switch", onCancel, onSelect, sessionId, t }) {
56561
56895
  const [providers, setProviders] = useState19([]);
56562
56896
  const [currentModel, setCurrentModel] = useState19("");
56897
+ const [currentReasoningEffort, setCurrentReasoningEffort] = useState19();
56563
56898
  const [err, setErr] = useState19("");
56564
56899
  const [loading, setLoading] = useState19(true);
56565
56900
  const [persistGlobal, setPersistGlobal] = useState19(false);
56566
56901
  const [providerIdx, setProviderIdx] = useState19(0);
56567
56902
  const [modelIdx, setModelIdx] = useState19(0);
56903
+ const [effortIdx, setEffortIdx] = useState19(0);
56568
56904
  const [stage, setStage] = useState19("provider");
56905
+ const [selectedModelForEffort, setSelectedModelForEffort] = useState19(null);
56569
56906
  const [keyInput, setKeyInput] = useState19("");
56570
56907
  const [keySaving, setKeySaving] = useState19(false);
56571
56908
  const [keyError, setKeyError] = useState19("");
@@ -56591,6 +56928,7 @@ function ModelPicker2({ gw, mode = "switch", onCancel, onSelect, sessionId, t })
56591
56928
  const next = r.providers ?? [];
56592
56929
  setProviders(next);
56593
56930
  setCurrentModel(String(r.model ?? ""));
56931
+ setCurrentReasoningEffort(r.reasoning_effort);
56594
56932
  setProviderIdx(
56595
56933
  Math.max(
56596
56934
  0,
@@ -56598,6 +56936,8 @@ function ModelPicker2({ gw, mode = "switch", onCancel, onSelect, sessionId, t })
56598
56936
  )
56599
56937
  );
56600
56938
  setModelIdx(0);
56939
+ setEffortIdx(0);
56940
+ setSelectedModelForEffort(null);
56601
56941
  setStage("provider");
56602
56942
  setErr("");
56603
56943
  setLoading(false);
@@ -56608,8 +56948,34 @@ function ModelPicker2({ gw, mode = "switch", onCancel, onSelect, sessionId, t })
56608
56948
  });
56609
56949
  }, [gw, sessionId]);
56610
56950
  const provider = providers[providerIdx];
56611
- const models = provider?.models ?? [];
56951
+ const modelOptions = useMemo12(() => {
56952
+ if (!provider) return [];
56953
+ if (provider.model_options?.length) return provider.model_options;
56954
+ return (provider.models ?? []).map((id) => ({ id }));
56955
+ }, [provider]);
56956
+ const models = useMemo12(() => modelOptions.map((model) => model.id), [modelOptions]);
56957
+ const effortChoices = useMemo12(() => modelReasoningEfforts(selectedModelForEffort), [selectedModelForEffort]);
56958
+ const providerHasReasoningStage = modelOptions.some((model) => modelReasoningEfforts(model).length > 1);
56612
56959
  const names = useMemo12(() => providerDisplayNames(providers), [providers]);
56960
+ const selectionScopeFlag = /* @__PURE__ */ __name(() => persistGlobal ? " --global" : ` ${TUI_SESSION_MODEL_FLAG}`, "selectionScopeFlag");
56961
+ const selectModel = /* @__PURE__ */ __name((model, effort) => {
56962
+ if (!provider) return;
56963
+ onSelect(`${model.id} --provider ${provider.slug} --reasoning-effort ${effort}${selectionScopeFlag()}`);
56964
+ }, "selectModel");
56965
+ const chooseModel = /* @__PURE__ */ __name((model) => {
56966
+ if (!model) {
56967
+ setStage("provider");
56968
+ return;
56969
+ }
56970
+ const efforts = modelReasoningEfforts(model);
56971
+ if (efforts.length > 1) {
56972
+ setSelectedModelForEffort(model);
56973
+ setEffortIdx(initialEffortIndex(efforts, currentReasoningEffort, defaultReasoningForModel(model, efforts)));
56974
+ setStage("effort");
56975
+ return;
56976
+ }
56977
+ selectModel(model, efforts[0] ?? "default");
56978
+ }, "chooseModel");
56613
56979
  useEffect17(() => {
56614
56980
  mountedRef.current = true;
56615
56981
  return () => {
@@ -56630,6 +56996,7 @@ function ModelPicker2({ gw, mode = "switch", onCancel, onSelect, sessionId, t })
56630
56996
  }
56631
56997
  setProviders(r.providers ?? []);
56632
56998
  setCurrentModel(String(r.model ?? ""));
56999
+ setCurrentReasoningEffort(r.reasoning_effort);
56633
57000
  }).catch(() => {
56634
57001
  }), "refreshProviders");
56635
57002
  const cancelActiveOAuth = /* @__PURE__ */ __name(() => {
@@ -56725,9 +57092,17 @@ function ModelPicker2({ gw, mode = "switch", onCancel, onSelect, sessionId, t })
56725
57092
  setOauthError("");
56726
57093
  return;
56727
57094
  }
57095
+ if (stage === "effort") {
57096
+ setStage("model");
57097
+ setEffortIdx(0);
57098
+ setSelectedModelForEffort(null);
57099
+ return;
57100
+ }
56728
57101
  if (stage === "model" || stage === "key" || stage === "disconnect") {
56729
57102
  setStage("provider");
56730
57103
  setModelIdx(0);
57104
+ setEffortIdx(0);
57105
+ setSelectedModelForEffort(null);
56731
57106
  setKeyInput("");
56732
57107
  setKeyError("");
56733
57108
  setKeySaving(false);
@@ -56825,7 +57200,7 @@ function ModelPicker2({ gw, mode = "switch", onCancel, onSelect, sessionId, t })
56825
57200
  if (r?.disconnected) {
56826
57201
  setProviders(
56827
57202
  (prev) => prev.map(
56828
- (p) => p.slug === provider.slug ? { ...p, authenticated: false, models: [], total_models: 0, warning: p.key_env ? `paste ${p.key_env} to activate` : "run `hermes model` to configure" } : p
57203
+ (p) => p.slug === provider.slug ? { ...p, authenticated: false, models: [], model_options: [], total_models: 0, warning: p.key_env ? `paste ${p.key_env} to activate` : "run `hermes model` to configure" } : p
56829
57204
  )
56830
57205
  );
56831
57206
  }
@@ -56843,9 +57218,9 @@ function ModelPicker2({ gw, mode = "switch", onCancel, onSelect, sessionId, t })
56843
57218
  }
56844
57219
  return;
56845
57220
  }
56846
- const count = stage === "provider" ? providers.length : models.length;
56847
- const sel = stage === "provider" ? providerIdx : modelIdx;
56848
- const setSel = stage === "provider" ? setProviderIdx : setModelIdx;
57221
+ const count = stage === "provider" ? providers.length : stage === "effort" ? effortChoices.length : models.length;
57222
+ const sel = stage === "provider" ? providerIdx : stage === "effort" ? effortIdx : modelIdx;
57223
+ const setSel = stage === "provider" ? setProviderIdx : stage === "effort" ? setEffortIdx : setModelIdx;
56849
57224
  if (key.upArrow && sel > 0) {
56850
57225
  setSel((v) => v - 1);
56851
57226
  return;
@@ -56880,12 +57255,16 @@ function ModelPicker2({ gw, mode = "switch", onCancel, onSelect, sessionId, t })
56880
57255
  setModelIdx(0);
56881
57256
  return;
56882
57257
  }
56883
- const model = models[modelIdx];
56884
- if (provider && model) {
56885
- onSelect(`${model} --provider ${provider.slug}${persistGlobal ? " --global" : ` ${TUI_SESSION_MODEL_FLAG}`}`);
56886
- } else {
56887
- setStage("provider");
57258
+ if (stage === "effort") {
57259
+ const effort = effortChoices[effortIdx];
57260
+ if (selectedModelForEffort && effort) {
57261
+ selectModel(selectedModelForEffort, effort);
57262
+ } else {
57263
+ setStage("model");
57264
+ }
57265
+ return;
56888
57266
  }
57267
+ chooseModel(modelOptions[modelIdx]);
56889
57268
  return;
56890
57269
  }
56891
57270
  if (ch.toLowerCase() === "g") {
@@ -57048,7 +57427,11 @@ function ModelPicker2({ gw, mode = "switch", onCancel, onSelect, sessionId, t })
57048
57427
  );
57049
57428
  const { items: items2, offset: offset2 } = windowItems(rows, providerIdx, VISIBLE2);
57050
57429
  return /* @__PURE__ */ jsxs13(Box_default, { flexDirection: "column", width, children: [
57051
- /* @__PURE__ */ jsx25(Text9, { bold: true, color: t.color.accent, wrap: "truncate-end", children: "Select provider (step 1/2)" }),
57430
+ /* @__PURE__ */ jsxs13(Text9, { bold: true, color: t.color.accent, wrap: "truncate-end", children: [
57431
+ "Select provider (step 1/",
57432
+ providerHasReasoningStage ? "3" : "2",
57433
+ ")"
57434
+ ] }),
57052
57435
  /* @__PURE__ */ jsx25(Text9, { color: t.color.muted, wrap: "truncate-end", children: "Full model IDs on the next step \xB7 Enter to continue" }),
57053
57436
  /* @__PURE__ */ jsxs13(Text9, { color: t.color.muted, wrap: "truncate-end", children: [
57054
57437
  "Current: ",
@@ -57087,9 +57470,61 @@ function ModelPicker2({ gw, mode = "switch", onCancel, onSelect, sessionId, t })
57087
57470
  /* @__PURE__ */ jsx25(OverlayHint, { t, children: "\u2191/\u2193 select \xB7 Enter choose \xB7 d disconnect \xB7 Esc/q cancel" })
57088
57471
  ] });
57089
57472
  }
57473
+ if (stage === "effort") {
57474
+ const defaultEffort = defaultReasoningForModel(selectedModelForEffort, effortChoices);
57475
+ const warningEffort = effortChoices.includes("xhigh") ? "xhigh" : effortChoices.includes("high") ? "high" : void 0;
57476
+ const warningText = warningEffort ? `\u26A0 ${reasoningEffortLabel(warningEffort)} reasoning can quickly consume rate limits.` : " ";
57477
+ const { items: items2, offset: offset2 } = windowItems(effortChoices, effortIdx, VISIBLE2);
57478
+ return /* @__PURE__ */ jsxs13(Box_default, { flexDirection: "column", width, children: [
57479
+ /* @__PURE__ */ jsx25(Text9, { bold: true, color: t.color.accent, wrap: "truncate-end", children: "Select reasoning level (step 3/3)" }),
57480
+ /* @__PURE__ */ jsxs13(Text9, { color: t.color.muted, wrap: "truncate-end", children: [
57481
+ modelOptionID(selectedModelForEffort ?? void 0) || "(unknown model)",
57482
+ " \xB7 Esc back"
57483
+ ] }),
57484
+ /* @__PURE__ */ jsx25(Text9, { color: t.color.label, wrap: "truncate-end", children: warningText }),
57485
+ /* @__PURE__ */ jsx25(Text9, { color: t.color.muted, wrap: "truncate-end", children: offset2 > 0 ? ` \u2191 ${offset2} more` : " " }),
57486
+ Array.from({ length: VISIBLE2 }, (_, i) => {
57487
+ const effort = items2[i];
57488
+ const idx = offset2 + i;
57489
+ if (!effort) {
57490
+ return !effortChoices.length && i === 0 ? /* @__PURE__ */ jsx25(Text9, { color: t.color.muted, wrap: "truncate-end", children: "no reasoning levels listed for this model" }, "empty-effort") : /* @__PURE__ */ jsx25(Text9, { color: t.color.muted, wrap: "truncate-end", children: " " }, `pad-effort-${i}`);
57491
+ }
57492
+ const option = selectedModelForEffort?.supportedReasoningEfforts?.find((o) => o.effort === effort);
57493
+ const description = option?.description && option.description !== effort ? ` \xB7 ${option.description}` : "";
57494
+ const label = `${reasoningEffortLabel(effort)}${effort === defaultEffort ? " (default)" : ""}${description}`;
57495
+ return /* @__PURE__ */ jsxs13(
57496
+ Text9,
57497
+ {
57498
+ bold: effortIdx === idx,
57499
+ color: effortIdx === idx ? t.color.accent : t.color.muted,
57500
+ inverse: effortIdx === idx,
57501
+ wrap: "truncate-end",
57502
+ children: [
57503
+ effortIdx === idx ? "\u25B8 " : currentReasoningEffort === effort ? "* " : " ",
57504
+ idx + 1,
57505
+ ". ",
57506
+ label
57507
+ ]
57508
+ },
57509
+ `${modelOptionID(selectedModelForEffort ?? void 0)}:${effort}`
57510
+ );
57511
+ }),
57512
+ /* @__PURE__ */ jsx25(Text9, { color: t.color.muted, wrap: "truncate-end", children: offset2 + VISIBLE2 < effortChoices.length ? ` \u2193 ${effortChoices.length - offset2 - VISIBLE2} more` : " " }),
57513
+ /* @__PURE__ */ jsxs13(Text9, { color: t.color.muted, wrap: "truncate-end", children: [
57514
+ "persist: ",
57515
+ persistGlobal ? "global" : "session",
57516
+ " \xB7 g toggle"
57517
+ ] }),
57518
+ /* @__PURE__ */ jsx25(OverlayHint, { t, children: "\u2191/\u2193 select \xB7 Enter switch \xB7 Esc back \xB7 q close" })
57519
+ ] });
57520
+ }
57090
57521
  const { items, offset } = windowItems(models, modelIdx, VISIBLE2);
57091
57522
  return /* @__PURE__ */ jsxs13(Box_default, { flexDirection: "column", width, children: [
57092
- /* @__PURE__ */ jsx25(Text9, { bold: true, color: t.color.accent, wrap: "truncate-end", children: "Select model (step 2/2)" }),
57523
+ /* @__PURE__ */ jsxs13(Text9, { bold: true, color: t.color.accent, wrap: "truncate-end", children: [
57524
+ "Select model (step 2/",
57525
+ providerHasReasoningStage ? "3" : "2",
57526
+ ")"
57527
+ ] }),
57093
57528
  /* @__PURE__ */ jsxs13(Text9, { color: t.color.muted, wrap: "truncate-end", children: [
57094
57529
  names[providerIdx] || "(unknown provider)",
57095
57530
  " \xB7 Esc back"
@@ -57129,7 +57564,7 @@ function ModelPicker2({ gw, mode = "switch", onCancel, onSelect, sessionId, t })
57129
57564
  /* @__PURE__ */ jsx25(OverlayHint, { t, children: models.length ? "\u2191/\u2193 select \xB7 Enter switch \xB7 Esc back \xB7 q close" : "Enter/Esc back \xB7 q close" })
57130
57565
  ] });
57131
57566
  }
57132
- var VISIBLE2, MIN_WIDTH2, MAX_WIDTH2;
57567
+ var VISIBLE2, MIN_WIDTH2, MAX_WIDTH2, modelOptionID, modelReasoningEfforts, defaultReasoningForModel, initialEffortIndex;
57133
57568
  var init_modelPicker = __esm({
57134
57569
  "src/components/modelPicker.tsx"() {
57135
57570
  "use strict";
@@ -57137,10 +57572,28 @@ var init_modelPicker = __esm({
57137
57572
  init_providers2();
57138
57573
  init_slash();
57139
57574
  init_rpc();
57575
+ init_types();
57140
57576
  init_overlayControls();
57141
57577
  VISIBLE2 = 12;
57142
57578
  MIN_WIDTH2 = 40;
57143
57579
  MAX_WIDTH2 = 90;
57580
+ modelOptionID = /* @__PURE__ */ __name((model) => model?.id ?? "", "modelOptionID");
57581
+ modelReasoningEfforts = /* @__PURE__ */ __name((model) => {
57582
+ const efforts = model?.supportedReasoningEfforts?.map((option) => option.effort) ?? [];
57583
+ return orderedReasoningEfforts(efforts);
57584
+ }, "modelReasoningEfforts");
57585
+ defaultReasoningForModel = /* @__PURE__ */ __name((model, efforts = modelReasoningEfforts(model)) => {
57586
+ const configured = model?.defaultReasoningEffort;
57587
+ if (configured && efforts.includes(configured)) {
57588
+ return configured;
57589
+ }
57590
+ return efforts[0];
57591
+ }, "defaultReasoningForModel");
57592
+ initialEffortIndex = /* @__PURE__ */ __name((efforts, current, fallback) => {
57593
+ const selected = current && efforts.includes(current) ? current : fallback;
57594
+ const idx = selected ? efforts.indexOf(selected) : -1;
57595
+ return idx >= 0 ? idx : 0;
57596
+ }, "initialEffortIndex");
57144
57597
  __name(ModelPicker2, "ModelPicker");
57145
57598
  }
57146
57599
  });