arcane-os 0.88.0 → 0.89.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.
Files changed (44) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +1 -1
  3. package/browser-runtime/ai/browser-decisions.mjs +31 -4
  4. package/browser-runtime/ai/core-local.mjs +105 -25
  5. package/browser-runtime/ai/decision-runtime.mjs +7 -0
  6. package/browser-runtime/ai/decision-worker.mjs +9 -1
  7. package/browser-runtime/core/client.mjs +6 -0
  8. package/browser-runtime/core/contracts.mjs +10 -0
  9. package/docs/reference/ai/browser-decisions.md +89 -7
  10. package/docs/reference/cli.md +10 -0
  11. package/docs/reference/core-client.md +59 -0
  12. package/docs/reference/core-native-packaging.md +57 -10
  13. package/docs/reference/core-repositories.md +47 -3
  14. package/docs/reference/inventory/package-api.json +15 -15
  15. package/docs/reference/inventory/runtime-components.json +1 -1
  16. package/docs/reference/inventory/runtime-modules.json +4 -4
  17. package/docs/reference/local-ai.md +86 -3
  18. package/docs/reference/mcp-stdio.md +76 -1
  19. package/docs/reference/native-app-control.md +17 -5
  20. package/docs/reference/protocols.md +14 -0
  21. package/docs/reference/runtime-modules.md +9 -3
  22. package/docs/reference/sdk-api.md +146 -23
  23. package/package.json +2 -2
  24. package/runtime/arcane/modules/RiskSignalAnalyzer.js +1 -5
  25. package/schemas/arcane-app.schema.json +13 -3
  26. package/schemas/arcane-package.schema.json +10 -1
  27. package/src/app-descriptor.mjs +6 -2
  28. package/src/cli/app-control.mjs +1 -1
  29. package/src/core/hosts/windows/ArcaneHost.cs +273 -5
  30. package/src/core/hosts/windows/ArcaneLauncher.cs +9 -0
  31. package/src/core/hosts/windows/ArcaneWindowControl.cs +7 -7
  32. package/src/core/repositories.mjs +11 -1
  33. package/src/core/services/local-ai.mjs +183 -22
  34. package/src/installed-sdk-runtime.mjs +2 -0
  35. package/src/local-ai/config.mjs +16 -0
  36. package/src/local-ai/dev.mjs +2 -2
  37. package/src/local-ai/discover.mjs +3 -0
  38. package/src/local-ai/install.mjs +130 -20
  39. package/src/local-ai/llama-execution-windows.ps1 +158 -0
  40. package/src/local-ai/llama-execution.mjs +169 -0
  41. package/src/local-ai/server.mjs +38 -8
  42. package/src/native/portable-layout.mjs +13 -3
  43. package/src/workspace-operation-lock.mjs +23 -10
  44. package/src/workspace-runtime.mjs +97 -1
@@ -85,15 +85,96 @@ async function removeTemporaryTree(location){
85
85
  await rm(location,{recursive:true,force:true}).catch(()=>{});
86
86
  }
87
87
 
