arcane-os 0.3.1 → 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 +14 -0
- package/README.md +86 -117
- package/bin/arcane-test.mjs +170 -46
- package/browser-runtime/ai/browser-speech-artifacts.mjs +855 -895
- package/browser-runtime/ai/browser-speech-providers.mjs +80 -204
- 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 -148
- package/browser-runtime/ai/speech-worker-runtime.mjs +642 -374
- 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 +1042 -427
- package/runtime/arcane/modules/AIProviderRuntime.js +618 -359
- 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 +27 -67
- 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 +109 -1558
- 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 -185
- package/docs/reference/ai/browser-speech-package-authority.json +0 -835
- package/docs/reference/ai/browser-speech.md +0 -1295
- 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 -719
- package/docs/reference/runtime-components.md +0 -1366
- package/docs/reference/runtime-entities.md +0 -303
- package/docs/reference/runtime-modules.md +0 -2965
- package/docs/reference/sdk-api.md +0 -6698
- 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,18 +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";
|
|
30
|
-
const
|
|
31
|
-
const NO_PROVIDER_WARNINGS = Object.freeze([]);
|
|
32
|
-
const WARN_FIRST_PROVIDER_WARNINGS = Object.freeze([WARN_FIRST_WARNING]);
|
|
27
|
+
const NO_PROVIDER_WARNINGS = completeValue([]);
|
|
33
28
|
const WORKER_FAILURE_CODES = new Set([
|
|
34
29
|
"ARCANE_AI_WORKER_CRASHED",
|
|
35
30
|
"ARCANE_AI_WORKER_MESSAGE_ERROR",
|
|
@@ -69,7 +64,9 @@ function trustedLoadFailure(error, role) {
|
|
|
69
64
|
if (isTrustedSpeechError(error)) return error;
|
|
70
65
|
return providerError(
|
|
71
66
|
"ARCANE_AI_PROVIDER_LOAD_FAILED",
|
|
72
|
-
|
|
67
|
+
typeof error?.message === "string" && error.message
|
|
68
|
+
? error.message
|
|
69
|
+
: `The browser ${role} provider load was rejected.`,
|
|
73
70
|
error,
|
|
74
71
|
`${role}-provider-load-rejected`,
|
|
75
72
|
);
|
|
@@ -79,7 +76,9 @@ function trustedRequestFailure(error, role) {
|
|
|
79
76
|
if (isTrustedSpeechError(error)) return error;
|
|
80
77
|
return providerError(
|
|
81
78
|
"ARCANE_AI_PROVIDER_REQUEST_FAILED",
|
|
82
|
-
|
|
79
|
+
typeof error?.message === "string" && error.message
|
|
80
|
+
? error.message
|
|
81
|
+
: `The browser ${role} engine operation was rejected.`,
|
|
83
82
|
error,
|
|
84
83
|
role === "stt"
|
|
85
84
|
? "stt-transcription-engine-operation-rejected"
|
|
@@ -91,7 +90,9 @@ function trustedWorkerFailure(error, role) {
|
|
|
91
90
|
if (isTrustedSpeechError(error)) return error;
|
|
92
91
|
return providerError(
|
|
93
92
|
"ARCANE_AI_WORKER_MESSAGE_REJECTED",
|
|
94
|
-
|
|
93
|
+
typeof error?.message === "string" && error.message
|
|
94
|
+
? error.message
|
|
95
|
+
: `The browser ${role} Worker returned an unreadable error envelope.`,
|
|
95
96
|
error,
|
|
96
97
|
`${role}-worker-error-envelope-rejected`,
|
|
97
98
|
);
|
|
@@ -101,7 +102,9 @@ function trustedLifecycleFailure(error, role, operation) {
|
|
|
101
102
|
if (isTrustedSpeechError(error)) return error;
|
|
102
103
|
return providerError(
|
|
103
104
|
"ARCANE_AI_INVALID_REQUEST",
|
|
104
|
-
|
|
105
|
+
typeof error?.message === "string" && error.message
|
|
106
|
+
? error.message
|
|
107
|
+
: `The browser ${role} ${operation} context could not be read.`,
|
|
105
108
|
error,
|
|
106
109
|
`${role}-provider-${operation}-context-read-rejected`,
|
|
107
110
|
);
|
|
@@ -132,8 +135,8 @@ function workerFailureCode(error) {
|
|
|
132
135
|
}
|
|
133
136
|
|
|
134
137
|
function requiredIdentifier(value, label) {
|
|
135
|
-
if (typeof value !== "string" || !value.trim()
|
|
136
|
-
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.`);
|
|
137
140
|
}
|
|
138
141
|
return value.trim();
|
|
139
142
|
}
|
|
@@ -144,7 +147,6 @@ function createProviderAuthority({
|
|
|
144
147
|
graph,
|
|
145
148
|
model,
|
|
146
149
|
runtime,
|
|
147
|
-
security,
|
|
148
150
|
}) {
|
|
149
151
|
if (graph !== undefined) {
|
|
150
152
|
if (model !== undefined || runtime !== undefined) {
|
|
@@ -161,9 +163,8 @@ function createProviderAuthority({
|
|
|
161
163
|
&& graph.providerId !== providerId) {
|
|
162
164
|
throw new TypeError("Browser speech graph providerId must match the provider id.");
|
|
163
165
|
}
|
|
164
|
-
return
|
|
166
|
+
return completeValue({
|
|
165
167
|
protocol: AI_MODEL_AUTHORITY_PROTOCOL,
|
|
166
|
-
admitted: true,
|
|
167
168
|
graph,
|
|
168
169
|
artifactGraphProtocol: graph.protocol,
|
|
169
170
|
providerId,
|
|
@@ -173,13 +174,11 @@ function createProviderAuthority({
|
|
|
173
174
|
revision: graph.model.revision,
|
|
174
175
|
dtype: graph.model.dtype,
|
|
175
176
|
defaultVoice: graph.model.defaultVoice,
|
|
176
|
-
voices: graph.model.voices ??
|
|
177
|
+
voices: graph.model.voices ?? completeValue([]),
|
|
177
178
|
inputSampleRate: graph.model.inputSampleRate,
|
|
178
179
|
outputSampleRate: graph.model.outputSampleRate,
|
|
179
180
|
runtime: graph.runtime,
|
|
180
181
|
files: graph.files,
|
|
181
|
-
security,
|
|
182
|
-
artifactGraphId: graph.identitySha256,
|
|
183
182
|
});
|
|
184
183
|
}
|
|
185
184
|
const authority = createBrowserSpeechAuthority({
|
|
@@ -187,7 +186,6 @@ function createProviderAuthority({
|
|
|
187
186
|
role,
|
|
188
187
|
model,
|
|
189
188
|
runtime,
|
|
190
|
-
security,
|
|
191
189
|
});
|
|
192
190
|
if (!isBrowserSpeechAuthority(authority)) {
|
|
193
191
|
throw new TypeError("Browser speech authority construction did not return an SDK authority.");
|
|
@@ -210,7 +208,7 @@ function linkSignal(signal) {
|
|
|
210
208
|
const forwardExternalAbort = () => controller.abort(signal?.reason);
|
|
211
209
|
if (signal?.aborted) forwardExternalAbort();
|
|
212
210
|
else signal?.addEventListener?.("abort", forwardExternalAbort, { once: true });
|
|
213
|
-
return
|
|
211
|
+
return completeValue({
|
|
214
212
|
controller,
|
|
215
213
|
abort(reason, code = "ARCANE_AI_REQUEST_ABORTED") {
|
|
216
214
|
if (controller.signal.aborted) return;
|
|
@@ -268,7 +266,7 @@ function isAbortSignal(value) {
|
|
|
268
266
|
}
|
|
269
267
|
|
|
270
268
|
function providerContext(context, role, operation) {
|
|
271
|
-
if (context === undefined) return
|
|
269
|
+
if (context === undefined) return completeValue({ signal: null });
|
|
272
270
|
if (!context || typeof context !== "object" || Array.isArray(context)) {
|
|
273
271
|
throw providerError(
|
|
274
272
|
"ARCANE_AI_INVALID_REQUEST",
|
|
@@ -294,39 +292,25 @@ function providerContext(context, role, operation) {
|
|
|
294
292
|
`${role}-provider-${operation}-signal-not-abort-signal`,
|
|
295
293
|
);
|
|
296
294
|
}
|
|
297
|
-
return
|
|
295
|
+
return completeValue({ context, signal });
|
|
298
296
|
}
|
|
299
297
|
|
|
300
|
-
function
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
if (!["unchecked", "pending", "verified", "failed"].includes(state)) {
|
|
309
|
-
throw new TypeError("Browser speech integrity outcome is invalid.");
|
|
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
|
+
}
|
|
310
306
|
}
|
|
311
|
-
return
|
|
312
|
-
state,
|
|
313
|
-
byteLength: Object.freeze({
|
|
314
|
-
enabled: security.checks.byteLength,
|
|
315
|
-
state: security.checks.byteLength ? state : "unchecked",
|
|
316
|
-
}),
|
|
317
|
-
sha256: Object.freeze({
|
|
318
|
-
enabled: security.checks.sha256,
|
|
319
|
-
state: security.checks.sha256 ? state : "unchecked",
|
|
320
|
-
}),
|
|
321
|
-
});
|
|
307
|
+
return null;
|
|
322
308
|
}
|
|
323
309
|
|
|
324
|
-
function providerWarnings(
|
|
325
|
-
const warnings =
|
|
326
|
-
? runtimeWarnings
|
|
327
|
-
: [...WARN_FIRST_PROVIDER_WARNINGS, ...runtimeWarnings];
|
|
310
|
+
function providerWarnings(runtimeWarnings = NO_PROVIDER_WARNINGS) {
|
|
311
|
+
const warnings = runtimeWarnings;
|
|
328
312
|
if (warnings.length === 0) return NO_PROVIDER_WARNINGS;
|
|
329
|
-
return
|
|
313
|
+
return completeValue([...new Set(warnings)]);
|
|
330
314
|
}
|
|
331
315
|
|
|
332
316
|
function publicStatus({
|
|
@@ -340,12 +324,10 @@ function publicStatus({
|
|
|
340
324
|
cache,
|
|
341
325
|
lifecycleReason,
|
|
342
326
|
activeOperation,
|
|
343
|
-
|
|
344
|
-
security,
|
|
345
|
-
integrity,
|
|
327
|
+
secureIntent,
|
|
346
328
|
warnings,
|
|
347
329
|
}) {
|
|
348
|
-
return
|
|
330
|
+
return completeValue({
|
|
349
331
|
role,
|
|
350
332
|
providerId: id,
|
|
351
333
|
modelId: authority.modelId,
|
|
@@ -358,11 +340,8 @@ function publicStatus({
|
|
|
358
340
|
generation,
|
|
359
341
|
errorCode,
|
|
360
342
|
cache,
|
|
361
|
-
security,
|
|
362
|
-
integrity,
|
|
343
|
+
...(secureIntent ? { security: secureIntent } : {}),
|
|
363
344
|
warnings,
|
|
364
|
-
artifactGraphId: authority.artifactGraphId ?? null,
|
|
365
|
-
artifactGraphAdmission,
|
|
366
345
|
});
|
|
367
346
|
}
|
|
368
347
|
|
|
@@ -542,9 +521,9 @@ function observeLoadOperation(record, { signal, progress }, role) {
|
|
|
542
521
|
settle(reject, abortError(signal, `${role}-load-cancelled`));
|
|
543
522
|
abandonIfUnobserved(`${role}-load-cancelled`);
|
|
544
523
|
};
|
|
545
|
-
const observer =
|
|
524
|
+
const observer = completeValue({
|
|
546
525
|
progress(value) {
|
|
547
|
-
if (settled) return;
|
|
526
|
+
if (settled || typeof progress !== "function") return;
|
|
548
527
|
try {
|
|
549
528
|
progress(value);
|
|
550
529
|
} catch (error) {
|
|
@@ -578,7 +557,6 @@ function observeLoadOperation(record, { signal, progress }, role) {
|
|
|
578
557
|
);
|
|
579
558
|
return;
|
|
580
559
|
}
|
|
581
|
-
if (record.hasProgress) observer.progress(record.latestProgress);
|
|
582
560
|
});
|
|
583
561
|
}
|
|
584
562
|
|
|
@@ -797,8 +775,8 @@ async function decodeSharedTranscriptionPayload(
|
|
|
797
775
|
mono[index] = sample;
|
|
798
776
|
}
|
|
799
777
|
throwIfAborted(signal, cancellationReason);
|
|
800
|
-
return
|
|
801
|
-
payload:
|
|
778
|
+
return completeValue({
|
|
779
|
+
payload: completeValue({ audio: mono, sampleRate }),
|
|
802
780
|
shared: true,
|
|
803
781
|
});
|
|
804
782
|
}
|
|
@@ -847,8 +825,8 @@ function cloneNativeTranscriptionPayload(payload, authority) {
|
|
|
847
825
|
);
|
|
848
826
|
}
|
|
849
827
|
}
|
|
850
|
-
return
|
|
851
|
-
payload:
|
|
828
|
+
return completeValue({
|
|
829
|
+
payload: completeValue({
|
|
852
830
|
audio: new Float32Array(descriptors.audio.value),
|
|
853
831
|
sampleRate,
|
|
854
832
|
}),
|
|
@@ -892,15 +870,13 @@ function normalizeSynthesisPayload(payload, authority) {
|
|
|
892
870
|
assertPayloadModel(payload, authority, { operationSubject: "tts-synthesis" });
|
|
893
871
|
textValue = descriptors.text.value;
|
|
894
872
|
}
|
|
895
|
-
const text = typeof textValue === "string" ? textValue
|
|
873
|
+
const text = typeof textValue === "string" ? textValue : "";
|
|
896
874
|
const voiceValue = Object.hasOwn(descriptors, "voice")
|
|
897
875
|
? descriptors.voice.value
|
|
898
876
|
: authority.defaultVoice;
|
|
899
|
-
const voice = typeof voiceValue === "string"
|
|
900
|
-
? voiceValue.trim()
|
|
901
|
-
: "";
|
|
877
|
+
const voice = typeof voiceValue === "string" ? voiceValue : "";
|
|
902
878
|
const speed = Object.hasOwn(descriptors, "speed") ? descriptors.speed.value : 1;
|
|
903
|
-
if (!text) {
|
|
879
|
+
if (!text.trim()) {
|
|
904
880
|
throw providerError(
|
|
905
881
|
"ARCANE_AI_INVALID_REQUEST",
|
|
906
882
|
"Kokoro requires nonempty text.",
|
|
@@ -908,7 +884,7 @@ function normalizeSynthesisPayload(payload, authority) {
|
|
|
908
884
|
"tts-synthesis-text-empty",
|
|
909
885
|
);
|
|
910
886
|
}
|
|
911
|
-
if (!voice) {
|
|
887
|
+
if (!voice.trim()) {
|
|
912
888
|
throw providerError(
|
|
913
889
|
"ARCANE_AI_INVALID_REQUEST",
|
|
914
890
|
"Kokoro requires a nonempty voice id.",
|
|
@@ -916,12 +892,12 @@ function normalizeSynthesisPayload(payload, authority) {
|
|
|
916
892
|
"tts-synthesis-voice-empty",
|
|
917
893
|
);
|
|
918
894
|
}
|
|
919
|
-
if (!Number.isFinite(speed) || speed <= 0
|
|
895
|
+
if (!Number.isFinite(speed) || speed <= 0) {
|
|
920
896
|
throw providerError(
|
|
921
897
|
"ARCANE_AI_INVALID_REQUEST",
|
|
922
|
-
"Kokoro speed must be greater than 0
|
|
898
|
+
"Kokoro speed must be greater than 0.",
|
|
923
899
|
undefined,
|
|
924
|
-
"tts-synthesis-speed-
|
|
900
|
+
"tts-synthesis-speed-not-positive",
|
|
925
901
|
);
|
|
926
902
|
}
|
|
927
903
|
if (authority.graph
|
|
@@ -933,8 +909,8 @@ function normalizeSynthesisPayload(payload, authority) {
|
|
|
933
909
|
"tts-synthesis-voice-not-declared",
|
|
934
910
|
);
|
|
935
911
|
}
|
|
936
|
-
return
|
|
937
|
-
payload:
|
|
912
|
+
return completeValue({
|
|
913
|
+
payload: completeValue({ text, voice, speed }),
|
|
938
914
|
shared,
|
|
939
915
|
});
|
|
940
916
|
}
|
|
@@ -1054,7 +1030,7 @@ function encodeSharedSynthesisResult(result, authority) {
|
|
|
1054
1030
|
true,
|
|
1055
1031
|
);
|
|
1056
1032
|
}
|
|
1057
|
-
return
|
|
1033
|
+
return completeValue({ audio: bytes, contentType: "audio/wav" });
|
|
1058
1034
|
}
|
|
1059
1035
|
|
|
1060
1036
|
function createBrowserSpeechProvider({
|
|
@@ -1079,35 +1055,23 @@ function createBrowserSpeechProvider({
|
|
|
1079
1055
|
if (!isDbopfsSpeechArtifactStore(store)) {
|
|
1080
1056
|
throw new TypeError("Browser speech providers require an SDK-created DBOPFS artifact store.");
|
|
1081
1057
|
}
|
|
1082
|
-
const configuredAppSecurity = normalizeModelSecurity(
|
|
1083
|
-
appSecurity,
|
|
1084
|
-
"Browser speech app security",
|
|
1085
|
-
);
|
|
1086
|
-
const configuredProviderSecurity = normalizeModelSecurity(
|
|
1087
|
-
security,
|
|
1088
|
-
"Browser speech provider security",
|
|
1089
|
-
);
|
|
1090
1058
|
const authority = createProviderAuthority({
|
|
1091
1059
|
providerId,
|
|
1092
1060
|
role,
|
|
1093
1061
|
graph,
|
|
1094
1062
|
model,
|
|
1095
1063
|
runtime,
|
|
1096
|
-
security: configuredProviderSecurity,
|
|
1097
|
-
});
|
|
1098
|
-
const defaultSecurity = resolveModelSecurity({
|
|
1099
|
-
app: configuredAppSecurity,
|
|
1100
|
-
binding: configuredProviderSecurity,
|
|
1101
1064
|
});
|
|
1065
|
+
const defaultSecureIntent = reportedSecureIntent(appSecurity, security);
|
|
1102
1066
|
const operation = ROLE_OPERATION[role];
|
|
1103
1067
|
const speech = role === "stt"
|
|
1104
|
-
?
|
|
1105
|
-
:
|
|
1068
|
+
? completeValue({ inputSampleRate: inputSampleRate(authority) })
|
|
1069
|
+
: completeValue({
|
|
1106
1070
|
outputSampleRate: outputSampleRate(authority),
|
|
1107
|
-
responseFormats:
|
|
1071
|
+
responseFormats: completeValue([TTS_RESPONSE_FORMAT]),
|
|
1108
1072
|
defaultResponseFormat: TTS_RESPONSE_FORMAT,
|
|
1109
1073
|
});
|
|
1110
|
-
const catalogEntry =
|
|
1074
|
+
const catalogEntry = completeValue({
|
|
1111
1075
|
id: authority.modelId,
|
|
1112
1076
|
providerId,
|
|
1113
1077
|
role,
|
|
@@ -1119,7 +1083,6 @@ function createBrowserSpeechProvider({
|
|
|
1119
1083
|
runtime: authority.runtime,
|
|
1120
1084
|
files: authority.files,
|
|
1121
1085
|
...(authority.graph ? {
|
|
1122
|
-
artifactGraphId: authority.artifactGraphId,
|
|
1123
1086
|
voices: authority.voices,
|
|
1124
1087
|
} : {}),
|
|
1125
1088
|
speech,
|
|
@@ -1127,7 +1090,6 @@ function createBrowserSpeechProvider({
|
|
|
1127
1090
|
let state = "unloaded";
|
|
1128
1091
|
let errorCode = null;
|
|
1129
1092
|
let cache = null;
|
|
1130
|
-
let artifactGraphAdmission = null;
|
|
1131
1093
|
let lifecycleReason = `${role}-provider-created`;
|
|
1132
1094
|
let activeOperation = null;
|
|
1133
1095
|
let generation = 0;
|
|
@@ -1137,18 +1099,9 @@ function createBrowserSpeechProvider({
|
|
|
1137
1099
|
let disposeOperation = null;
|
|
1138
1100
|
let requestOperation = null;
|
|
1139
1101
|
let lastWarnings = NO_PROVIDER_WARNINGS;
|
|
1140
|
-
let
|
|
1141
|
-
security: defaultSecurity,
|
|
1142
|
-
integrity: providerIntegrity(defaultSecurity),
|
|
1143
|
-
});
|
|
1102
|
+
let secureIntent = defaultSecureIntent;
|
|
1144
1103
|
|
|
1145
1104
|
function status() {
|
|
1146
|
-
const effectiveSecurity = active?.security
|
|
1147
|
-
?? loadOperation?.security
|
|
1148
|
-
?? lastIntegrity.security;
|
|
1149
|
-
const integrity = active?.integrity
|
|
1150
|
-
?? loadOperation?.integrity
|
|
1151
|
-
?? lastIntegrity.integrity;
|
|
1152
1105
|
const runtimeWarnings = active?.warnings
|
|
1153
1106
|
?? loadOperation?.warnings
|
|
1154
1107
|
?? lastWarnings;
|
|
@@ -1163,10 +1116,8 @@ function createBrowserSpeechProvider({
|
|
|
1163
1116
|
cache,
|
|
1164
1117
|
lifecycleReason,
|
|
1165
1118
|
activeOperation,
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
integrity,
|
|
1169
|
-
warnings: providerWarnings(effectiveSecurity, runtimeWarnings),
|
|
1119
|
+
secureIntent,
|
|
1120
|
+
warnings: providerWarnings(runtimeWarnings),
|
|
1170
1121
|
});
|
|
1171
1122
|
}
|
|
1172
1123
|
|
|
@@ -1207,7 +1158,7 @@ function createBrowserSpeechProvider({
|
|
|
1207
1158
|
localOnly: true,
|
|
1208
1159
|
|
|
1209
1160
|
catalog() {
|
|
1210
|
-
return
|
|
1161
|
+
return completeValue([catalogEntry]);
|
|
1211
1162
|
},
|
|
1212
1163
|
|
|
1213
1164
|
inspect(selection, context = {}) {
|
|
@@ -1219,14 +1170,14 @@ function createBrowserSpeechProvider({
|
|
|
1219
1170
|
&& (!Object.hasOwn(selection ?? {}, "role") || selection.role === role)
|
|
1220
1171
|
&& selection?.localOnly !== false;
|
|
1221
1172
|
if (!available) {
|
|
1222
|
-
return
|
|
1173
|
+
return completeValue({
|
|
1223
1174
|
available: false,
|
|
1224
1175
|
code: "ARCANE_AI_MODEL_AUTHORITY_REQUIRED",
|
|
1225
1176
|
message: "The selected browser speech model does not match this provider authority.",
|
|
1226
1177
|
reason: `${role}-provider-inspection-selection-authority-mismatch`,
|
|
1227
1178
|
});
|
|
1228
1179
|
}
|
|
1229
|
-
return
|
|
1180
|
+
return completeValue({ available: true, authority });
|
|
1230
1181
|
},
|
|
1231
1182
|
|
|
1232
1183
|
status,
|
|
@@ -1285,10 +1236,10 @@ function createBrowserSpeechProvider({
|
|
|
1285
1236
|
} catch (error) {
|
|
1286
1237
|
return Promise.reject(trustedLoadFailure(error, role));
|
|
1287
1238
|
}
|
|
1288
|
-
if (typeof loadProgress !== "function") {
|
|
1239
|
+
if (loadProgress !== undefined && typeof loadProgress !== "function") {
|
|
1289
1240
|
return Promise.reject(providerError(
|
|
1290
1241
|
"ARCANE_AI_INVALID_REQUEST",
|
|
1291
|
-
"Browser speech load progress must be a function.",
|
|
1242
|
+
"Browser speech load progress must be a function when supplied.",
|
|
1292
1243
|
undefined,
|
|
1293
1244
|
`${role}-load-progress-callback-not-function`,
|
|
1294
1245
|
));
|
|
@@ -1304,54 +1255,20 @@ function createBrowserSpeechProvider({
|
|
|
1304
1255
|
`${role}-load-selection-authority-mismatch`,
|
|
1305
1256
|
));
|
|
1306
1257
|
}
|
|
1307
|
-
|
|
1258
|
+
secureIntent = reportedSecureIntent(appSecurity, security, context.security);
|
|
1308
1259
|
try {
|
|
1309
|
-
if (authority.graph) {
|
|
1310
|
-
effectiveSecurity = resolveModelSecurity({
|
|
1311
|
-
app: graphSecurity(
|
|
1312
|
-
configuredAppSecurity,
|
|
1313
|
-
"Browser speech graph app security",
|
|
1314
|
-
),
|
|
1315
|
-
binding: graphSecurity(
|
|
1316
|
-
configuredProviderSecurity,
|
|
1317
|
-
"Browser speech graph provider security",
|
|
1318
|
-
),
|
|
1319
|
-
load: graphSecurity(
|
|
1320
|
-
context.security,
|
|
1321
|
-
"Browser speech graph load security",
|
|
1322
|
-
),
|
|
1323
|
-
});
|
|
1324
|
-
} else {
|
|
1325
|
-
effectiveSecurity = resolveModelSecurity({
|
|
1326
|
-
app: configuredAppSecurity,
|
|
1327
|
-
binding: configuredProviderSecurity,
|
|
1328
|
-
load: context.security,
|
|
1329
|
-
});
|
|
1330
|
-
}
|
|
1331
1260
|
throwIfAborted(loadSignal, `${role}-load-cancelled`);
|
|
1332
1261
|
} catch (error) {
|
|
1333
1262
|
return Promise.reject(trustedLoadFailure(error, role));
|
|
1334
1263
|
}
|
|
1335
1264
|
if (state === "ready" && active) {
|
|
1336
|
-
|
|
1337
|
-
return Promise.resolve(status());
|
|
1338
|
-
}
|
|
1339
|
-
lifecycleReason = `${role}-load-superseded-by-security-change`;
|
|
1340
|
-
return provider.unload().then(() => provider.load(context));
|
|
1265
|
+
return Promise.resolve(status());
|
|
1341
1266
|
}
|
|
1342
1267
|
if (loadOperation) {
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
}, role);
|
|
1348
|
-
}
|
|
1349
|
-
lifecycleReason = `${role}-load-superseded-by-security-change`;
|
|
1350
|
-
loadOperation.abort(
|
|
1351
|
-
`${role}-load-superseded-by-security-change`,
|
|
1352
|
-
"ARCANE_AI_OPERATION_SUPERSEDED",
|
|
1353
|
-
);
|
|
1354
|
-
return loadOperation.promise.catch(() => undefined).then(() => provider.load(context));
|
|
1268
|
+
return observeLoadOperation(loadOperation, {
|
|
1269
|
+
signal: loadSignal,
|
|
1270
|
+
progress: loadProgress,
|
|
1271
|
+
}, role);
|
|
1355
1272
|
}
|
|
1356
1273
|
generation += 1;
|
|
1357
1274
|
const operationGeneration = generation;
|
|
@@ -1362,27 +1279,14 @@ function createBrowserSpeechProvider({
|
|
|
1362
1279
|
errorCode = null;
|
|
1363
1280
|
const record = {
|
|
1364
1281
|
promise: null,
|
|
1365
|
-
security: effectiveSecurity,
|
|
1366
|
-
integrity: providerIntegrity(effectiveSecurity),
|
|
1367
1282
|
warnings: NO_PROVIDER_WARNINGS,
|
|
1368
1283
|
observers: new Set(),
|
|
1369
1284
|
settled: false,
|
|
1370
|
-
hasProgress: false,
|
|
1371
|
-
latestProgress: null,
|
|
1372
1285
|
abort: (
|
|
1373
1286
|
reason = `${role}-load-cancelled`,
|
|
1374
1287
|
code = "ARCANE_AI_REQUEST_ABORTED",
|
|
1375
1288
|
) => linked.abort(reason, code),
|
|
1376
|
-
publishProgress(progress) {
|
|
1377
|
-
record.latestProgress = progress;
|
|
1378
|
-
record.hasProgress = true;
|
|
1379
|
-
for (const observer of [...record.observers]) observer.progress(progress);
|
|
1380
|
-
},
|
|
1381
1289
|
};
|
|
1382
|
-
lastIntegrity = Object.freeze({
|
|
1383
|
-
security: effectiveSecurity,
|
|
1384
|
-
integrity: record.integrity,
|
|
1385
|
-
});
|
|
1386
1290
|
lastWarnings = NO_PROVIDER_WARNINGS;
|
|
1387
1291
|
const promise = Promise.resolve().then(async () => {
|
|
1388
1292
|
let prepared = null;
|
|
@@ -1390,18 +1294,11 @@ function createBrowserSpeechProvider({
|
|
|
1390
1294
|
try {
|
|
1391
1295
|
prepared = await store.prepare(authority.graph ?? authority, {
|
|
1392
1296
|
signal: linked.controller.signal,
|
|
1393
|
-
onProgress: record.publishProgress,
|
|
1394
1297
|
offline,
|
|
1395
|
-
security: effectiveSecurity,
|
|
1396
1298
|
});
|
|
1397
|
-
record.integrity = providerIntegrity(effectiveSecurity, "verified");
|
|
1398
1299
|
record.warnings = Array.isArray(prepared.warnings)
|
|
1399
|
-
?
|
|
1300
|
+
? completeValue([...prepared.warnings])
|
|
1400
1301
|
: NO_PROVIDER_WARNINGS;
|
|
1401
|
-
lastIntegrity = Object.freeze({
|
|
1402
|
-
security: effectiveSecurity,
|
|
1403
|
-
integrity: record.integrity,
|
|
1404
|
-
});
|
|
1405
1302
|
lastWarnings = record.warnings;
|
|
1406
1303
|
throwIfAborted(
|
|
1407
1304
|
linked.controller.signal,
|
|
@@ -1412,17 +1309,13 @@ function createBrowserSpeechProvider({
|
|
|
1412
1309
|
"ARCANE_AI_OPERATION_SUPERSEDED",
|
|
1413
1310
|
"Browser speech loading was superseded.",
|
|
1414
1311
|
undefined,
|
|
1415
|
-
|
|
1416
|
-
? lifecycleReason
|
|
1417
|
-
: `${role}-load-superseded-by-unload`,
|
|
1312
|
+
`${role}-load-superseded-by-unload`,
|
|
1418
1313
|
);
|
|
1419
1314
|
}
|
|
1420
1315
|
slot = {
|
|
1421
1316
|
prepared,
|
|
1422
1317
|
released: false,
|
|
1423
1318
|
client: null,
|
|
1424
|
-
security: effectiveSecurity,
|
|
1425
|
-
integrity: record.integrity,
|
|
1426
1319
|
warnings: record.warnings,
|
|
1427
1320
|
};
|
|
1428
1321
|
slot.client = createSpeechWorkerClient({
|
|
@@ -1443,7 +1336,6 @@ function createBrowserSpeechProvider({
|
|
|
1443
1336
|
? `${role}-worker-protocol-mismatch`
|
|
1444
1337
|
: `${role}-worker-crashed`);
|
|
1445
1338
|
activeOperation = null;
|
|
1446
|
-
artifactGraphAdmission = null;
|
|
1447
1339
|
}
|
|
1448
1340
|
try {
|
|
1449
1341
|
releaseSlot(slot);
|
|
@@ -1454,7 +1346,6 @@ function createBrowserSpeechProvider({
|
|
|
1454
1346
|
errorCode = releaseFailure.code;
|
|
1455
1347
|
lifecycleReason = releaseFailure.reason;
|
|
1456
1348
|
activeOperation = null;
|
|
1457
|
-
artifactGraphAdmission = null;
|
|
1458
1349
|
}
|
|
1459
1350
|
throw releaseFailure;
|
|
1460
1351
|
}
|
|
@@ -1469,19 +1360,16 @@ function createBrowserSpeechProvider({
|
|
|
1469
1360
|
`${role}-worker-client-authority-mismatch`,
|
|
1470
1361
|
);
|
|
1471
1362
|
}
|
|
1472
|
-
const configuration =
|
|
1363
|
+
const configuration = completeValue({
|
|
1473
1364
|
role,
|
|
1474
1365
|
runtime: prepared.runtime,
|
|
1475
1366
|
model: prepared.model,
|
|
1476
|
-
security: effectiveSecurity,
|
|
1477
1367
|
...(authority.graph ? {
|
|
1478
|
-
artifactGraphId: authority.artifactGraphId,
|
|
1479
1368
|
artifactGraphProtocol: authority.graph.protocol,
|
|
1480
1369
|
} : {}),
|
|
1481
1370
|
});
|
|
1482
1371
|
await slot.client.request("load", { configuration }, {
|
|
1483
1372
|
signal: linked.controller.signal,
|
|
1484
|
-
progress: record.publishProgress,
|
|
1485
1373
|
});
|
|
1486
1374
|
throwIfAborted(
|
|
1487
1375
|
linked.controller.signal,
|
|
@@ -1492,14 +1380,11 @@ function createBrowserSpeechProvider({
|
|
|
1492
1380
|
"ARCANE_AI_OPERATION_SUPERSEDED",
|
|
1493
1381
|
"Browser speech loading was superseded.",
|
|
1494
1382
|
undefined,
|
|
1495
|
-
|
|
1496
|
-
? lifecycleReason
|
|
1497
|
-
: `${role}-load-superseded-by-unload`,
|
|
1383
|
+
`${role}-load-superseded-by-unload`,
|
|
1498
1384
|
);
|
|
1499
1385
|
}
|
|
1500
1386
|
active = slot;
|
|
1501
1387
|
cache = prepared.cache;
|
|
1502
|
-
artifactGraphAdmission = prepared.artifactGraphAdmission ?? null;
|
|
1503
1388
|
state = "ready";
|
|
1504
1389
|
lifecycleReason = `${role}-load-completed`;
|
|
1505
1390
|
activeOperation = null;
|
|
@@ -1508,13 +1393,6 @@ function createBrowserSpeechProvider({
|
|
|
1508
1393
|
let failure = linked.controller.signal.aborted
|
|
1509
1394
|
? linkedAbortFailure(linked, `${role}-load-cancelled`)
|
|
1510
1395
|
: trustedLoadFailure(error, role);
|
|
1511
|
-
if (!linked.controller.signal.aborted && prepared === null) {
|
|
1512
|
-
record.integrity = providerIntegrity(effectiveSecurity, "failed");
|
|
1513
|
-
lastIntegrity = Object.freeze({
|
|
1514
|
-
security: effectiveSecurity,
|
|
1515
|
-
integrity: record.integrity,
|
|
1516
|
-
});
|
|
1517
|
-
}
|
|
1518
1396
|
try {
|
|
1519
1397
|
if (slot) await terminateSlot(slot, failure);
|
|
1520
1398
|
else prepared?.release();
|
|
@@ -1644,7 +1522,7 @@ function createBrowserSpeechProvider({
|
|
|
1644
1522
|
? encodeSharedSynthesisResult(result, authority)
|
|
1645
1523
|
: result;
|
|
1646
1524
|
})();
|
|
1647
|
-
requestOperation =
|
|
1525
|
+
requestOperation = completeValue({
|
|
1648
1526
|
promise,
|
|
1649
1527
|
abort: (
|
|
1650
1528
|
reason = ROLE_REQUEST_REASON[role],
|
|
@@ -1673,7 +1551,6 @@ function createBrowserSpeechProvider({
|
|
|
1673
1551
|
failure = trustedWorkerFailure(cleanupError, role);
|
|
1674
1552
|
}
|
|
1675
1553
|
state = "unloaded";
|
|
1676
|
-
artifactGraphAdmission = null;
|
|
1677
1554
|
}
|
|
1678
1555
|
lifecycleReason = failure?.reason
|
|
1679
1556
|
?? (failure?.code === "ARCANE_AI_REQUEST_ABORTED"
|
|
@@ -1746,7 +1623,6 @@ function createBrowserSpeechProvider({
|
|
|
1746
1623
|
`${role}-worker-terminated-by-unload`,
|
|
1747
1624
|
));
|
|
1748
1625
|
cache = null;
|
|
1749
|
-
artifactGraphAdmission = null;
|
|
1750
1626
|
state = "unloaded";
|
|
1751
1627
|
lifecycleReason = `${role}-unload-completed`;
|
|
1752
1628
|
activeOperation = null;
|
|
@@ -1813,7 +1689,7 @@ function createBrowserSpeechProvider({
|
|
|
1813
1689
|
);
|
|
1814
1690
|
},
|
|
1815
1691
|
};
|
|
1816
|
-
return
|
|
1692
|
+
return completeValue(provider);
|
|
1817
1693
|
}
|
|
1818
1694
|
|
|
1819
1695
|
export function createBrowserWhisperProvider(options = {}) {
|