arcane-os 0.76.0 → 0.78.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +36 -0
- package/README.md +1 -1
- package/browser-runtime/ai/dbopfs-model-resources.mjs +1 -1
- package/browser-runtime/ai/speech-worker-runtime.mjs +79 -3
- package/docs/reference/ai/browser-speech.md +144 -5
- package/docs/reference/inventory/package-api.json +1 -1
- package/docs/reference/inventory/runtime-modules.json +10 -8
- package/docs/reference/runtime-modules.md +114 -14
- package/package.json +2 -2
- package/runtime/arcane/modules/AI.js +346 -31
- package/runtime/arcane/modules/DBOPFS.js +24 -6
- package/runtime/arcane/modules/PreparedSpeech.js +111 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,41 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.78.0
|
|
4
|
+
|
|
5
|
+
- Add `AI.readPreparedTTS()` and shared `readPreparedSpeech()` for complete
|
|
6
|
+
cache-only narration lookup. Return a ready playback handle or `null` without
|
|
7
|
+
loading a model, unmuting, synthesizing, writing storage or repairing partial
|
|
8
|
+
audio. Preserve complete semantic matching and genuine storage errors.
|
|
9
|
+
- Keep lookup cancellation independent from a successfully returned handle.
|
|
10
|
+
Replay uses the existing prepared-playback owner and can borrow the caller's
|
|
11
|
+
silent reusable audio output; no autoplay permission is implied.
|
|
12
|
+
- Add optional existing-only DBOPFS table/file reads while preserving ordinary
|
|
13
|
+
lazy-creation defaults. Cache-only lookup and later audio reads leave missing
|
|
14
|
+
tables absent.
|
|
15
|
+
- Give Kokoro voice caching a real Worker-lifetime response cache after runtime
|
|
16
|
+
import, while DBOPFS remains the durable model/resource owner. Preserve saved
|
|
17
|
+
native caches and clear transient responses on unload.
|
|
18
|
+
- Report a complete stored model resource as cached loading rather than a new
|
|
19
|
+
network download. This release changes no model selection, native engine or
|
|
20
|
+
physical-device routing and makes no live-model execution claim.
|
|
21
|
+
|
|
22
|
+
## 0.77.0
|
|
23
|
+
|
|
24
|
+
- Add `AI.prepareAudioOutput()` for synchronous, silent preparation of a reusable
|
|
25
|
+
WebAudio output. Observe its actual context state through immediate subscription
|
|
26
|
+
replay and `AI_AUDIO_OUTPUT_STATE_EVENT`; resume and dispose explicitly.
|
|
27
|
+
- Add optional `audioOutput` to `playPreparedTTS()` so prepared narration borrows
|
|
28
|
+
that same output across plays. Completion and stop leave the borrowed context
|
|
29
|
+
open; omission preserves the existing per-playback context lifecycle.
|
|
30
|
+
- Keep replacement, pending suspension, decoding, cancellation and disposal tied
|
|
31
|
+
to their actual output and playback owners. Cleanup failures remain observable
|
|
32
|
+
and failed context closure can be retried. Independent native speech, saved
|
|
33
|
+
audio and detached preparation retain their existing ownership.
|
|
34
|
+
- Preparation plays no sound, creates no audio source, resumes no context,
|
|
35
|
+
changes no mute setting and loads no model. A running WebAudio context is not
|
|
36
|
+
a guarantee of future autoplay, native speech or HTML audio readiness. No
|
|
37
|
+
physical-GPU placement or native-engine change is included.
|
|
38
|
+
|
|
3
39
|
## 0.76.0
|
|
4
40
|
|
|
5
41
|
- Add public `textFormat:'plain'` to the `SpeechPlayback` constructor and
|
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ selected SDK projection or an explicit live SDK source mount. Both profiles
|
|
|
20
20
|
share the theme, packaging, event, cancellation,
|
|
21
21
|
and browser run contracts while retaining their selected application layout.
|
|
22
22
|
|
|
23
|
-
This checkout defines the `0.
|
|
23
|
+
This checkout defines the `0.78.0` SDK contract. Development applications declare
|
|
24
24
|
`arcane-os` as `latest` and retain their lockfile. Exact SDK pins begin when Roshi
|
|
25
25
|
designates a production build. Follow [Consumer updates](docs/consumer-updates.md)
|
|
26
26
|
to refresh and compare the installed SDK and lockfile with the published channel;
|
|
@@ -96,7 +96,7 @@ export function createDbopfsResourceStore({dbopfs, tableName, fetchImpl = global
|
|
|
96
96
|
const file = await parts.read(entry.name, {signal});
|
|
97
97
|
if (file) {
|
|
98
98
|
const state = await parts.state(entry.name, {signal});
|
|
99
|
-
onProgress?.({phase: '
|
|
99
|
+
onProgress?.({phase: 'load', message: 'Reading stored model resource', completed: state.parts.length,
|
|
100
100
|
total: state.parts.length, unit: 'shards', cached: true, url: request.url});
|
|
101
101
|
return {...entry.metadata, file};
|
|
102
102
|
}
|
|
@@ -997,6 +997,7 @@ function createOrdinaryArtifactModuleRouter(
|
|
|
997
997
|
role,
|
|
998
998
|
reader,
|
|
999
999
|
originalWorker,
|
|
1000
|
+
responseCaches,
|
|
1000
1001
|
) {
|
|
1001
1002
|
const nestedWorkers = new Set();
|
|
1002
1003
|
const router = completeValue({
|
|
@@ -1015,8 +1016,8 @@ function createOrdinaryArtifactModuleRouter(
|
|
|
1015
1016
|
return reader.fetchResource(input, init, modulePath);
|
|
1016
1017
|
},
|
|
1017
1018
|
|
|
1018
|
-
|
|
1019
|
-
|
|
1019
|
+
openCache(modulePath, name) {
|
|
1020
|
+
return responseCaches.open(name);
|
|
1020
1021
|
},
|
|
1021
1022
|
|
|
1022
1023
|
createWorker(modulePath, specifier, options = {}) {
|
|
@@ -1143,6 +1144,63 @@ function installOrdinaryArtifactModuleRouter(scope, configuration, role, send, s
|
|
|
1143
1144
|
const resourceClient = createModelResourceClient({ send });
|
|
1144
1145
|
let requestSignal = null;
|
|
1145
1146
|
let reportProgress = null;
|
|
1147
|
+
let cacheClosed = false;
|
|
1148
|
+
const responseCacheEntries = new Map();
|
|
1149
|
+
const responseCaches = {
|
|
1150
|
+
async open(name) {
|
|
1151
|
+
assertCacheActive(requestSignal);
|
|
1152
|
+
const cacheName = String(name);
|
|
1153
|
+
let cache = responseCacheEntries.get(cacheName);
|
|
1154
|
+
if (!cache) {
|
|
1155
|
+
const responses = new Map();
|
|
1156
|
+
cache = {
|
|
1157
|
+
responses,
|
|
1158
|
+
api: {
|
|
1159
|
+
async match(input) {
|
|
1160
|
+
assertCacheActive(requestSignal);
|
|
1161
|
+
const stored = responses.get(cacheRequestKey(input));
|
|
1162
|
+
if (!stored) return undefined;
|
|
1163
|
+
const response = new Response(stored.body, stored);
|
|
1164
|
+
Object.defineProperties(response, {
|
|
1165
|
+
url: { value: stored.url },
|
|
1166
|
+
redirected: { value: stored.redirected },
|
|
1167
|
+
type: { value: stored.type },
|
|
1168
|
+
});
|
|
1169
|
+
return response;
|
|
1170
|
+
},
|
|
1171
|
+
async put(input, response) {
|
|
1172
|
+
const signal = requestSignal;
|
|
1173
|
+
assertCacheActive(signal);
|
|
1174
|
+
const key = cacheRequestKey(input);
|
|
1175
|
+
const copy = response.clone();
|
|
1176
|
+
const body = await copy.blob();
|
|
1177
|
+
assertCacheActive(signal);
|
|
1178
|
+
responses.set(key, {
|
|
1179
|
+
body,
|
|
1180
|
+
status: response.status,
|
|
1181
|
+
statusText: response.statusText,
|
|
1182
|
+
headers: [...response.headers],
|
|
1183
|
+
url: response.url,
|
|
1184
|
+
redirected: response.redirected,
|
|
1185
|
+
type: response.type,
|
|
1186
|
+
});
|
|
1187
|
+
},
|
|
1188
|
+
},
|
|
1189
|
+
};
|
|
1190
|
+
responseCacheEntries.set(cacheName, cache);
|
|
1191
|
+
}
|
|
1192
|
+
return cache.api;
|
|
1193
|
+
},
|
|
1194
|
+
};
|
|
1195
|
+
function assertCacheActive(signal) {
|
|
1196
|
+
throwIfAborted(signal, 'speech-resource-cache-cancelled');
|
|
1197
|
+
if (cacheClosed) throw new Error('The speech Worker response cache was unloaded.');
|
|
1198
|
+
}
|
|
1199
|
+
function cacheRequestKey(input) {
|
|
1200
|
+
const RequestConstructor = scope.Request ?? Request;
|
|
1201
|
+
const request = new RequestConstructor(input);
|
|
1202
|
+
return JSON.stringify([request.method, request.url]);
|
|
1203
|
+
}
|
|
1146
1204
|
if (!is.function(originalFetch)) {
|
|
1147
1205
|
throw workerError(
|
|
1148
1206
|
"ARCANE_AI_PROVIDER_UNAVAILABLE",
|
|
@@ -1166,8 +1224,9 @@ function installOrdinaryArtifactModuleRouter(scope, configuration, role, send, s
|
|
|
1166
1224
|
role,
|
|
1167
1225
|
reader,
|
|
1168
1226
|
originalWorker,
|
|
1227
|
+
responseCaches,
|
|
1169
1228
|
);
|
|
1170
|
-
const originalProperties = ['fetch', 'Worker', MODULE_ROUTER_NAME].map(
|
|
1229
|
+
const originalProperties = ['fetch', 'Worker', 'caches', MODULE_ROUTER_NAME].map(
|
|
1171
1230
|
function originalWorkerProperty(name) {
|
|
1172
1231
|
return { name, descriptor: Object.getOwnPropertyDescriptor(scope, name) };
|
|
1173
1232
|
},
|
|
@@ -1240,7 +1299,22 @@ function installOrdinaryArtifactModuleRouter(scope, configuration, role, send, s
|
|
|
1240
1299
|
receive: resourceClient.receive,
|
|
1241
1300
|
setSignal(signal) { requestSignal = signal; },
|
|
1242
1301
|
setProgress(report) { reportProgress = report; },
|
|
1302
|
+
enableResponseCache() {
|
|
1303
|
+
// Transformers captures cache capability during import. Kokoro then uses
|
|
1304
|
+
// this real, Worker-lifetime match/put cache for voices; fetch remains the
|
|
1305
|
+
// sole route to durable DBOPFS resources. No native saved cache is touched.
|
|
1306
|
+
assertCacheActive(requestSignal);
|
|
1307
|
+
Object.defineProperty(scope, 'caches', {
|
|
1308
|
+
configurable: true,
|
|
1309
|
+
enumerable: true,
|
|
1310
|
+
writable: true,
|
|
1311
|
+
value: responseCaches,
|
|
1312
|
+
});
|
|
1313
|
+
},
|
|
1243
1314
|
async cleanup() {
|
|
1315
|
+
cacheClosed = true;
|
|
1316
|
+
for (const cache of responseCacheEntries.values()) cache.responses.clear();
|
|
1317
|
+
responseCacheEntries.clear();
|
|
1244
1318
|
resourceClient.close(new Error('The speech Worker resource owner was unloaded.'));
|
|
1245
1319
|
let cleanupFailure = null;
|
|
1246
1320
|
try {
|
|
@@ -1773,6 +1847,7 @@ export function createSpeechWorkerRuntime({ role, scope = globalThis, send } = {
|
|
|
1773
1847
|
);
|
|
1774
1848
|
const namespace = await import(entry.moduleUrl);
|
|
1775
1849
|
throwIfAborted(signal, `${role}-load-cancelled`);
|
|
1850
|
+
environment.enableResponseCache();
|
|
1776
1851
|
restoreNamespace = configureRuntimeNamespace(
|
|
1777
1852
|
namespace,
|
|
1778
1853
|
configuration,
|
|
@@ -2071,6 +2146,7 @@ export function installBrowserSpeechArtifactModuleWorker(role, scope = globalThi
|
|
|
2071
2146
|
target?.sourceUrl ?? request.targetUrl,
|
|
2072
2147
|
);
|
|
2073
2148
|
await import(target?.moduleUrl ?? request.targetUrl);
|
|
2149
|
+
environment.enableResponseCache();
|
|
2074
2150
|
scope.removeEventListener("message", bootstrap);
|
|
2075
2151
|
await new Promise((resolve) => queueMicrotask(resolve));
|
|
2076
2152
|
for (const queuedEvent of queued.splice(0)) replayWorkerMessage(scope, queuedEvent);
|
|
@@ -727,6 +727,54 @@ pause delays the next queued audio on the existing `AudioContext` clock; it
|
|
|
727
727
|
does not delay the preceding promise after that passage's last buffer ends.
|
|
728
728
|
The promise is a playback result, not a listener acknowledgement.
|
|
729
729
|
|
|
730
|
+
## Read stored narration without starting generation
|
|
731
|
+
|
|
732
|
+
`await ai.readPreparedTTS({parts,textFormat,storage,identity,signal,onState})`
|
|
733
|
+
returns a complete prepared handle or `null`. It uses the same complete inputs,
|
|
734
|
+
selected speech configuration, formatting treatment, segmentation, storage key,
|
|
735
|
+
and semantic `identity` as `prepareTTS()`. Supply the same options used to
|
|
736
|
+
prepare the audio. A changed selection or input is a cache miss, not permission
|
|
737
|
+
to generate replacement audio.
|
|
738
|
+
|
|
739
|
+
This operation never activates or unmutes a provider, synthesizes speech,
|
|
740
|
+
creates stored records, repairs partial audio, or joins pending generation.
|
|
741
|
+
With `storage`, it reads the existing manifest and every ordered audio file.
|
|
742
|
+
Missing or incomplete audio returns `null`; unreadable metadata and other
|
|
743
|
+
storage errors reject. Without storage, it finds only already-complete audio
|
|
744
|
+
retained by this same AI instance. A miss leaves all existing data unchanged.
|
|
745
|
+
|
|
746
|
+
```javascript
|
|
747
|
+
async function readNarration(text, key, signal) {
|
|
748
|
+
return ai.readPreparedTTS({
|
|
749
|
+
parts: [{
|
|
750
|
+
input: text,
|
|
751
|
+
voice: speechSelection.model.defaultVoice,
|
|
752
|
+
speed: 1,
|
|
753
|
+
pauseAfterMs: 0
|
|
754
|
+
}],
|
|
755
|
+
storage: {db: dbopfs, table: 'saved_narration', key},
|
|
756
|
+
identity: {
|
|
757
|
+
model: speechSelection.model,
|
|
758
|
+
runtime: speechSelection.runtime
|
|
759
|
+
},
|
|
760
|
+
signal
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
```
|
|
764
|
+
|
|
765
|
+
A returned handle has the ordinary `segments`, `state`, `ready`, `getAudio(index)`,
|
|
766
|
+
and `cancel()` contract and can be passed to `ai.playPreparedTTS()` from the
|
|
767
|
+
application's playback action, including with its existing `audioOutput`.
|
|
768
|
+
The lookup signal governs only the read and rejects cancellation as
|
|
769
|
+
`AbortError`. After a successful return, aborting that signal does not invalidate
|
|
770
|
+
the retained handle; explicit handle cancellation and playback cancellation
|
|
771
|
+
remain separate. A successful read does not promise future autoplay permission
|
|
772
|
+
or prevent a later application-owned removal of stored files.
|
|
773
|
+
|
|
774
|
+
Applications decide which saved narration candidates to inspect. For example,
|
|
775
|
+
newest-first lookup can skip a newer partial preparation and then inspect an
|
|
776
|
+
older complete candidate without activating any model.
|
|
777
|
+
|
|
730
778
|
## Prepare narration once and replay stored audio
|
|
731
779
|
|
|
732
780
|
Use `ai.prepareTTS()` when preparation must continue independently of playback,
|
|
@@ -889,7 +937,7 @@ provider cleanup remains observable through that operation's settlement.
|
|
|
889
937
|
|
|
890
938
|
### Playback controls and independent lifetimes
|
|
891
939
|
|
|
892
|
-
`ai.playPreparedTTS(prepared,{signal,onState})` returns
|
|
940
|
+
`ai.playPreparedTTS(prepared,{signal,onState,audioOutput})` returns
|
|
893
941
|
`{state,error,finished,pause(),resume(),stop()}` immediately. It uses the
|
|
894
942
|
existing AI audio-clock scheduler and waits for each earlier segment before scheduling
|
|
895
943
|
later audio. Ready adjacent buffers retain contiguous scheduling. Requested
|
|
@@ -902,8 +950,9 @@ states are `waiting`, `waiting-for-gesture`, `scheduled`, `paused`, `complete`,
|
|
|
902
950
|
`stopped`, and `error`. Use `waiting-for-gesture` to present an audio-unlock
|
|
903
951
|
control. A `false` result from `resume()` is not a first-segment-ready signal;
|
|
904
952
|
inspect `state` and `error` to distinguish a stopped or unavailable context
|
|
905
|
-
from browser gesture waiting.
|
|
906
|
-
context immediately, before the first
|
|
953
|
+
from browser gesture waiting. With `audioOutput` omitted or `null`, playback
|
|
954
|
+
creates its own audio context immediately, before the first segment is ready,
|
|
955
|
+
and closes that context after completion, stop, cancellation, or failure.
|
|
907
956
|
|
|
908
957
|
`finished` resolves `true` after natural playback completion and `false` after
|
|
909
958
|
stop, playback cancellation, or failure. Real failures also reach the existing
|
|
@@ -931,6 +980,87 @@ preparations. A page that starts several preparations owns and observes every
|
|
|
931
980
|
`ready` promise, even when it plays only one handle. Browser autoplay permission
|
|
932
981
|
still applies; neither promise proves that a person heard the audio.
|
|
933
982
|
|
|
983
|
+
### Prepare a silent reusable WebAudio output
|
|
984
|
+
|
|
985
|
+
`ai.prepareAudioOutput()` synchronously returns an AI-owned output handle.
|
|
986
|
+
It creates or reuses that AI's persistent WebAudio context and observes its
|
|
987
|
+
actual state. Preparation stays silent: it does not resume the context, unmute
|
|
988
|
+
speech, load a model, create an audio source, play a test sound, or install
|
|
989
|
+
gesture listeners. It does not wait for speech preparation or provider readiness.
|
|
990
|
+
|
|
991
|
+
The handle exposes `kind:'web-audio'`, live `state`, `contextState`, and `error`
|
|
992
|
+
getters, `subscribe(listener)`, `resume()`, and `dispose()`. Repeated preparation
|
|
993
|
+
returns the same handle until its output is disposed or its context closes.
|
|
994
|
+
`contextState` is the platform context's current state, or `null` when unavailable;
|
|
995
|
+
`error` retains the complete actual error, or `null`.
|
|
996
|
+
|
|
997
|
+
| Output state | Meaning |
|
|
998
|
+
| --- | --- |
|
|
999
|
+
| `ready` | This WebAudio context is currently `running`. |
|
|
1000
|
+
| `suspended`, `interrupted`, or another platform state | The context reports that state; suspension alone does not establish a gesture requirement. |
|
|
1001
|
+
| `gesture-required` | A resume operation actually reported `NotAllowedError` while the context was not running. |
|
|
1002
|
+
| `unavailable` | Context creation failed; `error` contains the actual failure. |
|
|
1003
|
+
| `error` | An output operation failed; inspect the complete `error`. |
|
|
1004
|
+
| `disposing`, `disposed` | Owner disposal is in progress or has completed. |
|
|
1005
|
+
| `closed` | The underlying context has closed outside completed owner disposal. |
|
|
1006
|
+
|
|
1007
|
+
`subscribe(listener)` immediately replays `{kind,state,contextState,error}` and
|
|
1008
|
+
then delivers state changes through the canonical AI event owner. It returns
|
|
1009
|
+
an idempotent unsubscribe function. The named export
|
|
1010
|
+
`AI_AUDIO_OUTPUT_STATE_EVENT` from `AI.js` is `'ai-audio-output-state'`; its
|
|
1011
|
+
canonical occurrences carry the same snapshot in `detail` and an output-specific
|
|
1012
|
+
`operationId`. Handle subscriptions select only their own output. Disposal or
|
|
1013
|
+
context closure releases those subscriptions after the terminal notification;
|
|
1014
|
+
a later handle subscription still receives the current snapshot immediately.
|
|
1015
|
+
|
|
1016
|
+
`resume()` is an explicit asynchronous action for the application's user-gesture
|
|
1017
|
+
flow. It resolves `true` only when the context is actually running, and `false`
|
|
1018
|
+
when unavailable, disposed, superseded, or held by a paused prepared playback.
|
|
1019
|
+
A platform rejection rejects with its actual error and retains that failure in
|
|
1020
|
+
the output state. It does not unmute speech or load a model. Output readiness
|
|
1021
|
+
does not establish HTML audio readiness, native-speech readiness, future autoplay
|
|
1022
|
+
permission, available speech audio, or audible delivery.
|
|
1023
|
+
|
|
1024
|
+
Pass the handle explicitly to borrow that exact output for prepared playback:
|
|
1025
|
+
|
|
1026
|
+
```javascript
|
|
1027
|
+
// ai and prepared are the same AI owner and preparation used above.
|
|
1028
|
+
const audioOutput = ai.prepareAudioOutput();
|
|
1029
|
+
|
|
1030
|
+
function observeNarrationOutput(listener) {
|
|
1031
|
+
return audioOutput.subscribe(listener);
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
function resumeNarrationOutput() {
|
|
1035
|
+
// Call from the application's user action and observe the returned promise.
|
|
1036
|
+
return audioOutput.resume();
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
function playNarration() {
|
|
1040
|
+
return ai.playPreparedTTS(prepared, {audioOutput});
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
function disposeNarrationOutput() {
|
|
1044
|
+
// Call when this owner no longer needs the output; observe its promise.
|
|
1045
|
+
return audioOutput.dispose();
|
|
1046
|
+
}
|
|
1047
|
+
```
|
|
1048
|
+
|
|
1049
|
+
The selected handle must be live and belong to the same AI instance; another
|
|
1050
|
+
owner's, closed, or disposing output throws `TypeError`. Borrowed playback
|
|
1051
|
+
retains the same ordered scheduling, pause/resume, cancellation, and single-lane
|
|
1052
|
+
replacement behavior. Completion or stopping playback leaves the output open
|
|
1053
|
+
for reuse. Merely preparing an output does not change the omitted-option path.
|
|
1054
|
+
|
|
1055
|
+
The application owns the output lifetime and calls `dispose()` when finished
|
|
1056
|
+
with it. Disposal stops playback bound to that context and any following work
|
|
1057
|
+
waiting on its clock, observes cleanup, and awaits the actual context close.
|
|
1058
|
+
It preserves detached preparation, saved audio, unrelated contexts, and model
|
|
1059
|
+
selection. Concurrent calls share the disposal promise. Actual cleanup or close
|
|
1060
|
+
errors reject and remain observable; a failed context close permits a later
|
|
1061
|
+
disposal retry. Stop/mute controls retain their existing playback/provider scope
|
|
1062
|
+
and do not replace this explicit output disposal.
|
|
1063
|
+
|
|
934
1064
|
## Stream chunks as they arrive
|
|
935
1065
|
|
|
936
1066
|
Use the configured `ai` created above. Run this snippet from an owned user
|
|
@@ -1549,8 +1679,13 @@ as graph runtimes:
|
|
|
1549
1679
|
Inside the dedicated speech Worker, native CacheStorage is temporarily
|
|
1550
1680
|
unavailable before runtime import, so the selected runtime takes its existing
|
|
1551
1681
|
fetch path. Transformers' public browser/custom/filesystem cache switches are
|
|
1552
|
-
disabled there.
|
|
1553
|
-
|
|
1682
|
+
disabled there. After the runtime import, Kokoro's voice `caches.open()` calls
|
|
1683
|
+
use a Worker-local response cache with actual `match`/`put` results. It retains
|
|
1684
|
+
complete response bodies and metadata in memory for that Worker only; a miss
|
|
1685
|
+
does not fetch, and ordinary fetch still owns durable DBOPFS persistence. This
|
|
1686
|
+
temporary response retention is separate from Kokoro's own in-memory voice
|
|
1687
|
+
arrays. Unload clears it, and an unfinished put cannot repopulate it afterward.
|
|
1688
|
+
The SDK neither clears nor changes saved native caches. The original Worker
|
|
1554
1689
|
environment is restored on unload or failed import. Existing explicit
|
|
1555
1690
|
`removeBrowserSpeechModelCache()` remains a separate caller-selected operation.
|
|
1556
1691
|
|
|
@@ -1564,6 +1699,10 @@ closed resource parts survive cancellation. Existing whole-file caches remain
|
|
|
1564
1699
|
readable without migration. HTTP resume is used only when the server returns
|
|
1565
1700
|
the requested range; otherwise the complete resource is downloaded anew.
|
|
1566
1701
|
Worker termination aborts and joins its parent-owned resource operations.
|
|
1702
|
+
An existing complete resource reports `phase:'load'`, `cached:true`, and
|
|
1703
|
+
`message:'Reading stored model resource'` with its complete shard progress.
|
|
1704
|
+
That event describes a DBOPFS read, not a network download or model-session
|
|
1705
|
+
reload. The resource owner does not fetch that stored response again.
|
|
1567
1706
|
|
|
1568
1707
|
Routing discovery is best effort and is not an admission gate. If the scanner
|
|
1569
1708
|
cannot interpret a module, that module is left unchanged and follows its native
|
|
@@ -10,13 +10,14 @@
|
|
|
10
10
|
},
|
|
11
11
|
"artifactCount": 93,
|
|
12
12
|
"javascriptArtifactCount": 91,
|
|
13
|
-
"esmExportCount":
|
|
13
|
+
"esmExportCount": 375,
|
|
14
14
|
"artifacts": [
|
|
15
15
|
{
|
|
16
16
|
"file": "runtime/arcane/modules/AI.js",
|
|
17
17
|
"name": "AI.js",
|
|
18
18
|
"kind": "esm",
|
|
19
19
|
"exports": [
|
|
20
|
+
"AI_AUDIO_OUTPUT_STATE_EVENT",
|
|
20
21
|
"AI_BROWSER_SPEECH_CONFIGURATION_PROTOCOL",
|
|
21
22
|
"AI_BROWSER_SPEECH_ERROR_CODES",
|
|
22
23
|
"AI_BROWSER_SPEECH_EVENT_TYPES",
|
|
@@ -30,7 +31,7 @@
|
|
|
30
31
|
"availability": "Browser + native bridge + TWiN Cloud",
|
|
31
32
|
"protocol": "arcane-ai-browser-speech-configuration/1, AIProviderRuntime arcane-ai-provider/2 routes, globalThis.arcaneEvents, TWiN Cloud HTTPS, Arcane.ollama, Arcane.speech, Android WebView bridge",
|
|
32
33
|
"normalization": "Complete mutable caller-owned browser speech authority, SDK-owned provider registration/replacement/disposal, explicit STT/TTS activation, normalized Whisper STT and Kokoro TTS execution with default capacities 1 and 4 respectively, explicit provider-neutral execution snapshots through status(role,{execution:true}), sticky readiness, canonical window.user readiness without compatibility-event object-identity admission or polling, exact ordered structural calls with required arguments.message, atomic nonblank all-ID tool-result sequencing, complete all-choice streaming/data validation, native Ollama structural adaptation, complete-response then all validated tool callbacks then completion ordering, observed async callbacks, Blob/File transcription, default repeated-formatting-mark removal from cloned outbound TTS input, explicit textFormat:'plain' preservation for complete synthesis/preparation while streaming cleanup remains automatic and caller/visible content stays exact, immediate exact-segment synthesis, per-call voice/speed capture, optional final-segment pause on the existing audio clock, optional per-call terminal playback completion, detached preparation with complete semantic DBOPFS audio reuse and same-owner pending request sharing; each preparation owns its pending activation interest, final-interest cancellation aborts that activation while other interests preserve it, complete cache hits start no model, and cancellation after readiness leaves the ready model loaded; independent prepared playback without a model load for cached audio, ordered audio-clock scheduling, playable audio, active-generation TTS operation-failure routing, mute, and cancellation are normalized; provider/model/runtime/voice policy remains caller-owned. Both speech roles default execution.device to auto: webnn-npu when navigator.ml.createContext is exposed, then webgpu when navigator.gpu is exposed, then wasm for CPU execution. Failed backend loads clean up the pool before trying fresh Workers with the same prepared model and dtype; explicit devices never fall back. STT capacity only accepts 1; TTS accepts 1 through 4. Execution snapshots expose requestedDevice, selectedDevice, maxConcurrentRequests, and activeRequestCount for both roles; selectedDevice is null while unloaded and names the successful upstream session backend request when loaded. It does not prove every operation used the physical NPU: WebNN unsupported operations may use WASM, and compatibility depends on the browser, driver, hardware, and graph.",
|
|
33
|
-
"surface": "Native fetchTTS(payload,signal) and fetchSTT(audio,signal) forward caller cancellation separately from their complete Core speech payloads, normalize aborts to AbortError and suppress late results; actual engine interruption remains service-owned. prepareTTSPlayback(payload,signal) silently returns a real Blob or native-speech descriptor; native controls expose finished/released/state/error/pause/resume/stop, and actual release owns the playback lane. fetchTTS and durable prepareTTS report ARCANE_AI_TTS_AUDIO_EXPORT_UNAVAILABLE for native voices. Request-local model overrides remain exact and never mutate the configured model. Browser-speech protocol/event/error/reason constants; default `AI`; read-only `providerRuntime`, `browserSpeechConfiguration`, and `browserSpeechDescriptor`; explicit `providerRuntime.status(role,{execution:true})` snapshots; `streamTTS(text,end,options={})` with optional voice, speed, pauseAfterMs, and waitForPlayback; default speech-input cleanup with SDK-internal preparation metadata and explicit complete-call textFormat:'plain' consumed before engine dispatch; `prepareTTS({parts,storage,identity,textFormat,signal,onState})` returning ordered segments, state, ready, getAudio(index), and cancel(); `playPreparedTTS(prepared,{signal,onState})` returning state, error, finished, pause(), resume(), and stop() with one playback lane per AI and observational state/error callbacks; configure/dispose speech, route lifecycle, declaration-validated chat/stream requests with exact ordered calls; optional streamRequest toolText:{name,field} and onToolText(text,call,displayId) for decoded selected argument text with actual call identity, distinct from ordinary prose and terminal tool execution; synthesis/transcription and playback controls; initializes from current canonical user readiness, installs `window.ai`, and projects `ai-ready` plus active-generation `ai-tts-failure`."
|
|
34
|
+
"surface": "Native fetchTTS(payload,signal) and fetchSTT(audio,signal) forward caller cancellation separately from their complete Core speech payloads, normalize aborts to AbortError and suppress late results; actual engine interruption remains service-owned. prepareTTSPlayback(payload,signal) silently returns a real Blob or native-speech descriptor; native controls expose finished/released/state/error/pause/resume/stop, and actual release owns the playback lane. fetchTTS and durable prepareTTS report ARCANE_AI_TTS_AUDIO_EXPORT_UNAVAILABLE for native voices. Request-local model overrides remain exact and never mutate the configured model. Browser-speech protocol/event/error/reason constants; default `AI`; read-only `providerRuntime`, `browserSpeechConfiguration`, and `browserSpeechDescriptor`; explicit `providerRuntime.status(role,{execution:true})` snapshots; `streamTTS(text,end,options={})` with optional voice, speed, pauseAfterMs, and waitForPlayback; default speech-input cleanup with SDK-internal preparation metadata and explicit complete-call textFormat:'plain' consumed before engine dispatch; `prepareTTS({parts,storage,identity,textFormat,signal,onState})` returning ordered segments, state, ready, getAudio(index), and cancel(); async `readPreparedTTS({parts,textFormat,storage,identity,signal,onState})` returning a complete ready handle or null through the same semantic matcher, with no activation, unmute, synthesis, playback, storage writes, or partial repair; manifest, audio availability, and returned-handle reads use DBOPFS createTable:false so absent tables remain absent; supplied adapters own that read-existing behavior; missing matches/audio return null, malformed metadata and storage failures reject, and omitted storage sees only completed matching same-AI in-memory audio; hits replay ready state, misses emit nothing, and the lookup signal detaches on settlement while returned-handle cancellation stays independent; `prepareAudioOutput()` returning the persistent WebAudio handle {kind,state,contextState,error,subscribe,resume,dispose} without resume, unmute, model load, sources, sound, or gesture listeners; current-state replay and output-specific canonical `AI_AUDIO_OUTPUT_STATE_EVENT` (`ai-audio-output-state`), with ready only for an actually running context; explicit owner disposal observes context-bound cleanup and close; `playPreparedTTS(prepared,{signal,onState,audioOutput})` returning state, error, finished, pause(), resume(), and stop() with one playback lane per AI and observational state/error callbacks; explicit live same-AI audioOutput borrows its context until owner disposal, while omission or null preserves per-playback terminal close; configure/dispose speech, route lifecycle, declaration-validated chat/stream requests with exact ordered calls; optional streamRequest toolText:{name,field} and onToolText(text,call,displayId) for decoded selected argument text with actual call identity, distinct from ordinary prose and terminal tool execution; synthesis/transcription and playback controls; initializes from current canonical user readiness, installs `window.ai`, and projects `ai-ready` plus active-generation `ai-tts-failure`."
|
|
34
35
|
},
|
|
35
36
|
{
|
|
36
37
|
"file": "runtime/arcane/modules/AIModelSelectionController.js",
|
|
@@ -582,7 +583,7 @@
|
|
|
582
583
|
"availability": "Browser / native WebView",
|
|
583
584
|
"protocol": "OPFS, DBOPFSWorker, Compression Streams, per-realm globalThis.arcaneEvents authority, and native BroadcastChannel for live same-app/storage-partition committed changes",
|
|
584
585
|
"normalization": "App scope and recognized file parsing are normalized; nonblank unreadable JSONL rows remain complete raw strings, empty-table removal reports removed, absent, or not-empty after a create:false directory lookup and without scanning or recursively clearing a directory, same-named files are preserved, aliases and cached handles are invalidated only when the directory is gone, and unexpected DOM/storage errors are preserved.",
|
|
585
|
-
"surface": "default `DBOPFS`; installs `window.dbopfs`, emits `dbopfs-ready`; subscribeChanges(listener,{signal,once}) observes DBOPFS_EVENT_TYPES.change (dbopfs-change), returns idempotent unsubscribe/.dispose, and publishes metadata {applicationId,storagePath,tableName,directoryName,fileName,action,originId,sequence,changeId,remote} after successful storage commits only. Actions are write/append/delete/table-delete; batches preserve partial success, remote receipts invalidate targeted aliases before local publication and never rebroadcast, and pagehide/pageshow closes/reopens the native channel and clears memory caches without replay or fabricated changes. Local subscription/storage continue when BroadcastChannel is unavailable. Table/file/backup APIs include removeEmptyTable(tableName). createCompressedPNG({tableNames,additionalTables,signal}) returns a PNG Blob without download or record writes; omitted names select all, [] none, and supplied tables replace matching saved tables in the export. downloadCompressedPNG(name,options) reuses the same preparation and initiates one download; cancellation prevents it. restoreFromPNG(file,{selectTables}) closes the decoded image after pixel extraction, awaits the optional caller selection/projection before any writes, and rejects DBOPFS_RESTORE_WRITE_FAILED AggregateError after all selected table batches settle when records fail, preserving original errors and failures {tableName,fileName,reason}; successful writes remain. No-options restore and setMany retain their contracts."
|
|
586
|
+
"surface": "default `DBOPFS`; installs `window.dbopfs`, emits `dbopfs-ready`; subscribeChanges(listener,{signal,once}) observes DBOPFS_EVENT_TYPES.change (dbopfs-change), returns idempotent unsubscribe/.dispose, and publishes metadata {applicationId,storagePath,tableName,directoryName,fileName,action,originId,sequence,changeId,remote} after successful storage commits only. Actions are write/append/delete/table-delete; batches preserve partial success, remote receipts invalidate targeted aliases before local publication and never rebroadcast, and pagehide/pageshow closes/reopens the native channel and clears memory caches without replay or fabricated changes. Local subscription/storage continue when BroadcastChannel is unavailable. Table/file/backup APIs include getTableHandle(tableName='',{create=true}={}), readFile(tableName='',fileName='',{createTable=true}={}), get(tableName='',fileName='',force=false,{createTable=true}={}), and removeEmptyTable(tableName). Explicit create:false/createTable:false reads existing tables without creation or joining pending create work; default lazy creation remains unchanged. Low-level missing tables/files reject NotFoundError; get retains parsed caching/force semantics and resolves null only for NotFoundError, propagating other failures. Worker fallback reads already use existing app/table/file handles. createCompressedPNG({tableNames,additionalTables,signal}) returns a PNG Blob without download or record writes; omitted names select all, [] none, and supplied tables replace matching saved tables in the export. downloadCompressedPNG(name,options) reuses the same preparation and initiates one download; cancellation prevents it. restoreFromPNG(file,{selectTables}) closes the decoded image after pixel extraction, awaits the optional caller selection/projection before any writes, and rejects DBOPFS_RESTORE_WRITE_FAILED AggregateError after all selected table batches settle when records fail, preserving original errors and failures {tableName,fileName,reason}; successful writes remain. No-options restore and setMany retain their contracts."
|
|
586
587
|
},
|
|
587
588
|
{
|
|
588
589
|
"file": "runtime/arcane/modules/DBOPFSDocumentLibrary.js",
|
|
@@ -1091,13 +1092,14 @@
|
|
|
1091
1092
|
"name": "PreparedSpeech.js",
|
|
1092
1093
|
"kind": "esm",
|
|
1093
1094
|
"exports": [
|
|
1094
|
-
"prepareSpeech"
|
|
1095
|
+
"prepareSpeech",
|
|
1096
|
+
"readPreparedSpeech"
|
|
1095
1097
|
],
|
|
1096
|
-
"summary": "Owns detached ordered speech preparation, complete semantic audio reuse, and per-caller cancellation behind AI.prepareTTS.",
|
|
1097
|
-
"availability": "Browser or native WebView with Blob, AbortController, an injected synthesis callback
|
|
1098
|
+
"summary": "Owns detached ordered speech preparation, complete semantic audio reuse, cache-only lookup, and per-caller cancellation behind AI.prepareTTS and AI.readPreparedTTS.",
|
|
1099
|
+
"availability": "Browser or native WebView with Blob, AbortController, optional ready DBOPFS, and an injected synthesis callback for generation",
|
|
1098
1100
|
"protocol": "In-process preparation handle plus caller-owned DBOPFS raw audio and version-1 semantic manifest",
|
|
1099
|
-
"normalization": "Preserves complete original parts and JSON-compatible semantic context, shares pending matching work on the same owner/storage group, admits segments in order, stores complete Blobs and MIME metadata, retains successful audio after failure or cancellation, and separates generation readiness from playback.",
|
|
1100
|
-
"surface": "Named prepareSpeech({owner,parts,originalParts,selection,segmentation,storage,identity,signal,onState,synthesize}) returns {segments,state,ready,getAudio(index),cancel()};
|
|
1101
|
+
"normalization": "Preserves complete original parts and JSON-compatible semantic context, shares pending matching work on the same owner/storage group, admits segments in order, stores complete Blobs and MIME metadata, retains successful audio after failure or cancellation, and separates generation readiness from playback. Cache-only lookup uses the same semantic matcher, reads every stored audio reference without writes or repair, and returns null for missing matches/audio while preserving genuine read failures.",
|
|
1102
|
+
"surface": "Named prepareSpeech({owner,parts,originalParts,selection,segmentation,storage,identity,signal,onState,synthesize}) returns {segments,state,ready,getAudio(index),cancel()}. Named async readPreparedSpeech({owner,parts,originalParts=parts,selection=null,segmentation=null,storage=null,identity=null,signal=null,onState=null}) returns a complete ready handle or null without synthesis, activation, unmute, playback, or storage writes. Manifest, audio availability, and returned-handle reads pass DBOPFS createTable:false, leaving absent tables absent; supplied adapters own the same read-existing contract. Omitted storage reads only completed matching same-owner in-memory preparations; pending or failed work returns null. A hit replays ready state and a miss emits nothing. The lookup signal detaches on settlement; the returned handle keeps independent explicit cancellation and MIME-preserving getAudio, including later storage errors. Applications use AI.prepareTTS, AI.readPreparedTTS, and AI.playPreparedTTS so the shared AI owner retains segmentation, provider readiness, capacity, and playback."
|
|
1101
1103
|
},
|
|
1102
1104
|
{
|
|
1103
1105
|
"file": "runtime/arcane/modules/PrintView.js",
|