arcane-os 0.95.0 → 0.97.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.
@@ -110,17 +110,18 @@ try {
110
110
  }
111
111
  ```
112
112
 
113
- `prepare({id,workingDirectory,members,signal,onProgress,refresh=false})` reuses
113
+ `prepare({id,workingDirectory,members,archives,signal,onProgress,refresh=false})` reuses
114
114
  a complete native acquisition or fetches the explicitly selected members.
115
115
  Every member has its original relative `path` and selected `url`; the caller
116
116
  supplies the complete companion-file set. Independent members download
117
- concurrently. Each complete response stream is written directly to its native
117
+ concurrently, with up to four independent source transfers active. Each complete response stream is written directly to its native
118
118
  file, without a whole-model buffer or base64 conversion. No model or runtime
119
119
  is selected or downloaded by service startup. The result remains the ready
120
120
  projection snapshot with ordered `members: [{path,nativePath}]`.
121
121
 
122
- Complete acquisitions persist under `workingDirectory/model-assets/` across
123
- release, native unload, service disposal and process restart. Relative working
122
+ Complete acquisitions and interrupted transfer state persist under
123
+ `workingDirectory/model-assets/` across release, native unload, service disposal
124
+ and process restart. Relative working
124
125
  directories resolve from `appRoot`. Reuse matches the entire ordered
125
126
  `[{path,url}]` selection in that actual store; the operation `id` is a separate
126
127
  live lifetime, not the stored selection. Different URLs, relative paths, member
@@ -136,14 +137,193 @@ untouched. Later ordinary preparations reuse the newer completed selection.
136
137
  A failed refresh leaves the earlier completed acquisition available. Refresh
137
138
  does not change the selected URLs or imply upstream revision discovery.
138
139
 
139
- Progress uses `open`, `download`, `complete`, `reuse` and `ready` phases, with
140
- `completed`, `total` and `unit:'files'`; member progress additionally includes
141
- `memberIndex` and `path`. Progress callbacks may return a promise, which the
140
+ Progress includes `open`, `download`, `resume`, `restart`, `complete`, `reuse`
141
+ and `ready`, plus waiting, heartbeat, stalled and terminal interruption states.
142
+ Records carry `state`, a user-facing `message`, `completed`, `total` and
143
+ `unit:'files'`; member progress additionally includes `memberIndex` and `path`.
144
+ Recovery guidance belongs in `recovery`; a complete serialized `error` belongs
145
+ in developer inspection. A stalled state means no recent transfer activity was
146
+ observed, not that the SDK has established an Internet outage. Render active
147
+ transfers without inventing a per-file percentage. Progress callbacks may return a promise, which the
142
148
  preparation observes in order. Each caller owns its callbacks; a slow or failed
143
149
  callback does not hold another caller's transfer or result. Callbacks must not
144
150
  await cleanup of the preparation currently calling them. Callback failures
145
151
  reject that caller's preparation. HTTP failures
146
152
  retain `url`, `status` and the complete response text in `response`.
153
+ During an active member, repeated activity notifications are emitted at most
154
+ once per second; opening, member completion and terminal phases remain distinct.
155
+ Progress describes files and phases, never transfer volume.
156
+
157
+ ### Selected archive companions
158
+
159
+ An optional `archives` array acquires exact companion members from an upstream
160
+ uncompressed TAR source through this same acquisition owner:
161
+
162
+ ```javascript
163
+ const projection = await modelAssets.prepare({
164
+ id: selectedOperationId,
165
+ workingDirectory: selectedModelDirectory,
166
+ members: selectedModelFiles,
167
+ archives: [{
168
+ url: selectedArchiveURL,
169
+ format: 'tar',
170
+ members: [
171
+ {path: 'tokenizer/model_config.yaml', member: 'model_config.yaml'},
172
+ {path: 'tokenizer/speakers.json', member: 'speakers.json'}
173
+ ]
174
+ }],
175
+ signal,
176
+ onProgress
177
+ });
178
+ ```
179
+
180
+ The result orders ordinary members first, followed by each archive's declared
181
+ members. Extraction preserves each complete selected file. TAR names and PAX
182
+ path records select members; a leading `./` is archive path framing. Missing
183
+ selected members reject preparation and identify the missing names. Gzip and
184
+ other archive formats are outside this reader's contract.
185
+
186
+ When the upstream catalog supplies aligned TAR segments, an archive may include
187
+ `ranges:[{start,end,stopBefore?}]`. The reader requests each inclusive HTTP range
188
+ once and parses its TAR framing; `stopBefore` ends that segment before the
189
+ specified member. Offsets and TAR lengths are solely external transport framing.
190
+ They are never download progress, model readiness criteria or content identity.
191
+ Ranges require the matching HTTP partial-content response; no complete-archive
192
+ fallback is silently substituted. Native reuse identifies archive members by
193
+ their selected URL, archive member name and output path, alongside ordinary
194
+ members, without re-fetching an already completed set.
195
+
196
+ ## Discover and open installed files
197
+
198
+ `discover({directories,catalog=[],recursive=true,signal})` inspects only the
199
+ explicit directories, without downloading, copying, loading or selecting a
200
+ model. Paths resolve from `appRoot`. It returns:
201
+
202
+ ```javascript
203
+ {
204
+ directories: [{directory, exists, error}], // error is present only on failure
205
+ models: [{
206
+ directory,
207
+ entryPath,
208
+ members: [{path, nativePath, available}],
209
+ missingMembers: [],
210
+ complete: true,
211
+ source: 'directory', // or 'catalog' or 'acquisition'
212
+ catalogId // present for a matching caller-owned catalog entry
213
+ }]
214
+ }
215
+ ```
216
+
217
+ Ordinary `.gguf` files are candidates. Split names such as
218
+ `moon-00001-of-00003.gguf` form one candidate with all expected members in numeric
219
+ order, including explicit missing members. The first shard is `entryPath`,
220
+ including when a completed acquisition declared only later shards or listed
221
+ them out of order. Acquisition candidates retain all their ordinary and
222
+ companion members alongside these required shards; separate filename groups
223
+ are never inferred to be one model. Symlinked model files and directories are
224
+ followed, with already visited directory targets reused rather than rescanned.
225
+ Discovery does not establish a model's role, runtime compatibility or successful
226
+ inference. Those selections remain with the application and engine owners.
227
+
228
+ Catalog entries are `{id,members:[{path,url?}],archives?,entryPath?}`. They describe
229
+ an exact companion set and can include nested tokenizer files or multiple model
230
+ files. A discovered catalog candidate reports every expected member, including
231
+ missing companions. Completed native acquisition records are also discoverable;
232
+ when the catalog supplies URLs or archive member names, acquisition matching
233
+ also preserves those recorded source selections. A different pinned source with
234
+ the same filenames is not relabeled as that catalog selection. Path-only catalogs
235
+ and ordinary installed-directory candidates make no source-revision claim.
236
+ Unfinished acquisition attempts are ignored. Discovery never removes either.
237
+ Set `recursive:false` for a directory-only inspection. Missing root directories
238
+ are reported with `exists:false`; other root access failures include their
239
+ complete serialized error. A failure while reading a descendant is surfaced.
240
+
241
+ `openExisting({id,directory,members:[{path}],signal})` opens a complete selected
242
+ set as an ordinary ready projection. It rechecks every member and reports
243
+ `MODEL_ASSET_MEMBERS_MISSING` with `members` when files are absent. It performs
244
+ no copy or network request. Engines use the same `retain(id)` lifecycle.
245
+ Preparation release, engine unload and service disposal never delete borrowed
246
+ originals. External edits or deletion remain controlled by the files' owner.
247
+
248
+ The application supplies persistent model directories separately from disposable
249
+ build output. The native launch context exposes its platform-specific
250
+ `modelsDirectory`; apps may additionally inspect selected application directories.
251
+ The model-assets service creates no platform directory policy of its own.
252
+
253
+ `resolveNativeLaunchContext({appId,context})` defaults `modelsDirectory` to
254
+ one shared location for the operating-system user, independent of the app ID.
255
+ It preserves an explicitly supplied `modelsDirectory` unchanged and creates,
256
+ moves or migrates no files:
257
+
258
+ | Host | Default shared model directory |
259
+ | --- | --- |
260
+ | Windows | `%LOCALAPPDATA%/Arcane/Models`; the user's `AppData/Local` when that environment variable is absent |
261
+ | macOS | `~/Library/Application Support/Arcane/Models` |
262
+ | Linux | `$XDG_DATA_HOME/Arcane/Models` when absolute, otherwise `~/.local/share/Arcane/Models` |
263
+ | Android or another host adaptation | `<stateRoot>/Models`; the host supplies an explicit shared `modelsDirectory` when its platform allows sharing |
264
+
265
+ Pass this `modelsDirectory` as the acquisition `workingDirectory` and as one
266
+ discovery directory. Completed files then live beneath its `model-assets`
267
+ subdirectory, separate from replaceable application builds. Additional selected
268
+ application directories can participate in the same discovery call.
269
+
270
+ ```javascript
271
+ import {resolveNativeLaunchContext} from 'arcane-os/core/host';
272
+
273
+ const {modelsDirectory} = resolveNativeLaunchContext({appId: 'moon-radio'});
274
+ const projection = await modelAssets.prepare({
275
+ id: selectedOperationId,
276
+ workingDirectory: modelsDirectory,
277
+ members: selectedMembers,
278
+ signal,
279
+ onProgress: renderPreparationProgress
280
+ });
281
+ ```
282
+
283
+ App settings, profiles, chat history, workspace and IPC retain their app-private
284
+ locations. This default applies to applications using the native launch resolver;
285
+ explicit acquisition directories and other provider-owned model stores keep
286
+ their existing contracts. Discover existing app-local models and open them in
287
+ place rather than copying or removing them.
288
+
289
+ ### Prepare a selected local runtime on demand
290
+
291
+ `createLocalAIService(configuration,{autoStart:false,...options})` registers the
292
+ existing local-AI owner without preparing or starting its selected runtimes at
293
+ Core startup. The default remains `autoStart:true`, preserving existing startup
294
+ behavior. After the user explicitly requests preparation, invoke
295
+ `client.invoke('localai.prepare', {}, {signal,timeoutMs:0})`. The operation uses
296
+ the configured existing runtime preparation callback and returns the current
297
+ local-AI state; it does not invent a runtime or model selection.
298
+
299
+ Preparation callback events are published as `localai.preparation` with their
300
+ complete original event payload. Subscribe through the existing Core event
301
+ owner to display actual preparation phases. Concurrent preparation requests
302
+ reuse the owner's in-flight work, and subsequent completed calls reuse its
303
+ prepared state. Runtime and model selection, explicit model acquisition and
304
+ microphone activation remain app-owned; preparation is not an inference or
305
+ microphone operation. Rendering the interface and
306
+ discovering installed files need not wait for runtime preparation.
307
+
308
+ ### Browser discovery and explicit download
309
+
310
+ `arcane-os/ai/core-model-assets` additionally exports
311
+ `discoverCoreModelAssets({client,directories,catalog,recursive,signal})`,
312
+ `openExistingCoreModelAssets({client,directory,members,signal})`, and
313
+ `downloadCoreModelAssets({client,workingDirectory,members,archives,refresh,signal,onProgress})`.
314
+ `client` defaults to the installed Core client. Discovery returns the native
315
+ record above; opening and downloading return `{id,directory,members,release}`.
316
+ The existing Blob-based `prepareCoreModelAssets` keeps its original contract.
317
+
318
+ Download progress callbacks receive the complete semantic progress record,
319
+ correlated to this preparation and observed in order. On request failure, queued
320
+ terminal progress is delivered before internal cleanup cancellation and promise
321
+ rejection. Explicit caller cancellation retains its no-late-callback boundary.
322
+ Callback failure cancels the operation. Cancellation joins explicit native cleanup and local callback
323
+ delivery before the helper settles; cleanup failures remain observable.
324
+ Callbacks must not await their own enclosing preparation or cancellation join.
325
+ Applications initiate downloads explicitly and display actual phases, selected
326
+ member paths, completed-file counts and errors in the appropriate surface.
147
327
 
148
328
  `release(id)` relinquishes preparation ownership without a request signal.
149
329
  Same-process callers sharing the actual store, complete source selection and
@@ -151,24 +331,31 @@ refresh mode share an acquisition, including across service instances. They
151
331
  keep independent operation IDs, retain handles, cancellation and callbacks.
152
332
  Cancelling, releasing or disposing one interested caller leaves the others
153
333
  running. The final interested caller aborts the acquisition and joins its
154
- streams, writes and closes before removing only its unfinished attempt. Original
334
+ streams, writes and closes while preserving completed members and resumable
335
+ partial transfers. Original
155
336
  failures and cleanup failures remain observable. A completed acquisition stays
156
337
  available even if a caller cancels or its callback fails after completion.
157
338
 
158
- The service records completion only after every selected response and file
159
- close finishes. That record is ordinary cache state describing the selected
160
- sources and completed acquisition. A missing original member requires another
161
- complete acquisition. Failed attempts are removed after their owned I/O drains;
162
- there is no partial-transfer resumption. An abruptly interrupted process can
163
- leave an unfinished directory, which later calls ignore without sweeping it.
164
- There is no automatic migration, eviction or removal of completed acquisitions.
165
-
166
- Independent processes use disjoint attempt directories and may fetch the same
167
- selection concurrently. They do not overwrite each other's files or coordinate
168
- a cross-process acquisition lock. Concurrent completions can produce multiple
169
- reusable sets; either complete result may be selected when their completion
170
- order ties. Applications may retain their existing shared Core owner without
171
- introducing an additional host or application-local cache copy.
339
+ The service promotes each file from its partial-transfer location only after
340
+ the complete response and file close finish. It records whole-acquisition
341
+ completion after every selected member is complete. These records describe
342
+ ordinary transfer and cache state, not content hashes or admission receipts.
343
+ Retry reuses completed members and resumes saved ordinary-file transfers with
344
+ HTTP Range and available HTTP continuation metadata. HTTP offsets and validators
345
+ remain transport-local and never become progress or model policy. If the source
346
+ cannot continue the saved representation, the SDK reports a restart of that
347
+ member; completed siblings remain available. Selected TAR companions preserve
348
+ completed files; an interrupted member may require replaying its TAR segment to
349
+ restore the archive parser's framing. No full-archive substitute is selected.
350
+
351
+ Processes selecting the same ordered sources and actual store coordinate a
352
+ short selection operation and one transfer owner. Another process waits with
353
+ observable status rather than starting a duplicate transfer. Cancelling a
354
+ waiting request stops its wait; it does not cancel the other process. If a
355
+ transfer owner exits, a later contender can reclaim its recorded operation and
356
+ continue retained data. This requires no additional application server, copied
357
+ cache implementation or new shared Core process. Existing completed models and
358
+ unrelated attempts are never swept or migrated automatically.
172
359
 
173
360
  ```javascript
