@yeaft/webchat-agent 1.0.156 → 1.0.157

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yeaft/webchat-agent",
3
- "version": "1.0.156",
3
+ "version": "1.0.157",
4
4
  "description": "Remote worker agent for Yeaft Web Code Agent — connects the native Yeaft engine, CLI providers, and workbench tools",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -41,6 +41,7 @@ import {
41
41
  } from './adapter.js';
42
42
  import {
43
43
  normalizeEffort,
44
+ getModelEffortOptions,
44
45
  getThinkingCapability,
45
46
  mapEffortToOpenAIReasoning,
46
47
  } from '../models.js';
@@ -57,10 +58,9 @@ function thinkingV1Enabled() {
57
58
  }
58
59
 
59
60
  /**
60
- * Translate a normalised effort ('minimal'|'low'|'medium'|'high'|'xhigh'|'max') into the value
61
- * accepted by the OpenAI Responses `reasoning.effort` field. Responses today
62
- * accepts 'minimal'|'low'|'medium'|'high'|'xhigh'. Unsupported Anthropic-only
63
- * adaptive efforts must be dropped, not downgraded.
61
+ * Translate a normalised effort into the OpenAI Responses `reasoning.effort`
62
+ * field. Model capability filtering happens at the call site because `max` is
63
+ * supported by GPT-5.6 but not by older OpenAI reasoning models.
64
64
  */
