arcane-os 0.5.13 → 0.5.14

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 (46) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/browser-runtime/ai/browser-wasm-llm-provider.mjs +11 -10
  3. package/browser-runtime/ai/browser-wllama-runtime.mjs +2 -1
  4. package/browser-runtime/ai/model-controller.mjs +6 -5
  5. package/browser-runtime/ai/speech-worker-client.mjs +45 -3
  6. package/browser-runtime/event-manager.mjs +2 -1
  7. package/browser-runtime/logging.mjs +58 -0
  8. package/docs/reference/README.md +8 -7
  9. package/docs/reference/ai/browser-speech.md +46 -1
  10. package/docs/reference/inventory/package-api.json +30 -2
  11. package/docs/reference/runtime-modules.md +18 -0
  12. package/docs/reference/sdk-api.md +94 -8
  13. package/package.json +3 -2
  14. package/runtime/arcane/components/app-bar.html +3 -1
  15. package/runtime/arcane/components/chat.html +22 -20
  16. package/runtime/arcane/components/dashboard-config.html +3 -1
  17. package/runtime/arcane/components/data-maintenance.html +4 -2
  18. package/runtime/arcane/components/data-view.html +3 -1
  19. package/runtime/arcane/components/directory-picker.html +3 -1
  20. package/runtime/arcane/components/file-manager.html +11 -9
  21. package/runtime/arcane/components/header.html +5 -3
  22. package/runtime/arcane/components/markdown-document.html +3 -1
  23. package/runtime/arcane/components/markdown-editor.html +4 -2
  24. package/runtime/arcane/components/modal.html +3 -1
  25. package/runtime/arcane/components/screen-capture.html +4 -2
  26. package/runtime/arcane/components/speech.html +10 -8
  27. package/runtime/arcane/components/table.html +3 -1
  28. package/runtime/arcane/components/voice-transcription.html +8 -6
  29. package/runtime/arcane/entities/Chat.js +5 -4
  30. package/runtime/arcane/entities/User.js +2 -1
  31. package/runtime/arcane/modules/AI.js +442 -292
  32. package/runtime/arcane/modules/AIProviderRuntime.js +359 -82
  33. package/runtime/arcane/modules/CommunicationAppController.js +2 -1
  34. package/runtime/arcane/modules/ComponentContracts.js +3 -2
  35. package/runtime/arcane/modules/ConversationTimebox.js +2 -1
  36. package/runtime/arcane/modules/DBOPFS.js +5 -4
  37. package/runtime/arcane/modules/Errors.js +3 -14
  38. package/runtime/arcane/modules/HTMLImport.js +4 -3
  39. package/runtime/arcane/modules/LocalAIReadinessController.js +2 -1
  40. package/runtime/arcane/modules/MD.js +3 -2
  41. package/runtime/arcane/modules/MailOutbox.mjs +2 -1
  42. package/runtime/arcane/modules/PersistentAIChatSession.js +2 -1
  43. package/runtime/arcane/modules/ScreenCapture.js +2 -1
  44. package/runtime/arcane/modules/ThemeBootstrap.js +3 -2
  45. package/runtime/arcane/modules/ToolCallRouter.js +2 -1
  46. package/src/import-map.mjs +65 -10
@@ -1,10 +1,12 @@
1
1
  <link rel="stylesheet" href="./arcane/css/theme.css?v=1"><link rel="stylesheet" href="./arcane/css/primitives.css?v=1">
2
2
  <style>:host{display:block}.capture{display:grid;gap:var(--arcane-space-4)}.actions{display:grid;gap:var(--arcane-space-3);grid-template-columns:repeat(3,minmax(0,1fr))}.action{display:grid;gap:var(--arcane-space-2);padding:var(--arcane-space-4);text-align:left}.action strong{font-size:1.05rem}.action span{color:var(--arcane-muted);font-size:.85rem}.recording{align-items:center;display:flex;gap:var(--arcane-space-3);justify-content:space-between;padding:var(--arcane-space-3);border:1px solid var(--arcane-border);border-radius:var(--arcane-radius-medium)}.preview{display:grid;place-items:center;min-height:16rem;overflow:auto;background:var(--arcane-surface-muted);border:1px solid var(--arcane-border);border-radius:var(--arcane-radius-large)}.preview :where(img,video){display:block}.empty{color:var(--arcane-muted);text-align:center}.status{margin:0;color:var(--arcane-muted)}@media(max-width:44rem){.actions{grid-template-columns:1fr}}</style>