174
361
  import {createModelAssetService} from 'arcane-os/core/model-assets';
@@ -186,7 +373,8 @@ try {
186
373
  ```
187
374
 
188
375
  `createModelAssetService({appRoot})` returns a Core service with `current()`,
189
- `prepare(options)`, `retain(id)`, `release(id)` and `dispose()`. `retain()` accepts only a completed projection
376
+ `discover(options)`, `openExisting(options)`, `prepare(options)`, `retain(id)`,
377
+ `release(id)` and `dispose()`. `retain()` accepts only a completed projection
190
378
  and returns `{id,directory,members,release}`. A native engine takes this handle
191
379
  before loading and releases it after actual unload, failed-load cleanup or
192
380
  worker exit. A rejected inference/load promise alone does not establish native
@@ -249,6 +437,10 @@ for source selection, cancellation and complete row/output contracts.
249
437
  | Method | Parameters | Result |
250
438
  | --- | --- | --- |
251
439
  | `modelAssets.open` | `{id,workingDirectory,members:[{path}]}` | Preparing projection and native member paths |
440
+ | `modelAssets.discover` | `{directories,catalog?,recursive?}` | Directory status and complete/incomplete model candidates |
441
+ | `modelAssets.openExisting` | `{id,directory,members:[{path}]}` | Ready retained projection of existing originals |
442
+ | `modelAssets.prepare` | `{id,workingDirectory,members,archives?,refresh?}` | Ready persistent acquisition; request-scoped `modelAssets.progress` emits `{id,progress}` |
443
+ | `modelAssets.cancelPrepare` | `{id}` | Cancel and join the matching native preparation, then release its preparation ownership |
252
444
  | `modelAssets.write` | `{id,memberIndex,contentBase64}` | `{id,memberIndex,written:true}` after the native write |
253
445
  | `modelAssets.complete` | `{id}` | Ready projection after all files close |
254
446
  | `modelAssets.release` | `{id}` | Released preparation ownership; native uses may retain the files |
@@ -258,6 +450,10 @@ The browser helper owns this transport sequence. Correlation IDs identify
258
450
  operations; they are unrelated to content identity. Original model files remain
259
451
  separate and complete. A retry creates a new working projection from the stored
260
452
  assets; it does not redownload them or replace the authoritative DBOPFS record.
453
+ `cancelPrepare` and `release` have service lifetime so cancellation of the
454
+ original request cannot cancel its cleanup control. Native preparation follows
455
+ the original request signal, including connection cancellation; raw RPC callers
456
+ join `cancelPrepare` when cancelling and release any successful unused projection.
261
457
 
262
458
  The implementation uses browser streams, the existing portable Core JSON
263
459
  transport and Node filesystem operations. Native paths and cleanup follow the
@@ -1,6 +1,6 @@
1
1
  # Native speech service
2
2
 
3
- `createSpeechService({stt, tts, signal} = {})` composes independently selected
3
+ `createSpeechService({stt, tts, signal, autoLoad = true} = {})` composes independently selected
4
4
  native speech engines into one Core service. It is the named and default export
5
5
  from `arcane-os/core/speech`.
6
6
 
@@ -50,6 +50,13 @@ independently. It returns immediately, allowing Core and `speech.status` to
50
50
  respond while models load. A failure in one role leaves the other role usable.
51
51
  Each engine publishes the actual state of its selected model.
52
52
 
53
+ Use `autoLoad: false` when the host first needs the user to select or download
54
+ model files. Startup still subscribes and exposes the ordinary unloaded state;
55
+ it does not call either engine's `load()`. Explicit `speech.load({role,
56
+ ...selection})` activates the prepared role afterward. The default remains
57
+ `true` for existing hosts. This option changes service startup, not the selected
58
+ engine's missing-model errors or inference readiness requirement.
59
+
53
60
  `current()` and `speech.status` return the same snapshot:
54
61
 
55
62
  | Field | Meaning |
@@ -66,9 +73,10 @@ An engine's state includes `providerId`, `modelId`, `state`, `loaded`, and
66
73
  `busy`, with its complete error or progress information when present. An
67
74
  omitted role has `state: 'unavailable'`, `loaded: false`, and `busy: false`.
68
75
  File presence, a spawned process, and a callable bridge do not establish loaded
69
- model readiness. Existing readiness consumers additionally recognize
70
- the engine IDs `whisper.cpp` and `kokoro-onnx`; another engine ID requires the
71
- corresponding consumer integration.
76
+ model readiness. The shared AI adapter follows the selected engine identity
77
+ and model in these per-role snapshots, including Nemotron and Magpie. Only the
78
+ older aggregate-only fallback retains fixed `whisper.cpp`/`whisper-small` and
79
+ `kokoro-onnx`/`kokoro` expectations.
72
80
 
73
81
  Each role also reports `supportsStreaming`: STT requires the configured
74
82
  engine's `openStream()` method, and TTS requires its `synthesizeStream()` method.
@@ -288,6 +296,122 @@ share one shutdown promise. Actual engine cleanup failures reject with an
288
296
  releases the service's event subscriptions. New work after shutdown begins
289
297
  rejects with `CORE_CLOSING`.
290
298
 
299
+ ## Selected NeMo model acquisition
300
+
301
+ `NEMO_SPEECH_MODELS`, `prepareNemotronModels`, and `prepareMagpieModels` are
302
+ named exports from `arcane-os/local-ai/nemotron`. Importing them performs no
303
+ network access, installation, model loading, or inference. The catalog records
304
+ NVIDIA's selected [NeMo-Speech.cpp v0.2.0 model index](https://github.com/NVIDIA/NeMo-Speech.cpp/blob/v0.2.0/models/index.json):
305
+
306
+ | Role | Complete default selection |
307
+ |---|---|
308
+ | `stt` | Nemotron 3.5 streaming ASR Q8_0 and Nemotron 3 Diarization Q8_0. |
309
+ | `tts` | Magpie multilingual v2607 F16, NanoCodec 22 kHz F16 decoder, and all ten matching tokenizer/configuration/speaker-map members from the same Magpie revision. |
310
+
311
+ Each role exposes `{modelId, providerId, name, members, resourcePaths}`.
312
+ `members` contains ordered `{path, url}` file sources. TTS also exposes
313
+ `archives`, with the upstream uncompressed `.nemo` TAR source, its selected
314
+ transport ranges, and exact `{path, member}` tokenizer entries. The shared
315
+ [model-assets service](model-assets.md) owns discovery, acquisition, progress,
316
+ cancellation, persistence, and projection lifetime; these helpers do not create
317
+ another downloader or store. Tokenizer files retain their complete original
318
+ contents. The selected tokenizer configuration and its referenced JSON speaker
319
+ map continue to own voice IDs.
320
+
321
+ ```js
322
+ import {createNemotronRuntime, prepareNemotronModels} from 'arcane-os/local-ai/nemotron';
323
+ import {createSpeechService} from 'arcane-os/core/speech';
324
+
325
+ const stt = createNemotronRuntime({runtime, modelAssets, gpu: -1});
326
+ const speech = createSpeechService({stt, autoLoad: false});
327
+ // Run this only for the user's explicit model preparation action.
328
+ const prepared = await prepareNemotronModels({
329
+ modelAssets,
330
+ workingDirectory: modelsDirectory,
331
+ directories: [modelsDirectory, appModelsDirectory],
332
+ signal,
333
+ onProgress: showModelProgress
334
+ });
335
+ try {
336
+ await stt.load({
337
+ assetProjectionId: prepared.assetProjectionId,
338
+ resourcePaths: prepared.resourcePaths,
339
+ signal
340
+ });
341
+ } finally {
342
+ await prepared.release();
343
+ }
344
+ ```
345
+
346
+ Both preparation helpers accept `{modelAssets, workingDirectory, directories,
347
+ id, signal, onProgress, refresh = false}`. With `directories`, they ask the
348
+ shared owner for the first complete matching catalog selection in those roots
349
+ before downloading. `openExisting()` borrows its original files and never
350
+ deletes them. Incomplete selections stay visible through the owner's discovery
351
+ results and are not mistaken for a complete model. When no complete match is
352
+ available, the existing acquisition path prepares the complete selected set;
353
+ its completed members and closed partial files remain available after an
354
+ interruption or cancellation. Explicit retry reuses those members and resumes
355
+ ordinary HTTP transfers when the source accepts their conditional range;
356
+ otherwise only that member restarts with an explicit progress event. TAR
357
+ segments replay while completed tokenizer companions remain reusable.
358
+ Matching cross-process selections wait for the actual transfer owner, then
359
+ reuse or resume its recorded attempt; unrelated downloads remain concurrent.
360
+ See [native acquisition and recovery](model-assets.md#native-ownership) for the
361
+ shared owner's complete contract. Explicit `refresh: true` requests a
362
+ fresh acquisition. The host chooses the roots: on Windows, Linux and macOS its
363
+ portable native launch-context resolver defaults `modelsDirectory` to the
364
+ operating-system user's shared `Arcane/Models` directory, independently of
365
+ app-private state and workspace. Explicit directory overrides remain unchanged;
366
+ other adapted hosts supply their shared directory explicitly or retain the
367
+ resolver's app-state fallback. This location applies to services using that
368
+ resolver value, not every provider store. Resolution moves no existing files.
369
+ An application may include its own `models` directory and prior app-local
370
+ model locations among the discovery roots, borrowing those originals in place.
371
+
372
+ The result is `{assetProjectionId, directory, members, resourcePaths, release}`.
373
+ `release()` is idempotent and releases preparation ownership. Each engine
374
+ retains the projection synchronously when `load()` accepts it, so the caller
375
+ may release its preparation after starting that load. Nemotron accepts
376
+ `modelAssets`, `assetProjectionId`, and `resourcePaths` in its constructor and
377
+ `assetProjectionId`/`resourcePaths` in `load()`, with roles `{model, diarization}`.
378
+ Magpie uses `{model, codec, tokenizerConfig}`. Native file paths still work
379
+ through the engines' existing options. Nemotron requires unloading before
380
+ changing an active selection; its retained files are released only after
381
+ native work and helper exit have joined. Load failures also join asset cleanup,
382
+ and cleanup failures remain observable.
383
+
384
+ `onProgress` receives ordered, awaited shared-owner phase and file-completion
385
+ events. Native acquisition includes `state`, plain `message`, file counts and
386
+ optional member, reason, recovery, error or other-owner activity. Its five-second
387
+ heartbeat and fifteen-second no-source-activity `stalled` state describe actual
388
+ transfer observations, not an inferred outage or percentage. `interrupted`,
389
+ `cancelled`, `failed` and `ready` are distinct terminal states. Direct
390
+ installed-directory reuse retains the existing `reuse`/`ready` records with
391
+ `completed`, `total` and `unit:'files'`.
392
+
393
+ Signal cancellation stops that caller's preparation interest; another interested
394
+ caller may continue the shared acquisition. The final cancellation joins I/O
395
+ and retains resumable data; cancelled subscribers keep their existing callback
396
+ boundary. An abort after projection creation releases preparation ownership
397
+ before rejecting. Engine load cancellation remains separate: cancelling the
398
+ caller's wait does not cancel a shared activation; explicit engine unload/close
399
+ owns that cancellation.
400
+
401
+ The default GGUF selections are individual complete upstream files. The shared
402
+ owner also discovers ordered GGUF shard sets and reports their missing members;
403
+ HTTP range transfers and tokenizer TAR ranges are transport mechanisms, not
404
+ additional model shards. A discovered shard set alone does not establish that a
405
+ selected native engine can load that model. The engine remains the authority
406
+ for model compatibility and actual readiness.
407
+
408
+ The [upstream native runtime](https://github.com/NVIDIA/NeMo-Speech.cpp/blob/v0.2.0/docs/install.md)
409
+ provides Windows, Linux, and macOS distributions. Model files and that runtime
410
+ remain separate upstream assets, not vendored SDK payloads. This acquisition
411
+ integration received source review only; no download, inference, or new platform
412
+ execution is claimed here. See the [Nemotron](native-nemotron.md) and
413
+ [Magpie](native-magpie.md) contracts for native preparation and execution evidence.
414
+
291
415
  ## Native Kokoro engine
292
416
 
293
417
  `createNativeKokoroRuntime` is the named and default export from