arcane-os 0.2.3 → 0.3.0

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 (117) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +8 -8
  3. package/browser-runtime/ARCANE_SDK_BROWSER_RELEASE.json +29 -22
  4. package/browser-runtime/ai/ARCANE_AI_BROWSER_SPEECH_COMPONENTS.json +203 -0
  5. package/browser-runtime/ai/browser-kokoro-worker.mjs +11 -2
  6. package/browser-runtime/ai/browser-speech-artifacts.mjs +3230 -397
  7. package/browser-runtime/ai/browser-speech-providers.mjs +1141 -157
  8. package/browser-runtime/ai/browser-speech.mjs +2 -0
  9. package/browser-runtime/ai/browser-whisper-worker.mjs +11 -2
  10. package/browser-runtime/ai/model-controller.mjs +285 -95
  11. package/browser-runtime/ai/speech-worker-client.mjs +247 -32
  12. package/browser-runtime/ai/speech-worker-runtime.mjs +2310 -167
  13. package/browser-runtime/event-manager.mjs +1097 -1
  14. package/docs/architecture.md +2 -2
  15. package/docs/event-manager.md +155 -27
  16. package/docs/reference/README.md +20 -20
  17. package/docs/reference/ai/browser-speech-package-authority.json +835 -0
  18. package/docs/reference/ai/browser-speech.md +1162 -246
  19. package/docs/reference/ai/browser-wasm.md +18 -7
  20. package/docs/reference/availability-and-normalization.md +2 -2
  21. package/docs/reference/behavioral-testing.md +28 -5
  22. package/docs/reference/cli.md +115 -7
  23. package/docs/reference/core/arcane-ai-contracts.md +1 -1
  24. package/docs/reference/event-manager.md +577 -32
  25. package/docs/reference/inventory/package-api.json +478 -2
  26. package/docs/reference/inventory/runtime-components.json +86 -37
  27. package/docs/reference/inventory/runtime-modules.json +126 -49
  28. package/docs/reference/mail.md +316 -0
  29. package/docs/reference/protocols.md +143 -33
  30. package/docs/reference/runtime-components.md +192 -80
  31. package/docs/reference/runtime-modules.md +584 -70
  32. package/docs/reference/sdk-api.md +1014 -25
  33. package/package.json +5 -4
  34. package/runtime/ARCANE_RUNTIME_RELEASE.json +145 -140
  35. package/runtime/arcane/components/app-bar.html +34 -13
  36. package/runtime/arcane/components/assistant-panel.html +110 -57
  37. package/runtime/arcane/components/calculator.html +7 -4
  38. package/runtime/arcane/components/chart.html +58 -17
  39. package/runtime/arcane/components/chat.html +606 -136
  40. package/runtime/arcane/components/conversation-view.html +13 -6
  41. package/runtime/arcane/components/dashboard-config.html +96 -59
  42. package/runtime/arcane/components/data-maintenance.html +69 -14
  43. package/runtime/arcane/components/data-view.html +53 -7
  44. package/runtime/arcane/components/directory-picker.html +118 -32
  45. package/runtime/arcane/components/document-inspector.html +47 -10
  46. package/runtime/arcane/components/file-drop.html +81 -35
  47. package/runtime/arcane/components/file-inspector.html +72 -22
  48. package/runtime/arcane/components/file-manager.html +374 -79
  49. package/runtime/arcane/components/integration-settings.html +12 -5
  50. package/runtime/arcane/components/local-ai-status.html +48 -19
  51. package/runtime/arcane/components/markdown-document.html +161 -68
  52. package/runtime/arcane/components/markdown-editor.html +110 -33
  53. package/runtime/arcane/components/media-embed.html +8 -5
  54. package/runtime/arcane/components/modal.html +15 -5
  55. package/runtime/arcane/components/output-panel.html +28 -23
  56. package/runtime/arcane/components/preferences-form.html +22 -4
  57. package/runtime/arcane/components/record-timeline.html +18 -2
  58. package/runtime/arcane/components/relationship-board.html +23 -3
  59. package/runtime/arcane/components/screen-capture.html +10 -4
  60. package/runtime/arcane/components/source-code-viewer.html +76 -9
  61. package/runtime/arcane/components/source-explanation.html +23 -3
  62. package/runtime/arcane/components/speech.html +453 -131
  63. package/runtime/arcane/components/summary-strip.html +22 -11
  64. package/runtime/arcane/components/table.html +39 -21
  65. package/runtime/arcane/components/task-progress.html +79 -21
  66. package/runtime/arcane/components/terminal-workspace.html +7 -4
  67. package/runtime/arcane/components/theme-editor.html +7 -3
  68. package/runtime/arcane/components/unified-inbox.html +9 -4
  69. package/runtime/arcane/components/voice-transcription.html +244 -72
  70. package/runtime/arcane/components/weather-widget.html +5 -3
  71. package/runtime/arcane/components/web-navigator.html +48 -8
  72. package/runtime/arcane/entities/Chat.js +1 -1
  73. package/runtime/arcane/entities/User.js +110 -23
  74. package/runtime/arcane/modules/AI.js +2109 -130
  75. package/runtime/arcane/modules/AIProviderRuntime.js +720 -13
  76. package/runtime/arcane/modules/AIRuntimeState.js +109 -52
  77. package/runtime/arcane/modules/ApiModelDatabase.js +390 -17
  78. package/runtime/arcane/modules/BrowserTestSuite.js +205 -28
  79. package/runtime/arcane/modules/CalculatorEngine.js +63 -3
  80. package/runtime/arcane/modules/CommunicationAppController.js +588 -28
  81. package/runtime/arcane/modules/CommunicationHub.js +590 -10
  82. package/runtime/arcane/modules/ComponentContracts.js +235 -37
  83. package/runtime/arcane/modules/ConversationTimebox.js +152 -33
  84. package/runtime/arcane/modules/DBLS.js +40 -7
  85. package/runtime/arcane/modules/DBOPFS.js +35 -11
  86. package/runtime/arcane/modules/DataMaintenance.js +12 -2
  87. package/runtime/arcane/modules/Errors.js +65 -7
  88. package/runtime/arcane/modules/HTMLImport.js +198 -14
  89. package/runtime/arcane/modules/LocalAIReadinessController.js +208 -29
  90. package/runtime/arcane/modules/Mail.js +738 -115
  91. package/runtime/arcane/modules/MailOutbox.mjs +1395 -0
  92. package/runtime/arcane/modules/MailTransport.mjs +197 -39
  93. package/runtime/arcane/modules/Ollama.js +36 -1
  94. package/runtime/arcane/modules/OpenMeteoWeatherProvider.js +583 -7
  95. package/runtime/arcane/modules/PreferenceStore.js +367 -33
  96. package/runtime/arcane/modules/RecordReviewStore.js +322 -23
  97. package/runtime/arcane/modules/ScreenCapture.js +1397 -15
  98. package/runtime/arcane/modules/SpeechPlayback.js +438 -41
  99. package/runtime/arcane/modules/TerminalClient.js +277 -12
  100. package/runtime/arcane/modules/ThemeBootstrap.js +80 -6
  101. package/runtime/arcane/modules/ThemeManager.js +39 -7
  102. package/runtime/arcane/modules/TimeGuard.js +131 -20
  103. package/runtime/arcane/modules/WaitForComponent.js +386 -33
  104. package/schemas/arcane-lock.schema.json +2 -2
  105. package/src/cli/main.mjs +435 -9
  106. package/src/event-manager.mjs +1097 -1
  107. package/src/import-map.mjs +21 -3
  108. package/src/index.mjs +13 -0
  109. package/src/installed-sdk-runtime.mjs +112 -0
  110. package/src/mail-api.mjs +22 -0
  111. package/src/mail-credentials.mjs +667 -0
  112. package/src/mail-server.mjs +1769 -0
  113. package/src/mail.mjs +261 -0
  114. package/src/sdk-browser-runtime.mjs +85 -41
  115. package/src/testing-loader.mjs +7 -0
  116. package/src/toolchain.mjs +3 -0
  117. package/src/workspace.mjs +1 -1