88
+ async function materializeBrowserDecisions(destination, signal, onEvent) {
89
+ const transformers = 'https://cdn.jsdelivr.net/npm/@huggingface/transformers@4.3.0/';
90
+ const onnx = 'https://cdn.jsdelivr.net/npm/onnxruntime-web@1.31.0-dev.20260914-8d85527a0/';
91
+ const files = [
92
+ {name: 'transformers.min.js', url: `${transformers}dist/transformers.min.js`},
93
+ {name: 'ort-wasm-simd-threaded.asyncify.mjs', url: `${onnx}dist/ort-wasm-simd-threaded.asyncify.mjs`},
94
+ {name: 'ort-wasm-simd-threaded.asyncify.wasm', url: `${onnx}dist/ort-wasm-simd-threaded.asyncify.wasm`},
95
+ {name: 'TRANSFORMERS-LICENSE', url: `${transformers}LICENSE`},
96
+ {name: 'ONNX-RUNTIME-LICENSE', url: `${onnx}LICENSE`}
97
+ ];
98
+ const controller = new AbortController();
99
+ function cancelDistribution() {
100
+ controller.abort(signal.reason);
101
+ }
102
+ signal?.addEventListener('abort', cancelDistribution, {once: true});
103
+ if (signal?.aborted) cancelDistribution();
104
+ let completed = 0;
105
+ try {
106
+ throwIfAborted(controller.signal);
107
+ await emit(
108
+ onEvent,
109
+ {type: 'workspace.decisions.started', completed, total: files.length}
110
+ );
111
+ throwIfAborted(controller.signal);
112
+ await mkdir(destination, {recursive: true});
113
+ async function acquireDistributionFile(file) {
114
+ try {
115
+ throwIfAborted(controller.signal);
116
+ const response = await fetch(file.url, {signal: controller.signal});
117
+ const content = new Uint8Array(await response.arrayBuffer());
118
+ if (!response.ok) {
119
+ const error = new Error(`Browser decision distribution returned HTTP ${response.status} ${response.statusText}.`);
120
+ error.code = 'ARCANE_DECISION_DISTRIBUTION_DOWNLOAD_FAILED';
121
+ error.response = {
122
+ url: response.url,
123
+ status: response.status,
124
+ statusText: response.statusText,
125
+ headers: [...response.headers],
126
+ content
127
+ };
128
+ throw error;
129
+ }
130
+ throwIfAborted(controller.signal);
131
+ // Upstream distribution content is opaque, including its own
132
+ // imports and license text. No SDK source rewriting applies.
133
+ await writeFile(path.join(destination, file.name), content);
134
+ throwIfAborted(controller.signal);
135
+ completed += 1;
136
+ await emit(
137
+ onEvent,
138
+ {type: 'workspace.decisions.progress', file: file.name, url: file.url, completed, total: files.length}
139
+ );
140
+ } catch (error) {
141
+ controller.abort(error);
142
+ throw error;
143
+ }
144
+ }
145
+ // One fixed distribution, acquired once for the selected projection;
146
+ // no model/app/architecture multiplier or installation script.
147
+ const outcomes = await Promise.allSettled(files.map(acquireDistributionFile));
148
+ const failures = new Set();
149
+ for (const outcome of outcomes) {
150
+ if (outcome.status === 'rejected') failures.add(outcome.reason);
151
+ }
152
+ if (failures.size === 1) throw [...failures][0];
153
+ if (failures.size > 1) {
154
+ throw new AggregateError([...failures], 'Browser decision distribution acquisition failed.');
155
+ }
156
+ throwIfAborted(controller.signal);
157
+ } finally {
158
+ signal?.removeEventListener('abort', cancelDistribution);
159
+ }
160
+ return {
161
+ transformersVersion: '4.3.0',
162
+ onnxRuntimeVersion: '1.31.0-dev.20260914-8d85527a0',
163
+ files: files.map(function distributionFilename(file) { return file.name; })
164
+ };
165
+ }
166
+
88
167
  export async function materializeWorkspaceRuntimeContent({
89
168
  workspaceRoot,
90
169
  runtimeRoot=path.join(getSdkRoot(),'runtime'),
91
170
  browserRuntimeRoot=getSdkBrowserRuntimeRoot(),
92
171
  sdkVersion=SDK_VERSION,
172
+ browserDecisions=false,
93
173
  signal,
94
174
  onEvent
95
175
  }={}){
96
176
  if(!workspaceRoot)fail('workspaceRoot is required to materialize a workspace runtime.');
177
+ if(!is.boolean(browserDecisions))throw new TypeError('browserDecisions must be a boolean.');
97
178
  throwIfAborted(signal);
98
179
  const workspace=await realDirectory(workspaceRoot,'Workspace root');
99
180
  const runtime=await realDirectory(runtimeRoot,'SDK runtime root');
@@ -109,6 +190,7 @@ export async function materializeWorkspaceRuntimeContent({
109
190
  const backupRoot=path.join(workspace,`${BACKUP_PREFIX}${token}`);
110
191
  let backedUp=false;
111
192
  let promoted=false;
193
+ let decisionDistribution;
112
194
 
113
195
  await mkdir(stagingRoot);
114
196
  try{
@@ -134,6 +216,14 @@ export async function materializeWorkspaceRuntimeContent({
134
216
  );
135
217
  }
136
218
 
219
+ if (browserDecisions) {
220
+ decisionDistribution = await materializeBrowserDecisions(
221
+ path.join(sdkDestination, 'ai', 'decisions-runtime'),
222
+ signal,
223
+ onEvent
224
+ );
225
+ }
226
+
137
227
  throwIfAborted(signal);
138
228
  try{
139
229
  const existing=await lstat(destinationRoot);
@@ -160,7 +250,13 @@ export async function materializeWorkspaceRuntimeContent({
160
250
  return {
161
251
  kind:'arcane-workspace-runtime-content',
162
252
  workspaceRoot:workspace,
163
- runtimeRoot:destinationRoot
253
+ runtimeRoot:destinationRoot,
254
+ ...(decisionDistribution ? {
255
+ browserDecisions: {
256
+ ...decisionDistribution,
257
+ directory: path.join(destinationRoot, 'sdk', 'ai', 'decisions-runtime')
258
+ }
259
+ } : {})
164
260
  };
165
261
  }catch(error){
166
262
  if(promoted)await removeTemporaryTree(destinationRoot);