arcane-os 0.69.0 → 0.71.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 +28 -0
- package/README.md +1 -1
- package/browser-runtime/ai/browser-decisions.mjs +46 -2
- package/browser-runtime/ai/browser-speech-artifacts.mjs +58 -62
- package/browser-runtime/ai/browser-speech-providers.mjs +3 -0
- package/browser-runtime/ai/browser-wasm-llm-provider.mjs +76 -164
- package/browser-runtime/ai/dbopfs-model-parts.mjs +344 -0
- package/browser-runtime/ai/dbopfs-model-resources.mjs +190 -0
- package/browser-runtime/ai/decision-runtime.mjs +17 -1
- package/browser-runtime/ai/decision-worker.mjs +21 -1
- package/browser-runtime/ai/model-resource-bridge.mjs +231 -0
- package/browser-runtime/ai/speech-worker-client.mjs +68 -28
- package/browser-runtime/ai/speech-worker-runtime.mjs +212 -97
- package/browser-runtime/core/contracts.mjs +1 -1
- package/docs/reference/README.md +2 -0
- package/docs/reference/ai/browser-decisions.md +57 -3
- package/docs/reference/ai/browser-speech.md +45 -17
- package/docs/reference/ai/browser-wasm.md +28 -20
- package/docs/reference/core-native-packaging.md +13 -0
- package/docs/reference/core-runtime.md +9 -0
- package/docs/reference/core-shared-host.md +190 -0
- package/docs/reference/inventory/package-api.json +3 -3
- package/docs/reference/inventory/runtime-components.json +2 -2
- package/docs/reference/inventory/runtime-modules.json +6 -6
- package/docs/reference/runtime-components.md +35 -1
- package/docs/reference/runtime-modules.md +97 -7
- package/docs/reference/sdk-api.md +69 -31
- package/package.json +2 -2
- package/runtime/arcane/components/theme-switcher.html +4 -4
- package/runtime/arcane/modules/AI.js +2 -18
- package/runtime/arcane/modules/AIProviderRuntime.js +77 -3
- package/runtime/arcane/modules/DBOPFS.js +180 -26
- package/src/core/host.mjs +2 -0
- package/src/core/runtime.mjs +9 -4
- package/src/core/services/packaged-web.mjs +8 -1
- package/src/core/shared-host.mjs +500 -0
- package/src/native/portable-layout.mjs +66 -34
|
@@ -477,6 +477,13 @@ the caller's ready DBOPFS instance; the separate JSON-compatible `identity`
|
|
|
477
477
|
adds application-owned semantic context. Reuse compares complete inputs rather
|
|
478
478
|
than an SDK version alone.
|
|
479
479
|
|
|
480
|
+
Only missing audio joins TTS activation. Each preparation supplies its own
|
|
481
|
+
cancellation lifetime to the existing provider runtime. The last pending
|
|
482
|
+
activation interest cancels the owned load; other preparation/playback or
|
|
483
|
+
joining direct-load interests preserve it. Cache-only preparation never loads
|
|
484
|
+
the model, and cancelling after activation does not unload an already-ready
|
|
485
|
+
model. Provider work owns actual cancellation and cleanup completion.
|
|
486
|
+
|
|
480
487
|
`state` is `queued`, `preparing`, `ready`, `error`, or `cancelled`.
|
|
481
488
|
`onState({state,completed,total,segments,error})` synchronously observes
|
|
482
489
|
preparation progress. `ready` resolves the complete record after every segment
|
|
@@ -486,8 +493,8 @@ the retained MIME type. Preparation failure rejects; cancellation rejects as
|
|
|
486
493
|
`AbortError` and preserves successfully stored segments. Matching pending
|
|
487
494
|
requests share synthesis only on the same AI instance and storage group;
|
|
488
495
|
cancelling one handle does not cancel another active matching caller.
|
|
489
|
-
Preparation cancellation prevents later synthesis
|
|
490
|
-
|
|
496
|
+
Preparation cancellation prevents later synthesis and releases its activation
|
|
497
|
+
interest. The provider's operation remains responsible for actual cleanup.
|
|
491
498
|
|
|
492
499
|
`playPreparedTTS(prepared,{signal,onState})` can attach immediately. Its returned
|
|
493
500
|
`{state,error,finished,pause(),resume(),stop()}` handle schedules segments in
|
|
@@ -1024,9 +1031,20 @@ The singleton exposes read-only `protocol`, `configured`, and `speechMuted`;
|
|
|
1024
1031
|
`supportsTranscriptionCapture(providerId=null)`;
|
|
1025
1032
|
`createTranscriptionCapture(options={})`;
|
|
1026
1033
|
`synthesize(payload,options={},preparation={})`; and
|
|
1027
|
-
`setSpeechMuted(muted)`. Provider payloads must be data-only; callbacks,
|
|
1034
|
+
`setSpeechMuted(muted,{signal}={})`. Provider payloads must be data-only; callbacks,
|
|
1028
1035
|
accessors, symbols, and cycles are rejected at the provider boundary.
|
|
1029
1036
|
|
|
1037
|
+
`setSpeechMuted(false,{signal})` shares pending activation while retaining an
|
|
1038
|
+
independent interest for each caller. Aborting one signal rejects only that
|
|
1039
|
+
caller's wait. When no pending interests remain, abort reaches the owned load.
|
|
1040
|
+
No-signal callers and direct `load('tts',...)` calls joining that activation
|
|
1041
|
+
also retain it. A rejected load preserves its complete error for the remaining
|
|
1042
|
+
callers. An already-aborted signal rejects before changing mute intent.
|
|
1043
|
+
Explicit `setSpeechMuted(true)` still cancels shared activation and unloads TTS;
|
|
1044
|
+
once accepted, that mute cleanup runs to completion rather than being undone
|
|
1045
|
+
by cancellation of its caller's signal. Completed activation is not unloaded
|
|
1046
|
+
merely because a former observer later aborts.
|
|
1047
|
+
|
|
1030
1048
|
Selection options admit `localOnly=false`; inspection admits
|
|
1031
1049
|
`{localOnly=false,signal=null}`; startup admits
|
|
1032
1050
|
`{startLanguageModel=true,startMuted=true,startTranscription=false,signal=null}` (including an omitted
|
|
@@ -2203,6 +2221,59 @@ default `DBOPFS`; installs `window.dbopfs`, emits `dbopfs-ready`; table/file/bac
|
|
|
2203
2221
|
|
|
2204
2222
|
Exact exports: `DBOPFS_EVENT_TYPES`, `DBOPFS_REASONS`, `default`.
|
|
2205
2223
|
|
|
2224
|
+
`subscribeChanges(listener, {signal, once})` observes
|
|
2225
|
+
`DBOPFS_EVENT_TYPES.change` (`dbopfs-change`) through the existing canonical
|
|
2226
|
+
`arcane-os/event-manager` source. It returns an idempotent unsubscribe function
|
|
2227
|
+
with `.dispose()`; aborting the supplied signal also removes the subscription.
|
|
2228
|
+
Each listener receives an event whose `detail` contains:
|
|
2229
|
+
|
|
2230
|
+
| Field | Meaning |
|
|
2231
|
+
| --- | --- |
|
|
2232
|
+
| `applicationId`, `storagePath` | Existing application identity and `apps/<application-id>` OPFS scope. |
|
|
2233
|
+
| `tableName`, `directoryName` | Logical table and physical directory; `memories` / `memory` retain their existing alias. |
|
|
2234
|
+
| `fileName` | Complete record filename, or `null` for a removed table. |
|
|
2235
|
+
| `action` | `write`, `append`, `delete`, or `table-delete`. A write does not claim whether a record was newly created. |
|
|
2236
|
+
| `originId`, `sequence`, `changeId` | Per-document origin and incrementing committed occurrence, shared by local and remote notifications for that occurrence. These are correlation fields, separate from realm-local canonical event-source IDs. |
|
|
2237
|
+
| `remote` | `false` at the writing DBOPFS source, `true` when received from another live document. |
|
|
2238
|
+
|
|
2239
|
+
A write is published after writable-stream `close()` succeeds, or after the
|
|
2240
|
+
existing OPFS worker acknowledges its completed write/flush/close. A removal
|
|
2241
|
+
is published after OPFS removes the record or table. The affected cached record
|
|
2242
|
+
or table, including alias keys and removed-table handles, is invalidated before
|
|
2243
|
+
listeners run. A read already in progress may still resolve its earlier
|
|
2244
|
+
snapshot; it cannot repopulate the cache after an intervening delivered change.
|
|
2245
|
+
Notification metadata does not contain, rewrite, or reread record bodies.
|
|
2246
|
+
|
|
2247
|
+
`set()` and raw `writeFile()` publish one occurrence per completed write;
|
|
2248
|
+
`setMany()`, `deleteMany()`, clear operations, and restore retain their existing
|
|
2249
|
+
per-record results and publish only their actual successful mutations. A
|
|
2250
|
+
partial batch is not a transaction: successful records are announced even when
|
|
2251
|
+
siblings fail. Missing records, absent/non-empty empty-table removal, failed
|
|
2252
|
+
writes/removals, and reads emit no committed-change event. `deleteTable()` keeps
|
|
2253
|
+
its existing return/error-reporting behavior; its returned `true` alone does
|
|
2254
|
+
not imply that a committed-change event was emitted.
|
|
2255
|
+
|
|
2256
|
+
Within one document, subscribe through this source or the canonical event
|
|
2257
|
+
manager, rather than consuming both as separate changes. Across documents,
|
|
2258
|
+
DBOPFS owns one native `BroadcastChannel` named from the application storage
|
|
2259
|
+
path. Native storage-partition matching plus that path selects the receiving
|
|
2260
|
+
documents. A remote receiver invalidates its affected cache before publishing
|
|
2261
|
+
locally and never rebroadcasts the occurrence. There is no echo back to the
|
|
2262
|
+
writing DBOPFS source. Ordinary storage success remains success if a transport
|
|
2263
|
+
notification fails; the notification error is reported separately.
|
|
2264
|
+
|
|
2265
|
+
Subscriptions are live-only: subscribe before the application's initial read,
|
|
2266
|
+
then let the application map the table/file/action to its own records and UI.
|
|
2267
|
+
DBOPFS performs no polling, initial full-table read, automatic picker refresh,
|
|
2268
|
+
or event-history replay. The channel lasts for the owning page, independent of
|
|
2269
|
+
individual listener lifetimes, so a document that only writes still announces
|
|
2270
|
+
its commits. `pagehide` closes the channel; `pageshow` after suspension reopens
|
|
2271
|
+
it and clears only that document's in-memory caches. Resuming a page does not
|
|
2272
|
+
fabricate a storage mutation. Applications own any resume-time view refresh.
|
|
2273
|
+
Missed events during suspension, process termination, or realm loss are not
|
|
2274
|
+
replayed; the stored records remain the authority. Direct OPFS writes made
|
|
2275
|
+
outside DBOPFS are not observed by this contract.
|
|
2276
|
+
|
|
2206
2277
|
`createCompressedPNG({tableNames, additionalTables, signal})` returns an
|
|
2207
2278
|
`image/png` Blob without downloading or writing records. Omitted `tableNames`
|
|
2208
2279
|
selects all discovered saved tables; `[]` selects none. Named absent tables
|
|
@@ -2244,16 +2315,35 @@ unreadable row remains in its original string form so the owning application
|
|
|
2244
2315
|
can display, diagnose, or recover it without silent data loss. DOM and storage
|
|
2245
2316
|
errors remain observable. The logical `memories` table continues to map to the
|
|
2246
2317
|
physical `memory` directory, and successful or already-absent empty-table
|
|
2247
|
-
removal invalidates both alias and cached-handle state.
|
|
2248
|
-
|
|
2318
|
+
removal invalidates both alias and cached-handle state. Cross-document change
|
|
2319
|
+
delivery requires native `BroadcastChannel` in the same origin and storage
|
|
2320
|
+
partition, with the same application scope; separate WebView profiles do not
|
|
2321
|
+
share this transport. If it is unavailable, local change subscriptions and
|
|
2322
|
+
ordinary DBOPFS storage continue, without cross-document notification or a
|
|
2323
|
+
polling replacement. Transport: OPFS, DBOPFSWorker, Compression Streams, native
|
|
2324
|
+
BroadcastChannel, and the existing event-pubsub-backed canonical event owner.
|
|
2249
2325
|
[Deep protocol details](protocols.md).
|
|
2250
2326
|
|
|
2251
2327
|
### Example
|
|
2252
2328
|
|
|
2253
2329
|
```javascript
|
|
2254
|
-
import
|
|
2330
|
+
import '/arcane/modules/DBOPFS.js';
|
|
2331
|
+
|
|
2332
|
+
await dbopfs.readyPromise;
|
|
2333
|
+
const lifetime = new AbortController();
|
|
2334
|
+
const stop = dbopfs.subscribeChanges(function showCommittedParade(event) {
|
|
2335
|
+
const change = event.detail;
|
|
2336
|
+
if (change.tableName === 'moon_bus') {
|
|
2337
|
+
console.log(change.action, change.fileName, change.remote);
|
|
2338
|
+
}
|
|
2339
|
+
}, {signal: lifetime.signal});
|
|
2255
2340
|
|
|
2256
|
-
|
|
2341
|
+
await dbopfs.set('moon_bus', 'squid-parade.json', {
|
|
2342
|
+
destination: 'The moon', passengers: ['Captain Squid', 'One confused lobster']
|
|
2343
|
+
});
|
|
2344
|
+
// Another live page using this app/storage scope receives the same occurrence.
|
|
2345
|
+
// The application can read that one record when its view needs the body.
|
|
2346
|
+
stop(); // lifetime.abort() is the equivalent subscription cleanup.
|
|
2257
2347
|
```
|
|
2258
2348
|
|
|
2259
2349
|
## DBOPFSDocumentLibrary.js
|
|
@@ -136,7 +136,7 @@ Root applications import `AI` through `arcane-os/ai` in the managed browser map.
|
|
|
136
136
|
`ai.prepareTTSPlayback(payload,signal)` silently prepares real Blob audio or a
|
|
137
137
|
native speech descriptor for playback. Native browser voices expose no audio
|
|
138
138
|
file; use this path or shared streaming/SpeechPlayback for native output.
|
|
139
|
-
`ai.prepareTTS({parts,storage,identity,signal,onState})` prepares complete
|
|
139
|
+
`ai.prepareTTS({parts,storage,identity,textFormat,signal,onState})` prepares complete
|
|
140
140
|
punctuation segments without playing them and optionally saves/reuses their
|
|
141
141
|
audio through application-owned DBOPFS. Its immediate handle exposes ordered
|
|
142
142
|
segments, preparation state, `ready`, `getAudio(index)`, and `cancel()`.
|
|
@@ -149,6 +149,17 @@ for complete inputs, storage ownership, progress, errors, and cancellation,
|
|
|
149
149
|
and [AI.js](runtime-modules.md#aijs) for the managed runtime contract.
|
|
150
150
|
These are AI instance methods, not new npm export names.
|
|
151
151
|
|
|
152
|
+
Complete synthesis calls can select `textFormat:'plain'` to preserve supplied
|
|
153
|
+
speech text, including repeated formatting marks and surrounding whitespace.
|
|
154
|
+
This applies to `ai.fetchTTS(payload,signal)`, audio-file preparation through
|
|
155
|
+
`ai.prepareTTSPlayback(payload,signal)`, durable `ai.prepareTTS(...)`, provider
|
|
156
|
+
runtime TTS requests and direct Kokoro synthesis. The SDK consumes the control
|
|
157
|
+
before engine dispatch; durable preparation retains the existing punctuation
|
|
158
|
+
segmentation. Omitted or other values retain automatic speech cleanup.
|
|
159
|
+
Streaming `streamTTS`, `finishTTS` and shared `SpeechPlayback` retain their
|
|
160
|
+
automatic cleanup; this option does not change upstream engine capacity.
|
|
161
|
+
See [exact complete synthesis input](ai/browser-speech.md#exact-complete-synthesis-input).
|
|
162
|
+
|
|
152
163
|
## Shared operation contract
|
|
153
164
|
|
|
154
165
|
High-level operations accept one options object. Common fields are `workspaceRoot`, `appId`, `target`, `signal`, and `onEvent`; only fields meaningful to that operation are consumed. Long work acknowledges first, owns its child tasks, keeps event delivery ordered and backpressured, emits progress or heartbeats, observes cancellation where safe, and rejects with `ArcaneError` on failure.
|
|
@@ -7530,6 +7541,12 @@ published SDK/runtime dependencies and the canonical Core entry/client into a
|
|
|
7530
7541
|
fresh output directory. Service factories are imported only when a real native
|
|
7531
7542
|
host later launches the generated Core entry, never during packaging.
|
|
7532
7543
|
|
|
7544
|
+
An optional `appDescriptor.native.window` record carries `width`, `height` and
|
|
7545
|
+
`resizable` into `manifest.window`. Dimensions are positive integral logical
|
|
7546
|
+
client units; `resizable` is an optional boolean. Omitted fields remain omitted.
|
|
7547
|
+
The portable payload records the selection; the selected platform host owns
|
|
7548
|
+
its window behavior. See [initial native window size](core-native-packaging.md#initial-native-window-size).
|
|
7549
|
+
|
|
7533
7550
|
### Availability and normalization
|
|
7534
7551
|
|
|
7535
7552
|
**Node; produces a portable payload rather than an executable host.** Existing
|
|
@@ -7641,6 +7658,16 @@ the selected output root. There is no compiler or OS checkout requirement for
|
|
|
7641
7658
|
application builds. The build machine needs its normal Node/npm toolchain and
|
|
7642
7659
|
`tar`; execution needs Windows x64, .NET Framework 4.6.2 or later and WebView2.
|
|
7643
7660
|
|
|
7661
|
+
The matching Windows host consumes `appDescriptor.native.window` through
|
|
7662
|
+
`manifest.window`. Optional `width` and `height` select initial client dimensions
|
|
7663
|
+
in 96-DPI logical units; the host scales them for its actual DPI and fits the
|
|
7664
|
+
outer window within the current screen's usable area. Optional `resizable:false`
|
|
7665
|
+
selects a fixed-size border and disables maximizing. Omitted fields preserve
|
|
7666
|
+
the existing host defaults. Initial sizing preserves a window already restored
|
|
7667
|
+
to a non-normal state and does not add saved-bounds storage. The initial-fit
|
|
7668
|
+
logic is not reapplied during later user resizing. See [initial native window size](core-native-packaging.md#initial-native-window-size)
|
|
7669
|
+
for a complete descriptor example and the other-platform boundary.
|
|
7670
|
+
|
|
7644
7671
|
The returned `arcane-native-builder/1` provider exposes `describe`, `doctor`,
|
|
7645
7672
|
`prepare`, `build`, `verify` and `run`. Build returns `{app,target,manifest}`
|
|
7646
7673
|
with the actual directory at `target.rootDir`. Verification reads the artifact;
|
|
@@ -8844,13 +8871,14 @@ occurrences use source `ai-model-controller`; every load or unload owns one
|
|
|
8844
8871
|
non-null operation ID shared by its state and progress occurrences. Public
|
|
8845
8872
|
progress retains the exact selected `modelId`, `phase`, state, and nested
|
|
8846
8873
|
file-progress field names. Browser-WASM download progress preserves
|
|
8847
|
-
`completed`, `total`, and `unit:'files'
|
|
8848
|
-
|
|
8849
|
-
|
|
8850
|
-
|
|
8851
|
-
|
|
8852
|
-
|
|
8853
|
-
|
|
8874
|
+
`completed`, `total`, and `unit:'files'`, with complete per-file `members`
|
|
8875
|
+
reporting stored shards. A member's `total` remains `null` until its complete
|
|
8876
|
+
response is stored, unless the HTTP range plan already establishes its shard
|
|
8877
|
+
total. `activeTransfers`, `transferLimit`, and `transferMode` describe the
|
|
8878
|
+
active download work. Closed shards, active-worker changes, and completion
|
|
8879
|
+
boundaries publish semantic progress; the existing heartbeat repeats the latest
|
|
8880
|
+
record. These fields describe files and shards, without byte-based display
|
|
8881
|
+
requirements or estimated transfer rates.
|
|
8854
8882
|
Accessor-bearing provider status
|
|
8855
8883
|
and malformed progress reject with `ARCANE_AI_PROVIDER_STATUS_INVALID` and
|
|
8856
8884
|
`ARCANE_AI_PROVIDER_PROGRESS_INVALID`. After disposal, lifecycle operations fail with
|
|
@@ -9443,8 +9471,8 @@ console.log(provider.status().state); // unloaded
|
|
|
9443
9471
|
### Overview
|
|
9444
9472
|
|
|
9445
9473
|
Adapts an existing DBOPFS instance into a complete multi-model, ordered
|
|
9446
|
-
multi-file cache without renaming its public methods.
|
|
9447
|
-
|
|
9474
|
+
multi-file cache without renaming its public methods. Every new full response
|
|
9475
|
+
or Range transfer is committed in ordered parts. An interrupted set
|
|
9448
9476
|
is not a cache hit, but its completed members remain available to the next
|
|
9449
9477
|
install so only missing work is fetched.
|
|
9450
9478
|
|
|
@@ -9462,25 +9490,26 @@ workers
|
|
|
9462
9490
|
when the probe returns a usable total from exposed `Content-Range` or optional
|
|
9463
9491
|
descriptor `bytes`. The mutable
|
|
9464
9492
|
result contains `kind`, `tableName`, `downloadConcurrency`, the original
|
|
9465
|
-
`adapter`, and `ready`, `install`, `ensure`, and `
|
|
9493
|
+
`adapter`, and `ready`, `install`, `ensure`, `remove`, and `fetchResource`. `ensure()` returns
|
|
9466
9494
|
`files` and `cache:'cached'|'installed'`. It preserves the complete model content.
|
|
9467
9495
|
`offline:true` never downloads and rejects a miss with
|
|
9468
9496
|
`ARCANE_AI_MODEL_OFFLINE_MISS`.
|
|
9469
9497
|
|
|
9470
|
-
When supplied, `onProgress` receives
|
|
9471
|
-
|
|
9472
|
-
`
|
|
9473
|
-
|
|
9474
|
-
|
|
9475
|
-
|
|
9476
|
-
|
|
9477
|
-
|
|
9478
|
-
|
|
9479
|
-
|
|
9480
|
-
|
|
9481
|
-
|
|
9482
|
-
|
|
9483
|
-
|
|
9498
|
+
When supplied, `onProgress` receives ordered file progress and `members`
|
|
9499
|
+
records with each file's `name`, committed `completed` parts, `total`, and
|
|
9500
|
+
`unit:'shards'`. Streaming totals remain `null` until the response finishes;
|
|
9501
|
+
previously complete files identify `state:'cached'`. `activeTransfers` is the
|
|
9502
|
+
current transfer-worker count, `transferLimit` is selected concurrency, and
|
|
9503
|
+
`transferMode` is `probing`, `files`, `ranges`, or `single`. Closed-part and
|
|
9504
|
+
lifecycle changes publish promptly, with an existing 250 ms heartbeat. No
|
|
9505
|
+
byte progress, transfer rate, or estimated completion time is produced.
|
|
9506
|
+
|
|
9507
|
+
`fetchResource(input,{signal,onProgress,...requestOptions})` returns
|
|
9508
|
+
`{file,status,statusText,headers,url,redirected}` for a complete stored Fetch
|
|
9509
|
+
response. SDK browser model loaders use this shared owner for configuration,
|
|
9510
|
+
tokenizer, graph, external-data, and other fetched resources. Successful
|
|
9511
|
+
complete GET resources may be reused; supported HTTP ranges resume closed
|
|
9512
|
+
parts after interruption. Request framing stays at the transport boundary.
|
|
9484
9513
|
|
|
9485
9514
|
### Availability and normalization
|
|
9486
9515
|
|
|
@@ -9491,9 +9520,9 @@ resumable Range parts. For any source member, a followed redirect that
|
|
|
9491
9520
|
turns the first `bytes=0-0` probe into `200` triggers one direct probe of the
|
|
9492
9521
|
final URL. Confirmed support cancels the original body and starts the Range
|
|
9493
9522
|
pool; refusal keeps the original response as the ordinary single-fetch path,
|
|
9494
|
-
|
|
9523
|
+
without requiring a readable `Content-Length`. A missing `Content-Range` can
|
|
9495
9524
|
use optional descriptor `bytes` for Range planning; without either total, the
|
|
9496
|
-
store
|
|
9525
|
+
store uses one full fetch streamed into ordered persistent parts. A confirmed source member is divided into
|
|
9497
9526
|
deterministic OPFS Range parts of roughly 4 MB each, up to 4,096 parts. A
|
|
9498
9527
|
monolithic source may use at most
|
|
9499
9528
|
`downloadConcurrency` Range workers; split sources keep one Range worker per
|
|
@@ -9502,7 +9531,7 @@ in order as one logical Blob. A later non-206, contradictory exposed Range
|
|
|
9502
9531
|
response, or incorrectly framed body fails rather than assembling partial
|
|
9503
9532
|
content. On failure or cancellation, peers settle while completed shards and
|
|
9504
9533
|
Range parts remain available for retry; an unfinished active part restarts after
|
|
9505
|
-
refresh. A persisted Range part whose length does not match its requested HTTP
|
|
9534
|
+
refresh. Existing whole-file caches remain readable without migration. A persisted Range part whose length does not match its requested HTTP
|
|
9506
9535
|
frame is discarded and fetched again. A complete whole member supersedes its
|
|
9507
9536
|
redundant current Range fragments. Cleanup failure is warned without
|
|
9508
9537
|
hiding the usable model. Wllama requires every selected model Blob or File to
|
|
@@ -9784,7 +9813,7 @@ retains the complete list of deletions already completed.
|
|
|
9784
9813
|
|
|
9785
9814
|
Adapts an existing DBOPFS instance into a complete caller-selected speech
|
|
9786
9815
|
runtime/model store. It serializes each selected model with an exclusive Web
|
|
9787
|
-
Lock,
|
|
9816
|
+
Lock, preserves complete peer files and closed parts after failure, stores ordinary mutable selection
|
|
9788
9817
|
metadata before content, and treats a changed file inventory or source mapping
|
|
9789
9818
|
as a cache miss that is downloaded again.
|
|
9790
9819
|
|
|
@@ -9794,7 +9823,7 @@ as a cache miss that is downloaded again.
|
|
|
9794
9823
|
createDbopfsSpeechArtifactStore({ dbopfs, tableName='arcane_ai_browser_speech', fetchImpl=null, objectUrlFactory=null }={})
|
|
9795
9824
|
```
|
|
9796
9825
|
|
|
9797
|
-
The mutable result is `{protocol,tableName,prepare,remove}`.
|
|
9826
|
+
The mutable result is `{protocol,tableName,prepare,remove,fetchResource}`.
|
|
9798
9827
|
`prepare(authority,{signal,onProgress,offline=false,security})` uses a complete
|
|
9799
9828
|
compatible cache or downloads every declared file, preserves complete content
|
|
9800
9829
|
and materializes object URLs, then returns
|
|
@@ -9804,9 +9833,18 @@ needs those URLs. `offline:true` never fetches and rejects a miss with
|
|
|
9804
9833
|
authority's files and selection metadata. Optional `onProgress` receives
|
|
9805
9834
|
preparation and download records with `phase`, `stage`, `message`, and the
|
|
9806
9835
|
current `file` when available. File operations report actual `completed` and
|
|
9807
|
-
`total` inventory counts with `unit:'files'
|
|
9836
|
+
`total` inventory counts with `unit:'files'`, and `resource` carries the current
|
|
9837
|
+
closed-part progress with `unit:'shards'`; module preparation uses
|
|
9808
9838
|
`total:null`. Cancellation stops progress along with the owning operation.
|
|
9809
9839
|
|
|
9840
|
+
New files stream into ordered DBOPFS parts with no required response length.
|
|
9841
|
+
Existing complete whole files stay readable without migration.
|
|
9842
|
+
`fetchResource(input,{signal,onProgress,...requestOptions})` returns
|
|
9843
|
+
`{file,status,statusText,headers,url,redirected}` and gives the dedicated
|
|
9844
|
+
speech Worker the same complete resource storage owner for runtime fetches.
|
|
9845
|
+
Native ESM imports and unmapped classic Worker internals retain their platform
|
|
9846
|
+
loader; they are not claimed as intercepted HTTP fetches.
|
|
9847
|
+
|
|
9810
9848
|
### Availability and normalization
|
|
9811
9849
|
|
|
9812
9850
|
**Browser with a ready DBOPFS instance, OPFS, Web Locks, Fetch or an injected
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "arcane-os",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.71.0",
|
|
4
4
|
"description": "Arcane OS JavaScript SDK, project-local CLI, browser runtime, and repository-portable application packager.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.mjs",
|
|
@@ -123,7 +123,7 @@
|
|
|
123
123
|
"test": "npm run test:unit && npm run test:functional && npm run test:integration && npm run test:regression",
|
|
124
124
|
"test:release": "node ./bin/arcane-test.mjs test/npm-release.test.mjs",
|
|
125
125
|
"test:unit": "node ./bin/arcane-test.mjs test/app-descriptor.test.mjs test/app-schema.test.mjs test/app-selection.test.mjs test/contracts.test.mjs test/conversation-closing-report.test.mjs test/doctor.test.mjs test/mail-credentials.test.mjs test/mail-outbox.test.mjs test/mail-public-api.test.mjs test/mail-send.test.mjs test/mail-transport.test.mjs test/native-windows-icon.test.mjs test/targets.test.mjs test/workspace-operation-lock.test.mjs",
|
|
126
|
-
"test:functional": "node ./bin/arcane-test.mjs test/ai-ollama-readiness.test.mjs test/core-ollama-lifecycle.test.mjs test/ai-request-model.test.mjs test/ai-request-retry.test.mjs test/ai-stream-cancellation.test.mjs test/browser-decisions.test.mjs test/browser-speech-providers.test.mjs test/browser-wasm-gpu-notice.test.mjs test/browser-wasm-download-resume.test.mjs test/chat-input.test.mjs test/chat-stream-speech.test.mjs test/chat-transcript-scroll.test.mjs test/chat-transient-retention.test.mjs test/cli.test.mjs test/continuous-voice-capture.test.mjs test/codex-app-server.test.mjs test/mcp-stdio-server.test.mjs test/core-classic-source.test.mjs test/core-client.test.mjs test/core-client-installation.test.mjs test/core-ai-installation.test.mjs test/core-llama-lifecycle.test.mjs test/core-development.test.mjs test/core-host.test.mjs test/core-model-assets.test.mjs test/core-native-services.test.mjs test/core-packaged-web.test.mjs test/core-runtime.test.mjs test/core-stdio.test.mjs test/dbopfs-document-library.test.mjs test/dbopfs-png.test.mjs test/dbopfs.test.mjs test/dev-server.test.mjs test/dev-pwa.test.mjs test/dom-event-instrumentation.test.mjs test/event-manager.test.mjs test/events.test.mjs test/file-preview.test.mjs test/import-map.test.mjs test/local-image-editing.test.mjs test/mail-cli.test.mjs test/mail-runtime.test.mjs test/mail-server.test.mjs test/markdown-editor.test.mjs test/markdown-media.test.mjs test/markdown-source-map.test.mjs test/modal.test.mjs test/native-decisions.test.mjs test/native-onnx.test.mjs test/native-module-packaging.test.mjs test/native-module-resource-references.test.mjs test/packaging.test.mjs test/pwa-packaging.test.mjs test/pwa-client.test.mjs test/pwa-install.test.mjs test/pwa-worker.test.mjs test/persistent-ai-chat-session.test.mjs test/reference-completeness.test.mjs test/rendered-print.test.mjs test/runtime-api-behavior.test.mjs test/runtime.test.mjs test/scaffold.test.mjs test/speech-playback.test.mjs test/site.test.mjs test/theme-manager.test.mjs test/twin-cloud-images.test.mjs test/twin-cloud.test.mjs test/websocket-client-import-map.test.mjs test/websocket.test.mjs test/update-check.test.mjs test/user-explicit-updates.test.mjs",
|
|
126
|
+
"test:functional": "node ./bin/arcane-test.mjs test/ai-ollama-readiness.test.mjs test/core-ollama-lifecycle.test.mjs test/ai-request-model.test.mjs test/ai-request-retry.test.mjs test/ai-stream-cancellation.test.mjs test/browser-decisions.test.mjs test/browser-speech-providers.test.mjs test/browser-speech-artifacts.test.mjs test/browser-speech-resource-bridge.test.mjs test/browser-wasm-gpu-notice.test.mjs test/browser-wasm-download-resume.test.mjs test/chat-input.test.mjs test/chat-stream-speech.test.mjs test/chat-transcript-scroll.test.mjs test/chat-transient-retention.test.mjs test/cli.test.mjs test/continuous-voice-capture.test.mjs test/codex-app-server.test.mjs test/mcp-stdio-server.test.mjs test/core-classic-source.test.mjs test/core-client.test.mjs test/core-client-installation.test.mjs test/core-ai-installation.test.mjs test/core-llama-lifecycle.test.mjs test/core-development.test.mjs test/core-host.test.mjs test/core-shared-host.test.mjs test/core-model-assets.test.mjs test/core-native-services.test.mjs test/core-packaged-web.test.mjs test/core-runtime.test.mjs test/core-stdio.test.mjs test/dbopfs-committed-events.test.mjs test/dbopfs-document-library.test.mjs test/dbopfs-model-resources.test.mjs test/dbopfs-png.test.mjs test/dbopfs.test.mjs test/dev-server.test.mjs test/dev-pwa.test.mjs test/dom-event-instrumentation.test.mjs test/event-manager.test.mjs test/events.test.mjs test/file-preview.test.mjs test/import-map.test.mjs test/local-image-editing.test.mjs test/mail-cli.test.mjs test/mail-runtime.test.mjs test/mail-server.test.mjs test/markdown-editor.test.mjs test/markdown-media.test.mjs test/markdown-source-map.test.mjs test/modal.test.mjs test/model-resource-bridge.test.mjs test/native-decisions.test.mjs test/native-onnx.test.mjs test/native-module-packaging.test.mjs test/native-module-resource-references.test.mjs test/packaging.test.mjs test/pwa-packaging.test.mjs test/pwa-client.test.mjs test/pwa-install.test.mjs test/pwa-worker.test.mjs test/persistent-ai-chat-session.test.mjs test/reference-completeness.test.mjs test/rendered-print.test.mjs test/runtime-api-behavior.test.mjs test/runtime.test.mjs test/scaffold.test.mjs test/speech-playback.test.mjs test/site.test.mjs test/theme-manager.test.mjs test/twin-cloud-images.test.mjs test/twin-cloud.test.mjs test/websocket-client-import-map.test.mjs test/websocket.test.mjs test/update-check.test.mjs test/user-explicit-updates.test.mjs",
|
|
127
127
|
"test:integration": "node ./bin/arcane-test.mjs test/installed-package-runtime.test.mjs test/root-app-layout.test.mjs test/integrated-shared.test.mjs test/integrated-workspace.test.mjs test/mail-browser.test.mjs test/native-macos-provider.test.mjs test/native-plan.test.mjs test/native-portable-provider.test.mjs test/native-provider-loader.test.mjs test/npm-release.test.mjs test/release-bundle.test.mjs test/release-capability-smoke.test.mjs test/shared-payload-batch.test.mjs test/tarball.test.mjs test/browser-wasm-cpu.test.mjs test/wllama-webgpu-runtime.test.mjs",
|
|
128
128
|
"test:regression": "node ./bin/arcane-test.mjs test/channel-workflows.test.mjs test/html-import-registration.test.mjs test/html-import-packaged-script.test.mjs test/logging-regression.test.mjs test/markdown-speech.test.mjs test/prepared-speech.test.mjs test/native-provider-generation.test.mjs test/speech-queue-regression.test.mjs test/testing.test.mjs test/test-sets.test.mjs",
|
|
129
129
|
"check": "node tools/check-source.mjs && npm test",
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
max-inline-size:100%;
|
|
16
16
|
padding:.2rem;
|
|
17
17
|
border:1px solid var(--app-bar-border,var(--border-color,rgba(255,255,255,.25)));
|
|
18
|
-
border-radius:1.6rem;
|
|
19
|
-
background:color-mix(in srgb,currentColor 8%,transparent);
|
|
18
|
+
border-radius:var(--theme-switcher-radius,1.6rem);
|
|
19
|
+
background:var(--theme-switcher-background,color-mix(in srgb,currentColor 8%,transparent));
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
button {
|
|
@@ -28,11 +28,11 @@
|
|
|
28
28
|
min-block-size:2.75rem;
|
|
29
29
|
padding:.5rem .75rem;
|
|
30
30
|
border:0;
|
|
31
|
-
border-radius:1.4rem;
|
|
31
|
+
border-radius:var(--theme-switcher-option-radius,1.4rem);
|
|
32
32
|
background:transparent;
|
|
33
33
|
color:inherit;
|
|
34
34
|
cursor:pointer;
|
|
35
|
-
font:700 .875rem/1.25 system-ui,sans-serif;
|
|
35
|
+
font:var(--theme-switcher-font,700 .875rem/1.25 system-ui,sans-serif);
|
|
36
36
|
text-align:center;
|
|
37
37
|
white-space:normal;
|
|
38
38
|
overflow-wrap:anywhere;
|
|
@@ -1182,7 +1182,6 @@ class AI {
|
|
|
1182
1182
|
#speechJobSequence=0;
|
|
1183
1183
|
#stopOllamaReady=null;
|
|
1184
1184
|
#ttsSegmentation={...DEFAULT_TTS_SEGMENTATION};
|
|
1185
|
-
#preparedSpeechReadiness = null;
|
|
1186
1185
|
#preparedSpeechPlaybacks = new Set();
|
|
1187
1186
|
#nativeSpeechPlaybacks = new Set();
|
|
1188
1187
|
#speechUnlockContext = null;
|
|
@@ -4394,7 +4393,7 @@ class AI {
|
|
|
4394
4393
|
runtime.#browserSpeechTransition,activation.controller.signal
|
|
4395
4394
|
);
|
|
4396
4395
|
activation.controller.signal.throwIfAborted();
|
|
4397
|
-
await runtime.#providerRuntime.setSpeechMuted(false);
|
|
4396
|
+
await runtime.#providerRuntime.setSpeechMuted(false, {signal: activation.controller.signal});
|
|
4398
4397
|
if(runtime.#speechActivation!==activation)return false;
|
|
4399
4398
|
const status=runtime.#providerRuntime.status('tts');
|
|
4400
4399
|
runtime.muted=status.state!=='ready'||status.loaded!==true;
|
|
@@ -6409,22 +6408,7 @@ class AI {
|
|
|
6409
6408
|
if(selected) {
|
|
6410
6409
|
const status = runtime.#providerRuntime.status('tts');
|
|
6411
6410
|
if(status.state !== 'ready' || status.loaded !== true) {
|
|
6412
|
-
|
|
6413
|
-
const readiness = runtime.#providerRuntime.setSpeechMuted(false);
|
|
6414
|
-
runtime.#preparedSpeechReadiness = readiness;
|
|
6415
|
-
readiness.finally(
|
|
6416
|
-
function releasePreparedSpeechReadiness() {
|
|
6417
|
-
if(runtime.#preparedSpeechReadiness === readiness) {
|
|
6418
|
-
runtime.#preparedSpeechReadiness = null;
|
|
6419
|
-
}
|
|
6420
|
-
}
|
|
6421
|
-
).catch(
|
|
6422
|
-
function reportPreparedSpeechReadinessFailure(error) {
|
|
6423
|
-
runtime.#traceSpeech('prepareTTS.readiness.error', {error});
|
|
6424
|
-
}
|
|
6425
|
-
);
|
|
6426
|
-
}
|
|
6427
|
-
await runtime.#preparedSpeechReadiness;
|
|
6411
|
+
await runtime.#providerRuntime.setSpeechMuted(false, {signal: requestSignal});
|
|
6428
6412
|
assertPreparedSpeechSelection();
|
|
6429
6413
|
}
|
|
6430
6414
|
}
|
|
@@ -1688,6 +1688,7 @@ export class AIProviderRuntime {
|
|
|
1688
1688
|
#speechDesiredMuted = true;
|
|
1689
1689
|
#speechTransition = Promise.resolve();
|
|
1690
1690
|
#speechTransitionOwners = 0;
|
|
1691
|
+
#speechActivation = null;
|
|
1691
1692
|
#unsubscribeIntents = null;
|
|
1692
1693
|
#closed = false;
|
|
1693
1694
|
#closing = false;
|
|
@@ -2784,6 +2785,11 @@ export class AIProviderRuntime {
|
|
|
2784
2785
|
}
|
|
2785
2786
|
if (slot.loadPromise) {
|
|
2786
2787
|
if (this.#sameSelection(slot.selection, targetSelection)) {
|
|
2788
|
+
const activation = this.#speechActivation;
|
|
2789
|
+
if (role === 'tts' && activation?.loadPromise === slot.loadPromise
|
|
2790
|
+
&& signal !== activation.controller.signal) {
|
|
2791
|
+
return this.#observeSpeechActivation(activation, signal);
|
|
2792
|
+
}
|
|
2787
2793
|
return slot.loadPromise;
|
|
2788
2794
|
}
|
|
2789
2795
|
return Promise.reject(
|
|
@@ -2957,6 +2963,10 @@ export class AIProviderRuntime {
|
|
|
2957
2963
|
}
|
|
2958
2964
|
);
|
|
2959
2965
|
slot.loadPromise = loadOperation;
|
|
2966
|
+
if (role === 'tts' && this.#speechActivation
|
|
2967
|
+
&& signal === this.#speechActivation.controller.signal) {
|
|
2968
|
+
this.#speechActivation.loadPromise = loadOperation;
|
|
2969
|
+
}
|
|
2960
2970
|
publishAIRuntimeRoleState(
|
|
2961
2971
|
role,
|
|
2962
2972
|
roleRecord(
|
|
@@ -4270,19 +4280,34 @@ export class AIProviderRuntime {
|
|
|
4270
4280
|
return true;
|
|
4271
4281
|
}
|
|
4272
4282
|
|
|
4273
|
-
async setSpeechMuted(muted) {
|
|
4283
|
+
async setSpeechMuted(muted, {signal = null} = {}) {
|
|
4274
4284
|
arcaneLogging.debug('[Arcane speech runtime] setSpeechMuted', muted);
|
|
4275
4285
|
this.#assertOpen();
|
|
4276
4286
|
this.#assertNotConfiguring();
|
|
4277
4287
|
if (!is.boolean(muted)) {
|
|
4278
4288
|
fail('AI speech muted state must be a boolean.');
|
|
4279
4289
|
}
|
|
4290
|
+
assertAbortSignal(signal);
|
|
4291
|
+
if (signal?.aborted) throw normalizedAbort(signal.reason);
|
|
4292
|
+
if (!muted && !this.#speechDesiredMuted && this.#speechActivation
|
|
4293
|
+
&& !this.#speechActivation.controller.signal.aborted) {
|
|
4294
|
+
return this.#observeSpeechActivation(this.#speechActivation, signal);
|
|
4295
|
+
}
|
|
4280
4296
|
this.#speechDesiredMuted = muted;
|
|
4281
4297
|
if (muted) {
|
|
4282
4298
|
this.#speechMuted = true;
|
|
4299
|
+
this.#speechActivation?.controller.abort();
|
|
4283
4300
|
this.#slots.tts.loadController?.abort();
|
|
4284
4301
|
this.cancel('tts');
|
|
4285
4302
|
}
|
|
4303
|
+
const activation = muted ? null : {
|
|
4304
|
+
controller: new AbortController(),
|
|
4305
|
+
interests: new Set(),
|
|
4306
|
+
promise: null,
|
|
4307
|
+
loadPromise: null,
|
|
4308
|
+
settled: false
|
|
4309
|
+
};
|
|
4310
|
+
this.#speechActivation = activation;
|
|
4286
4311
|
const runtime = this;
|
|
4287
4312
|
this.#speechTransitionOwners += 1;
|
|
4288
4313
|
const transition = this.#speechTransition.catch(
|
|
@@ -4290,6 +4315,9 @@ export class AIProviderRuntime {
|
|
|
4290
4315
|
).then(
|
|
4291
4316
|
async function reconcileLatestAISpeechPreference() {
|
|
4292
4317
|
while (true) {
|
|
4318
|
+
if (activation?.controller.signal.aborted) {
|
|
4319
|
+
throw normalizedAbort(activation.controller.signal.reason);
|
|
4320
|
+
}
|
|
4293
4321
|
const desiredMuted = runtime.#speechDesiredMuted;
|
|
4294
4322
|
if (desiredMuted) {
|
|
4295
4323
|
runtime.cancel('tts');
|
|
@@ -4306,7 +4334,11 @@ export class AIProviderRuntime {
|
|
|
4306
4334
|
if (runtime.#speechDesiredMuted) {
|
|
4307
4335
|
continue;
|
|
4308
4336
|
}
|
|
4309
|
-
|
|
4337
|
+
const currentActivation = runtime.#speechActivation;
|
|
4338
|
+
if (currentActivation?.controller.signal.aborted) {
|
|
4339
|
+
throw normalizedAbort(currentActivation.controller.signal.reason);
|
|
4340
|
+
}
|
|
4341
|
+
await runtime.load('tts', {signal: currentActivation?.controller.signal ?? null});
|
|
4310
4342
|
}
|
|
4311
4343
|
if (runtime.#speechDesiredMuted === desiredMuted) {
|
|
4312
4344
|
runtime.#speechMuted = desiredMuted;
|
|
@@ -4318,10 +4350,52 @@ export class AIProviderRuntime {
|
|
|
4318
4350
|
).finally(
|
|
4319
4351
|
function releaseAIProviderSpeechTransitionOwnership() {
|
|
4320
4352
|
runtime.#speechTransitionOwners -= 1;
|
|
4353
|
+
if (activation) {
|
|
4354
|
+
activation.settled = true;
|
|
4355
|
+
if (runtime.#speechActivation === activation) {
|
|
4356
|
+
runtime.#speechActivation = null;
|
|
4357
|
+
}
|
|
4358
|
+
}
|
|
4321
4359
|
}
|
|
4322
4360
|
);
|
|
4323
4361
|
this.#speechTransition = transition;
|
|
4324
|
-
return transition;
|
|
4362
|
+
if (!activation) return transition;
|
|
4363
|
+
activation.promise = transition;
|
|
4364
|
+
return this.#observeSpeechActivation(activation, signal);
|
|
4365
|
+
}
|
|
4366
|
+
|
|
4367
|
+
#observeSpeechActivation(activation, signal) {
|
|
4368
|
+
return new Promise(function observeAIProviderSpeechActivation(resolve, reject) {
|
|
4369
|
+
const interest = {};
|
|
4370
|
+
let settled = false;
|
|
4371
|
+
activation.interests.add(interest);
|
|
4372
|
+
function releaseSpeechActivationInterest() {
|
|
4373
|
+
activation.interests.delete(interest);
|
|
4374
|
+
signal?.removeEventListener('abort', cancelSpeechActivationInterest);
|
|
4375
|
+
}
|
|
4376
|
+
function cancelSpeechActivationInterest() {
|
|
4377
|
+
if (settled) return;
|
|
4378
|
+
settled = true;
|
|
4379
|
+
releaseSpeechActivationInterest();
|
|
4380
|
+
if (!activation.settled && activation.interests.size === 0) {
|
|
4381
|
+
activation.controller.abort(signal?.reason);
|
|
4382
|
+
}
|
|
4383
|
+
reject(normalizedAbort(signal?.reason));
|
|
4384
|
+
}
|
|
4385
|
+
signal?.addEventListener('abort', cancelSpeechActivationInterest, {once: true});
|
|
4386
|
+
activation.promise.then(function speechActivationReady(status) {
|
|
4387
|
+
if (settled) return;
|
|
4388
|
+
settled = true;
|
|
4389
|
+
releaseSpeechActivationInterest();
|
|
4390
|
+
resolve(status);
|
|
4391
|
+
}, function speechActivationFailed(error) {
|
|
4392
|
+
if (settled) return;
|
|
4393
|
+
settled = true;
|
|
4394
|
+
releaseSpeechActivationInterest();
|
|
4395
|
+
reject(error);
|
|
4396
|
+
});
|
|
4397
|
+
if (signal?.aborted) cancelSpeechActivationInterest();
|
|
4398
|
+
});
|
|
4325
4399
|
}
|
|
4326
4400
|
|
|
4327
4401
|
#providerFor(slot) {
|