arcane-os 0.3.3 → 0.3.5
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.
- package/CHANGELOG.md +22 -0
- package/README.md +7 -7
- package/browser-runtime/ai/browser-wasm-llm-provider.mjs +315 -119
- package/browser-runtime/ai/model-controller.mjs +439 -95
- package/package.json +1 -1
- package/runtime/arcane/components/assistant-panel.html +2 -1
- package/runtime/arcane/components/chat.html +466 -206
- package/runtime/arcane/components/speech.html +107 -30
- package/runtime/arcane/components/voice-transcription.html +27 -3
- package/runtime/arcane/entities/Chat.js +165 -97
- package/runtime/arcane/entities/IntentEnvelope.js +52 -118
- package/runtime/arcane/entities/TWiNPolicyDecision.js +33 -130
- package/runtime/arcane/entities/User.js +38 -44
- package/runtime/arcane/modules/AI.js +569 -302
- package/runtime/arcane/modules/AIProviderRuntime.js +765 -135
- package/runtime/arcane/modules/AIRuntimeState.js +22 -24
- package/runtime/arcane/modules/ApiModelDatabase.js +54 -48
- package/runtime/arcane/modules/CaseEvidenceIndexer.js +6 -10
- package/runtime/arcane/modules/CommunicationHub.js +90 -94
- package/runtime/arcane/modules/ComponentContracts.js +34 -9
- package/runtime/arcane/modules/ConfiguredAIChatSession.js +77 -77
- package/runtime/arcane/modules/ConversationTimebox.js +76 -104
- package/runtime/arcane/modules/DBLS.js +14 -12
- package/runtime/arcane/modules/DBOPFS.js +20 -15
- package/runtime/arcane/modules/Errors.js +196 -436
- package/runtime/arcane/modules/HTMLImport.js +49 -32
- package/runtime/arcane/modules/Ollama.js +16 -14
- package/runtime/arcane/modules/PersistentAIChatSession.js +174 -93
- package/runtime/arcane/modules/RecordReviewStore.js +40 -35
- package/runtime/arcane/modules/TerminalClient.js +12 -14
- package/runtime/arcane/modules/ThemeBootstrap.js +7 -7
- package/runtime/arcane/modules/ThemeManager.js +5 -5
- package/runtime/arcane/modules/TimeGuard.js +5 -65
- package/runtime/arcane/modules/WaitForComponent.js +43 -40
- package/src/cli/main.mjs +12 -11
- package/src/installed-sdk-runtime.mjs +11 -1
- package/src/mail-server.mjs +12 -5
- package/src/mail.mjs +25 -19
- package/src/workspace.mjs +32 -9
|
@@ -149,7 +149,8 @@
|
|
|
149
149
|
subscribeAIRuntimeState
|
|
150
150
|
} = await import('../modules/AIRuntimeState.js');
|
|
151
151
|
const {
|
|
152
|
-
createSTTActivationController
|
|
152
|
+
createSTTActivationController,
|
|
153
|
+
formatAIRuntimeProgress
|
|
153
154
|
} = await import('../modules/ComponentContracts.js');
|
|
154
155
|
|
|
155
156
|
const host = this;
|
|
@@ -333,8 +334,13 @@
|
|
|
333
334
|
muteButton.addEventListener('click', muteToggle, lifecycleListenerOptions);
|
|
334
335
|
window.addEventListener(
|
|
335
336
|
'pagehide',
|
|
336
|
-
|
|
337
|
-
{
|
|
337
|
+
handlePageHide,
|
|
338
|
+
{ signal: runtimeStateAbortController.signal }
|
|
339
|
+
);
|
|
340
|
+
window.addEventListener(
|
|
341
|
+
'pageshow',
|
|
342
|
+
handlePageShow,
|
|
343
|
+
{ signal: runtimeStateAbortController.signal }
|
|
338
344
|
);
|
|
339
345
|
|
|
340
346
|
subscribeAIRuntimeState(
|
|
@@ -723,10 +729,10 @@
|
|
|
723
729
|
return 'Voice release requested.';
|
|
724
730
|
}
|
|
725
731
|
if (ttsRole.state === 'loading') {
|
|
726
|
-
return `Voice ${
|
|
732
|
+
return `Voice ${formatAIRuntimeProgress(ttsRole.progress, 'loading')}; Cancel is available.`;
|
|
727
733
|
}
|
|
728
734
|
if (ttsRole.state === 'unloading') {
|
|
729
|
-
return `Voice ${
|
|
735
|
+
return `Voice ${formatAIRuntimeProgress(ttsRole.progress, 'releasing')}.`;
|
|
730
736
|
}
|
|
731
737
|
if (ttsRole.state === 'ready') {
|
|
732
738
|
if (host.muted) {
|
|
@@ -746,13 +752,6 @@
|
|
|
746
752
|
return 'Voice unavailable.';
|
|
747
753
|
}
|
|
748
754
|
|
|
749
|
-
function formatRoleProgress(progress, fallback) {
|
|
750
|
-
if (!progress) {
|
|
751
|
-
return fallback;
|
|
752
|
-
}
|
|
753
|
-
return progress.phase;
|
|
754
|
-
}
|
|
755
|
-
|
|
756
755
|
function visibleErrorMessage(error, fallback) {
|
|
757
756
|
try {
|
|
758
757
|
if (error?.userSafe === true) {
|
|
@@ -1235,11 +1234,14 @@
|
|
|
1235
1234
|
}
|
|
1236
1235
|
|
|
1237
1236
|
const generation = ++recordingGeneration;
|
|
1238
|
-
|
|
1237
|
+
const operationId = nextSpeechOperationId('transcription');
|
|
1238
|
+
transcriptionOperationId = operationId;
|
|
1239
1239
|
recordingRequested = true;
|
|
1240
1240
|
localStatus = {
|
|
1241
1241
|
message: 'Recording. Release to transcribe.',
|
|
1242
|
-
tone: ''
|
|
1242
|
+
tone: '',
|
|
1243
|
+
owner: 'recording',
|
|
1244
|
+
generation
|
|
1243
1245
|
};
|
|
1244
1246
|
renderControls();
|
|
1245
1247
|
renderStatus();
|
|
@@ -1248,6 +1250,9 @@
|
|
|
1248
1250
|
try {
|
|
1249
1251
|
stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
1250
1252
|
} catch (error) {
|
|
1253
|
+
if (generation !== recordingGeneration || destroyed) {
|
|
1254
|
+
return false;
|
|
1255
|
+
}
|
|
1251
1256
|
recordingRequested = false;
|
|
1252
1257
|
resetRecordPress();
|
|
1253
1258
|
if (
|
|
@@ -1268,7 +1273,7 @@
|
|
|
1268
1273
|
{
|
|
1269
1274
|
bubbles: true,
|
|
1270
1275
|
composed: true,
|
|
1271
|
-
operationId
|
|
1276
|
+
operationId,
|
|
1272
1277
|
publicDetail: {
|
|
1273
1278
|
...publicSpeechErrorFields(
|
|
1274
1279
|
error,
|
|
@@ -1278,7 +1283,9 @@
|
|
|
1278
1283
|
}
|
|
1279
1284
|
}
|
|
1280
1285
|
);
|
|
1281
|
-
transcriptionOperationId
|
|
1286
|
+
if (transcriptionOperationId === operationId) {
|
|
1287
|
+
transcriptionOperationId = null;
|
|
1288
|
+
}
|
|
1282
1289
|
} else {
|
|
1283
1290
|
reportTranscriptionError(error);
|
|
1284
1291
|
}
|
|
@@ -1287,15 +1294,25 @@
|
|
|
1287
1294
|
return false;
|
|
1288
1295
|
}
|
|
1289
1296
|
|
|
1297
|
+
const currentGeneration = generation === recordingGeneration;
|
|
1290
1298
|
if (
|
|
1291
1299
|
!recordingRequested
|
|
1292
|
-
||
|
|
1300
|
+
|| !currentGeneration
|
|
1293
1301
|
|| sttRole.state !== 'ready'
|
|
1294
1302
|
|| sttRole.busy
|
|
1295
1303
|
|| destroyed
|
|
1296
1304
|
) {
|
|
1297
1305
|
stopMediaStream(stream);
|
|
1298
|
-
|
|
1306
|
+
if (currentGeneration) {
|
|
1307
|
+
recordingRequested = false;
|
|
1308
|
+
resetRecordPress();
|
|
1309
|
+
clearRecordingStatus(generation);
|
|
1310
|
+
if (transcriptionOperationId === operationId) {
|
|
1311
|
+
transcriptionOperationId = null;
|
|
1312
|
+
}
|
|
1313
|
+
renderControls();
|
|
1314
|
+
renderStatus();
|
|
1315
|
+
}
|
|
1299
1316
|
return false;
|
|
1300
1317
|
}
|
|
1301
1318
|
|
|
@@ -1311,6 +1328,7 @@
|
|
|
1311
1328
|
const processor = audioContext.createScriptProcessor(4096, 1, 1);
|
|
1312
1329
|
session = {
|
|
1313
1330
|
generation,
|
|
1331
|
+
operationId,
|
|
1314
1332
|
recorder,
|
|
1315
1333
|
stream,
|
|
1316
1334
|
audioContext,
|
|
@@ -1349,7 +1367,6 @@
|
|
|
1349
1367
|
recorder.start();
|
|
1350
1368
|
return true;
|
|
1351
1369
|
} catch (error) {
|
|
1352
|
-
recordingRequested = false;
|
|
1353
1370
|
if (session) {
|
|
1354
1371
|
session.cancelled = true;
|
|
1355
1372
|
releaseCaptureSession(session);
|
|
@@ -1359,8 +1376,11 @@
|
|
|
1359
1376
|
} else {
|
|
1360
1377
|
stopMediaStream(stream);
|
|
1361
1378
|
}
|
|
1362
|
-
|
|
1363
|
-
|
|
1379
|
+
if (generation === recordingGeneration) {
|
|
1380
|
+
recordingRequested = false;
|
|
1381
|
+
resetRecordPress();
|
|
1382
|
+
reportTranscriptionError(error);
|
|
1383
|
+
}
|
|
1364
1384
|
return false;
|
|
1365
1385
|
}
|
|
1366
1386
|
}
|
|
@@ -1377,9 +1397,34 @@
|
|
|
1377
1397
|
}
|
|
1378
1398
|
}
|
|
1379
1399
|
|
|
1400
|
+
function clearRecordingStatus(generation) {
|
|
1401
|
+
if (
|
|
1402
|
+
localStatus?.owner === 'recording'
|
|
1403
|
+
&& localStatus.generation === generation
|
|
1404
|
+
) {
|
|
1405
|
+
localStatus = null;
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1380
1409
|
function stop() {
|
|
1410
|
+
const pendingCaptureStart = recordingRequested && !captureSession;
|
|
1411
|
+
const generation = recordingGeneration;
|
|
1412
|
+
const operationId = transcriptionOperationId;
|
|
1381
1413
|
recordingRequested = false;
|
|
1382
1414
|
clearSilenceTimer();
|
|
1415
|
+
if (pendingCaptureStart) {
|
|
1416
|
+
// getUserMedia cannot be cancelled synchronously. Retire this
|
|
1417
|
+
// generation so its eventual settlement cannot touch a retry.
|
|
1418
|
+
recordingGeneration += 1;
|
|
1419
|
+
resetRecordPress();
|
|
1420
|
+
clearRecordingStatus(generation);
|
|
1421
|
+
if (transcriptionOperationId === operationId) {
|
|
1422
|
+
transcriptionOperationId = null;
|
|
1423
|
+
}
|
|
1424
|
+
renderControls();
|
|
1425
|
+
renderStatus();
|
|
1426
|
+
return true;
|
|
1427
|
+
}
|
|
1383
1428
|
if (captureSession && captureSession.recorder.state !== 'inactive') {
|
|
1384
1429
|
captureSession.recorder.stop();
|
|
1385
1430
|
}
|
|
@@ -1387,22 +1432,32 @@
|
|
|
1387
1432
|
}
|
|
1388
1433
|
|
|
1389
1434
|
async function finalizeCapture(session) {
|
|
1435
|
+
const currentGeneration = session.generation === recordingGeneration;
|
|
1436
|
+
const currentOperation = transcriptionOperationId === session.operationId;
|
|
1390
1437
|
releaseCaptureSession(session);
|
|
1391
1438
|
if (captureSession === session) {
|
|
1392
1439
|
captureSession = null;
|
|
1393
1440
|
}
|
|
1394
|
-
|
|
1395
|
-
|
|
1441
|
+
if (currentGeneration) {
|
|
1442
|
+
recordingRequested = false;
|
|
1443
|
+
resetRecordPress();
|
|
1444
|
+
}
|
|
1396
1445
|
|
|
1397
1446
|
if (
|
|
1398
1447
|
session.cancelled
|
|
1399
|
-
||
|
|
1448
|
+
|| !currentGeneration
|
|
1400
1449
|
|| sttRole.state !== 'ready'
|
|
1401
1450
|
|| sttRole.busy
|
|
1402
1451
|
|| destroyed
|
|
1403
1452
|
) {
|
|
1404
|
-
|
|
1405
|
-
|
|
1453
|
+
if (currentGeneration) {
|
|
1454
|
+
clearRecordingStatus(session.generation);
|
|
1455
|
+
if (currentOperation) {
|
|
1456
|
+
transcriptionOperationId = null;
|
|
1457
|
+
}
|
|
1458
|
+
renderControls();
|
|
1459
|
+
renderStatus();
|
|
1460
|
+
}
|
|
1406
1461
|
return false;
|
|
1407
1462
|
}
|
|
1408
1463
|
|
|
@@ -1415,12 +1470,13 @@
|
|
|
1415
1470
|
'audio.webm',
|
|
1416
1471
|
{ type: 'audio/webm' }
|
|
1417
1472
|
);
|
|
1418
|
-
return transcribeAudio(audioFile);
|
|
1473
|
+
return transcribeAudio(audioFile, session.operationId);
|
|
1419
1474
|
}
|
|
1420
1475
|
|
|
1421
|
-
async function transcribeAudio(audioFile) {
|
|
1476
|
+
async function transcribeAudio(audioFile, operationId = null) {
|
|
1422
1477
|
const generation = ++transcriptionGeneration;
|
|
1423
|
-
transcriptionOperationId =
|
|
1478
|
+
transcriptionOperationId = operationId
|
|
1479
|
+
|| transcriptionOperationId
|
|
1424
1480
|
|| nextSpeechOperationId('transcription');
|
|
1425
1481
|
const controller = new AbortController();
|
|
1426
1482
|
transcriptionAbortController?.abort();
|
|
@@ -1636,6 +1692,26 @@
|
|
|
1636
1692
|
keyboardRecording = false;
|
|
1637
1693
|
}
|
|
1638
1694
|
|
|
1695
|
+
function handlePageHide(event) {
|
|
1696
|
+
if (event.persisted === true) {
|
|
1697
|
+
return;
|
|
1698
|
+
}
|
|
1699
|
+
destroy();
|
|
1700
|
+
}
|
|
1701
|
+
|
|
1702
|
+
function handlePageShow(event) {
|
|
1703
|
+
if (event.persisted !== true || destroyed) {
|
|
1704
|
+
return;
|
|
1705
|
+
}
|
|
1706
|
+
restoreFromPageCache();
|
|
1707
|
+
}
|
|
1708
|
+
|
|
1709
|
+
function restoreFromPageCache() {
|
|
1710
|
+
synchronizeAIMutedState();
|
|
1711
|
+
renderControls();
|
|
1712
|
+
renderStatus();
|
|
1713
|
+
}
|
|
1714
|
+
|
|
1639
1715
|
function destroy() {
|
|
1640
1716
|
if (destroyed) {
|
|
1641
1717
|
return;
|
|
@@ -1644,7 +1720,8 @@
|
|
|
1644
1720
|
destroyed = true;
|
|
1645
1721
|
runtimeStateAbortController.abort();
|
|
1646
1722
|
sttActivationController.destroy();
|
|
1647
|
-
window.removeEventListener('pagehide',
|
|
1723
|
+
window.removeEventListener('pagehide', handlePageHide);
|
|
1724
|
+
window.removeEventListener('pageshow', handlePageShow);
|
|
1648
1725
|
microphonePermissionStatus?.removeEventListener?.(
|
|
1649
1726
|
'change',
|
|
1650
1727
|
microphonePermissionListener
|
|
@@ -212,8 +212,13 @@
|
|
|
212
212
|
completeButton.addEventListener('click',completeStream,lifecycleListenerOptions);
|
|
213
213
|
window.addEventListener(
|
|
214
214
|
'pagehide',
|
|
215
|
-
|
|
216
|
-
{
|
|
215
|
+
handlePageHide,
|
|
216
|
+
{signal:runtimeStateAbortController.signal}
|
|
217
|
+
);
|
|
218
|
+
window.addEventListener(
|
|
219
|
+
'pageshow',
|
|
220
|
+
handlePageShow,
|
|
221
|
+
{signal:runtimeStateAbortController.signal}
|
|
217
222
|
);
|
|
218
223
|
|
|
219
224
|
const sttActivationController=createSTTActivationController(
|
|
@@ -982,6 +987,24 @@
|
|
|
982
987
|
};
|
|
983
988
|
}
|
|
984
989
|
|
|
990
|
+
function handlePageHide(event){
|
|
991
|
+
if(event.persisted===true){
|
|
992
|
+
return;
|
|
993
|
+
}
|
|
994
|
+
destroy();
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
function handlePageShow(event){
|
|
998
|
+
if(event.persisted!==true||destroyed){
|
|
999
|
+
return;
|
|
1000
|
+
}
|
|
1001
|
+
restoreFromPageCache();
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
function restoreFromPageCache(){
|
|
1005
|
+
renderState();
|
|
1006
|
+
}
|
|
1007
|
+
|
|
985
1008
|
function destroy(){
|
|
986
1009
|
if(destroyed){
|
|
987
1010
|
return true;
|
|
@@ -989,7 +1012,8 @@
|
|
|
989
1012
|
destroyed=true;
|
|
990
1013
|
runtimeStateAbortController.abort();
|
|
991
1014
|
sttActivationController.destroy();
|
|
992
|
-
window.removeEventListener('pagehide',
|
|
1015
|
+
window.removeEventListener('pagehide',handlePageHide);
|
|
1016
|
+
window.removeEventListener('pageshow',handlePageShow);
|
|
993
1017
|
if(!cancelSTTOperation('component-destroyed')){
|
|
994
1018
|
sessionGeneration++;
|
|
995
1019
|
}
|