@@ -2,9 +2,28 @@ import {
2
2
  checkLocalAIReadiness,
3
3
  deriveLocalAIRequirements
4
4
  } from './LocalAIReadiness.js?v=4';
5
+ import {
6
+ createArcaneEventSource,
7
+ projectArcaneDOMEvent
8
+ } from 'arcane-os/event-manager';
5
9
 
6
10
  const SLOT_NAMES=Object.freeze(['llm','stt','tts']);
7
11
 
12
+ export const LOCAL_AI_READINESS_CONTROLLER_EVENT_TYPES=Object.freeze({
13
+ changed:'local-ai-readiness-change'
14
+ });
15
+ export const LOCAL_AI_READINESS_CONTROLLER_ERROR_CODES=Object.freeze({
16
+ aborted:'ARCANE_LOCAL_AI_READINESS_CONTROLLER_ABORTED',
17
+ disposed:'ARCANE_LOCAL_AI_READINESS_CONTROLLER_DISPOSED',
18
+ statusReadyTimeout:'ARCANE_LOCAL_AI_STATUS_COMPONENT_READY_TIMEOUT'
19
+ });
20
+ export const LOCAL_AI_READINESS_CONTROLLER_REASONS=Object.freeze({
21
+ checked:'local-ai-readiness-checked',
22
+ aborted:'local-ai-readiness-controller-aborted',
23
+ disposed:'local-ai-readiness-controller-disposed',
24
+ statusReadyTimeout:'local-ai-status-component-ready-timeout'
25
+ });
26
+
8
27
  function availabilityFromReport(report={}){
9
28
  const slots=report.slots||{};
10
29
  return Object.freeze(Object.fromEntries(SLOT_NAMES.map(name=>{
@@ -29,19 +48,85 @@ function pendingReport(requirements){
29
48
  });
30
49
  }
31
50
 
32
- async function waitForStatusComponent(status){
51
+ function localAIReadinessAbortError(reason,disposed=false){
52
+ const code=disposed
53
+ ?LOCAL_AI_READINESS_CONTROLLER_ERROR_CODES.disposed
54
+ :LOCAL_AI_READINESS_CONTROLLER_ERROR_CODES.aborted;
55
+ const errorReason=disposed
56
+ ?LOCAL_AI_READINESS_CONTROLLER_REASONS.disposed
57
+ :LOCAL_AI_READINESS_CONTROLLER_REASONS.aborted;
58
+ if(reason instanceof Error
59
+ &&reason.name==='AbortError'
60
+ &&reason.code===code
61
+ &&reason.reason===errorReason){
62
+ return reason;
63
+ }
64
+
65
+ const error=new Error(
66
+ disposed
67
+ ?'The local AI readiness controller has been disposed.'
68
+ :'The local AI readiness check was aborted.'
69
+ );
70
+ error.name='AbortError';
71
+ error.code=code;
72
+ error.reason=errorReason;
73
+ if(reason!==undefined){
74
+ error.cause=reason;
75
+ }
76
+ return error;
77
+ }
78
+
79
+ async function waitForStatusComponent(status,signal){
33
80
  if(!status||status.ready){
34
81
  return status;
35
82
  }
36
83
 
37
- await new Promise(resolve=>{
38
- const timeout=globalThis.setTimeout(done,10000);
39
- function done(){
84
+ await new Promise(function waitForLocalAIStatusPromise(resolve,reject){
85
+ let settled=false;
86
+ const timeout=globalThis.setTimeout(failTimeout,10000);
87
+
88
+ function cleanup(){
40
89
  globalThis.clearTimeout(timeout);
41
- status.removeEventListener('local-ai-status-ready',done);
90
+ status.removeEventListener('local-ai-status-ready',complete);
91
+ signal?.removeEventListener('abort',abortWait);
92
+ }
93
+
94
+ function complete(){
95
+ if(settled){
96
+ return;
97
+ }
98
+ settled=true;
99
+ cleanup();
42
100
  resolve();
43
101
  }
44
- status.addEventListener('local-ai-status-ready',done,{once:true});
102
+
103
+ function fail(error){
104
+ if(settled){
105
+ return;
106
+ }
107
+ settled=true;
108
+ cleanup();
109
+ reject(error);
110
+ }
111
+
112
+ function failTimeout(){
113
+ const error=new Error('Local AI status component readiness timed out after 10000 ms.');
114
+ error.code=LOCAL_AI_READINESS_CONTROLLER_ERROR_CODES.statusReadyTimeout;
115
+ error.reason=LOCAL_AI_READINESS_CONTROLLER_REASONS.statusReadyTimeout;
116
+ fail(error);
117
+ }
118
+
119
+ function abortWait(){
120
+ fail(localAIReadinessAbortError(signal?.reason));
121
+ }
122
+
123
+ if(signal?.aborted){
124
+ abortWait();
125
+ return;
126
+ }
127
+
128
+ signal?.addEventListener('abort',abortWait,{once:true});
129
+ status.addEventListener('local-ai-status-ready',complete,{once:true});
45
130
  });
46
131
  return status;
47
132
  }
@@ -62,35 +147,76 @@ export function createLocalAIReadinessController({
62
147
  throw new TypeError('A chat component is required.');
63
148
  }
64
149
 
150
+ const lifecycleController=new AbortController();
151
+ const eventOwner=Object.freeze({kind:'local-ai-readiness-controller'});
152
+ const events=createArcaneEventSource(
153
+ eventOwner,
154
+ {
155
+ source:'local-ai-readiness-controller',
156
+ eventTypes:Object.freeze(
157
+ Object.values(LOCAL_AI_READINESS_CONTROLLER_EVENT_TYPES)
158
+ )
159
+ }
160
+ );
65
161
  let active=null;
162
+ let chatReadyListenerPending=false;
163
+ let destroyed=false;
66
164
  let latestReport=null;
67
165
  let currentAvailability=availabilityFromReport({});
166
+ let operationSequence=0;
68
167
 
69
- const publishAvailability=()=>{
168
+ function publishAvailability(){
169
+ if(destroyed){
170
+ return;
171
+ }
70
172
  if(typeof chat.setAIAvailability==='function'){
173
+ if(chatReadyListenerPending){
174
+ chat.removeEventListener('chat-ready',publishAvailability);
175
+ }
176
+ chatReadyListenerPending=false;
71
177
  chat.setAIAvailability(currentAvailability);
72
- }else{
73
- chat.addEventListener('chat-ready',publishAvailability,{once:true});
178
+ }else if(!chatReadyListenerPending){
179
+ chatReadyListenerPending=true;
180
+ chat.addEventListener(
181
+ 'chat-ready',
182
+ publishAvailability,
183
+ {
184
+ once:true,
185
+ signal:lifecycleController.signal
186
+ }
187
+ );
74
188
  }
75
- };
189
+ }
76
190
 
77
- const applyAvailability=report=>{
191
+ function applyAvailability(report){
78
192
  currentAvailability=availabilityFromReport(report);
79
193
  publishAvailability();
80
- };
194
+ }
81
195
 
82
- const check=async()=>{
196
+ async function check(){
197
+ if(destroyed){
198
+ throw localAIReadinessAbortError(
199
+ lifecycleController.signal.reason,
200
+ true
201
+ );
202
+ }
83
203
  if(active){
84
204
  return active;
85
205
  }
86
206
 
87
- active=(async()=>{
207
+ active=(async function runLocalAIReadinessCheck(){
88
208
  const preferenceTuple=selectedPreferences(preferences);
89
209
  const requirements=deriveLocalAIRequirements(preferenceTuple);
90
210
  const required=SLOT_NAMES.some(name=>requirements[name].required);
91
211
 
92
212
  applyAvailability(pendingReport(requirements));
93
- await waitForStatusComponent(status);
213
+ await waitForStatusComponent(status,lifecycleController.signal);
214
+ if(destroyed){
215
+ throw localAIReadinessAbortError(
216
+ lifecycleController.signal.reason,
217
+ true
218
+ );
219
+ }
94
220
  status?.configure?.({profileHref});
95
221
  if(required){
96
222
  status?.begin?.(requirements);
@@ -102,42 +228,95 @@ export function createLocalAIReadinessController({
102
228
  ...checkOptions,
103
229
  preferences:preferenceTuple
104
230
  });
231
+ if(destroyed){
232
+ throw localAIReadinessAbortError(
233
+ lifecycleController.signal.reason,
234
+ true
235
+ );
236
+ }
105
237
  status?.present?.(latestReport);
106
238
  applyAvailability(latestReport);
107
239
  if(typeof onChange==='function'){
108
240
  onChange(latestReport);
109
241
  }
110
- chat.dispatchEvent(new CustomEvent('local-ai-readiness-change',{
111
- bubbles:true,
112
- composed:true,
113
- detail:{report:latestReport}
114
- }));
242
+ const availability=availabilityFromReport(latestReport);
243
+ if(destroyed){
244
+ throw localAIReadinessAbortError(
245
+ lifecycleController.signal.reason,
246
+ true
247
+ );
248
+ }
249
+ operationSequence+=1;
250
+ const operationId=`${events.instanceId}:check:${operationSequence.toString(36)}`;
251
+ const reason=LOCAL_AI_READINESS_CONTROLLER_REASONS.checked;
252
+ const publication=events.dispatch(
253
+ LOCAL_AI_READINESS_CONTROLLER_EVENT_TYPES.changed,
254
+ Object.freeze({operationId,reason,report:latestReport}),
255
+ {
256
+ operationId,
257
+ publicDetail:Object.freeze({availability,reason})
258
+ }
259
+ );
260
+ projectArcaneDOMEvent(
261
+ chat,
262
+ publication.occurrence,
263
+ {
264
+ bubbles:true,
265
+ composed:true
266
+ }
267
+ );
115
268
  return latestReport;
116
- })().finally(()=>{
269
+ })().finally(function releaseLocalAIReadinessCheck(){
117
270
  active=null;
118
271
  });
119
272
 
120
273
  return active;
121
- };
274
+ }
122
275
 
123
- const ensure=()=>{
276
+ function ensure(){
124
277
  if(active){
125
278
  return active;
126
279
  }
127
280
  return latestReport
128
281
  ?Promise.resolve(latestReport)
129
282
  :check();
130
- };
283
+ }
284
+
285
+ function retry(){
286
+ void check().catch(function ignoreDestroyedLocalAIRetry(error){
287
+ if(error?.name!=='AbortError'){
288
+ console.error('Local AI readiness retry failed.',error);
289
+ }
290
+ });
291
+ }
292
+ status?.addEventListener(
293
+ 'local-ai-retry',
294
+ retry,
295
+ {signal:lifecycleController.signal}
296
+ );
131
297
 
132
- const retry=()=>void check();
133
- status?.addEventListener('local-ai-retry',retry);
298
+ function destroy(){
299
+ if(destroyed){
300
+ return false;
301
+ }
302
+ destroyed=true;
303
+ lifecycleController.abort(
304
+ localAIReadinessAbortError(undefined,true)
305
+ );
306
+ if(chatReadyListenerPending){
307
+ chat.removeEventListener('chat-ready',publishAvailability);
308
+ chatReadyListenerPending=false;
309
+ }
310
+ status?.removeEventListener('local-ai-retry',retry);
311
+ events.dispose();
312
+ return true;
313
+ }
134
314
 
135
315
  return Object.freeze({
136
316
  check,
137
317
  ensure,
138
- destroy(){
139
- status?.removeEventListener('local-ai-retry',retry);
140
- },
318
+ destroy,
319
+ dispose:destroy,
141
320
  get availability(){
142
321
  return currentAvailability;
143
322
  },