arcane-os 0.64.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.
Files changed (50) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/README.md +1 -1
  3. package/browser-runtime/ai/browser-decisions.mjs +3 -2
  4. package/browser-runtime/ai/decision-runtime.mjs +14 -24
  5. package/browser-runtime/core/client.mjs +62 -5
  6. package/docs/guides/native-whisper-openvino.md +334 -0
  7. package/docs/publishing.md +39 -0
  8. package/docs/reference/README.md +11 -1
  9. package/docs/reference/ai/browser-decisions.md +7 -4
  10. package/docs/reference/arcane-ollama.md +1 -1
  11. package/docs/reference/codex-app-server.md +269 -0
  12. package/docs/reference/core-client.md +60 -2
  13. package/docs/reference/core-development.md +157 -0
  14. package/docs/reference/core-native-packaging.md +13 -0
  15. package/docs/reference/core-repositories.md +232 -4
  16. package/docs/reference/git-text-snapshot.md +15 -1
  17. package/docs/reference/inventory/package-api.json +174 -6
  18. package/docs/reference/inventory/runtime-modules.json +8 -6
  19. package/docs/reference/local-ai.md +80 -3
  20. package/docs/reference/local-image-generation.md +5 -0
  21. package/docs/reference/local-image-sdxl.md +1 -1
  22. package/docs/reference/local-whisper.md +7 -1
  23. package/docs/reference/mcp-stdio.md +263 -0
  24. package/docs/reference/native-speech.md +6 -0
  25. package/docs/reference/runtime-components.md +18 -1
  26. package/docs/reference/runtime-modules.md +10 -2
  27. package/docs/reference/sdk-api.md +346 -4
  28. package/package.json +6 -2
  29. package/runtime/arcane/modules/AI.js +156 -13
  30. package/runtime/arcane/modules/Ollama.js +1 -1
  31. package/runtime/arcane/modules/OllamaModelIdentifier.js +13 -0
  32. package/schemas/arcane-app.schema.json +8 -1
  33. package/schemas/arcane-package.schema.json +8 -1
  34. package/src/codex/app-server.mjs +845 -0
  35. package/src/core/development.mjs +457 -0
  36. package/src/core/repositories.mjs +178 -9
  37. package/src/core/services/local-ai.mjs +80 -10
  38. package/src/dev-server.mjs +3 -2
  39. package/src/git-identity.mjs +132 -0
  40. package/src/git-text-snapshot.mjs +5 -3
  41. package/src/index.mjs +1 -0
  42. package/src/local-ai/dev.mjs +80 -426
  43. package/src/local-ai/whisper/config.mjs +31 -3
  44. package/src/local-ai/whisper/index.mjs +62 -5
  45. package/src/local-ai/whisper/install.mjs +72 -14
  46. package/src/local-ai/whisper/native/main.cpp +33 -2
  47. package/src/local-ai/whisper/openvino-build.mjs +138 -0
  48. package/src/mcp/stdio-server.mjs +579 -0
  49. package/src/native/portable-layout.mjs +3 -1
  50. package/src/toolchain.mjs +19 -8
@@ -54,15 +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) |
63
+ | Select the complete SDXL Base 1.0 checkpoint for text-to-image generation | [SDXL Base 1.0 through Core](local-image-sdxl.md) |
60
64
  | Compose independently loaded transcription and synthesis engines | [Native speech service](native-speech.md) |
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) |
61
67
  | Edit an original PNG with a complete prompt and explicit strength | [Local image editing](local-image-editing.md) |
62
68
  | Project complete stored model files into native working files | [Model assets through Core](model-assets.md) |
63
69
  | Retain a Nemotron model and diarize caller-fed audio streams | [Native speaker diarization](diarization.md) |
64
70
  | Persist application-selected preferences through Core | [Core preferences service](core-preferences.md) |
65
71
  | Read complete selected Git text without changing a checkout | [Git text snapshots](git-text-snapshot.md) |
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) |
66
74
  | Build an installed-SDK portable Core payload | [Portable Core packaging](core-native-packaging.md) |