3
3
  <section class="capture"><div class="actions"><button class="action arcane-card" type="button" data-capture="image"><strong>Screenshot</strong><span>Capture the selected display as PNG.</span></button><button class="action arcane-card" type="button" data-capture="video"><strong>Record video</strong><span>Record the selected display in a browser-supported video format.</span></button><button class="action arcane-card" type="button" data-capture="gif"><strong>Record GIF</strong><span>Record the selected display as an animated GIF.</span></button></div><div id="recording" class="recording" hidden><span id="recordingLabel">Recording…</span><button id="stop" class="arcane-button" type="button">Stop recording</button></div><div id="preview" class="preview"><div class="empty"><strong>No capture yet</strong><p>Your capture remains on this device unless you choose to save it.</p></div></div><p id="status" class="status" role="status" aria-live="polite">Choose a capture type. Your browser will ask which display to share.</p></section>
4
- <script type="module">const host=this,root=host.shadowRoot,preview=root.querySelector('#preview'),recording=root.querySelector('#recording'),status=root.querySelector('#status');const {default:ScreenCapture}=await import('../modules/ScreenCapture.js');const {createArcaneEventSource,projectArcaneDOMEvent}=await import('arcane-os/event-manager');const events=createArcaneEventSource(host,{source:'arcane.component.screen-capture',eventTypes:['screen-capture-result','screen-capture-ready']});const capture=new ScreenCapture(),listeners=new AbortController();let destroyed=false,operationGeneration=0,previewUrl='';
4
+ <script type="module">
5
+ const {arcaneLogging}=await import('arcane-os/logging');
6
+ const host=this,root=host.shadowRoot,preview=root.querySelector('#preview'),recording=root.querySelector('#recording'),status=root.querySelector('#status');const {default:ScreenCapture}=await import('../modules/ScreenCapture.js');const {createArcaneEventSource,projectArcaneDOMEvent}=await import('arcane-os/event-manager');const events=createArcaneEventSource(host,{source:'arcane.component.screen-capture',eventTypes:['screen-capture-result','screen-capture-ready']});const capture=new ScreenCapture(),listeners=new AbortController();let destroyed=false,operationGeneration=0,previewUrl='';
5
7
  function emit(type,detail={},publicDetail={},operationId=null){if(destroyed)return false;const publication=events.dispatch(type,detail,{operationId,publicDetail});return projectArcaneDOMEvent(host,publication.occurrence,{bubbles:true,composed:true})}
6
8
  function current(generation){return !destroyed&&generation===operationGeneration}
7
- function cleanupCaptureStart(originalError=null){const cleanupErrors=[];if(capture.recorder&&capture.recorder.state!=='inactive'){try{capture.recorder.stop()}catch(error){cleanupErrors.push(error)}}try{capture.stopTracks(capture.stream)}catch(error){cleanupErrors.push(error)}try{capture.reset()}catch(error){cleanupErrors.push(error)}for(const error of cleanupErrors)console.error('Error cleaning up rejected screen capture start:',error);return originalError}
9
+ function cleanupCaptureStart(originalError=null){const cleanupErrors=[];if(capture.recorder&&capture.recorder.state!=='inactive'){try{capture.recorder.stop()}catch(error){cleanupErrors.push(error)}}try{capture.stopTracks(capture.stream)}catch(error){cleanupErrors.push(error)}try{capture.reset()}catch(error){cleanupErrors.push(error)}for(const error of cleanupErrors)arcaneLogging.error('Error cleaning up rejected screen capture start:',error);return originalError}
8
10
  async function onCapture(event){const mode=event.target.closest('[data-capture]')?.dataset.capture;if(!mode||destroyed)return;const generation=++operationGeneration;setButtons(true);try{if(mode==='image'){status.textContent='Choose a display to capture…';const result=await capture.captureImage();if(!current(generation))return;showResult(result,generation)}else{status.textContent='Choose a display to record…';if(mode==='video')await capture.startVideo();else await capture.startGif();if(!current(generation)){cleanupCaptureStart();return}recording.hidden=false;root.querySelector('#recordingLabel').textContent=mode==='gif'?'Recording GIF frames…':'Recording video…';status.textContent='Recording. Return here and press Stop when finished.'}}catch(error){const originalError=mode==='image'?error:cleanupCaptureStart(error);if(!current(generation))return;status.textContent=originalError.message;setButtons(false)}}
9
11
  async function onStop(){if(destroyed)return;const generation=operationGeneration;root.querySelector('#stop').disabled=true;status.textContent='Finishing capture…';try{const result=await capture.stop();if(current(generation)&&result)showResult(result,generation)}catch(error){if(current(generation))status.textContent=error.message}finally{if(current(generation)){recording.hidden=true;root.querySelector('#stop').disabled=false;setButtons(false)}}}
10
12
  root.querySelector('.actions').addEventListener('click',onCapture,{signal:listeners.signal});root.querySelector('#stop').addEventListener('click',onStop,{signal:listeners.signal});
@@ -132,6 +132,8 @@
132
132
  <button id="muteButton" type="button" class="mute" aria-pressed="true" aria-label="Voice unavailable" disabled>Voice unavailable</button>
