arcane-os 0.5.16 → 0.5.18

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.
Files changed (190) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/README.md +42 -19
  3. package/bin/arcane-test.mjs +16 -13
  4. package/browser-runtime/ai/browser-speech-artifacts.mjs +41 -38
  5. package/browser-runtime/ai/browser-speech-providers.mjs +53 -46
  6. package/browser-runtime/ai/browser-wasm-llm-provider.mjs +127 -124
  7. package/browser-runtime/ai/browser-wasm.mjs +6 -3
  8. package/browser-runtime/ai/browser-wllama-runtime.mjs +26 -23
  9. package/browser-runtime/ai/model-controller.mjs +94 -91
  10. package/browser-runtime/ai/speech-worker-client.mjs +12 -9
  11. package/browser-runtime/ai/speech-worker-runtime.mjs +56 -53
  12. package/browser-runtime/dependencies/strong-type/index.js +207 -82
  13. package/browser-runtime/dependencies/strong-type/package.json +15 -6
  14. package/browser-runtime/dom-event-instrumentation.mjs +24 -20
  15. package/browser-runtime/event-manager.mjs +111 -108
  16. package/browser-runtime/speech-text.mjs +35 -0
  17. package/docs/architecture.md +1 -1
  18. package/docs/reference/README.md +8 -8
  19. package/docs/reference/ai/browser-speech.md +230 -16
  20. package/docs/reference/ai/twin-cloud.md +1 -1
  21. package/docs/reference/availability-and-normalization.md +42 -1
  22. package/docs/reference/cli.md +1 -1
  23. package/docs/reference/core/arcane-ai-contracts.md +1 -1
  24. package/docs/reference/inventory/package-api.json +60 -4
  25. package/docs/reference/inventory/runtime-components.json +2 -2
  26. package/docs/reference/inventory/runtime-modules.json +29 -31
  27. package/docs/reference/protocols.md +17 -13
  28. package/docs/reference/runtime-components.md +6 -5
  29. package/docs/reference/runtime-modules.md +212 -70
  30. package/docs/reference/sdk-api.md +197 -11
  31. package/examples/wasm-ai-demo/index.html +1 -0
  32. package/node_modules/event-pubsub/node_modules/strong-type/README.md +408 -0
  33. package/node_modules/event-pubsub/node_modules/strong-type/assets/strong-type-header.png +0 -0
  34. package/node_modules/event-pubsub/node_modules/strong-type/index.js +1151 -0
  35. package/node_modules/event-pubsub/node_modules/strong-type/licence +21 -0
  36. package/node_modules/event-pubsub/node_modules/strong-type/node.js +125 -0
  37. package/node_modules/event-pubsub/node_modules/strong-type/package.json +61 -0
  38. package/node_modules/strong-type/README.md +42 -24
  39. package/node_modules/strong-type/benchmark/core.js +324 -0
  40. package/node_modules/strong-type/index.js +207 -82
  41. package/node_modules/strong-type/package.json +14 -6
  42. package/package.json +4 -3
  43. package/runtime/arcane/components/app-bar.html +5 -2
  44. package/runtime/arcane/components/assistant-panel.html +5 -2
  45. package/runtime/arcane/components/chart.html +15 -12
  46. package/runtime/arcane/components/chat.html +118 -141
  47. package/runtime/arcane/components/dashboard-config.html +6 -3
  48. package/runtime/arcane/components/data-view.html +4 -1
  49. package/runtime/arcane/components/directory-picker.html +5 -2
  50. package/runtime/arcane/components/document-inspector.html +6 -3
  51. package/runtime/arcane/components/file-drop.html +5 -2
  52. package/runtime/arcane/components/file-inspector.html +9 -6
  53. package/runtime/arcane/components/file-manager.html +16 -13
  54. package/runtime/arcane/components/local-ai-status.html +5 -2
  55. package/runtime/arcane/components/markdown-document.html +14 -11
  56. package/runtime/arcane/components/markdown-editor.html +4 -1
  57. package/runtime/arcane/components/modal.html +4 -1
  58. package/runtime/arcane/components/output-panel.html +6 -3
  59. package/runtime/arcane/components/preferences-form.html +4 -1
  60. package/runtime/arcane/components/record-timeline.html +8 -5
  61. package/runtime/arcane/components/relationship-board.html +9 -6
  62. package/runtime/arcane/components/source-code-viewer.html +12 -9
  63. package/runtime/arcane/components/source-explanation.html +8 -5
  64. package/runtime/arcane/components/speech.html +24 -21
  65. package/runtime/arcane/components/summary-strip.html +4 -1
  66. package/runtime/arcane/components/task-progress.html +6 -3
  67. package/runtime/arcane/components/terminal-workspace.html +4 -1
  68. package/runtime/arcane/components/voice-transcription.html +11 -8
  69. package/runtime/arcane/components/web-navigator.html +5 -2
  70. package/runtime/arcane/entities/ApiModelRecord.js +5 -2
  71. package/runtime/arcane/entities/Calculation.js +5 -2
  72. package/runtime/arcane/entities/Chat.js +33 -33
  73. package/runtime/arcane/entities/CommunicationMessage.js +4 -1
  74. package/runtime/arcane/entities/CommunicationThread.js +4 -1
  75. package/runtime/arcane/entities/File.js +1 -1
  76. package/runtime/arcane/entities/Image.js +8 -5
  77. package/runtime/arcane/entities/IntentEnvelope.js +20 -17
  78. package/runtime/arcane/entities/Preference.js +9 -6
  79. package/runtime/arcane/entities/Theme.js +5 -2
  80. package/runtime/arcane/entities/User.js +11 -11
  81. package/runtime/arcane/entities/Weather.js +5 -2
  82. package/runtime/arcane/modules/AI.js +503 -187
  83. package/runtime/arcane/modules/AIPreferenceRuntime.js +5 -2
  84. package/runtime/arcane/modules/AIPreferenceTuple.js +9 -6
  85. package/runtime/arcane/modules/AIProviderRuntime.js +117 -92
  86. package/runtime/arcane/modules/AIResponseURLPolicy.js +148 -62
  87. package/runtime/arcane/modules/AIRuntimeState.js +29 -26
  88. package/runtime/arcane/modules/AnsiText.js +4 -1
  89. package/runtime/arcane/modules/ApiModelDatabase.js +22 -19
  90. package/runtime/arcane/modules/AppDataScope.js +8 -5
  91. package/runtime/arcane/modules/ArcaneCommunicationBridge.js +4 -1
  92. package/runtime/arcane/modules/ArcaneNavigationPolicy.js +8 -5
  93. package/runtime/arcane/modules/ArcaneNetworkPolicy.js +18 -15
  94. package/runtime/arcane/modules/AsyncBoundary.js +13 -10
  95. package/runtime/arcane/modules/BrowserTestSuite.js +19 -16
  96. package/runtime/arcane/modules/CalculatorEngine.js +5 -2
  97. package/runtime/arcane/modules/ChatRecords.js +18 -15
  98. package/runtime/arcane/modules/CommunicationAppController.js +9 -6
  99. package/runtime/arcane/modules/CommunicationHub.js +9 -6
  100. package/runtime/arcane/modules/CommunicationPreferences.js +4 -1
  101. package/runtime/arcane/modules/CommunicationProviderRegistry.js +4 -1
  102. package/runtime/arcane/modules/ComponentContracts.js +56 -53
  103. package/runtime/arcane/modules/ConfiguredAIChatSession.js +30 -27
  104. package/runtime/arcane/modules/ConversationActionItems.js +15 -12
  105. package/runtime/arcane/modules/ConversationClosingReport.js +11 -8
  106. package/runtime/arcane/modules/ConversationTimebox.js +28 -25
  107. package/runtime/arcane/modules/CoreLocalModelCatalog.js +17 -14
  108. package/runtime/arcane/modules/DBLS.js +7 -4
  109. package/runtime/arcane/modules/DBOPFS.js +7 -7
  110. package/runtime/arcane/modules/DBOPFSDocumentLibrary.js +34 -31
  111. package/runtime/arcane/modules/DevelopmentWorkspace.js +4 -1
  112. package/runtime/arcane/modules/DirectoryPicker.js +8 -5
  113. package/runtime/arcane/modules/DocumentLexicalSearch.js +11 -8
  114. package/runtime/arcane/modules/DocumentNavigation.js +7 -4
  115. package/runtime/arcane/modules/Errors.js +28 -25
  116. package/runtime/arcane/modules/HTMLImport.js +7 -4
  117. package/runtime/arcane/modules/IsolatedModelQuestionRunner.js +15 -12
  118. package/runtime/arcane/modules/LocalAIReadiness.js +21 -18
  119. package/runtime/arcane/modules/LocalAIReadinessController.js +6 -3
  120. package/runtime/arcane/modules/MD.js +1 -1
  121. package/runtime/arcane/modules/Mail.js +46 -43
  122. package/runtime/arcane/modules/MailOutbox.mjs +48 -45
  123. package/runtime/arcane/modules/MailTransport.mjs +29 -26
  124. package/runtime/arcane/modules/MarkdownSpeech.js +1 -50
  125. package/runtime/arcane/modules/MemoryRecords.js +7 -4
  126. package/runtime/arcane/modules/MessageAdvisory.js +6 -3
  127. package/runtime/arcane/modules/ModelDefinition.js +7 -4
  128. package/runtime/arcane/modules/Ollama.js +6 -3
  129. package/runtime/arcane/modules/OllamaModelIdentifier.js +4 -1
  130. package/runtime/arcane/modules/OpenMeteoWeatherProvider.js +18 -15
  131. package/runtime/arcane/modules/PersistentAIChatSession.js +33 -30
  132. package/runtime/arcane/modules/PreferenceStore.js +20 -17
  133. package/runtime/arcane/modules/PreparedSpeech.js +485 -0
  134. package/runtime/arcane/modules/Questionnaire.js +6 -3
  135. package/runtime/arcane/modules/RecordLinkIndex.js +4 -1
  136. package/runtime/arcane/modules/RecordPassageIndex.js +9 -6
  137. package/runtime/arcane/modules/RecordReviewStore.js +12 -9
  138. package/runtime/arcane/modules/RiskSignalAnalyzer.js +4 -1
  139. package/runtime/arcane/modules/ScopedOPFSCache.js +6 -3
  140. package/runtime/arcane/modules/ScreenCapture.js +20 -17
  141. package/runtime/arcane/modules/SpeechPlayback.js +61 -28
  142. package/runtime/arcane/modules/StaticDocumentCatalog.js +33 -30
  143. package/runtime/arcane/modules/SystemAppearance.js +5 -2
  144. package/runtime/arcane/modules/SystemToolRegistry.js +6 -3
  145. package/runtime/arcane/modules/TerminalClient.js +14 -11
  146. package/runtime/arcane/modules/TerminalCommandRegistry.js +4 -1
  147. package/runtime/arcane/modules/ThemeBootstrap.js +9 -6
  148. package/runtime/arcane/modules/ThemeManager.js +5 -2
  149. package/runtime/arcane/modules/TimeGuard.js +1 -1
  150. package/runtime/arcane/modules/ToolCallRouter.js +11 -8
  151. package/runtime/arcane/modules/WaitForComponent.js +19 -16
  152. package/runtime/arcane/modules/YouTubeMedia.js +4 -1
  153. package/runtime/strong-type/index.js +1276 -352
  154. package/runtime/strong-type/package.json +69 -45
  155. package/src/app-descriptor.mjs +17 -14
  156. package/src/application-tests.mjs +4 -1
  157. package/src/cli/main.mjs +8 -5
  158. package/src/constants.mjs +4 -1
  159. package/src/dev-server.mjs +8 -5
  160. package/src/doctor.mjs +5 -2
  161. package/src/dom-event-instrumentation.mjs +24 -20
  162. package/src/errors.mjs +7 -3
  163. package/src/event-manager.mjs +111 -108
  164. package/src/event-queue.mjs +8 -5
  165. package/src/events.mjs +12 -9
  166. package/src/import-map.mjs +36 -27
  167. package/src/installed-sdk-runtime.mjs +4 -1
  168. package/src/integrated-provider-loader.mjs +9 -6
  169. package/src/mail-credentials.mjs +16 -13
  170. package/src/mail-server.mjs +53 -50
  171. package/src/mail.mjs +18 -15
  172. package/src/native-plan.mjs +12 -9
  173. package/src/native-provider-loader.mjs +7 -4
  174. package/src/packager/core.mjs +18 -15
  175. package/src/process.mjs +6 -3
  176. package/src/release-bundle.mjs +14 -11
  177. package/src/runtime.mjs +4 -1
  178. package/src/scaffold.mjs +9 -6
  179. package/src/sdk-browser-runtime.mjs +4 -1
  180. package/src/source-server.mjs +17 -14
  181. package/src/targets/index.mjs +5 -2
  182. package/src/templates/workspace-template.mjs +13 -6
  183. package/src/testing-loader.mjs +7 -4
  184. package/src/testing.mjs +10 -7
  185. package/src/toolchain.mjs +13 -10
  186. package/src/update-check.mjs +9 -6
  187. package/src/workspace-operation-lock.mjs +14 -11
  188. package/src/workspace-runtime.mjs +4 -1
  189. package/src/workspace.mjs +17 -14
  190. package/runtime/arcane/modules/AIResponseLength.js +0 -32
