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
  import { arcaneLogging } from 'arcane-os/logging';
2
5
  export const MAIL_OUTBOX_PROTOCOL='arcane-mail-outbox/1';
3
6
  export const MAIL_OUTBOX_TABLE='mail_outbox';
@@ -30,7 +33,7 @@ function coded(error,code){
30
33
 
31
34
  function reportMailOutboxObserverError(error){
32
35
  try{
33
- if(typeof globalThis.reportError==='function'){
36
+ if(is.function(globalThis.reportError)){
34
37
  globalThis.reportError(error);
35
38
  return;
36
39
  }
@@ -45,14 +48,14 @@ function fail(message,code='MAIL_OUTBOX_INVALID',ErrorType=Error,cause){
45
48
 
46
49
  function isPlainRecord(value){
47
50
  return Boolean(value)
48
- &&typeof value==='object'
49
- &&!Array.isArray(value)
51
+ &&is.object(value)
52
+ &&!is.array(value)
50
53
  &&Object.getPrototypeOf(value)===Object.prototype;
51
54
  }
52
55
 
53
56
  function timestamp(value,label,{nullable=false}={}){
54
57
  if(nullable&&value===null) return null;
55
- if(!Number.isSafeInteger(value)||value<0){
58
+ if(!is.safeInteger(value)||value<0){
56
59
  fail(`${label} must be a nonnegative epoch-millisecond integer.`,'MAIL_OUTBOX_RECORD_INVALID');
57
60
  }
58
61
  return value;
@@ -61,31 +64,31 @@ function timestamp(value,label,{nullable=false}={}){
61
64
  function clockMilliseconds(clock){
62
65
  const value=clock();
63
66
  const milliseconds=value instanceof Date?value.getTime():Number(value);
64
- if(!Number.isSafeInteger(milliseconds)||milliseconds<0){
67
+ if(!is.safeInteger(milliseconds)||milliseconds<0){
65
68
  fail('Mail outbox clock returned an invalid time.','MAIL_OUTBOX_CLOCK_INVALID');
66
69
  }
67
70
  return milliseconds;
68
71
  }
69
72
 
70
73
  function safeString(value,pattern){
71
- return typeof value==='string'&&pattern.test(value)?value:null;
74
+ return is.string(value)&&pattern.test(value)?value:null;
72
75
  }
73
76
 
74
77
  function safeStatusCode(value){
75
- return Number.isSafeInteger(value)&&value>=100&&value<=599?value:null;
78
+ return is.safeInteger(value)&&value>=100&&value<=599?value:null;
76
79
  }
77
80
 
78
81
  function retryAfterSeconds(value){
79
82
  if(value===undefined||value===null) return null;
80
83
  const seconds=Number(value);
81
- if(!Number.isSafeInteger(seconds)||seconds<0){
84
+ if(!is.safeInteger(seconds)||seconds<0){
82
85
  return null;
83
86
  }
84
87
  return seconds;
85
88
  }
86
89
 
87
90
  function parseSerializedReport(value){
88
- if(typeof value!=='string'||!value){
91
+ if(!is.string(value)||!value){
89
92
  fail('Mail outbox record contains no serialized report.','MAIL_OUTBOX_RECORD_INVALID');
90
93
  }
91
94
  let parsed;
@@ -115,7 +118,7 @@ function serializeReport(report){
115
118
  }catch(error){
116
119
  fail('Mail outbox report must be JSON serializable.','MAIL_OUTBOX_REPORT_INVALID',TypeError,error);
117
120
  }
118
- if(typeof serialized!=='string'||!serialized){
121
+ if(!is.string(serialized)||!serialized){
119
122
  fail('Mail outbox report must be JSON serializable.','MAIL_OUTBOX_REPORT_INVALID',TypeError);
120
123
  }
121
124
  return serialized;
@@ -124,13 +127,13 @@ function serializeReport(report){
124
127
  function quarantineSnapshot(value){
125
128
  try{
126
129
  const serialized=JSON.stringify(value);
127
- if(typeof serialized==='string')return serialized;
130
+ if(is.string(serialized))return serialized;
128
131
  }catch{}
129
132
  return null;
130
133
  }
131
134
 
132
135
  function reportKey(value){
133
- if(typeof value!=='string'||!REPORT_KEY_PATTERN.test(value)){
136
+ if(!is.string(value)||!REPORT_KEY_PATTERN.test(value)){
134
137
  fail(
135
138
  'Mail outbox reportKey must contain safe characters.',
136
139
  'MAIL_OUTBOX_REPORT_KEY_INVALID',
@@ -145,7 +148,7 @@ function recordFileName(value){
145
148
  }
146
149
 
147
150
  function reportKeyFromFileName(value){
148
- if(typeof value!=='string'||!value.endsWith(FILE_SUFFIX)){
151
+ if(!is.string(value)||!value.endsWith(FILE_SUFFIX)){
149
152
  fail('Mail outbox contains an invalid record filename.','MAIL_OUTBOX_RECORD_INVALID');
150
153
  }
151
154
  return reportKey(value.slice(0,-FILE_SUFFIX.length));
@@ -158,7 +161,7 @@ function invalidRecordMetadata(fileName,error){
158
161
  repairable=true;
159
162
  }catch{}
160
163
  return {
161
- fileName:typeof fileName==='string'?fileName:'',
164
+ fileName:is.string(fileName)?fileName:'',
162
165
  code:safeString(error?.code,SAFE_CODE_PATTERN)||'MAIL_OUTBOX_RECORD_UNREADABLE',
163
166
  repairable
164
167
  };
@@ -174,7 +177,7 @@ function normalizedFailure(value){
174
177
  fail('Mail outbox failure record is invalid.','MAIL_OUTBOX_RECORD_INVALID');
175
178
  }
176
179
  const code=safeString(value.code,SAFE_CODE_PATTERN);
177
- if(!code||typeof value.retryable!=='boolean'||typeof value.uncertain!=='boolean'){
180
+ if(!code||!is.boolean(value.retryable)||!is.boolean(value.uncertain)){
178
181
  fail('Mail outbox failure record is invalid.','MAIL_OUTBOX_RECORD_INVALID');
179
182
  }
180
183
  return {
@@ -209,8 +212,8 @@ function normalizedResult(value,{invalidCode='MAIL_OUTBOX_RECORD_INVALID'}={}){
209
212
  acceptanceAuthority,
210
213
  requestId:safeString(value.requestId,SAFE_ID_PATTERN),
211
214
  providerId:safeString(value.providerId,SAFE_ID_PATTERN),
212
- providerStatus:typeof value.providerStatus==='string'
213
- ||Number.isSafeInteger(value.providerStatus)
215
+ providerStatus:is.string(value.providerStatus)
216
+ ||is.safeInteger(value.providerStatus)
214
217
  ?value.providerStatus
215
218
  :null,
216
219
  providerCode:safeString(value.providerCode,SAFE_CODE_PATTERN),
@@ -303,7 +306,7 @@ function normalizedRecord(value,expectedReportKey=null){
303
306
  if(!STATE_SET.has(value.state)){
304
307
  fail('Mail outbox record contains an invalid state.','MAIL_OUTBOX_RECORD_INVALID');
305
308
  }
306
- if(!Number.isSafeInteger(value.attempts)||value.attempts<0){
309
+ if(!is.safeInteger(value.attempts)||value.attempts<0){
307
310
  fail('Mail outbox record contains an invalid attempt count.','MAIL_OUTBOX_RECORD_INVALID');
308
311
  }
309
312
  const record={
@@ -432,12 +435,12 @@ function deliveryFailure(error){
432
435
  const milliseconds=Number(error?.retryAfterMs);
433
436
  const seconds=retryAfterSeconds(
434
437
  error?.retryAfterSeconds??(
435
- Number.isFinite(milliseconds)&&milliseconds>=0
438
+ is.finite(milliseconds)&&milliseconds>=0
436
439
  ?Math.ceil(milliseconds/1000)
437
440
  :null
438
441
  )
439
442
  );
440
- const errorRecord=error&&typeof error==='object'&&!Array.isArray(error)
443
+ const errorRecord=error&&is.object(error)&&!is.array(error)
441
444
  ?{...error}
442
445
  :{};
443
446
  return {
@@ -445,9 +448,9 @@ function deliveryFailure(error){
445
448
  result:null,
446
449
  failure:{
447
450
  ...errorRecord,
448
- name:typeof error?.name==='string'?error.name:'Error',
449
- message:typeof error?.message==='string'?error.message:String(error??''),
450
- ...(typeof error?.stack==='string'?{stack:error.stack}:{}),
451
+ name:is.string(error?.name)?error.name:'Error',
452
+ message:is.string(error?.message)?error.message:String(error??''),
453
+ ...(is.string(error?.stack)?{stack:error.stack}:{}),
451
454
  code:safeString(error?.code,SAFE_CODE_PATTERN)||'MAIL_DELIVERY_FAILED',
452
455
  statusCode:safeStatusCode(error?.statusCode),
453
456
  retryable,
@@ -484,10 +487,10 @@ function abortedDeliveryError(error,signal){
484
487
 
485
488
  function validateSignal(signal){
486
489
  if(signal===null||signal===undefined) return null;
487
- if(typeof signal!=='object'
488
- ||typeof signal.aborted!=='boolean'
489
- ||typeof signal.addEventListener!=='function'
490
- ||typeof signal.removeEventListener!=='function'){
490
+ if(!is.object(signal)
491
+ ||!is.boolean(signal.aborted)
492
+ ||!is.function(signal.addEventListener)
493
+ ||!is.function(signal.removeEventListener)){
491
494
  fail('Mail outbox signal must be an AbortSignal.','MAIL_OUTBOX_INVALID',TypeError);
492
495
  }
493
496
  return signal;
@@ -522,7 +525,7 @@ function waitForOutboxOperation(operation,signal){
522
525
  }
523
526
 
524
527
  function drainReason(value){
525
- if(typeof value!=='string'||!/^[a-z][a-z0-9_-]*$/.test(value)){
528
+ if(!is.string(value)||!/^[a-z][a-z0-9_-]*$/.test(value)){
526
529
  fail('Mail outbox drain reason is invalid.','MAIL_OUTBOX_INVALID',TypeError);
527
530
  }
528
531
  return value;
@@ -583,16 +586,16 @@ class MailOutbox{
583
586
  return globalThis.navigator?.onLine!==false;
584
587
  },
585
588
  lockManager=undefined,
586
- onlineTarget=typeof globalThis.addEventListener==='function'?globalThis:null,
589
+ onlineTarget=is.function(globalThis.addEventListener)?globalThis:null,
587
590
  onRecordCommitted=null,
588
591
  quarantineTable=QUARANTINE_TABLE,
589
592
  table=MAIL_OUTBOX_TABLE
590
593
  }={}){
591
- if(!storage||typeof storage!=='object'){
594
+ if(!storage||!is.object(storage)){
592
595
  fail('Mail outbox storage is required.','MAIL_OUTBOX_STORAGE_UNAVAILABLE',TypeError);
593
596
  }
594
597
  for(const method of ['get','set','getAllKeys']){
595
- if(typeof storage[method]!=='function'){
598
+ if(!is.function(storage[method])){
596
599
  fail(
597
600
  `Mail outbox storage must provide ${method}().`,
598
601
  'MAIL_OUTBOX_STORAGE_UNAVAILABLE',
@@ -603,36 +606,36 @@ class MailOutbox{
603
606
  const resolvedLockManager=lockManager
604
607
  ??storage.lockManager
605
608
  ??globalThis.navigator?.locks;
606
- if(!resolvedLockManager||typeof resolvedLockManager.request!=='function'){
609
+ if(!resolvedLockManager||!is.function(resolvedLockManager.request)){
607
610
  fail(
608
611
  'Mail outbox requires a Web Locks compatible lock manager.',
609
612
  'MAIL_OUTBOX_LOCK_UNAVAILABLE',
610
613
  TypeError
611
614
  );
612
615
  }
613
- if(typeof deliver!=='function'){
616
+ if(!is.function(deliver)){
614
617
  fail('Mail outbox deliver must be a function.','MAIL_OUTBOX_INVALID',TypeError);
615
618
  }
616
- if(typeof clock!=='function'||typeof isOnline!=='function'){
619
+ if(!is.function(clock)||!is.function(isOnline)){
617
620
  fail('Mail outbox clock and isOnline hooks must be functions.','MAIL_OUTBOX_INVALID',TypeError);
618
621
  }
619
622
  if(onlineTarget!==null&&(
620
- typeof onlineTarget?.addEventListener!=='function'
621
- ||typeof onlineTarget?.removeEventListener!=='function'
623
+ !is.function(onlineTarget?.addEventListener)
624
+ ||!is.function(onlineTarget?.removeEventListener)
622
625
  )){
623
626
  fail('Mail outbox onlineTarget must be an EventTarget.','MAIL_OUTBOX_INVALID',TypeError);
624
627
  }
625
- if(onRecordCommitted!==null&&typeof onRecordCommitted!=='function'){
628
+ if(onRecordCommitted!==null&&!is.function(onRecordCommitted)){
626
629
  fail(
627
630
  'Mail outbox onRecordCommitted must be a function.',
628
631
  'MAIL_OUTBOX_INVALID',
629
632
  TypeError
630
633
  );
631
634
  }
632
- if(typeof table!=='string'||!TABLE_PATTERN.test(table)){
635
+ if(!is.string(table)||!TABLE_PATTERN.test(table)){
633
636
  fail('Mail outbox table name is invalid.','MAIL_OUTBOX_INVALID',TypeError);
634
637
  }
635
- if(typeof quarantineTable!=='string'||!TABLE_PATTERN.test(quarantineTable)
638
+ if(!is.string(quarantineTable)||!TABLE_PATTERN.test(quarantineTable)
636
639
  ||quarantineTable===table){
637
640
  fail('Mail outbox quarantine table name is invalid.','MAIL_OUTBOX_INVALID',TypeError);
638
641
  }
@@ -656,7 +659,7 @@ class MailOutbox{
656
659
 
657
660
  async #ready(){
658
661
  try{
659
- if(this.#storage.readyPromise&&typeof this.#storage.readyPromise.then==='function'){
662
+ if(this.#storage.readyPromise&&is.function(this.#storage.readyPromise.then)){
660
663
  await this.#storage.readyPromise;
661
664
  }
662
665
  }catch(error){
@@ -684,7 +687,7 @@ class MailOutbox{
684
687
  if(!this.#onRecordCommitted) return;
685
688
  try{
686
689
  const result=this.#onRecordCommitted(record);
687
- if(result&&typeof result.then==='function'){
690
+ if(result&&is.function(result.then)){
688
691
  result.catch(reportMailOutboxObserverError);
689
692
  }
690
693
  }catch(error){
@@ -773,7 +776,7 @@ class MailOutbox{
773
776
  }catch(error){
774
777
  fail('Mail outbox could not list durable storage.','MAIL_OUTBOX_STORAGE_FAILED',Error,error);
775
778
  }
776
- if(!Array.isArray(keys)){
779
+ if(!is.array(keys)){
777
780
  fail('Mail outbox storage returned an invalid key list.','MAIL_OUTBOX_STORAGE_FAILED');
778
781
  }
779
782
  return [...keys];
@@ -807,7 +810,7 @@ class MailOutbox{
807
810
  }
808
811
 
809
812
  #assertInvalidTarget(fileName,inventory){
810
- if(typeof fileName!=='string'||!fileName){
813
+ if(!is.string(fileName)||!fileName){
811
814
  fail('Mail outbox invalid-record filename is required.','MAIL_OUTBOX_INVALID',TypeError);
812
815
  }
813
816
  const target=inventory.invalidRecords.find(
@@ -848,7 +851,7 @@ class MailOutbox{
848
851
  }
849
852
 
850
853
  #assertDeleteAvailable(){
851
- if(typeof this.#storage.delete!=='function'){
854
+ if(!is.function(this.#storage.delete)){
852
855
  fail(
853
856
  'Mail outbox storage does not support invalid-record deletion.',
854
857
  'MAIL_OUTBOX_DELETE_UNAVAILABLE'
@@ -1026,7 +1029,7 @@ class MailOutbox{
1026
1029
  }
1027
1030
 
1028
1031
  async enqueue({report,reportKey:key}={}, {attempt=true,signal=null}={}){
1029
- if(typeof attempt!=='boolean'){
1032
+ if(!is.boolean(attempt)){
1030
1033
  fail('Mail outbox attempt must be boolean.','MAIL_OUTBOX_INVALID',TypeError);
1031
1034
  }
1032
1035
  const resolvedSignal=validateSignal(signal);
@@ -1,3 +1,6 @@
1
+ import Is from 'strong-type';
2
+ const is=new Is(false);
3
+
1
4
  const REPORT_KEY_PATTERN=/^[a-zA-Z0-9._:-]+$/;
2
5
  const REQUEST_ID_PATTERN=/^[a-zA-Z0-9-]+$/;
3
6
  const PROVIDER_ID_PATTERN=/^[a-zA-Z0-9._:-]+$/;
@@ -18,16 +21,16 @@ export class MailTransportError extends Error {
18
21
  this.code=code;
19
22
  this.details=details;
20
23
  this.retryable=Boolean(retryable);
21
- this.retryAfterMs=Number.isSafeInteger(retryAfterMs)&&retryAfterMs>0
24
+ this.retryAfterMs=is.safeInteger(retryAfterMs)&&retryAfterMs>0
22
25
  ? retryAfterMs
23
26
  : 0;
24
- this.statusCode=Number.isSafeInteger(statusCode)?statusCode:0;
27
+ this.statusCode=is.safeInteger(statusCode)?statusCode:0;
25
28
  this.uncertain=Boolean(uncertain);
26
29
  }
27
30
  }
28
31
 
29
32
  export function normalizeMailEndpoint(endpoint,base=globalThis.location?.href){
30
- if(typeof endpoint!=='string'||!endpoint.trim()){
33
+ if(!is.string(endpoint)||!endpoint.trim()){
31
34
  throw new Error('Mail endpoint is required');
32
35
  }
33
36
  let url;
@@ -47,7 +50,7 @@ export function normalizeMailEndpoint(endpoint,base=globalThis.location?.href){
47
50
  }
48
51
 
49
52
  export function serializeMailReport(report){
50
- if(!report||typeof report!=='object'||Array.isArray(report)){
53
+ if(!report||!is.object(report)||is.array(report)){
51
54
  throw new Error('Mail report must be a JSON object');
52
55
  }
53
56
  try{
@@ -62,7 +65,7 @@ export function serializeMailReport(report){
62
65
  }
63
66
 
64
67
  function validateSerializedReport(serializedReport){
65
- if(typeof serializedReport!=='string'||!serializedReport){
68
+ if(!is.string(serializedReport)||!serializedReport){
66
69
  throw new Error('Serialized mail report is required');
67
70
  }
68
71
  let parsed;
@@ -71,7 +74,7 @@ function validateSerializedReport(serializedReport){
71
74
  }catch{
72
75
  throw new Error('Serialized mail report must contain valid JSON');
73
76
  }
74
- if(!parsed||typeof parsed!=='object'||Array.isArray(parsed)){
77
+ if(!parsed||!is.object(parsed)||is.array(parsed)){
75
78
  throw new Error('Serialized mail report must contain a JSON object');
76
79
  }
77
80
  return serializedReport;
@@ -80,14 +83,14 @@ function validateSerializedReport(serializedReport){
80
83
  function parseJsonObject(value){
81
84
  try{
82
85
  const parsed=JSON.parse(value);
83
- return parsed&&typeof parsed==='object'&&!Array.isArray(parsed)?parsed:null;
86
+ return parsed&&is.object(parsed)&&!is.array(parsed)?parsed:null;
84
87
  }catch{
85
88
  return null;
86
89
  }
87
90
  }
88
91
 
89
92
  function parseRetryAfter(value,now=Date.now()){
90
- if(typeof value!=='string'||!value.trim()){
93
+ if(!is.string(value)||!value.trim()){
91
94
  return 0;
92
95
  }
93
96
  const trimmed=value.trim();
@@ -95,7 +98,7 @@ function parseRetryAfter(value,now=Date.now()){
95
98
  return Math.max(0,Math.ceil(Number(trimmed)*1000));
96
99
  }
97
100
  const timestamp=Date.parse(trimmed);
98
- return Number.isFinite(timestamp)
101
+ return is.finite(timestamp)
99
102
  ? Math.max(0,timestamp-now)
100
103
  : 0;
101
104
  }
@@ -110,19 +113,19 @@ function invalidSuccessResponse(response,responseText){
110
113
  function parseDeliveryResponse(response,responseText){
111
114
  const body=parseJsonObject(responseText);
112
115
  if(!body
113
- || typeof body.requestId!=='string'||!REQUEST_ID_PATTERN.test(body.requestId)
116
+ || !is.string(body.requestId)||!REQUEST_ID_PATTERN.test(body.requestId)
114
117
  || !Object.hasOwn(RESPONSE_CONTRACT,body.status)
115
118
  || RESPONSE_CONTRACT[body.status]!==response.status) {
116
119
  throw invalidSuccessResponse(response,responseText);
117
120
  }
118
121
  for(const field of ['accepted','rejected']){
119
122
  if(body[field]!==undefined
120
- && (!Number.isSafeInteger(body[field])||body[field]<0)) {
123
+ && (!is.safeInteger(body[field])||body[field]<0)) {
121
124
  throw invalidSuccessResponse(response,responseText);
122
125
  }
123
126
  }
124
127
  if(body.providerId!==undefined
125
- && (typeof body.providerId!=='string'||!PROVIDER_ID_PATTERN.test(body.providerId))){
128
+ && (!is.string(body.providerId)||!PROVIDER_ID_PATTERN.test(body.providerId))){
126
129
  throw invalidSuccessResponse(response,responseText);
127
130
  }
128
131
 
@@ -135,7 +138,7 @@ function parseDeliveryResponse(response,responseText){
135
138
  status:body.status,
136
139
  statusCode:response.status,
137
140
  ...(body.providerId?{providerId:body.providerId}:{}),
138
- retryAfterMs:Number.isSafeInteger(body.retryAfterMs)&&body.retryAfterMs>0
141
+ retryAfterMs:is.safeInteger(body.retryAfterMs)&&body.retryAfterMs>0
139
142
  ? body.retryAfterMs
140
143
  : parseRetryAfter(response.headers?.get?.('retry-after')),
141
144
  };
@@ -143,14 +146,14 @@ function parseDeliveryResponse(response,responseText){
143
146
 
144
147
  function parseRejection(response,responseText){
145
148
  const body=parseJsonObject(responseText);
146
- const source=body?.error&&typeof body.error==='object'&&!Array.isArray(body.error)
149
+ const source=body?.error&&is.object(body.error)&&!is.array(body.error)
147
150
  ? body.error
148
151
  : body;
149
152
  const rawCode=source?.code;
150
- const code=typeof rawCode==='string'&&ERROR_CODE_PATTERN.test(rawCode)
153
+ const code=is.string(rawCode)&&ERROR_CODE_PATTERN.test(rawCode)
151
154
  ? rawCode
152
155
  : `MAIL_HTTP_${String(response.status)}`;
153
- let retryable=typeof source?.retryable==='boolean'
156
+ let retryable=is.boolean(source?.retryable)
154
157
  ? source.retryable
155
158
  : RETRYABLE_STATUS_CODES.has(response.status);
156
159
  if(code==='invalid_idempotent_request'||NON_RETRYABLE_RATE_CODES.has(code)){
@@ -158,8 +161,8 @@ function parseRejection(response,responseText){
158
161
  }else if(code==='concurrent_idempotent_requests'){
159
162
  retryable=true;
160
163
  }
161
- const uncertain=typeof source?.uncertain==='boolean'?source.uncertain:false;
162
- const bodyRetryAfter=Number.isSafeInteger(source?.retryAfterMs)&&source.retryAfterMs>0
164
+ const uncertain=is.boolean(source?.uncertain)?source.uncertain:false;
165
+ const bodyRetryAfter=is.safeInteger(source?.retryAfterMs)&&source.retryAfterMs>0
163
166
  ? source.retryAfterMs
164
167
  : 0;
165
168
  return new MailTransportError(`Mail server rejected the request (${response.status})`,{
@@ -173,8 +176,8 @@ function parseRejection(response,responseText){
173
176
  }
174
177
 
175
178
  async function readResponseText(response){
176
- if(!response.body||typeof response.body.getReader!=='function'){
177
- if(typeof response.text!=='function'){
179
+ if(!response.body||!is.function(response.body.getReader)){
180
+ if(!is.function(response.text)){
178
181
  throw new MailTransportError('Mail server returned an unreadable response',{
179
182
  code:'MAIL_UNREADABLE_RESPONSE',statusCode:response.status,uncertain:true,
180
183
  });
@@ -225,21 +228,21 @@ export async function sendMailReport({
225
228
  serializedReport,
226
229
  signal,
227
230
  }){
228
- if(typeof fetchImpl!=='function'){
231
+ if(!is.function(fetchImpl)){
229
232
  throw new MailTransportError('Mail transport is unavailable',{code:'MAIL_UNAVAILABLE'});
230
233
  }
231
234
  const resolvedEndpoint=normalizeMailEndpoint(endpoint);
232
- if(typeof appName!=='string'||!/^[a-z0-9](?:[a-z0-9-]{0,62})$/.test(appName)){
235
+ if(!is.string(appName)||!/^[a-z0-9](?:[a-z0-9-]{0,62})$/.test(appName)){
233
236
  throw new Error('Mail application identity is invalid');
234
237
  }
235
- if(typeof reportKey!=='string'||!REPORT_KEY_PATTERN.test(reportKey)){
238
+ if(!is.string(reportKey)||!REPORT_KEY_PATTERN.test(reportKey)){
236
239
  throw new Error('Mail report key must contain safe characters');
237
240
  }
238
241
  if(requestTimeout!==null&&requestTimeout!==undefined
239
- &&(!Number.isSafeInteger(requestTimeout)||requestTimeout<1)){
242
+ &&(!is.safeInteger(requestTimeout)||requestTimeout<1)){
240
243
  throw new Error('Mail request timeout must be a positive integer');
241
244
  }
242
- if(appKey!==undefined&&appKey!==null&&typeof appKey!=='string'){
245
+ if(appKey!==undefined&&appKey!==null&&!is.string(appKey)){
243
246
  throw new Error('Mail application key must be a string');
244
247
  }
245
248
  if(signal!==undefined&&!(signal instanceof AbortSignal)){
@@ -252,7 +255,7 @@ export async function sendMailReport({
252
255
  'Idempotency-Key':reportKey,
253
256
  'X-Mail-App':appName,
254
257
  };
255
- if(typeof appKey==='string'&&appKey){
258
+ if(is.string(appKey)&&appKey){
256
259
  headers['X-Mail-Key']=appKey;
257
260
  }
258
261
 
@@ -1,50 +1 @@
1
- const FORMATTING_MARKERS=new Set(['*','#','_','`','~']);
2
-
3
- // Speech-only filtering; single markers and all other text remain literal.
4
- class MarkdownSpeech {
5
- #pendingMarker='';
6
- #repeated=false;
7
-
8
- append(text='',end=false){
9
- if(typeof text!=='string'){
10
- throw new TypeError('Markdown speech input must be text.');
11
- }
12
-
13
- let narration='';
14
-
15
- for(const character of text){
16
- if(character===this.#pendingMarker){
17
- this.#repeated=true;
18
- continue;
19
- }
20
-
21
- if(this.#pendingMarker&&!this.#repeated){
22
- narration+=this.#pendingMarker;
23
- }
24
- this.reset();
25
-
26
- if(FORMATTING_MARKERS.has(character)){
27
- // Wait for the next character to distinguish one mark from a run.
28
- this.#pendingMarker=character;
29
- }else{
30
- narration+=character;
31
- }
32
- }
33
-
34
- if(end){
35
- if(this.#pendingMarker&&!this.#repeated){
36
- narration+=this.#pendingMarker;
37
- }
38
- this.reset();
39
- }
40
-
41
- return narration;
42
- }
43
-
44
- reset(){
45
- this.#pendingMarker='';
46
- this.#repeated=false;
47
- }
48
- }
49
-
50
- export {MarkdownSpeech};
1
+ export {MarkdownSpeech} from 'arcane-os/speech-text';
@@ -1,5 +1,8 @@
1
+ import Is from 'strong-type';
2
+ const is=new Is(false);
3
+
1
4
  function normalizeMemoryContent(content=''){
2
- if(typeof content!=='string'){
5
+ if(!is.string(content)){
3
6
  return '';
4
7
  }
5
8
 
@@ -9,7 +12,7 @@ function normalizeMemoryContent(content=''){
9
12
  try{
10
13
  const parsed=JSON.parse(normalized);
11
14
 
12
- if(typeof parsed!=='string'){
15
+ if(!is.string(parsed)){
13
16
  break;
14
17
  }
15
18
 
@@ -23,14 +26,14 @@ function normalizeMemoryContent(content=''){
23
26
  }
24
27
 
25
28
  function hasMemoryContent(memory={}){
26
- const records=Array.isArray(memory)
29
+ const records=is.array(memory)
27
30
  ?memory
28
31
  :[memory];
29
32
 
30
33
  return records.some(
31
34
  record=>Boolean(
32
35
  normalizeMemoryContent(
33
- typeof record==='string'
36
+ is.string(record)
34
37
  ?record
35
38
  :record?.memory
36
39
  )
@@ -1,7 +1,10 @@
1
+ import Is from 'strong-type';
2
+ const is=new Is(false);
3
+
1
4
  function completeText(value){return String(value??'');}
2
5
 
3
6
  export function normalizeContentAdvisory(value){
4
- if(!value||typeof value!=='object')return null;
7
+ if(!value||!is.object(value))return null;
5
8
  return {
6
9
  level:['critical','high','caution','low','unavailable'].includes(value.level)?value.level:'caution',
7
10
  title:completeText(value.title??'Content advisory'),
@@ -27,10 +30,10 @@ export function unavailableMessageInspection(messages){
27
30
 
28
31
  export async function inspectMessageRecords(messages,inspector,{prepare}={}){
29
32
  const advisories=new Map();let failures=0;
30
- if(typeof inspector!=='function')return {advisories,failures};
33
+ if(!is.function(inspector))return {advisories,failures};
31
34
  const records=Array.from(messages||[]);
32
35
  let context;
33
- try{context=typeof prepare==='function'?await prepare(records):undefined;}
36
+ try{context=is.function(prepare)?await prepare(records):undefined;}
34
37
  catch{return unavailableMessageInspection(records);}
35
38
  for(const message of records){
36
39
  try{const advisory=normalizeContentAdvisory(await inspector(message,context));if(advisory){if(advisory.level==='unavailable')failures+=1;advisories.set(message,advisory);}}
@@ -1,3 +1,6 @@
1
+ import Is from 'strong-type';
2
+ const is=new Is(false);
3
+
1
4
  const MODEL_REFERENCE=/^[A-Za-z0-9][A-Za-z0-9._/-]*(?::[A-Za-z0-9][A-Za-z0-9._-]*)?$/;
2
5
  const PARAMETER_NAME=/^[a-z][a-z0-9_]*$/;
3
6
 
@@ -8,13 +11,13 @@ function fail(message){
8
11
  }
9
12
 
10
13
  async function completeResponseText(response){
11
- if(typeof response?.text!=='function'){
14
+ if(!is.function(response?.text)){
12
15
  fail('The model definition response is not readable.');
13
16
  }
14
17
 
15
18
  const text=await response.text();
16
19
 
17
- if(typeof text!=='string'){
20
+ if(!is.string(text)){
18
21
  fail('The model definition response is not readable text.');
19
22
  }
20
23
 
@@ -27,7 +30,7 @@ async function completeResponseText(response){
27
30
  * are rejected so browser prompts cannot silently drift from the definition.
28
31
  */
29
32
  export function parseModelDefinition(source){
30
- if(typeof source!=='string'
33
+ if(!is.string(source)
31
34
  ||!source
32
35
  ||source.includes('\0')
33
36
  ){
@@ -85,7 +88,7 @@ export function parseModelDefinition(source){
85
88
  export async function loadModelDefinitionSystemPrompt(url,{
86
89
  fetchImpl=globalThis.fetch
87
90
  }={}){
88
- if(typeof fetchImpl!=='function'){
91
+ if(!is.function(fetchImpl)){
89
92
  fail('A browser fetch implementation is required.');
90
93
  }
91
94