@stigmer/react 3.38.1 → 3.38.3

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.
@@ -13,5 +13,5 @@ export { DEFAULT_HARNESS, HARNESS_LABELS, HARNESS_META, HARNESS_OPTIONS, toProto
13
13
  export type { HarnessOption, HarnessDisplayInfo } from "./harness.js";
14
14
  export { FAST_SERVICE_TIER, toProtoServiceTier, fromProtoServiceTier } from "./service-tier.js";
15
15
  export type { ServiceTierOption } from "./service-tier.js";
16
- export { toProtoThinkingMode, fromProtoThinkingMode } from "./thinking-mode.js";
16
+ export { toProtoThinkingMode, fromProtoThinkingMode, thinkingSelectable, thinkingLocked } from "./thinking-mode.js";
17
17
  export type { ThinkingModeOption } from "./thinking-mode.js";
@@ -116,20 +116,30 @@ export interface ModelInfo {
116
116
  readonly visionCapability?: boolean;
117
117
  /**
118
118
  * Tri-state thinking capability from the registry's `capabilities` block
119
- * (stigmer/stigmer#772), the same absence convention as
120
- * {@link visionCapability}:
119
+ * (stigmer/stigmer#772, stigmer/stigmer#1280), the same absence
120
+ * convention as {@link visionCapability}:
121
121
  *
122
- * - `true` — the model declares an extended-reasoning (thinking) variant;
123
- * the thinking switch in the model popover's options area may render
124
- * - `false` — explicitly assessed as having none
122
+ * - `true` — the model can be asked to think: its entry declares a
123
+ * thinking form, a fixed budget (`thinking`) or adaptive depth
124
+ * (`adaptiveThinking`); the thinking switch in the model popover's
125
+ * options area may render
126
+ * - `false` — explicitly assessed as having neither
125
127
  * - `undefined` — never assessed; **stay silent, never treat as false**
126
128
  *
127
129
  * Capability-gated, not pricing-gated (unlike {@link serviceTiers}):
128
130
  * thinking bills at base per-token rates, so no priced variant exists to
129
- * key on. v1 honors the selection on the cursor harness only — gate any
130
- * control on `harness === "cursor"` as well as this flag.
131
+ * key on. The entry is per harness, so the flag already describes the
132
+ * harness this model runs on; `thinkingSelectable` is the gate to use.
131
133
  */
132
134
  readonly thinkingCapable?: boolean;
135
+ /**
136
+ * Tri-state: `true` for a model that always thinks and refuses a request
137
+ * to turn thinking off (the server refuses an explicit "disabled" for
138
+ * it), `false` for one that may turn it off, `undefined` when never
139
+ * assessed. A control shows thinking on and locked for a `true` model
140
+ * (`thinkingLocked`); absence is never read as `false`.
141
+ */
142
+ readonly thinkingRequired?: boolean;
133
143
  }
134
144
 
135
145
  /**
@@ -246,14 +256,29 @@ function parseVisionCapability(capabilities: unknown): boolean | undefined {
246
256
  }
247
257
 
248
258
  /**
249
- * Extract `capabilities.thinking` with the same tri-state convention as
250
- * {@link parseVisionCapability} (stigmer/stigmer#772): a missing or
251
- * malformed `capabilities` block stays `undefined`, never coerced to false.
259
+ * Whether the model can be asked to think: `capabilities.thinking` (a fixed
260
+ * budget) or `capabilities.adaptiveThinking` (adaptive depth), with the
261
+ * same tri-state convention as {@link parseVisionCapability}
262
+ * (stigmer/stigmer#772): a missing or malformed `capabilities` block, or
263
+ * one stating neither flag as a boolean, stays `undefined`, never coerced
264
+ * to false.
252
265
  */
253
266
  function parseThinkingCapability(capabilities: unknown): boolean | undefined {
254
267
  if (!capabilities || typeof capabilities !== "object") return undefined;
255
- const thinking = (capabilities as Record<string, unknown>).thinking;
256
- return typeof thinking === "boolean" ? thinking : undefined;
268
+ const { thinking, adaptiveThinking } = capabilities as Record<string, unknown>;
269
+ if (thinking === true || adaptiveThinking === true) return true;
270
+ return typeof thinking === "boolean" || typeof adaptiveThinking === "boolean" ? false : undefined;
271
+ }
272
+
273
+ /**
274
+ * Extract `capabilities.thinkingRequired`, tri-state: only a boolean
275
+ * states it. The runner reads the same flag (`shared/model-registry.ts`);
276
+ * both sides must agree that absence means "never assessed".
277
+ */
278
+ function parseThinkingRequired(capabilities: unknown): boolean | undefined {
279
+ if (!capabilities || typeof capabilities !== "object") return undefined;
280
+ const required = (capabilities as Record<string, unknown>).thinkingRequired;
281
+ return typeof required === "boolean" ? required : undefined;
257
282
  }
258
283
 
259
284
  /**
@@ -312,6 +337,7 @@ export function parseRegistryDocument(data: unknown): ModelRegistryDocument {
312
337
  .map((m) => {
313
338
  const visionCapability = parseVisionCapability(m.capabilities);
314
339
  const thinkingCapable = parseThinkingCapability(m.capabilities);
340
+ const thinkingRequired = parseThinkingRequired(m.capabilities);
315
341
  return {
316
342
  modelId: m.id,
317
343
  provider: m.provider as Provider,
@@ -329,6 +355,7 @@ export function parseRegistryDocument(data: unknown): ModelRegistryDocument {
329
355
  // (tri-state absence, not an explicit undefined).
330
356
  ...(visionCapability !== undefined ? { visionCapability } : {}),
331
357
  ...(thinkingCapable !== undefined ? { thinkingCapable } : {}),
358
+ ...(thinkingRequired !== undefined ? { thinkingRequired } : {}),
332
359
  };
333
360
  });
334
361
 
@@ -1,5 +1,7 @@
1
1
  import { ThinkingMode } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
2
2
 
3
+ import type { ModelInfo } from "./registry.js";
4
+
3
5
  /**
4
6
  * String form of the thinking mode for component props and hook inputs
5
7
  * (stigmer/stigmer#772). Mirrors {@link ServiceTierOption}: components
@@ -46,3 +48,25 @@ export function fromProtoThinkingMode(
46
48
  return undefined;
47
49
  }
48
50
  }
51
+
52
+ /**
53
+ * Whether the thinking switch may act on this model: the model's registry
54
+ * entry declares a thinking form ({@link ModelInfo.thinkingCapable}). The
55
+ * entry belongs to the harness the model runs on, so the answer holds on
56
+ * either harness; there is no separate harness check. The one gate every
57
+ * control and submit path uses, so a switch never renders where the server
58
+ * would refuse the selection.
59
+ */
60
+ export function thinkingSelectable(model: ModelInfo): boolean {
61
+ return model.thinkingCapable === true;
62
+ }
63
+
64
+ /**
65
+ * Whether the model always thinks ({@link ModelInfo.thinkingRequired}):
66
+ * its switch shows on and cannot be turned off, and a submit carries
67
+ * "enabled" for it, never "disabled", which the server refuses for such a
68
+ * model.
69
+ */
70
+ export function thinkingLocked(model: ModelInfo): boolean {
71
+ return model.thinkingRequired === true;
72
+ }