@@ -538,59 +538,35 @@
538
538
  }
539
539
  }
540
540
 
541
- table {
542
- width:calc(100% - .5em);
543
- margin: auto; /* Center the table horizontally */
541
+ /* Native table layout shares columns across rows and grows with complete cell content. */
542
+ .chat_output table {
543
+ width: 100%;
544
+ margin: .75em 0;
544
545
  border-collapse: collapse;
545
- overflow-x: auto; /* Enables horizontal scrolling */
546
- display:table;
547
- background: var(--modal-background);
548
- box-shadow: 0 0 0.7em var(--border-color);
549
- border-radius: 0.5em 0.5em 0.5em 0.5em;
550
- border-spacing: unset;
551
- }
552
-
553
- thead, tbody {
554
- display: table; /* These elements behave like table rows */
555
- width: 100%; /* Table rows take full available space */
556
- table-layout: fixed; /* Ensures the table layout respects the width settings */
557
- }
558
-
559
- tbody tr{
560
- height: 130px;
561
- }
562
-
563
- thead tr{
564
- background: var(--background);
565
- color: var(--platinum);
566
- border-radius: .5em .5em 0 0;
567
- }
568
-
569
- tr{
570
- display: grid;
571
- grid-template-columns:
572
- 6em 1fr 1fr 1fr;
546
+ border-spacing: 0;
547
+ background: var(--arcane-surface, var(--background));
548
+ color: var(--text-color);
573
549
  }
574
550
 
575
- tr:hover{
576
- background:var(--secondary-color);
551
+ .chat_output th,
552
+ .chat_output td {
553
+ padding: .6em .75em;
554
+ border: 1px solid var(--arcane-border, var(--border-color));
555
+ vertical-align: top;
556
+ overflow-wrap: anywhere;
577
557
  }
578
558
 
579
- th, td {
580
- text-align: left;
581
- word-wrap: break-word;
559
+ .chat_output th:not([align]),
560
+ .chat_output td:not([align]) {
561
+ text-align: start;
582
562
  }
583
563
 
584
- th{
585
- border:none;
586
- padding:.4em;
564
+ .chat_output thead {
565
+ background: var(--arcane-surface-muted, var(--secondary-color));
587
566
  }
588
567
 
589
- td{
590
- background: var(--border-color);
591
- padding:.4em;
592
- border: 1px solid var(--accent-color);
593
- overflow: visible;
568
+ .chat_output tbody tr:hover {
569
+ background: var(--arcane-surface-muted, var(--secondary-color));
594
570
  }
595
571
  </style>
596
572
 
@@ -833,6 +809,9 @@
833
809
  </section>
834
810
 
835
811
  <script type="module">
812
+ const {default:Is}=await import('strong-type');
813
+ const is=new Is(false);
814
+
836
815
  const {arcaneLogging}=await import('arcane-os/logging');
837
816
 
838
817
  const {
@@ -909,7 +888,7 @@
909
888
  host.language=host.language||'English';
910
889
  host.name = host.name||'User';
911
890
  host.aiName='AI';
912
- let requestedInitialSpeechMuted=typeof speech.initialMuted==='boolean'
891
+ let requestedInitialSpeechMuted=is.boolean(speech.initialMuted)
913
892
  ?speech.initialMuted
914
893
  :true;
915
894
  let initialSpeechMuteListenerInstalled=false;
@@ -1017,8 +996,8 @@
1017
996
  let causeCode='';
1018
997
  let message='';
1019
998
  try{
1020
- causeCode=typeof error?.code==='string'?error.code:'';
1021
- message=typeof error?.message==='string'?error.message:'';
999
+ causeCode=is.string(error?.code)?error.code:'';
1000
+ message=is.string(error?.message)?error.message:'';
1022
1001
  }catch{}
1023
1002
  return {
1024
1003
  error,
@@ -1035,7 +1014,7 @@
1035
1014
  }
1036
1015
 
1037
1016
  function isPlainRecord(value){
1038
- if(!value||typeof value!=='object'||Array.isArray(value))return false;
1017
+ if(!value||!is.object(value)||is.array(value))return false;
1039
1018
  const prototype=Object.getPrototypeOf(value);
1040
1019
  return prototype===Object.prototype||prototype===null;
1041
1020
  }
@@ -1045,10 +1024,10 @@
1045
1024
  // Only errors deliberately classified by their owner may provide UI copy.
1046
1025
  // Provider and protocol diagnostics remain complete in console/events.
1047
1026
  if(error?.userSafe===true){
1048
- const message=typeof error.userMessage==='string'
1027
+ const message=is.string(error.userMessage)
1049
1028
  ?error.userMessage
1050
1029
  :error.message;
1051
- if(typeof message==='string'&&message.trim()){
1030
+ if(is.string(message)&&message.trim()){
1052
1031
  return message;
1053
1032
  }
1054
1033
  }
@@ -1057,10 +1036,10 @@
1057
1036
  }
1058
1037
 
1059
1038
  function visibleProgressText(value,fallback,field){
1060
- if(typeof value==='string'){
1039
+ if(is.string(value)){
1061
1040
  return value||fallback;
1062
1041
  }
1063
- if(value&&typeof value==='object'){
1042
+ if(value&&is.object(value)){
1064
1043
  arcaneLogging.error(`Arcane chat progress ${field} is not text.`,value);
1065
1044
  return visibleErrorMessage(value,fallback);
1066
1045
  }
@@ -1085,10 +1064,10 @@
1085
1064
 
1086
1065
  function compatibleChatSession(session){
1087
1066
  return Boolean(session)
1088
- &&typeof session==='object'
1089
- &&typeof session.ready==='function'
1090
- &&typeof session.history==='function'
1091
- &&typeof session.send==='function';
1067
+ &&is.object(session)
1068
+ &&is.function(session.ready)
1069
+ &&is.function(session.history)
1070
+ &&is.function(session.send);
1092
1071
  }
1093
1072
 
1094
1073
  function scrollTranscriptToBottom(){
@@ -1102,7 +1081,7 @@
1102
1081
  const value=timestamp instanceof Date
1103
1082
  ?timestamp
1104
1083
  :new Date(timestamp);
1105
- if(timestamp!==undefined&&timestamp!==null&&Number.isFinite(value.getTime())){
1084
+ if(timestamp!==undefined&&timestamp!==null&&is.finite(value.getTime())){
1106
1085
  time.dateTime=value.toISOString();
1107
1086
  time.textContent=value.toLocaleTimeString([],{
1108
1087
  hour:'2-digit',
@@ -1119,7 +1098,7 @@
1119
1098
 
1120
1099
  function normalizeVisibleToolCalls(value){
1121
1100
  if(value===undefined) return [];
1122
- if(!Array.isArray(value)){
1101
+ if(!is.array(value)){
1123
1102
  const failure=new TypeError('Chat session tool calls must be an array.');
1124
1103
  failure.code='AI_CHAT_INVALID_TOOL_CALL';
1125
1104
  throw failure;
@@ -1128,18 +1107,18 @@
1128
1107
  return value.map((call,index)=>{
1129
1108
  if(
1130
1109
  !call
1131
- ||typeof call!=='object'
1132
- ||Array.isArray(call)
1110
+ ||!is.object(call)
1111
+ ||is.array(call)
1133
1112
  ||call.type!=='function'
1134
1113
  ||!call.function
1135
- ||typeof call.function!=='object'
1136
- ||Array.isArray(call.function)
1137
- ||typeof call.id!=='string'
1114
+ ||!is.object(call.function)
1115
+ ||is.array(call.function)
1116
+ ||!is.string(call.id)
1138
1117
  ||!call.id.trim()
1139
1118
  ||ids.has(call.id)
1140
- ||typeof call.function.name!=='string'
1119
+ ||!is.string(call.function.name)
1141
1120
  ||!call.function.name.trim()
1142
- ||typeof call.function.arguments!=='string'
1121
+ ||!is.string(call.function.arguments)
1143
1122
  ){
1144
1123
  const failure=new TypeError(`Chat session tool call ${index+1} is invalid.`);
1145
1124
  failure.code='AI_CHAT_INVALID_TOOL_CALL';
@@ -1157,9 +1136,9 @@
1157
1136
  }
1158
1137
 
1159
1138
  function copyCompleteChatData(value,seen=new Map()){
1160
- if(value===null||typeof value!=='object') return value;
1139
+ if(value===null||!is.object(value)) return value;
1161
1140
  if(seen.has(value)) return seen.get(value);
1162
- if(Array.isArray(value)){
1141
+ if(is.array(value)){
1163
1142
  const result=[];
1164
1143
  seen.set(value,result);
1165
1144
  for(const item of value) result.push(copyCompleteChatData(item,seen));
@@ -1177,12 +1156,12 @@
1177
1156
 
1178
1157
  function sameCompleteChatData(left,right,seen=new Map()){
1179
1158
  if(Object.is(left,right)) return true;
1180
- if(!left||!right||typeof left!=='object'||typeof right!=='object') return false;
1181
- if(Array.isArray(left)!==Array.isArray(right)) return false;
1159
+ if(!left||!right||!is.object(left)||!is.object(right)) return false;
1160
+ if(is.array(left)!==is.array(right)) return false;
1182
1161
  const matched=seen.get(left);
1183
1162
  if(matched!==undefined) return matched===right;
1184
1163
  seen.set(left,right);
1185
- if(Array.isArray(left)){
1164
+ if(is.array(left)){
1186
1165
  return left.length===right.length
1187
1166
  &&left.every((value,index)=>sameCompleteChatData(value,right[index],seen));
1188
1167
  }
@@ -1216,7 +1195,7 @@
1216
1195
  failure.code='AI_CHAT_INVALID_TOOL_CALL';
1217
1196
  throw failure;
1218
1197
  }
1219
- if(typeof argumentsRecord.message!=='string'||!argumentsRecord.message.trim()){
1198
+ if(!is.string(argumentsRecord.message)||!argumentsRecord.message.trim()){
1220
1199
  const failure=new TypeError(
1221
1200
  'Structural tool call arguments must include a nonempty user-facing message.'
1222
1201
  );
@@ -1227,7 +1206,7 @@
1227
1206
  }
1228
1207
 
1229
1208
  function setPendingStructuralToolCalls(calls=[]){
1230
- if(!Array.isArray(calls)||calls.length===0){
1209
+ if(!is.array(calls)||calls.length===0){
1231
1210
  pendingStructuralToolCalls=[];
1232
1211
  pendingStructuralToolMessage='';
1233
1212
  return [];
@@ -1244,8 +1223,8 @@
1244
1223
  }
1245
1224
 
1246
1225
  function sameStructuralToolCalls(left,right){
1247
- return Array.isArray(left)
1248
- &&Array.isArray(right)
1226
+ return is.array(left)
1227
+ &&is.array(right)
1249
1228
  &&left.length===right.length
1250
1229
  &&left.every((call,index)=>sameStructuralToolCall(call,right[index]));
1251
1230
  }
@@ -1308,7 +1287,7 @@
1308
1287
  }
1309
1288
 
1310
1289
  function setTranscriptMessageContent(item,content,timestamp){
1311
- if(typeof content!=='string'){
1290
+ if(!is.string(content)){
1312
1291
  throw new TypeError('Chat session message content must be a string.');
1313
1292
  }
1314
1293
  item.querySelector('.thinking')?.remove();
@@ -1345,8 +1324,8 @@
1345
1324
  }
1346
1325
 
1347
1326
  function latestCommittedTranscriptTurn(transcript,requestMessages){
1348
- if(!Array.isArray(transcript)) return null;
1349
- const expected=Array.isArray(requestMessages)
1327
+ if(!is.array(transcript)) return null;
1328
+ const expected=is.array(requestMessages)
1350
1329
  ?requestMessages
1351
1330
  :[requestMessages];
1352
1331
  if(!expected.length) return null;
@@ -1362,7 +1341,7 @@
1362
1341
  }
1363
1342
 
1364
1343
  function createTranscriptMessage(role,content,name,{timestamp,toolCalls}={}){
1365
- if(typeof content!=='string'){
1344
+ if(!is.string(content)){
1366
1345
  throw new TypeError('Chat session message content must be a string.');
1367
1346
  }
1368
1347
  const item=chatOutput.ownerDocument.createElement('li');
@@ -1402,7 +1381,7 @@
1402
1381
  }
1403
1382
 
1404
1383
  function createSavedRecordFallback(message,index){
1405
- const content=isPlainRecord(message)&&typeof message.content==='string'
1384
+ const content=isPlainRecord(message)&&is.string(message.content)
1406
1385
  ?message.content
1407
1386
  :'Saved record content is available in details.';
1408
1387
  const item=createTranscriptMessage(
@@ -1425,7 +1404,7 @@
1425
1404
  }
1426
1405
 
1427
1406
  function renderSessionHistory(history){
1428
- if(!Array.isArray(history)){
1407
+ if(!is.array(history)){
1429
1408
  throw new TypeError('Chat session history must be an array.');
1430
1409
  }
1431
1410
  const fragment=chatOutput.ownerDocument.createDocumentFragment();
@@ -1442,7 +1421,7 @@
1442
1421
  'AI_CHAT_INCOHERENT_PERSISTENCE'
1443
1422
  );
1444
1423
  }
1445
- if(typeof message.content!=='string'){
1424
+ if(!is.string(message.content)){
1446
1425
  throw chatError(
1447
1426
  'Saved chat contains a record without text content.',
1448
1427
  'AI_CHAT_INCOHERENT_PERSISTENCE'
@@ -1465,7 +1444,7 @@
1465
1444
  if(message.role==='tool'){
1466
1445
  if(message.tool_call_id!==undefined){
1467
1446
  if(
1468
- typeof message.tool_call_id!=='string'
1447
+ !is.string(message.tool_call_id)
1469
1448
  ||!message.tool_call_id.trim()
1470
1449
  ||!message.content.trim()
1471
1450
  ||!restoredPendingToolCalls.has(message.tool_call_id)
@@ -1494,7 +1473,7 @@
1494
1473
  const name=message.role==='user'
1495
1474
  ?host.name
1496
1475
  :message.role==='tool'
1497
- ?typeof message.name==='string'&&message.name.trim()
1476
+ ?is.string(message.name)&&message.name.trim()
1498
1477
  ?`Tool · ${message.name}`
1499
1478
  :'Tool'
1500
1479
  :message.role==='system'
@@ -1573,7 +1552,7 @@
1573
1552
  ai
1574
1553
  });
1575
1554
  await session.ready();
1576
- const history=typeof session.transcript==='function'
1555
+ const history=is.function(session.transcript)
1577
1556
  ?await session.transcript()
1578
1557
  :await session.history();
1579
1558
  if(destroyed||generation!==sessionBindingGeneration){
@@ -1653,10 +1632,10 @@
1653
1632
 
1654
1633
  function isAbortSignal(value){
1655
1634
  return value!==null
1656
- &&typeof value==='object'
1657
- &&typeof value.aborted==='boolean'
1658
- &&typeof value.addEventListener==='function'
1659
- &&typeof value.removeEventListener==='function';
1635
+ &&is.object(value)
1636
+ &&is.boolean(value.aborted)
1637
+ &&is.function(value.addEventListener)
1638
+ &&is.function(value.removeEventListener);
1660
1639
  }
1661
1640
 
1662
1641
  function createChatSubmissionAbort(reason,message){
@@ -1793,7 +1772,7 @@
1793
1772
  host.destroy=destroy;
1794
1773
  host.session=null;
1795
1774
  host.sessionStatus={state:'idle',message:'Chat session is not connected.'};
1796
- host.modelName=typeof host.modelName==='string'?host.modelName:'';
1775
+ host.modelName=is.string(host.modelName)?host.modelName:'';
1797
1776
  host.aiAvailability={llm:false,stt:false,tts:false};
1798
1777
  host.conversationComplete=false;
1799
1778
  applyAIAvailability(host.aiAvailability);
@@ -1826,7 +1805,7 @@
1826
1805
  );
1827
1806
 
1828
1807
  function setConversationComplete(complete=true){
1829
- if(typeof complete!=='boolean'){
1808
+ if(!is.boolean(complete)){
1830
1809
  throw new TypeError('Conversation completion state must be boolean.');
1831
1810
  }
1832
1811
 
@@ -1847,7 +1826,7 @@
1847
1826
  }
1848
1827
 
1849
1828
  function setInitialSpeechMuted(initialMuted){
1850
- if(typeof initialMuted!=='boolean'){
1829
+ if(!is.boolean(initialMuted)){
1851
1830
  throw new TypeError('initialMuted must be boolean');
1852
1831
  }
1853
1832
 
@@ -1883,13 +1862,13 @@
1883
1862
  }={}
1884
1863
  ){
1885
1864
  const messageId=String(id);
1886
- const normalizedTotal=Number.isFinite(Number(total))
1865
+ const normalizedTotal=is.finite(Number(total))
1887
1866
  ?Number(total)
1888
1867
  :0;
1889
- const normalizedCurrent=Number.isFinite(Number(current))
1868
+ const normalizedCurrent=is.finite(Number(current))
1890
1869
  ?Number(current)
1891
1870
  :0;
1892
- const normalizedFailed=Number.isFinite(Number(failed))
1871
+ const normalizedFailed=is.finite(Number(failed))
1893
1872
  ?Number(failed)
1894
1873
  :0;
1895
1874
  const message=messageId
@@ -1998,9 +1977,9 @@
1998
1977
 
1999
1978
  function hasSelectedAIRuntimeRole(role){
2000
1979
  const record=latestAIRuntimeRoles?.[role];
2001
- return typeof record?.providerId==='string'
1980
+ return is.string(record?.providerId)
2002
1981
  &&record.providerId.length>0
2003
- &&typeof record?.modelId==='string'
1982
+ &&is.string(record?.modelId)
2004
1983
  &&record.modelId.length>0;
2005
1984
  }
2006
1985
 
@@ -2046,14 +2025,14 @@
2046
2025
  }
2047
2026
 
2048
2027
  function selected(){
2049
- return typeof role?.providerId==='string'
2028
+ return is.string(role?.providerId)
2050
2029
  &&role.providerId.length>0
2051
- &&typeof role?.modelId==='string'
2030
+ &&is.string(role?.modelId)
2052
2031
  &&role.modelId.length>0;
2053
2032
  }
2054
2033
 
2055
2034
  function modelDisplayName(){
2056
- const configured=typeof host.modelName==='string'
2035
+ const configured=is.string(host.modelName)
2057
2036
  ?host.modelName.trim()
2058
2037
  :'';
2059
2038
  return configured||role?.modelId||'the selected language model';
@@ -2063,9 +2042,9 @@
2063
2042
  const totalBytes=role?.progress?.totalBytes;
2064
2043
  const loadedBytes=role?.progress?.loadedBytes;
2065
2044
  if(
2066
- Number.isFinite(totalBytes)
2045
+ is.finite(totalBytes)
2067
2046
  &&totalBytes>0
2068
- &&Number.isFinite(loadedBytes)
2047
+ &&is.finite(loadedBytes)
2069
2048
  &&loadedBytes>=0
2070
2049
  ){
2071
2050
  return {
@@ -2077,13 +2056,13 @@
2077
2056
  }
2078
2057
  const total=Number(role?.progress?.total);
2079
2058
  const completed=Number(role?.progress?.completed);
2080
- const unit=typeof role?.progress?.unit==='string'&&role.progress.unit
2059
+ const unit=is.string(role?.progress?.unit)&&role.progress.unit
2081
2060
  ?role.progress.unit
2082
2061
  :'items';
2083
2062
  if(
2084
- !Number.isFinite(total)
2063
+ !is.finite(total)
2085
2064
  ||total<=0
2086
- ||!Number.isFinite(completed)
2065
+ ||!is.finite(completed)
2087
2066
  ){
2088
2067
  return null;
2089
2068
  }
@@ -2096,7 +2075,7 @@
2096
2075
  }
2097
2076
 
2098
2077
  function formatBytes(value){
2099
- if(!Number.isFinite(value)||value<0){
2078
+ if(!is.finite(value)||value<0){
2100
2079
  return '';
2101
2080
  }
2102
2081
  const bytes=value;
@@ -2127,7 +2106,7 @@
2127
2106
  }
2128
2107
 
2129
2108
  function approximateDuration(value){
2130
- if(!Number.isFinite(value)||value<0){
2109
+ if(!is.finite(value)||value<0){
2131
2110
  return '';
2132
2111
  }
2133
2112
  const suppliedSeconds=value;
@@ -2151,13 +2130,13 @@
2151
2130
  function activeTransferText(){
2152
2131
  const active=role?.progress?.activeTransfers;
2153
2132
  const limit=role?.progress?.transferLimit;
2154
- const mode=typeof role?.progress?.transferMode==='string'
2133
+ const mode=is.string(role?.progress?.transferMode)
2155
2134
  ?role.progress.transferMode.trim().toLowerCase()
2156
2135
  :'';
2157
2136
  if(
2158
- !Number.isSafeInteger(active)
2137
+ !is.safeInteger(active)
2159
2138
  ||active<0
2160
- ||!Number.isSafeInteger(limit)
2139
+ ||!is.safeInteger(limit)
2161
2140
  ||limit<1
2162
2141
  ||!mode
2163
2142
  ){
@@ -2183,7 +2162,7 @@
2183
2162
 
2184
2163
  function progressElapsedText(){
2185
2164
  const elapsedMilliseconds=Number(role?.progress?.elapsedMs);
2186
- if(!Number.isFinite(elapsedMilliseconds)||elapsedMilliseconds<0){
2165
+ if(!is.finite(elapsedMilliseconds)||elapsedMilliseconds<0){
2187
2166
  return '';
2188
2167
  }
2189
2168
  const elapsedSeconds=Math.floor(elapsedMilliseconds/1000);
@@ -2201,7 +2180,7 @@
2201
2180
 
2202
2181
  function progressMessage(){
2203
2182
  const measured=determinateProgress();
2204
- const phaseValue=typeof role?.progress?.phase==='string'&&role.progress.phase
2183
+ const phaseValue=is.string(role?.progress?.phase)&&role.progress.phase
2205
2184
  ?role.progress.phase
2206
2185
  :'loading';
2207
2186
  const phase=phaseValue.replaceAll('-',' ');
@@ -2217,7 +2196,7 @@
2217
2196
  );
2218
2197
  parts.push(`${formatBytes(remainingBytes)} remaining`);
2219
2198
  const bytesPerSecond=role?.progress?.bytesPerSecond;
2220
- if(Number.isFinite(bytesPerSecond)&&bytesPerSecond>=0){
2199
+ if(is.finite(bytesPerSecond)&&bytesPerSecond>=0){
2221
2200
  parts.push(`${formatBytes(bytesPerSecond)}/s`);
2222
2201
  }else{
2223
2202
  parts.push('Calculating speed');
@@ -2342,7 +2321,7 @@
2342
2321
  requestError='';
2343
2322
  const intent={role:'llm',action,reason:'user'};
2344
2323
  const request={intent,state:role};
2345
- const operationId=typeof role?.operationId==='string'&&role.operationId
2324
+ const operationId=is.string(role?.operationId)&&role.operationId
2346
2325
  ?role.operationId
2347
2326
  :createOperationId('llm-activation');
2348
2327
  const generation=++requestGeneration;
@@ -2480,9 +2459,9 @@
2480
2459
  if(
2481
2460
  !controller
2482
2461
  ||requiredMethods.some(function missingConversationTimeboxMethod(name){
2483
- return typeof controller[name]!=='function';
2462
+ return !is.function(controller[name]);
2484
2463
  })
2485
- ||typeof controller.limitReachedMessage!=='string'
2464
+ ||!is.string(controller.limitReachedMessage)
2486
2465
  ){
2487
2466
  throw new TypeError('A compatible conversation timebox controller is required.');
2488
2467
  }
@@ -2583,7 +2562,7 @@
2583
2562
  }
2584
2563
 
2585
2564
  function queueConversationTimeboxMessage(message,context,guard){
2586
- const operationId=typeof context?.operationId==='string'&&context.operationId
2565
+ const operationId=is.string(context?.operationId)&&context.operationId
2587
2566
  ?context.operationId
2588
2567
  :nextChatOperationId('conversation-timebox');
2589
2568
  const deliveryContext={...context,operationId};
@@ -2597,7 +2576,7 @@
2597
2576
  if(destroyed||aiRuntimeStateAbortController.signal.aborted){
2598
2577
  return false;
2599
2578
  }
2600
- if(typeof guard==='function'&&!guard()){
2579
+ if(is.function(guard)&&!guard()){
2601
2580
  return false;
2602
2581
  }
2603
2582
  let lastError;
@@ -2615,7 +2594,7 @@
2615
2594
  if(destroyed||aiRuntimeStateAbortController.signal.aborted){
2616
2595
  return false;
2617
2596
  }
2618
- if(typeof guard==='function'&&!guard()){
2597
+ if(is.function(guard)&&!guard()){
2619
2598
  return false;
2620
2599
  }
2621
2600
  if(
@@ -2638,7 +2617,7 @@
2638
2617
  if(destroyed||aiRuntimeStateAbortController.signal.aborted){
2639
2618
  return false;
2640
2619
  }
2641
- if(typeof guard==='function'&&!guard()){
2620
+ if(is.function(guard)&&!guard()){
2642
2621
  return false;
2643
2622
  }
2644
2623
  return requireConversationTimeboxDelivery(delivered);
@@ -2657,7 +2636,7 @@
2657
2636
  if(destroyed||aiRuntimeStateAbortController.signal.aborted){
2658
2637
  return false;
2659
2638
  }
2660
- if(typeof guard==='function'&&!guard()){
2639
+ if(is.function(guard)&&!guard()){
2661
2640
  return false;
2662
2641
  }
2663
2642
  if(result!==false){
@@ -2673,7 +2652,7 @@
2673
2652
  if(destroyed||aiRuntimeStateAbortController.signal.aborted){
2674
2653
  return false;
2675
2654
  }
2676
- if(typeof guard==='function'&&!guard()){
2655
+ if(is.function(guard)&&!guard()){
2677
2656
  return false;
2678
2657
  }
2679
2658
  setConversationTimeboxError(
@@ -2746,7 +2725,7 @@
2746
2725
  );
2747
2726
 
2748
2727
  async function streamMessage(text='', id='', isThinking){
2749
- if(typeof text!=='string'){
2728
+ if(!is.string(text)){
2750
2729
  if(text instanceof Error){
2751
2730
  arcaneLogging.error('Arcane chat stream delivered an Error as content.',text);
2752
2731
  throw text;
@@ -2816,10 +2795,8 @@
2816
2795
  &&speech.muted===false
2817
2796
  ){
2818
2797
  const aiRuntime=boundChatAI??globalThis.ai;
2819
- if(typeof aiRuntime?.streamTTS==='function'){
2820
- void Promise.resolve(aiRuntime.streamTTS(
2821
- text,false,{textFormat:'markdown'}
2822
- )).catch(
2798
+ if(is.function(aiRuntime?.streamTTS)){
2799
+ void Promise.resolve(aiRuntime.streamTTS(text)).catch(
2823
2800
  reportTTSError
2824
2801
  );
2825
2802
  }else{
@@ -3072,7 +3049,7 @@
3072
3049
  );
3073
3050
  }
3074
3051
 
3075
- const sessionRequestMessages=Array.isArray(sessionRequest)
3052
+ const sessionRequestMessages=is.array(sessionRequest)
3076
3053
  ?sessionRequest
3077
3054
  :[sessionRequest];
3078
3055
  if(!sessionRequestMessages.length){
@@ -3105,7 +3082,7 @@
3105
3082
  status:'Working'
3106
3083
  }
3107
3084
  );
3108
- const result=typeof session.stream==='function'
3085
+ const result=is.function(session.stream)
3109
3086
  ?await session.stream(
3110
3087
  request,
3111
3088
  {
@@ -3156,13 +3133,13 @@
3156
3133
  ){
3157
3134
  return false;
3158
3135
  }
3159
- if(!result||typeof result!=='object'||Array.isArray(result)){
3136
+ if(!result||!is.object(result)||is.array(result)){
3160
3137
  throw new TypeError('The chat session returned an invalid result.');
3161
3138
  }
3162
- if(!result.message||typeof result.message.content!=='string'){
3139
+ if(!result.message||!is.string(result.message.content)){
3163
3140
  throw new TypeError('The chat session result must contain assistant message content.');
3164
3141
  }
3165
- const committedTurn=typeof session.transcript==='function'
3142
+ const committedTurn=is.function(session.transcript)
3166
3143
  ?latestCommittedTranscriptTurn(
3167
3144
  await session.transcript(),
3168
3145
  sessionRequestMessages
@@ -3353,7 +3330,7 @@
3353
3330
  error.code='AI_CHAT_TOOL_RESULT_NOT_PENDING';
3354
3331
  throw error;
3355
3332
  }
3356
- if(!Array.isArray(options.results)||options.results.length===0){
3333
+ if(!is.array(options.results)||options.results.length===0){
3357
3334
  throw new TypeError('Tool results must be a nonempty array.');
3358
3335
  }
3359
3336
  const statuses=new Map([
@@ -3395,7 +3372,7 @@
3395
3372
  }
3396
3373
  const toolCallId=result.toolCallId;
3397
3374
  if(
3398
- typeof toolCallId!=='string'
3375
+ !is.string(toolCallId)
3399
3376
  ||!pendingById.has(toolCallId)
3400
3377
  ||resultsById.has(toolCallId)
3401
3378
  ){
@@ -3418,11 +3395,11 @@
3418
3395
  'Tool-result status must be executed, declined, cancelled, or not-executed.'
3419
3396
  );
3420
3397
  }
3421
- if(typeof result.message!=='string'||!result.message.trim()){
3398
+ if(!is.string(result.message)||!result.message.trim()){
3422
3399
  throw new TypeError('Tool-result message must contain user-facing text.');
3423
3400
  }
3424
3401
  const persist=result.persist??true;
3425
- if(typeof persist!=='boolean'){
3402
+ if(!is.boolean(persist)){
3426
3403
  throw new TypeError('Tool-result persist must be boolean.');
3427
3404
  }
3428
3405
  resultsById.set(toolCallId,{
@@ -3452,7 +3429,7 @@
3452
3429
  error.code='AI_CHAT_INCOHERENT_PERSISTENCE';
3453
3430
  throw error;
3454
3431
  }
3455
- const operationId=typeof context.operationId==='string'&&context.operationId
3432
+ const operationId=is.string(context.operationId)&&context.operationId
3456
3433
  ?context.operationId
3457
3434
  :nextChatOperationId('tool-results');
3458
3435
  const ownership=createChatSubmissionOwnership(context.signal??null);
@@ -3582,10 +3559,10 @@
3582
3559
  if(destroyed){
3583
3560
  return false;
3584
3561
  }
3585
- if(typeof textOverride!=='string'){
3562
+ if(!is.string(textOverride)){
3586
3563
  throw new TypeError('Programmatic chat messages must be strings.');
3587
3564
  }
3588
- if(!context||typeof context!=='object'||Array.isArray(context)){
3565
+ if(!context||!is.object(context)||is.array(context)){
3589
3566
  throw new TypeError('Chat submission context must be an object.');
3590
3567
  }
3591
3568
  if(host.conversationComplete){
@@ -3612,7 +3589,7 @@
3612
3589
  if(!text.trim()){
3613
3590
  return false;
3614
3591
  }
3615
- const operationId=typeof submissionContext.operationId==='string'
3592
+ const operationId=is.string(submissionContext.operationId)
3616
3593
  &&submissionContext.operationId
3617
3594
  ?submissionContext.operationId
3618
3595
  :nextChatOperationId('message');
@@ -3682,7 +3659,7 @@
3682
3659
  if(host.aiAvailability.tts&&speech.muted===false){
3683
3660
  const aiRuntime=boundChatAI??globalThis.ai;
3684
3661
  aiRuntime?.stopAudio?.();
3685
- if(typeof aiRuntime?.resumeAudio==='function'){
3662
+ if(is.function(aiRuntime?.resumeAudio)){
3686
3663
  void Promise.resolve(aiRuntime.resumeAudio()).catch(
3687
3664
  function reportPlaybackResumeFailure(error){
3688
3665
  reportTTSError(error,'playback-resume');
@@ -3719,7 +3696,7 @@
3719
3696
  if(destroyed){
3720
3697
  return false;
3721
3698
  }
3722
- if(typeof speech.reportTTSError==='function'){
3699
+ if(is.function(speech.reportTTSError)){
3723
3700
  try{
3724
3701
  speech.reportTTSError(error,boundary);
3725
3702
  return true;