65
65
  function effortForResponses(effort) {
66
66
  return mapEffortToOpenAIReasoning(effort);
@@ -248,7 +248,7 @@ export class OpenAIResponsesAdapter extends LLMAdapter {
248
248
  // ─── Streaming ──────────────────────────────────────────
249
249
 
250
250
  /**
251
- * @param {{ model: string, system: string, messages: import('./adapter.js').UnifiedMessage[], tools?: import('./adapter.js').UnifiedToolDef[], maxTokens?: number, effort?: 'minimal'|'low'|'medium'|'high'|'xhigh'|'max', effortSource?: 'user'|'auto', extraBody?: object, signal?: AbortSignal, onRawExchange?: ({rawRequest, rawResponse}) => void }} params
251
+ * @param {{ model: string, system: string, messages: import('./adapter.js').UnifiedMessage[], tools?: import('./adapter.js').UnifiedToolDef[], maxTokens?: number, effort?: 'minimal'|'low'|'medium'|'high'|'xhigh'|'max', effortSource?: 'user'|'auto', effortContext?: object, extraBody?: object, signal?: AbortSignal, onRawExchange?: ({rawRequest, rawResponse}) => void, onRequestStart?: () => void }} params
252
252
  *
253
253
  * NOTE on `extraBody`: any keys you spread here are merged verbatim into
254
254
  * the wire body and — because the verbatim debug feature is intentionally
@@ -257,7 +257,7 @@ export class OpenAIResponsesAdapter extends LLMAdapter {
257
257
  * `api-key` headers are auto-redacted (see `redactRawRequest` in
258
258
  * `adapter.js`); request-body fields are caller-controlled.
259
259
  */
260
- async *stream({ model, system, messages, tools, maxTokens = 16384, effort, effortSource, extraBody, signal, onRawExchange, onRequestStart }) {
260
+ async *stream({ model, system, messages, tools, maxTokens = 16384, effort, effortSource, effortContext = {}, extraBody, signal, onRawExchange, onRequestStart }) {
261
261
  if (signal?.aborted) throw new LLMAbortError();
262
262
 
263
263
  const body = {
@@ -276,8 +276,9 @@ export class OpenAIResponsesAdapter extends LLMAdapter {
276
276
  // Unknown / unsupported models silently drop the field.
277
277
  const normEffort = normalizeEffort(effort);
278
278
  if ((thinkingV1Enabled() || effortSource === 'user') && normEffort) {
279
- const cap = getThinkingCapability(model);
280
- if (cap.supportsThinking && cap.thinkingProtocol === 'openai-reasoning') {
279
+ const cap = getThinkingCapability(model, effortContext);
280
+ const supportedEfforts = getModelEffortOptions(model, effortContext);
281
+ if (cap.supportsThinking && cap.thinkingProtocol === 'openai-reasoning' && supportedEfforts.includes(normEffort)) {
281
282
  const wireEffort = effortForResponses(normEffort);
282
283
  if (wireEffort) body.reasoning = { effort: wireEffort };
283
284
  }
@@ -517,7 +518,7 @@ export class OpenAIResponsesAdapter extends LLMAdapter {
517
518
  * expose them, mirror the stream() instrumentation. Parity with
518
519
  * anthropic.js's `call()`.
519
520
  */
520
- async call({ model, system, messages, maxTokens = 4096, effort, effortSource, extraBody, signal, onRequestStart }) {
521
+ async call({ model, system, messages, maxTokens = 4096, effort, effortSource, effortContext = {}, extraBody, signal, onRequestStart }) {
521
522
  if (signal?.aborted) throw new LLMAbortError();
522
523
 
523
524
  const body = {
@@ -530,8 +531,9 @@ export class OpenAIResponsesAdapter extends LLMAdapter {
530
531
  // Mirror stream()'s thinking injection for non-streaming side queries.
531
532
  const normEffort = normalizeEffort(effort);
532
533
  if ((thinkingV1Enabled() || effortSource === 'user') && normEffort) {
533
- const cap = getThinkingCapability(model);
534
- if (cap.supportsThinking && cap.thinkingProtocol === 'openai-reasoning') {
534
+ const cap = getThinkingCapability(model, effortContext);
535
+ const supportedEfforts = getModelEffortOptions(model, effortContext);
536
+ if (cap.supportsThinking && cap.thinkingProtocol === 'openai-reasoning' && supportedEfforts.includes(normEffort)) {
535
537
  const wireEffort = effortForResponses(normEffort);
536
538
  if (wireEffort) body.reasoning = { effort: wireEffort };
537
539
  }
package/yeaft/models.js CHANGED
@@ -34,7 +34,7 @@ import { lookupModelLimitSync } from './llm/models-dev.js';
34
34
  * @property {'anthropic' | 'anthropic-adaptive' | 'openai-reasoning' | 'none'} [thinkingProtocol] — task-327a:
35
35
  * 'anthropic' → thinking:{type:'enabled', budget_tokens:N}
36
36
  * 'anthropic-adaptive' → thinking:{type:'adaptive'} + output_config:{effort}
37
- * 'openai-reasoning' → reasoning:{effort:'minimal'|'low'|'medium'|'high'|'xhigh'}
37
+ * 'openai-reasoning' → reasoning:{effort:'minimal'|'low'|'medium'|'high'|'xhigh'|'max'}
38
38
  * 'none' (default) → parameter silently dropped by router
39
39
  * @property {'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max' | null} [defaultEffort] — adapter-level default
40
40
  * when caller doesn't specify effort (null = no default / decision-tree decides).
@@ -405,12 +405,12 @@ export const ANTHROPIC_THINKING_BUDGETS = {
405
405
  };
406
406
 
407
407
  /**
408
- * Map a Yeaft effort level to the OpenAI reasoning.effort enum. Current
409
- * OpenAI APIs expose xhigh, but not Anthropic's 'max'. Unsupported values are
410
- * dropped by returning null; the adapter MUST NOT error.
408
+ * Map a Yeaft effort level to the OpenAI reasoning.effort enum. Supported
409
+ * values are model-dependent; capability filtering happens before this wire
410
+ * translation. Unsupported values are dropped; the adapter MUST NOT error.
411
411
  *
412
412
  * @param {Effort} effort
413
- * @returns {'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | null}
413
+ * @returns {'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max' | null}
414
414
  */
415
415
  export function mapEffortToOpenAIReasoning(effort) {
416
416
  if (!effort) return null;
@@ -420,11 +420,13 @@ export function mapEffortToOpenAIReasoning(effort) {
420
420
  case 'medium': return 'medium';
421
421
  case 'high': return 'high';
422
422
  case 'xhigh': return 'xhigh';
423
+ case 'max': return 'max';
423
424
  default: return null;
424
425
  }
425
426
  }
426
427
 
427
428
  export const OPENAI_REASONING_EFFORT_OPTIONS = ['minimal', 'low', 'medium', 'high', 'xhigh'];
429
+ export const OPENAI_MAX_REASONING_EFFORT_OPTIONS = ['low', 'medium', 'high', 'xhigh', 'max'];
428
430
  export const ANTHROPIC_MANUAL_EFFORT_OPTIONS = ['low', 'medium', 'high'];
429
431
  export const ANTHROPIC_ADAPTIVE_EFFORT_OPTIONS = ['low', 'medium', 'high', 'xhigh', 'max'];
430
432
  export const ANTHROPIC_ADAPTIVE_MAX_EFFORT_OPTIONS = ['low', 'medium', 'high', 'max'];
@@ -454,6 +456,19 @@ function inferThinkingCapability(model) {
454
456
  const id = parseModelRef(model).modelId.toLowerCase();
455
457
  if (!id) return null;
456
458
 
459
+ // GPT-5.6 adds the model-specific `max` tier. Keep this ahead of the generic
460
+ // GPT inference so older OpenAI models do not advertise a value they reject.
461
+ // Provider suffixes such as gpt-5.6-sol are variants of the same model family.
462
+ if (/^gpt-5\.6($|[-.])/.test(id)) {
463
+ return {
464
+ supportsThinking: true,
465
+ thinkingProtocol: 'openai-reasoning',
466
+ defaultEffort: null,
467
+ maxBudgetTokens: null,
468
+ effortOptions: OPENAI_MAX_REASONING_EFFORT_OPTIONS,
469
+ };
470
+ }
471
+
457
472
  if (/^(gpt-5|o1|o3|o4|chatgpt-|codex-)/.test(id)) {
458
473
  return { supportsThinking: true, thinkingProtocol: 'openai-reasoning', defaultEffort: null, maxBudgetTokens: null };
459
474
  }