arcane-os 0.65.0 → 0.66.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 CHANGED
@@ -1,5 +1,29 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.66.0
4
+
5
+ - Add `readGitIdentity` from `arcane-os/core/repositories` for separate global,
6
+ repository-local and effective Git configuration observations. Preserve full
7
+ name/email/githubUser values, empty versus unset fields, configured includes,
8
+ joined cancellation and complete process/observer failures.
9
+ - Add optional construction-time `gitIdentity: {name, email, username}` to
10
+ repository workspaces and bare text snapshots. Explicit author fields apply
11
+ only to child Git configuration/environment; omitted fields retain ordinary
12
+ inheritance. Username is a credential hint, not authenticated-account proof.
13
+ - Preserve complete selected text, existing clone/pull/commit/push behavior,
14
+ ordered workspace operations and shared snapshot acquisition. No Git config
15
+ file, remote, credentials, application preferences or data is rewritten.
16
+ - Preserve original process errors when an observer failure is already included,
17
+ and report non-UTF-8 Git identity output with its complete raw diagnostics
18
+ instead of returning substituted identity text.
19
+ - Honor an explicit browser decision `dtype` while preserving the existing
20
+ Laya FP16 and Julia FP32 defaults. Share the complete tensor-record preparation
21
+ with the separately developing native owner; this does not deliver native Laya.
22
+ - Keep the dependency tree unchanged. Native Laya and target-bound repository
23
+ publication remain separate work. Retained compatible native assets continue
24
+ to use matching numeric-release routes; optional NPU upload follow-through
25
+ remains separately tracked.
26
+
3
27
  ## 0.65.0
4
28
 
