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
@@ -1,3 +1,6 @@
1
+ import Is from 'strong-type';
2
+ const is=new Is(false);
3
+
1
4
  const ACTION_ITEM_STATUSES=['open','completed'];
2
5
  const ACTION_ITEM_BASES=['user_commitment','optional_homework'];
3
6
  const DEFAULT_PRESENTATION_COOLDOWN_MS=7*24*60*60*1000;
@@ -12,7 +15,7 @@ function invalid(message){
12
15
  }
13
16
 
14
17
  function isPlainRecord(value){
15
- if(!value||typeof value!=='object'||Array.isArray(value)){
18
+ if(!value||!is.object(value)||is.array(value)){
16
19
  return false;
17
20
  }
18
21
 
@@ -21,7 +24,7 @@ function isPlainRecord(value){
21
24
  }
22
25
 
23
26
  function normalizedText(value,label='Action item'){
24
- if(typeof value!=='string'){
27
+ if(!is.string(value)){
25
28
  throw invalid(`${label} must be a string.`);
26
29
  }
27
30
  if(UNSAFE_TEXT_CONTROL_PATTERN.test(value)){
@@ -35,7 +38,7 @@ function normalizedText(value,label='Action item'){
35
38
  }
36
39
 
37
40
  function normalizedId(value,label='Action item id'){
38
- if(typeof value!=='string'||!ACTION_ITEM_ID_PATTERN.test(value)){
41
+ if(!is.string(value)||!ACTION_ITEM_ID_PATTERN.test(value)){
39
42
  throw invalid(`${label} is invalid.`);
40
43
  }
41
44
 
@@ -47,7 +50,7 @@ function normalizedOptionalId(value,label){
47
50
  return null;
48
51
  }
49
52
 
50
- if(typeof value!=='string'||!CONVERSATION_ID_PATTERN.test(value)){
53
+ if(!is.string(value)||!CONVERSATION_ID_PATTERN.test(value)){
51
54
  throw invalid(`${label} is invalid.`);
52
55
  }
53
56
 
@@ -58,7 +61,7 @@ function normalizedTimestamp(value,label,{nullable=false}={}){
58
61
  if(nullable&&(value===undefined||value===null)){
59
62
  return null;
60
63
  }
61
- if(!Number.isSafeInteger(value)||value<0){
64
+ if(!is.safeInteger(value)||value<0){
62
65
  throw invalid(`${label} must be a non-negative integer timestamp.`);
63
66
  }
64
67
 
@@ -70,7 +73,7 @@ function normalizedNow(value){
70
73
  }
71
74
 
72
75
  function normalizedRevision(value){
73
- if(!Number.isSafeInteger(value)||value<1){
76
+ if(!is.safeInteger(value)||value<1){
74
77
  throw invalid('revision must be a positive integer.');
75
78
  }
76
79
 
@@ -98,7 +101,7 @@ function nextRevision(item){
98
101
  }
99
102
 
100
103
  function defaultActionItemId(){
101
- if(typeof globalThis.crypto?.randomUUID==='function'){
104
+ if(is.function(globalThis.crypto?.randomUUID)){
102
105
  return globalThis.crypto.randomUUID();
103
106
  }
104
107
 
@@ -110,7 +113,7 @@ function completeItems(items){
110
113
  }
111
114
 
112
115
  export function normalizeRememberedConversationActions(value=[]){
113
- if(!Array.isArray(value))throw invalid('Remembered actions must be an array.');
116
+ if(!is.array(value))throw invalid('Remembered actions must be an array.');
114
117
 
115
118
  return completeItems(value.map(function normalizeRememberedAction(action){
116
119
  if(!isPlainRecord(action)){
@@ -186,7 +189,7 @@ export function normalizeConversationActionItem(value){
186
189
  }
187
190
 
188
191
  export function normalizeConversationActionItems(value=[]){
189
- if(!Array.isArray(value))throw invalid('Action items must be an array.');
192
+ if(!is.array(value))throw invalid('Action items must be an array.');
190
193
 
191
194
  const ids=new Set();
192
195
  const items=value.map(function normalizeActionItemRecord(item){
@@ -231,7 +234,7 @@ export function rememberConversationActionItems(current=[],actions=[],{
231
234
  }={}){
232
235
  const items=[...normalizeConversationActionItems(current)];
233
236
  const remembered=normalizeRememberedConversationActions(actions);
234
- if(typeof idFactory!=='function'){
237
+ if(!is.function(idFactory)){
235
238
  throw invalid('idFactory must be a function.');
236
239
  }
237
240
 
@@ -353,7 +356,7 @@ export function selectConversationActionItemsForPresentation(current=[],{
353
356
  throw invalid('conversationId is required.');
354
357
  }
355
358
  const timestamp=normalizedNow(now);
356
- if(!Number.isSafeInteger(cooldownMs)||cooldownMs<0){
359
+ if(!is.safeInteger(cooldownMs)||cooldownMs<0){
357
360
  throw invalid('cooldownMs must be a non-negative integer.');
358
361
  }
359
362
  const selected=outstandingConversationActionItems(current)
@@ -390,7 +393,7 @@ export function markConversationActionItemsPresented(current=[],ids=[],{
390
393
  throw invalid('conversationId is required.');
391
394
  }
392
395
  const timestamp=normalizedNow(now);
393
- if(!Array.isArray(ids))throw invalid('Presented ids must be an array.');
396
+ if(!is.array(ids))throw invalid('Presented ids must be an array.');
394
397
  const requested=new Set(ids.map(id=>normalizedId(id)));
395
398
 
396
399
  let matched=0;
@@ -1,3 +1,6 @@
1
+ import Is from 'strong-type';
2
+ const is=new Is(false);
3
+
1
4
  import {
2
5
  CONVERSATION_ACTION_ITEM_BASES,
3
6
  normalizeRememberedConversationActions
@@ -18,7 +21,7 @@ function invalid(message){
18
21
  }
19
22
 
20
23
  function isPlainRecord(value){
21
- if(!value||typeof value!=='object'||Array.isArray(value)){
24
+ if(!value||!is.object(value)||is.array(value)){
22
25
  return false;
23
26
  }
24
27
 
@@ -33,7 +36,7 @@ function normalizedText(value,label,{required=false}={}){
33
36
  }
34
37
  return '';
35
38
  }
36
- if(typeof value!=='string'){
39
+ if(!is.string(value)){
37
40
  throw invalid(`${label} must be a string.`);
38
41
  }
39
42
 
@@ -44,7 +47,7 @@ function normalizedText(value,label,{required=false}={}){
44
47
  }
45
48
 
46
49
  function normalizedFinalMessage(value){
47
- if(typeof value!=='string'){
50
+ if(!is.string(value)){
48
51
  throw invalid('final_message must be a string.');
49
52
  }
50
53
 
@@ -55,7 +58,7 @@ function normalizedFinalMessage(value){
55
58
  }
56
59
 
57
60
  function parseArguments(value){
58
- if(typeof value==='string'){
61
+ if(is.string(value)){
59
62
  if(!value)throw invalid('The closing-report arguments are required.');
60
63
 
61
64
  try{
@@ -91,10 +94,10 @@ export function createConversationClosingReportTool({
91
94
  name=DEFAULT_TOOL_NAME,
92
95
  description='Prepare a complete closing report when the user clearly ends a conversation.'
93
96
  }={}){
94
- if(typeof name!=='string'||!TOOL_NAME_PATTERN.test(name)){
97
+ if(!is.string(name)||!TOOL_NAME_PATTERN.test(name)){
95
98
  throw invalid('The closing-report tool name is invalid.');
96
99
  }
97
- if(typeof description!=='string'||!description.trim()){
100
+ if(!is.string(description)||!description.trim()){
98
101
  throw invalid('The closing-report tool description is invalid.');
99
102
  }
100
103
 
@@ -157,7 +160,7 @@ export function conversationClosingReportInstruction({
157
160
  if(enabled!==true){
158
161
  return '';
159
162
  }
160
- if(typeof toolName!=='string'||!TOOL_NAME_PATTERN.test(toolName)){
163
+ if(!is.string(toolName)||!TOOL_NAME_PATTERN.test(toolName)){
161
164
  throw invalid('The closing-report tool name is invalid.');
162
165
  }
163
166
 
@@ -195,7 +198,7 @@ export function normalizeConversationClosingReport(value){
195
198
  export function classifyConversationClosingReportCalls(calls={}, {
196
199
  toolName=DEFAULT_TOOL_NAME
197
200
  }={}){
198
- if(typeof toolName!=='string'||!TOOL_NAME_PATTERN.test(toolName)){
201
+ if(!is.string(toolName)||!TOOL_NAME_PATTERN.test(toolName)){
199
202
  throw invalid('The closing-report tool name is invalid.');
200
203
  }
201
204
  if(!isPlainRecord(calls)){
@@ -1,3 +1,6 @@
1
+ import Is from 'strong-type';
2
+ const is=new Is(false);
3
+
1
4
  import { arcaneLogging } from 'arcane-os/logging';
2
5
  import {createArcaneEventSource} from 'arcane-os/event-manager';
3
6
 
@@ -64,7 +67,7 @@ const conversationTimeboxTool={
64
67
  };
65
68
 
66
69
  function isPlainRecord(value){
67
- if(!value||typeof value!=='object'||Array.isArray(value))return false;
70
+ if(!value||!is.object(value)||is.array(value))return false;
68
71
  const prototype=Object.getPrototypeOf(value);
69
72
  return prototype===Object.prototype||prototype===null;
70
73
  }
@@ -73,7 +76,7 @@ function parseToolArguments(value){
73
76
  if(isPlainRecord(value)){
74
77
  return value;
75
78
  }
76
- if(typeof value!=='string'){
79
+ if(!is.string(value)){
77
80
  throw new TypeError('The conversation timebox tool requires JSON object arguments.');
78
81
  }
79
82
  try{
@@ -95,13 +98,13 @@ function normalizeConversationTimeboxCommand(value){
95
98
  if(!['set','adjust','clear'].includes(input.action)){
96
99
  throw new TypeError('Conversation timebox action must be "set", "adjust", or "clear".');
97
100
  }
98
- if(typeof input.message!=='string'||!input.message.trim()){
101
+ if(!is.string(input.message)||!input.message.trim()){
99
102
  throw new TypeError('Conversation timebox message must contain user-facing text.');
100
103
  }
101
104
  if(input.action==='clear'){
102
105
  return {...input,action:'clear',message:input.message};
103
106
  }
104
- if(!Number.isSafeInteger(input.duration_milliseconds)||input.duration_milliseconds<=0){
107
+ if(!is.safeInteger(input.duration_milliseconds)||input.duration_milliseconds<=0){
105
108
  throw new TypeError('duration_milliseconds must be an explicit positive whole number.');
106
109
  }
107
110
  return {
@@ -113,7 +116,7 @@ function normalizeConversationTimeboxCommand(value){
113
116
  }
114
117
 
115
118
  function appendConversationTimeboxOpeningInstruction(message=''){
116
- if(typeof message!=='string'){
119
+ if(!is.string(message)){
117
120
  throw new TypeError('The opening message must be a string.');
118
121
  }
119
122
  const base=message.trim();
@@ -123,7 +126,7 @@ function appendConversationTimeboxOpeningInstruction(message=''){
123
126
  }
124
127
 
125
128
  function formatConversationElapsed(milliseconds=0){
126
- if(!Number.isFinite(milliseconds)||milliseconds<0){
129
+ if(!is.finite(milliseconds)||milliseconds<0){
127
130
  throw new TypeError('Elapsed time must be a non-negative finite number.');
128
131
  }
129
132
  const totalSeconds=Math.floor(milliseconds/1000);
@@ -141,7 +144,7 @@ function createConversationTimeboxControlMessage(action,milliseconds){
141
144
  }
142
145
  if(
143
146
  !['set','adjust'].includes(action)
144
- ||!Number.isSafeInteger(milliseconds)
147
+ ||!is.safeInteger(milliseconds)
145
148
  ||milliseconds<=0
146
149
  ){
147
150
  throw new TypeError('A timebox control message requires an explicit positive whole-millisecond duration.');
@@ -160,7 +163,7 @@ function conversationTimeboxSubmissionKey(context={}){
160
163
  const revision=context.timeboxRevision;
161
164
  if(
162
165
  !source.startsWith('conversation-timebox')
163
- ||!Number.isSafeInteger(revision)
166
+ ||!is.safeInteger(revision)
164
167
  ||revision<1
165
168
  ){
166
169
  return '';
@@ -181,10 +184,10 @@ function conversationTimeboxError(message,code,reason,ErrorType=Error){
181
184
 
182
185
  function isAbortSignal(value){
183
186
  return Boolean(value)
184
- &&typeof value==='object'
185
- &&typeof value.aborted==='boolean'
186
- &&typeof value.addEventListener==='function'
187
- &&typeof value.removeEventListener==='function';
187
+ &&is.object(value)
188
+ &&is.boolean(value.aborted)
189
+ &&is.function(value.addEventListener)
190
+ &&is.function(value.removeEventListener);
188
191
  }
189
192
 
190
193
  function normalizeSubscriptionOptions(value={}){
@@ -197,7 +200,7 @@ function normalizeSubscriptionOptions(value={}){
197
200
  );
198
201
  }
199
202
  if((value.once!==undefined
200
- &&typeof value.once!=='boolean'
203
+ &&!is.boolean(value.once)
201
204
  )||(
202
205
  value.signal!==undefined
203
206
  &&value.signal!==null
@@ -289,11 +292,11 @@ class ConversationTimebox{
289
292
  ['cancel',this.#cancel],
290
293
  ['onListenerError',onListenerError]
291
294
  ]){
292
- if(typeof value!=='function'){
295
+ if(!is.function(value)){
293
296
  throw new TypeError(`${name} must be a function.`);
294
297
  }
295
298
  }
296
- if(!Number.isFinite(this.#tickMs)||this.#tickMs<=0){
299
+ if(!is.finite(this.#tickMs)||this.#tickMs<=0){
297
300
  throw new RangeError('tickMs must be a positive finite number.');
298
301
  }
299
302
  this.#events=createArcaneEventSource(this,{
@@ -323,16 +326,16 @@ class ConversationTimebox{
323
326
 
324
327
  setLimitMilliseconds(milliseconds,{source='user'}={}){
325
328
  this.#assertActive();
326
- if(!Number.isSafeInteger(milliseconds)||milliseconds<=0){
329
+ if(!is.safeInteger(milliseconds)||milliseconds<=0){
327
330
  throw new TypeError('Conversation time must be an explicit positive whole number of milliseconds.');
328
331
  }
329
- if(typeof source!=='string'||!source.trim()){
332
+ if(!is.string(source)||!source.trim()){
330
333
  throw new TypeError('Conversation timebox source must contain text.');
331
334
  }
332
335
  const commandSource=source.trim();
333
336
  const now=this.#readClock();
334
337
  const dueAtMs=now+milliseconds;
335
- if(!Number.isSafeInteger(dueAtMs)||dueAtMs>MAX_DATE_MILLISECONDS){
338
+ if(!is.safeInteger(dueAtMs)||dueAtMs>MAX_DATE_MILLISECONDS){
336
339
  throw new RangeError('The requested conversation duration exceeds the supported date range.');
337
340
  }
338
341
  if(this.#startedAtMs===null){
@@ -351,10 +354,10 @@ class ConversationTimebox{
351
354
 
352
355
  adjustLimitMilliseconds(milliseconds,{source='user'}={}){
353
356
  this.#assertActive();
354
- if(!Number.isSafeInteger(milliseconds)||milliseconds<=0){
357
+ if(!is.safeInteger(milliseconds)||milliseconds<=0){
355
358
  throw new TypeError('Conversation adjustment must be an explicit positive whole number of milliseconds.');
356
359
  }
357
- if(typeof source!=='string'||!source.trim()){
360
+ if(!is.string(source)||!source.trim()){
358
361
  throw new TypeError('Conversation timebox source must contain text.');
359
362
  }
360
363
  const commandSource=source.trim();
@@ -364,7 +367,7 @@ class ConversationTimebox{
364
367
 
365
368
  const now=this.#readClock();
366
369
  const dueAtMs=Math.max(now,this.#dueAtMs)+milliseconds;
367
- if(!Number.isSafeInteger(dueAtMs)||dueAtMs>MAX_DATE_MILLISECONDS){
370
+ if(!is.safeInteger(dueAtMs)||dueAtMs>MAX_DATE_MILLISECONDS){
368
371
  throw new RangeError('The requested conversation adjustment exceeds the supported date range.');
369
372
  }
370
373
 
@@ -380,7 +383,7 @@ class ConversationTimebox{
380
383
 
381
384
  clearLimit({source='user'}={}){
382
385
  this.#assertActive();
383
- if(typeof source!=='string'||!source.trim()){
386
+ if(!is.string(source)||!source.trim()){
384
387
  throw new TypeError('Conversation timebox source must contain text.');
385
388
  }
386
389
  const commandSource=source.trim();
@@ -417,7 +420,7 @@ class ConversationTimebox{
417
420
  }
418
421
 
419
422
  subscribe(listener,options={}){
420
- if(typeof listener!=='function'){
423
+ if(!is.function(listener)){
421
424
  throw conversationTimeboxError(
422
425
  'Conversation timebox listener must be a function.',
423
426
  conversationTimeboxErrorCodes.subscriptionHandlerInvalid,
@@ -511,7 +514,7 @@ class ConversationTimebox{
511
514
 
512
515
  #readClock(){
513
516
  const now=this.#clock();
514
- if(!Number.isSafeInteger(now)||now<0||now>MAX_DATE_MILLISECONDS){
517
+ if(!is.safeInteger(now)||now<0||now>MAX_DATE_MILLISECONDS){
515
518
  throw new TypeError('Conversation timebox clock must return a supported non-negative integer timestamp.');
516
519
  }
517
520
  return this.#nowMs===null?now:Math.max(now,this.#nowMs);
@@ -581,7 +584,7 @@ function consumeConversationTimeboxCall(calls,controller){
581
584
  if(!isPlainRecord(calls)){
582
585
  throw new TypeError('Streamed conversation tool calls must be a plain object.');
583
586
  }
584
- if(!controller||typeof controller.applyCommand!=='function'){
587
+ if(!controller||!is.function(controller.applyCommand)){
585
588
  throw new TypeError('A conversation timebox controller is required.');
586
589
  }
587
590
  const remainingCalls={...calls};
@@ -1,3 +1,6 @@
1
+ import Is from 'strong-type';
2
+ const is=new Is(false);
3
+
1
4
  import {normalizeOllamaModelIdentifier} from './OllamaModelIdentifier.js';
2
5
 
3
6
  const MAX_CORE_LOCAL_SPEECH_MODELS=8;
@@ -48,7 +51,7 @@ function modelDescriptor(model){
48
51
  }
49
52
 
50
53
  function boundedParallelRequests(value,{allowZero=false}={}){
51
- return Number.isSafeInteger(value)
54
+ return is.safeInteger(value)
52
55
  &&(allowZero?value>=0:value>=1)
53
56
  ?value
54
57
  :null;
@@ -57,7 +60,7 @@ function boundedParallelRequests(value,{allowZero=false}={}){
57
60
  function admissionCode(compatibility){
58
61
  const code=compatibility?.code;
59
62
 
60
- return typeof code==='string'&&/^[A-Z][A-Z0-9_]{0,95}$/.test(code)
63
+ return is.string(code)&&/^[A-Z][A-Z0-9_]{0,95}$/.test(code)
61
64
  ?code
62
65
  :null;
63
66
  }
@@ -91,8 +94,8 @@ function admissionAwareModelDescriptor(model,{rejected=false,activeFallback=null
91
94
  throw new TypeError('Arcane Core returned an invalid local-model identifier.');
92
95
  }
93
96
 
94
- const compatibility=model?.compatibility&&typeof model.compatibility==='object'
95
- &&!Array.isArray(model.compatibility)
97
+ const compatibility=model?.compatibility&&is.object(model.compatibility)
98
+ &&!is.array(model.compatibility)
96
99
  ?model.compatibility
97
100
  :null;
98
101
  const activeParallelRequests=boundedParallelRequests(
@@ -103,7 +106,7 @@ function admissionAwareModelDescriptor(model,{rejected=false,activeFallback=null
103
106
  {allowZero:true}
104
107
  );
105
108
  const activeParallelRequestsAllowed=
106
- typeof compatibility?.activeParallelRequestsAllowed==='boolean'
109
+ is.boolean(compatibility?.activeParallelRequestsAllowed)
107
110
  ?compatibility.activeParallelRequestsAllowed
108
111
  :null;
109
112
  const repairRequired=compatibility?.creationRequired===true
@@ -160,7 +163,7 @@ function admissionAwareModelDescriptor(model,{rejected=false,activeFallback=null
160
163
  export function getCoreLocalModelCatalog(status){
161
164
  const models=status?.models?.ollama;
162
165
 
163
- if(!Array.isArray(models)){
166
+ if(!is.array(models)){
164
167
  return Object.freeze([]);
165
168
  }
166
169
  return Object.freeze(models.map(modelDescriptor));
@@ -172,10 +175,10 @@ export function getCoreLocalModelCatalog(status){
172
175
  * diagnostic messages in the application UI.
173
176
  */
174
177
  export function getCoreLocalModelCatalogWithAdmissionFailures(status){
175
- const admitted=Array.isArray(status?.models?.ollama)
178
+ const admitted=is.array(status?.models?.ollama)
176
179
  ?status.models.ollama
177
180
  :[];
178
- const rejected=Array.isArray(status?.admission?.rejected)
181
+ const rejected=is.array(status?.admission?.rejected)
179
182
  ?status.admission.rejected
180
183
  :[];
181
184
 
@@ -207,7 +210,7 @@ export function getCoreLocalModelCatalogWithAdmissionFailures(status){
207
210
  }
208
211
 
209
212
  function hasAvailableSpeechRole(models,role){
210
- if(!Array.isArray(models)){
213
+ if(!is.array(models)){
211
214
  return false;
212
215
  }
213
216
  if(models.length>MAX_CORE_LOCAL_SPEECH_MODELS){
@@ -218,16 +221,16 @@ function hasAvailableSpeechRole(models,role){
218
221
  for(const model of models){
219
222
  if(
220
223
  !model
221
- ||typeof model!=='object'
222
- ||Array.isArray(model)
223
- ||typeof model.id!=='string'
224
+ ||!is.object(model)
225
+ ||is.array(model)
226
+ ||!is.string(model.id)
224
227
  ||!model.id
225
228
  ||model.id.length>128
226
- ||typeof model.name!=='string'
229
+ ||!is.string(model.name)
227
230
  ||!model.name
228
231
  ||model.name.length>128
229
232
  ||model.provider!=='speech'
230
- ||!Array.isArray(model.roles)
233
+ ||!is.array(model.roles)
231
234
  ||model.roles.length<1
232
235
  ||model.roles.length>2
233
236
  ||!model.roles.every(candidate=>candidate==='stt'||candidate==='tts')
@@ -1,3 +1,6 @@
1
+ import Is from 'strong-type';
2
+ const is=new Is(false);
3
+
1
4
  import {
2
5
  APP_LOCAL_STORAGE_PREFIX,
3
6
  resolveBrowserApplicationId
@@ -30,8 +33,8 @@ class DBLS {
30
33
  }
31
34
 
32
35
  if(!storage
33
- ||typeof storage.getItem!=='function'
34
- ||typeof storage.setItem!=='function'){
36
+ ||!is.function(storage.getItem)
37
+ ||!is.function(storage.setItem)){
35
38
  const error=new Error('Local storage is unavailable in this browser.');
36
39
  error.code='APP_DATA_STORAGE_UNAVAILABLE';
37
40
  throw error;
@@ -68,7 +71,7 @@ class DBLS {
68
71
 
69
72
  // Set an item
70
73
  set(key='', value) {
71
- if(typeof value !== 'string' && typeof value !== 'number'){
74
+ if(!is.string(value) && !is.number(value)){
72
75
  value=JSON.stringify(value);
73
76
  }
74
77
  this.storage.setItem(this.storageKey(key), value);
@@ -196,7 +199,7 @@ class DBLS {
196
199
  }
197
200
  }
198
201
 
199
- if(typeof window.dbls?.get !== "function"){
202
+ if(!is.function(window.dbls?.get)){
200
203
  window.dbls=new DBLS();
201
204
  window.dbls.ready=true;
202
205
 
@@ -1,5 +1,5 @@
1
1
  import { arcaneLogging } from 'arcane-os/logging';
2
- import Is from "../../node_modules/strong-type/index.js";
2
+ import Is from 'strong-type';
3
3
  import {openApplicationDataDirectory} from './AppDataScope.js';
4
4
  import {
5
5
  createArcaneEventSource,
@@ -208,7 +208,7 @@ class DBOPFS {
208
208
  */
209
209
  async #requestFileWorker(data={},transfer=[]){
210
210
 
211
- if(typeof this.#writeWorker?.postMessage!=='function'){
211
+ if(!is.function(this.#writeWorker?.postMessage)){
212
212
  this.#writeWorker=new Worker(
213
213
  new URL('./DBOPFSWorker.js',import.meta.url)
214
214
  );
@@ -438,7 +438,7 @@ class DBOPFS {
438
438
 
439
439
  let dataToWrite=value
440
440
 
441
- if(typeof value!=='string'){
441
+ if(!is.string(value)){
442
442
  dataToWrite=JSON.stringify(dataToWrite)
443
443
  }
444
444
 
@@ -499,7 +499,7 @@ class DBOPFS {
499
499
  {create:true}
500
500
  );
501
501
 
502
- if(typeof handle.createWritable!=='function'){
502
+ if(!is.function(handle.createWritable)){
503
503
  return this.#writeFileWithWorker(
504
504
  table.name,
505
505
  fileName,
@@ -542,7 +542,7 @@ class DBOPFS {
542
542
  const table=await this.getTableHandle(tableName)
543
543
  const handle=await table.getFileHandle(fileName,{create:false})
544
544
 
545
- if(typeof handle.getFile==='function'){
545
+ if(is.function(handle.getFile)){
546
546
  return handle.getFile()
547
547
  }
548
548
 
@@ -564,7 +564,7 @@ class DBOPFS {
564
564
  const table=await this.getTableHandle(tableName)
565
565
  const handle=await table.getFileHandle(fileName,{create:false})
566
566
 
567
- if(typeof handle.getFile!=='function'){
567
+ if(!is.function(handle.getFile)){
568
568
  return {
569
569
  lastModified:null,
570
570
  size:null,
@@ -1177,7 +1177,7 @@ class DBOPFS {
1177
1177
  }
1178
1178
  }
1179
1179
 
1180
- if(typeof window.dbopfs?.get!=="function"){
1180
+ if(!is.function(window.dbopfs?.get)){
1181
1181
  window.dbopfs=new DBOPFS();
1182
1182
  }
1183
1183