arcane-os 0.3.0 → 0.3.2
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 +27 -0
- package/README.md +86 -117
- package/bin/arcane-test.mjs +170 -46
- package/browser-runtime/ai/browser-speech-artifacts.mjs +887 -909
- package/browser-runtime/ai/browser-speech-providers.mjs +96 -152
- package/browser-runtime/ai/browser-wasm-llm-provider.mjs +627 -819
- package/browser-runtime/ai/browser-wasm.mjs +24 -35
- package/browser-runtime/ai/browser-wllama-runtime.mjs +64 -316
- package/browser-runtime/ai/model-controller.mjs +584 -181
- package/browser-runtime/ai/speech-worker-client.mjs +8 -146
- package/browser-runtime/ai/speech-worker-runtime.mjs +643 -363
- package/browser-runtime/dom-event-instrumentation.mjs +55 -147
- package/browser-runtime/event-manager.mjs +239 -624
- package/package.json +5 -6
- package/runtime/arcane/components/app-bar.html +3 -15
- package/runtime/arcane/components/assistant-panel.html +10 -10
- package/runtime/arcane/components/calculator.html +1 -1
- package/runtime/arcane/components/chat.html +1359 -135
- package/runtime/arcane/components/conversation-view.html +2 -2
- package/runtime/arcane/components/document-inspector.html +11 -17
- package/runtime/arcane/components/file-manager.html +13 -56
- package/runtime/arcane/components/markdown-document.html +82 -281
- package/runtime/arcane/components/markdown-editor.html +7 -10
- package/runtime/arcane/components/media-embed.html +6 -6
- package/runtime/arcane/components/screen-capture.html +4 -4
- package/runtime/arcane/components/source-explanation.html +2 -2
- package/runtime/arcane/components/speech.html +112 -68
- package/runtime/arcane/components/terminal-workspace.html +4 -4
- package/runtime/arcane/components/theme-editor.html +1 -1
- package/runtime/arcane/components/unified-inbox.html +2 -2
- package/runtime/arcane/components/voice-transcription.html +31 -21
- package/runtime/arcane/entities/Calculation.js +2 -3
- package/runtime/arcane/entities/Chat.js +228 -43
- package/runtime/arcane/entities/Preference.js +3 -5
- package/runtime/arcane/entities/Weather.js +5 -5
- package/runtime/arcane/modules/AI.js +1050 -427
- package/runtime/arcane/modules/AIProviderRuntime.js +658 -363
- package/runtime/arcane/modules/AIResponseLength.js +9 -19
- package/runtime/arcane/modules/AIRuntimeState.js +109 -72
- package/runtime/arcane/modules/ArcaneNavigationPolicy.js +45 -32
- package/runtime/arcane/modules/BrowserTestSuite.js +78 -122
- package/runtime/arcane/modules/CalculatorEngine.js +9 -9
- package/runtime/arcane/modules/CommunicationAppController.js +3 -7
- package/runtime/arcane/modules/ComponentContracts.js +30 -32
- package/runtime/arcane/modules/ConfiguredAIChatSession.js +281 -230
- package/runtime/arcane/modules/ConversationActionItems.js +26 -59
- package/runtime/arcane/modules/ConversationClosingReport.js +34 -61
- package/runtime/arcane/modules/ConversationTimebox.js +27 -15
- package/runtime/arcane/modules/DBOPFSDocumentLibrary.js +152 -344
- package/runtime/arcane/modules/DocumentLexicalSearch.js +25 -91
- package/runtime/arcane/modules/HTMLImport.js +54 -1
- package/runtime/arcane/modules/IsolatedModelQuestionRunner.js +40 -203
- package/runtime/arcane/modules/LocalAIReadiness.js +40 -60
- package/runtime/arcane/modules/LocalAIReadinessController.js +15 -13
- package/runtime/arcane/modules/MD.js +1 -45
- package/runtime/arcane/modules/Mail.js +51 -103
- package/runtime/arcane/modules/MailOutbox.mjs +95 -193
- package/runtime/arcane/modules/MailTransport.mjs +36 -57
- package/runtime/arcane/modules/ModelDefinition.js +22 -106
- package/runtime/arcane/modules/OpenMeteoWeatherProvider.js +39 -101
- package/runtime/arcane/modules/PersistentAIChatSession.js +281 -18
- package/runtime/arcane/modules/PreferenceStore.js +102 -30
- package/runtime/arcane/modules/RiskSignalAnalyzer.js +8 -9
- package/runtime/arcane/modules/ScopedOPFSCache.js +7 -42
- package/runtime/arcane/modules/ScreenCapture.js +175 -128
- package/runtime/arcane/modules/SpeechPlayback.js +46 -149
- package/runtime/arcane/modules/StaticDocumentCatalog.js +173 -407
- package/runtime/arcane/modules/ToolCallRouter.js +25 -12
- package/runtime/arcane/modules/YouTubeMedia.js +6 -5
- package/schemas/arcane-app-bundle.schema.json +13 -78
- package/schemas/arcane-app.schema.json +9 -25
- package/schemas/arcane-lock.schema.json +18 -151
- package/schemas/arcane-package.schema.json +2 -16
- package/schemas/native-build-plan.schema.json +119 -122
- package/src/app-descriptor.mjs +75 -132
- package/src/application-tests.mjs +200 -0
- package/src/cli/main.mjs +27 -46
- package/src/constants.mjs +3 -4
- package/src/dev-server.mjs +30 -324
- package/src/doctor.mjs +92 -154
- package/src/dom-event-instrumentation.mjs +55 -147
- package/src/errors.mjs +2 -3
- package/src/event-manager.mjs +239 -624
- package/src/event-queue.mjs +3 -3
- package/src/import-map.mjs +273 -1028
- package/src/index.mjs +14 -16
- package/src/installed-sdk-runtime.mjs +40 -62
- package/src/integrated-provider-loader.mjs +53 -382
- package/src/mail-api.mjs +0 -2
- package/src/mail-server.mjs +224 -580
- package/src/mail.mjs +4 -10
- package/src/native-plan.mjs +163 -598
- package/src/native-provider-loader.mjs +104 -1063
- package/src/packager/core.mjs +485 -3229
- package/src/process.mjs +5 -10
- package/src/release-bundle.mjs +292 -2405
- package/src/runtime.mjs +76 -396
- package/src/scaffold.mjs +30 -80
- package/src/sdk-browser-runtime.mjs +70 -626
- package/src/source-server.mjs +588 -0
- package/src/targets/index.mjs +78 -188
- package/src/templates/workspace-template.mjs +19 -135
- package/src/testing-loader.mjs +164 -0
- package/src/testing.mjs +1 -1
- package/src/toolchain.mjs +131 -544
- package/src/update-check.mjs +26 -64
- package/src/workspace-operation-lock.mjs +139 -430
- package/src/workspace-runtime.mjs +112 -779
- package/src/workspace.mjs +40 -302
- package/browser-runtime/ARCANE_SDK_BROWSER_RELEASE.json +0 -218
- package/browser-runtime/ai/ARCANE_AI_BROWSER_SPEECH_COMPONENTS.json +0 -203
- package/browser-runtime/ai/ARCANE_AI_BROWSER_WASM_COMPONENTS.json +0 -80
- package/browser-runtime/ai/internal/sha256.mjs +0 -166
- package/docs/architecture.md +0 -344
- package/docs/compatibility.md +0 -36
- package/docs/event-manager.md +0 -294
- package/docs/platform-targets.md +0 -108
- package/docs/publishing.md +0 -201
- package/docs/reference/README.md +0 -187
- package/docs/reference/ai/browser-speech-package-authority.json +0 -835
- package/docs/reference/ai/browser-speech.md +0 -1252
- package/docs/reference/ai/browser-wasm.md +0 -530
- package/docs/reference/arcane-ollama.md +0 -288
- package/docs/reference/availability-and-normalization.md +0 -183
- package/docs/reference/behavioral-testing.md +0 -133
- package/docs/reference/cli.md +0 -779
- package/docs/reference/core/README.md +0 -62
- package/docs/reference/core/arcane-ai-contracts.md +0 -907
- package/docs/reference/core/arcane-api.md +0 -601
- package/docs/reference/core/arcane-entities.md +0 -65
- package/docs/reference/core/arcane-events.md +0 -134
- package/docs/reference/core/ollama-module.md +0 -181
- package/docs/reference/core/reference/arcane-api/ai-and-ollama.md +0 -1909
- package/docs/reference/core/reference/arcane-api/applications-terminal-capabilities.md +0 -1057
- package/docs/reference/core/reference/arcane-api/core-and-events.md +0 -320
- package/docs/reference/core/reference/arcane-api/filesystem-storage-preferences-appearance.md +0 -610
- package/docs/reference/core/reference/arcane-api/namespaces.md +0 -1157
- package/docs/reference/core/reference/arcane-api/platform-installation-users-system.md +0 -1423
- package/docs/reference/core/reference/arcane-api/session-provisioning-diagnostics-development.md +0 -315
- package/docs/reference/event-manager.md +0 -1511
- package/docs/reference/inventory/package-api.json +0 -3284
- package/docs/reference/inventory/runtime-components.json +0 -1011
- package/docs/reference/inventory/runtime-entities.json +0 -26
- package/docs/reference/inventory/runtime-modules.json +0 -1431
- package/docs/reference/mail.md +0 -316
- package/docs/reference/protocols.md +0 -677
- package/docs/reference/runtime-components.md +0 -1366
- package/docs/reference/runtime-entities.md +0 -303
- package/docs/reference/runtime-modules.md +0 -2960
- package/docs/reference/sdk-api.md +0 -6694
- package/docs/roadmap.md +0 -79
- package/docs/work-amplification.md +0 -129
- package/runtime/ARCANE_RUNTIME_RELEASE.json +0 -826
|
@@ -5,11 +5,8 @@ import {
|
|
|
5
5
|
isBrowserSpeechAuthority,
|
|
6
6
|
isDbopfsSpeechArtifactStore,
|
|
7
7
|
} from "./browser-speech-artifacts.mjs";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
resolveModelSecurity,
|
|
11
|
-
sameModelSecurity,
|
|
12
|
-
} from "./model-controller.mjs";
|
|
8
|
+
|
|
9
|
+
const completeValue = (value) => value;
|
|
13
10
|
import {
|
|
14
11
|
createSpeechWorkerClient,
|
|
15
12
|
isSpeechWorkerClient,
|
|
@@ -18,15 +15,16 @@ import {
|
|
|
18
15
|
|
|
19
16
|
const AI_PROVIDER_PROTOCOL = "arcane-ai-provider/2";
|
|
20
17
|
const AI_MODEL_AUTHORITY_PROTOCOL = "arcane-ai-model-authority/1";
|
|
21
|
-
const ROLE_OPERATION =
|
|
18
|
+
const ROLE_OPERATION = completeValue({ stt: "transcribe", tts: "synthesize" });
|
|
22
19
|
const STT_SAMPLE_RATE = 16_000;
|
|
23
20
|
const TTS_SAMPLE_RATE = 24_000;
|
|
24
21
|
const TTS_RESPONSE_FORMAT = "wav";
|
|
25
|
-
const ROLE_REQUEST_REASON =
|
|
22
|
+
const ROLE_REQUEST_REASON = completeValue({
|
|
26
23
|
stt: "stt-transcription-cancelled",
|
|
27
24
|
tts: "tts-synthesis-cancelled",
|
|
28
25
|
});
|
|
29
26
|
const UNMAPPED_PROVIDER_REASON = "browser-speech-provider-error-reason-unmapped";
|
|
27
|
+
const NO_PROVIDER_WARNINGS = completeValue([]);
|
|
30
28
|
const WORKER_FAILURE_CODES = new Set([
|
|
31
29
|
"ARCANE_AI_WORKER_CRASHED",
|
|
32
30
|
"ARCANE_AI_WORKER_MESSAGE_ERROR",
|
|
@@ -66,7 +64,9 @@ function trustedLoadFailure(error, role) {
|
|
|
66
64
|
if (isTrustedSpeechError(error)) return error;
|
|
67
65
|
return providerError(
|
|
68
66
|
"ARCANE_AI_PROVIDER_LOAD_FAILED",
|
|
69
|
-
|
|
67
|
+
typeof error?.message === "string" && error.message
|
|
68
|
+
? error.message
|
|
69
|
+
: `The browser ${role} provider load was rejected.`,
|
|
70
70
|
error,
|
|
71
71
|
`${role}-provider-load-rejected`,
|
|
72
72
|
);
|
|
@@ -76,7 +76,9 @@ function trustedRequestFailure(error, role) {
|
|
|
76
76
|
if (isTrustedSpeechError(error)) return error;
|
|
77
77
|
return providerError(
|
|
78
78
|
"ARCANE_AI_PROVIDER_REQUEST_FAILED",
|
|
79
|
-
|
|
79
|
+
typeof error?.message === "string" && error.message
|
|
80
|
+
? error.message
|
|
81
|
+
: `The browser ${role} engine operation was rejected.`,
|
|
80
82
|
error,
|
|
81
83
|
role === "stt"
|
|
82
84
|
? "stt-transcription-engine-operation-rejected"
|
|
@@ -88,7 +90,9 @@ function trustedWorkerFailure(error, role) {
|
|
|
88
90
|
if (isTrustedSpeechError(error)) return error;
|
|
89
91
|
return providerError(
|
|
90
92
|
"ARCANE_AI_WORKER_MESSAGE_REJECTED",
|
|
91
|
-
|
|
93
|
+
typeof error?.message === "string" && error.message
|
|
94
|
+
? error.message
|
|
95
|
+
: `The browser ${role} Worker returned an unreadable error envelope.`,
|
|
92
96
|
error,
|
|
93
97
|
`${role}-worker-error-envelope-rejected`,
|
|
94
98
|
);
|
|
@@ -98,7 +102,9 @@ function trustedLifecycleFailure(error, role, operation) {
|
|
|
98
102
|
if (isTrustedSpeechError(error)) return error;
|
|
99
103
|
return providerError(
|
|
100
104
|
"ARCANE_AI_INVALID_REQUEST",
|
|
101
|
-
|
|
105
|
+
typeof error?.message === "string" && error.message
|
|
106
|
+
? error.message
|
|
107
|
+
: `The browser ${role} ${operation} context could not be read.`,
|
|
102
108
|
error,
|
|
103
109
|
`${role}-provider-${operation}-context-read-rejected`,
|
|
104
110
|
);
|
|
@@ -129,8 +135,8 @@ function workerFailureCode(error) {
|
|
|
129
135
|
}
|
|
130
136
|
|
|
131
137
|
function requiredIdentifier(value, label) {
|
|
132
|
-
if (typeof value !== "string" || !value.trim()
|
|
133
|
-
throw new TypeError(`${label} must be a trimmed
|
|
138
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
139
|
+
throw new TypeError(`${label} must be a nonempty trimmed string.`);
|
|
134
140
|
}
|
|
135
141
|
return value.trim();
|
|
136
142
|
}
|
|
@@ -141,7 +147,6 @@ function createProviderAuthority({
|
|
|
141
147
|
graph,
|
|
142
148
|
model,
|
|
143
149
|
runtime,
|
|
144
|
-
security,
|
|
145
150
|
}) {
|
|
146
151
|
if (graph !== undefined) {
|
|
147
152
|
if (model !== undefined || runtime !== undefined) {
|
|
@@ -158,9 +163,8 @@ function createProviderAuthority({
|
|
|
158
163
|
&& graph.providerId !== providerId) {
|
|
159
164
|
throw new TypeError("Browser speech graph providerId must match the provider id.");
|
|
160
165
|
}
|
|
161
|
-
return
|
|
166
|
+
return completeValue({
|
|
162
167
|
protocol: AI_MODEL_AUTHORITY_PROTOCOL,
|
|
163
|
-
admitted: true,
|
|
164
168
|
graph,
|
|
165
169
|
artifactGraphProtocol: graph.protocol,
|
|
166
170
|
providerId,
|
|
@@ -170,13 +174,11 @@ function createProviderAuthority({
|
|
|
170
174
|
revision: graph.model.revision,
|
|
171
175
|
dtype: graph.model.dtype,
|
|
172
176
|
defaultVoice: graph.model.defaultVoice,
|
|
173
|
-
voices: graph.model.voices ??
|
|
177
|
+
voices: graph.model.voices ?? completeValue([]),
|
|
174
178
|
inputSampleRate: graph.model.inputSampleRate,
|
|
175
179
|
outputSampleRate: graph.model.outputSampleRate,
|
|
176
180
|
runtime: graph.runtime,
|
|
177
181
|
files: graph.files,
|
|
178
|
-
security,
|
|
179
|
-
artifactGraphId: graph.identitySha256,
|
|
180
182
|
});
|
|
181
183
|
}
|
|
182
184
|
const authority = createBrowserSpeechAuthority({
|
|
@@ -184,7 +186,6 @@ function createProviderAuthority({
|
|
|
184
186
|
role,
|
|
185
187
|
model,
|
|
186
188
|
runtime,
|
|
187
|
-
security,
|
|
188
189
|
});
|
|
189
190
|
if (!isBrowserSpeechAuthority(authority)) {
|
|
190
191
|
throw new TypeError("Browser speech authority construction did not return an SDK authority.");
|
|
@@ -207,7 +208,7 @@ function linkSignal(signal) {
|
|
|
207
208
|
const forwardExternalAbort = () => controller.abort(signal?.reason);
|
|
208
209
|
if (signal?.aborted) forwardExternalAbort();
|
|
209
210
|
else signal?.addEventListener?.("abort", forwardExternalAbort, { once: true });
|
|
210
|
-
return
|
|
211
|
+
return completeValue({
|
|
211
212
|
controller,
|
|
212
213
|
abort(reason, code = "ARCANE_AI_REQUEST_ABORTED") {
|
|
213
214
|
if (controller.signal.aborted) return;
|
|
@@ -265,7 +266,7 @@ function isAbortSignal(value) {
|
|
|
265
266
|
}
|
|
266
267
|
|
|
267
268
|
function providerContext(context, role, operation) {
|
|
268
|
-
if (context === undefined) return
|
|
269
|
+
if (context === undefined) return completeValue({ signal: null });
|
|
269
270
|
if (!context || typeof context !== "object" || Array.isArray(context)) {
|
|
270
271
|
throw providerError(
|
|
271
272
|
"ARCANE_AI_INVALID_REQUEST",
|
|
@@ -291,11 +292,25 @@ function providerContext(context, role, operation) {
|
|
|
291
292
|
`${role}-provider-${operation}-signal-not-abort-signal`,
|
|
292
293
|
);
|
|
293
294
|
}
|
|
294
|
-
return
|
|
295
|
+
return completeValue({ context, signal });
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function reportedSecureIntent(...scopes) {
|
|
299
|
+
for (const scope of scopes) {
|
|
300
|
+
try {
|
|
301
|
+
if (scope?.secure === true) return completeValue({ secure: true });
|
|
302
|
+
} catch {
|
|
303
|
+
// Intent reporting never turns a security-shaped value into an ordinary
|
|
304
|
+
// provider admission check. Future hardening requires user review.
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return null;
|
|
295
308
|
}
|
|
296
309
|
|
|
297
|
-
function
|
|
298
|
-
|
|
310
|
+
function providerWarnings(runtimeWarnings = NO_PROVIDER_WARNINGS) {
|
|
311
|
+
const warnings = runtimeWarnings;
|
|
312
|
+
if (warnings.length === 0) return NO_PROVIDER_WARNINGS;
|
|
313
|
+
return completeValue([...new Set(warnings)]);
|
|
299
314
|
}
|
|
300
315
|
|
|
301
316
|
function publicStatus({
|
|
@@ -309,10 +324,10 @@ function publicStatus({
|
|
|
309
324
|
cache,
|
|
310
325
|
lifecycleReason,
|
|
311
326
|
activeOperation,
|
|
312
|
-
|
|
313
|
-
|
|
327
|
+
secureIntent,
|
|
328
|
+
warnings,
|
|
314
329
|
}) {
|
|
315
|
-
return
|
|
330
|
+
return completeValue({
|
|
316
331
|
role,
|
|
317
332
|
providerId: id,
|
|
318
333
|
modelId: authority.modelId,
|
|
@@ -325,9 +340,8 @@ function publicStatus({
|
|
|
325
340
|
generation,
|
|
326
341
|
errorCode,
|
|
327
342
|
cache,
|
|
328
|
-
security,
|
|
329
|
-
|
|
330
|
-
artifactGraphAdmission,
|
|
343
|
+
...(secureIntent ? { security: secureIntent } : {}),
|
|
344
|
+
warnings,
|
|
331
345
|
});
|
|
332
346
|
}
|
|
333
347
|
|
|
@@ -507,9 +521,9 @@ function observeLoadOperation(record, { signal, progress }, role) {
|
|
|
507
521
|
settle(reject, abortError(signal, `${role}-load-cancelled`));
|
|
508
522
|
abandonIfUnobserved(`${role}-load-cancelled`);
|
|
509
523
|
};
|
|
510
|
-
const observer =
|
|
524
|
+
const observer = completeValue({
|
|
511
525
|
progress(value) {
|
|
512
|
-
if (settled) return;
|
|
526
|
+
if (settled || typeof progress !== "function") return;
|
|
513
527
|
try {
|
|
514
528
|
progress(value);
|
|
515
529
|
} catch (error) {
|
|
@@ -543,7 +557,6 @@ function observeLoadOperation(record, { signal, progress }, role) {
|
|
|
543
557
|
);
|
|
544
558
|
return;
|
|
545
559
|
}
|
|
546
|
-
if (record.hasProgress) observer.progress(record.latestProgress);
|
|
547
560
|
});
|
|
548
561
|
}
|
|
549
562
|
|
|
@@ -762,8 +775,8 @@ async function decodeSharedTranscriptionPayload(
|
|
|
762
775
|
mono[index] = sample;
|
|
763
776
|
}
|
|
764
777
|
throwIfAborted(signal, cancellationReason);
|
|
765
|
-
return
|
|
766
|
-
payload:
|
|
778
|
+
return completeValue({
|
|
779
|
+
payload: completeValue({ audio: mono, sampleRate }),
|
|
767
780
|
shared: true,
|
|
768
781
|
});
|
|
769
782
|
}
|
|
@@ -812,8 +825,8 @@ function cloneNativeTranscriptionPayload(payload, authority) {
|
|
|
812
825
|
);
|
|
813
826
|
}
|
|
814
827
|
}
|
|
815
|
-
return
|
|
816
|
-
payload:
|
|
828
|
+
return completeValue({
|
|
829
|
+
payload: completeValue({
|
|
817
830
|
audio: new Float32Array(descriptors.audio.value),
|
|
818
831
|
sampleRate,
|
|
819
832
|
}),
|
|
@@ -857,15 +870,13 @@ function normalizeSynthesisPayload(payload, authority) {
|
|
|
857
870
|
assertPayloadModel(payload, authority, { operationSubject: "tts-synthesis" });
|
|
858
871
|
textValue = descriptors.text.value;
|
|
859
872
|
}
|
|
860
|
-
const text = typeof textValue === "string" ? textValue
|
|
873
|
+
const text = typeof textValue === "string" ? textValue : "";
|
|
861
874
|
const voiceValue = Object.hasOwn(descriptors, "voice")
|
|
862
875
|
? descriptors.voice.value
|
|
863
876
|
: authority.defaultVoice;
|
|
864
|
-
const voice = typeof voiceValue === "string"
|
|
865
|
-
? voiceValue.trim()
|
|
866
|
-
: "";
|
|
877
|
+
const voice = typeof voiceValue === "string" ? voiceValue : "";
|
|
867
878
|
const speed = Object.hasOwn(descriptors, "speed") ? descriptors.speed.value : 1;
|
|
868
|
-
if (!text) {
|
|
879
|
+
if (!text.trim()) {
|
|
869
880
|
throw providerError(
|
|
870
881
|
"ARCANE_AI_INVALID_REQUEST",
|
|
871
882
|
"Kokoro requires nonempty text.",
|
|
@@ -873,7 +884,7 @@ function normalizeSynthesisPayload(payload, authority) {
|
|
|
873
884
|
"tts-synthesis-text-empty",
|
|
874
885
|
);
|
|
875
886
|
}
|
|
876
|
-
if (!voice) {
|
|
887
|
+
if (!voice.trim()) {
|
|
877
888
|
throw providerError(
|
|
878
889
|
"ARCANE_AI_INVALID_REQUEST",
|
|
879
890
|
"Kokoro requires a nonempty voice id.",
|
|
@@ -881,12 +892,12 @@ function normalizeSynthesisPayload(payload, authority) {
|
|
|
881
892
|
"tts-synthesis-voice-empty",
|
|
882
893
|
);
|
|
883
894
|
}
|
|
884
|
-
if (!Number.isFinite(speed) || speed <= 0
|
|
895
|
+
if (!Number.isFinite(speed) || speed <= 0) {
|
|
885
896
|
throw providerError(
|
|
886
897
|
"ARCANE_AI_INVALID_REQUEST",
|
|
887
|
-
"Kokoro speed must be greater than 0
|
|
898
|
+
"Kokoro speed must be greater than 0.",
|
|
888
899
|
undefined,
|
|
889
|
-
"tts-synthesis-speed-
|
|
900
|
+
"tts-synthesis-speed-not-positive",
|
|
890
901
|
);
|
|
891
902
|
}
|
|
892
903
|
if (authority.graph
|
|
@@ -898,8 +909,8 @@ function normalizeSynthesisPayload(payload, authority) {
|
|
|
898
909
|
"tts-synthesis-voice-not-declared",
|
|
899
910
|
);
|
|
900
911
|
}
|
|
901
|
-
return
|
|
902
|
-
payload:
|
|
912
|
+
return completeValue({
|
|
913
|
+
payload: completeValue({ text, voice, speed }),
|
|
903
914
|
shared,
|
|
904
915
|
});
|
|
905
916
|
}
|
|
@@ -1019,7 +1030,7 @@ function encodeSharedSynthesisResult(result, authority) {
|
|
|
1019
1030
|
true,
|
|
1020
1031
|
);
|
|
1021
1032
|
}
|
|
1022
|
-
return
|
|
1033
|
+
return completeValue({ audio: bytes, contentType: "audio/wav" });
|
|
1023
1034
|
}
|
|
1024
1035
|
|
|
1025
1036
|
function createBrowserSpeechProvider({
|
|
@@ -1044,35 +1055,23 @@ function createBrowserSpeechProvider({
|
|
|
1044
1055
|
if (!isDbopfsSpeechArtifactStore(store)) {
|
|
1045
1056
|
throw new TypeError("Browser speech providers require an SDK-created DBOPFS artifact store.");
|
|
1046
1057
|
}
|
|
1047
|
-
const configuredAppSecurity = normalizeModelSecurity(
|
|
1048
|
-
appSecurity,
|
|
1049
|
-
"Browser speech app security",
|
|
1050
|
-
);
|
|
1051
|
-
const configuredProviderSecurity = normalizeModelSecurity(
|
|
1052
|
-
security,
|
|
1053
|
-
"Browser speech provider security",
|
|
1054
|
-
);
|
|
1055
1058
|
const authority = createProviderAuthority({
|
|
1056
1059
|
providerId,
|
|
1057
1060
|
role,
|
|
1058
1061
|
graph,
|
|
1059
1062
|
model,
|
|
1060
1063
|
runtime,
|
|
1061
|
-
security: configuredProviderSecurity,
|
|
1062
|
-
});
|
|
1063
|
-
const defaultSecurity = resolveModelSecurity({
|
|
1064
|
-
app: configuredAppSecurity,
|
|
1065
|
-
binding: configuredProviderSecurity,
|
|
1066
1064
|
});
|
|
1065
|
+
const defaultSecureIntent = reportedSecureIntent(appSecurity, security);
|
|
1067
1066
|
const operation = ROLE_OPERATION[role];
|
|
1068
1067
|
const speech = role === "stt"
|
|
1069
|
-
?
|
|
1070
|
-
:
|
|
1068
|
+
? completeValue({ inputSampleRate: inputSampleRate(authority) })
|
|
1069
|
+
: completeValue({
|
|
1071
1070
|
outputSampleRate: outputSampleRate(authority),
|
|
1072
|
-
responseFormats:
|
|
1071
|
+
responseFormats: completeValue([TTS_RESPONSE_FORMAT]),
|
|
1073
1072
|
defaultResponseFormat: TTS_RESPONSE_FORMAT,
|
|
1074
1073
|
});
|
|
1075
|
-
const catalogEntry =
|
|
1074
|
+
const catalogEntry = completeValue({
|
|
1076
1075
|
id: authority.modelId,
|
|
1077
1076
|
providerId,
|
|
1078
1077
|
role,
|
|
@@ -1084,7 +1083,6 @@ function createBrowserSpeechProvider({
|
|
|
1084
1083
|
runtime: authority.runtime,
|
|
1085
1084
|
files: authority.files,
|
|
1086
1085
|
...(authority.graph ? {
|
|
1087
|
-
artifactGraphId: authority.artifactGraphId,
|
|
1088
1086
|
voices: authority.voices,
|
|
1089
1087
|
} : {}),
|
|
1090
1088
|
speech,
|
|
@@ -1092,7 +1090,6 @@ function createBrowserSpeechProvider({
|
|
|
1092
1090
|
let state = "unloaded";
|
|
1093
1091
|
let errorCode = null;
|
|
1094
1092
|
let cache = null;
|
|
1095
|
-
let artifactGraphAdmission = null;
|
|
1096
1093
|
let lifecycleReason = `${role}-provider-created`;
|
|
1097
1094
|
let activeOperation = null;
|
|
1098
1095
|
let generation = 0;
|
|
@@ -1101,8 +1098,13 @@ function createBrowserSpeechProvider({
|
|
|
1101
1098
|
let unloadOperation = null;
|
|
1102
1099
|
let disposeOperation = null;
|
|
1103
1100
|
let requestOperation = null;
|
|
1101
|
+
let lastWarnings = NO_PROVIDER_WARNINGS;
|
|
1102
|
+
let secureIntent = defaultSecureIntent;
|
|
1104
1103
|
|
|
1105
1104
|
function status() {
|
|
1105
|
+
const runtimeWarnings = active?.warnings
|
|
1106
|
+
?? loadOperation?.warnings
|
|
1107
|
+
?? lastWarnings;
|
|
1106
1108
|
return publicStatus({
|
|
1107
1109
|
role,
|
|
1108
1110
|
id: providerId,
|
|
@@ -1114,8 +1116,8 @@ function createBrowserSpeechProvider({
|
|
|
1114
1116
|
cache,
|
|
1115
1117
|
lifecycleReason,
|
|
1116
1118
|
activeOperation,
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
+
secureIntent,
|
|
1120
|
+
warnings: providerWarnings(runtimeWarnings),
|
|
1119
1121
|
});
|
|
1120
1122
|
}
|
|
1121
1123
|
|
|
@@ -1156,7 +1158,7 @@ function createBrowserSpeechProvider({
|
|
|
1156
1158
|
localOnly: true,
|
|
1157
1159
|
|
|
1158
1160
|
catalog() {
|
|
1159
|
-
return
|
|
1161
|
+
return completeValue([catalogEntry]);
|
|
1160
1162
|
},
|
|
1161
1163
|
|
|
1162
1164
|
inspect(selection, context = {}) {
|
|
@@ -1168,14 +1170,14 @@ function createBrowserSpeechProvider({
|
|
|
1168
1170
|
&& (!Object.hasOwn(selection ?? {}, "role") || selection.role === role)
|
|
1169
1171
|
&& selection?.localOnly !== false;
|
|
1170
1172
|
if (!available) {
|
|
1171
|
-
return
|
|
1173
|
+
return completeValue({
|
|
1172
1174
|
available: false,
|
|
1173
1175
|
code: "ARCANE_AI_MODEL_AUTHORITY_REQUIRED",
|
|
1174
1176
|
message: "The selected browser speech model does not match this provider authority.",
|
|
1175
1177
|
reason: `${role}-provider-inspection-selection-authority-mismatch`,
|
|
1176
1178
|
});
|
|
1177
1179
|
}
|
|
1178
|
-
return
|
|
1180
|
+
return completeValue({ available: true, authority });
|
|
1179
1181
|
},
|
|
1180
1182
|
|
|
1181
1183
|
status,
|
|
@@ -1234,10 +1236,10 @@ function createBrowserSpeechProvider({
|
|
|
1234
1236
|
} catch (error) {
|
|
1235
1237
|
return Promise.reject(trustedLoadFailure(error, role));
|
|
1236
1238
|
}
|
|
1237
|
-
if (typeof loadProgress !== "function") {
|
|
1239
|
+
if (loadProgress !== undefined && typeof loadProgress !== "function") {
|
|
1238
1240
|
return Promise.reject(providerError(
|
|
1239
1241
|
"ARCANE_AI_INVALID_REQUEST",
|
|
1240
|
-
"Browser speech load progress must be a function.",
|
|
1242
|
+
"Browser speech load progress must be a function when supplied.",
|
|
1241
1243
|
undefined,
|
|
1242
1244
|
`${role}-load-progress-callback-not-function`,
|
|
1243
1245
|
));
|
|
@@ -1253,62 +1255,20 @@ function createBrowserSpeechProvider({
|
|
|
1253
1255
|
`${role}-load-selection-authority-mismatch`,
|
|
1254
1256
|
));
|
|
1255
1257
|
}
|
|
1256
|
-
|
|
1258
|
+
secureIntent = reportedSecureIntent(appSecurity, security, context.security);
|
|
1257
1259
|
try {
|
|
1258
|
-
if (authority.graph) {
|
|
1259
|
-
effectiveSecurity = resolveModelSecurity({
|
|
1260
|
-
app: graphSecurity(
|
|
1261
|
-
configuredAppSecurity,
|
|
1262
|
-
"Browser speech graph app security",
|
|
1263
|
-
),
|
|
1264
|
-
binding: graphSecurity(
|
|
1265
|
-
configuredProviderSecurity,
|
|
1266
|
-
"Browser speech graph provider security",
|
|
1267
|
-
),
|
|
1268
|
-
load: graphSecurity(
|
|
1269
|
-
context.security,
|
|
1270
|
-
"Browser speech graph load security",
|
|
1271
|
-
),
|
|
1272
|
-
});
|
|
1273
|
-
if (effectiveSecurity.secure !== true) {
|
|
1274
|
-
throw providerError(
|
|
1275
|
-
"ARCANE_AI_SECURE_MODE_REQUIRED",
|
|
1276
|
-
"Authenticated browser speech artifact graphs require explicit secure:true.",
|
|
1277
|
-
undefined,
|
|
1278
|
-
`${role}-artifact-graph-secure-mode-required`,
|
|
1279
|
-
);
|
|
1280
|
-
}
|
|
1281
|
-
} else {
|
|
1282
|
-
effectiveSecurity = resolveModelSecurity({
|
|
1283
|
-
app: configuredAppSecurity,
|
|
1284
|
-
binding: configuredProviderSecurity,
|
|
1285
|
-
load: context.security,
|
|
1286
|
-
});
|
|
1287
|
-
}
|
|
1288
1260
|
throwIfAborted(loadSignal, `${role}-load-cancelled`);
|
|
1289
1261
|
} catch (error) {
|
|
1290
1262
|
return Promise.reject(trustedLoadFailure(error, role));
|
|
1291
1263
|
}
|
|
1292
1264
|
if (state === "ready" && active) {
|
|
1293
|
-
|
|
1294
|
-
return Promise.resolve(status());
|
|
1295
|
-
}
|
|
1296
|
-
lifecycleReason = `${role}-load-superseded-by-security-change`;
|
|
1297
|
-
return provider.unload().then(() => provider.load(context));
|
|
1265
|
+
return Promise.resolve(status());
|
|
1298
1266
|
}
|
|
1299
1267
|
if (loadOperation) {
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
}, role);
|
|
1305
|
-
}
|
|
1306
|
-
lifecycleReason = `${role}-load-superseded-by-security-change`;
|
|
1307
|
-
loadOperation.abort(
|
|
1308
|
-
`${role}-load-superseded-by-security-change`,
|
|
1309
|
-
"ARCANE_AI_OPERATION_SUPERSEDED",
|
|
1310
|
-
);
|
|
1311
|
-
return loadOperation.promise.catch(() => undefined).then(() => provider.load(context));
|
|
1268
|
+
return observeLoadOperation(loadOperation, {
|
|
1269
|
+
signal: loadSignal,
|
|
1270
|
+
progress: loadProgress,
|
|
1271
|
+
}, role);
|
|
1312
1272
|
}
|
|
1313
1273
|
generation += 1;
|
|
1314
1274
|
const operationGeneration = generation;
|
|
@@ -1319,31 +1279,27 @@ function createBrowserSpeechProvider({
|
|
|
1319
1279
|
errorCode = null;
|
|
1320
1280
|
const record = {
|
|
1321
1281
|
promise: null,
|
|
1322
|
-
|
|
1282
|
+
warnings: NO_PROVIDER_WARNINGS,
|
|
1323
1283
|
observers: new Set(),
|
|
1324
1284
|
settled: false,
|
|
1325
|
-
hasProgress: false,
|
|
1326
|
-
latestProgress: null,
|
|
1327
1285
|
abort: (
|
|
1328
1286
|
reason = `${role}-load-cancelled`,
|
|
1329
1287
|
code = "ARCANE_AI_REQUEST_ABORTED",
|
|
1330
1288
|
) => linked.abort(reason, code),
|
|
1331
|
-
publishProgress(progress) {
|
|
1332
|
-
record.latestProgress = progress;
|
|
1333
|
-
record.hasProgress = true;
|
|
1334
|
-
for (const observer of [...record.observers]) observer.progress(progress);
|
|
1335
|
-
},
|
|
1336
1289
|
};
|
|
1290
|
+
lastWarnings = NO_PROVIDER_WARNINGS;
|
|
1337
1291
|
const promise = Promise.resolve().then(async () => {
|
|
1338
1292
|
let prepared = null;
|
|
1339
1293
|
let slot = null;
|
|
1340
1294
|
try {
|
|
1341
1295
|
prepared = await store.prepare(authority.graph ?? authority, {
|
|
1342
1296
|
signal: linked.controller.signal,
|
|
1343
|
-
onProgress: record.publishProgress,
|
|
1344
1297
|
offline,
|
|
1345
|
-
security: effectiveSecurity,
|
|
1346
1298
|
});
|
|
1299
|
+
record.warnings = Array.isArray(prepared.warnings)
|
|
1300
|
+
? completeValue([...prepared.warnings])
|
|
1301
|
+
: NO_PROVIDER_WARNINGS;
|
|
1302
|
+
lastWarnings = record.warnings;
|
|
1347
1303
|
throwIfAborted(
|
|
1348
1304
|
linked.controller.signal,
|
|
1349
1305
|
() => linked.reason(`${role}-load-cancelled`),
|
|
@@ -1353,16 +1309,14 @@ function createBrowserSpeechProvider({
|
|
|
1353
1309
|
"ARCANE_AI_OPERATION_SUPERSEDED",
|
|
1354
1310
|
"Browser speech loading was superseded.",
|
|
1355
1311
|
undefined,
|
|
1356
|
-
|
|
1357
|
-
? lifecycleReason
|
|
1358
|
-
: `${role}-load-superseded-by-unload`,
|
|
1312
|
+
`${role}-load-superseded-by-unload`,
|
|
1359
1313
|
);
|
|
1360
1314
|
}
|
|
1361
1315
|
slot = {
|
|
1362
1316
|
prepared,
|
|
1363
1317
|
released: false,
|
|
1364
1318
|
client: null,
|
|
1365
|
-
|
|
1319
|
+
warnings: record.warnings,
|
|
1366
1320
|
};
|
|
1367
1321
|
slot.client = createSpeechWorkerClient({
|
|
1368
1322
|
role,
|
|
@@ -1382,7 +1336,6 @@ function createBrowserSpeechProvider({
|
|
|
1382
1336
|
? `${role}-worker-protocol-mismatch`
|
|
1383
1337
|
: `${role}-worker-crashed`);
|
|
1384
1338
|
activeOperation = null;
|
|
1385
|
-
artifactGraphAdmission = null;
|
|
1386
1339
|
}
|
|
1387
1340
|
try {
|
|
1388
1341
|
releaseSlot(slot);
|
|
@@ -1393,7 +1346,6 @@ function createBrowserSpeechProvider({
|
|
|
1393
1346
|
errorCode = releaseFailure.code;
|
|
1394
1347
|
lifecycleReason = releaseFailure.reason;
|
|
1395
1348
|
activeOperation = null;
|
|
1396
|
-
artifactGraphAdmission = null;
|
|
1397
1349
|
}
|
|
1398
1350
|
throw releaseFailure;
|
|
1399
1351
|
}
|
|
@@ -1408,19 +1360,16 @@ function createBrowserSpeechProvider({
|
|
|
1408
1360
|
`${role}-worker-client-authority-mismatch`,
|
|
1409
1361
|
);
|
|
1410
1362
|
}
|
|
1411
|
-
const configuration =
|
|
1363
|
+
const configuration = completeValue({
|
|
1412
1364
|
role,
|
|
1413
1365
|
runtime: prepared.runtime,
|
|
1414
1366
|
model: prepared.model,
|
|
1415
|
-
security: effectiveSecurity,
|
|
1416
1367
|
...(authority.graph ? {
|
|
1417
|
-
artifactGraphId: authority.artifactGraphId,
|
|
1418
1368
|
artifactGraphProtocol: authority.graph.protocol,
|
|
1419
1369
|
} : {}),
|
|
1420
1370
|
});
|
|
1421
1371
|
await slot.client.request("load", { configuration }, {
|
|
1422
1372
|
signal: linked.controller.signal,
|
|
1423
|
-
progress: record.publishProgress,
|
|
1424
1373
|
});
|
|
1425
1374
|
throwIfAborted(
|
|
1426
1375
|
linked.controller.signal,
|
|
@@ -1431,14 +1380,11 @@ function createBrowserSpeechProvider({
|
|
|
1431
1380
|
"ARCANE_AI_OPERATION_SUPERSEDED",
|
|
1432
1381
|
"Browser speech loading was superseded.",
|
|
1433
1382
|
undefined,
|
|
1434
|
-
|
|
1435
|
-
? lifecycleReason
|
|
1436
|
-
: `${role}-load-superseded-by-unload`,
|
|
1383
|
+
`${role}-load-superseded-by-unload`,
|
|
1437
1384
|
);
|
|
1438
1385
|
}
|
|
1439
1386
|
active = slot;
|
|
1440
1387
|
cache = prepared.cache;
|
|
1441
|
-
artifactGraphAdmission = prepared.artifactGraphAdmission ?? null;
|
|
1442
1388
|
state = "ready";
|
|
1443
1389
|
lifecycleReason = `${role}-load-completed`;
|
|
1444
1390
|
activeOperation = null;
|
|
@@ -1576,7 +1522,7 @@ function createBrowserSpeechProvider({
|
|
|
1576
1522
|
? encodeSharedSynthesisResult(result, authority)
|
|
1577
1523
|
: result;
|
|
1578
1524
|
})();
|
|
1579
|
-
requestOperation =
|
|
1525
|
+
requestOperation = completeValue({
|
|
1580
1526
|
promise,
|
|
1581
1527
|
abort: (
|
|
1582
1528
|
reason = ROLE_REQUEST_REASON[role],
|
|
@@ -1605,7 +1551,6 @@ function createBrowserSpeechProvider({
|
|
|
1605
1551
|
failure = trustedWorkerFailure(cleanupError, role);
|
|
1606
1552
|
}
|
|
1607
1553
|
state = "unloaded";
|
|
1608
|
-
artifactGraphAdmission = null;
|
|
1609
1554
|
}
|
|
1610
1555
|
lifecycleReason = failure?.reason
|
|
1611
1556
|
?? (failure?.code === "ARCANE_AI_REQUEST_ABORTED"
|
|
@@ -1678,7 +1623,6 @@ function createBrowserSpeechProvider({
|
|
|
1678
1623
|
`${role}-worker-terminated-by-unload`,
|
|
1679
1624
|
));
|
|
1680
1625
|
cache = null;
|
|
1681
|
-
artifactGraphAdmission = null;
|
|
1682
1626
|
state = "unloaded";
|
|
1683
1627
|
lifecycleReason = `${role}-unload-completed`;
|
|
1684
1628
|
activeOperation = null;
|
|
@@ -1745,7 +1689,7 @@ function createBrowserSpeechProvider({
|
|
|
1745
1689
|
);
|
|
1746
1690
|
},
|
|
1747
1691
|
};
|
|
1748
|
-
return
|
|
1692
|
+
return completeValue(provider);
|
|
1749
1693
|
}
|
|
1750
1694
|
|
|
1751
1695
|
export function createBrowserWhisperProvider(options = {}) {
|