arcane-os 0.2.2 → 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.
- package/CHANGELOG.md +29 -0
- package/README.md +8 -8
- package/browser-runtime/ARCANE_SDK_BROWSER_RELEASE.json +29 -22
- package/browser-runtime/ai/ARCANE_AI_BROWSER_SPEECH_COMPONENTS.json +203 -0
- package/browser-runtime/ai/browser-kokoro-worker.mjs +11 -2
- package/browser-runtime/ai/browser-speech-artifacts.mjs +3230 -397
- package/browser-runtime/ai/browser-speech-providers.mjs +1141 -157
- package/browser-runtime/ai/browser-speech.mjs +2 -0
- package/browser-runtime/ai/browser-whisper-worker.mjs +11 -2
- package/browser-runtime/ai/model-controller.mjs +285 -95
- package/browser-runtime/ai/speech-worker-client.mjs +247 -32
- package/browser-runtime/ai/speech-worker-runtime.mjs +2310 -167
- package/browser-runtime/event-manager.mjs +1097 -1
- package/docs/architecture.md +2 -2
- package/docs/event-manager.md +155 -27
- package/docs/reference/README.md +27 -27
- package/docs/reference/ai/browser-speech-package-authority.json +835 -0
- package/docs/reference/ai/browser-speech.md +1162 -246
- package/docs/reference/ai/browser-wasm.md +18 -7
- package/docs/reference/availability-and-normalization.md +6 -3
- package/docs/reference/behavioral-testing.md +29 -6
- package/docs/reference/cli.md +117 -9
- package/docs/reference/core/arcane-ai-contracts.md +1 -1
- package/docs/reference/event-manager.md +577 -32
- package/docs/reference/inventory/package-api.json +478 -2
- package/docs/reference/inventory/runtime-components.json +108 -44
- package/docs/reference/inventory/runtime-modules.json +131 -53
- package/docs/reference/mail.md +316 -0
- package/docs/reference/protocols.md +157 -43
- package/docs/reference/runtime-components.md +258 -83
- package/docs/reference/runtime-modules.md +613 -77
- package/docs/reference/sdk-api.md +1014 -25
- package/package.json +5 -4
- package/runtime/ARCANE_RUNTIME_RELEASE.json +145 -140
- package/runtime/arcane/components/app-bar.html +34 -13
- package/runtime/arcane/components/assistant-panel.html +110 -57
- package/runtime/arcane/components/calculator.html +7 -4
- package/runtime/arcane/components/chart.html +58 -17
- package/runtime/arcane/components/chat.html +606 -136
- package/runtime/arcane/components/conversation-view.html +13 -6
- package/runtime/arcane/components/dashboard-config.html +96 -59
- package/runtime/arcane/components/data-maintenance.html +69 -14
- package/runtime/arcane/components/data-view.html +53 -7
- package/runtime/arcane/components/directory-picker.html +118 -32
- package/runtime/arcane/components/document-inspector.html +47 -10
- package/runtime/arcane/components/file-drop.html +81 -35
- package/runtime/arcane/components/file-inspector.html +72 -22
- package/runtime/arcane/components/file-manager.html +374 -79
- package/runtime/arcane/components/integration-settings.html +12 -5
- package/runtime/arcane/components/local-ai-status.html +48 -19
- package/runtime/arcane/components/markdown-document.html +161 -68
- package/runtime/arcane/components/markdown-editor.html +110 -33
- package/runtime/arcane/components/media-embed.html +8 -5
- package/runtime/arcane/components/modal.html +15 -5
- package/runtime/arcane/components/output-panel.html +28 -23
- package/runtime/arcane/components/preferences-form.html +22 -4
- package/runtime/arcane/components/record-timeline.html +18 -2
- package/runtime/arcane/components/relationship-board.html +23 -3
- package/runtime/arcane/components/screen-capture.html +10 -4
- package/runtime/arcane/components/source-code-viewer.html +76 -9
- package/runtime/arcane/components/source-explanation.html +23 -3
- package/runtime/arcane/components/speech.html +462 -384
- package/runtime/arcane/components/summary-strip.html +22 -11
- package/runtime/arcane/components/table.html +39 -21
- package/runtime/arcane/components/task-progress.html +79 -21
- package/runtime/arcane/components/terminal-workspace.html +7 -4
- package/runtime/arcane/components/theme-editor.html +7 -3
- package/runtime/arcane/components/unified-inbox.html +9 -4
- package/runtime/arcane/components/voice-transcription.html +639 -98
- package/runtime/arcane/components/weather-widget.html +5 -3
- package/runtime/arcane/components/web-navigator.html +48 -8
- package/runtime/arcane/entities/Chat.js +1 -1
- package/runtime/arcane/entities/User.js +110 -23
- package/runtime/arcane/modules/AI.js +2109 -130
- package/runtime/arcane/modules/AIProviderRuntime.js +720 -13
- package/runtime/arcane/modules/AIRuntimeState.js +109 -52
- package/runtime/arcane/modules/ApiModelDatabase.js +390 -17
- package/runtime/arcane/modules/BrowserTestSuite.js +205 -28
- package/runtime/arcane/modules/CalculatorEngine.js +63 -3
- package/runtime/arcane/modules/CommunicationAppController.js +588 -28
- package/runtime/arcane/modules/CommunicationHub.js +590 -10
- package/runtime/arcane/modules/ComponentContracts.js +470 -0
- package/runtime/arcane/modules/ConversationTimebox.js +152 -33
- package/runtime/arcane/modules/DBLS.js +40 -7
- package/runtime/arcane/modules/DBOPFS.js +35 -11
- package/runtime/arcane/modules/DataMaintenance.js +12 -2
- package/runtime/arcane/modules/Errors.js +65 -7
- package/runtime/arcane/modules/HTMLImport.js +198 -14
- package/runtime/arcane/modules/LocalAIReadinessController.js +208 -29
- package/runtime/arcane/modules/Mail.js +738 -115
- package/runtime/arcane/modules/MailOutbox.mjs +1395 -0
- package/runtime/arcane/modules/MailTransport.mjs +197 -39
- package/runtime/arcane/modules/Ollama.js +36 -1
- package/runtime/arcane/modules/OpenMeteoWeatherProvider.js +583 -7
- package/runtime/arcane/modules/PreferenceStore.js +367 -33
- package/runtime/arcane/modules/RecordReviewStore.js +322 -23
- package/runtime/arcane/modules/ScreenCapture.js +1397 -15
- package/runtime/arcane/modules/SpeechPlayback.js +438 -41
- package/runtime/arcane/modules/TerminalClient.js +277 -12
- package/runtime/arcane/modules/ThemeBootstrap.js +80 -6
- package/runtime/arcane/modules/ThemeManager.js +39 -7
- package/runtime/arcane/modules/TimeGuard.js +131 -20
- package/runtime/arcane/modules/WaitForComponent.js +386 -33
- package/schemas/arcane-lock.schema.json +2 -2
- package/src/cli/main.mjs +435 -9
- package/src/event-manager.mjs +1097 -1
- package/src/import-map.mjs +21 -3
- package/src/index.mjs +13 -0
- package/src/installed-sdk-runtime.mjs +112 -0
- package/src/mail-api.mjs +22 -0
- package/src/mail-credentials.mjs +667 -0
- package/src/mail-server.mjs +1769 -0
- package/src/mail.mjs +261 -0
- package/src/sdk-browser-runtime.mjs +85 -41
- package/src/testing-loader.mjs +7 -0
- package/src/toolchain.mjs +3 -0
- package/src/workspace.mjs +1 -1
|
@@ -684,7 +684,31 @@
|
|
|
684
684
|
</section>
|
|
685
685
|
|
|
686
686
|
<script type="module">
|
|
687
|
+
const {
|
|
688
|
+
createArcaneEventSource,
|
|
689
|
+
projectArcaneDOMEvent
|
|
690
|
+
}=await import('arcane-os/event-manager');
|
|
691
|
+
|
|
687
692
|
const host=this;
|
|
693
|
+
const events=createArcaneEventSource(
|
|
694
|
+
host,
|
|
695
|
+
{
|
|
696
|
+
source:'arcane.component.chat',
|
|
697
|
+
eventTypes:[
|
|
698
|
+
'chat-ready',
|
|
699
|
+
'chat-send-message',
|
|
700
|
+
'chat-send-error',
|
|
701
|
+
'chat-file-uploaded',
|
|
702
|
+
'chat-file-upload-error',
|
|
703
|
+
'chat-language-changed',
|
|
704
|
+
'chat-language-change-error',
|
|
705
|
+
'chat-ai-activation-request',
|
|
706
|
+
'chat-ai-activation-error',
|
|
707
|
+
'chat-speech-synthesis-error',
|
|
708
|
+
'conversation-timebox-error'
|
|
709
|
+
]
|
|
710
|
+
}
|
|
711
|
+
);
|
|
688
712
|
const shadowRoot=host.shadowRoot;
|
|
689
713
|
const chatArea=shadowRoot.querySelector('.chat_area');
|
|
690
714
|
const textArea=shadowRoot.querySelector('#chat_input');
|
|
@@ -728,7 +752,176 @@
|
|
|
728
752
|
let conversationTimeboxError='';
|
|
729
753
|
let lastQueuedTimeboxRevision=null;
|
|
730
754
|
let hostSubmissionGeneration=0;
|
|
755
|
+
let eventOperationSequence=0;
|
|
756
|
+
let destroyed=false;
|
|
757
|
+
const activeSubmissionOwnerships=new Set();
|
|
731
758
|
const conversationTimeboxRetryDelays=Object.freeze([0,500,1_500]);
|
|
759
|
+
const chatReasons=Object.freeze({
|
|
760
|
+
ready:'chat-ready',
|
|
761
|
+
messageSubmissionRequested:'message-submission-requested',
|
|
762
|
+
messageSubmissionCancelled:'message-submission-cancelled',
|
|
763
|
+
callerSignalAborted:'caller-signal-aborted',
|
|
764
|
+
componentDestroyed:'component-destroyed',
|
|
765
|
+
hostMessageSubmissionRejected:'host-message-submission-rejected',
|
|
766
|
+
fileStorageCompleted:'file-storage-completed',
|
|
767
|
+
fileStorageRejected:'file-storage-rejected',
|
|
768
|
+
languageChangeRequested:'language-change-requested',
|
|
769
|
+
languageChangeCallbackRejected:'language-change-callback-rejected',
|
|
770
|
+
languageModelActivationRequested:'language-model-activation-requested',
|
|
771
|
+
languageModelActivationRejected:'language-model-activation-rejected',
|
|
772
|
+
speechSynthesisRejected:'speech-synthesis-rejected',
|
|
773
|
+
conversationTimeboxDeliveryRejected:'conversation-timebox-delivery-rejected'
|
|
774
|
+
});
|
|
775
|
+
const chatErrorCodes=Object.freeze({
|
|
776
|
+
messageSubmissionAborted:'ARCANE_CHAT_MESSAGE_SUBMISSION_ABORTED',
|
|
777
|
+
languageModelActivationRejected:'ARCANE_CHAT_LANGUAGE_MODEL_ACTIVATION_REQUEST_REJECTED',
|
|
778
|
+
hostMessageSubmissionRejected:'ARCANE_CHAT_HOST_MESSAGE_SUBMISSION_REJECTED',
|
|
779
|
+
fileStorageRejected:'ARCANE_CHAT_FILE_STORAGE_REJECTED',
|
|
780
|
+
languageChangeCallbackRejected:'ARCANE_CHAT_LANGUAGE_CHANGE_CALLBACK_REJECTED',
|
|
781
|
+
speechSynthesisRejected:'ARCANE_CHAT_SPEECH_SYNTHESIS_REQUEST_REJECTED',
|
|
782
|
+
conversationTimeboxDeliveryRejected:'ARCANE_CHAT_CONVERSATION_TIMEBOX_DELIVERY_REJECTED'
|
|
783
|
+
});
|
|
784
|
+
|
|
785
|
+
function nextChatOperationId(kind){
|
|
786
|
+
eventOperationSequence+=1;
|
|
787
|
+
return `${events.descriptor.instanceId}:${kind}:${eventOperationSequence}`;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
function dispatchChatEvent(
|
|
791
|
+
type,
|
|
792
|
+
compatibilityDetail={},
|
|
793
|
+
{
|
|
794
|
+
publicDetail={},
|
|
795
|
+
operationId=null,
|
|
796
|
+
bubbles=false,
|
|
797
|
+
composed=false,
|
|
798
|
+
cancelable=false
|
|
799
|
+
}={}
|
|
800
|
+
){
|
|
801
|
+
if(destroyed){
|
|
802
|
+
return false;
|
|
803
|
+
}
|
|
804
|
+
const publication=events.dispatch(
|
|
805
|
+
type,
|
|
806
|
+
compatibilityDetail,
|
|
807
|
+
{
|
|
808
|
+
publicDetail,
|
|
809
|
+
operationId,
|
|
810
|
+
cancelable
|
|
811
|
+
}
|
|
812
|
+
);
|
|
813
|
+
if(!publication.accepted){
|
|
814
|
+
return false;
|
|
815
|
+
}
|
|
816
|
+
return projectArcaneDOMEvent(
|
|
817
|
+
host,
|
|
818
|
+
publication.occurrence,
|
|
819
|
+
{
|
|
820
|
+
bubbles,
|
|
821
|
+
composed,
|
|
822
|
+
cancelable
|
|
823
|
+
}
|
|
824
|
+
);
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
function publicErrorFields(error,boundaryCode){
|
|
828
|
+
let causeCode='';
|
|
829
|
+
try{
|
|
830
|
+
causeCode=typeof error?.code==='string'?error.code.trim():'';
|
|
831
|
+
}catch{}
|
|
832
|
+
return Object.freeze({
|
|
833
|
+
code:boundaryCode,
|
|
834
|
+
...(causeCode&&causeCode!==boundaryCode?{causeCode}:{})
|
|
835
|
+
});
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
function isAbortSignal(value){
|
|
839
|
+
return value!==null
|
|
840
|
+
&&typeof value==='object'
|
|
841
|
+
&&typeof value.aborted==='boolean'
|
|
842
|
+
&&typeof value.addEventListener==='function'
|
|
843
|
+
&&typeof value.removeEventListener==='function';
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
function createChatSubmissionAbort(reason,message){
|
|
847
|
+
const error=new Error(message);
|
|
848
|
+
error.name='AbortError';
|
|
849
|
+
error.code=chatErrorCodes.messageSubmissionAborted;
|
|
850
|
+
error.reason=reason;
|
|
851
|
+
return error;
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
function createChatSubmissionOwnership(callerSignal=null){
|
|
855
|
+
if(callerSignal!==null&&callerSignal!==undefined&&!isAbortSignal(callerSignal)){
|
|
856
|
+
throw new TypeError('Chat submission signal must be an AbortSignal.');
|
|
857
|
+
}
|
|
858
|
+
const controller=new AbortController();
|
|
859
|
+
const subscriptions=[];
|
|
860
|
+
let released=false;
|
|
861
|
+
|
|
862
|
+
function releaseChatSubmissionOwnership(){
|
|
863
|
+
if(released){
|
|
864
|
+
return false;
|
|
865
|
+
}
|
|
866
|
+
released=true;
|
|
867
|
+
for(const {signal,listener} of subscriptions.splice(0)){
|
|
868
|
+
signal.removeEventListener('abort',listener);
|
|
869
|
+
}
|
|
870
|
+
activeSubmissionOwnerships.delete(ownership);
|
|
871
|
+
return true;
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
function abortChatSubmission(reason){
|
|
875
|
+
if(!controller.signal.aborted){
|
|
876
|
+
controller.abort(reason);
|
|
877
|
+
}
|
|
878
|
+
releaseChatSubmissionOwnership();
|
|
879
|
+
return true;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
const ownership=Object.freeze({
|
|
883
|
+
signal:controller.signal,
|
|
884
|
+
abort:abortChatSubmission,
|
|
885
|
+
release:releaseChatSubmissionOwnership
|
|
886
|
+
});
|
|
887
|
+
activeSubmissionOwnerships.add(ownership);
|
|
888
|
+
|
|
889
|
+
function followAbortSignal(signal,fallbackReason){
|
|
890
|
+
if(!signal||controller.signal.aborted){
|
|
891
|
+
return;
|
|
892
|
+
}
|
|
893
|
+
if(signal.aborted){
|
|
894
|
+
abortChatSubmission(signal.reason??fallbackReason);
|
|
895
|
+
return;
|
|
896
|
+
}
|
|
897
|
+
function forwardChatSubmissionAbort(){
|
|
898
|
+
abortChatSubmission(signal.reason??fallbackReason);
|
|
899
|
+
}
|
|
900
|
+
subscriptions.push({signal,listener:forwardChatSubmissionAbort});
|
|
901
|
+
signal.addEventListener('abort',forwardChatSubmissionAbort,{once:true});
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
try{
|
|
905
|
+
followAbortSignal(
|
|
906
|
+
aiRuntimeStateAbortController.signal,
|
|
907
|
+
createChatSubmissionAbort(
|
|
908
|
+
chatReasons.componentDestroyed,
|
|
909
|
+
'The chat component was destroyed before message submission settled.'
|
|
910
|
+
)
|
|
911
|
+
);
|
|
912
|
+
followAbortSignal(
|
|
913
|
+
callerSignal,
|
|
914
|
+
createChatSubmissionAbort(
|
|
915
|
+
chatReasons.callerSignalAborted,
|
|
916
|
+
'The caller canceled the chat message submission.'
|
|
917
|
+
)
|
|
918
|
+
);
|
|
919
|
+
}catch(error){
|
|
920
|
+
releaseChatSubmissionOwnership();
|
|
921
|
+
throw error;
|
|
922
|
+
}
|
|
923
|
+
return ownership;
|
|
924
|
+
}
|
|
732
925
|
|
|
733
926
|
host.sendMessage=host.sendMessage||async function(text=''){
|
|
734
927
|
console.warn('html-import chat.html `host.sendMessage` method not implemented.\n\nThis can also be done by listening for the "chat-send-message" event.\n\nIt is still best practice to implement the `.sendMessage` method atleast with an empty function.\n\n')
|
|
@@ -767,7 +960,11 @@
|
|
|
767
960
|
signal:aiRuntimeStateAbortController.signal
|
|
768
961
|
}
|
|
769
962
|
);
|
|
770
|
-
window.addEventListener(
|
|
963
|
+
window.addEventListener(
|
|
964
|
+
'pagehide',
|
|
965
|
+
destroy,
|
|
966
|
+
{once:true,signal:aiRuntimeStateAbortController.signal}
|
|
967
|
+
);
|
|
771
968
|
|
|
772
969
|
function setConversationComplete(complete=true){
|
|
773
970
|
if(typeof complete!=='boolean'){
|
|
@@ -805,7 +1002,7 @@
|
|
|
805
1002
|
initialSpeechMuteListenerInstalled=false;
|
|
806
1003
|
speech.configure?.({initialMuted:requestedInitialSpeechMuted});
|
|
807
1004
|
},
|
|
808
|
-
{once:true}
|
|
1005
|
+
{once:true,signal:aiRuntimeStateAbortController.signal}
|
|
809
1006
|
);
|
|
810
1007
|
}
|
|
811
1008
|
|
|
@@ -944,10 +1141,22 @@
|
|
|
944
1141
|
send.setAttribute('aria-label',send.title);
|
|
945
1142
|
}
|
|
946
1143
|
|
|
947
|
-
function createAIActivationController({
|
|
1144
|
+
function createAIActivationController({
|
|
1145
|
+
host,
|
|
1146
|
+
panel,
|
|
1147
|
+
title,
|
|
1148
|
+
status,
|
|
1149
|
+
button,
|
|
1150
|
+
publish=dispatchChatEvent,
|
|
1151
|
+
createOperationId=nextChatOperationId,
|
|
1152
|
+
readErrorFields=publicErrorFields,
|
|
1153
|
+
reasons=chatReasons,
|
|
1154
|
+
errorCodes=chatErrorCodes
|
|
1155
|
+
}){
|
|
948
1156
|
let role=null;
|
|
949
1157
|
let requestPending=false;
|
|
950
1158
|
let requestError='';
|
|
1159
|
+
let requestGeneration=0;
|
|
951
1160
|
let destroyed=false;
|
|
952
1161
|
|
|
953
1162
|
function visibleError(error,fallback){
|
|
@@ -981,6 +1190,10 @@
|
|
|
981
1190
|
}
|
|
982
1191
|
|
|
983
1192
|
function render(){
|
|
1193
|
+
if(destroyed){
|
|
1194
|
+
panel.hidden=true;
|
|
1195
|
+
return;
|
|
1196
|
+
}
|
|
984
1197
|
const visible=selected()&&role.state!=='ready';
|
|
985
1198
|
panel.hidden=!visible;
|
|
986
1199
|
panel.setAttribute(
|
|
@@ -1047,16 +1260,27 @@
|
|
|
1047
1260
|
requestError='';
|
|
1048
1261
|
const intent=Object.freeze({role:'llm',action,reason:'user'});
|
|
1049
1262
|
const request=Object.freeze({intent,state:role});
|
|
1050
|
-
const
|
|
1263
|
+
const operationId=typeof role?.operationId==='string'&&role.operationId
|
|
1264
|
+
?role.operationId
|
|
1265
|
+
:createOperationId('llm-activation');
|
|
1266
|
+
const generation=++requestGeneration;
|
|
1267
|
+
const accepted=publish(
|
|
1051
1268
|
'chat-ai-activation-request',
|
|
1269
|
+
request,
|
|
1052
1270
|
{
|
|
1053
1271
|
bubbles:true,
|
|
1054
1272
|
composed:true,
|
|
1055
1273
|
cancelable:true,
|
|
1056
|
-
|
|
1274
|
+
operationId,
|
|
1275
|
+
publicDetail:{
|
|
1276
|
+
role:'llm',
|
|
1277
|
+
action,
|
|
1278
|
+
reason:reasons.languageModelActivationRequested,
|
|
1279
|
+
state:role.state
|
|
1280
|
+
}
|
|
1057
1281
|
}
|
|
1058
1282
|
);
|
|
1059
|
-
if(!
|
|
1283
|
+
if(!accepted||destroyed||generation!==requestGeneration){
|
|
1060
1284
|
render();
|
|
1061
1285
|
return false;
|
|
1062
1286
|
}
|
|
@@ -1064,8 +1288,14 @@
|
|
|
1064
1288
|
render();
|
|
1065
1289
|
try{
|
|
1066
1290
|
await host.requestAIActivation(intent);
|
|
1291
|
+
if(destroyed||generation!==requestGeneration){
|
|
1292
|
+
return false;
|
|
1293
|
+
}
|
|
1067
1294
|
return true;
|
|
1068
1295
|
}catch(error){
|
|
1296
|
+
if(destroyed||generation!==requestGeneration){
|
|
1297
|
+
return false;
|
|
1298
|
+
}
|
|
1069
1299
|
if(action==='load'
|
|
1070
1300
|
&&cancellation(error)
|
|
1071
1301
|
&&['unloaded','unloading'].includes(role?.state)){
|
|
@@ -1076,20 +1306,30 @@
|
|
|
1076
1306
|
`The language model ${action} request failed.`
|
|
1077
1307
|
);
|
|
1078
1308
|
requestError=message;
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1309
|
+
publish(
|
|
1310
|
+
'chat-ai-activation-error',
|
|
1311
|
+
Object.freeze({request,error,message}),
|
|
1312
|
+
{
|
|
1313
|
+
bubbles:true,
|
|
1314
|
+
composed:true,
|
|
1315
|
+
operationId,
|
|
1316
|
+
publicDetail:{
|
|
1317
|
+
role:'llm',
|
|
1318
|
+
action,
|
|
1319
|
+
reason:reasons.languageModelActivationRejected,
|
|
1320
|
+
...readErrorFields(
|
|
1321
|
+
error,
|
|
1322
|
+
errorCodes.languageModelActivationRejected
|
|
1323
|
+
)
|
|
1086
1324
|
}
|
|
1087
|
-
|
|
1325
|
+
}
|
|
1088
1326
|
);
|
|
1089
1327
|
return false;
|
|
1090
1328
|
}finally{
|
|
1091
|
-
|
|
1092
|
-
|
|
1329
|
+
if(!destroyed&&generation===requestGeneration){
|
|
1330
|
+
requestPending=false;
|
|
1331
|
+
render();
|
|
1332
|
+
}
|
|
1093
1333
|
}
|
|
1094
1334
|
}
|
|
1095
1335
|
|
|
@@ -1104,7 +1344,9 @@
|
|
|
1104
1344
|
}
|
|
1105
1345
|
if(role?.state!==nextRole.state
|
|
1106
1346
|
||role?.providerId!==nextRole.providerId
|
|
1107
|
-
||role?.modelId!==nextRole.modelId
|
|
1347
|
+
||role?.modelId!==nextRole.modelId
|
|
1348
|
+
||role?.operationId!==nextRole.operationId){
|
|
1349
|
+
requestGeneration+=1;
|
|
1108
1350
|
requestError='';
|
|
1109
1351
|
requestPending=false;
|
|
1110
1352
|
}
|
|
@@ -1117,6 +1359,8 @@
|
|
|
1117
1359
|
return;
|
|
1118
1360
|
}
|
|
1119
1361
|
destroyed=true;
|
|
1362
|
+
requestGeneration+=1;
|
|
1363
|
+
requestPending=false;
|
|
1120
1364
|
button.removeEventListener('click',activateSelectedAI);
|
|
1121
1365
|
panel.hidden=true;
|
|
1122
1366
|
}
|
|
@@ -1256,6 +1500,10 @@
|
|
|
1256
1500
|
}
|
|
1257
1501
|
|
|
1258
1502
|
function queueConversationTimeboxMessage(message,context,guard){
|
|
1503
|
+
const operationId=typeof context?.operationId==='string'&&context.operationId
|
|
1504
|
+
?context.operationId
|
|
1505
|
+
:nextChatOperationId('conversation-timebox');
|
|
1506
|
+
const deliveryContext=Object.freeze({...context,operationId});
|
|
1259
1507
|
const prior=programmaticSubmissionQueue.catch(
|
|
1260
1508
|
function ignorePriorProgrammaticSubmissionFailure(){
|
|
1261
1509
|
return false;
|
|
@@ -1263,6 +1511,12 @@
|
|
|
1263
1511
|
);
|
|
1264
1512
|
const queued=prior.then(
|
|
1265
1513
|
async function dispatchConversationTimeboxMessage(){
|
|
1514
|
+
if(destroyed||aiRuntimeStateAbortController.signal.aborted){
|
|
1515
|
+
return false;
|
|
1516
|
+
}
|
|
1517
|
+
if(typeof guard==='function'&&!guard()){
|
|
1518
|
+
return false;
|
|
1519
|
+
}
|
|
1266
1520
|
let lastError;
|
|
1267
1521
|
let attemptedSubmissionGeneration=null;
|
|
1268
1522
|
for(
|
|
@@ -1271,10 +1525,13 @@
|
|
|
1271
1525
|
attempt++
|
|
1272
1526
|
){
|
|
1273
1527
|
const retryDelay=conversationTimeboxRetryDelays[attempt];
|
|
1274
|
-
if(retryDelay>0){
|
|
1275
|
-
|
|
1528
|
+
if(retryDelay>0&&!(await waitForConversationTimeboxRetry(retryDelay))){
|
|
1529
|
+
return false;
|
|
1276
1530
|
}
|
|
1277
1531
|
await hostSubmissionBarrier.waitForIdle();
|
|
1532
|
+
if(destroyed||aiRuntimeStateAbortController.signal.aborted){
|
|
1533
|
+
return false;
|
|
1534
|
+
}
|
|
1278
1535
|
if(typeof guard==='function'&&!guard()){
|
|
1279
1536
|
return false;
|
|
1280
1537
|
}
|
|
@@ -1288,15 +1545,24 @@
|
|
|
1288
1545
|
const delivery=submitMessage(
|
|
1289
1546
|
message,
|
|
1290
1547
|
{
|
|
1291
|
-
...
|
|
1548
|
+
...deliveryContext,
|
|
1292
1549
|
retry:attempt>0,
|
|
1293
1550
|
reuseVisibleMessage:attempt>0
|
|
1294
1551
|
}
|
|
1295
1552
|
);
|
|
1296
1553
|
attemptedSubmissionGeneration=hostSubmissionGeneration;
|
|
1297
1554
|
const delivered=await delivery;
|
|
1555
|
+
if(destroyed||aiRuntimeStateAbortController.signal.aborted){
|
|
1556
|
+
return false;
|
|
1557
|
+
}
|
|
1558
|
+
if(typeof guard==='function'&&!guard()){
|
|
1559
|
+
return false;
|
|
1560
|
+
}
|
|
1298
1561
|
return requireConversationTimeboxDelivery(delivered);
|
|
1299
1562
|
}catch(error){
|
|
1563
|
+
if(destroyed||aiRuntimeStateAbortController.signal.aborted){
|
|
1564
|
+
return false;
|
|
1565
|
+
}
|
|
1300
1566
|
lastError=error;
|
|
1301
1567
|
}
|
|
1302
1568
|
}
|
|
@@ -1305,6 +1571,12 @@
|
|
|
1305
1571
|
);
|
|
1306
1572
|
programmaticSubmissionQueue=queued.then(
|
|
1307
1573
|
function finishConversationTimeboxSubmission(result){
|
|
1574
|
+
if(destroyed||aiRuntimeStateAbortController.signal.aborted){
|
|
1575
|
+
return false;
|
|
1576
|
+
}
|
|
1577
|
+
if(typeof guard==='function'&&!guard()){
|
|
1578
|
+
return false;
|
|
1579
|
+
}
|
|
1308
1580
|
if(result!==false){
|
|
1309
1581
|
clearConversationTimeboxError();
|
|
1310
1582
|
const current=conversationTimeboxController?.snapshot();
|
|
@@ -1315,21 +1587,31 @@
|
|
|
1315
1587
|
return result;
|
|
1316
1588
|
},
|
|
1317
1589
|
function reportConversationTimeboxSubmissionFailure(error){
|
|
1590
|
+
if(destroyed||aiRuntimeStateAbortController.signal.aborted){
|
|
1591
|
+
return false;
|
|
1592
|
+
}
|
|
1318
1593
|
if(typeof guard==='function'&&!guard()){
|
|
1319
1594
|
return false;
|
|
1320
1595
|
}
|
|
1321
1596
|
setConversationTimeboxError(
|
|
1322
1597
|
'The time check could not reach the AI.'
|
|
1323
1598
|
);
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1599
|
+
dispatchChatEvent(
|
|
1600
|
+
'conversation-timebox-error',
|
|
1601
|
+
{error,context:{...deliveryContext}},
|
|
1602
|
+
{
|
|
1603
|
+
bubbles:true,
|
|
1604
|
+
composed:true,
|
|
1605
|
+
operationId,
|
|
1606
|
+
publicDetail:{
|
|
1607
|
+
source:deliveryContext.source||'conversation-timebox',
|
|
1608
|
+
reason:chatReasons.conversationTimeboxDeliveryRejected,
|
|
1609
|
+
...publicErrorFields(
|
|
1610
|
+
error,
|
|
1611
|
+
chatErrorCodes.conversationTimeboxDeliveryRejected
|
|
1612
|
+
)
|
|
1331
1613
|
}
|
|
1332
|
-
|
|
1614
|
+
}
|
|
1333
1615
|
);
|
|
1334
1616
|
console.error('The conversation timebox message could not be sent.');
|
|
1335
1617
|
return false;
|
|
@@ -1339,22 +1621,46 @@
|
|
|
1339
1621
|
}
|
|
1340
1622
|
|
|
1341
1623
|
function waitForConversationTimeboxRetry(delayMilliseconds){
|
|
1624
|
+
const signal=aiRuntimeStateAbortController.signal;
|
|
1625
|
+
if(signal.aborted){
|
|
1626
|
+
return Promise.resolve(false);
|
|
1627
|
+
}
|
|
1342
1628
|
return new Promise(function scheduleConversationTimeboxRetry(resolve){
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1629
|
+
let settled=false;
|
|
1630
|
+
let timer=null;
|
|
1631
|
+
function finishRetryWait(completed){
|
|
1632
|
+
if(settled){
|
|
1633
|
+
return;
|
|
1634
|
+
}
|
|
1635
|
+
settled=true;
|
|
1636
|
+
if(timer!==null){
|
|
1637
|
+
clearTimeout(timer);
|
|
1638
|
+
}
|
|
1639
|
+
signal.removeEventListener('abort',cancelRetryWait);
|
|
1640
|
+
resolve(completed);
|
|
1351
1641
|
}
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1642
|
+
function cancelRetryWait(){
|
|
1643
|
+
finishRetryWait(false);
|
|
1644
|
+
}
|
|
1645
|
+
timer=setTimeout(function completeRetryWait(){
|
|
1646
|
+
finishRetryWait(true);
|
|
1647
|
+
},delayMilliseconds);
|
|
1648
|
+
signal.addEventListener('abort',cancelRetryWait,{once:true});
|
|
1649
|
+
if(signal.aborted){
|
|
1650
|
+
cancelRetryWait();
|
|
1651
|
+
}
|
|
1652
|
+
});
|
|
1355
1653
|
}
|
|
1356
1654
|
|
|
1357
1655
|
host.ready=true;
|
|
1656
|
+
dispatchChatEvent(
|
|
1657
|
+
'chat-ready',
|
|
1658
|
+
{chat:host},
|
|
1659
|
+
{
|
|
1660
|
+
operationId:nextChatOperationId('ready'),
|
|
1661
|
+
publicDetail:{ready:true,reason:chatReasons.ready}
|
|
1662
|
+
}
|
|
1663
|
+
);
|
|
1358
1664
|
|
|
1359
1665
|
async function streamMessage(text='', id='', isThinking){
|
|
1360
1666
|
if(!id){
|
|
@@ -1429,20 +1735,26 @@
|
|
|
1429
1735
|
chatOutput.scrollTop = chatOutput.scrollHeight;
|
|
1430
1736
|
}
|
|
1431
1737
|
|
|
1432
|
-
textArea.addEventListener(
|
|
1738
|
+
textArea.addEventListener(
|
|
1739
|
+
'keyup',
|
|
1740
|
+
resizeTextArea,
|
|
1741
|
+
{signal:aiRuntimeStateAbortController.signal}
|
|
1742
|
+
);
|
|
1433
1743
|
|
|
1434
1744
|
send.addEventListener(
|
|
1435
1745
|
'click',
|
|
1436
1746
|
function submitChatMessageFromButton(){
|
|
1437
1747
|
void submitMessage('',{source:'user'});
|
|
1438
|
-
}
|
|
1748
|
+
},
|
|
1749
|
+
{signal:aiRuntimeStateAbortController.signal}
|
|
1439
1750
|
);
|
|
1440
1751
|
|
|
1441
1752
|
uploadBtn.addEventListener(
|
|
1442
1753
|
'click',
|
|
1443
1754
|
function(){
|
|
1444
1755
|
fileInput.click();
|
|
1445
|
-
}
|
|
1756
|
+
},
|
|
1757
|
+
{signal:aiRuntimeStateAbortController.signal}
|
|
1446
1758
|
);
|
|
1447
1759
|
|
|
1448
1760
|
fileInput.addEventListener(
|
|
@@ -1455,23 +1767,51 @@
|
|
|
1455
1767
|
for(let i=0;i<fileInput.files.length;i++){
|
|
1456
1768
|
const file=fileInput.files[i];
|
|
1457
1769
|
const newFile=new FileEntity(file.name, 'documents');
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1770
|
+
const operationId=nextChatOperationId('file-upload');
|
|
1771
|
+
try{
|
|
1772
|
+
await newFile.save(file);
|
|
1773
|
+
if(destroyed)return;
|
|
1774
|
+
dispatchChatEvent(
|
|
1775
|
+
'chat-file-uploaded',
|
|
1776
|
+
{file},
|
|
1777
|
+
{
|
|
1778
|
+
bubbles:true,
|
|
1779
|
+
composed:true,
|
|
1780
|
+
cancelable:true,
|
|
1781
|
+
operationId,
|
|
1782
|
+
publicDetail:{
|
|
1783
|
+
size:Number.isFinite(file.size)?file.size:null,
|
|
1784
|
+
type:String(file.type||''),
|
|
1785
|
+
reason:chatReasons.fileStorageCompleted
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1788
|
+
);
|
|
1789
|
+
}catch(error){
|
|
1790
|
+
if(destroyed)return;
|
|
1791
|
+
dispatchChatEvent(
|
|
1792
|
+
'chat-file-upload-error',
|
|
1793
|
+
{file,error},
|
|
1794
|
+
{
|
|
1795
|
+
bubbles:true,
|
|
1796
|
+
composed:true,
|
|
1797
|
+
operationId,
|
|
1798
|
+
publicDetail:{
|
|
1799
|
+
size:Number.isFinite(file.size)?file.size:null,
|
|
1800
|
+
type:String(file.type||''),
|
|
1801
|
+
reason:chatReasons.fileStorageRejected,
|
|
1802
|
+
...publicErrorFields(
|
|
1803
|
+
error,
|
|
1804
|
+
chatErrorCodes.fileStorageRejected
|
|
1805
|
+
)
|
|
1806
|
+
}
|
|
1807
|
+
}
|
|
1808
|
+
);
|
|
1809
|
+
}
|
|
1471
1810
|
}
|
|
1472
1811
|
|
|
1473
1812
|
fileInput.value = '';
|
|
1474
|
-
}
|
|
1813
|
+
},
|
|
1814
|
+
{signal:aiRuntimeStateAbortController.signal}
|
|
1475
1815
|
);
|
|
1476
1816
|
|
|
1477
1817
|
speech.addEventListener(
|
|
@@ -1481,29 +1821,62 @@
|
|
|
1481
1821
|
event.detail?.text||'',
|
|
1482
1822
|
{source:'speech'}
|
|
1483
1823
|
);
|
|
1484
|
-
}
|
|
1824
|
+
},
|
|
1825
|
+
{signal:aiRuntimeStateAbortController.signal}
|
|
1485
1826
|
)
|
|
1486
1827
|
|
|
1487
1828
|
shadowRoot.querySelector('#languages').addEventListener(
|
|
1488
1829
|
'change',
|
|
1489
1830
|
function() {
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
const chatEvent = new CustomEvent(
|
|
1831
|
+
const language=this.value;
|
|
1832
|
+
const operationId=nextChatOperationId('language-change');
|
|
1833
|
+
const accepted=dispatchChatEvent(
|
|
1494
1834
|
'chat-language-changed',
|
|
1835
|
+
{message:language},
|
|
1495
1836
|
{
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1837
|
+
bubbles:true,
|
|
1838
|
+
cancelable:true,
|
|
1839
|
+
operationId,
|
|
1840
|
+
publicDetail:{
|
|
1841
|
+
language,
|
|
1842
|
+
reason:chatReasons.languageChangeRequested
|
|
1843
|
+
}
|
|
1501
1844
|
}
|
|
1502
1845
|
);
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1846
|
+
if(!accepted){
|
|
1847
|
+
this.value=host.language;
|
|
1848
|
+
return;
|
|
1849
|
+
}
|
|
1850
|
+
host.language=language;
|
|
1851
|
+
console.info("Language changed to:",host.language);
|
|
1852
|
+
let result;
|
|
1853
|
+
try{result=host.languageChanged(host.language);}
|
|
1854
|
+
catch(error){result=Promise.reject(error);}
|
|
1855
|
+
void Promise.resolve(result).catch(
|
|
1856
|
+
function reportLanguageChangeCallbackFailure(error){
|
|
1857
|
+
if(destroyed)return false;
|
|
1858
|
+
dispatchChatEvent(
|
|
1859
|
+
'chat-language-change-error',
|
|
1860
|
+
{error,language},
|
|
1861
|
+
{
|
|
1862
|
+
bubbles:true,
|
|
1863
|
+
composed:true,
|
|
1864
|
+
operationId,
|
|
1865
|
+
publicDetail:{
|
|
1866
|
+
language,
|
|
1867
|
+
reason:chatReasons.languageChangeCallbackRejected,
|
|
1868
|
+
...publicErrorFields(
|
|
1869
|
+
error,
|
|
1870
|
+
chatErrorCodes.languageChangeCallbackRejected
|
|
1871
|
+
)
|
|
1872
|
+
}
|
|
1873
|
+
}
|
|
1874
|
+
);
|
|
1875
|
+
return false;
|
|
1876
|
+
}
|
|
1877
|
+
);
|
|
1878
|
+
},
|
|
1879
|
+
{signal:aiRuntimeStateAbortController.signal}
|
|
1507
1880
|
);
|
|
1508
1881
|
|
|
1509
1882
|
function getMilTime() {
|
|
@@ -1528,27 +1901,50 @@
|
|
|
1528
1901
|
textArea.style.height = `${Math.min(scrollHeight, maxHeight)}px`;
|
|
1529
1902
|
}
|
|
1530
1903
|
|
|
1531
|
-
function observeHostSubmission(result,context){
|
|
1532
|
-
const observed=hostSubmissionBarrier.track(result).
|
|
1904
|
+
function observeHostSubmission(result,context,ownership){
|
|
1905
|
+
const observed=hostSubmissionBarrier.track(result).then(
|
|
1906
|
+
function settleHostSubmission(value){
|
|
1907
|
+
if(destroyed||context.signal.aborted){
|
|
1908
|
+
return false;
|
|
1909
|
+
}
|
|
1910
|
+
return value;
|
|
1911
|
+
},
|
|
1533
1912
|
function reportHostSubmissionFailure(error){
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1913
|
+
if(destroyed||context.signal.aborted){
|
|
1914
|
+
return false;
|
|
1915
|
+
}
|
|
1916
|
+
dispatchChatEvent(
|
|
1917
|
+
'chat-send-error',
|
|
1918
|
+
{error,context:{...context}},
|
|
1919
|
+
{
|
|
1920
|
+
bubbles:true,
|
|
1921
|
+
composed:true,
|
|
1922
|
+
operationId:context.operationId||null,
|
|
1923
|
+
publicDetail:{
|
|
1924
|
+
source:context.source||'user',
|
|
1925
|
+
reason:chatReasons.hostMessageSubmissionRejected,
|
|
1926
|
+
...publicErrorFields(
|
|
1927
|
+
error,
|
|
1928
|
+
chatErrorCodes.hostMessageSubmissionRejected
|
|
1929
|
+
)
|
|
1541
1930
|
}
|
|
1542
|
-
|
|
1931
|
+
}
|
|
1543
1932
|
);
|
|
1544
1933
|
console.error('The chat message could not be submitted.');
|
|
1545
1934
|
return false;
|
|
1546
1935
|
}
|
|
1936
|
+
).finally(
|
|
1937
|
+
function releaseHostSubmissionOwnership(){
|
|
1938
|
+
ownership.release();
|
|
1939
|
+
}
|
|
1547
1940
|
);
|
|
1548
1941
|
return observed;
|
|
1549
1942
|
}
|
|
1550
1943
|
|
|
1551
1944
|
async function submitMessage(textOverride='',context={}) {
|
|
1945
|
+
if(destroyed){
|
|
1946
|
+
return false;
|
|
1947
|
+
}
|
|
1552
1948
|
if(typeof textOverride!=='string'){
|
|
1553
1949
|
throw new TypeError('Programmatic chat messages must be strings.');
|
|
1554
1950
|
}
|
|
@@ -1571,60 +1967,104 @@
|
|
|
1571
1967
|
if(!text.trim()){
|
|
1572
1968
|
return false;
|
|
1573
1969
|
}
|
|
1574
|
-
const
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
// console.log(forceText,text,1);
|
|
1581
|
-
if(!submissionContext.synthetic&&!submissionContext.reuseVisibleMessage){
|
|
1582
|
-
hostSubmissionGeneration++;
|
|
1583
|
-
chatOutput.innerHTML += `
|
|
1584
|
-
<li class='user'>
|
|
1585
|
-
<strong>${host.name} ${messageTime}:</strong>
|
|
1586
|
-
<br>
|
|
1587
|
-
<br>
|
|
1588
|
-
<div class='markdown'>
|
|
1589
|
-
${new MD(text).rendered}
|
|
1590
|
-
</div>
|
|
1591
|
-
</li>
|
|
1592
|
-
`;
|
|
1593
|
-
}
|
|
1594
|
-
chatOutput.scrollTop = chatOutput.scrollHeight;
|
|
1595
|
-
|
|
1596
|
-
const chatEvent = new CustomEvent(
|
|
1597
|
-
'chat-send-message',
|
|
1970
|
+
const operationId=typeof submissionContext.operationId==='string'
|
|
1971
|
+
&&submissionContext.operationId
|
|
1972
|
+
?submissionContext.operationId
|
|
1973
|
+
:nextChatOperationId('message');
|
|
1974
|
+
const ownership=createChatSubmissionOwnership(submissionContext.signal??null);
|
|
1975
|
+
const eventContext=Object.freeze(
|
|
1598
1976
|
{
|
|
1599
|
-
|
|
1977
|
+
...submissionContext,
|
|
1978
|
+
operationId,
|
|
1979
|
+
signal:ownership.signal
|
|
1980
|
+
}
|
|
1981
|
+
);
|
|
1982
|
+
if(ownership.signal.aborted){
|
|
1983
|
+
ownership.release();
|
|
1984
|
+
return false;
|
|
1985
|
+
}
|
|
1986
|
+
let accepted;
|
|
1987
|
+
try{
|
|
1988
|
+
accepted=dispatchChatEvent(
|
|
1989
|
+
'chat-send-message',
|
|
1990
|
+
{
|
|
1600
1991
|
message:text,
|
|
1601
|
-
context:
|
|
1992
|
+
context:eventContext
|
|
1602
1993
|
},
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1994
|
+
{
|
|
1995
|
+
bubbles:true,
|
|
1996
|
+
composed:true,
|
|
1997
|
+
cancelable:true,
|
|
1998
|
+
operationId,
|
|
1999
|
+
publicDetail:{
|
|
2000
|
+
messageLength:text.length,
|
|
2001
|
+
source:eventContext.source,
|
|
2002
|
+
synthetic:eventContext.synthetic===true,
|
|
2003
|
+
reason:chatReasons.messageSubmissionRequested
|
|
2004
|
+
}
|
|
2005
|
+
}
|
|
2006
|
+
);
|
|
2007
|
+
}catch(error){
|
|
2008
|
+
ownership.release();
|
|
2009
|
+
throw error;
|
|
2010
|
+
}
|
|
2011
|
+
if(!accepted){
|
|
2012
|
+
ownership.abort(
|
|
2013
|
+
createChatSubmissionAbort(
|
|
2014
|
+
chatReasons.messageSubmissionCancelled,
|
|
2015
|
+
'The chat message submission was canceled before delivery.'
|
|
2016
|
+
)
|
|
2017
|
+
);
|
|
2018
|
+
return false;
|
|
2019
|
+
}
|
|
2020
|
+
if(destroyed||ownership.signal.aborted){
|
|
2021
|
+
ownership.release();
|
|
2022
|
+
return false;
|
|
2023
|
+
}
|
|
2024
|
+
try{
|
|
2025
|
+
const messageTime = getMilTime();
|
|
2026
|
+
if(!eventContext.preserveDraft){
|
|
2027
|
+
textArea.value='';
|
|
1606
2028
|
}
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
2029
|
+
|
|
2030
|
+
//console.log(host,host.name);
|
|
2031
|
+
// console.log(forceText,text,1);
|
|
2032
|
+
if(!eventContext.synthetic&&!eventContext.reuseVisibleMessage){
|
|
2033
|
+
hostSubmissionGeneration++;
|
|
2034
|
+
chatOutput.innerHTML += `
|
|
2035
|
+
<li class='user'>
|
|
2036
|
+
<strong>${host.name} ${messageTime}:</strong>
|
|
2037
|
+
<br>
|
|
2038
|
+
<br>
|
|
2039
|
+
<div class='markdown'>
|
|
2040
|
+
${new MD(text).rendered}
|
|
2041
|
+
</div>
|
|
2042
|
+
</li>
|
|
2043
|
+
`;
|
|
1616
2044
|
}
|
|
1617
|
-
|
|
2045
|
+
chatOutput.scrollTop = chatOutput.scrollHeight;
|
|
1618
2046
|
|
|
1619
|
-
|
|
2047
|
+
if(host.aiAvailability.tts&&speech.muted===false){
|
|
2048
|
+
const aiRuntime=globalThis.ai;
|
|
2049
|
+
aiRuntime?.stopAudio?.();
|
|
2050
|
+
if(typeof aiRuntime?.resumeAudio==='function'){
|
|
2051
|
+
void Promise.resolve(aiRuntime.resumeAudio()).catch(
|
|
2052
|
+
reportTTSError
|
|
2053
|
+
);
|
|
2054
|
+
}
|
|
2055
|
+
}
|
|
1620
2056
|
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
2057
|
+
let result;
|
|
2058
|
+
try{
|
|
2059
|
+
result=host.sendMessage(text,eventContext);
|
|
2060
|
+
}catch(error){
|
|
2061
|
+
result=Promise.reject(error);
|
|
2062
|
+
}
|
|
2063
|
+
return observeHostSubmission(result,eventContext,ownership);
|
|
1624
2064
|
}catch(error){
|
|
1625
|
-
|
|
2065
|
+
ownership.release();
|
|
2066
|
+
throw error;
|
|
1626
2067
|
}
|
|
1627
|
-
return observeHostSubmission(result,submissionContext);
|
|
1628
2068
|
}
|
|
1629
2069
|
|
|
1630
2070
|
async function receivedMessage(name = 'Assistant', text = '', id=''){
|
|
@@ -1644,32 +2084,62 @@
|
|
|
1644
2084
|
}
|
|
1645
2085
|
|
|
1646
2086
|
function reportTTSError(error){
|
|
2087
|
+
if(destroyed){
|
|
2088
|
+
return false;
|
|
2089
|
+
}
|
|
1647
2090
|
if(typeof speech.reportTTSError==='function'){
|
|
1648
|
-
|
|
1649
|
-
|
|
2091
|
+
try{
|
|
2092
|
+
speech.reportTTSError(error);
|
|
2093
|
+
return true;
|
|
2094
|
+
}catch(reportingError){
|
|
2095
|
+
error=reportingError;
|
|
2096
|
+
}
|
|
1650
2097
|
}
|
|
1651
2098
|
|
|
1652
2099
|
const message=typeof error?.message==='string'&&error.message.trim()
|
|
1653
2100
|
?error.message.trim().slice(0,240)
|
|
1654
2101
|
:'Speech synthesis failed.';
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
2102
|
+
dispatchChatEvent(
|
|
2103
|
+
'chat-speech-synthesis-error',
|
|
2104
|
+
{error,message},
|
|
2105
|
+
{
|
|
2106
|
+
bubbles:true,
|
|
2107
|
+
composed:true,
|
|
2108
|
+
operationId:nextChatOperationId('speech-synthesis'),
|
|
2109
|
+
publicDetail:{
|
|
2110
|
+
reason:chatReasons.speechSynthesisRejected,
|
|
2111
|
+
...publicErrorFields(
|
|
2112
|
+
error,
|
|
2113
|
+
chatErrorCodes.speechSynthesisRejected
|
|
2114
|
+
)
|
|
1662
2115
|
}
|
|
1663
|
-
|
|
2116
|
+
}
|
|
1664
2117
|
);
|
|
1665
2118
|
console.error('Speech synthesis failed.');
|
|
2119
|
+
return false;
|
|
1666
2120
|
}
|
|
1667
2121
|
|
|
1668
2122
|
function destroy(){
|
|
1669
|
-
|
|
2123
|
+
if(destroyed){
|
|
2124
|
+
return;
|
|
2125
|
+
}
|
|
2126
|
+
destroyed=true;
|
|
2127
|
+
const destroyReason=createChatSubmissionAbort(
|
|
2128
|
+
chatReasons.componentDestroyed,
|
|
2129
|
+
'The chat component was destroyed before message submission settled.'
|
|
2130
|
+
);
|
|
2131
|
+
for(const ownership of [...activeSubmissionOwnerships]){
|
|
2132
|
+
ownership.abort(destroyReason);
|
|
2133
|
+
}
|
|
2134
|
+
aiRuntimeStateAbortController.abort(destroyReason);
|
|
1670
2135
|
aiActivationController.destroy();
|
|
1671
2136
|
window.removeEventListener('pagehide',destroy);
|
|
2137
|
+
conversationTimeboxUnsubscribe?.();
|
|
2138
|
+
conversationTimeboxUnsubscribe=null;
|
|
2139
|
+
conversationTimeboxController=null;
|
|
2140
|
+
initialSpeechMuteListenerInstalled=false;
|
|
1672
2141
|
speech.destroy?.();
|
|
2142
|
+
events.dispose();
|
|
1673
2143
|
host.ready=false;
|
|
1674
2144
|
}
|
|
1675
2145
|
|