arcane-os 0.34.0 → 0.34.2

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/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.34.2
4
+
5
+ - Remove temporary `persist:false` Chat cards on completion, failure,
6
+ cancellation, destruction and session replacement. Temporary input is not
7
+ restored into the composer after a rejected request.
8
+ - Track each submission's exact created cards, preserving earlier persistent
9
+ cards and newer submissions even when operation identifiers are reused.
10
+ Persistent transcript and draft behavior remain unchanged; existing saved
11
+ history is not migrated or rewritten.
12
+
13
+ ## 0.34.1
14
+
15
+ - Honor an optional request-local `model` in `AI.fetchRequest()` and
16
+ `AI.streamRequest()`. Direct and registered built-in TWiN/native Ollama
17
+ requests carry the supplied model without changing the shared selection.
18
+ Calls that omit the option retain the selected default.
19
+ - Preserve complete messages, streaming, tools, cancellation and request
20
+ observers. Provider-reported stream model metadata remains authoritative;
21
+ generated stream completion metadata falls back to the request's model.
22
+ The option does not load or switch a browser-WASM model.
23
+
3
24
  ## 0.34.0
4
25
 
5
26
  - Add `createDigitalOceanFalTTSProvider` through `arcane-os/ai/browser-speech`
package/README.md CHANGED
@@ -19,7 +19,7 @@ version-locked SDK runtime, while an integrated Arcane checkout uses its live
19
19
  `arcane/` runtime. Both profiles share the theme, packaging, event, cancellation,
20
20
  and browser run contracts while retaining their selected application layout.
21
21
 
22
- This checkout defines the `0.34.0` SDK contract. Applications pin one exact npm
22
+ This checkout defines the `0.34.2` SDK contract. Applications pin one exact npm
23
23
  version and lockfile; registry state is deliberately not baked into application
24
24
  artifacts.
25
25
 
@@ -85,7 +85,7 @@ Create one browser application, install its pinned SDK, and start its source
85
85
  server:
86
86
 
87
87
  ```bash
88
- npx arcane-os@0.34.0 new hello-speech --path ./hello-speech --target browser
88
+ npx arcane-os@0.34.1 new hello-speech --path ./hello-speech --target browser
89
89
  cd hello-speech
90
90
  npm install
91
91
  ```
@@ -211,6 +211,38 @@ controller's `abort()` when the operation is cancelled or its page detaches.
211
211
  `streamRequest()` is the corresponding object-form streaming API. See the