133
133
 
134
134
  <script type="module">
135
+ const {arcaneLogging}=await import('arcane-os/logging');
136
+
135
137
  const completeValue = (value) => value;
136
138
  const {
137
139
  createArcaneEventSource,
@@ -140,7 +142,7 @@
140
142
 
141
143
  void import('../modules/DBLS.js').catch(
142
144
  function reportDBLSImportFailure(error) {
143
- console.error('The shared database runtime could not load.');
145
+ arcaneLogging.error('The shared database runtime could not load.');
144
146
  }
145
147
  );
146
148
  const aiModuleImport = import('../modules/AI.js');
@@ -365,7 +367,7 @@
365
367
  if (destroyed) {
366
368
  return;
367
369
  }
368
- console.error('The shared AI runtime could not load.');
370
+ arcaneLogging.error('The shared AI runtime could not load.');
369
371
  }
370
372
  );
371
373
 
@@ -476,10 +478,10 @@
476
478
  sttRole = snapshot.roles.stt;
477
479
  ttsRole = snapshot.roles.tts;
478
480
  if (sttRole.state === 'error' && previousSTTRole.state !== 'error') {
479
- console.error('Arcane transcription runtime failed.', sttRole.error);
481
+ arcaneLogging.error('Arcane transcription runtime failed.', sttRole.error);
480
482
  }
481
483
  if (ttsRole.state === 'error' && previousTTSRole.state !== 'error') {
482
- console.error('Arcane voice runtime failed.', ttsRole.error);
484
+ arcaneLogging.error('Arcane voice runtime failed.', ttsRole.error);
483
485
  }
484
486
  clearSettledTTSIntent();
485
487
  applyRoleTransitions(previousSTTRole, previousTTSRole);
@@ -1048,7 +1050,7 @@
1048
1050
  `ARCANE_SPEECH_TTS_${action.toUpperCase()}_REJECTED`
1049
1051
  );
1050
1052
  const reason = `tts-${action}-rejected`;