67
75
  | Pass host-selected state defaults and unchanged explicit launch values to services | [Launch-time locations](core-native-packaging.md#launch-time-locations) |
68
76
  | Build a Windows executable without an OS checkout | [Windows executable packaging](core-native-packaging.md#windows-executable) |
@@ -139,7 +147,8 @@ node tools/build-reference-site.mjs --write --source-ref <documentation-commit>
139
147
 
140
148
  The same option is accepted by `createReferenceSite({sourceRef})` and
141
149
  `writeReferenceSite({sourceRef})`. It reads the selected commit's package
142
- 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
143
152
  runtime sources, and authored site inputs directly from Git in memory. It
144
153
  creates no checkout or export and leaves source drafts untouched. Rendering
145
154
  uses the current canonical generator and its authored semantic contracts;
@@ -227,6 +236,7 @@ Ollama, Wllama, Whisper, Kokoro, native, or cloud transport:
227
236
  | Use Core-normalized chat | [`globalThis.Arcane.ai`](core/arcane-ai-contracts.md) | Native/Core only when separately admitted. |
228
237
  | Run a caller-selected GGUF LLM locally | [`arcane-os/ai/browser-wasm`](ai/browser-wasm.md) | Browser secure context with WebAssembly and OPFS/DBOPFS; explicit CPU selection or WebGPU with full offload and no automatic CPU fallback. |
229
238
  | Run application-selected native llama.cpp/Ollama chat | [`arcane-os/ai/core-local`, `arcane-os/core/local-ai`](local-ai.md) | Explicit Core connection and selected native runtimes; installation, availability and loaded-model readiness remain distinct. |
239
+ | Transcribe complete recordings with a retained native Whisper model | [`arcane-os/local-ai/whisper`](local-whisper.md) and [Core speech](native-speech.md) | Matching native helper, Whisper libraries, decoder and selected model; Windows defaults and explicit other-platform distributions have distinct preparation requirements. |
230
240
  | Run a local ONNX graph with named tensors | [`arcane-os/local-ai/onnx`, `arcane-os/ai/core-onnx`](local-ai.md#native-onnx-sessions) | Retained native workers; browser access requires an available Core ONNX service. |
231
241
  | Generate local images from complete prompts and selected model resources | [`arcane-os/local-ai/image`, `arcane-os/ai/core-image`](local-image-generation.md) | Prepared native image runtime; browser access requires its Core service. CPU Windows/Linux and Mac Metal/CPU paths have distinct actual host requirements. |
232
242
  | Supply complete stored model members to a native engine | [`arcane-os/ai/core-model-assets`, `arcane-os/core/model-assets`](model-assets.md) | Available Core service; DBOPFS originals stay authoritative while native owners retain working files. |
@@ -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
@@ -120,7 +120,7 @@ non-writable `globalThis.arcaneOllama` convenience and emits
120
120
  | `version()` | `Arcane.ollama.version()` | Raw service version diagnostic. | [version](core/reference/arcane-api/ai-and-ollama.md#arcaneollamaversion) |
121
121
  | `models()` | `Arcane.ollama.models()` | Raw installed-model diagnostic. | [models](core/reference/arcane-api/ai-and-ollama.md#arcaneollamamodels) |
122
122
  | `list()` | Calls `Arcane.ollama.models()` | Module alias for `models()`; it does not call the bridge's separate `list` alias. | [list](core/reference/arcane-api/ai-and-ollama.md#arcaneollamalist) |
123
- | `running()` | `Arcane.ollama.running()` | Raw resident-model diagnostic. | [running](core/reference/arcane-api/ai-and-ollama.md#arcaneollamarunning) |
123
+ | `running(options?)` | `Arcane.ollama.running(options?)` | Raw resident-model diagnostic; optional `{signal, timeoutMs, onRequest}` uses the Core request lifetime. No-argument calls remain supported. | [running](core/reference/arcane-api/ai-and-ollama.md#arcaneollamarunning) |
124
124
  | `show(model, options)` | `Arcane.ollama.show(...)` | Raw bounded model metadata. | [show](core/reference/arcane-api/ai-and-ollama.md#arcaneollamashow) |
125
125
  | `generate(request, options)` | `Arcane.ollama.generate(...)` | Admitted generation; optional chunk callback/signal/timeout. | [generate](core/reference/arcane-api/ai-and-ollama.md#arcaneollamagenerate) |
126
126
  | `chat(request, options)` | `Arcane.ollama.chat(...)` | Admitted chat/tools; optional chunk callback/signal/timeout. | [chat](core/reference/arcane-api/ai-and-ollama.md#arcaneollamachat) |
@@ -0,0 +1,269 @@
1
+ # Native Codex App Server sessions
2
+
3
+ `arcane-os/codex/app-server` is an optional Node entrypoint for an explicitly
4
+ selected native Codex App Server. It exports
5
+ `createCodexAppServerSession(options)` and
6
+ `openCodexAppServerSession(options)`.
7
+
8
+ The adapter owns JSONL correlation and session lifecycle, reusing the SDK's
9
+ `runProcess` and semantic event manager. Applications own connection selection,
10
+ thread routing, prompts, approval decisions, visible presentation and explicit
11
+ send actions. It does not install Codex, authenticate an account, attach to a
12
+ desktop chat, start a thread during connection, or choose a model.
13
+
14
+ The [official Codex App Server documentation](https://learn.chatgpt.com/docs/app-server)
15
+ defines the external protocol: stdio JSONL frames omit `jsonrpc`; initialization
16
+ uses `initialize` followed by `initialized`; `turn/start` acceptance is distinct
17
+ from the terminal `turn/completed` notification. This transport is separate from
18
+ Core's Content-Length-framed `arcane/1` connection.
19
+
20
+ The source baseline is the current, unversioned official App Server page read
21
+ on October 6, 2026, specifically its Protocol, Initialization, Threads, Events
22
+ and approval/request sections. This adapter does not claim a pinned Codex CLI
23
+ release or generated-schema revision. The documentation describes schema
24
+ generation as specific to the CLI version used; no CLI or schema generator was
25
+ executed to establish this source contract.
26
+
27
+ ## Open an explicitly selected connection
28
+
29
+ ```javascript
30
+ import {createCodexAppServerSession} from 'arcane-os/codex/app-server';
31
+
32
+ const session = createCodexAppServerSession(
33
+ {
34
+ executable: selectedCodexExecutable,
35
+ args: ['app-server', '--listen', 'stdio://'],
36
+ cwd: selectedWorkingDirectory,
37
+ clientInfo: {name: 'moon_cheese_inventory', title: 'Moon Cheese Inventory', version: '1'}
38
+ }
39
+ );
40
+
41
+ // Store the handle before awaiting so the host can dispose during initialization.
42
+ connectionOwner.session = session;
43
+ session.subscribe('status', showConnectionStatus);
44
+ session.subscribe('serverRequest', askForActualDecision);
45
+ await session.connect();
46
+
47
+ const {thread} = await session.createThread(selectedThreadOptions);
48
+ const result = await session.runTurn(
49
+ {
50
+ threadId: thread.id,
51
+ input: 'Count the moon cheese and explain the missing wheel.',
52
+ signal: requestController.signal,
53
+ onDelta: showSelectedLiveDelta,
54
+ onItem: updateSelectedItem,
55
+ onTurn: showTurnStatus
56
+ }
57
+ );
58
+
59
+ // These records contain assistant text, not commands, reasoning or tool envelopes.
60
+ showVisibleResult(result.visibleItems, result.status);
61
+ await session.dispose();
62
+ ```
63
+
64
+ The example's named callbacks and owners belong to the application. Keep a
65
+ session alive across related operations; it launches one process per connection,
66
+ not one per request. `openCodexAppServerSession(options)` creates a session,
67
+ awaits connection readiness and returns it; use the synchronous factory when
68
+ the host needs the handle during connection or needs initialization events.
69
+ If opening fails, the convenience function disposes the owned process before
70
+ rejecting.
71
+
72
+ Options are `executable` (required), `args` (defaults shown above), `cwd`, `env`,
73
+ `clientInfo` and `capabilities`. `env` uses `runProcess`'s environment overrides.
74
+ Arguments are actual executable arguments; there is no shell command parser.
75
+ On Windows, select an actual native executable or explicitly select Node and
76
+ the installed CLI entry script in `args`, instead of passing a `.cmd` launcher.
77
+ No operating-system-specific credential store or home-directory assumption is
78
+ introduced. Node and the caller-selected native executable are required on
79
+ Windows, Linux and macOS; Android requires a host that supplies those process
80
+ facilities. A browser renderer cannot import or launch this Node-only adapter.
81
+
82
+ The default client identity is
83
+ `{name:'arcane_sdk',title:'Arcane SDK',version:'1'}`. Caller-supplied identity and
84
+ capabilities are forwarded to initialization. Experimental methods require the
85
+ server's corresponding capability selection. Avoid opting out of the turn/item
86
+ notifications that `runTurn` needs; the adapter does not override your selection
87
+ or manufacture missing events.
88
+
89
+ ## Session methods
90
+
91
+ | Method | Result and ownership |
92
+ | --- | --- |
93
+ | `connect()` | Promise of the same session after initialization; concurrent calls share the in-flight connection. Ready calls reuse it. |
94
+ | `listThreads(params)` | Complete native `thread/list` result, including `data` and cursor. The caller owns pagination. |
95
+ | `readThread({threadId,includeTurns,...options})` | Complete native `thread/read` result. No thread resume or event subscription. |
96
+ | `readTurn({threadId,turnId})` | Reads the thread with `includeTurns:true`, selects the exact turn, and returns the response shape below. No new turn or thread resume. |
97
+ | `createThread(params)` | Complete native `thread/start` result, including `thread`; the server subscribes the connection to that new thread. |
98
+ | `resumeThread({threadId,...options})` | Complete native `thread/resume` result for the caller's explicitly selected existing thread. |
99
+ | `request(method,params)` | Complete native result, or a correlated RPC error. Use for additional provider methods; no automatic retries. |
100
+ | `runTurn(options)` | Waits for the exact accepted turn's terminal notification. Does not create, resume or select a thread. |
101
+ | `interrupt({threadId,turnId})` | Native interrupt acknowledgement only; the terminal notification establishes the actual outcome. |
102
+ | `respond({id,result},context)` / `respond({id,error},context)` | Queues the actual correlated server-request reply and returns `{id,submitted:true}`. Retain the originating context across asynchronous work. Submission is not resolution. |
103
+ | `subscribe(type,listener,options)` | Returns an unsubscribe function; supports the shared event owner's `once` and `signal`. `status` replays immediately. |
104
+ | `reconnect()` | Drains the old owned process and initializes a new one. Never replays requests or resumes threads. |
105
+ | `dispose()` | Cancels the owned process tree, drains input/output/process completion, releases subscriptions and permanently disposes this session. |
106
+
107
+ Requests require a ready connection and do not implicitly connect.
108
+ `readThread` returns native `{thread:{turns:[{id,status,items,...}],...},...}`
109
+ when turns are requested. `readTurn` returns the stored status as supplied,
110
+ including an in-progress status if that is what the server returns. A missing
111
+ exact ID rejects with `ARCANE_CODEX_TURN_NOT_FOUND`; it never picks another turn.
112
+ Neither read operation claims the returned conversation belongs to the desktop
113
+ application. Access and available records depend on the selected App Server.
114
+
115
+ ## Input, live callbacks and complete output
116
+
117
+ `runTurn({threadId,input,signal,onDelta,onItem,onTurn,...turnOptions})` forwards
118
+ the remaining options to native `turn/start`. A native input array is sent
119
+ unchanged. A string receives only the required native text-item envelope
120
+ `[{type:'text',text:input}]`; the string itself is unchanged. No prompt prefix,
121
+ trimming, classification, normalization or implicit tool selection occurs.
122
+
123
+ Register or resume the selected thread before running a turn so the connection
124
+ receives its item/turn notifications. Local correlation listeners are installed
125
+ before submission. Events that arrive ahead of the acceptance response are
126
+ retained in order until the returned turn ID can correlate them.
127
+
128
+ - `onDelta({threadId,turnId,itemId,delta,phase,method,params})` receives complete
129
+ `item/agentMessage/delta` or `item/commandExecution/outputDelta` content. Use
130
+ `method` to select assistant text for the ordinary UI; command output belongs
131
+ in an explicitly selected inspection surface. `params` preserves all native
132
+ fields. Other delta methods remain available through `notification`.
133
+ - `onItem({threadId,turnId,itemId,text,phase,stage,item,params})` receives item
134
+ starts and completions. `stage` is `started` or `completed`; `phase` is the
135
+ provider's phase or `null`, not the lifecycle stage. `text` is agent-message
136
+ text, and is `null` for other item types. `item` remains the complete native
137
+ object, including extension fields.
138
+ - `onTurn({threadId,turnId,status,turn})` receives actual turn lifecycle events.
139
+ The `turnAccepted` session event separately carries the start response.
140
+
141
+ The resolved result is
142
+ `{threadId,turnId,status,items,visibleItems,error,turn}`:
143
+
144
+ - `status` is the actual terminal status: `completed`, `interrupted` or `failed`.
145
+ A provider failure resolves this terminal result with its actual `error`;
146
+ transport and start-request errors reject. Inspect status before committing a
147
+ successful application response.
148
+ - `items` contains complete native item objects in first-observed order, updated
149
+ by their final item snapshots and any terminal turn items. It includes commands
150
+ and other diagnostic/protocol item types; it is not durable chat history.
151
+ - `visibleItems` contains only agent-message projections
152
+ `{itemId,text,phase}`. Text is complete and unchanged. All supplied phases,
153
+ including `commentary`, `final_answer` or `null`, remain distinct. The app
154
+ decides which phases belong in its visible response; the adapter never joins
155
+ items, inserts separators or rewrites text.
156
+ - `turn` and `error` preserve the complete terminal provider objects. There is
157
+ no synthesized successful turn or substituted assistant failure message.
158
+
159
+ Callbacks are observational and invoked in incoming order, without awaiting
160
+ returned promises. This allows an approval owner to await another RPC without
161
+ deadlocking stdout. Synchronous failures and rejected callback promises produce
162
+ `observerError`; a rejection arriving after disposal goes to `console.error`.
163
+ Applications own completion of their asynchronous presentation/decision work.
164
+
165
+ ## Events and server requests
166
+
167
+ `session.status` is `{state,...details}`, with states `disconnected`,
168
+ `connecting`, `ready`, and `disposed`. Ready includes the complete `initialized`
169
+ result; connection failure includes `error`. There is no implied authentication,
170
+ model readiness, thread ownership or persistence guarantee in a ready status.
171
+
172
+ Session event names are `status`, `message`, `notification`, `stderr`, `process`,
173
+ `error`, `serverRequest`, `serverRequestResponded`, `serverRequestResolved`,
174
+ `turnAccepted`, `delta`, `item`, `turn` and `observerError`. Listener arguments
175
+ are detail objects directly, not DOM events. Shared semantic event names use
176
+ the `arcane.codex.app-server.` prefix and lowercase hyphen-separated suffixes.
177
+ The public subscription aliases retain their original spelling:
178
+
179
+ | Session alias | Shared semantic event |
180
+ | --- | --- |
181
+ | `serverRequest` | `arcane.codex.app-server.server-request` |
182
+ | `serverRequestResponded` | `arcane.codex.app-server.server-request-responded` |
183
+ | `serverRequestResolved` | `arcane.codex.app-server.server-request-resolved` |
184
+ | `turnAccepted` | `arcane.codex.app-server.turn-accepted` |
185
+ | `observerError` | `arcane.codex.app-server.observer-error` |
186
+
187
+ The other aliases are already lowercase and are appended unchanged to the
188
+ prefix. Declaration, subscription and publication share this same mapping;
189
+ native Codex wire method names and payload fields are unchanged.
190
+
191
+ `message` exposes every complete decoded JSONL envelope; `notification` exposes
192
+ all native notification methods. `stderr` preserves complete `{stream,chunk}`
193
+ records. `process` forwards the SDK process owner's lifecycle events. These
194
+ diagnostics are neither application conversation text nor saved chat history.
195
+
196
+ Every inbound request with a method and ID emits `serverRequest`. Its listener
197
+ receives the complete native envelope as its first argument and a separate
198
+ origin-bound reply context as its second. This includes approvals, permissions, user input,
199
+ MCP elicitation and experimental tool calls. The adapter does not approve,
200
+ decline, fabricate tool results or invoke application tools itself. The
201
+ application returns the exact native result or error for that request method:
202
+
203
+ ```javascript
204
+ async function askForActualDecision(request, context) {
205
+ const result = await applicationDecisionOwner.answer(request);
206
+ context.respond(
207
+ {result}
208
+ );
209
+ }
210
+ ```
211
+
212
+ The stdout reader keeps consuming while that decision is pending.
213
+ The context is `{id,respond}`. `context.respond({result})` or
214
+ `context.respond({error})` binds the reply to that exact request and its native
215
+ ID. Equivalently, pass the same context object as the second argument to
216
+ `session.respond({id:request.id,result},context)`. Retain it before awaiting a
217
+ human decision or other work. It is SDK control metadata, not part of the native
218
+ request or reply payload.
219
+
220
+ ID-only `session.respond({id,result})` remains supported while that native ID
221
+ has appeared only once in this session. Once an ID is reused, including after
222
+ reconnect, ID-only replies throw `ARCANE_CODEX_REQUEST_CONTEXT_REQUIRED` rather
223
+ than selecting an origin implicitly. A context from an older request throws
224
+ `ARCANE_CODEX_STALE_REQUEST_CONTEXT` when a current request reuses its ID. If
225
+ there is no corresponding current request, the existing not-ready/not-found
226
+ error applies. No such failure writes a reply to the new process. This also
227
+ handles ID reuse within one connection after a request resolves.
228
+
229
+ `pendingServerRequests` contains `{request,responded,context,contextRequired}`
230
+ entries for the current connection. An attention owner can retain that same
231
+ context when presenting an already pending request. Submission changes
232
+ `responded`; the server's
233
+ `serverRequest/resolved` notification removes the entry. A late reply after
234
+ resolution, disconnect, or a prior submitted reply is reported explicitly.
235
+ Disconnection clears pending requests and includes their complete prior entries
236
+ in the connection `error` event as `pendingServerRequests`.
237
+ `serverRequestResolved` receives the native `{threadId,requestId}` params after
238
+ the corresponding pending entry is removed. The `serverRequestResponded` event
239
+ receives `{request,response}`, while `serverRequest` receives the raw request
240
+ envelope directly. Clear application attention from actual pending state or
241
+ resolution/disconnection, rather than treating local submission as resolution.
242
+
243
+ ## Cancellation and unknown results
244
+
245
+ A pre-aborted turn rejects with `ARCANE_CANCELLED` and
246
+ `details.outcome:'not-submitted'`. After submission, abort requests interruption
247
+ as soon as the accepted turn ID is known. It stops that call's `onDelta` and
248
+ `onItem` delivery; raw session events and actual `onTurn` terminal status remain
249
+ observable. A successful interrupt acknowledgement is not a terminal result.
250
+ If interruption itself fails, the operation rejects with an unknown outcome.
251
+
252
+ Connection loss or disposal before terminal completion rejects a turn with
253
+ `ARCANE_CODEX_TURN_UNKNOWN`, `details.outcome:'unknown'`, the actual thread ID,
254
+ the turn ID when known, and observed native items. Ordinary pending RPCs reject
255
+ with `ARCANE_CODEX_CONNECTION_LOST` and unknown outcome. Native RPC rejection
256
+ uses `ARCANE_CODEX_RPC` and retains `details.error`, `response`, `method`,
257
+ `requestId`, `params` and `outcome:'rejected'`.
258
+
259
+ There are no implicit retries, timer-based completions, generic shutdown RPCs,
260
+ or claims that stdin EOF durably saves a turn. After reconnecting, use the known
261
+ thread and turn IDs with `readTurn` to reconcile. If acceptance was never
262
+ observed and no turn ID is known, inspect `readThread({threadId,includeTurns:true})`
263
+ and let the application resolve that ambiguity. Never automatically repeat
264
+ `turn/start` because its outcome is uncertain. Resume a selected thread only
265
+ when the application explicitly intends further work on it.
266
+
267
+ This page describes the SDK source contract. Native Codex, authentication,
268
+ provider behavior and platform execution require evidence from the selected
269
+ installed runtime; authored fixture coverage is not evidence of those outcomes.
@@ -1,8 +1,8 @@
1
1
  # Core browser client
2
2
 
3
3
  `arcane-os/core/client` is the browser-safe `arcane/1` RPC owner. It exports
4
- `createCoreClient`, `createCoreFacade`, `installCoreClient` and
5
- `getInstalledCoreClient`.
4
+ `createCoreClient`, `createCoreFacade`, `installCoreClient`,
5
+ `getInstalledCoreClient` and `subscribeCoreClient`.
6
6
  `arcane-os/core/contracts` exports `CORE_PROTOCOL`, `CORE_READY_EVENTS`,
7
7
  `CORE_FRAME_CONTRACTS`, `CORE_METHOD_CONTRACTS`, `CoreError` and
8
8
  `serializeCoreError`. Contracts describe transport and existing method shapes;
@@ -43,6 +43,57 @@ need not inspect private installation fields. It returns `null` after that
43
43
  installation closes or fails. A
44
44
  foreign application's `Arcane` object is not evidence of an SDK installation.
45
45
 
46
+ ### Observe installation and retirement
47
+
48
+ ```js
49
+ import {subscribeCoreClient} from 'arcane-os/core/client';
50
+
51
+ const lifetime=new AbortController();
52
+ const stop=subscribeCoreClient(function useCurrentClient({client,reason,error}){
53
+ console.log('Moon-base connection owner:',client,reason);
54
+ if(error)console.error('Core transport failed:',error);
55
+ },{signal:lifetime.signal});
56
+ // Call stop() or lifetime.abort() when this observer's owner ends its lifetime.
57
+ ```
58
+
59
+ `subscribeCoreClient(listener, {global=globalThis, emitCurrent=true, signal}={})`
60
+ observes the exact SDK-installed client for that global without installing or
61
+ connecting one. By default it synchronously replays
62
+ `{client, previousClient:null, reason:'current', error:null}` after subscribing.
63
+ `client` is the same live object returned by `getInstalledCoreClient`, or `null`.
64
+ Set `emitCurrent:false` to observe only subsequent changes. The returned
65
+ unsubscribe function is idempotent; aborting `signal` removes the subscription.
66
+ A pre-aborted signal registers nothing and emits no replay.
67
+
68
+ Later notifications use the same fields. `reason:'installed'` follows the
69
+ connection attempt while that installation is still live, with
70
+ `previousClient:null` and `error:null`. Installation does not imply a connected
71
+ transport: a standalone client retains its ordinary disconnected capabilities.
72
+ Repeated installation returns the existing client without another notification.
73
+ `reason:'closed'` and `reason:'transport-failed'` identify the retired client in
74
+ `previousClient`; a transport failure retains its complete actual `CoreError`
75
+ in `error`, while ordinary close uses `null`.
76
+
77
+ Retirement releases and restores owned globals before notifying, once per
78
+ installation. `client` is read again after cleanup, so a replacement installed
79
+ reentrantly during cleanup or error reporting remains the current client.
80
+ Notifications do not restore or close that replacement. A listener may also
81
+ install a replacement after observing retirement; its installation produces
82
+ the normal next notification. Reentrant changes queue until the current
83
+ notification finishes reaching its observers. Each notification retains the
84
+ exact client and error references captured for that transition, so observers
85
+ receive retirement before a replacement installed by another observer. Global
86
+ installation changes remain synchronous; reading the accessor inside a callback
87
+ may therefore show a newer client than that callback's transition snapshot.
88
+
89
+ The existing shared SDK event owner delivers changes without polling or a
90
+ second event bus. Classic host injection may install synchronously before that
91
+ event owner is available. The first ESM subscriber attaches the shared event
92
+ source lazily and reads the exact classic-installed client from the live
93
+ installation. Source-scoped delivery preserves the client and error references;
94
+ it does not use the event owner's global diagnostic snapshots. No historical
95
+ installation record is retained for replay.
96
+
46
97
  The native adapters select WebView2, WebKitGTK or Android WebView. Ordinary
47
98
  browsers remain `standalone`; they do not probe a local server. Development
48
99
  HTTP is selected only by the existing explicit `__ARCANE_DEV_HTTP__` flag.
@@ -73,6 +124,13 @@ event data retain their complete supplied content. JSON encoding belongs only
73
124
  to transports that require it; values crossing those transports must be
74
125
  JSON-compatible. There is no client method allowlist or content limit.
75
126
  The default request timeout is ten minutes; `timeoutMs:0` disables that timer.
127
+
128
+ `Arcane.ollama.running({signal, timeoutMs, onRequest}={})` forwards these request
129
+ options to the existing Core invocation without putting them in the Ollama
130
+ payload. It returns the complete resident-model snapshot; aborting its signal
131
+ cancels that inspection through the same request control path. Existing
132
+ `Arcane.ollama.running()` calls remain supported.
133
+
76
134
  The compatibility facade retains its operation-specific timeouts and streaming
77
135
  callbacks. Streaming IDs and isolated-operation IDs are separate protocol
78
136
  correlation fields alongside the supplied request fields.
@@ -0,0 +1,157 @@
1
+ # Application Core services during source development
2
+
3
+ `developApplication()` can compose an application's Core services without
4
+ selecting, installing or loading a local AI engine. It uses the existing SDK
5
+ source server and one app-scoped Core dispatcher; the app owns its service
6
+ implementations and the explicit actions that start native work.
7
+
8
+ ## Select services
9
+
10
+ The ordinary `arcane dev` command reads `native.services` from the selected
11
+ application descriptor:
12
+
13
+ ```json
14
+ {
15
+ "native": {
16
+ "services": [
17
+ {"module": "bridge/moon-cheese.mjs", "options": {"shelf": "Lunar cheddar"}}
18
+ ]
19
+ }
20
+ }
21
+ ```
22
+
23
+ This is the `native` portion of the application's existing descriptor, not a
24
+ complete descriptor. Module paths resolve from the selected app root. Each
25
+ module exports its factory as default, using the same native packaging contract:
26
+
27
+ ```javascript
28
+ export default function createMoonCheese(options, context) {
29
+ return {
30
+ name: 'moon-cheese',
31
+ methods: {
32
+ 'moon-cheese.current': function current() {
33
+ return {shelf: options.shelf, message: 'The Moon is ready for cheese.'};
34
+ }
35
+ }
36
+ };
37
+ }
38
+ ```
39
+
40
+ The factory receives `(options, context)` without changing authored options.
41
+ Context supplies `appRoot`, the operation `signal`, and `onEvent`; a programmatic
42
+ call's explicit `context` fields take precedence, matching the native
43
+ launch-context convention. A factory may return a service promise. Service
44
+ definitions retain the Core `name`, `methods`, `start`, `drain` and `dispose`
45
+ contract. Services start independently after composition; a method waits only
46
+ for its own service's startup. A process such as Codex starts only when the
47
+ application service's explicit connect method requests it, not because the
48
+ development listener opened.
49
+
50
+ Programmatic composition uses:
51
+
52
+ ```javascript
53
+ import {developApplication} from 'arcane-os';
54
+
55
+ const development = await developApplication({
56
+ workspaceRoot: process.cwd(),
57
+ serviceModules: [{module: 'bridge/moon-cheese.mjs', options: {shelf: 'Lunar cheddar'}}],
58
+ context: {stateRoot: './selected-app-state'}
59
+ });
60
+ // Application work proceeds. Its owner later calls await development.close().
61
+ ```
62
+
63
+ `serviceModules` replaces the descriptor's module list when supplied, including
64
+ an explicitly empty array. `services` adds already-constructed Core service
65
+ definitions. Both can coexist with configured native local-AI services inside
66
+ one dispatcher. No services and no selected local AI preserves ordinary static
67
+ source serving. The result adds `core` when composition is selected and retains
68
+ the existing `localAI` result when local AI is selected.
69
+
70
+ ## Connect the page without blocking rendering
71
+
72
+ Load `/arcane-core.js` as an independent module. It immediately exports `client`
73
+ after synchronous installation, without awaiting SSE connection or Core/service
74
+ readiness. The existing `/arcane-local-ai.js` URL remains an alias for the same
75
+ bootstrap behavior. Managed import maps resolve its public Core-client import.
76
+
77
+ ```javascript
78
+ import {client} from '/arcane-core.js';
79
+
80
+ client.events.when('core.ready', function dispatcherReady() {
81
+ client.invoke('moon-cheese.current').then(
82
+ function showCheese(result) { console.log(result); },
83
+ function reportCheeseFailure(error) { console.error(error); }
84
+ );
85
+ });
86
+ ```
87
+
88
+ The SDK does not inject a script or hold the page's first render. Existing native
89
+ clients remain installed; a disconnected standalone client can be replaced by
90
+ this explicitly selected development connection. `core.ready` establishes
91
+ dispatcher readiness, not a connected application process or loaded model.
92
+ Application status methods remain the owner of application state and pending
93
+ requests. Generic replay supplies current Core/service lifecycle, not historical
94
+ responses, a repository catalog or an application's operation journal.
95
+
96
+ A page module that loads independently of the bootstrap can use
97
+ `subscribeCoreClient()` from `arcane-os/core/client`. It immediately replays the
98
+ installed client (or `null`), then reports installation, close and terminal
99
+ transport failure. This lets rendering proceed before the connection exists.
100
+ Installation alone is not service readiness; observe the returned client's
101
+ runtime and service events. Retire subscriptions with the page's owning lifetime.
102
+
103
+ The development transport uses `/events` for ordered SSE frames and `/rpc` for
104
+ JSON requests. The shared Core client owns correlation, timeout and cancellation.
105
+ Responses and correlated streaming events return to their originating document.
106
+ Uncorrelated service lifecycle events reach connected documents. The payload
107
+ is passed unchanged to the service; the adapter adds no application policy.
108
+ Connection and complete parsing/transport errors reach the client's existing
109
+ failure/diagnostic boundary. Reopening a page creates a new document connection,
110
+ not a second service runtime.
111
+
112
+ ## Direct composition
113
+
114
+ `createDevelopmentCore()` is exported from `arcane-os` and
115
+ `arcane-os/core/development`. It accepts
116
+ `{appRoot=process.cwd(), application, version, services=[], serviceModules=[],
117
+ context={}, getReplayEvents, signal, onEvent}` and returns synchronously:
118
+
119
+ - `ready`: the runtime after service modules are composed and dispatcher startup
120
+ is accepted; individual services may still be starting or may fail separately;
121
+ - `handler(request,response)`: an asynchronous boolean indicating whether an SDK
122
+ Core route was consumed;
123
+ - `current()`: `{state,error,core}`, with complete Core service/lifecycle state;
124
+ - `close()`: one idempotent shutdown promise.
125
+
126
+ Use `startDevServer({..., core})` to give that server the handler and shutdown
127
+ owner. A failed server start remains the caller's cleanup responsibility for
128
+ direct composition; `developApplication()` performs this cleanup itself.
129
+ `getReplayEvents()` optionally returns complete current `{event,data}` snapshots
130
+ for service-specific replay. It neither stores history nor replaces application
131
+ status methods. Existing local-AI composition uses it for its established model,
132
+ image and speech snapshots.
133
+
134
+ ## Cancellation and shutdown
135
+
136
+ A document disconnect or its `requests.cancelAll` cancels only its own
137
+ request-lifetime operations. Another document's work remains active. An accepted
138
+ `lifetime:'service'` operation survives renderer cancellation; the service owns
139
+ its durable completion. A disconnected document receives no later response,
140
+ and reconnecting does not automatically repeat the operation.
141
+
142
+ Closing the development owner stops new Core requests, cancels request-lifetime
143
+ work, waits for accepted service work, drains and disposes services, and joins
144
+ queued SSE writes before ending remaining connections. Failures remain complete
145
+ and observable. The SDK claims no completion for a service that ignores its
146
+ cancellation or never settles. It never kills the application process merely
147
+ because one page disconnects.
148
+
149
+ Definitions supplied in `services` belong to this cleanup owner as soon as the
150
+ handle is returned. Calling `close()` before its preparation microtask still
151
+ drains and disposes those definitions without starting them or loading modules.
152
+
153
+ This is one development-server process on Windows, Linux or macOS. It is not a
154
+ cross-process host discovery/start-or-reuse service, nor control of existing
155
+ Codex desktop tasks. Android requires its separate host adaptation. See
156
+ [Core runtime](core-runtime.md) and [Core client](core-client.md) for the underlying
157
+ public contracts.
@@ -282,6 +282,19 @@ results and CPU/Metal platform boundaries.
282
282
 
283
283
  ### Injected native speech engines
284
284
 
285
+ Selecting `whisper.cpp` in `native.localAI.runtimes` composes the SDK's retained
286
+ Whisper engine as STT in the shared speech service. Portable assembly retains
287
+ the selected helper, runtime variants and their libraries, FFmpeg decoder and
288
+ configured model files under the app's native runtime tree. The executable
289
+ host, helper, decoder and models remain separate files; selecting speech does
290
+ not embed them inside one application executable. Runtime paths relocate with
291
+ the payload. The default Windows x64 preparation uses the matching SDK release's
292
+ helper plus its MSVC/OpenMP redistributable closure. Other platforms require
293
+ their matching helper, runtime variants and decoder; the public helper builder
294
+ uses matching Whisper headers/libraries with the caller's native toolchain.
295
+ See [native Whisper preparation and packaging](local-whisper.md). Assembly is
296
+ distinct from execution on the target host.
297
+
285
298
  An application-owned service factory can return
286
299
  `createSpeechService({stt,tts,signal})` from `arcane-os/core/speech`, supplying
287
300
  either role or both. Include that factory through the existing