212
212
  [AI module reference](../runtime-modules.md#aijs) for its complete options.
213
213
 
214
+ ## Use a model for one browser request
215
+
216
+ Both object-form methods accept `model` for that request. For example, an
217
+ application can use 20B for document search while retaining its configured
218
+ 120B conversation model:
219
+
220
+ ```javascript
221
+ const result = await ai.fetchRequest({
222
+ model: 'openai-gpt-oss-20b',
223
+ messages: searchMessages
224
+ });
225
+
226
+ await ai.streamRequest({
227
+ model: 'openai-gpt-oss-20b',
228
+ messages: summaryMessages,
229
+ onChunk: displaySummaryChunk
230
+ });
231
+ ```
232
+
233
+ `searchMessages`, `summaryMessages`, and `displaySummaryChunk` are supplied by
234
+ the application. Their content and callbacks keep their ordinary contracts.
235
+ The exact model goes into each TWiN request payload without changing `ai.model`
236
+ or selecting another provider. Concurrent calls can choose different models;
237
+ later calls that omit `model` (or pass `undefined`) use the configured model.
238
+ Retries keep the same request model. Provider-returned model metadata remains
239
+ authoritative; an assembled stream completion uses the request model only when
240
+ the response omits it.
241
+
242
+ The browser API forwards an explicitly supplied model to registered providers,
243
+ whose own request contract determines its meaning. This does not switch or
244
+ activate the loaded model of a browser-WASM provider.
245
+
214
246
  ## Migrate saved preference tuples before using them
215
247
 
216
248
  The six tuple slots consumed by `ai.setAI(...tuple)` are:
@@ -413,6 +413,15 @@ the public result term; `disposition` remains an accepted input spelling.
413
413
  When a submitted turn uses `persist:false`, Chat may show its cards while that
414
414
  one operation is active, then removes them when the operation settles; neither
415
415
  the input nor response remains in the retained transcript or model context.
416
+ This cleanup also applies to failed, cancelled, detached, or replaced-session
417
+ requests. Temporary input is not restored into the composer, and a temporary
418
+ failure does not become an assistant history card. The request still rejects
419
+ normally and keeps its diagnostic/status reporting. Cleanup owns the exact
420
+ operation's nodes so a completion listener can start another turn safely.
421
+ Submission code passes those card references directly when it creates them;
422
+ reusing an operation identifier never selects earlier conversation cards for
423
+ cleanup or timestamp updates.
424
+ Persistent-turn recovery and existing saved history remain unchanged.
416
425
 
417
426
  For streaming sessions, every provisional structural card and the terminal
418
427
  result must preserve the same choice, ordered call position, exact ID, type,
@@ -164,6 +164,21 @@ entry points. `streamMessage(...)` and `streamRequest(options)` deliver
164
164
  incremental responses. The positional and object forms share the existing
165
165
  provider implementations; neither form is a retired compatibility API.
166
166
 
167
+ `fetchRequest({model,...})` and `streamRequest({model,...})` accept an optional
168
+ request-local model. On the built-in TWiN Cloud route, its exact value becomes
169
+ the outbound `model` field; native Ollama receives the same request-local value.
170
+ Omitting it or passing `undefined` preserves the configured model. Each built-in
171
+ request captures its model before asynchronous callbacks, so concurrent requests
172
+ may use different models without assigning `ai.model`, changing provider
173
+ selection, or loading/unloading another model. Request observers and retries
174
+ receive that request's model. Stream/native completion metadata uses it only
175
+ when the provider omits its own `model` field.
176
+
177
+ Registered providers receive a supplied `model` unchanged in their request
178
+ payload; an omitted value remains absent. The provider owns interpretation.
179
+ This option does not switch a loaded browser-WASM model or its lifecycle:
180
+ explicit provider/model selection and activation remain necessary there.
181
+
167
182
  Built-in cloud chat decodes an HTTP error body once as JSON or text and rejects
168
183
  with that complete value unchanged. It does not reconstruct an Error, replace
169
184
  the message, or add `providerMessage`, `status`, or an SDK failure code to the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arcane-os",
3
- "version": "0.34.0",
3
+ "version": "0.34.2",
4
4
  "description": "Arcane OS JavaScript SDK, project-local CLI, browser runtime, and repository-portable application packager.",
5
5
  "type": "module",
6
6
  "main": "./src/index.mjs",
@@ -86,7 +86,7 @@
86
86
  "test": "npm run test:unit && npm run test:functional && npm run test:integration && npm run test:regression",
87
87
  "test:release": "node ./bin/arcane-test.mjs test/npm-release.test.mjs",
88
88
  "test:unit": "node ./bin/arcane-test.mjs test/app-descriptor.test.mjs test/app-schema.test.mjs test/app-selection.test.mjs test/contracts.test.mjs test/doctor.test.mjs test/mail-credentials.test.mjs test/mail-outbox.test.mjs test/mail-public-api.test.mjs test/mail-send.test.mjs test/mail-transport.test.mjs test/targets.test.mjs test/workspace-operation-lock.test.mjs",
89
- "test:functional": "node ./bin/arcane-test.mjs test/ai-request-retry.test.mjs test/ai-stream-cancellation.test.mjs test/browser-speech-providers.test.mjs test/browser-wasm-gpu-notice.test.mjs test/browser-wasm-download-resume.test.mjs test/chat-stream-speech.test.mjs test/chat-transcript-scroll.test.mjs test/cli.test.mjs test/dbopfs-document-library.test.mjs test/dbopfs.test.mjs test/dev-server.test.mjs test/dev-pwa.test.mjs test/dom-event-instrumentation.test.mjs test/event-manager.test.mjs test/events.test.mjs test/import-map.test.mjs test/mail-cli.test.mjs test/mail-runtime.test.mjs test/mail-server.test.mjs test/modal.test.mjs test/packaging.test.mjs test/pwa-packaging.test.mjs test/pwa-client.test.mjs test/pwa-install.test.mjs test/pwa-worker.test.mjs test/persistent-ai-chat-session.test.mjs test/reference-completeness.test.mjs test/runtime-api-behavior.test.mjs test/runtime.test.mjs test/scaffold.test.mjs test/speech-playback.test.mjs test/site.test.mjs test/twin-cloud.test.mjs test/update-check.test.mjs test/user-explicit-updates.test.mjs",
89
+ "test:functional": "node ./bin/arcane-test.mjs test/ai-request-model.test.mjs test/ai-request-retry.test.mjs test/ai-stream-cancellation.test.mjs test/browser-speech-providers.test.mjs test/browser-wasm-gpu-notice.test.mjs test/browser-wasm-download-resume.test.mjs test/chat-stream-speech.test.mjs test/chat-transcript-scroll.test.mjs test/chat-transient-retention.test.mjs test/cli.test.mjs test/dbopfs-document-library.test.mjs test/dbopfs.test.mjs test/dev-server.test.mjs test/dev-pwa.test.mjs test/dom-event-instrumentation.test.mjs test/event-manager.test.mjs test/events.test.mjs test/import-map.test.mjs test/mail-cli.test.mjs test/mail-runtime.test.mjs test/mail-server.test.mjs test/modal.test.mjs test/packaging.test.mjs test/pwa-packaging.test.mjs test/pwa-client.test.mjs test/pwa-install.test.mjs test/pwa-worker.test.mjs test/persistent-ai-chat-session.test.mjs test/reference-completeness.test.mjs test/runtime-api-behavior.test.mjs test/runtime.test.mjs test/scaffold.test.mjs test/speech-playback.test.mjs test/site.test.mjs test/twin-cloud.test.mjs test/update-check.test.mjs test/user-explicit-updates.test.mjs",
90
90
  "test:integration": "node ./bin/arcane-test.mjs test/installed-package-runtime.test.mjs test/root-app-layout.test.mjs test/integrated-shared.test.mjs test/integrated-workspace.test.mjs test/mail-browser.test.mjs test/native-plan.test.mjs test/native-provider-loader.test.mjs test/npm-release.test.mjs test/release-bundle.test.mjs test/release-capability-smoke.test.mjs test/shared-payload-batch.test.mjs test/tarball.test.mjs test/browser-wasm-cpu.test.mjs test/wllama-webgpu-runtime.test.mjs",
91
91
  "test:regression": "node ./bin/arcane-test.mjs test/channel-workflows.test.mjs test/html-import-registration.test.mjs test/logging-regression.test.mjs test/markdown-speech.test.mjs test/prepared-speech.test.mjs test/native-provider-generation.test.mjs test/speech-queue-regression.test.mjs test/testing.test.mjs test/test-sets.test.mjs",
92
92
  "check": "node tools/check-source.mjs && npm test",
@@ -3188,15 +3188,12 @@
3188
3188
  ].includes(error?.code);
