@dudousxd/nestjs-agent-react 0.24.0 → 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -31,6 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  // src/index.ts
32
32
  var src_exports = {};
33
33
  __export(src_exports, {
34
+ ALL_AGENTS: () => import_nestjs_agent_core4.ALL_AGENTS,
34
35
  AgentBackendUnsupportedError: () => AgentBackendUnsupportedError,
35
36
  AgentChatTransport: () => AgentChatTransport,
36
37
  AgentClient: () => AgentClient,
@@ -99,22 +100,87 @@ __export(src_exports, {
99
100
  useThreads: () => useThreads,
100
101
  useToolCatalog: () => useToolCatalog,
101
102
  useTranscriptItem: () => useTranscriptItem,
102
- validateAnswer: () => import_nestjs_agent_core.validateElicitationAnswer,
103
- validateAnswerValue: () => import_nestjs_agent_core.validateElicitationValue
103
+ validateAnswer: () => import_nestjs_agent_core2.validateElicitationAnswer,
104
+ validateAnswerValue: () => import_nestjs_agent_core2.validateElicitationValue
104
105
  });
105
106
  module.exports = __toCommonJS(src_exports);
106
107
 
108
+ // src/http-error.ts
109
+ function readErrorAnswer(text) {
110
+ if (text == null || text === "") return {
111
+ body: void 0,
112
+ message: void 0,
113
+ code: void 0
114
+ };
115
+ let body = text;
116
+ try {
117
+ body = JSON.parse(text);
118
+ } catch {
119
+ return {
120
+ body: text,
121
+ message: void 0,
122
+ code: void 0
123
+ };
124
+ }
125
+ if (body === null || typeof body !== "object") return {
126
+ body,
127
+ message: void 0,
128
+ code: void 0
129
+ };
130
+ const record = body;
131
+ const message = typeof record.message === "string" && record.message !== "" ? record.message : Array.isArray(record.message) && record.message.length > 0 && record.message.every((each) => typeof each === "string") ? record.message.join("; ") : void 0;
132
+ const code = typeof record.code === "string" && record.code !== "" ? record.code : void 0;
133
+ return {
134
+ body,
135
+ message,
136
+ code
137
+ };
138
+ }
139
+ __name(readErrorAnswer, "readErrorAnswer");
140
+ async function readErrorResponse(response) {
141
+ let text;
142
+ try {
143
+ text = await response.text();
144
+ } catch {
145
+ text = void 0;
146
+ }
147
+ return readErrorAnswer(text);
148
+ }
149
+ __name(readErrorResponse, "readErrorResponse");
150
+ function reportHttpError(listener, error) {
151
+ if (listener === void 0) return;
152
+ try {
153
+ listener(error);
154
+ } catch {
155
+ }
156
+ }
157
+ __name(reportHttpError, "reportHttpError");
158
+
107
159
  // src/client.ts
108
- var AgentHttpError = class extends Error {
160
+ var AgentHttpError = class _AgentHttpError extends Error {
109
161
  static {
110
162
  __name(this, "AgentHttpError");
111
163
  }
112
164
  status;
113
165
  method;
114
166
  path;
115
- constructor(status, method, path, statusText) {
116
- super(`Agent request failed: ${method} ${path} \u2192 ${status} ${statusText}`), this.status = status, this.method = method, this.path = path;
167
+ /** The answer's body: parsed JSON, else its text; `undefined` when empty. */
168
+ body;
169
+ /** The body's `code` (`quota_exceeded`, …), when the server sent one. */
170
+ code;
171
+ constructor(status, method, path, statusText, answer = {
172
+ body: void 0,
173
+ message: void 0,
174
+ code: void 0
175
+ }) {
176
+ super(answer.message ?? `Agent request failed: ${method} ${path} \u2192 ${status} ${statusText}`), this.status = status, this.method = method, this.path = path;
117
177
  this.name = "AgentHttpError";
178
+ this.body = answer.body;
179
+ this.code = answer.code;
180
+ }
181
+ /** Read a refused `response`'s body into an error. */
182
+ static async from(response, method, path) {
183
+ return new _AgentHttpError(response.status, method, path, response.statusText, await readErrorResponse(response));
118
184
  }
119
185
  };
120
186
  function normalizeAgentPath(path) {
@@ -149,7 +215,7 @@ var AgentClient = class {
149
215
  } : {}
150
216
  });
151
217
  if (!response.ok || !response.body) {
152
- throw new AgentHttpError(response.status, "POST", `${this.agentPath()}/chat`, response.statusText);
218
+ throw await this.failure(response, "POST", `${this.agentPath()}/chat`);
153
219
  }
154
220
  return streamResponse(response);
155
221
  }
@@ -176,7 +242,7 @@ var AgentClient = class {
176
242
  return null;
177
243
  }
178
244
  if (!response.ok || !response.body) {
179
- throw new AgentHttpError(response.status, "GET", `${this.agentPath()}${path}`, response.statusText);
245
+ throw await this.failure(response, "GET", `${this.agentPath()}${path}`);
180
246
  }
181
247
  return streamResponse(response);
182
248
  }
@@ -269,7 +335,9 @@ var AgentClient = class {
269
335
  };
270
336
  xhr.onload = () => {
271
337
  if (xhr.status < 200 || xhr.status >= 300) {
272
- reject(new AgentHttpError(xhr.status, "POST", `${this.agentPath()}/attachments`, xhr.statusText));
338
+ const error = new AgentHttpError(xhr.status, "POST", `${this.agentPath()}/attachments`, xhr.statusText, readErrorAnswer(xhr.responseText));
339
+ reportHttpError(this.options.onHttpError, error);
340
+ reject(error);
273
341
  return;
274
342
  }
275
343
  onProgress?.(1);
@@ -332,6 +400,7 @@ var AgentClient = class {
332
400
  * out takes the pre-picked default the request carried, resolved server-side against the request
333
401
  * the run already holds. Omit the whole object and the user has confirmed every pre-picked
334
402
  * answer — which is the point of the surface, so it is a valid submission rather than a blank.
403
+ * `via` names the surface the answer came through (the server records `'web'` when omitted).
335
404
  */
336
405
  answerToolCall(input) {
337
406
  return this.request("POST", "/tool-call/answer", input);
@@ -354,7 +423,10 @@ var AgentClient = class {
354
423
  path: this.agentPath(),
355
424
  headers: /* @__PURE__ */ __name(() => this.resolveHeaders(), "headers"),
356
425
  fetch: this.fetchImpl(),
357
- ...this.credentials()
426
+ ...this.credentials(),
427
+ ...this.options.onHttpError !== void 0 ? {
428
+ onHttpError: this.options.onHttpError
429
+ } : {}
358
430
  };
359
431
  }
360
432
  baseUrl() {
@@ -397,9 +469,15 @@ var AgentClient = class {
397
469
  });
398
470
  return this.handleResponse(response, method, path);
399
471
  }
472
+ /** The error for a refused `response`, already reported to `onHttpError`. */
473
+ async failure(response, method, path) {
474
+ const error = await AgentHttpError.from(response, method, path);
475
+ reportHttpError(this.options.onHttpError, error);
476
+ return error;
477
+ }
400
478
  async handleResponse(response, method, path) {
401
479
  if (!response.ok) {
402
- throw new AgentHttpError(response.status, method, path, response.statusText);
480
+ throw await this.failure(response, method, path);
403
481
  }
404
482
  if (response.status === 204) return void 0;
405
483
  const text = await response.text();
@@ -1754,14 +1832,16 @@ __name(GenerativeUI, "GenerativeUI");
1754
1832
 
1755
1833
  // src/provider.tsx
1756
1834
  var AgentBackendContext = sharedContext("@dudousxd/nestjs-agent-react:agent-backend");
1757
- function AgentProvider({ backend, baseUrl, path, headers, getHeaders, credentials, fetch, attachments, genui, children }) {
1835
+ function AgentProvider({ backend, baseUrl, path, headers, getHeaders, credentials, fetch, onHttpError, attachments, genui, children }) {
1758
1836
  const latestHeaders = (0, import_react5.useRef)({
1759
1837
  headers,
1760
- getHeaders
1838
+ getHeaders,
1839
+ onHttpError
1761
1840
  });
1762
1841
  latestHeaders.current = {
1763
1842
  headers,
1764
- getHeaders
1843
+ getHeaders,
1844
+ onHttpError
1765
1845
  };
1766
1846
  const upload = attachments?.upload;
1767
1847
  const value = (0, import_react5.useMemo)(() => backend ?? new AgentClient({
@@ -1782,6 +1862,7 @@ function AgentProvider({ backend, baseUrl, path, headers, getHeaders, credential
1782
1862
  upload
1783
1863
  }
1784
1864
  } : {},
1865
+ onHttpError: /* @__PURE__ */ __name((error) => latestHeaders.current.onHttpError?.(error), "onHttpError"),
1785
1866
  getHeaders: /* @__PURE__ */ __name(async () => {
1786
1867
  const current = latestHeaders.current;
1787
1868
  const dynamic = await current.getHeaders?.() ?? {};
@@ -2195,6 +2276,7 @@ function useModels(options = {}) {
2195
2276
  providers,
2196
2277
  models,
2197
2278
  defaultModel: data?.default ?? null,
2279
+ locked: data?.locked ?? null,
2198
2280
  find: /* @__PURE__ */ __name((id) => id == null ? void 0 : byId.get(id), "find"),
2199
2281
  isLoading,
2200
2282
  error,
@@ -2210,6 +2292,7 @@ function useModels(options = {}) {
2210
2292
  __name(useModels, "useModels");
2211
2293
 
2212
2294
  // src/quota/use-quota.ts
2295
+ var import_nestjs_agent_core = require("@dudousxd/nestjs-agent-core");
2213
2296
  var import_react11 = require("react");
2214
2297
 
2215
2298
  // src/threads/threads-events.ts
@@ -2265,6 +2348,7 @@ function useQuota(options = {}) {
2265
2348
  day: windows.find((window2) => window2.period === "day"),
2266
2349
  month: windows.find((window2) => window2.period === "month"),
2267
2350
  blocked: data?.blocked ?? null,
2351
+ warning: data === void 0 || data.blocked !== void 0 ? null : data.warning ?? (0, import_nestjs_agent_core.quotaWarning)(windows) ?? null,
2268
2352
  isLoading,
2269
2353
  error,
2270
2354
  refresh
@@ -2708,11 +2792,11 @@ var import_react18 = __toESM(require("react"), 1);
2708
2792
  var import_react17 = require("react");
2709
2793
 
2710
2794
  // src/transcript/model.ts
2711
- var import_nestjs_agent_core2 = require("@dudousxd/nestjs-agent-core");
2795
+ var import_nestjs_agent_core3 = require("@dudousxd/nestjs-agent-core");
2712
2796
  var import_ai2 = require("ai");
2713
2797
 
2714
2798
  // src/elicitation/answers.ts
2715
- var import_nestjs_agent_core = require("@dudousxd/nestjs-agent-core");
2799
+ var import_nestjs_agent_core2 = require("@dudousxd/nestjs-agent-core");
2716
2800
  function pad(value) {
2717
2801
  return String(value).padStart(2, "0");
2718
2802
  }
@@ -3307,7 +3391,7 @@ function readElicitationRequest(part) {
3307
3391
  if (!isRecord3(candidate) || typeof candidate.id !== "string" || typeof candidate.prompt !== "string") {
3308
3392
  return null;
3309
3393
  }
3310
- const input2 = (0, import_nestjs_agent_core2.readElicitationInput)(candidate.input) ?? null;
3394
+ const input2 = (0, import_nestjs_agent_core3.readElicitationInput)(candidate.input) ?? null;
3311
3395
  const rawOptions = Array.isArray(candidate.options) ? candidate.options : [];
3312
3396
  if (rawOptions.length === 0 && (input2 === null || input2.type === "select")) {
3313
3397
  return null;
@@ -3353,7 +3437,9 @@ function readElicitationOutcome(part) {
3353
3437
  answers,
3354
3438
  skipped: output.skipped,
3355
3439
  defaulted: Array.isArray(output.defaulted) ? output.defaulted.filter((id) => typeof id === "string") : [],
3356
- summary: typeof output.summary === "string" ? output.summary : null
3440
+ summary: typeof output.summary === "string" ? output.summary : null,
3441
+ answeredBy: typeof output.answeredBy === "string" ? output.answeredBy : null,
3442
+ answeredVia: typeof output.answeredVia === "string" ? output.answeredVia : null
3357
3443
  };
3358
3444
  }
3359
3445
  __name(readElicitationOutcome, "readElicitationOutcome");
@@ -3365,7 +3451,7 @@ function buildElicitationBlock(key, part, request, options) {
3365
3451
  const picked = isPending ? options.picked(toolCallId, question.id) : void 0;
3366
3452
  const selected = picked ?? outcome?.answers[question.id] ?? question.defaults;
3367
3453
  const pick = /* @__PURE__ */ __name((values) => options.pick(toolCallId, question.id, values), "pick");
3368
- const error = isPending ? (0, import_nestjs_agent_core2.validateElicitationAnswer)({
3454
+ const error = isPending ? (0, import_nestjs_agent_core3.validateElicitationAnswer)({
3369
3455
  id: question.id,
3370
3456
  prompt: question.prompt,
3371
3457
  options: question.options.map(({ value, label }) => ({
@@ -3803,7 +3889,7 @@ function useChatTranscript(options) {
3803
3889
  },
3804
3890
  scroll,
3805
3891
  stop: {
3806
- available: isBusy && options.onStop !== void 0,
3892
+ available: isBusy && options.readOnly !== true && options.onStop !== void 0,
3807
3893
  isStopping: stopRequested,
3808
3894
  stop
3809
3895
  }
@@ -4073,11 +4159,12 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
4073
4159
  ...options.toolCatalog !== void 0 ? {
4074
4160
  toolCatalog: options.toolCatalog
4075
4161
  } : {},
4076
- ...handlers.onAnswer !== void 0 ? {
4162
+ // Read-only still lifts question sets into their block, so the outcome shows.
4163
+ ...handlers.onAnswer !== void 0 || options.readOnly === true && options.onAnswer !== null ? {
4077
4164
  elicitation: {
4078
4165
  picked: /* @__PURE__ */ __name((toolCallId, questionId) => picks.get(pickKey(toolCallId, questionId)), "picked"),
4079
4166
  pick,
4080
- canAnswer: true,
4167
+ canAnswer: handlers.onAnswer !== void 0,
4081
4168
  canSkip: handlers.onSkip !== void 0,
4082
4169
  answer,
4083
4170
  skip,
@@ -4105,7 +4192,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
4105
4192
  copy: callbacks.copy
4106
4193
  },
4107
4194
  edit: {
4108
- available: isUser && options.editable === true && options.onEditSubmit !== void 0,
4195
+ available: isUser && options.readOnly !== true && options.editable === true && options.onEditSubmit !== void 0,
4109
4196
  isEditing,
4110
4197
  draft: draft ?? text,
4111
4198
  canSave: (draft ?? "").trim().length > 0,
@@ -4121,11 +4208,11 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
4121
4208
  }), "getTextareaProps")
4122
4209
  },
4123
4210
  fork: {
4124
- available: options.onFork !== void 0,
4211
+ available: options.readOnly !== true && options.onFork !== void 0,
4125
4212
  run: callbacks.fork
4126
4213
  },
4127
4214
  regenerate: {
4128
- available: isLastAssistant && options.regeneratable === true && options.onRegenerate !== void 0,
4215
+ available: isLastAssistant && options.readOnly !== true && options.regeneratable === true && options.onRegenerate !== void 0,
4129
4216
  run: callbacks.regenerate
4130
4217
  }
4131
4218
  });
@@ -4134,7 +4221,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
4134
4221
  }
4135
4222
  __name(useTranscriptItems, "useTranscriptItems");
4136
4223
  function settleHandlers(options, backend) {
4137
- const pick = /* @__PURE__ */ __name((own, fallback2) => own === null ? void 0 : own ?? fallback2, "pick");
4224
+ const pick = /* @__PURE__ */ __name((own, fallback2) => options.readOnly === true || own === null ? void 0 : own ?? fallback2, "pick");
4138
4225
  return {
4139
4226
  onAnswer: pick(options.onAnswer, backend.answerToolCall ? async (input) => {
4140
4227
  await backend.answerToolCall?.(input);
@@ -4427,10 +4514,13 @@ function joinClasses(...entries) {
4427
4514
  __name(joinClasses, "joinClasses");
4428
4515
 
4429
4516
  // src/components/message-list.tsx
4430
- function MessageList({ messages, status, renderToolPart, renderToolGroup, renderText, renderReasoning, renderFiles, renderUi, reasoningLabel, getMeta, onFork, editable, onEditSubmit, regeneratable, onRegenerate, getUsage, getCreatedAt, emptyState, typingIndicator, followUps, onFollowUpClick, error, onRetry, classNames }) {
4517
+ function MessageList({ messages, status, renderToolPart, renderToolGroup, renderText, renderReasoning, renderFiles, renderUi, reasoningLabel, getMeta, readOnly, onFork, editable, onEditSubmit, regeneratable, onRegenerate, getUsage, getCreatedAt, emptyState, typingIndicator, followUps, onFollowUpClick, error, onRetry, classNames }) {
4431
4518
  const transcript = useChatTranscript({
4432
4519
  messages,
4433
4520
  status,
4521
+ ...readOnly !== void 0 ? {
4522
+ readOnly
4523
+ } : {},
4434
4524
  ...onFork ? {
4435
4525
  onFork
4436
4526
  } : {},
@@ -4895,6 +4985,7 @@ function isPromise(value) {
4895
4985
  __name(isPromise, "isPromise");
4896
4986
 
4897
4987
  // src/presentation/use-tool-catalog.ts
4988
+ var import_nestjs_agent_core4 = require("@dudousxd/nestjs-agent-core");
4898
4989
  var import_react20 = require("react");
4899
4990
  var shared2 = /* @__PURE__ */ new WeakMap();
4900
4991
  function load2(client, agent) {
@@ -5014,7 +5105,7 @@ function useApprovalCountdown(expiresAt, options = {}) {
5014
5105
  __name(useApprovalCountdown, "useApprovalCountdown");
5015
5106
 
5016
5107
  // src/stored-message-to-ui-message.ts
5017
- var import_nestjs_agent_core3 = require("@dudousxd/nestjs-agent-core");
5108
+ var import_nestjs_agent_core5 = require("@dudousxd/nestjs-agent-core");
5018
5109
  function isRefusal(result) {
5019
5110
  if (result.denied === true) {
5020
5111
  return true;
@@ -5027,7 +5118,7 @@ function refusalReason(result) {
5027
5118
  const { output } = result;
5028
5119
  if (output !== null && typeof output === "object" && "reason" in output) {
5029
5120
  const reason = output.reason;
5030
- return typeof reason === "string" && reason !== import_nestjs_agent_core3.DEFAULT_REFUSAL_REASON ? reason : void 0;
5121
+ return typeof reason === "string" && reason !== import_nestjs_agent_core5.DEFAULT_REFUSAL_REASON ? reason : void 0;
5031
5122
  }
5032
5123
  return void 0;
5033
5124
  }
@@ -5297,12 +5388,13 @@ function useAgentChat(options = {}) {
5297
5388
  const settlingRunIdRef = (0, import_react23.useRef)(void 0);
5298
5389
  const autoResumeRunIdRef = (0, import_react23.useRef)(void 0);
5299
5390
  const [activeRunId, setActiveRunId] = (0, import_react23.useState)(null);
5300
- const [isLoadingHistory, setIsLoadingHistory] = (0, import_react23.useState)(false);
5391
+ const [historyFor, setHistoryFor] = (0, import_react23.useState)(void 0);
5301
5392
  const [historyError, setHistoryError] = (0, import_react23.useState)(null);
5302
5393
  const [threadModel, setThreadModel] = (0, import_react23.useState)(null);
5303
5394
  const [pickedModel, setPickedModel] = (0, import_react23.useState)(void 0);
5304
5395
  const pickedModelRef = (0, import_react23.useRef)(pickedModel);
5305
5396
  pickedModelRef.current = pickedModel;
5397
+ const lockedRef = (0, import_react23.useRef)(false);
5306
5398
  const pendingPin = (0, import_react23.useRef)(void 0);
5307
5399
  const regenerateNext = (0, import_react23.useRef)(false);
5308
5400
  const currentThreadId = (0, import_react23.useCallback)(() => latest.current.threadId ?? createdThreadId.current, []);
@@ -5350,7 +5442,7 @@ function useAgentChat(options = {}) {
5350
5442
  const regenerate2 = regenerateNext.current;
5351
5443
  regenerateNext.current = false;
5352
5444
  const threadId = currentThreadId();
5353
- const model = current.model ?? pickedModelRef.current;
5445
+ const model = current.model ?? (lockedRef.current ? void 0 : pickedModelRef.current);
5354
5446
  return {
5355
5447
  // Read per send: a host that switches agents before the first message (a picker on a
5356
5448
  // new chat) sends the one picked now, not the one this chat mounted with.
@@ -5444,6 +5536,7 @@ function useAgentChat(options = {}) {
5444
5536
  setRunId(void 0);
5445
5537
  setActiveRunId(null);
5446
5538
  setThreadModel(null);
5539
+ setPickedModel(void 0);
5447
5540
  setBackgroundRuns([]);
5448
5541
  setHistoryError(null);
5449
5542
  autoResumeRunIdRef.current = void 0;
@@ -5460,7 +5553,6 @@ function useAgentChat(options = {}) {
5460
5553
  const wantResume = current.resume !== false && current.resumeRunId === void 0;
5461
5554
  if (!wantHistory && !wantResume) return;
5462
5555
  let cancelled = false;
5463
- if (wantHistory) setIsLoadingHistory(true);
5464
5556
  client.getThread(threadId).then((thread) => {
5465
5557
  if (cancelled || thread == null) return;
5466
5558
  const active = thread.activeRunId ?? null;
@@ -5481,17 +5573,17 @@ function useAgentChat(options = {}) {
5481
5573
  if (cancelled) return;
5482
5574
  setHistoryError(error instanceof Error ? error : new Error(String(error)));
5483
5575
  }).finally(() => {
5484
- if (!cancelled) setIsLoadingHistory(false);
5576
+ if (!cancelled) setHistoryFor(threadId);
5485
5577
  });
5486
5578
  return () => {
5487
5579
  cancelled = true;
5488
- setIsLoadingHistory(false);
5489
5580
  };
5490
5581
  }, [
5491
5582
  client,
5492
5583
  chatId,
5493
5584
  options.threadId
5494
5585
  ]);
5586
+ const isLoadingHistory = options.threadId !== void 0 && options.threadId !== createdThreadId.current && options.history !== false && options.initialMessages === void 0 && historyFor !== options.threadId;
5495
5587
  const turnInFlight = (0, import_react23.useRef)(false);
5496
5588
  const isTurnInFlight = (0, import_react23.useCallback)(() => turnInFlight.current || transport.isAttemptLive, [
5497
5589
  transport
@@ -5608,19 +5700,25 @@ function useAgentChat(options = {}) {
5608
5700
  }, [
5609
5701
  client
5610
5702
  ]);
5611
- const answer = (0, import_react23.useCallback)(async ({ toolCallId, answers }) => {
5703
+ const answer = (0, import_react23.useCallback)(async ({ toolCallId, answers, via }) => {
5612
5704
  await requireBackendMethod(client, "answerToolCall")({
5613
5705
  toolCallId,
5614
5706
  ...answers !== void 0 ? {
5615
5707
  answers
5708
+ } : {},
5709
+ ...via !== void 0 ? {
5710
+ via
5616
5711
  } : {}
5617
5712
  });
5618
5713
  }, [
5619
5714
  client
5620
5715
  ]);
5621
- const skip = (0, import_react23.useCallback)(async ({ toolCallId }) => {
5716
+ const skip = (0, import_react23.useCallback)(async ({ toolCallId, via }) => {
5622
5717
  await requireBackendMethod(client, "skipToolCall")({
5623
- toolCallId
5718
+ toolCallId,
5719
+ ...via !== void 0 ? {
5720
+ via
5721
+ } : {}
5624
5722
  });
5625
5723
  }, [
5626
5724
  client
@@ -5700,10 +5798,14 @@ function useAgentChat(options = {}) {
5700
5798
  modelsWantedRef.current = true;
5701
5799
  queueMicrotask(() => setModelsWanted(true));
5702
5800
  }, []);
5703
- const selectModel = (0, import_react23.useCallback)((id) => setPickedModel(id), []);
5801
+ lockedRef.current = catalog.locked !== null;
5802
+ const selectModel = (0, import_react23.useCallback)((id) => {
5803
+ if (!lockedRef.current) setPickedModel(id);
5804
+ }, []);
5704
5805
  const pinToThread = (0, import_react23.useCallback)(async (model) => {
5705
5806
  const threadId = currentThreadId();
5706
5807
  setThreadModel(model);
5808
+ setPickedModel(void 0);
5707
5809
  if (threadId === void 0) {
5708
5810
  pendingPin.current = {
5709
5811
  model
@@ -5729,11 +5831,16 @@ function useAgentChat(options = {}) {
5729
5831
  wantModels();
5730
5832
  return catalog.providers;
5731
5833
  },
5732
- selected: options.model ?? pickedModel ?? threadModel ?? catalog.defaultModel ?? null,
5834
+ selected: catalog.locked?.model ?? options.model ?? pickedModel ?? threadModel ?? catalog.defaultModel ?? null,
5835
+ pinned: threadModel,
5733
5836
  get defaultModel() {
5734
5837
  wantModels();
5735
5838
  return catalog.defaultModel;
5736
5839
  },
5840
+ get locked() {
5841
+ wantModels();
5842
+ return catalog.locked;
5843
+ },
5737
5844
  select: selectModel,
5738
5845
  pinToThread,
5739
5846
  isLoading: modelsWanted && catalog.isLoading,
@@ -5887,6 +5994,7 @@ var QuotaBlockedError = class extends Error {
5887
5994
  };
5888
5995
  // Annotate the CommonJS export names for ESM import in node:
5889
5996
  0 && (module.exports = {
5997
+ ALL_AGENTS,
5890
5998
  AgentBackendUnsupportedError,
5891
5999
  AgentChatTransport,
5892
6000
  AgentClient,