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.
- package/CHANGELOG.md +44 -0
- package/README.md +42 -19
- package/bin/arcane-test.mjs +16 -13
- package/browser-runtime/ai/browser-speech-artifacts.mjs +41 -38
- package/browser-runtime/ai/browser-speech-providers.mjs +53 -46
- package/browser-runtime/ai/browser-wasm-llm-provider.mjs +127 -124
- package/browser-runtime/ai/browser-wasm.mjs +6 -3
- package/browser-runtime/ai/browser-wllama-runtime.mjs +26 -23
- package/browser-runtime/ai/model-controller.mjs +94 -91
- package/browser-runtime/ai/speech-worker-client.mjs +12 -9
- package/browser-runtime/ai/speech-worker-runtime.mjs +56 -53
- package/browser-runtime/dependencies/strong-type/index.js +207 -82
- package/browser-runtime/dependencies/strong-type/package.json +15 -6
- package/browser-runtime/dom-event-instrumentation.mjs +24 -20
- package/browser-runtime/event-manager.mjs +111 -108
- package/browser-runtime/speech-text.mjs +35 -0
- package/docs/architecture.md +1 -1
- package/docs/reference/README.md +8 -8
- package/docs/reference/ai/browser-speech.md +230 -16
- package/docs/reference/ai/twin-cloud.md +1 -1
- package/docs/reference/availability-and-normalization.md +42 -1
- package/docs/reference/cli.md +1 -1
- package/docs/reference/core/arcane-ai-contracts.md +1 -1
- package/docs/reference/inventory/package-api.json +60 -4
- package/docs/reference/inventory/runtime-components.json +2 -2
- package/docs/reference/inventory/runtime-modules.json +29 -31
- package/docs/reference/protocols.md +17 -13
- package/docs/reference/runtime-components.md +6 -5
- package/docs/reference/runtime-modules.md +212 -70
- package/docs/reference/sdk-api.md +197 -11
- package/examples/wasm-ai-demo/index.html +1 -0
- package/node_modules/event-pubsub/node_modules/strong-type/README.md +408 -0
- package/node_modules/event-pubsub/node_modules/strong-type/assets/strong-type-header.png +0 -0
- package/node_modules/event-pubsub/node_modules/strong-type/index.js +1151 -0
- package/node_modules/event-pubsub/node_modules/strong-type/licence +21 -0
- package/node_modules/event-pubsub/node_modules/strong-type/node.js +125 -0
- package/node_modules/event-pubsub/node_modules/strong-type/package.json +61 -0
- package/node_modules/strong-type/README.md +42 -24
- package/node_modules/strong-type/benchmark/core.js +324 -0
- package/node_modules/strong-type/index.js +207 -82
- package/node_modules/strong-type/package.json +14 -6
- package/package.json +4 -3
- package/runtime/arcane/components/app-bar.html +5 -2
- package/runtime/arcane/components/assistant-panel.html +5 -2
- package/runtime/arcane/components/chart.html +15 -12
- package/runtime/arcane/components/chat.html +118 -141
- package/runtime/arcane/components/dashboard-config.html +6 -3
- package/runtime/arcane/components/data-view.html +4 -1
- package/runtime/arcane/components/directory-picker.html +5 -2
- package/runtime/arcane/components/document-inspector.html +6 -3
- package/runtime/arcane/components/file-drop.html +5 -2
- package/runtime/arcane/components/file-inspector.html +9 -6
- package/runtime/arcane/components/file-manager.html +16 -13
- package/runtime/arcane/components/local-ai-status.html +5 -2
- package/runtime/arcane/components/markdown-document.html +14 -11
- package/runtime/arcane/components/markdown-editor.html +4 -1
- package/runtime/arcane/components/modal.html +4 -1
- package/runtime/arcane/components/output-panel.html +6 -3
- package/runtime/arcane/components/preferences-form.html +4 -1
- package/runtime/arcane/components/record-timeline.html +8 -5
- package/runtime/arcane/components/relationship-board.html +9 -6
- package/runtime/arcane/components/source-code-viewer.html +12 -9
- package/runtime/arcane/components/source-explanation.html +8 -5
- package/runtime/arcane/components/speech.html +24 -21
- package/runtime/arcane/components/summary-strip.html +4 -1
- package/runtime/arcane/components/task-progress.html +6 -3
- package/runtime/arcane/components/terminal-workspace.html +4 -1
- package/runtime/arcane/components/voice-transcription.html +11 -8
- package/runtime/arcane/components/web-navigator.html +5 -2
- package/runtime/arcane/entities/ApiModelRecord.js +5 -2
- package/runtime/arcane/entities/Calculation.js +5 -2
- package/runtime/arcane/entities/Chat.js +33 -33
- package/runtime/arcane/entities/CommunicationMessage.js +4 -1
- package/runtime/arcane/entities/CommunicationThread.js +4 -1
- package/runtime/arcane/entities/File.js +1 -1
- package/runtime/arcane/entities/Image.js +8 -5
- package/runtime/arcane/entities/IntentEnvelope.js +20 -17
- package/runtime/arcane/entities/Preference.js +9 -6
- package/runtime/arcane/entities/Theme.js +5 -2
- package/runtime/arcane/entities/User.js +11 -11
- package/runtime/arcane/entities/Weather.js +5 -2
- package/runtime/arcane/modules/AI.js +503 -187
- package/runtime/arcane/modules/AIPreferenceRuntime.js +5 -2
- package/runtime/arcane/modules/AIPreferenceTuple.js +9 -6
- package/runtime/arcane/modules/AIProviderRuntime.js +117 -92
- package/runtime/arcane/modules/AIResponseURLPolicy.js +148 -62
- package/runtime/arcane/modules/AIRuntimeState.js +29 -26
- package/runtime/arcane/modules/AnsiText.js +4 -1
- package/runtime/arcane/modules/ApiModelDatabase.js +22 -19
- package/runtime/arcane/modules/AppDataScope.js +8 -5
- package/runtime/arcane/modules/ArcaneCommunicationBridge.js +4 -1
- package/runtime/arcane/modules/ArcaneNavigationPolicy.js +8 -5
- package/runtime/arcane/modules/ArcaneNetworkPolicy.js +18 -15
- package/runtime/arcane/modules/AsyncBoundary.js +13 -10
- package/runtime/arcane/modules/BrowserTestSuite.js +19 -16
- package/runtime/arcane/modules/CalculatorEngine.js +5 -2
- package/runtime/arcane/modules/ChatRecords.js +18 -15
- package/runtime/arcane/modules/CommunicationAppController.js +9 -6
- package/runtime/arcane/modules/CommunicationHub.js +9 -6
- package/runtime/arcane/modules/CommunicationPreferences.js +4 -1
- package/runtime/arcane/modules/CommunicationProviderRegistry.js +4 -1
- package/runtime/arcane/modules/ComponentContracts.js +56 -53
- package/runtime/arcane/modules/ConfiguredAIChatSession.js +30 -27
- package/runtime/arcane/modules/ConversationActionItems.js +15 -12
- package/runtime/arcane/modules/ConversationClosingReport.js +11 -8
- package/runtime/arcane/modules/ConversationTimebox.js +28 -25
- package/runtime/arcane/modules/CoreLocalModelCatalog.js +17 -14
- package/runtime/arcane/modules/DBLS.js +7 -4
- package/runtime/arcane/modules/DBOPFS.js +7 -7
- package/runtime/arcane/modules/DBOPFSDocumentLibrary.js +34 -31
- package/runtime/arcane/modules/DevelopmentWorkspace.js +4 -1
- package/runtime/arcane/modules/DirectoryPicker.js +8 -5
- package/runtime/arcane/modules/DocumentLexicalSearch.js +11 -8
- package/runtime/arcane/modules/DocumentNavigation.js +7 -4
- package/runtime/arcane/modules/Errors.js +28 -25
- package/runtime/arcane/modules/HTMLImport.js +7 -4
- package/runtime/arcane/modules/IsolatedModelQuestionRunner.js +15 -12
- package/runtime/arcane/modules/LocalAIReadiness.js +21 -18
- package/runtime/arcane/modules/LocalAIReadinessController.js +6 -3
- package/runtime/arcane/modules/MD.js +1 -1
- package/runtime/arcane/modules/Mail.js +46 -43
- package/runtime/arcane/modules/MailOutbox.mjs +48 -45
- package/runtime/arcane/modules/MailTransport.mjs +29 -26
- package/runtime/arcane/modules/MarkdownSpeech.js +1 -50
- package/runtime/arcane/modules/MemoryRecords.js +7 -4
- package/runtime/arcane/modules/MessageAdvisory.js +6 -3
- package/runtime/arcane/modules/ModelDefinition.js +7 -4
- package/runtime/arcane/modules/Ollama.js +6 -3
- package/runtime/arcane/modules/OllamaModelIdentifier.js +4 -1
- package/runtime/arcane/modules/OpenMeteoWeatherProvider.js +18 -15
- package/runtime/arcane/modules/PersistentAIChatSession.js +33 -30
- package/runtime/arcane/modules/PreferenceStore.js +20 -17
- package/runtime/arcane/modules/PreparedSpeech.js +485 -0
- package/runtime/arcane/modules/Questionnaire.js +6 -3
- package/runtime/arcane/modules/RecordLinkIndex.js +4 -1
- package/runtime/arcane/modules/RecordPassageIndex.js +9 -6
- package/runtime/arcane/modules/RecordReviewStore.js +12 -9
- package/runtime/arcane/modules/RiskSignalAnalyzer.js +4 -1
- package/runtime/arcane/modules/ScopedOPFSCache.js +6 -3
- package/runtime/arcane/modules/ScreenCapture.js +20 -17
- package/runtime/arcane/modules/SpeechPlayback.js +61 -28
- package/runtime/arcane/modules/StaticDocumentCatalog.js +33 -30
- package/runtime/arcane/modules/SystemAppearance.js +5 -2
- package/runtime/arcane/modules/SystemToolRegistry.js +6 -3
- package/runtime/arcane/modules/TerminalClient.js +14 -11
- package/runtime/arcane/modules/TerminalCommandRegistry.js +4 -1
- package/runtime/arcane/modules/ThemeBootstrap.js +9 -6
- package/runtime/arcane/modules/ThemeManager.js +5 -2
- package/runtime/arcane/modules/TimeGuard.js +1 -1
- package/runtime/arcane/modules/ToolCallRouter.js +11 -8
- package/runtime/arcane/modules/WaitForComponent.js +19 -16
- package/runtime/arcane/modules/YouTubeMedia.js +4 -1
- package/runtime/strong-type/index.js +1276 -352
- package/runtime/strong-type/package.json +69 -45
- package/src/app-descriptor.mjs +17 -14
- package/src/application-tests.mjs +4 -1
- package/src/cli/main.mjs +8 -5
- package/src/constants.mjs +4 -1
- package/src/dev-server.mjs +8 -5
- package/src/doctor.mjs +5 -2
- package/src/dom-event-instrumentation.mjs +24 -20
- package/src/errors.mjs +7 -3
- package/src/event-manager.mjs +111 -108
- package/src/event-queue.mjs +8 -5
- package/src/events.mjs +12 -9
- package/src/import-map.mjs +36 -27
- package/src/installed-sdk-runtime.mjs +4 -1
- package/src/integrated-provider-loader.mjs +9 -6
- package/src/mail-credentials.mjs +16 -13
- package/src/mail-server.mjs +53 -50
- package/src/mail.mjs +18 -15
- package/src/native-plan.mjs +12 -9
- package/src/native-provider-loader.mjs +7 -4
- package/src/packager/core.mjs +18 -15
- package/src/process.mjs +6 -3
- package/src/release-bundle.mjs +14 -11
- package/src/runtime.mjs +4 -1
- package/src/scaffold.mjs +9 -6
- package/src/sdk-browser-runtime.mjs +4 -1
- package/src/source-server.mjs +17 -14
- package/src/targets/index.mjs +5 -2
- package/src/templates/workspace-template.mjs +13 -6
- package/src/testing-loader.mjs +7 -4
- package/src/testing.mjs +10 -7
- package/src/toolchain.mjs +13 -10
- package/src/update-check.mjs +9 -6
- package/src/workspace-operation-lock.mjs +14 -11
- package/src/workspace-runtime.mjs +4 -1
- package/src/workspace.mjs +17 -14
- 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
|
const PROVIDER_ID=/^[a-z0-9][a-z0-9._-]{1,63}$/;
|
|
2
5
|
|
|
3
6
|
export default class CommunicationProviderRegistry{
|
|
@@ -5,7 +8,7 @@ export default class CommunicationProviderRegistry{
|
|
|
5
8
|
register(provider){
|
|
6
9
|
const id=String(provider?.id||'').trim().toLowerCase();
|
|
7
10
|
if(!PROVIDER_ID.test(id)) throw new TypeError('Communication provider id is invalid.');
|
|
8
|
-
for(const method of ['listThreads','getMessages','send']) if(
|
|
11
|
+
for(const method of ['listThreads','getMessages','send']) if(!is.function(provider[method])) throw new TypeError(`Provider ${id} must implement ${method}().`);
|
|
9
12
|
if(this.providers.has(id)) throw new RangeError(`Communication provider already registered: ${id}`);
|
|
10
13
|
this.providers.set(id,{...provider,id,label:String(provider.label||id),channels:Object.freeze(Array.from(provider.channels||['other'])),listThreads:provider.listThreads.bind(provider),getMessages:provider.getMessages.bind(provider),send:provider.send.bind(provider)});
|
|
11
14
|
return this.get(id);
|
|
@@ -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
|
import {
|
|
3
6
|
createArcaneEventSource,
|
|
@@ -46,7 +49,7 @@ function projectSTTActivationEvent(
|
|
|
46
49
|
EventClass,
|
|
47
50
|
options={}
|
|
48
51
|
){
|
|
49
|
-
if(
|
|
52
|
+
if(!is.function(globalThis.CustomEvent)){
|
|
50
53
|
throw codedError(
|
|
51
54
|
'The STT activation DOM compatibility projection requires CustomEvent.',
|
|
52
55
|
STT_ACTIVATION_ERROR_CODES.domProjectionUnavailable
|
|
@@ -81,14 +84,14 @@ function record(value,label){
|
|
|
81
84
|
if(value===undefined){
|
|
82
85
|
return {};
|
|
83
86
|
}
|
|
84
|
-
if(!value
|
|
87
|
+
if(!value||!is.object(value)||is.array(value)){
|
|
85
88
|
throw new TypeError(`${label} must be an object`);
|
|
86
89
|
}
|
|
87
90
|
return value;
|
|
88
91
|
}
|
|
89
92
|
|
|
90
93
|
function string(value,fallback='',allowEmpty=false){
|
|
91
|
-
if(
|
|
94
|
+
if(!is.string(value)){
|
|
92
95
|
return fallback;
|
|
93
96
|
}
|
|
94
97
|
const normalized=value.trim();
|
|
@@ -96,7 +99,7 @@ function string(value,fallback='',allowEmpty=false){
|
|
|
96
99
|
}
|
|
97
100
|
|
|
98
101
|
function text(value,fallback='',allowEmpty=false){
|
|
99
|
-
if(
|
|
102
|
+
if(!is.string(value)){
|
|
100
103
|
return fallback;
|
|
101
104
|
}
|
|
102
105
|
return value||(allowEmpty?'':fallback);
|
|
@@ -126,10 +129,10 @@ function optionAlias(input,previous,keys,fallback){
|
|
|
126
129
|
|
|
127
130
|
function optionalCallback(input,previous,keys,label=keys[0]){
|
|
128
131
|
const value=optionAlias(input,previous,keys,null);
|
|
129
|
-
if(value!==null&&value!==undefined
|
|
132
|
+
if(value!==null&&value!==undefined&&!is.function(value)){
|
|
130
133
|
throw new TypeError(`${label} must be a function or null`);
|
|
131
134
|
}
|
|
132
|
-
return
|
|
135
|
+
return is.function(value)?value:null;
|
|
133
136
|
}
|
|
134
137
|
|
|
135
138
|
function finiteNumber(value,label){
|
|
@@ -137,7 +140,7 @@ function finiteNumber(value,label){
|
|
|
137
140
|
return null;
|
|
138
141
|
}
|
|
139
142
|
const normalized=Number(value);
|
|
140
|
-
if(!
|
|
143
|
+
if(!is.finite(normalized)){
|
|
141
144
|
throw new TypeError(`${label} must be a finite number or null`);
|
|
142
145
|
}
|
|
143
146
|
return normalized;
|
|
@@ -147,7 +150,7 @@ function stringRecord(value,defaults,label){
|
|
|
147
150
|
value=record(value,label);
|
|
148
151
|
const normalized={...defaults};
|
|
149
152
|
for(const [key,item] of Object.entries(value)){
|
|
150
|
-
if(
|
|
153
|
+
if(!is.string(item)){
|
|
151
154
|
throw new TypeError(`${label}.${key} must be a string`);
|
|
152
155
|
}
|
|
153
156
|
normalized[key]=item;
|
|
@@ -189,7 +192,7 @@ function normalizeChartOptions(input={},previous={}){
|
|
|
189
192
|
};
|
|
190
193
|
|
|
191
194
|
for(const [source,target] of Object.entries(aliases)){
|
|
192
|
-
if(
|
|
195
|
+
if(is.string(input[source])){
|
|
193
196
|
labels[target]=input[source];
|
|
194
197
|
}
|
|
195
198
|
}
|
|
@@ -208,8 +211,8 @@ function normalizeChartOptions(input={},previous={}){
|
|
|
208
211
|
const chartOptions=option(input,previous,'chartOptions',null);
|
|
209
212
|
if(
|
|
210
213
|
chartOptions!==null
|
|
211
|
-
|
|
212
|
-
&&(!chartOptions
|
|
214
|
+
&&!is.function(chartOptions)
|
|
215
|
+
&&(!chartOptions||!is.object(chartOptions)||is.array(chartOptions))
|
|
213
216
|
){
|
|
214
217
|
throw new TypeError('chartOptions must be an object, function, or null');
|
|
215
218
|
}
|
|
@@ -237,7 +240,7 @@ function normalizeChartOptions(input={},previous={}){
|
|
|
237
240
|
parseY:optionalCallback(input,previous,['parseY']),
|
|
238
241
|
formatTime:optionalCallback(input,previous,['formatTime']),
|
|
239
242
|
formatValue:optionalCallback(input,previous,['formatValue']),
|
|
240
|
-
chartOptions:
|
|
243
|
+
chartOptions:is.object(chartOptions)&&chartOptions!==null
|
|
241
244
|
?{...chartOptions}
|
|
242
245
|
:chartOptions,
|
|
243
246
|
data
|
|
@@ -249,22 +252,22 @@ function numericX(value,time=true){
|
|
|
249
252
|
return value.getTime();
|
|
250
253
|
}
|
|
251
254
|
const number=Number(value);
|
|
252
|
-
if(
|
|
255
|
+
if(is.finite(number)){
|
|
253
256
|
return number;
|
|
254
257
|
}
|
|
255
|
-
if(time&&
|
|
258
|
+
if(time&&is.string(value)){
|
|
256
259
|
const parsed=Date.parse(value);
|
|
257
|
-
return
|
|
260
|
+
return is.finite(parsed)?parsed:NaN;
|
|
258
261
|
}
|
|
259
262
|
return NaN;
|
|
260
263
|
}
|
|
261
264
|
|
|
262
265
|
function normalizeChartRows(data=[],options={}){
|
|
263
266
|
const config=normalizeChartOptions(options);
|
|
264
|
-
if(data&&
|
|
267
|
+
if(data&&is.object(data)&&!is.array(data)){
|
|
265
268
|
data=Object.entries(data);
|
|
266
269
|
}
|
|
267
|
-
if(!
|
|
270
|
+
if(!is.array(data)){
|
|
268
271
|
return [];
|
|
269
272
|
}
|
|
270
273
|
const normalized=[];
|
|
@@ -272,12 +275,12 @@ function normalizeChartRows(data=[],options={}){
|
|
|
272
275
|
for(let index=0;index<data.length;index++){
|
|
273
276
|
const original=data[index];
|
|
274
277
|
const item=config.mapRow?config.mapRow(original,index):original;
|
|
275
|
-
const x=
|
|
278
|
+
const x=is.array(item)
|
|
276
279
|
?item[0]
|
|
277
280
|
:config.xKey
|
|
278
281
|
?item?.[config.xKey]
|
|
279
282
|
:item?.date??item?.timestamp??item?.x;
|
|
280
|
-
const y=
|
|
283
|
+
const y=is.array(item)
|
|
281
284
|
?item[1]
|
|
282
285
|
:config.yKey
|
|
283
286
|
?item?.[config.yKey]
|
|
@@ -287,7 +290,7 @@ function normalizeChartRows(data=[],options={}){
|
|
|
287
290
|
const normalizedX=numericX(parsedX,config.time);
|
|
288
291
|
const normalizedY=Number(parsedY);
|
|
289
292
|
|
|
290
|
-
if(
|
|
293
|
+
if(is.finite(normalizedX)&&is.finite(normalizedY)){
|
|
291
294
|
normalized.push([normalizedX,normalizedY]);
|
|
292
295
|
}
|
|
293
296
|
}
|
|
@@ -297,7 +300,7 @@ function normalizeChartRows(data=[],options={}){
|
|
|
297
300
|
}
|
|
298
301
|
|
|
299
302
|
function normalizeDashboardDefinitions(values=[]){
|
|
300
|
-
if(!
|
|
303
|
+
if(!is.array(values)){
|
|
301
304
|
throw new TypeError('Dashboard definitions must be an array');
|
|
302
305
|
}
|
|
303
306
|
const keys=new Set();
|
|
@@ -349,7 +352,7 @@ function normalizeDashboardVisibility(values={}){
|
|
|
349
352
|
values=record(values,'Dashboard visibility');
|
|
350
353
|
const visibility={};
|
|
351
354
|
for(const [key,value] of Object.entries(values)){
|
|
352
|
-
if(
|
|
355
|
+
if(is.boolean(value)){
|
|
353
356
|
visibility[key]=value;
|
|
354
357
|
}
|
|
355
358
|
}
|
|
@@ -372,7 +375,7 @@ function normalizeDashboardOptions(input={},previous={}){
|
|
|
372
375
|
triggerLabel:'trigger'
|
|
373
376
|
};
|
|
374
377
|
for(const [source,target] of Object.entries(aliases)){
|
|
375
|
-
if(
|
|
378
|
+
if(is.string(input[source])){
|
|
376
379
|
labels[target]=input[source];
|
|
377
380
|
}
|
|
378
381
|
}
|
|
@@ -380,7 +383,7 @@ function normalizeDashboardOptions(input={},previous={}){
|
|
|
380
383
|
labels,
|
|
381
384
|
definitions:own(input,'definitions')
|
|
382
385
|
?normalizeDashboardDefinitions(input.definitions)
|
|
383
|
-
:
|
|
386
|
+
:is.array(previous.definitions)
|
|
384
387
|
?normalizeDashboardDefinitions(previous.definitions)
|
|
385
388
|
:[],
|
|
386
389
|
visibility:own(input,'visibility')
|
|
@@ -396,7 +399,7 @@ function effectiveDashboardVisibility(definitions=[],visibility={}){
|
|
|
396
399
|
definitions.map(
|
|
397
400
|
definition=>[
|
|
398
401
|
definition.key,
|
|
399
|
-
|
|
402
|
+
is.boolean(visibility[definition.key])
|
|
400
403
|
?visibility[definition.key]
|
|
401
404
|
:definition.defaultVisible
|
|
402
405
|
]
|
|
@@ -405,16 +408,16 @@ function effectiveDashboardVisibility(definitions=[],visibility={}){
|
|
|
405
408
|
}
|
|
406
409
|
|
|
407
410
|
function formatAIRuntimeProgress(progress,fallback){
|
|
408
|
-
const phase=
|
|
411
|
+
const phase=is.string(progress?.phase)&&progress.phase
|
|
409
412
|
?progress.phase
|
|
410
413
|
:fallback;
|
|
411
414
|
const completed=progress?.completed;
|
|
412
415
|
const total=progress?.total;
|
|
413
|
-
const unit=
|
|
416
|
+
const unit=is.string(progress?.unit)
|
|
414
417
|
?progress.unit
|
|
415
418
|
:'';
|
|
416
|
-
if(!
|
|
417
|
-
||!
|
|
419
|
+
if(!is.finite(completed)
|
|
420
|
+
||!is.finite(total)
|
|
418
421
|
||total<=0
|
|
419
422
|
||!unit){
|
|
420
423
|
return phase;
|
|
@@ -429,15 +432,15 @@ function createSTTActivationController({
|
|
|
429
432
|
EventClass=globalThis.CustomEvent,
|
|
430
433
|
eventSource=null
|
|
431
434
|
}){
|
|
432
|
-
if(!host
|
|
433
|
-
|
|
435
|
+
if(!host||!is.function(host.dispatchEvent)
|
|
436
|
+
||!is.function(host.requestSTTActivation)){
|
|
434
437
|
throw codedError(
|
|
435
438
|
'The STT activation host must provide dispatchEvent() and requestSTTActivation().',
|
|
436
439
|
STT_ACTIVATION_ERROR_CODES.hostInvalid,
|
|
437
440
|
TypeError
|
|
438
441
|
);
|
|
439
442
|
}
|
|
440
|
-
if(
|
|
443
|
+
if(!is.function(EventClass)){
|
|
441
444
|
throw codedError(
|
|
442
445
|
'The STT activation event class must be a constructor.',
|
|
443
446
|
STT_ACTIVATION_ERROR_CODES.eventClassInvalid,
|
|
@@ -445,15 +448,15 @@ function createSTTActivationController({
|
|
|
445
448
|
);
|
|
446
449
|
}
|
|
447
450
|
if(!button
|
|
448
|
-
|
|
449
|
-
|
|
451
|
+
||!is.function(button.addEventListener)
|
|
452
|
+
||!is.function(button.removeEventListener)){
|
|
450
453
|
throw codedError(
|
|
451
454
|
'The STT activation button must provide addEventListener() and removeEventListener().',
|
|
452
455
|
STT_ACTIVATION_ERROR_CODES.buttonInvalid,
|
|
453
456
|
TypeError
|
|
454
457
|
);
|
|
455
458
|
}
|
|
456
|
-
if(
|
|
459
|
+
if(!is.function(onChange)){
|
|
457
460
|
throw codedError(
|
|
458
461
|
'The STT activation onChange callback must be a function.',
|
|
459
462
|
STT_ACTIVATION_ERROR_CODES.onChangeInvalid,
|
|
@@ -462,10 +465,10 @@ function createSTTActivationController({
|
|
|
462
465
|
}
|
|
463
466
|
if(eventSource!==null
|
|
464
467
|
&&(
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
468
|
+
!is.object(eventSource)
|
|
469
|
+
||!is.function(eventSource.dispatch)
|
|
470
|
+
||!is.function(eventSource.dispose)
|
|
471
|
+
||!is.string(eventSource.instanceId)
|
|
469
472
|
)){
|
|
470
473
|
throw codedError(
|
|
471
474
|
'The STT activation event source must be a compatible Arcane event source.',
|
|
@@ -489,7 +492,7 @@ function createSTTActivationController({
|
|
|
489
492
|
let destroyed=false;
|
|
490
493
|
|
|
491
494
|
function visibleError(error,fallback){
|
|
492
|
-
const message=
|
|
495
|
+
const message=is.string(error?.message)
|
|
493
496
|
?error.message
|
|
494
497
|
:'';
|
|
495
498
|
return message||fallback;
|
|
@@ -504,9 +507,9 @@ function createSTTActivationController({
|
|
|
504
507
|
}
|
|
505
508
|
|
|
506
509
|
function selected(){
|
|
507
|
-
return
|
|
510
|
+
return is.string(role?.providerId)
|
|
508
511
|
&&role.providerId.length>0
|
|
509
|
-
&&
|
|
512
|
+
&&is.string(role?.modelId)
|
|
510
513
|
&&role.modelId.length>0;
|
|
511
514
|
}
|
|
512
515
|
|
|
@@ -622,7 +625,7 @@ function createSTTActivationController({
|
|
|
622
625
|
action:nextAction,
|
|
623
626
|
reason:STT_ACTIVATION_REASONS.explicitRequest,
|
|
624
627
|
state:role.state,
|
|
625
|
-
roleOperationId:
|
|
628
|
+
roleOperationId:is.string(role?.operationId)
|
|
626
629
|
?role.operationId
|
|
627
630
|
:null
|
|
628
631
|
}
|
|
@@ -692,7 +695,7 @@ function createSTTActivationController({
|
|
|
692
695
|
action:nextAction,
|
|
693
696
|
code,
|
|
694
697
|
reason,
|
|
695
|
-
causeCode:
|
|
698
|
+
causeCode:is.string(error?.code)
|
|
696
699
|
?error.code
|
|
697
700
|
:null
|
|
698
701
|
}
|
|
@@ -851,13 +854,13 @@ function normalizeVoiceOptions(input={},previous={}){
|
|
|
851
854
|
transcriptionLabel:'transcription'
|
|
852
855
|
};
|
|
853
856
|
for(const [source,target] of Object.entries(aliases)){
|
|
854
|
-
if(
|
|
857
|
+
if(is.string(input[source])){
|
|
855
858
|
labels[target]=input[source];
|
|
856
859
|
}
|
|
857
860
|
}
|
|
858
861
|
|
|
859
862
|
const constraints=option(input,previous,'mediaConstraints',{audio:true});
|
|
860
|
-
if(!constraints
|
|
863
|
+
if(!constraints||!is.object(constraints)||is.array(constraints)){
|
|
861
864
|
throw new TypeError('mediaConstraints must be an object');
|
|
862
865
|
}
|
|
863
866
|
if(!own(constraints,'audio')||constraints.audio===false){
|
|
@@ -869,14 +872,14 @@ function normalizeVoiceOptions(input={},previous={}){
|
|
|
869
872
|
'mimeTypes',
|
|
870
873
|
['audio/webm;codecs=opus','audio/mp4','audio/webm']
|
|
871
874
|
);
|
|
872
|
-
if(!
|
|
875
|
+
if(!is.array(mimeTypes)){
|
|
873
876
|
throw new TypeError('mimeTypes must be an array');
|
|
874
877
|
}
|
|
875
|
-
if(mimeTypes.some(value
|
|
878
|
+
if(mimeTypes.some(value=>!is.string(value))){
|
|
876
879
|
throw new TypeError('mimeTypes must contain only strings');
|
|
877
880
|
}
|
|
878
881
|
const separator=option(input,previous,'separator','\n\n');
|
|
879
|
-
if(
|
|
882
|
+
if(!is.string(separator)){
|
|
880
883
|
throw new TypeError('separator must be a string');
|
|
881
884
|
}
|
|
882
885
|
|
|
@@ -936,7 +939,7 @@ const MARKDOWN_LABELS=completeValue({
|
|
|
936
939
|
});
|
|
937
940
|
|
|
938
941
|
function normalizeMarkdownFormats(values=MARKDOWN_FORMATS){
|
|
939
|
-
if(!
|
|
942
|
+
if(!is.array(values)){
|
|
940
943
|
throw new TypeError('Markdown formats must be an array');
|
|
941
944
|
}
|
|
942
945
|
const ids=new Set();
|
|
@@ -984,7 +987,7 @@ function normalizeMarkdownOptions(input={},previous={}){
|
|
|
984
987
|
toolbarLabel:'toolbar'
|
|
985
988
|
};
|
|
986
989
|
for(const [source,target] of Object.entries(aliases)){
|
|
987
|
-
if(
|
|
990
|
+
if(is.string(input[source])){
|
|
988
991
|
labels[target]=input[source];
|
|
989
992
|
}
|
|
990
993
|
}
|
|
@@ -1012,11 +1015,11 @@ function applyMarkdownFormat(value='',selectionStart=0,selectionEnd=selectionSta
|
|
|
1012
1015
|
const rawEnd=Number(selectionEnd);
|
|
1013
1016
|
const start=Math.max(
|
|
1014
1017
|
0,
|
|
1015
|
-
Math.min(value.length,
|
|
1018
|
+
Math.min(value.length,is.finite(rawStart)?rawStart:0)
|
|
1016
1019
|
);
|
|
1017
1020
|
const end=Math.max(
|
|
1018
1021
|
start,
|
|
1019
|
-
Math.min(value.length,
|
|
1022
|
+
Math.min(value.length,is.finite(rawEnd)?rawEnd:start)
|
|
1020
1023
|
);
|
|
1021
1024
|
const selected=value.slice(start,end);
|
|
1022
1025
|
const content=selected||format.placeholder;
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import Is from 'strong-type';
|
|
2
|
+
const is=new Is(false);
|
|
3
|
+
|
|
1
4
|
import {recurringChatMessages} from './ChatRecords.js';
|
|
2
5
|
|
|
3
6
|
const FORBIDDEN_REQUEST_FIELDS=new Set([
|
|
@@ -12,7 +15,7 @@ const FORBIDDEN_REQUEST_FIELDS=new Set([
|
|
|
12
15
|
]);
|
|
13
16
|
|
|
14
17
|
function isPlainRecord(value){
|
|
15
|
-
if(!value
|
|
18
|
+
if(!value||!is.object(value)||is.array(value))return false;
|
|
16
19
|
const prototype=Object.getPrototypeOf(value);
|
|
17
20
|
return prototype===Object.prototype||prototype===null;
|
|
18
21
|
}
|
|
@@ -23,7 +26,7 @@ function coded(error,code){
|
|
|
23
26
|
}
|
|
24
27
|
|
|
25
28
|
function contentText(value,label,{optional=false}={}){
|
|
26
|
-
if(
|
|
29
|
+
if(!is.string(value)) throw new TypeError(`${label} must be a string.`);
|
|
27
30
|
if(!value.trim()){
|
|
28
31
|
if(optional) return null;
|
|
29
32
|
throw new TypeError(`${label} must contain text.`);
|
|
@@ -33,7 +36,7 @@ function contentText(value,label,{optional=false}={}){
|
|
|
33
36
|
|
|
34
37
|
function optionalMetadata(value,label){
|
|
35
38
|
if(value===undefined||value===null||value==='') return null;
|
|
36
|
-
if(
|
|
39
|
+
if(!is.string(value)){
|
|
37
40
|
throw coded(new TypeError(`${label} must be a string when provided.`),'AI_CHAT_INVALID_RESPONSE');
|
|
38
41
|
}
|
|
39
42
|
if(!value.trim()) return null;
|
|
@@ -41,12 +44,12 @@ function optionalMetadata(value,label){
|
|
|
41
44
|
}
|
|
42
45
|
|
|
43
46
|
function usageCount(value){
|
|
44
|
-
return
|
|
47
|
+
return is.safeInteger(value)&&value>=0?value:null;
|
|
45
48
|
}
|
|
46
49
|
|
|
47
50
|
function providerUsageCount(value,label){
|
|
48
51
|
if(value===undefined) return null;
|
|
49
|
-
if(!
|
|
52
|
+
if(!is.safeInteger(value)||value<0){
|
|
50
53
|
throw coded(
|
|
51
54
|
new TypeError(`${label} must be a nonnegative integer when provided.`),
|
|
52
55
|
'AI_CHAT_INVALID_RESPONSE',
|
|
@@ -57,10 +60,10 @@ function providerUsageCount(value,label){
|
|
|
57
60
|
|
|
58
61
|
function signalLike(value){
|
|
59
62
|
return value===undefined||value===null||(
|
|
60
|
-
|
|
61
|
-
&&
|
|
62
|
-
&&
|
|
63
|
-
&&
|
|
63
|
+
is.object(value)
|
|
64
|
+
&&is.boolean(value.aborted)
|
|
65
|
+
&&is.function(value.addEventListener)
|
|
66
|
+
&&is.function(value.removeEventListener)
|
|
64
67
|
);
|
|
65
68
|
}
|
|
66
69
|
|
|
@@ -76,7 +79,7 @@ function assertMessageKeys(value,allowed,label){
|
|
|
76
79
|
}
|
|
77
80
|
|
|
78
81
|
function toolCallArgumentMessage(value,label){
|
|
79
|
-
if(
|
|
82
|
+
if(!is.string(value)){
|
|
80
83
|
throw new TypeError(`${label} must be a JSON string.`);
|
|
81
84
|
}
|
|
82
85
|
let argumentsRecord;
|
|
@@ -94,7 +97,7 @@ function toolCallArgumentMessage(value,label){
|
|
|
94
97
|
'AI_CHAT_INVALID_TOOL_CALL',
|
|
95
98
|
);
|
|
96
99
|
}
|
|
97
|
-
if(
|
|
100
|
+
if(!is.string(argumentsRecord.message)||!argumentsRecord.message.trim()){
|
|
98
101
|
throw coded(
|
|
99
102
|
new TypeError(`${label}.message must contain user-facing text.`),
|
|
100
103
|
'AI_CHAT_TOOL_MESSAGE_REQUIRED',
|
|
@@ -105,7 +108,7 @@ function toolCallArgumentMessage(value,label){
|
|
|
105
108
|
|
|
106
109
|
function requireToolMessageSchemas(value,label){
|
|
107
110
|
if(value===undefined) return;
|
|
108
|
-
if(!
|
|
111
|
+
if(!is.array(value)) throw new TypeError(`${label} must be an array.`);
|
|
109
112
|
for(let index=0;index<value.length;index++){
|
|
110
113
|
const tool=value[index];
|
|
111
114
|
const parameters=tool?.function?.parameters;
|
|
@@ -119,9 +122,9 @@ function requireToolMessageSchemas(value,label){
|
|
|
119
122
|
||!isPlainRecord(parameters.properties)
|
|
120
123
|
||!isPlainRecord(messageSchema)
|
|
121
124
|
||messageSchema.type!=='string'
|
|
122
|
-
||!
|
|
125
|
+
||!is.integer(messageSchema.minLength)
|
|
123
126
|
||messageSchema.minLength<1
|
|
124
|
-
||!
|
|
127
|
+
||!is.array(parameters.required)
|
|
125
128
|
||!parameters.required.includes('message')
|
|
126
129
|
){
|
|
127
130
|
throw coded(
|
|
@@ -141,7 +144,7 @@ function normalizeRequestOptions(value,label){
|
|
|
141
144
|
if(forbidden) throw new TypeError(`${label}.${forbidden} is managed by the chat session.`);
|
|
142
145
|
requireToolMessageSchemas(value.tools,`${label}.tools`);
|
|
143
146
|
for(const key of ['parallelToolCalls','parallel_tool_calls']){
|
|
144
|
-
if(Object.hasOwn(value,key)
|
|
147
|
+
if(Object.hasOwn(value,key)&&!is.boolean(value[key])){
|
|
145
148
|
throw new TypeError(`${label}.${key} must be a boolean when provided.`);
|
|
146
149
|
}
|
|
147
150
|
}
|
|
@@ -151,12 +154,12 @@ function normalizeRequestOptions(value,label){
|
|
|
151
154
|
export function normalizeStructuralToolCall(call,label='Structural tool call'){
|
|
152
155
|
try{
|
|
153
156
|
if(!isPlainRecord(call)) throw new TypeError(`${label} must be a plain object.`);
|
|
154
|
-
if(
|
|
157
|
+
if(!is.string(call.id)||!call.id.trim()){
|
|
155
158
|
throw new TypeError(`${label}.id must contain text.`);
|
|
156
159
|
}
|
|
157
160
|
if(call.type!=='function') throw new TypeError(`${label}.type must be function.`);
|
|
158
161
|
if(!isPlainRecord(call.function)) throw new TypeError(`${label}.function must be a plain object.`);
|
|
159
|
-
if(
|
|
162
|
+
if(!is.string(call.function.name)||!call.function.name.trim()){
|
|
160
163
|
throw new TypeError(`${label}.function.name must contain text.`);
|
|
161
164
|
}
|
|
162
165
|
toolCallArgumentMessage(call.function.arguments,`${label}.function.arguments`);
|
|
@@ -177,7 +180,7 @@ export function normalizeStructuralToolCall(call,label='Structural tool call'){
|
|
|
177
180
|
|
|
178
181
|
function normalizeToolCalls(value,label){
|
|
179
182
|
if(value===undefined) return null;
|
|
180
|
-
if(!
|
|
183
|
+
if(!is.array(value)){
|
|
181
184
|
throw new TypeError(`${label} must be an array of structural tool calls.`);
|
|
182
185
|
}
|
|
183
186
|
const ids=new Set();
|
|
@@ -204,13 +207,13 @@ function normalizeMessage(value,label,allowedRoles){
|
|
|
204
207
|
if(
|
|
205
208
|
value.role==='assistant'
|
|
206
209
|
&&Object.hasOwn(value,'reasoning_content')
|
|
207
|
-
|
|
210
|
+
&&!is.string(value.reasoning_content)
|
|
208
211
|
){
|
|
209
212
|
throw new TypeError(`${label}.reasoning_content must be a string when provided.`);
|
|
210
213
|
}
|
|
211
214
|
const hasReasoning=Boolean(
|
|
212
215
|
value.role==='assistant'
|
|
213
|
-
&&
|
|
216
|
+
&&is.string(value.reasoning_content)
|
|
214
217
|
&&value.reasoning_content.length
|
|
215
218
|
);
|
|
216
219
|
let content;
|
|
@@ -274,7 +277,7 @@ function publicMessage(value){
|
|
|
274
277
|
|
|
275
278
|
function normalizeInitialMessages(value){
|
|
276
279
|
if(value===undefined) return [];
|
|
277
|
-
if(!
|
|
280
|
+
if(!is.array(value)) throw new TypeError('initialMessages must be an array.');
|
|
278
281
|
const messages=value.map((item,index)=>normalizeMessage(
|
|
279
282
|
item,
|
|
280
283
|
`initialMessages[${index}]`,
|
|
@@ -285,14 +288,14 @@ function normalizeInitialMessages(value){
|
|
|
285
288
|
}
|
|
286
289
|
|
|
287
290
|
function normalizeInputMessages(value){
|
|
288
|
-
if(
|
|
291
|
+
if(is.string(value)){
|
|
289
292
|
return [normalizeMessage(
|
|
290
293
|
{role:'user',content:value},
|
|
291
294
|
'The user message',
|
|
292
295
|
new Set(['user']),
|
|
293
296
|
)];
|
|
294
297
|
}
|
|
295
|
-
if(
|
|
298
|
+
if(is.array(value)){
|
|
296
299
|
if(!value.length){
|
|
297
300
|
throw new TypeError('The request tool-result batch must not be empty.');
|
|
298
301
|
}
|
|
@@ -375,7 +378,7 @@ function pendingToolCallIds(messages,validate=false){
|
|
|
375
378
|
|
|
376
379
|
async function configuredArcaneChat(request){
|
|
377
380
|
const api=globalThis.Arcane?.ai;
|
|
378
|
-
if(
|
|
381
|
+
if(!is.function(api?.chat)){
|
|
379
382
|
throw coded(
|
|
380
383
|
new Error('The configured Arcane AI chat capability is unavailable.'),
|
|
381
384
|
'AI_CHAT_UNAVAILABLE',
|
|
@@ -427,7 +430,7 @@ function normalizeSessionResponse(response){
|
|
|
427
430
|
}
|
|
428
431
|
|
|
429
432
|
function normalizeOpenAICompatibleResponse(response){
|
|
430
|
-
if(!
|
|
433
|
+
if(!is.array(response.choices)||response.choices.length===0){
|
|
431
434
|
throw coded(
|
|
432
435
|
new TypeError('The chat provider completion must contain at least one choice.'),
|
|
433
436
|
'AI_CHAT_INVALID_RESPONSE',
|
|
@@ -530,8 +533,8 @@ export default class ConfiguredAIChatSession{
|
|
|
530
533
|
const chat=options.chat===undefined?configuredArcaneChat:options.chat;
|
|
531
534
|
const contextBuilder=options.contextBuilder??null;
|
|
532
535
|
const request=normalizeRequestOptions(options.request,'request');
|
|
533
|
-
if(
|
|
534
|
-
if(contextBuilder!==null
|
|
536
|
+
if(!is.function(chat)) throw new TypeError('chat must be a function.');
|
|
537
|
+
if(contextBuilder!==null&&!is.function(contextBuilder)){
|
|
535
538
|
throw new TypeError('contextBuilder must be a function when provided.');
|
|
536
539
|
}
|
|
537
540
|
const rawSystemPrompt=options.systemPrompt??'';
|