3189
3189
  }
3190
3190
 
3191
- function restoreRejectedStructuralDraft(messageId,operationId,text){
3191
+ function restoreRejectedStructuralDraft(messageId,requestMessages,text){
3192
3192
  const response=[...chatOutput.children].find(
3193
3193
  candidate=>candidate.id===`message-${messageId}`
3194
3194
  )||null;
3195
- const request=[...chatOutput.children].find(
3196
- candidate=>candidate.dataset.operationId===operationId
3197
- )||null;
3198
3195
  response?.remove();
3199
- request?.remove();
3196
+ for(const request of requestMessages) request.remove();
3200
3197
  const existing=textArea.value;
3201
3198
  if(existing!==text){
3202
3199
  textArea.value=existing
@@ -3206,14 +3203,15 @@
3206
3203
  textArea.style.height='auto';
3207
3204
  textArea.style.height=`${textArea.scrollHeight}px`;
3208
3205
  scrollTranscriptToBottom();
3209
- return Boolean(response||request);
3206
+ return Boolean(response||requestMessages.length);
3210
3207
  }
3211
3208
 
3212
3209
  async function sendMessageThroughBoundSession(
3213
3210
  text,
3214
3211
  context,
3215
3212
  sessionRequest={content:text,role:'user'},
3216
- perTurnRequest=null
3213
+ perTurnRequest=null,
3214
+ requestMessages=[]
3217
3215
  ){
3218
3216
  const session=boundChatSession;
3219
3217
  const bindingGeneration=sessionBindingGeneration;
@@ -3248,12 +3246,21 @@
3248
3246
  const streamedStructuralToolCalls=[];
3249
3247
  const messageId=`session-${++sessionMessageSequence}`;
3250
3248
  const sessionMessageToken=Symbol(messageId);
3249
+ const transientCards=retainTurn?[]:[...requestMessages];
3250
+ function removeTransientSessionCards(){
3251
+ if(!transientCards.length) return;
3252
+ // The submitter supplies only nodes created for this submission.
3253
+ for(const card of transientCards) card.remove();
3254
+ transientCards.length=0;
3255
+ scrollTranscriptToBottom();
3256
+ }
3251
3257
  activeSessionMessageToken=sessionMessageToken;
3252
3258
  sessionMessagePending=true;
3253
3259
  applyAIAvailability(host.aiAvailability);
3254
3260
  setSessionStatus('sending','AI is responding…');
3255
3261
  try{
3256
- receivedMessage(host.aiName, '', messageId);
3262
+ const responseCard=receivedMessage(host.aiName, '', messageId);
3263
+ if(!retainTurn) transientCards.push(responseCard);
3257
3264
  await streamMessage('',messageId,true);
3258
3265
  setMessageProgress(
3259
3266
  messageId,
@@ -3337,9 +3344,6 @@
3337
3344
  result.message.content,
3338
3345
  committedTurn?.response?.timestamp??result.message.timestamp
3339
3346
  );
3340
- const requestMessages=[...chatOutput.children].filter(
3341
- candidate=>candidate.dataset.operationId===context.operationId
3342
- );
3343
3347
  for(const [index,requestMessage] of requestMessages.entries()){
3344
3348
  setTranscriptMessageTimestamp(
3345
3349
  requestMessage,
@@ -3365,10 +3369,8 @@
3365
3369
  }
3366
3370
  setPendingStructuralToolCalls(terminalToolCalls);
3367
3371
  }else{
3368
- for(const requestMessage of requestMessages) requestMessage.remove();
3369
- message.remove();
3372
+ removeTransientSessionCards();
3370
3373
  setPendingStructuralToolCalls(previousPendingToolCalls);
3371
- scrollTranscriptToBottom();
3372
3374
  }
3373
3375
  setSessionStatus(
3374
3376
  pendingStructuralToolMessage?'tool':'ready',
@@ -3417,16 +3419,13 @@
3417
3419
  const response=[...chatOutput.children].find(
3418
3420
  candidate=>candidate.id===`message-${messageId}`
3419
3421
  )||null;
3420
- const requestMessages=[...chatOutput.children].filter(
3421
- candidate=>candidate.dataset.operationId===context.operationId
3422
- );
3423
3422
  response?.remove();
3424
3423
  for(const requestMessage of requestMessages){
3425
3424
  requestMessage.remove();
3426
3425
  }
3427
3426
  scrollTranscriptToBottom();
3428
- }else{
3429
- restoreRejectedStructuralDraft(messageId,context.operationId,text);
3427
+ }else if(retainTurn){
3428
+ restoreRejectedStructuralDraft(messageId,requestMessages,text);
3430
3429
  }
3431
3430
  arcaneLogging.error('Arcane structural tool protocol failure.',error);
3432
3431
  setSessionStatus(
@@ -3436,14 +3435,11 @@
3436
3435
  }else if(!destroyed&&bindingGeneration===sessionBindingGeneration){
3437
3436
  arcaneLogging.error('Arcane chat request failed.',error);
3438
3437
  if(sessionRequestMessages.every(message=>message.role==='tool')){
3439
- const requestMessages=[...chatOutput.children].filter(
3440
- candidate=>candidate.dataset.operationId===context.operationId
3441
- );
3442
3438
  for(const requestMessage of requestMessages){
3443
3439
  requestMessage.remove();
3444
3440
  }
3445
3441
  }
3446
- renderSessionMessageFailure(messageId,error);
3442
+ if(retainTurn) renderSessionMessageFailure(messageId,error);
3447
3443
  setSessionStatus(
3448
3444
  'error',
3449
3445
  visibleErrorMessage(error,'The AI request failed.')
@@ -3471,6 +3467,7 @@
3471
3467
  }
3472
3468
  throw error;
3473
3469
  }finally{
3470
+ removeTransientSessionCards();
3474
3471
  if(activeSessionMessageToken===sessionMessageToken){
3475
3472
  activeSessionMessageToken=null;
3476
3473
  sessionMessagePending=false;
@@ -3625,6 +3622,7 @@
3625
3622
  return false;
3626
3623
  }
3627
3624
  try{
3625
+ const requestMessages=[];
3628
3626
  for(const result of orderedResults){
3629
3627
  const visibleMessage=appendTranscriptMessage(
3630
3628
  'tool',
@@ -3633,12 +3631,14 @@
3633
3631
  );
3634
3632
  visibleMessage.dataset.operationId=operationId;
3635
3633
  visibleMessage.dataset.toolCallId=result.tool_call_id;
3634
+ requestMessages.push(visibleMessage);
3636
3635
  }
3637
3636
  const result=sendMessageThroughBoundSession(
3638
3637
  orderedResults.map(message=>message.content).join('\n'),
3639
3638
  eventContext,
3640
3639
  orderedResults,
3641
- perTurnRequest
3640
+ perTurnRequest,
3641
+ requestMessages
3642
3642
  );
3643
3643
  return observeHostSubmission(result,eventContext,ownership);
3644
3644
  }catch(error){
@@ -3824,6 +3824,7 @@
3824
3824
  return false;
3825
3825
  }
3826
3826
  try{
3827
+ const requestMessages=[];
3827
3828
  if(!eventContext.preserveDraft){
3828
3829
  textArea.value='';
3829
3830
  }
@@ -3834,6 +3835,7 @@
3834
3835
  hostSubmissionGeneration++;
3835
3836
  const visibleMessage=appendTranscriptMessage('user',text,host.name);
3836
3837
  visibleMessage.dataset.operationId=operationId;
3838
+ requestMessages.push(visibleMessage);
3837
3839
  }
3838
3840
  scrollTranscriptToBottom(!eventContext.synthetic);
3839
3841
 
@@ -3854,7 +3856,9 @@
3854
3856
  let result;
3855
3857
  try{
3856
3858
  result=boundChatSession
3857
- ?sendMessageThroughBoundSession(text,eventContext)
3859
+ ?sendMessageThroughBoundSession(
3860
+ text,eventContext,undefined,null,requestMessages
3861
+ )
3858
3862
  :host.sendMessage(text,eventContext);
3859
3863
  }catch(error){
3860
3864
  result=Promise.reject(error);
@@ -4443,7 +4443,8 @@ class AI {
4443
4443
  function ignoreBuiltInLLMProviderRequest(){},
4444
4444
  signal,
4445
4445
  payload.reasoningEffort,
4446
- onRetry
4446
+ onRetry,
4447
+ payload.model
4447
4448
  );
4448
4449
  }
4449
4450
 
@@ -4474,7 +4475,8 @@ class AI {
4474
4475
  function ignoreBuiltInLLMStreamResult(){},
4475
4476
  payload.reasoningEffort,
4476
4477
  observeToolText,
4477
- onRetry
4478
+ onRetry,
4479
+ payload.model
4478
4480
  );
4479
4481
  }
4480
4482
 
@@ -4555,7 +4557,7 @@ class AI {
4555
4557
  });
4556
4558
  }
4557
4559
 
4558
- #openAICompatibleOllamaResponse(response={},id=Date.now()){
4560
+ #openAICompatibleOllamaResponse(response={},id=Date.now(),model=this.model){
4559
4561
  const responseRecord=isPlainAIRecord(response)?response:{};
4560
4562
  const message=isPlainAIRecord(responseRecord.message)
4561
4563
  ?responseRecord.message
@@ -4597,7 +4599,7 @@ class AI {
4597
4599
  :Math.floor(Date.now()/1000),
4598
4600
  model:Object.hasOwn(responseRecord,'model')
4599
4601
  ?responseRecord.model
4600
- :this.model,
4602
+ :model,
4601
4603
  choices:[
4602
4604
  {
4603
4605
  index:0,
@@ -4620,6 +4622,7 @@ class AI {
4620
4622
 
4621
4623
  async streamRequest({
4622
4624
  messages=[],
4625
+ model,
4623
4626
  structuredOutput=false,
4624
4627
  localOnly=false,
4625
4628
  onChunk=function ignoreStreamChunk(){},
@@ -4676,6 +4679,7 @@ class AI {
4676
4679
  if(this.#shouldUseProviderRuntime('llm',this.llmService,localOnly)){
4677
4680
  const request={
4678
4681
  messages,
4682
+ ...(model!==undefined?{model}:{}),
4679
4683
  structuredOutput,
4680
4684
  tools,
4681
4685
  toolChoice,
@@ -4801,7 +4805,8 @@ class AI {
4801
4805
  onDataResult,
4802
4806
  normalizedReasoningEffort,
4803
4807
  observeToolText,
4804
- onRetry
4808
+ onRetry,
4809
+ model
4805
4810
  );
4806
4811
  const structuralToolCalls=normalizeAICompletionToolCalls(
4807
4812
  completion
@@ -4910,7 +4915,8 @@ class AI {
4910
4915
  dataResultHandler=function ignoreBuiltInStreamDataResult(){},
4911
4916
  reasoningEffort,
4912
4917
  observeToolText=null,
4913
- onRetry = null
4918
+ onRetry = null,
4919
+ model=this.model
4914
4920
  ){
4915
4921
  let speechTurnCompleted=false;
4916
4922
 
@@ -4934,7 +4940,7 @@ class AI {
4934
4940
  reasoningEffort===undefined?this.reasoningEffort:reasoningEffort
4935
4941
  );
4936
4942
  const request={
4937
- model:this.model,
4943
+ model,
4938
4944
  messages:messages,
4939
4945
  stream:true
4940
4946
  }
@@ -4981,7 +4987,7 @@ class AI {
4981
4987
  const ollamaMessages=this.#ollamaMessages(messages,tool_choice);
4982
4988
  const adaptNativeToolCalls=value=>this.#openAICompatibleOllamaToolCalls(value,id);
4983
4989
  const ollamaRequest={
4984
- model:this.model,
4990
+ model,
4985
4991
  messages:ollamaMessages,
4986
4992
  stream:true,
4987
4993
  ...(normalizedReasoningEffort
@@ -5091,7 +5097,8 @@ class AI {
5091
5097
  :nativeContent
5092
5098
  }
5093
5099
  },
5094
- id
5100
+ id,
5101
+ model
5095
5102
  );
5096
5103
  const structuralToolCalls=normalizeAICompletionToolCalls(
5097
5104
  nativeCompletion
@@ -5583,7 +5590,7 @@ class AI {
5583
5590
  created:Object.hasOwn(streamMetadata,'created')
5584
5591
  ?streamMetadata.created
5585
5592
  :Math.floor(Date.now()/1000),
5586
- model:Object.hasOwn(streamMetadata,'model')?streamMetadata.model:this.model,
5593
+ model:Object.hasOwn(streamMetadata,'model')?streamMetadata.model:model,
5587
5594
  choices:completionChoices
5588
5595
  };
5589
5596
  const terminalToolCalls=normalizeAICompletionToolCalls(completion);
@@ -5628,6 +5635,7 @@ class AI {
5628
5635
 
5629
5636
  async fetchRequest({
5630
5637
  messages=[],
5638
+ model,
5631
5639
  structuredOutput=false,
5632
5640
  localOnly=false,
5633
5641
  tools=[],
@@ -5670,6 +5678,7 @@ class AI {
5670
5678
  }
5671
5679
  const request={
5672
5680
  messages,
5681
+ ...(model!==undefined?{model}:{}),
5673
5682
  structuredOutput,
5674
5683
  tools,
5675
5684
  toolChoice,
@@ -5726,7 +5735,8 @@ class AI {
5726
5735
  onRequest,
5727
5736
  signal,
5728
5737
  normalizedReasoningEffort,
5729
- onRetry
5738
+ onRetry,
5739
+ model
5730
5740
  );
5731
5741
  }
5732
5742
 
@@ -5780,7 +5790,8 @@ class AI {
5780
5790
  requestHandler=function ignoreFetchRequest(){},
5781
5791
  signal=null,
5782
5792
  reasoningEffort,
5783
- onRetry = null
5793
+ onRetry = null,
5794
+ model=this.model
5784
5795
  ){
5785
5796
  validateAIStructuralRequest(messages,tools,parallel_tool_calls);
5786
5797
  this.#assertServiceConfigured(this.llmService);
@@ -5799,7 +5810,7 @@ class AI {
5799
5810
  reasoningEffort===undefined?this.reasoningEffort:reasoningEffort
5800
5811
  );
5801
5812
  const request={
5802
- model:this.model,
5813
+ model,
5803
5814
  messages:messages,
5804
5815
  stream:false
5805
5816
  }
@@ -5838,7 +5849,7 @@ class AI {
5838
5849
  const ollamaTools=this.#ollamaTools(tools,tool_choice);
5839
5850
  const ollamaMessages=this.#ollamaMessages(messages,tool_choice);
5840
5851
  const nativeRequest={
5841
- model:this.model,
5852
+ model,
5842
5853
  messages:ollamaMessages,
5843
5854
  stream:false,
5844
5855
  ...(normalizedReasoningEffort
@@ -5869,7 +5880,8 @@ class AI {
5869
5880
  }
5870
5881
  const responseJSON=this.#openAICompatibleOllamaResponse(
5871
5882
  nativeResponse,
5872
- id
5883
+ id,
5884
+ model
5873
5885
  );
5874
5886
  this.#assertRequiredOllamaToolCall(
5875
5887
  normalizeAICompletionToolCalls(responseJSON),