5
29
  - Add optional precompiled Windows x64 Intel NPU encoding for native Whisper
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.65.0` SDK contract. Development applications declare
23
+ This checkout defines the `0.66.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;
@@ -74,7 +74,8 @@ class BrowserDecisionModel {
74
74
  #error = null;
75
75
  #disposed = false;
76
76
 
77
- constructor({family, model, revision = 'main', device = 'webgpu', runtime = {}} = {}) {
77
+ constructor({family, model, revision = 'main', device = 'webgpu',
78
+ dtype = family === 'laya' ? 'fp16' : 'fp32', runtime = {}} = {}) {
78
79
  if (family !== 'laya' && family !== 'julia') {
79
80
  throw new TypeError('family must be "laya" or "julia".');
80
81
  }
@@ -89,7 +90,7 @@ class BrowserDecisionModel {
89
90
  model: requiredText(model, 'model'),
90
91
  revision: requiredText(revision, 'revision'),
91
92
  device: requiredText(device, 'device'),
92
- dtype: family === 'laya' ? 'fp16' : 'fp32',
93
+ dtype: requiredText(dtype, 'dtype'),
93
94
  runtime: {...runtime, moduleUrl: requiredText(moduleUrl, 'runtime.moduleUrl')}
94
95
  };
95
96
  this.#events = createArcaneEventSource(
@@ -82,7 +82,8 @@ export function encodeDecisionRows(tokenizer, rows, family) {
82
82
  return {encoded, pad};
83
83
  }
84
84
 
85
- function decisionInputs(Tensor, encoded, pad) {
85
+ /** Plain tensor records shared by browser and native ONNX execution owners. */
86
+ export function createDecisionInputs(encoded, pad) {
86
87
  let sequenceLength = 0;
87
88
  let optionCount = 0;
88
89
  for (const row of encoded) {
@@ -108,26 +109,11 @@ function decisionInputs(Tensor, encoded, pad) {
108
109
  types[rowIndex] = BigInt(row.qtype);
109
110
  }
110
111
  return {
111
- input_ids: new Tensor(
112
- 'int64', ids,
113
- [batch, sequenceLength]
114
- ),
115
- attention_mask: new Tensor(
116
- 'int64', attention,
117
- [batch, sequenceLength]
118
- ),
119
- marker_pos: new Tensor(
120
- 'int64', positions,
121
- [batch, optionCount]
122
- ),
123
- marker_mask: new Tensor(
124
- 'bool', mask,
125
- [batch, optionCount]
126
- ),
127
- qtype: new Tensor(
128
- 'int64', types,
129
- [batch]
130
- )
112
+ input_ids: {type: 'int64', data: ids, dims: [batch, sequenceLength]},
113
+ attention_mask: {type: 'int64', data: attention, dims: [batch, sequenceLength]},
114
+ marker_pos: {type: 'int64', data: positions, dims: [batch, optionCount]},
115
+ marker_mask: {type: 'bool', data: mask, dims: [batch, optionCount]},
116
+ qtype: {type: 'int64', data: types, dims: [batch]}
131
117
  };
132
118
  }
133
119
 
@@ -210,7 +196,8 @@ export function decodeDecisionOutputs(rows, outputs) {
210
196
  }
211
197
 
212
198
  export async function loadDecisionRuntime(configuration, report) {
213
- const {family, model: repository, revision = 'main', device = 'webgpu', runtime} = configuration;
199
+ const {family, model: repository, revision = 'main', device = 'webgpu',
200
+ dtype = family === 'laya' ? 'fp16' : 'fp32', runtime} = configuration;
214
201
  report(
215
202
  {phase: 'loading-runtime'}
216
203
  );
@@ -219,7 +206,7 @@ export async function loadDecisionRuntime(configuration, report) {
219
206
  namespace.env.backends.onnx.wasm.wasmPaths = runtime.wasmPaths;
220
207
  }
221
208
  const {AutoTokenizer, AutoModel, PreTrainedModel, Tensor} = namespace;
222
- const options = {revision, device, dtype: family === 'laya' ? 'fp16' : 'fp32'};
209
+ const options = {revision, device, dtype};
223
210
  // No progress_callback: upstream 4.3 otherwise performs file-size
224
211
  // aggregation. This owner reports actual semantic lifecycle transitions.
225
212
  report(
@@ -261,7 +248,10 @@ export async function loadDecisionRuntime(configuration, report) {
261
248
  async function evaluate(rows) {
262
249
  const {encoded, pad} = encodeDecisionRows(tokenizer, rows, family);
263
250
  if (encoded.length === 0) return {decisions: [], outputs: {}};
264
- const inputs = decisionInputs(Tensor, encoded, pad);
251
+ const inputs = {};
252
+ for (const [name, record] of Object.entries(createDecisionInputs(encoded, pad))) {
253
+ inputs[name] = new Tensor(record.type, record.data, record.dims);
254
+ }
265
255
  let output;
266
256
  try {
267
257
  output = await model(inputs);
@@ -116,6 +116,45 @@ tag and GitHub release title must both be the same bare numeric
116
116
  `MAJOR.MINOR.PATCH`. Prerelease versions do not get a misleading numeric
117
117
  GitHub release, and no release creates a Git branch for an npm dist-tag.
118
118
 
119
+ ## Native runtime assets on numeric releases
120
+
121
+ Default SDK-hosted native asset URLs use the installed SDK's version and its
122
+ matching numeric GitHub release. Publishing npm alone does not make those
123
+ downloads available. Every applicable numeric release therefore includes its
124
+ compatible retained SDK-hosted assets as release follow-through:
125
+
126
+ | Selected capability | Windows x64 release asset |
127
+ | --- | --- |
128
+ | Native application host | `arcane-native-windows-x64.tar.gz` |
129
+ | Ordinary native Whisper helper | `arcane-whisper-windows-x64.tar.gz` |
130
+ | Optional Intel NPU Whisper encoder | `arcane-whisper-openvino-windows-x64.tar.gz` |
131
+
132
+ Attach each asset used by the selected package's default SDK-hosted download
133
+ paths, including later releases whose native implementation is unchanged.
134
+ Include the optional NPU asset when that release supplies `encoder:
135
+ "openvino-npu"`; applications still opt in, and the ordinary Whisper asset
136
+ remains available. Upstream runtime and model versions retain their separate
137
+ upstream distribution URLs; they are not renamed to the SDK version.
138
+
139
+ Reuse the producer's retained compatible archive and its recorded selected
140
+ evidence. Do not rebuild or redownload an unchanged archive for each release.
141
+ An actual native change belongs to its producer under the existing build and
142
+ dependency authority. Preserve the complete runtime closure and its packaging
143
+ notices together. Models continue through their upstream acquisition path
144
+ unless their redistribution is separately requested.
145
+
146
+ The release owner assigns each upload, observes its outcome and records the
147
+ exact asset name and public release URL in the durable release handoff. If an
148
+ upload result is uncertain, inspect that release's existing asset before
149
+ retrying. Retain the exact pending operation, executing owner and last result
150
+ until delivery finishes; reassign idle delegated work rather than treating
151
+ delegation as completion.
152
+
153
+ Source delivery, selected package verification, npm publication, native asset
154
+ delivery, requester notices and documentation/site delivery are distinct.
155
+ Native archive follow-through does not gate npm publication or require a new
156
+ package build. Consumer adoption remains with each application owner.
157
+
119
158
  ## Documentation publication
120
159
 
121
160
  After every successful npm release and its applicable requester notices, the
@@ -54,18 +54,23 @@ alone does not make bare module names resolve in a browser.
54
54
  | Connect a browser to one Core transport | [Core browser client](core-client.md) |
55
55
  | Cancel one Core speech synthesis request without changing its payload | [Speech request lifetime](core-client.md#events-and-request-lifetime) and [Core facade](sdk-api.md#createcorefacade) |
56
56
  | Compose native Core dispatch, service lifecycle, and stdio hosting | [Native Core runtime](core-runtime.md) |
57
+ | Compose application Core services during source development, independently of local AI | [Development Core services](core-development.md) |
58
+ | Own an explicitly selected native Codex App Server session | [Codex App Server](codex-app-server.md) |
59
+ | Expose application-owned tools and static resources through Node STDIO | [MCP STDIO server](mcp-stdio.md) |
57
60
  | Run application-selected llama.cpp, Ollama or ONNX through Core | [Local AI through Core](local-ai.md) |
58
61
  | Generate complete local PNG images with a retained native model | [Local image generation](local-image-generation.md) |
59
62
  | Select FLUX.2 Klein 4B and its three complementary model resources | [FLUX.2 Klein 4B through Core](local-image-flux.md) |
60
63
  | Select the complete SDXL Base 1.0 checkpoint for text-to-image generation | [SDXL Base 1.0 through Core](local-image-sdxl.md) |
61
64
  | Compose independently loaded transcription and synthesis engines | [Native speech service](native-speech.md) |
62
65
  | Keep a native Whisper model loaded across complete recordings | [Native Whisper transcription](local-whisper.md) |
66
+ | Select the optional Intel NPU encoder with independent CUDA or CPU decoding | [Intel NPU Whisper](../guides/native-whisper-openvino.md) |
63
67
  | Edit an original PNG with a complete prompt and explicit strength | [Local image editing](local-image-editing.md) |
64
68
  | Project complete stored model files into native working files | [Model assets through Core](model-assets.md) |
65
69
  | Retain a Nemotron model and diarize caller-fed audio streams | [Native speaker diarization](diarization.md) |
66
70
  | Persist application-selected preferences through Core | [Core preferences service](core-preferences.md) |
67
71
  | Read complete selected Git text without changing a checkout | [Git text snapshots](git-text-snapshot.md) |
68
72
  | Own a native working checkout under per-user ArcaneData/Repos | [Native repository workspaces](core-repositories.md) |
73
+ | Write, commit and push complete selected repository text | [Selected-text repository writing](core-repositories.md#write-commit-and-push-selected-text) |
69
74
  | Build an installed-SDK portable Core payload | [Portable Core packaging](core-native-packaging.md) |
70
75
  | Pass host-selected state defaults and unchanged explicit launch values to services | [Launch-time locations](core-native-packaging.md#launch-time-locations) |
71
76
  | Build a Windows executable without an OS checkout | [Windows executable packaging](core-native-packaging.md#windows-executable) |
@@ -142,7 +147,8 @@ node tools/build-reference-site.mjs --write --source-ref <documentation-commit>
142
147
 
143
148
  The same option is accepted by `createReferenceSite({sourceRef})` and
144
149
  `writeReferenceSite({sourceRef})`. It reads the selected commit's package
145
- version, complete reference documents and inventories, inventory-selected
150
+ version, complete reference documents and inventories, the canonical
151
+ `docs/guides/native-whisper-openvino.md` guide when present, inventory-selected
146
152
  runtime sources, and authored site inputs directly from Git in memory. It
147
153
  creates no checkout or export and leaves source drafts untouched. Rendering
148
154
  uses the current canonical generator and its authored semantic contracts;
@@ -1,7 +1,8 @@
1
1
  # Browser typed decisions
2
2
 
3
- `arcane-os/ai/browser-decisions` owns browser-local Laya typed-decisions FP16
4
- and Julia-1 FP32 inference. It is separate from chat and speech because these
3
+ `arcane-os/ai/browser-decisions` owns browser-local Laya typed decisions
4
+ and Julia-1 inference. Laya defaults to FP16 and Julia to FP32; an explicit
5
+ `dtype` is forwarded unchanged to the selected upstream loader. It is separate from chat and speech because these
5
6
  models score caller-supplied options instead of generating conversational text.
6
7
  Applications supply the model repository, state, questions, option meanings and
7
8
  policy; the SDK owns the reusable tokenizer, tensors, inference and lifecycle.
@@ -66,6 +67,7 @@ it does not substitute a quantized graph or another model.
66
67
  | `model` | Required upstream repository/base URL accepted by Transformers.js. The application owns its selection. |
67
68
  | `revision` | Upstream revision, default `main`; select a stable revision when repeatable model selection is needed. |
68
69
  | `device` | Transformers.js backend, default `webgpu`; an explicit supported alternative is caller-owned. No automatic backend fallback. |
70
+ | `dtype` | Exact upstream precision selection; defaults to `fp16` for Laya and `fp32` for Julia. For Laya FP32, supply `dtype: 'fp32'`. Unsupported selections fail at the selected upstream loader rather than being replaced. |
69
71
  | `runtime.moduleUrl` | Defaults to the self-contained CDN entry `https://cdn.jsdelivr.net/npm/@huggingface/transformers@4.3.0/dist/transformers.min.js`. The `.web.js` build expects a bundler to resolve its bare ONNX Runtime import and is unsuitable for a native module Worker. An explicit compatible public runtime URL may be supplied. |
70
72
  | `runtime.wasmPaths` | Optional upstream ONNX WASM location passed to the selected runtime's public environment API. |
71
73
 
@@ -75,10 +77,11 @@ network, cross-origin and backend availability still apply. WebGPU needs a
75
77
  compatible browser/device context. Unsupported precision or backend errors
76
78
  are returned, not replaced with a different model, backend or precision.
77
79
 
78
- Laya selects `dtype: 'fp16'`, loading `onnx/model_fp16.onnx` and its
80
+ Laya defaults to `dtype: 'fp16'`, loading `onnx/model_fp16.onnx` and its
79
81
  `model_fp16.onnx_data` companion through Transformers.js. The published mixed
80
82
  precision graph retains selected operations/outputs in FP32; FP16 selection
81
- does not claim every graph operation runs in half precision.
83
+ does not claim every graph operation runs in half precision. Explicit
84
+ `dtype: 'fp32'` selects the repository's FP32 graph through that same loader.
82
85
 
83
86
  Julia selects `dtype: 'fp32'`, loading root `model.onnx` with the exact
84
87
  `model.onnx.data` external-data name. Its custom five-input graph is loaded
@@ -46,7 +46,7 @@ const status = await repository.status({signal});
46
46
  await repository.close();
47
47
  ```
48
48
 
49
- `createRepositoryWorkspace({name,directory,dataRoot,remote,branch,onEvent,run}={})`
49
+ `createRepositoryWorkspace({name,directory,dataRoot,remote,branch,gitIdentity,onEvent,run}={})`
50
50
  returns `{directory,open,status,pull,push,write,close,drain,dispose}`. The `directory`
51
51
  property is the absolute selected working path.
52
52
 
@@ -87,6 +87,124 @@ A failed or cancelled clone reports the actual failure; any remaining directory
87
87
  is retained for application-owned inspection. There is no automatic deletion or
88
88
  destructive retry. Filesystem failures and complete Git diagnostics propagate.
89
89
 
90
+ ## Git identity configuration
91
+
92
+ ```javascript
93
+ import {readGitIdentity, createRepositoryWorkspace} from 'arcane-os/core/repositories';
94
+
95
+ const defaults = await readGitIdentity({signal});
96
+ const observations = await readGitIdentity({directory: existingRepositoryDirectory, signal});
97
+ const repository = createRepositoryWorkspace({
98
+ name: 'moon-cheese-dispatches',
99
+ remote: applicationConnection.remote,
100
+ gitIdentity: {name: 'Moon Dispatcher', email: 'moon@example.invalid', username: 'moon-account'}
101
+ });
102
+ ```
103
+
104
+ `readGitIdentity({directory,signal,onEvent,run=runProcess}={})` reads only
105
+ `user.name`, `user.email` and `github.user`. It returns:
106
+
107
+ ```text
108
+ {
109
+ global: {name, email, githubUser},
110
+ local: {name, email, githubUser} | null,
111
+ effective: {name, email, githubUser} | null
112
+ }
113
+ ```
114
+
115
+ Every observed field is the complete UTF-8 Git configuration string, including
116
+ an empty string, leading U+FEFF and embedded newlines, or `null` when unset.
117
+ Git can store values outside UTF-8; those produce
118
+ `ARCANE_GIT_IDENTITY_NOT_TEXT` instead of replacement characters. Global and
119
+ local observations select Git's corresponding file scopes and honor configured
120
+ includes. With an existing
121
+ repository `directory`, `effective` reads ordinary Git configuration in that
122
+ repository context, including system, global, local, worktree and command
123
+ configuration where applicable. Equal local and global values remain separate
124
+ observations; the SDK does not infer who wrote or owns a setting. The last
125
+ configured scalar value wins within each observation.
126
+
127
+ Without `directory`, only global configuration is read and `local`/`effective`
128
+ are `null`. Relative directories resolve from the process working directory.
129
+ Git retains its normal environment and conditional-include context; when no
130
+ directory is supplied, that context is the process working directory. These
131
+ are independent configuration observations, not a transaction or a claim about
132
+ the identity of a prior commit, environment-derived author, or authenticated
133
+ GitHub account. `github.user` is a non-secret configured default, not proof of
134
+ authentication.
135
+
136
+ One targeted Git command reads each requested scope. Independent observations
137
+ start concurrently and all accepted commands finish their process cleanup
138
+ before the reader settles. `signal` uses the existing process owner; the shared
139
+ SDK event queue serializes `onEvent` delivery with its usual backpressure.
140
+ The reader consumes raw stdout through the existing process contract; stdout
141
+ is parsed as configuration rather than emitted as text events. Process
142
+ lifecycle and stderr events retain their ordinary route. A supplied `run`
143
+ adapter must honor that raw-output contract.
144
+ Git's no-match result produces unset fields; other process,
145
+ configuration, observer and cancellation failures remain complete. A single
146
+ failure retains its original object, code, cause and process `details`; the
147
+ reader adds complete captured stdout as the `rawStdout` Buffer on that error.
148
+ If the error already has that property or cannot accept it, a new error retains
149
+ the original as `cause`, preserves its code, process details and exit code, and
150
+ holds this observation's `rawStdout`. Original causal failures stay on that
151
+ original error; an attachment exception is retained as `attachmentError`.
152
+ An existing `rawStdout` property is never overwritten or invoked to read its value.
153
+ The process result's `stdout` remains `null` in raw mode, and its stderr and
154
+ other diagnostics remain intact. A decoding failure likewise carries its
155
+ original decoding `cause`, complete process `details` and `rawStdout`.
156
+ An observer failure already represented by a process error is not added again;
157
+ genuinely separate failures use `AggregateError.errors`, each retaining its
158
+ own diagnostics.
159
+ No configuration file is written and no credential helper is queried by this
160
+ reader. The application owns any saved non-secret defaults and repository
161
+ overrides through its existing preferences.
162
+
163
+ Both this workspace factory and
164
+ [`createGitTextSnapshot`](git-text-snapshot.md) accept optional
165
+ `gitIdentity:{name?,email?,username?}`. They capture supplied strings once during
166
+ I/O-free construction. Later edits to that object or a UI selection do not
167
+ change accepted work. Omitted or `undefined` fields preserve ordinary inherited
168
+ behavior; removing an application override means omitting that field on the
169
+ next owner, not changing unmanaged repository configuration. Empty strings are
170
+ passed explicitly to Git. Non-string values, NUL and text that cannot be
171
+ represented by the native process transport report `TypeError` without silently
172
+ rewriting the input.
173
+
174
+ An application's explicit "global author" choice differs from ordinary Git
175
+ inheritance. Pass the observed global `name` and `email` as selected strings to
176
+ override repository-local author values for those fields. A global `null` means
177
+ that field is unset, not a selected value: passing `null` reports `TypeError`,
178
+ and omitting the field allows normal inheritance, including repository-local
179
+ configuration. The application must describe that partial/inherited choice
180
+ accurately or obtain the missing value before presenting an all-global author
181
+ selection. An empty string is a distinct explicit value that Git may reject.
182
+ Changed application choices apply to future owners; accepted operations retain
183
+ their original selection and may finish without being discarded.
184
+
185
+ Each supplied name/email becomes command-local `-c user.name=...` or
186
+ `-c user.email=...` and the corresponding child-only `GIT_AUTHOR_*` and
187
+ `GIT_COMMITTER_*` fields. Those selected fields take precedence over inherited
188
+ author/committer settings and environment values; omitted fields, dates and
189
+ unrelated environment remain inherited. Git retains its ordinary commit
190
+ identity formatting and may reject an unusable identity. The SDK changes
191
+ neither `process.env` nor global/local Git files.
192
+
193
+ `username` supplies only command-local `credential.username`. Git's existing
194
+ credential helper and remote protocol still own actual authentication. A
195
+ URL-embedded username or helper behavior can affect the selected credential;
196
+ this option neither rewrites the remote nor selects an SSH account or proves a
197
+ GitHub login. No account enumeration, secret storage or `gh` dependency is added.
198
+ See Git's [configuration](https://git-scm.com/docs/git-config),
199
+ [author environment](https://git-scm.com/docs/git#_git_commits) and
200
+ [credential username](https://git-scm.com/docs/gitcredentials) contracts.
201
+
202
+ The selected settings accompany every workspace Git command, including clone,
203
+ pull, commit and push, and every bare-snapshot Git command, including init and
204
+ fetch. Existing arguments, content, output, errors, queue and cancellation
205
+ semantics remain unchanged. Other repository helpers and CLI cwd defaults keep
206
+ their existing behavior.
207
+
90
208
  ## Write, commit and push selected text
91
209
 
92
210
  ```javascript
@@ -122,8 +240,9 @@ One `git --literal-pathspecs add -- <paths>` stages only those exact names.
122
240
  One `git --literal-pathspecs commit --only --cleanup=verbatim --file - -- <paths>`
123
241
  commits only those paths, leaving unrelated staged paths staged. The original
124
242
  message is streamed unchanged through standard input. One ordinary `git push`
125
- then uses that repository's existing remote/ref configuration, identity,
126
- credentials and hooks. There is no implicit pull, branch switch, forced push,
243
+ then uses that repository's existing remote/ref configuration, the optional
244
+ process-local identity selection above, existing credentials and hooks.
245
+ There is no implicit pull, branch switch, forced push,
127
246
  hook bypass, automatic retry, rollback or reset. If Git reports no changes to
128
247
  commit, that actual failure is returned; the SDK creates no empty commit.
129
248
  These are Git's documented [selected-path commit](https://git-scm.com/docs/git-commit#Documentation/git-commit.txt---only)
@@ -33,12 +33,26 @@ policy stable for the lifetime of its snapshot owner.
33
33
 
34
34
  ## API and lifecycle
35
35
 
36
- `createGitTextSnapshot({cacheDirectory,remote,ref,selectPath,onEvent,run})`
36
+ `createGitTextSnapshot({cacheDirectory,remote,ref,selectPath,gitIdentity,onEvent,run})`
37
37
  returns `{refresh,close,drain,dispose}`. `run` defaults to the SDK's existing
38
38
  `runProcess`; applications normally omit it. It is the same process adapter
39
39
  contract, not a second process supervisor. `onEvent` is the ordinary SDK event
40
40
  callback and follows its asynchronous completion/error contract.
41
41
 
42
+ Optional `gitIdentity:{name?,email?,username?}` uses the shared
43
+ [Git identity configuration contract](core-repositories.md#git-identity-configuration).
44
+ Supplied strings are captured during construction and accompany every Git
45
+ command, including bare initialization and fetch, without changing the remote,
46
+ ref, config files or process environment. Explicit name/email use command-local
47
+ configuration and child-only author/committer fields; omitted fields remain
48
+ inherited. `username` is only Git's credential username hint, not authenticated
49
+ account proof or SSH account selection. Read separate global/local/effective
50
+ configuration through `readGitIdentity` from `arcane-os/core/repositories`;
51
+ the reader also accepts an existing bare cache directory. Applications retain
52
+ saved non-secret choices and per-connection overrides. Identity selection adds
53
+ no process or query to refresh and does not attach a caller's abort to shared
54
+ acquisition.
55
+
42
56
  - `refresh({signal}={})` fetches the selected ref and returns
43
57
  `{revision,files:[{path,content}]}`. `revision` is Git's opaque selected commit
44
58
  name, not an SDK integrity record. Every file comes from that exact revision,
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "sdkVersion": "0.64.0",
3
+ "sdkVersion": "0.65.0",
4
4
  "environment": {
5
5
  "runtime": "Node.js for Node entrypoints; browser for browser-only entrypoints",
6
6
  "minimumVersion": "22.23.2 for Node entrypoints",
7
7
  "moduleSystem": "ESM"
8
8
  },
9
- "memberCount": 288,
9
+ "memberCount": 290,
10
10
  "runtimeSubpathPatterns": {
11
11
  "arcane-os/modules/*": "./runtime/arcane/modules/*",
12
12
  "arcane-os/entities/*": "./runtime/arcane/entities/*"
@@ -54,6 +54,20 @@
54
54
  "protocol": "Owned configure/build/install process lifecycle",
55
55
  "normalization": "Returns platform/architecture/root/executable/runtimeDirectory and complete configure/compile/install diagnostics; preserves cancellation and process failures, installs no model or toolchain, performs no inference"
56
56
  },
57
+ {
58
+ "id": "local-ai-whisper-openvino-build:buildWhisperOpenVinoRuntime",
59
+ "name": "buildWhisperOpenVinoRuntime",
60
+ "displayName": "buildWhisperOpenVinoRuntime()",
61
+ "kind": "function",
62
+ "signature": "async buildWhisperOpenVinoRuntime({sourceDirectory,openvinoDirectory,cudaDirectory,outputRoot,cmake='cmake',generator,cmakeArgs=[],env,signal,onEvent}={})",
63
+ "entrypoints": ["arcane-os/local-ai/whisper/openvino-build"],
64
+ "primaryImport": "arcane-os/local-ai/whisper/openvino-build",
65
+ "group": "Core native speech",
66
+ "summary": "Produces the optional Intel NPU encoder runtime from installed matching Whisper/OpenVINO sources and an existing Windows x64 toolchain.",
67
+ "availability": "Windows x64 Node with Whisper 1.9.4 source, OpenVINO 2026.3.1 development tree and compatible CMake/MSVC; other platform distributions require separate native producers",
68
+ "protocol": "Owned configure/build process lifecycle and complete native runtime assembly",
69
+ "normalization": "Returns version/openvinoVersion/platform/architecture/encoder/backend/backends/root/directory/libraryDirectory/copied sourceDirectory and complete configure/compile diagnostics; retains NPU/CPU and optional supplied CUDA libraries plus notices, preserves process failures and cancellation, installs no model, driver or toolchain"
70
+ },
57
71
  {
58
72
  "id": "codex-app-server:createCodexAppServerSession",
59
73
  "name": "createCodexAppServerSession",
@@ -143,14 +157,28 @@
143
157
  "name": "createRepositoryWorkspace",
144
158
  "displayName": "createRepositoryWorkspace()",
145
159
  "kind": "function",
146
- "signature": "createRepositoryWorkspace({name,directory,dataRoot,remote,branch,onEvent,run=runProcess}={})",
160
+ "signature": "createRepositoryWorkspace({name,directory,dataRoot,remote,branch,gitIdentity,onEvent,run=runProcess}={})",
147
161
  "entrypoints": ["arcane-os/core/repositories"],
148
162
  "primaryImport": "arcane-os/core/repositories",
149
163
  "group": "Native repository workspaces",
150
164
  "summary": "Owns one selected native working checkout with ArcaneData/Repos defaults and existing Git/process lifecycle.",
151
165
  "availability": "Node with Git on Windows, Linux and macOS; Android requires host process adaptation",
152
166
  "protocol": "Git working repository and SDK process adapter",
153
- "normalization": "Returns directory, open, status, pull, push, write and identical close/drain/dispose hooks. Only missing or empty destinations clone; existing roots are inspected without rewriting, resetting or pulling. Explicit paths remain selected. Same-directory operations serialize within the process; cancellation and shutdown join accepted process work. write snapshots exact selected text and paths, writes files, stages/commits only literal owned paths and non-force pushes, preserving unrelated staged files and ordinary Git filters. Complete outputs and causal failures retain confirmed local/committed/pushed and uncertain outcomes."
167
+ "normalization": "Returns directory, open, status, pull, push, write and identical close/drain/dispose hooks. Only missing or empty destinations clone; existing roots are inspected without rewriting, resetting or pulling. Explicit paths remain selected. Same-directory operations serialize within the process; cancellation and shutdown join accepted process work. write snapshots exact selected text and paths, writes files, stages/commits only literal owned paths and non-force pushes, preserving unrelated staged files and ordinary Git filters. Complete outputs and causal failures retain confirmed local/committed/pushed and uncertain outcomes. Optional gitIdentity captures name/email/username for child Git operations only; omitted fields remain inherited and no config file is written."
168
+ },
169
+ {
170
+ "id": "core-repositories:readGitIdentity",
171
+ "name": "readGitIdentity",
172
+ "displayName": "readGitIdentity()",
173
+ "kind": "function",
174
+ "signature": "readGitIdentity({directory,signal,onEvent,run=runProcess}={})",
175
+ "entrypoints": ["arcane-os/core/repositories"],
176
+ "primaryImport": "arcane-os/core/repositories",
177
+ "group": "Native repository workspaces",
178
+ "summary": "Reads separate global, repository-local and effective non-secret Git identity configuration.",
179
+ "availability": "Node with Git on Windows, Linux and macOS; Android requires host process adaptation",
180
+ "protocol": "Git config NUL-delimited transport through the SDK process adapter",
181
+ "normalization": "Returns {global,local,effective} observations with complete name/email/githubUser strings or null when unset. Empty strings and configured includes are preserved. Without directory only global is observed and local/effective are null. Concurrent scope reads join process cleanup; complete failures propagate singly or through AggregateError. Changes no config files and claims no authenticated account or environment-resolved author."
154
182
  },
155
183
  {
156
184
  "id": "core-speech:createSpeechService",
@@ -311,14 +339,14 @@
311
339
  "name": "createGitTextSnapshot",
312
340
  "displayName": "createGitTextSnapshot()",
313
341
  "kind": "function",
314
- "signature": "createGitTextSnapshot({cacheDirectory,remote,ref,selectPath,onEvent,run=runProcess}={})",
342
+ "signature": "createGitTextSnapshot({cacheDirectory,remote,ref,selectPath,gitIdentity,onEvent,run=runProcess}={})",
315
343
  "entrypoints": ["arcane-os"],
316
344
  "primaryImport": "arcane-os",
317
345
  "group": "Workspace, doctor, repository, and server",
318
346
  "summary": "Reads complete selected text from one fetched revision using a dedicated bare Git object cache and batched retrieval.",
319
347
  "availability": "Node with Git; Android requires a host supplying Git/process capability",
320
348
  "protocol": "Git tree and batched blob transport through the shared process owner",
321
- "normalization": "Returns {refresh,close,drain,dispose}; refresh yields {revision,files:[{path,content}]}, shares concurrent work, reuses unchanged revisions, preserves exact UTF-8 text, isolates caller cancellation and drains accepted work. See git-text-snapshot.md."
349
+ "normalization": "Returns {refresh,close,drain,dispose}; refresh yields {revision,files:[{path,content}]}, shares concurrent work, reuses unchanged revisions, preserves exact UTF-8 text, isolates caller cancellation and drains accepted work. Optional gitIdentity captures name/email/username for child Git commands including bare init and fetch, without changing config files, payloads or shared acquisition lifetime. See git-text-snapshot.md."
322
350
  },
323
351
  {
324
352
  "id": "diarization:createDiarization",
@@ -1013,13 +1013,14 @@
1013
1013
  "kind": "esm",
1014
1014
  "exports": [
1015
1015
  "isOllamaModelIdentifier",
1016
- "normalizeOllamaModelIdentifier"
1016
+ "normalizeOllamaModelIdentifier",
1017
+ "sameOllamaModelIdentifier"
1017
1018
  ],
1018
- "summary": "Validates and canonicalizes the syntax of Ollama model identifiers without granting model admission.",
1019
+ "summary": "Recognizes Ollama model identifier syntax and compares ordinary resident-model aliases without changing requests.",
1019
1020
  "availability": "Cross-host",
1020
1021
  "protocol": "In-process only",
1021
- "normalization": "Fully normalized string/boolean result.",
1022
- "surface": "`normalizeOllamaModelIdentifier()`, `isOllamaModelIdentifier()`."
1022
+ "normalization": "Exact identifier/null or boolean results; alias comparison applies Ollama's default registry, namespace and tag without changing either input.",
1023
+ "surface": "`normalizeOllamaModelIdentifier()`, `isOllamaModelIdentifier()`, `sameOllamaModelIdentifier(left, right)`."
1023
1024
  },
1024
1025
  {
1025
1026
  "file": "runtime/arcane/modules/OllamaSettings.js",
@@ -70,6 +70,13 @@ assembly compose it through the shared speech service without changing the
70
70
  browser's selected STT route. See [native Whisper](local-whisper.md) for the
71
71
  complete configuration, platform, media and shutdown contracts.
72
72
 
73
+ Adding `encoder: 'openvino-npu'` selects the optional Intel NPU audio encoder
74
+ with a separately selected CUDA or CPU decoder. The Windows x64 precompiled
75
+ runtime uses paired GGML/XML/BIN model resources and the computer's installed
76
+ compatible Intel NPU driver. See the [Intel NPU Whisper guide](../guides/native-whisper-openvino.md)
77
+ for preparation, observed initialization, cancellation/reload and custom
78
+ runtime production. Omitting the encoder retains ordinary Whisper preparation.
79
+
73
80
  The selected model stays loaded across recordings. An eligible native
74
81
  accelerated failure may drain that helper and retry the same complete recording
75
82
  once on the prepared CPU backend. That accepted request retains its actual
@@ -7,6 +7,12 @@ the model or recording files are reused. The browser's existing
7
7
  `AI.fetchSTT(audioFile, signal)` and Core `speech.transcribe` routes retain the
8
8
  application's selected model and cancellation signal.
9
9
 
10
+ For the optional Intel NPU encoder, add `encoder: "openvino-npu"` to the
11
+ runtime record. The [Intel NPU guide](../guides/native-whisper-openvino.md)
12
+ covers the Windows x64 precompiled distribution, paired GGML/XML/BIN model
13
+ resources, independent CUDA/CPU decoding and joined cancellation. Omitting
14
+ `encoder` preserves the ordinary runtime and model selection below.
15
+
10
16
  ## Select the runtime and model
11
17
 
12
18
  An application's `arcane-app.json` selects native speech alongside its other
@@ -130,7 +136,7 @@ prepared descriptor returned by `ensureLocalAIRuntimes`; alternatively,
130
136
 
131
137
  | Member | Contract |
132
138
  | --- | --- |
133
- | `current()` | Returns `providerId`, selected `modelId`, lifecycle `state`, `loaded`, `busy`, correlated `requestId` or null, requested/observed backend, evidence and any error. |
139
+ | `current()` | Returns `providerId`, selected `modelId`, lifecycle `state`, `loaded`, `busy`, correlated `requestId` or null, `requestedBackend`, `observedBackend`, `backendEvidence`, `requestedEncoder`, `observedEncoder`, `encoderEvidence` and any error. Encoder initialization evidence is separate from decoder evidence. |
134
140
  | `subscribe(listener, {replay = true, signal} = {})` | Immediately replays current state by default and returns an unsubscribe function. |
135
141
  | `load({modelId, signal} = {})` | Prepares the runtime if necessary, then waits for actual native model readiness. With no selected model it remains unloaded. |
136
142
  | `transcribe(request, {signal, onProgress, requestId} = {})` | Consumes the complete `audioBase64` recording. Optional `language` defaults to native automatic detection; `translate: true` selects Whisper's translation operation. `model` must match the loaded model. Optional `requestId` carries the caller's existing operation identity through lifecycle snapshots as separate control information; it does not alter the recording or request payload. |
@@ -216,7 +216,7 @@ normal close or EOF and rejects with an `AggregateError` when transport or
216
216
  diagnostic delivery failed. Observe it even when the initiating caller continues
217
217
  other work. Request failures alone do not close the session.
218
218
 
219
- ## MCP_PROTOCOL_VERSION
219
+ ## `MCP_PROTOCOL_VERSION`
220
220
 
221
221
  `MCP_PROTOCOL_VERSION` is exactly `'2025-11-25'`. Initialization always returns
222
222
  that supported version. A client proposing another version must accept this
@@ -26,6 +26,12 @@ selection, supported formats, and native process ownership remain with the
26
26
  selected engine and its host composition. Browser-only operation remains
27
27
  independent of this native service.
28
28
 
29
+ The SDK's [retained Whisper engine](local-whisper.md) supplies native STT.
30
+ Its optional [Intel NPU encoder](../guides/native-whisper-openvino.md) uses the
31
+ same service, readiness events, complete request/result contract and cancellation
32
+ path. The host selects that encoder and its paired model resources; the browser
33
+ continues calling `speech.transcribe` without loading native libraries itself.
34
+
29
35
  ## Startup and status
30
36
 
31
37
  `start(context)` subscribes to each configured engine and starts their loads
@@ -60,7 +60,7 @@ runtime layouts.
60
60
  | `arcane-os/core/host` | Node-only reusable Core host lifecycle with explicitly supplied services. |
61
61
  | `arcane-os/codex/app-server` | Explicit native Codex App Server session, complete live turns, server requests and recovery. |
62
62
  | `arcane-os/mcp/stdio` | Node MCP STDIO server for app-owned tools and static resources. |
63
- | `arcane-os/core/repositories` | Native persistent ArcaneData paths and explicitly selected connected working repositories. |
63
+ | `arcane-os/core/repositories` | Native persistent ArcaneData paths, connected working repositories and scoped non-secret Git identity configuration. |
64
64
  | `arcane-os/core/packaged-web` | Core-owned packaged web listener with a persisted loopback origin and ready/failure events. |
65
65
  | `arcane-os/core/preferences` | Existing preference RPC methods with an application-selected file, ordered writes and owned drain. |
66
66
  | `arcane-os/core/diarization` | Lazy native speaker-diarization service, explicit stream methods and lifecycle/result events. |
@@ -74,6 +74,9 @@ runtime layouts.
74
74
  | `arcane-os/ai/core-image` | Browser image lifecycle and complete PNG Blobs through an available Core service. |
75
75
  | `arcane-os/core/image` | Native image Core service with independent runtime preparation and retained model ownership. |
76
76
  | `arcane-os/core/speech` | Independent injected STT/TTS engine loading, request cancellation and joined Core service lifetime. |
77
+ | `arcane-os/local-ai/whisper` | Retained native Whisper model, complete recordings and joined helper lifetime. |
78
+ | `arcane-os/local-ai/whisper/build` | First-party persistent helper build against selected installed Whisper headers and libraries. |
79
+ | `arcane-os/local-ai/whisper/openvino-build` | Optional Windows x64 Intel NPU encoder runtime producer using selected installed sources and toolchain. |
77
80
  | `arcane-os/ai/core-model-assets` | Complete stored browser model members projected through Core into native working files. |
78
81
  | `arcane-os/core/model-assets` | Native working-file preparation, engine retain handles and joined cleanup. |
79
82
  | `arcane-os/integrated-provider` | Fixed integrated shared-development provider. |
@@ -405,6 +408,7 @@ The remaining data-only subpaths are eight JSON Schemas and package metadata.
405
408
  | `readCoreLaunchContext()` | function | `arcane-os/core/host` | Native Core host composition | Node; reads explicitly selected launch JSON |
406
409
  | `resolveArcaneDataPaths()` | function | `arcane-os/core/repositories` | Native repository workspaces | Node on Windows, Linux and macOS; Android supplies its data root |
407
410
  | `createRepositoryWorkspace()` | function | `arcane-os/core/repositories` | Native repository workspaces | Node with Git; Android requires host process adaptation |
411
+ | `readGitIdentity()` | function | `arcane-os/core/repositories` | Native repository workspaces | Node with Git; Android requires host process adaptation |
408
412
  | `createWindowsNativeProvider()` | function | `arcane-os/native/windows-provider` | Windows native provider | Node assembly; Windows x64/WebView2 execution |
409
413
  | `arcaneNativeBuilderProvider` | singleton | `arcane-os/native/windows-provider` | Windows native provider | Node assembly; Windows x64/WebView2 execution |
410
414
  | `arcaneNativeBuilderProvider default export` | singleton | `arcane-os/native/windows-provider` | Windows native provider | Node assembly; Windows x64/WebView2 execution |
@@ -430,6 +434,7 @@ The remaining data-only subpaths are eight JSON Schemas and package metadata.
430
434
  | `createWhisperRuntime()` | function | `arcane-os/local-ai/whisper` | Core native speech | Node with a prepared matching helper, runtime, decoder and selected model |
431
435
  | `createWhisperRuntime default export` | function | `arcane-os/local-ai/whisper` | Core native speech | Same retained native engine factory |
432
436
  | `buildWhisperHelper()` | function | `arcane-os/local-ai/whisper/build` | Core native speech | Node with installed CMake/C++17 toolchain and matching Whisper source/runtime |
437
+ | `buildWhisperOpenVinoRuntime()` | function | `arcane-os/local-ai/whisper/openvino-build` | Core native speech | Windows x64 Node with installed matching Whisper/OpenVINO sources and CMake/MSVC toolchain |
433
438
  | `createSpeechService default export` | function | `arcane-os/core/speech` | Core native speech | Same shared speech service factory |
434
439
  | `prepareCoreModelAssets()` | function | `arcane-os/ai/core-model-assets` | Core model assets | Browser with complete Blob/File members and available Core service |
435
440
  | `createModelAssetService()` | function | `arcane-os/core/model-assets` | Core model assets | Node Core with an application-selected working directory |
@@ -5209,7 +5214,7 @@ a dedicated bare Git cache. It does not create or alter a working checkout.
5209
5214
  ### Signature, lifecycle, and result
5210
5215
 
5211
5216
  ```text
5212
- createGitTextSnapshot({cacheDirectory,remote,ref,selectPath,onEvent,run=runProcess}={})
5217
+ createGitTextSnapshot({cacheDirectory,remote,ref,selectPath,gitIdentity,onEvent,run=runProcess}={})
5213
5218
  ```
5214
5219
 
5215
5220
  Import from `arcane-os`. The application owns the cache location, remote, ref,
@@ -5221,6 +5226,11 @@ await accepted refresh/process cleanup. Errors remain observable instead of
5221
5226
  returning stale or partial success. See [Git text snapshots](git-text-snapshot.md)
5222
5227
  for complete inputs, text semantics, errors, events and Core service composition.
5223
5228
 
5229
+ Optional `gitIdentity:{name?,email?,username?}` captures a child-process-only
5230
+ selection for init/fetch and subsequent Git commands, preserving shared refresh
5231
+ cancellation and complete content. See [Git identity configuration](core-repositories.md#git-identity-configuration)
5232
+ for author precedence, scoped defaults and credential username semantics.
5233
+
5224
5234
  ### Example
5225
5235
 
5226
5236
  ```js
@@ -7881,7 +7891,7 @@ explicit existing path. The application owns the remote, branch and Core API.
7881
7891
  ### Signature and result
7882
7892
 
7883
7893
  ```text
7884
- createRepositoryWorkspace({name,directory,dataRoot,remote,branch,onEvent,run=runProcess}={})
7894
+ createRepositoryWorkspace({name,directory,dataRoot,remote,branch,gitIdentity,onEvent,run=runProcess}={})
7885
7895
  ```
7886
7896
 
7887
7897
  Returns `{directory,open,status,pull,push,write,close,drain,dispose}`. Construction does
@@ -7892,6 +7902,12 @@ work and preserves the checkout. No CLI cwd, launch state, preference, snapshot
7892
7902
  cache or existing user-data location changes. See the [complete inputs, results,
7893
7903
  errors and lifecycle](core-repositories.md#one-connected-working-checkout).
7894
7904
 
7905
+ Optional `gitIdentity:{name?,email?,username?}` captures explicit non-secret
7906
+ settings for child Git operations without writing global/local configuration.
7907
+ Selected author fields override inherited child author/committer fields;
7908
+ omitted fields remain inherited. Username remains a credential hint, with
7909
+ authentication owned by ordinary Git and its configured helper.
7910
+
7895
7911
  `write({files:[{path,content}],message,signal})` snapshots the exact selected
7896
7912
  strings, writes those repository-relative files, commits only their literal
7897
7913
  paths and performs an ordinary non-force push. It retains unrelated staged
@@ -7911,6 +7927,45 @@ await repository.open({signal});
7911
7927
  await repository.close();
7912
7928
  ```
7913
7929
 
7930
+ ## readGitIdentity()
7931
+
7932
+ ### Overview
7933
+
7934
+ Reads non-secret global, repository-local and effective Git configuration
7935
+ without changing files or querying credentials.
7936
+
7937
+ ### Signature and result
7938
+
7939
+ ```text
7940
+ readGitIdentity({directory,signal,onEvent,run=runProcess}={})
7941
+ ```
7942
+
7943
+ Import from `arcane-os/core/repositories`. Returns a promise for
7944
+ `{global,local,effective}`, each observed scope containing `{name,email,githubUser}`.
7945
+ Fields preserve complete UTF-8 strings, including empty values, leading U+FEFF
7946
+ and newlines; unset fields are `null`. Git values outside UTF-8 fail with
7947
+ `ARCANE_GIT_IDENTITY_NOT_TEXT` instead of replacement characters.
7948
+ With no directory, only global configuration is read and local/effective
7949
+ are `null`. Existing repository directories, including bare caches, select the
7950
+ context for all three observations. Configured includes and ordinary Git
7951
+ precedence remain in effect. These are config observations, not authenticated
7952
+ accounts or environment-resolved commit identities.
7953
+
7954
+ Independent scope queries run concurrently and join cleanup before settlement.
7955
+ Raw stdout is parser-owned rather than emitted as text events; an injected
7956
+ `run` must honor the existing raw-output process contract. Lifecycle and stderr
7957
+ events retain their ordinary route. Errors preserve their original object,
7958
+ code, cause and process diagnostics, with complete captured output added as
7959
+ `rawStdout` (a Buffer). The raw-mode process result retains `stdout:null`.
7960
+ If that property already exists or cannot be attached, a wrapper retains the
7961
+ original as `cause`, its code/details/exit code and this observation's raw
7962
+ output; original causal failures remain on the cause. An attachment exception
7963
+ is retained as `attachmentError`. Existing property values remain untouched.
7964
+ Decoding failures include their cause, complete process details and raw output.
7965
+ Already-represented observer errors appear once; separate failures use
7966
+ `AggregateError`. See the [complete contract and
7967
+ per-process selection example](core-repositories.md#git-identity-configuration).
7968
+
7914
7969
  ## createCoreLocalAIProvider()
7915
7970
 
7916
7971
  ### Overview
@@ -8141,7 +8196,7 @@ Whisper backend libraries, decoder and model files; see the complete
8141
8196
 
8142
8197
  | Member | Contract |
8143
8198
  | --- | --- |
8144
- | `current()` | Returns `providerId`, `modelId`, `state`, `loaded`, `busy`, `requestId`, `requestedBackend`, `observedBackend`, `backendEvidence` and `error`. State reports actual helper/model readiness. |
8199
+ | `current()` | Returns `providerId`, `modelId`, `state`, `loaded`, `busy`, `requestId`, `requestedBackend`, `observedBackend`, `backendEvidence`, `requestedEncoder`, `observedEncoder`, `encoderEvidence` and `error`. State reports actual helper/model readiness; encoder and decoder initialization evidence remain separate. |
8145
8200
  | `subscribe(listener,{replay=true,signal}={})` | Observes the current snapshot immediately by default, then each state change; returns the unsubscribe closure. |
8146
8201
  | `load({modelId,signal}={})` | Prepares the selected runtime if necessary and retains its initialized helper/model. Same-model loading is shared; replacement remains explicit. |
8147
8202
  | `transcribe(request,{signal,onProgress,requestId}={})` | Accepts the complete `{audioBase64,mimeType?,model?,language?,translate?}` recording for the loaded model and returns `{text,language,duration,segments}`. `audioBase64` transports the original media; the decoder recognizes its actual format. Progress is ordered; control metadata stays outside the request. |
@@ -8205,6 +8260,43 @@ includes the selected compiler's redistributable runtime closure. Use a
8205
8260
  toolchain and prepared runtime matching the target platform/architecture; a
8206
8261
  source package alone is not a compiled helper. See the [helper build procedure](local-whisper.md).
8207
8262
 
8263
+ ## buildWhisperOpenVinoRuntime()
8264
+
8265
+ `buildWhisperOpenVinoRuntime({sourceDirectory,openvinoDirectory,cudaDirectory,
8266
+ outputRoot,cmake='cmake',generator,cmakeArgs=[],env,signal,onEvent}={})` from
8267
+ `arcane-os/local-ai/whisper/openvino-build` produces the optional native Intel
8268
+ NPU encoder runtime on Windows x64. This producer uses an already installed
8269
+ compatible CMake/MSVC environment, Whisper 1.9.4 source and OpenVINO 2026.3.1
8270
+ development tree. Required `sourceDirectory`, `openvinoDirectory` and
8271
+ `outputRoot` name those inputs and the producer-owned output. It installs no
8272
+ driver, toolchain or model and performs no inference.
8273
+
8274
+ The producer copies Whisper source into `outputRoot/source`, applies its
8275
+ encoder-failure propagation and stderr diagnostic corrections there, and runs
8276
+ CMake configure/build into `outputRoot/build`. The original source stays
8277
+ unchanged. It assembles the runtime under `outputRoot/runtime`, retaining
8278
+ OpenVINO/NPU, CPU, TBB, import libraries and packaging notices. Optional
8279
+ `cudaDirectory` names the matching upstream b5130 CUDA library directory;
8280
+ its selected precompiled backend libraries add CUDA alongside CPU. Omission
8281
+ produces the NPU-plus-CPU tree. `cmake`, `generator`, `cmakeArgs` and `env`
8282
+ configure the existing build environment.
8283
+
8284
+ The result is `{version,openvinoVersion,platform,architecture,encoder,backend,
8285
+ backends,root,directory,libraryDirectory,sourceDirectory,diagnostics}`.
8286
+ `directory` equals `root`; the returned `sourceDirectory` is the copied source.
8287
+ `diagnostics` retains complete `configure` and `compile` process results.
8288
+ `signal` and `onEvent` use the existing process cancellation and diagnostic
8289
+ contract. Missing required strings produce a `TypeError`; an unsupported
8290
+ producer host or unmatched source correction reports an error. Filesystem,
8291
+ process, cancellation and observer failures remain observable, with partial
8292
+ producer output left for its owner.
8293
+
8294
+ Build the matching first-party helper separately with `buildWhisperHelper`,
8295
+ then retain its complete installed `bin` alongside this runtime. The
8296
+ [complete producer example](../guides/native-whisper-openvino.md#produce-the-optional-native-runtime)
8297
+ covers that composition. Applications normally select the precompiled asset
8298
+ through `encoder: 'openvino-npu'`; they do not run this producer at startup.
8299
+
8208
8300
  ## createSpeechService()
8209
8301
 
8210
8302
  ### Overview and usage
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arcane-os",
3
- "version": "0.65.0",
3
+ "version": "0.66.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",
@@ -5,6 +5,9 @@ import Is from 'strong-type';
5
5
  import {ArcaneError, throwIfAborted} from '../errors.mjs';
6
6
  import {runProcess} from '../process.mjs';
7
7
  import {repositoryPull, repositoryPush, repositoryStatus} from '../repository.mjs';
8
+ import {createGitIdentityRunner} from '../git-identity.mjs';
9
+
10
+ export {readGitIdentity} from '../git-identity.mjs';
8
11
 
9
12
  const is = new Is(false);
10
13
  const directoryOperations = new Map();
@@ -41,7 +44,7 @@ export function resolveArcaneDataPaths({dataRoot} = {}) {
41
44
 
42
45
  /** One connected checkout, composed into an application's own Core service. */
43
46
  export function createRepositoryWorkspace(
44
- {name, directory, dataRoot, remote, branch, onEvent, run = runProcess} = {}
47
+ {name, directory, dataRoot, remote, branch, gitIdentity, onEvent, run = runProcess} = {}
45
48
  ) {
46
49
  let selected = directory;
47
50
  if (selected === undefined) {
@@ -55,12 +58,13 @@ export function createRepositoryWorkspace(
55
58
  if (remote !== undefined) requireString(remote, 'remote');
56
59
  if (branch !== undefined) requireString(branch, 'branch');
57
60
  if (!is.function(run)) throw new TypeError('run must implement the SDK process adapter.');
61
+ const execute = createGitIdentityRunner(run, gitIdentity);
58
62
  const repositoryDirectory = path.resolve(selected);
59
63
  const pending = new Set();
60
64
  let opened = false;
61
65
  let closing = null;
62
66
 
63
- async function prepare(signal, execute = run) {
67
+ async function prepare(signal, prepareRun = execute) {
64
68
  throwIfAborted(signal);
65
69
  if (opened) return {directory: repositoryDirectory, cloned: false};
66
70
  let entries;
@@ -74,7 +78,7 @@ export function createRepositoryWorkspace(
74
78
  if (entries.length) {
75
79
  // A parent checkout or a bare object cache is not this connection's
76
80
  // working root. Inspection never changes an existing destination.
77
- const result = await execute(
81
+ const result = await prepareRun(
78
82
  'git',
79
83
  ['rev-parse', '--is-inside-work-tree', '--show-prefix'],
80
84
  {cwd: repositoryDirectory, signal, onEvent}
@@ -97,7 +101,7 @@ export function createRepositoryWorkspace(
97
101
  const arguments_ = ['clone', '--progress'];
98
102
  if (branch !== undefined) arguments_.push('--branch', branch);
99
103
  arguments_.push('--', remote, repositoryDirectory);
100
- const result = await execute(
104
+ const result = await prepareRun(
101
105
  'git',
102
106
  arguments_,
103
107
  {cwd: path.dirname(repositoryDirectory), signal, onEvent}
@@ -142,7 +146,7 @@ export function createRepositoryWorkspace(
142
146
  return accept(
143
147
  async function readRepositoryStatus() {
144
148
  await prepare(signal);
145
- return repositoryStatus({workspaceRoot: repositoryDirectory, signal, onEvent, run});
149
+ return repositoryStatus({workspaceRoot: repositoryDirectory, signal, onEvent, run: execute});
146
150
  },
147
151
  signal
148
152
  );
@@ -152,7 +156,7 @@ export function createRepositoryWorkspace(
152
156
  return accept(
153
157
  async function pullRepository() {
154
158
  await prepare(signal);
155
- return repositoryPull({workspaceRoot: repositoryDirectory, signal, onEvent, run});
159
+ return repositoryPull({workspaceRoot: repositoryDirectory, signal, onEvent, run: execute});
156
160
  },
157
161
  signal
158
162
  );
@@ -162,7 +166,7 @@ export function createRepositoryWorkspace(
162
166
  return accept(
163
167
  async function pushRepository() {
164
168
  await prepare(signal);
165
- return repositoryPush({workspaceRoot: repositoryDirectory, signal, onEvent, run});
169
+ return repositoryPush({workspaceRoot: repositoryDirectory, signal, onEvent, run: execute});
166
170
  },
167
171
  signal
168
172
  );
@@ -260,7 +264,7 @@ export function createRepositoryWorkspace(
260
264
  }
261
265
 
262
266
  try {
263
- const result = await run(
267
+ const result = await execute(
264
268
  'git',
265
269
  args,
266
270
  {cwd: options.cwd ?? repositoryDirectory, signal, onEvent: observeEvent,
@@ -0,0 +1,132 @@
1
+ import path from 'node:path';
2
+ import Is from 'strong-type';
3
+ import {ArcaneError, throwIfAborted} from './errors.mjs';
4
+ import {runProcess} from './process.mjs';
5
+ import {createEventQueue} from './event-queue.mjs';
6
+
7
+ const is = new Is(false);
8
+
9
+ /** Read only non-secret identity configuration through ordinary Git scopes. */
10
+ export async function readGitIdentity({directory, signal, onEvent, run = runProcess} = {}) {
11
+ const cwd = directory === undefined ? undefined : path.resolve(directory);
12
+ throwIfAborted(signal);
13
+ const events = createEventQueue(onEvent);
14
+
15
+ async function observeScope(scope) {
16
+ const args = ['config'];
17
+ if (scope !== 'effective') args.push(`--${scope}`);
18
+ args.push('--includes', '--null', '--get-regexp', '^(user[.](name|email)|github[.]user)$');
19
+ const chunks = [];
20
+ try {
21
+ const result = await run('git', args, {
22
+ cwd, signal, onEvent: events.send, allowNonzero: true,
23
+ outputEncoding: {stdout: null},
24
+ captureOutput: {stdout: false},
25
+ emitOutputEvents: {stdout: false},
26
+ // Git configuration may contain non-UTF-8 values. Keep its raw
27
+ // output until close rather than silently replacing characters.
28
+ onOutput: function observeOutput({stream, chunk}) {
29
+ if (stream === 'stdout') chunks.push(chunk);
30
+ }
31
+ });
32
+ try { throwIfAborted(signal); }
33
+ catch (error) { error.details = result; throw error; }
34
+ const identity = {name: null, email: null, githubUser: null};
35
+ if (result.code === 1) return identity;
36
+ if (result.code !== 0) {
37
+ throw new ArcaneError('ARCANE_OPERATION_FAILED',
38
+ `Git ${scope} identity lookup exited with code ${String(result.code)}.`, {details: result});
39
+ }
40
+ let output;
41
+ try { output = new TextDecoder('utf-8', {fatal: true, ignoreBOM: true}).decode(Buffer.concat(chunks)); }
42
+ catch (cause) {
43
+ throw new ArcaneError('ARCANE_GIT_IDENTITY_NOT_TEXT',
44
+ `Git ${scope} identity configuration cannot be represented as UTF-8 text.`, {cause, details: result});
45
+ }
46
+ const fields = {'user.name': 'name', 'user.email': 'email', 'github.user': 'githubUser'};
47
+ // Git's NUL record framing preserves complete values, including newlines
48
+ // and empty strings. The last occurrence is the effective scalar value.
49
+ for (const record of output.split('\0')) {
50
+ if (record === '') continue;
51
+ const separator = record.indexOf('\n');
52
+ const key = separator === -1 ? record : record.slice(0, separator);
53
+ if (Object.hasOwn(fields, key)) {
54
+ identity[fields[key]] = separator === -1 ? '' : record.slice(separator + 1);
55
+ }
56
+ }
57
+ return identity;
58
+ } catch (error) {
59
+ // Keep the process error and its native details intact. Raw stdout
60
+ // belongs to this reader because the process owner did not decode it.
61
+ const rawStdout = Buffer.concat(chunks);
62
+ let attached = false;
63
+ let attachmentError;
64
+ try {
65
+ if (error !== null && (is.object(error) || is.function(error)) && !('rawStdout' in error)) {
66
+ Object.defineProperty(error, 'rawStdout', {
67
+ value: rawStdout, writable: true, enumerable: true, configurable: true
68
+ });
69
+ attached = true;
70
+ }
71
+ } catch (cause) { attachmentError = cause; }
72
+ if (attached) throw error;
73
+ const failure = new ArcaneError(error?.code ?? 'ARCANE_OPERATION_FAILED',
74
+ error instanceof Error ? error.message : String(error),
75
+ {cause: error, details: error?.details, exitCode: error?.exitCode});
76
+ failure.rawStdout = rawStdout;
77
+ if (attachmentError) failure.attachmentError = attachmentError;
78
+ throw failure;
79
+ }
80
+ }
81
+
82
+ const scopes = cwd === undefined ? ['global'] : ['global', 'local', 'effective'];
83
+ const observations = await Promise.allSettled(scopes.map(observeScope));
84
+ const failures = observations.filter(function failed(result) {
85
+ return result.status === 'rejected';
86
+ }).map(function cause(result) { return result.reason; });
87
+ try { await events.drain(); }
88
+ catch (error) {
89
+ const visited = new Set();
90
+ function containsObserverFailure(failure) {
91
+ if (failure === error) return true;
92
+ if (failure === null || failure === undefined || visited.has(failure)) return false;
93
+ visited.add(failure);
94
+ return containsObserverFailure(failure.cause)
95
+ || (is.array(failure.errors) && failure.errors.some(containsObserverFailure));
96
+ }
97
+ const represented = failures.some(containsObserverFailure);
98
+ if (!represented) failures.push(error);
99
+ }
100
+ if (failures.length === 1) throw failures[0];
101
+ if (failures.length) throw new AggregateError(failures, 'Git identity observations failed.');
102
+ const result = {global: null, local: null, effective: null};
103
+ for (const [index, scope] of scopes.entries()) result[scope] = observations[index].value;
104
+ return result;
105
+ }
106
+
107
+ /** Capture one factory's selection without changing Git files or process.env. */
108
+ export function createGitIdentityRunner(run, {name, email, username} = {}) {
109
+ const configuration = [];
110
+ const environment = {};
111
+ for (const [field, value] of Object.entries({name, email, username})) {
112
+ if (value === undefined) continue;
113
+ if (!is.string(value) || !value.isWellFormed() || value.includes('\0')) {
114
+ throw new TypeError(`gitIdentity.${field} must be text representable as a native process argument.`);
115
+ }
116
+ configuration.push('-c', `${field === 'username' ? 'credential.username' : `user.${field}`}=${value}`);
117
+ if (field === 'name') {
118
+ environment.GIT_AUTHOR_NAME = value;
119
+ environment.GIT_COMMITTER_NAME = value;
120
+ } else if (field === 'email') {
121
+ environment.GIT_AUTHOR_EMAIL = value;
122
+ environment.GIT_COMMITTER_EMAIL = value;
123
+ }
124
+ }
125
+ if (!configuration.length) return run;
126
+ const selectsAuthor = name !== undefined || email !== undefined;
127
+ return function runSelectedGit(command, args, options = {}) {
128
+ return run(command, [...configuration, ...args], selectsAuthor
129
+ ? {...options, env: {...options.env, ...environment}}
130
+ : options);
131
+ };
132
+ }
@@ -4,6 +4,7 @@ import path from 'node:path';
4
4
  import {runProcess} from './process.mjs';
5
5
  import {ArcaneError, ERROR_CODES, throwIfAborted} from './errors.mjs';
6
6
  import {createEventQueue} from './event-queue.mjs';
7
+ import {createGitIdentityRunner} from './git-identity.mjs';
7
8
 
8
9
  const is = new Is(false);
9
10
  const snapshotRef = 'refs/arcane/text-snapshot';
@@ -13,11 +14,12 @@ const snapshotRef = 'refs/arcane/text-snapshot';
13
14
  * One application service owns each dedicated cache directory. Construction
14
15
  * performs no I/O; refresh coalesces callers and close drains accepted work.
15
16
  */
16
- export function createGitTextSnapshot({cacheDirectory, remote, ref, selectPath, onEvent, run = runProcess} = {}) {
17
+ export function createGitTextSnapshot({cacheDirectory, remote, ref, selectPath, gitIdentity, onEvent, run = runProcess} = {}) {
17
18
  for (const [name, value] of Object.entries({cacheDirectory, remote, ref})) {
18
19
  if (!is.string(value) || value === '') throw new TypeError(`${name} must be a nonempty string.`);
19
20
  }
20
21
  if (!is.function(selectPath)) throw new TypeError('selectPath must be a function.');
22
+ const execute = createGitIdentityRunner(run, gitIdentity);
21
23
  const directory = path.resolve(cacheDirectory);
22
24
  let prepared = false;
23
25
  let retained = null;
@@ -34,7 +36,7 @@ export function createGitTextSnapshot({cacheDirectory, remote, ref, selectPath,
34
36
  };
35
37
  }
36
38
  try {
37
- return await run('git', ['--git-dir', directory, ...args], execution);
39
+ return await execute('git', ['--git-dir', directory, ...args], execution);
38
40
  } catch (error) {
39
41
  // runProcess first drains its child and preserves complete transport
40
42
  // failures. Keep the Git parser's public code with that full cause.
@@ -50,7 +52,7 @@ export function createGitTextSnapshot({cacheDirectory, remote, ref, selectPath,
50
52
  if (prepared) return;
51
53
  await mkdir(directory, {recursive: true});
52
54
  if ((await readdir(directory)).length === 0) {
53
- await run('git', ['init', '--bare', directory], {onEvent: events.send});
55
+ await execute('git', ['init', '--bare', directory], {onEvent: events.send});
54
56
  }
55
57
  const result = await git(['rev-parse', '--is-bare-repository'], {onEvent: events.send});
56
58
  // This is the selected cache's functional role: never reinterpret an