1051
- console.error(`Arcane voice ${action} request failed.`, error);
1053
+ arcaneLogging.error(`Arcane voice ${action} request failed.`, error);
1052
1054
  dispatchSpeechEvent(
1053
1055
  'speech-tts-lifecycle-error',
1054
1056
  {
@@ -1113,7 +1115,7 @@
1113
1115
  tone: 'error'
1114
1116
  };
1115
1117
  const errorFields = publicSpeechErrorFields(error, failure.code);
1116
- console.error(`Arcane ${normalizedBoundary} speech operation failed.`, error);
1118
+ arcaneLogging.error(`Arcane ${normalizedBoundary} speech operation failed.`, error);
1117
1119
  dispatchSpeechEvent(
1118
1120
  'speech-synthesis-error',
1119
1121
  {
@@ -1535,7 +1537,7 @@
1535
1537
  'ARCANE_SPEECH_STT_TRANSCRIPTION_REQUEST_REJECTED'
1536
1538
  );
1537
1539
  const reason = 'stt-transcription-rejected';
1538
- console.error('Arcane speech transcription failed.', error);
1540
+ arcaneLogging.error('Arcane speech transcription failed.', error);
1539
1541
  dispatchSpeechEvent(
1540
1542
  'speech-transcription-error',
1541
1543
  {
@@ -1642,7 +1644,7 @@
1642
1644
  if (session.audioContext.state !== 'closed') {
1643
1645
  void session.audioContext.close().catch(
1644
1646
  function reportAudioContextCloseFailure(error) {
1645
- console.warn('The microphone audio context could not close.');
1647
+ arcaneLogging.warn('The microphone audio context could not close.');
1646
1648
  }
1647
1649
  );
1648
1650
  }
@@ -17,6 +17,8 @@
17
17
  </table>
18
18
 
19
19
  <script type="module">
20
+ const {arcaneLogging}=await import('arcane-os/logging');
21
+
20
22
  const host = this;
21
23
  const shadowRoot=this.shadowRoot;
22
24
  const {createArcaneEventSource,projectArcaneDOMEvent}=
@@ -68,7 +70,7 @@
68
70
  tableBody.innerHTML = '';
69
71
 
70
72
  tableArray.forEach(
71
- (row)=>{ console.log('row',row);
73
+ (row)=>{ arcaneLogging.log('row',row);
72
74
  const tr = tableBody.insertRow();
73
75
  row.forEach(
74
76
  (cellValue)=>{
@@ -60,6 +60,8 @@
60
60
  </section>
61
61
 
62
62
  <script type="module">
63
+ const {arcaneLogging}=await import('arcane-os/logging');
64
+
63
65
  const completeValue=(value)=>value;
64
66
  const {
65
67
  createArcaneEventSource,
@@ -347,7 +349,7 @@
347
349
  return false;
348
350
  }
349
351
  releaseMicrophone();
350
- console.error('Unable to start recording:',error);
352
+ arcaneLogging.error('Unable to start recording:',error);
351
353
  setState('error',options.messages.startError);
352
354
  return false;
353
355
  }
@@ -497,7 +499,7 @@
497
499
  if(!isCurrentVoiceOperation(generation,'saving')){
498
500
  return false;
499
501
  }
500
- console.error('Unable to save transcription:',error);
502
+ arcaneLogging.error('Unable to save transcription:',error);
501
503
  setState('error',options.messages.saveError);
502
504
  return false;
503
505
  }
@@ -526,7 +528,7 @@
526
528
  );
527
529
  return false;
528
530
  }
529
- console.error('Unable to transcribe recording:',error);
531
+ arcaneLogging.error('Unable to transcribe recording:',error);
530
532
  setState('error',options.messages.transcribeError);
531
533
  return false;
532
534
  }finally{
@@ -634,7 +636,7 @@
634
636
  function recordingError(event){
635
637
  setState('error',options.messages.interrupted);
636
638
  releaseMicrophone();
637
- console.error('Audio recorder error:',event.error||event);
639
+ arcaneLogging.error('Audio recorder error:',event.error||event);
638
640
  }
639
641
 
640
642
  async function completeStream(){
@@ -679,7 +681,7 @@
679
681
  if(!isCurrentVoiceOperation(generation,'completing')){
680
682
  return false;
681
683
  }
682
- console.error('Unable to complete transcription:',error);
684
+ arcaneLogging.error('Unable to complete transcription:',error);
683
685
  setState('error',options.messages.completeError);
684
686
  return false;
685
687
  }
@@ -822,7 +824,7 @@
822
824
  const previousRole=sttRole;
823
825
  sttRole=snapshot.roles.stt;
824
826
  if(sttRole.state==='error'&&previousRole.state!=='error'){
825
- console.error('Arcane transcription runtime failed.',sttRole.error);
827
+ arcaneLogging.error('Arcane transcription runtime failed.',sttRole.error);
826
828
  }
827
829
  sttActivationController.synchronize(sttRole);
828
830
  if(previousRole.state==='ready'&&sttRole.state!=='ready'){
@@ -1,3 +1,4 @@
1
+ import { arcaneLogging } from 'arcane-os/logging';
1
2
  import Is from '../../node_modules/strong-type/index.js';
2
3
  import '../modules/DBOPFS.js';
3
4
  import '../modules/AI.js';
@@ -114,7 +115,7 @@ function structuralToolMessage(call){
114
115
  function storedStructuralToolCalls(value){
115
116
  if(value===undefined) return [];
116
117
  if(!Array.isArray(value)){
117
- console.error('Arcane stored assistant tool calls were not an array and were not retained.');
118
+ arcaneLogging.error('Arcane stored assistant tool calls were not an array and were not retained.');
118
119
  return [];
119
120
  }
120
121
  const result=[];
@@ -122,7 +123,7 @@ function storedStructuralToolCalls(value){
122
123
  try{
123
124
  result.push(copyToolCalls([call])[0]);
124
125
  }catch(error){
125
- console.error(
126
+ arcaneLogging.error(
126
127
  `Arcane stored assistant tool call ${index+1} had no usable user-facing message and was not retained.`,
127
128
  error
128
129
  );
@@ -840,7 +841,7 @@ class ChatEntity{
840
841
  try{
841
842
  return JSON.parse(row);
842
843
  }catch(error){
843
- console.error(
844
+ arcaneLogging.error(
844
845
  `Arcane saved chat row ${index+1} could not be parsed.`,
845
846
  error,
846
847
  row
@@ -951,7 +952,7 @@ ${JSON.stringify(transcript)}`
951
952
  .map(message=>copyCompleteValue(message));
952
953
  const queued=this.#memoryQueue.then(()=>this.#writeMemory(snapshot,request));
953
954
  this.#memoryQueue=queued.catch(()=>{
954
- console.warn('Unable to update chat memory.');
955
+ arcaneLogging.warn('Unable to update chat memory.');
955
956
  return false;
956
957
  });
957
958
  return this.#memoryQueue;
@@ -1,3 +1,4 @@
1
+ import { arcaneLogging } from 'arcane-os/logging';
1
2
  import Is from '../../node_modules/strong-type/index.js';
2
3
  import DBLS from '../modules/DBLS.js';
3
4
  import {
@@ -818,7 +819,7 @@ class UserEntity {
818
819
  try {
819
820
  this[key] = src[key];
820
821
  } catch(e){
821
- console.warn(`UserEntity.explicit setter skipping invalid field ${key}: ${e.message}`);
822
+ arcaneLogging.warn(`UserEntity.explicit setter skipping invalid field ${key}: ${e.message}`);
822
823
  }
823
824
  }
824
825