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,4 @@
|
|
|
1
|
+
import Is from "../dependencies/strong-type/index.js";
|
|
1
2
|
import {
|
|
2
3
|
createBrowserSpeechAuthority,
|
|
3
4
|
isBrowserSpeechArtifactGraph,
|
|
@@ -5,6 +6,9 @@ import {
|
|
|
5
6
|
isBrowserSpeechAuthority,
|
|
6
7
|
isDbopfsSpeechArtifactStore,
|
|
7
8
|
} from "./browser-speech-artifacts.mjs";
|
|
9
|
+
import { stripSpeechFormatting } from "../speech-text.mjs";
|
|
10
|
+
|
|
11
|
+
const is = new Is(false);
|
|
8
12
|
|
|
9
13
|
const completeValue = (value) => value;
|
|
10
14
|
import {
|
|
@@ -45,7 +49,7 @@ function providerError(code, message, cause, reason) {
|
|
|
45
49
|
? "AbortError"
|
|
46
50
|
: "ArcaneBrowserSpeechProviderError";
|
|
47
51
|
error.code = code;
|
|
48
|
-
error.reason =
|
|
52
|
+
error.reason = is.string(reason) && reason
|
|
49
53
|
? reason
|
|
50
54
|
: UNMAPPED_PROVIDER_REASON;
|
|
51
55
|
PROVIDER_ERRORS.add(error);
|
|
@@ -53,7 +57,7 @@ function providerError(code, message, cause, reason) {
|
|
|
53
57
|
}
|
|
54
58
|
|
|
55
59
|
function isBrowserSpeechProviderError(value) {
|
|
56
|
-
return
|
|
60
|
+
return is.object(value)
|
|
57
61
|
&& value !== null
|
|
58
62
|
&& PROVIDER_ERRORS.has(value);
|
|
59
63
|
}
|
|
@@ -68,7 +72,7 @@ function trustedLoadFailure(error, role) {
|
|
|
68
72
|
if (isTrustedSpeechError(error)) return error;
|
|
69
73
|
return providerError(
|
|
70
74
|
"ARCANE_AI_PROVIDER_LOAD_FAILED",
|
|
71
|
-
|
|
75
|
+
is.string(error?.message) && error.message
|
|
72
76
|
? error.message
|
|
73
77
|
: `The browser ${role} provider load was rejected.`,
|
|
74
78
|
error,
|
|
@@ -80,7 +84,7 @@ function trustedRequestFailure(error, role) {
|
|
|
80
84
|
if (isTrustedSpeechError(error)) return error;
|
|
81
85
|
return providerError(
|
|
82
86
|
"ARCANE_AI_PROVIDER_REQUEST_FAILED",
|
|
83
|
-
|
|
87
|
+
is.string(error?.message) && error.message
|
|
84
88
|
? error.message
|
|
85
89
|
: `The browser ${role} engine operation was rejected.`,
|
|
86
90
|
error,
|
|
@@ -94,7 +98,7 @@ function trustedWorkerFailure(error, role) {
|
|
|
94
98
|
if (isTrustedSpeechError(error)) return error;
|
|
95
99
|
return providerError(
|
|
96
100
|
"ARCANE_AI_WORKER_MESSAGE_REJECTED",
|
|
97
|
-
|
|
101
|
+
is.string(error?.message) && error.message
|
|
98
102
|
? error.message
|
|
99
103
|
: `The browser ${role} Worker returned an unreadable error envelope.`,
|
|
100
104
|
error,
|
|
@@ -106,7 +110,7 @@ function trustedLifecycleFailure(error, role, operation) {
|
|
|
106
110
|
if (isTrustedSpeechError(error)) return error;
|
|
107
111
|
return providerError(
|
|
108
112
|
"ARCANE_AI_INVALID_REQUEST",
|
|
109
|
-
|
|
113
|
+
is.string(error?.message) && error.message
|
|
110
114
|
? error.message
|
|
111
115
|
: `The browser ${role} ${operation} context could not be read.`,
|
|
112
116
|
error,
|
|
@@ -115,8 +119,8 @@ function trustedLifecycleFailure(error, role, operation) {
|
|
|
115
119
|
}
|
|
116
120
|
|
|
117
121
|
function resolveReason(reason, fallback = UNMAPPED_PROVIDER_REASON) {
|
|
118
|
-
const resolved =
|
|
119
|
-
return
|
|
122
|
+
const resolved = is.function(reason) ? reason() : reason;
|
|
123
|
+
return is.string(resolved) && resolved ? resolved : fallback;
|
|
120
124
|
}
|
|
121
125
|
|
|
122
126
|
function abortError(signal, reason = UNMAPPED_PROVIDER_REASON) {
|
|
@@ -139,7 +143,7 @@ function workerFailureCode(error) {
|
|
|
139
143
|
}
|
|
140
144
|
|
|
141
145
|
function requiredIdentifier(value, label) {
|
|
142
|
-
if (
|
|
146
|
+
if (!is.string(value) || !value.trim()) {
|
|
143
147
|
throw new TypeError(`${label} must be a nonempty trimmed string.`);
|
|
144
148
|
}
|
|
145
149
|
return value.trim();
|
|
@@ -158,7 +162,7 @@ function normalizeSpeechExecution(role, execution) {
|
|
|
158
162
|
maxConcurrentRequests: DEFAULT_TTS_MAX_CONCURRENT_REQUESTS,
|
|
159
163
|
});
|
|
160
164
|
}
|
|
161
|
-
if (!execution ||
|
|
165
|
+
if (!execution || !is.object(execution) || is.array(execution)) {
|
|
162
166
|
throw new TypeError("Browser Kokoro execution must be a plain data record.");
|
|
163
167
|
}
|
|
164
168
|
const prototype = Object.getPrototypeOf(execution);
|
|
@@ -184,7 +188,7 @@ function normalizeSpeechExecution(role, execution) {
|
|
|
184
188
|
throw new TypeError('Browser Kokoro execution.device must be "auto", "webgpu", or "wasm".');
|
|
185
189
|
}
|
|
186
190
|
if (
|
|
187
|
-
!
|
|
191
|
+
!is.safeInteger(maxConcurrentRequests)
|
|
188
192
|
|| maxConcurrentRequests < 1
|
|
189
193
|
|| maxConcurrentRequests > MAX_TTS_CONCURRENT_REQUESTS
|
|
190
194
|
) {
|
|
@@ -319,15 +323,15 @@ function linkedAbortFailure(linked, fallbackReason) {
|
|
|
319
323
|
function isAbortSignal(value) {
|
|
320
324
|
return value === null
|
|
321
325
|
|| value === undefined
|
|
322
|
-
|| (
|
|
323
|
-
&&
|
|
324
|
-
&&
|
|
325
|
-
&&
|
|
326
|
+
|| (is.object(value)
|
|
327
|
+
&& is.boolean(value.aborted)
|
|
328
|
+
&& is.function(value.addEventListener)
|
|
329
|
+
&& is.function(value.removeEventListener));
|
|
326
330
|
}
|
|
327
331
|
|
|
328
332
|
function providerContext(context, role, operation) {
|
|
329
333
|
if (context === undefined) return completeValue({ signal: null });
|
|
330
|
-
if (!context ||
|
|
334
|
+
if (!context || !is.object(context) || is.array(context)) {
|
|
331
335
|
throw providerError(
|
|
332
336
|
"ARCANE_AI_INVALID_REQUEST",
|
|
333
337
|
`Browser ${role} ${operation} context must be an object.`,
|
|
@@ -470,7 +474,7 @@ function genericPayloadDescriptors(
|
|
|
470
474
|
}
|
|
471
475
|
const descriptors = Object.getOwnPropertyDescriptors(payload);
|
|
472
476
|
for (const key of Reflect.ownKeys(descriptors)) {
|
|
473
|
-
if (
|
|
477
|
+
if (is.symbol(key) || !allowedKeys.includes(key)) {
|
|
474
478
|
throw providerError(
|
|
475
479
|
"ARCANE_AI_INVALID_REQUEST",
|
|
476
480
|
`${label} contains an unknown field.`,
|
|
@@ -501,7 +505,7 @@ function genericPayloadDescriptors(
|
|
|
501
505
|
}
|
|
502
506
|
|
|
503
507
|
function audioMimeEssence(value, label, reasonPrefix) {
|
|
504
|
-
if (
|
|
508
|
+
if (!is.string(value)) {
|
|
505
509
|
throw providerError(
|
|
506
510
|
"ARCANE_AI_INVALID_REQUEST",
|
|
507
511
|
`${label} must be an audio MIME type.`,
|
|
@@ -585,7 +589,7 @@ function observeLoadOperation(record, { signal, progress }, role) {
|
|
|
585
589
|
};
|
|
586
590
|
const observer = completeValue({
|
|
587
591
|
progress(value) {
|
|
588
|
-
if (settled ||
|
|
592
|
+
if (settled || !is.function(progress)) return;
|
|
589
593
|
try {
|
|
590
594
|
progress(value);
|
|
591
595
|
} catch (error) {
|
|
@@ -641,7 +645,7 @@ async function decodeSharedTranscriptionPayload(
|
|
|
641
645
|
operationSubject: "stt-transcription",
|
|
642
646
|
});
|
|
643
647
|
const audio = descriptors.audio.value;
|
|
644
|
-
if (
|
|
648
|
+
if (!is.function(globalThis.Blob)) {
|
|
645
649
|
throw providerError(
|
|
646
650
|
"ARCANE_AI_INVALID_REQUEST",
|
|
647
651
|
"Shared speech transcription requires the browser Blob constructor.",
|
|
@@ -649,7 +653,7 @@ async function decodeSharedTranscriptionPayload(
|
|
|
649
653
|
"stt-transcription-blob-constructor-unavailable",
|
|
650
654
|
);
|
|
651
655
|
}
|
|
652
|
-
if (!(audio
|
|
656
|
+
if (!is.instanceCheck(audio, Blob)) {
|
|
653
657
|
throw providerError(
|
|
654
658
|
"ARCANE_AI_INVALID_REQUEST",
|
|
655
659
|
"Shared speech transcription audio must be a Blob or File.",
|
|
@@ -684,7 +688,7 @@ async function decodeSharedTranscriptionPayload(
|
|
|
684
688
|
}
|
|
685
689
|
const OfflineAudioContext = globalThis.OfflineAudioContext
|
|
686
690
|
?? globalThis.webkitOfflineAudioContext;
|
|
687
|
-
if (
|
|
691
|
+
if (!is.function(OfflineAudioContext)) {
|
|
688
692
|
throw providerError(
|
|
689
693
|
"ARCANE_AI_AUDIO_DECODE_UNAVAILABLE",
|
|
690
694
|
`Shared Blob transcription requires OfflineAudioContext decoding at ${sampleRate} Hz.`,
|
|
@@ -703,7 +707,7 @@ async function decodeSharedTranscriptionPayload(
|
|
|
703
707
|
"stt-browser-offline-audio-context-construction-rejected",
|
|
704
708
|
);
|
|
705
709
|
}
|
|
706
|
-
if (
|
|
710
|
+
if (!is.function(decoder.decodeAudioData)) {
|
|
707
711
|
throw providerError(
|
|
708
712
|
"ARCANE_AI_AUDIO_DECODE_UNAVAILABLE",
|
|
709
713
|
"Shared Blob transcription requires browser audio decoding.",
|
|
@@ -734,7 +738,7 @@ async function decodeSharedTranscriptionPayload(
|
|
|
734
738
|
"stt-browser-audio-decode-operation-rejected",
|
|
735
739
|
);
|
|
736
740
|
}
|
|
737
|
-
if (!decoded ||
|
|
741
|
+
if (!decoded || !is.object(decoded)) {
|
|
738
742
|
throw providerError(
|
|
739
743
|
"ARCANE_AI_AUDIO_DECODE_FAILED",
|
|
740
744
|
"Decoded speech audio must be an AudioBuffer-like object.",
|
|
@@ -750,7 +754,7 @@ async function decodeSharedTranscriptionPayload(
|
|
|
750
754
|
"stt-browser-decoded-audio-sample-rate-mismatch",
|
|
751
755
|
);
|
|
752
756
|
}
|
|
753
|
-
if (!
|
|
757
|
+
if (!is.safeInteger(decoded.length)) {
|
|
754
758
|
throw providerError(
|
|
755
759
|
"ARCANE_AI_AUDIO_DECODE_FAILED",
|
|
756
760
|
"Decoded speech audio frame length must be a safe integer.",
|
|
@@ -766,7 +770,7 @@ async function decodeSharedTranscriptionPayload(
|
|
|
766
770
|
"stt-browser-decoded-audio-empty",
|
|
767
771
|
);
|
|
768
772
|
}
|
|
769
|
-
if (!
|
|
773
|
+
if (!is.safeInteger(decoded.numberOfChannels)) {
|
|
770
774
|
throw providerError(
|
|
771
775
|
"ARCANE_AI_AUDIO_DECODE_FAILED",
|
|
772
776
|
"Decoded speech audio channel count must be a safe integer.",
|
|
@@ -782,7 +786,7 @@ async function decodeSharedTranscriptionPayload(
|
|
|
782
786
|
"stt-browser-decoded-audio-channel-count-zero",
|
|
783
787
|
);
|
|
784
788
|
}
|
|
785
|
-
if (
|
|
789
|
+
if (!is.function(decoded.getChannelData)) {
|
|
786
790
|
throw providerError(
|
|
787
791
|
"ARCANE_AI_AUDIO_DECODE_FAILED",
|
|
788
792
|
"Decoded speech audio must expose getChannelData().",
|
|
@@ -803,7 +807,7 @@ async function decodeSharedTranscriptionPayload(
|
|
|
803
807
|
"stt-browser-decoded-audio-channel-read-rejected",
|
|
804
808
|
);
|
|
805
809
|
}
|
|
806
|
-
if (!(channel
|
|
810
|
+
if (!is.instanceCheck(channel, Float32Array)) {
|
|
807
811
|
throw providerError(
|
|
808
812
|
"ARCANE_AI_AUDIO_DECODE_FAILED",
|
|
809
813
|
"Decoded speech audio channels must be Float32Array values.",
|
|
@@ -826,7 +830,7 @@ async function decodeSharedTranscriptionPayload(
|
|
|
826
830
|
let sample = 0;
|
|
827
831
|
for (const channel of channels) sample += channel[index];
|
|
828
832
|
sample /= channels.length;
|
|
829
|
-
if (!
|
|
833
|
+
if (!is.finite(sample)) {
|
|
830
834
|
throw providerError(
|
|
831
835
|
"ARCANE_AI_AUDIO_DECODE_FAILED",
|
|
832
836
|
"Decoded speech audio contains a non-finite sample.",
|
|
@@ -853,7 +857,7 @@ function cloneNativeTranscriptionPayload(payload, authority) {
|
|
|
853
857
|
);
|
|
854
858
|
assertPayloadModel(payload, authority, { operationSubject: "stt-transcription" });
|
|
855
859
|
const sampleRate = inputSampleRate(authority);
|
|
856
|
-
if (!(descriptors.audio.value
|
|
860
|
+
if (!is.instanceCheck(descriptors.audio.value, Float32Array)) {
|
|
857
861
|
throw providerError(
|
|
858
862
|
"ARCANE_AI_INVALID_REQUEST",
|
|
859
863
|
"Whisper requires Float32Array audio.",
|
|
@@ -878,7 +882,7 @@ function cloneNativeTranscriptionPayload(payload, authority) {
|
|
|
878
882
|
);
|
|
879
883
|
}
|
|
880
884
|
for (const sample of descriptors.audio.value) {
|
|
881
|
-
if (!
|
|
885
|
+
if (!is.finite(sample)) {
|
|
882
886
|
throw providerError(
|
|
883
887
|
"ARCANE_AI_INVALID_REQUEST",
|
|
884
888
|
"Whisper audio must contain only finite Float32 PCM samples.",
|
|
@@ -896,7 +900,7 @@ function cloneNativeTranscriptionPayload(payload, authority) {
|
|
|
896
900
|
});
|
|
897
901
|
}
|
|
898
902
|
|
|
899
|
-
function normalizeSynthesisPayload(payload, authority) {
|
|
903
|
+
function normalizeSynthesisPayload(payload, authority, speechInputPrepared) {
|
|
900
904
|
const shared = Object.hasOwn(payload, "input");
|
|
901
905
|
let descriptors;
|
|
902
906
|
let textValue;
|
|
@@ -932,11 +936,12 @@ function normalizeSynthesisPayload(payload, authority) {
|
|
|
932
936
|
assertPayloadModel(payload, authority, { operationSubject: "tts-synthesis" });
|
|
933
937
|
textValue = descriptors.text.value;
|
|
934
938
|
}
|
|
935
|
-
const
|
|
939
|
+
const suppliedText = is.string(textValue) ? textValue : "";
|
|
940
|
+
const text = speechInputPrepared === true ? suppliedText : stripSpeechFormatting(suppliedText);
|
|
936
941
|
const voiceValue = Object.hasOwn(descriptors, "voice")
|
|
937
942
|
? descriptors.voice.value
|
|
938
943
|
: authority.defaultVoice;
|
|
939
|
-
const voice =
|
|
944
|
+
const voice = is.string(voiceValue) ? voiceValue : "";
|
|
940
945
|
const speed = Object.hasOwn(descriptors, "speed") ? descriptors.speed.value : 1;
|
|
941
946
|
if (!text.trim()) {
|
|
942
947
|
throw providerError(
|
|
@@ -954,7 +959,7 @@ function normalizeSynthesisPayload(payload, authority) {
|
|
|
954
959
|
"tts-synthesis-voice-empty",
|
|
955
960
|
);
|
|
956
961
|
}
|
|
957
|
-
if (!
|
|
962
|
+
if (!is.finite(speed) || speed <= 0) {
|
|
958
963
|
throw providerError(
|
|
959
964
|
"ARCANE_AI_INVALID_REQUEST",
|
|
960
965
|
"Kokoro speed must be greater than 0.",
|
|
@@ -983,8 +988,9 @@ async function normalizeRequestPayload(
|
|
|
983
988
|
authority,
|
|
984
989
|
signal,
|
|
985
990
|
cancellationReason,
|
|
991
|
+
speechInputPrepared,
|
|
986
992
|
) {
|
|
987
|
-
if (!payload ||
|
|
993
|
+
if (!payload || !is.object(payload) || is.array(payload)) {
|
|
988
994
|
throw providerError(
|
|
989
995
|
"ARCANE_AI_INVALID_REQUEST",
|
|
990
996
|
"Speech request payload must be an object.",
|
|
@@ -998,8 +1004,8 @@ async function normalizeRequestPayload(
|
|
|
998
1004
|
const audio = Object.getOwnPropertyDescriptor(payload, "audio");
|
|
999
1005
|
if (Object.hasOwn(payload, "mimeType")
|
|
1000
1006
|
|| (Object.hasOwn(audio ?? {}, "value")
|
|
1001
|
-
&&
|
|
1002
|
-
&& audio.value
|
|
1007
|
+
&& is.function(globalThis.Blob)
|
|
1008
|
+
&& is.instanceCheck(audio.value, Blob))) {
|
|
1003
1009
|
return decodeSharedTranscriptionPayload(
|
|
1004
1010
|
payload,
|
|
1005
1011
|
authority,
|
|
@@ -1009,12 +1015,12 @@ async function normalizeRequestPayload(
|
|
|
1009
1015
|
}
|
|
1010
1016
|
return cloneNativeTranscriptionPayload(payload, authority);
|
|
1011
1017
|
}
|
|
1012
|
-
return normalizeSynthesisPayload(payload, authority);
|
|
1018
|
+
return normalizeSynthesisPayload(payload, authority, speechInputPrepared);
|
|
1013
1019
|
}
|
|
1014
1020
|
|
|
1015
1021
|
function encodeSharedSynthesisResult(result, authority) {
|
|
1016
1022
|
const sampleRate = outputSampleRate(authority);
|
|
1017
|
-
if (!result ||
|
|
1023
|
+
if (!result || !is.object(result)) {
|
|
1018
1024
|
throw providerError(
|
|
1019
1025
|
"ARCANE_AI_INVALID_PROVIDER_RESULT",
|
|
1020
1026
|
"Browser Kokoro must return a synthesis result object.",
|
|
@@ -1022,7 +1028,7 @@ function encodeSharedSynthesisResult(result, authority) {
|
|
|
1022
1028
|
"tts-synthesis-result-not-object",
|
|
1023
1029
|
);
|
|
1024
1030
|
}
|
|
1025
|
-
if (!(result.audio
|
|
1031
|
+
if (!is.instanceCheck(result.audio, Float32Array)) {
|
|
1026
1032
|
throw providerError(
|
|
1027
1033
|
"ARCANE_AI_INVALID_PROVIDER_RESULT",
|
|
1028
1034
|
"Browser Kokoro must return Float32 PCM audio.",
|
|
@@ -1077,7 +1083,7 @@ function encodeSharedSynthesisResult(result, authority) {
|
|
|
1077
1083
|
view.setUint32(40, result.audio.length * 2, true);
|
|
1078
1084
|
for (let index = 0; index < result.audio.length; index += 1) {
|
|
1079
1085
|
const sample = result.audio[index];
|
|
1080
|
-
if (!
|
|
1086
|
+
if (!is.finite(sample)) {
|
|
1081
1087
|
throw providerError(
|
|
1082
1088
|
"ARCANE_AI_INVALID_PROVIDER_RESULT",
|
|
1083
1089
|
"Browser Kokoro returned a non-finite PCM sample.",
|
|
@@ -1112,7 +1118,7 @@ function createBrowserSpeechProvider({
|
|
|
1112
1118
|
if (localOnly !== true) {
|
|
1113
1119
|
throw new TypeError("Browser speech providers are localOnly.");
|
|
1114
1120
|
}
|
|
1115
|
-
if (
|
|
1121
|
+
if (!is.boolean(offline)) {
|
|
1116
1122
|
throw new TypeError("Browser speech offline must be a boolean.");
|
|
1117
1123
|
}
|
|
1118
1124
|
if (!isDbopfsSpeechArtifactStore(store)) {
|
|
@@ -1443,7 +1449,7 @@ function createBrowserSpeechProvider({
|
|
|
1443
1449
|
} catch (error) {
|
|
1444
1450
|
return Promise.reject(trustedLoadFailure(error, role));
|
|
1445
1451
|
}
|
|
1446
|
-
if (loadProgress !== undefined &&
|
|
1452
|
+
if (loadProgress !== undefined && !is.function(loadProgress)) {
|
|
1447
1453
|
return Promise.reject(providerError(
|
|
1448
1454
|
"ARCANE_AI_INVALID_REQUEST",
|
|
1449
1455
|
"Browser speech load progress must be a function when supplied.",
|
|
@@ -1511,7 +1517,7 @@ function createBrowserSpeechProvider({
|
|
|
1511
1517
|
prepared,
|
|
1512
1518
|
released: false,
|
|
1513
1519
|
};
|
|
1514
|
-
record.warnings =
|
|
1520
|
+
record.warnings = is.array(prepared.warnings)
|
|
1515
1521
|
? completeValue([...prepared.warnings])
|
|
1516
1522
|
: NO_PROVIDER_WARNINGS;
|
|
1517
1523
|
lastWarnings = record.warnings;
|
|
@@ -1619,7 +1625,7 @@ function createBrowserSpeechProvider({
|
|
|
1619
1625
|
}, role);
|
|
1620
1626
|
},
|
|
1621
1627
|
|
|
1622
|
-
async request(context = {}) {
|
|
1628
|
+
async request(context = {}, { speechInputPrepared = false } = {}) {
|
|
1623
1629
|
let requestContext;
|
|
1624
1630
|
try {
|
|
1625
1631
|
requestContext = providerContext(context, role, "request");
|
|
@@ -1713,6 +1719,7 @@ function createBrowserSpeechProvider({
|
|
|
1713
1719
|
authority,
|
|
1714
1720
|
linked.controller.signal,
|
|
1715
1721
|
browserSpeechRequestAbortReason,
|
|
1722
|
+
speechInputPrepared,
|
|
1716
1723
|
);
|
|
1717
1724
|
throwIfAborted(
|
|
1718
1725
|
linked.controller.signal,
|