arcane-os 0.64.0 → 0.65.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 (41) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +1 -1
  3. package/browser-runtime/core/client.mjs +62 -5
  4. package/docs/guides/native-whisper-openvino.md +334 -0
  5. package/docs/reference/README.md +4 -0
  6. package/docs/reference/arcane-ollama.md +1 -1
  7. package/docs/reference/codex-app-server.md +269 -0
  8. package/docs/reference/core-client.md +60 -2
  9. package/docs/reference/core-development.md +157 -0
  10. package/docs/reference/core-native-packaging.md +13 -0
  11. package/docs/reference/core-repositories.md +112 -3
  12. package/docs/reference/inventory/package-api.json +143 -3
  13. package/docs/reference/inventory/runtime-modules.json +3 -2
  14. package/docs/reference/local-ai.md +73 -3
  15. package/docs/reference/local-image-generation.md +5 -0
  16. package/docs/reference/local-image-sdxl.md +1 -1
  17. package/docs/reference/mcp-stdio.md +263 -0
  18. package/docs/reference/runtime-components.md +18 -1
  19. package/docs/reference/runtime-modules.md +10 -2
  20. package/docs/reference/sdk-api.md +251 -1
  21. package/package.json +6 -2
  22. package/runtime/arcane/modules/AI.js +156 -13
  23. package/runtime/arcane/modules/Ollama.js +1 -1
  24. package/runtime/arcane/modules/OllamaModelIdentifier.js +13 -0
  25. package/schemas/arcane-app.schema.json +8 -1
  26. package/schemas/arcane-package.schema.json +8 -1
  27. package/src/codex/app-server.mjs +845 -0
  28. package/src/core/development.mjs +457 -0
  29. package/src/core/repositories.mjs +170 -5
  30. package/src/core/services/local-ai.mjs +80 -10
  31. package/src/dev-server.mjs +3 -2
  32. package/src/index.mjs +1 -0
  33. package/src/local-ai/dev.mjs +80 -426
  34. package/src/local-ai/whisper/config.mjs +31 -3
  35. package/src/local-ai/whisper/index.mjs +62 -5
  36. package/src/local-ai/whisper/install.mjs +72 -14
  37. package/src/local-ai/whisper/native/main.cpp +33 -2
  38. package/src/local-ai/whisper/openvino-build.mjs +138 -0
  39. package/src/mcp/stdio-server.mjs +579 -0
  40. package/src/native/portable-layout.mjs +3 -1
  41. package/src/toolchain.mjs +19 -8
@@ -0,0 +1,263 @@
1
+ # MCP STDIO server
2
+
3
+ Expose application-owned tools and static resources to an MCP client with
4
+ `arcane-os/mcp/stdio`. This optional Node entrypoint owns one JSON-RPC session,
5
+ UTF-8 newline framing, discovery, request correlation, cooperative cancellation,
6
+ and graceful drain. The application owns its launch command, tool definitions,
7
+ resource contents, business behavior, and underlying service lifetime.
8
+
9
+ Availability: Node on Windows, Linux, and macOS, using the package's supported
10
+ Node version. Android requires a host that supplies the same Node stream and
11
+ process environment. Importing the entrypoint starts no process or listener.
12
+ It is separate from the native Core `arcane/1` transport and its Content-Length
13
+ frames; the two stream formats are not interchangeable.
14
+
15
+ ## createMcpStdioServer
16
+
17
+ ```javascript
18
+ import {createMcpStdioServer} from 'arcane-os/mcp/stdio';
19
+
20
+ const menu = {
21
+ content: 'Moon soup\nExtra tentacles available.',
22
+ message: {chef: 'Octopus', open: true}
23
+ };
24
+
25
+ const server = createMcpStdioServer(
26
+ {
27
+ serverInfo: {name: 'moon-pantry', version: '1.0.0'},
28
+ instructions: 'Ask moon.menu for the complete lunar dinner menu.',
29
+ tools: [
30
+ {
31
+ name: 'moon.menu',
32
+ description: 'Read the complete lunar dinner menu.',
33
+ inputSchema: {type: 'object'},
34
+ annotations: {readOnlyHint: true},
35
+ handler: readMenu
36
+ }
37
+ ],
38
+ resources: [
39
+ {
40
+ uri: 'pantry://menu',
41
+ name: 'menu',
42
+ title: 'Lunar dinner menu',
43
+ mimeType: 'application/json',
44
+ handler: readMenuResource
45
+ }
46
+ ]
47
+ }
48
+ );
49
+
50
+ function readMenu(args, {signal}) {
51
+ signal.throwIfAborted();
52
+ return {
53
+ content: [{type: 'text', text: JSON.stringify(menu)}],
54
+ structuredContent: menu
55
+ };
56
+ }
57
+
58
+ function readMenuResource({uri}, {signal}) {
59
+ signal.throwIfAborted();
60
+ return {
61
+ contents: [{uri, mimeType: 'application/json', text: JSON.stringify(menu)}]
62
+ };
63
+ }
64
+
65
+ server.start();
66
+ try {
67
+ await server.closed;
68
+ } catch {
69
+ // The adapter already sent its diagnostic to stderr.
70
+ process.exitCode = 1;
71
+ }
72
+ ```
73
+
74
+ Save this in the consuming application and configure the MCP client's subprocess
75
+ command as `node` with that application's script path. Client configuration
76
+ file formats and process launch policy remain client-owned. Change `menu` to
77
+ change the complete result; no model, repository, or native host is needed for
78
+ this example.
79
+
80
+ The application explicitly converts its domain record into an MCP result.
81
+ The SDK does not guess from `content`, `message`, or other domain field names.
82
+ The serialized text in this example is an application-selected representation;
83
+ the SDK never adds it automatically.
84
+
85
+ ### Parameters
86
+
87
+ `createMcpStdioServer(options)` returns a server object synchronously.
88
+
89
+ | Option | Contract |
90
+ | --- | --- |
91
+ | `serverInfo` | Required MCP implementation object with `name` and `version` strings. Optional MCP implementation fields are passed through. |
92
+ | `instructions` | Optional complete instruction string returned by initialization. |
93
+ | `tools` | Optional static array; each entry is a complete MCP tool descriptor plus `handler(args, context)`. Defaults to `[]`. Tool names identify handlers exactly and must be unique. |
94
+ | `resources` | Optional static array; each entry is a complete MCP resource descriptor plus `handler(params, context)`. Defaults to `[]`. Each resource requires `uri` and `name`; URIs identify handlers exactly and must be unique. |
95
+ | `onDiagnostic` | Optional error observer. Omit it for complete error text on the selected stderr stream. It may return a promise, which participates in drain. Observer failures are reported to stderr and retained by `closed`. |
96
+
97
+ The factory captures catalog entries at construction. Discovery preserves
98
+ descriptor fields, including authored schemas, descriptions, annotations,
99
+ icons, and metadata; only the local `handler` function is omitted. No tool
100
+ arguments, descriptions, schemas, resources, or result content are injected,
101
+ trimmed, rewritten, or shortened. Supply JSON-serializable protocol values.
102
+
103
+ The SDK checks the request envelope and required result container. It does not
104
+ provide a general JSON Schema evaluator. The application supplies a valid
105
+ `inputSchema` object, owns the handler's input semantics, and ensures any
106
+ declared `outputSchema` matches its structured result. This adapter does not
107
+ implement task-augmented execution; descriptors must describe ordinary tool
108
+ execution.
109
+
110
+ ### Handler inputs and results
111
+
112
+ Tool handlers receive `params.arguments` unchanged. Omitted arguments become
113
+ an empty object as the protocol's no-argument case. Resource handlers receive
114
+ the complete `resources/read` params object, including its exact `uri` and
115
+ any metadata. A handler may return its result directly or return a promise.
116
+
117
+ Each handler receives a context with:
118
+
119
+ - `signal`: its own request's `AbortSignal`;
120
+ - `requestId`: the original string or integer ID, including numeric zero;
121
+ - `method`: `tools/call` or `resources/read`;
122
+ - `params`: the complete request params object;
123
+ - `clientInfo` and `clientCapabilities`: the client's initialization objects.
124
+
125
+ A tool handler returns an explicit MCP `CallToolResult`, including a `content`
126
+ array and optional `structuredContent`, `isError`, `_meta`, or other protocol
127
+ fields. All supplied content blocks and JSON fields pass through. Resource
128
+ handlers return an explicit `ReadResourceResult` with a `contents` array;
129
+ complete text or already encoded binary resource contents pass through.
130
+ The application creates these protocol result objects deliberately. A plain
131
+ domain object with `content: 'some text'` is not a `CallToolResult` and produces
132
+ an internal protocol error rather than being silently converted.
133
+
134
+ For a domain failure, return a complete `CallToolResult` with `isError: true`.
135
+ An ordinary exception thrown by a tool handler becomes an `isError: true`
136
+ text result containing the exception's complete message, and the complete
137
+ exception is also reported through diagnostics. Throw `McpProtocolError` for
138
+ an explicit JSON-RPC failure. Other resource or adapter exceptions produce
139
+ `-32603` and a diagnostic; malformed result containers and non-serializable
140
+ results also produce `-32603`.
141
+ Custom fields on an ordinary thrown error remain in diagnostics unless the
142
+ application explicitly maps its known outcome to `structuredContent` in an
143
+ `isError: true` tool result.
144
+
145
+ ## start
146
+
147
+ `server.start({input, output, error})` attaches to caller-owned Node streams and
148
+ returns the same server. Defaults are `process.stdin`, `process.stdout`, and
149
+ `process.stderr`. Start each server exactly once with an open readable input and
150
+ open writable output/diagnostic streams. Output and diagnostics must be separate
151
+ streams. Byte-mode input preserves split UTF-8 sequences; a stream already
152
+ decoded into strings remains responsible for its own decoding.
153
+
154
+ Messages are processed as their newline arrives, without waiting for EOF. A
155
+ final complete JSON record is also accepted when EOF arrives without its final
156
+ newline. LF and CRLF transport endings are accepted. JSON escaping preserves
157
+ embedded line breaks in the decoded payload. Malformed JSON receives `-32700`;
158
+ its complete original line is included in diagnostics. Invalid UTF-8 fails at
159
+ the decoder and closes the transport with the complete affected input chunks
160
+ and pending decoded text in its diagnostic error.
161
+
162
+ Requests execute independently, including overlapping calls to one tool.
163
+ Only complete output writes are ordered, waiting for each Node write callback
164
+ before sending the next record. An error, close, or finish event during a pending
165
+ output or diagnostic write settles that write as failed even when its callback
166
+ never arrives; a later callback cannot settle it again. The application decides
167
+ whether its own data access requires narrower serialization. The adapter adds
168
+ no process-wide queue.
169
+
170
+ Only JSON-RPC messages are written to `output`. Application handlers and their
171
+ dependencies must also reserve stdout for protocol traffic; use stderr or the
172
+ application's diagnostic owner for logs. The adapter does not replace global
173
+ console functions.
174
+
175
+ ## cancel
176
+
177
+ `server.cancel(requestId, reason)` requests cooperative cancellation and returns
178
+ `true` only when an active cancellable handler was signalled. String and numeric
179
+ IDs remain distinct. Unknown, completed, already cancelled, and initialization
180
+ requests return `false`. The same behavior handles client
181
+ `notifications/cancelled` messages. Late results from cancelled work have no
182
+ protocol response, as described by the
183
+ [MCP cancellation contract](https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/cancellation).
184
+
185
+ The handler must pass its signal to its actual I/O or computation and release
186
+ owned resources. Signalling does not prove external work stopped. The adapter
187
+ retains the handler promise until it really settles; a handler that ignores
188
+ cancellation can keep drain and close pending.
189
+
190
+ ## drain
191
+
192
+ `await server.drain()` waits for accepted handlers, diagnostic observers, and
193
+ output write callbacks to settle. It does not stop input or abort requests, so
194
+ newly accepted work can extend the wait. Use `close()` when input must first
195
+ stop. Tool and request errors are delivered as protocol outcomes; transport and
196
+ diagnostic delivery failures are retained by `closed`.
197
+
198
+ ## close
199
+
200
+ `await server.close({cancelPending: false})` stops reading, pauses the input,
201
+ drains accepted work and writes, detaches the adapter's stream listeners, and
202
+ settles `closed`. It is idempotent. Normal input EOF follows this same graceful
203
+ path. Explicit `cancelPending: true` aborts pending work instead; repeated close
204
+ may request cancellation while an earlier graceful close is draining.
205
+
206
+ Stream failures request cancellation, report diagnostics, and reject `closed`
207
+ after owned work settles. An input closed without EOF is a transport failure.
208
+ The adapter does not end or destroy caller streams, terminate the process,
209
+ install process signal handlers, start an application host, or close
210
+ application services. The launcher owns those actions after the adapter drains.
211
+
212
+ ## closed
213
+
214
+ `server.closed` is a promise for actual session cleanup. It resolves after a
215
+ normal close or EOF and rejects with an `AggregateError` when transport or
216
+ diagnostic delivery failed. Observe it even when the initiating caller continues
217
+ other work. Request failures alone do not close the session.
218
+
219
+ ## MCP_PROTOCOL_VERSION
220
+
221
+ `MCP_PROTOCOL_VERSION` is exactly `'2025-11-25'`. Initialization always returns
222
+ that supported version. A client proposing another version must accept this
223
+ profile or disconnect. This is the initialize/initialized STDIO profile;
224
+ support for later discovery-based profiles is not implied. See the normative
225
+ [lifecycle](https://modelcontextprotocol.io/specification/2025-11-25/basic/lifecycle)
226
+ and [STDIO transport](https://modelcontextprotocol.io/specification/2025-11-25/basic/transports).
227
+
228
+ Supported requests are `initialize`, `ping`, `tools/list`, `tools/call`,
229
+ `resources/list`, `resources/read`, and `resources/templates/list` (an empty
230
+ template catalog). Tools and resources capabilities are advertised when their
231
+ catalogs are nonempty. Lists return their complete catalogs without pagination;
232
+ supplying a cursor receives `-32602`. Send `notifications/initialized` after
233
+ the initialization response before ordinary operations.
234
+
235
+ There are no prompts, subscriptions, list-change events, MCP logging requests,
236
+ server-to-client requests, sampling, elicitation, task augmentation, HTTP
237
+ transport, or automatic client configuration. Unknown methods return `-32601`;
238
+ notifications never receive responses. JSON-RPC batches are not part of this
239
+ profile. IDs must be strings or integers and are unique for the session.
240
+
241
+ The result shapes follow the official
242
+ [tool](https://modelcontextprotocol.io/specification/2025-11-25/server/tools)
243
+ and [resource](https://modelcontextprotocol.io/specification/2025-11-25/server/resources)
244
+ contracts. This narrow adapter does not claim every optional MCP capability.
245
+
246
+ ## McpProtocolError
247
+
248
+ `new McpProtocolError(code, message, data)` represents an explicit protocol
249
+ failure. `code` is a JSON-RPC integer, `message` is its complete string, and
250
+ optional `data` is JSON-serializable protocol detail. It extends `Error` and
251
+ exposes `name`, `code`, `message`, and optional `data`.
252
+
253
+ The adapter uses `-32700` for malformed JSON, `-32600` for invalid requests,
254
+ `-32601` for unknown methods, `-32602` for malformed params or unknown tools,
255
+ `-32603` for internal failures, and `-32002` for a missing resource. Its
256
+ `-32000` lifecycle error requests initialization before ordinary operations.
257
+
258
+ ## Evidence boundary
259
+
260
+ `test/mcp-stdio-server.test.mjs` contains focused in-memory stream fixtures for
261
+ these contracts. Authored fixtures and source review do not establish an
262
+ executed client integration or platform run. Run local tests only under the
263
+ applicable explicit testing or selected-output authority.
@@ -1742,6 +1742,14 @@ owner, and the voice control retains its state-specific labels and retry action.
1742
1742
  Desktop speech and the dedicated `voice-transcription.html` component retain their
1743
1743
  existing behavior.
1744
1744
 
1745
+ An already accepted file transcription retains its exact owned runtime
1746
+ operation through the same provider/model's unloaded, busy `recovering` state
1747
+ or correlated pending error. This preserves the actual RPC result or failure;
1748
+ it does not enable another recording. Unload, close, provider/model replacement,
1749
+ transport loss and caller cancellation still invalidate owned work. Native Web
1750
+ Speech microphone capture keeps its independent readiness and final/interim
1751
+ cancellation lifetime. See [native speech recovery](native-speech.md).
1752
+
1745
1753
  On desktop, the Hold to talk control remains disabled unless sticky STT state
1746
1754
  is `ready`, the role is not busy, and microphone capture is available. For an explicitly
1747
1755
  selected STT route, a separate keyboard-operable control presents Start
@@ -2115,7 +2123,16 @@ Unknown provider/runtime diagnostics remain complete in the developer console
2115
2123
  and error events, while controls use generic visible outcomes unless the Error
2116
2124
  owner explicitly marks nonblank copy with `userSafe:true`.
2117
2125
 
2118
- For the default manual mode, if sticky readiness is lost during microphone acquisition, capture, or the STT
2126
+ An already accepted file transcription can continue through the exact same
2127
+ provider/model/runtime operation's unloaded, busy recovery or pending-error
2128
+ state. This uses the real runtime operation ID rather than the component's local
2129
+ counter; another request supplies no continuation authority. New recording and
2130
+ transcription remain readiness-gated, and actual unload, close, replacement,
2131
+ transport loss or cancellation still ends the owned request. The same shared
2132
+ rule applies to an active continuous file-transcription job. Native Web Speech
2133
+ capture retains its separate readiness, stop/final-drain and interim ownership.
2134
+
2135
+ Outside that exact continuing request, if sticky readiness is lost during microphone acquisition, capture, or the STT
2119
2136
  request, the component invalidates the session, aborts the owned request signal,
2120
2137
  releases media, discards late completion, and emits
2121
2138
  `speech-transcription-cancelled`. Its source-local reason is
@@ -3227,9 +3227,17 @@ Validates and canonicalizes the syntax of Ollama model identifiers without grant
3227
3227
 
3228
3228
  ### Public surface
3229
3229
 
3230
- `normalizeOllamaModelIdentifier()`, `isOllamaModelIdentifier()`.
3230
+ `normalizeOllamaModelIdentifier()`, `isOllamaModelIdentifier()`,
3231
+ `sameOllamaModelIdentifier(left, right)`.
3231
3232
 
3232
- Exact exports: `isOllamaModelIdentifier`, `normalizeOllamaModelIdentifier`.
3233
+ Exact exports: `isOllamaModelIdentifier`, `normalizeOllamaModelIdentifier`,
3234
+ `sameOllamaModelIdentifier`.
3235
+
3236
+ The comparison helper applies Ollama's default registry (`registry.ollama.ai`),
3237
+ namespace (`library`) and tag (`latest`) with case-insensitive name comparison.
3238
+ It compares a selected name with resident-model names without rewriting either
3239
+ string or any outbound payload; different explicit hosts, namespaces or tags
3240
+ remain distinct. The existing identifier-validation functions are unchanged.
3233
3241
 
3234
3242
  ### Availability and normalization
3235
3243
 
@@ -55,8 +55,11 @@ runtime layouts.
55
55
  | `arcane-os/core/contracts` | Shared Core protocol, frame, method and diagnostic error contracts. |
56
56
  | `arcane-os/core/classic-source` | Node generator for the canonical classic-script Core client projection. |
57
57
  | `arcane-os/core/runtime` | App-neutral native Core dispatcher and service lifecycle. |
58
+ | `arcane-os/core/development` | App-service composition and document-owned Core transport during source development, independently of local AI. |
58
59
  | `arcane-os/core/stdio` | Framed Node stdio transport with graceful runtime drain. |
59
60
  | `arcane-os/core/host` | Node-only reusable Core host lifecycle with explicitly supplied services. |
61
+ | `arcane-os/codex/app-server` | Explicit native Codex App Server session, complete live turns, server requests and recovery. |
62
+ | `arcane-os/mcp/stdio` | Node MCP STDIO server for app-owned tools and static resources. |
60
63
  | `arcane-os/core/repositories` | Native persistent ArcaneData paths and explicitly selected connected working repositories. |
61
64
  | `arcane-os/core/packaged-web` | Core-owned packaged web listener with a persisted loopback origin and ready/failure events. |
62
65
  | `arcane-os/core/preferences` | Existing preference RPC methods with an application-selected file, ordered writes and owned drain. |
@@ -383,6 +386,13 @@ The remaining data-only subpaths are eight JSON Schemas and package metadata.
383
386
  | `createCoreFacade()` | function | `arcane-os/core/client` | Core browser client | Browser and native WebView JavaScript hosts |
384
387
  | `installCoreClient()` | function | `arcane-os/core/client` | Core browser client | Browser and native WebView JavaScript hosts |
385
388
  | `getInstalledCoreClient()` | function | `arcane-os/core/client` | Core browser client | Browser and native WebView JavaScript hosts |
389
+ | `subscribeCoreClient()` | function | `arcane-os/core/client` | Core browser client | Browser and native WebView JavaScript hosts |
390
+ | `createDevelopmentCore()` | function | `arcane-os/core/development`, `arcane-os` | Core source development | Node on Windows, Linux and macOS; Android requires host adaptation |
391
+ | `createCodexAppServerSession()` | function | `arcane-os/codex/app-server` | Native Codex sessions | Node with an explicitly selected Codex App Server executable |
392
+ | `openCodexAppServerSession()` | function | `arcane-os/codex/app-server` | Native Codex sessions | Node with an explicitly selected Codex App Server executable |
393
+ | `createMcpStdioServer()` | function | `arcane-os/mcp/stdio` | MCP STDIO | Node streams on Windows, Linux and macOS; Android needs host adaptation |
394
+ | `MCP_PROTOCOL_VERSION` | constant | `arcane-os/mcp/stdio` | MCP STDIO | Node |
395
+ | `McpProtocolError` | class | `arcane-os/mcp/stdio` | MCP STDIO | Node |
386
396
  | `createCoreClassicSource()` | function | `arcane-os/core/classic-source` | Core classic client projection | Node; generated source runs in browser and native WebView renderers |
387
397
  | `createCoreRuntime()` | function | `arcane-os/core/runtime` | Native Core runtime | Node on Windows, Linux and macOS native hosts; Android requires host adaptation |
388
398
  | `encodeCoreFrame()` | function | `arcane-os/core/stdio` | Native Core stdio transport | Node |
@@ -417,6 +427,9 @@ The remaining data-only subpaths are eight JSON Schemas and package metadata.
417
427
  | `createLocalImageService()` | function | `arcane-os/core/image` | Core local images | Node Core with selected stable-diffusion.cpp requirements |
418
428
  | `createLocalImageService default export` | function | `arcane-os/core/image` | Core local images | Same native image service factory |
419
429
  | `createSpeechService()` | function | `arcane-os/core/speech` | Core native speech | Node Core with explicitly supplied speech engines |
430
+ | `createWhisperRuntime()` | function | `arcane-os/local-ai/whisper` | Core native speech | Node with a prepared matching helper, runtime, decoder and selected model |
431
+ | `createWhisperRuntime default export` | function | `arcane-os/local-ai/whisper` | Core native speech | Same retained native engine factory |
432
+ | `buildWhisperHelper()` | function | `arcane-os/local-ai/whisper/build` | Core native speech | Node with installed CMake/C++17 toolchain and matching Whisper source/runtime |
420
433
  | `createSpeechService default export` | function | `arcane-os/core/speech` | Core native speech | Same shared speech service factory |
421
434
  | `prepareCoreModelAssets()` | function | `arcane-os/ai/core-model-assets` | Core model assets | Browser with complete Blob/File members and available Core service |
422
435
  | `createModelAssetService()` | function | `arcane-os/core/model-assets` | Core model assets | Node Core with an application-selected working directory |
@@ -4138,6 +4151,11 @@ inspection is distinct from executing them or verifying a browser deployment.
4138
4151
 
4139
4152
  Starts one owned browser development server with exact runtime/app route mappings and a caller-selected bind address.
4140
4153
 
4154
+ Source callers may pass `core`, a handle returned by
4155
+ [`createDevelopmentCore()`](#createdevelopmentcore). The server delegates the
4156
+ Core HTTP routes and closes that owner during shutdown. The existing internal
4157
+ `localAI` handle remains accepted; `core` takes precedence when explicitly supplied.
4158
+
4141
4159
  HTTP and HTTPS serving use published `node-http-server` 10.0.0. The SDK
4142
4160
  selects source routes and supplies generated representations; the module owns
4143
4161
  static-file conditional GET/HEAD handling and response delivery. The SDK
@@ -4658,6 +4676,14 @@ async function usedescribeTargets(...arguments_) {
4658
4676
 
4659
4677
  Starts one owned browser development server for the selected application.
4660
4678
 
4679
+ The selected descriptor's `native.services` composes application Core services
4680
+ independently of local AI. Programmatic `serviceModules:[{module,options}]`
4681
+ replaces that list; `services` adds constructed service definitions and `context`
4682
+ supplies app-owned launch fields. The result adds `core` when composed and retains
4683
+ `localAI` for selected native local AI. Page rendering and the listener do not
4684
+ wait for service readiness. See [source-development Core](core-development.md)
4685
+ for explicit `/arcane-core.js` loading, cancellation and current-state replay.
4686
+
4661
4687
  `http`, `https`, `certPath`, `keyPath`, and `tls` follow the
4662
4688
  [`startDevServer()` transport contract](#startdevserver), alongside `host`, `port`, and
4663
4689
  `httpPort`. By default, `port` selects HTTPS and `httpPort` selects the HTTP `308`
@@ -7062,6 +7088,46 @@ const client = getInstalledCoreClient();
7062
7088
  console.log(client ? client.runtime.current() : 'No SDK client is installed.');
7063
7089
  ```
7064
7090
 
7091
+ ## subscribeCoreClient()
7092
+
7093
+ ### Overview
7094
+
7095
+ Observes the selected global's SDK client installation without creating a
7096
+ transport or delaying rendering. Applications can subscribe before independently
7097
+ loaded development or native bootstrap code installs the client.
7098
+
7099
+ ### Signature and result
7100
+
7101
+ ```text
7102
+ subscribeCoreClient(listener,{global=globalThis,emitCurrent=true,signal}={})
7103
+ ```
7104
+
7105
+ Import from `arcane-os/core/client`. Returns an idempotent unsubscribe function.
7106
+ The listener receives `{client,previousClient,reason,error}` with the exact
7107
+ client and error objects. Reasons are `current`, `installed`, `closed` and
7108
+ `transport-failed`. Current state replays synchronously unless disabled; an
7109
+ already-aborted signal registers nothing. Close or terminal failure releases
7110
+ the prior installation before notification. A reentrant replacement is retained.
7111
+
7112
+ ### Availability and normalization
7113
+
7114
+ **Browser and native WebView JavaScript hosts.** Installation is distinct from
7115
+ Core, service or model readiness. Read `client.runtime.current()` and subscribe
7116
+ to that client's authoritative runtime/service events for those states. This
7117
+ observer starts no service and uses the shared event owner, not polling. See
7118
+ [Core client](core-client.md) and [source-development Core](core-development.md).
7119
+
7120
+ ### Example
7121
+
7122
+ ```javascript
7123
+ import {subscribeCoreClient} from 'arcane-os/core/client';
7124
+
7125
+ const unsubscribe = subscribeCoreClient(function installationChanged({client}) {
7126
+ console.log(client ? client.runtime.current() : 'Core is not installed.');
7127
+ });
7128
+ // The application owner calls unsubscribe() when this observer is retired.
7129
+ ```
7130
+
7065
7131
  ## createCoreClassicSource()
7066
7132
 
7067
7133
  ### Overview
@@ -7102,6 +7168,93 @@ const source = await createCoreClassicSource({
7102
7168
  nativeHost.setDocumentCreatedScript(source);
7103
7169
  ```
7104
7170
 
7171
+ ## createCodexAppServerSession()
7172
+
7173
+ Import from `arcane-os/codex/app-server`. Creates a synchronous session handle
7174
+ without starting a process. `connect()` starts the caller-selected executable
7175
+ and completes native JSONL initialization. The session owns explicit thread
7176
+ creation/resumption, complete native requests, live turn callbacks, exact-turn
7177
+ completion, interruption, real server-request replies and process retirement.
7178
+ Application-owned prompts, approvals and display policy stay with the caller.
7179
+ It neither attaches to desktop chats nor installs or authenticates Codex.
7180
+ See the complete [session options, methods, events, results, errors and recovery
7181
+ contract](codex-app-server.md).
7182
+
7183
+ ## openCodexAppServerSession()
7184
+
7185
+ Import from `arcane-os/codex/app-server`. Creates the same session, awaits
7186
+ `connect()`, and returns it; failed opening disposes the owned process before
7187
+ rejecting. Prefer `createCodexAppServerSession()` when the caller must retain
7188
+ the handle during initialization. Neither factory creates or resumes a thread.
7189
+ See [connection ownership](codex-app-server.md#open-an-explicitly-selected-connection).
7190
+
7191
+ ## createMcpStdioServer()
7192
+
7193
+ Import from `arcane-os/mcp/stdio`. Creates one optional Node STDIO session for
7194
+ application-owned tool and static-resource descriptors. `start()` attaches
7195
+ caller-owned streams; handlers receive complete arguments and return explicit
7196
+ MCP result containers. Requests run independently; protocol output writes are
7197
+ ordered. Normal EOF and `close()` drain accepted work. Cancellation is
7198
+ cooperative, and transport failures remain observable through `closed`.
7199
+ See [options, handlers, methods, errors and supported profile](mcp-stdio.md).
7200
+
7201
+ ## MCP_PROTOCOL_VERSION
7202
+
7203
+ Import from `arcane-os/mcp/stdio`. The supported initialization profile is
7204
+ exactly `'2025-11-25'`. This does not claim later profiles or every optional
7205
+ capability. See [protocol support](mcp-stdio.md#mcp_protocol_version).
7206
+
7207
+ ## McpProtocolError
7208
+
7209
+ Import from `arcane-os/mcp/stdio`. `new McpProtocolError(code,message,data)`
7210
+ represents an explicit JSON-RPC error with complete message and optional data.
7211
+ Ordinary domain tool failures use an explicit `CallToolResult` with `isError`.
7212
+ See [error outcomes](mcp-stdio.md#mcpprotocolerror).
7213
+
7214
+ ## createDevelopmentCore()
7215
+
7216
+ ### Overview
7217
+
7218
+ Composes application-selected Core services and the SDK-owned development HTTP
7219
+ transport independently of local AI. Import from `arcane-os/core/development`
7220
+ or `arcane-os`; ordinary applications normally use `developApplication()` with
7221
+ their descriptor's `native.services`.
7222
+
7223
+ ### Signature and result
7224
+
7225
+ `createDevelopmentCore({appRoot=process.cwd(),application,version,services=[],serviceModules=[],context={},getReplayEvents,signal,onEvent}={})`
7226
+ returns `{ready,handler,current,close}` synchronously. Module default factories
7227
+ receive unchanged `(options,context)`. `ready` reports composed dispatcher
7228
+ startup, not readiness of every service. `current()` returns complete
7229
+ `{state,error,core}`; `handler(request,response)` owns `/rpc`, `/events` and both
7230
+ bootstrap aliases; idempotent `close()` joins accepted service work and output.
7231
+
7232
+ ### Availability and example
7233
+
7234
+ Node on Windows, Linux and macOS. Browser callers use the public Core client.
7235
+ Android needs its host adapter. This adds no cross-process host discovery.
7236
+
7237
+ ```javascript
7238
+ import {createDevelopmentCore, startDevServer} from 'arcane-os';
7239
+
7240
+ const core = createDevelopmentCore({
7241
+ appRoot: process.cwd(),
7242
+ serviceModules: [{module: 'bridge/moon-cheese.mjs', options: {shelf: 'Lunar cheddar'}}]
7243
+ });
7244
+ let server;
7245
+ try {
7246
+ server = await startDevServer({workspaceRoot: process.cwd(), core});
7247
+ await server.closed;
7248
+ } finally {
7249
+ await core.close();
7250
+ }
7251
+ ```
7252
+
7253
+ The [complete guide](core-development.md) defines document cancellation,
7254
+ service-lifetime drain, diagnostics, optional explicit service snapshots and
7255
+ non-blocking page bootstrap. App policy and explicit native process activation
7256
+ remain app-owned.
7257
+
7105
7258
  ## createCoreRuntime()
7106
7259
 
7107
7260
  ### Overview
@@ -7731,7 +7884,7 @@ explicit existing path. The application owns the remote, branch and Core API.
7731
7884
  createRepositoryWorkspace({name,directory,dataRoot,remote,branch,onEvent,run=runProcess}={})
7732
7885
  ```
7733
7886
 
7734
- Returns `{directory,open,status,pull,push,close,drain,dispose}`. Construction does
7887
+ Returns `{directory,open,status,pull,push,write,close,drain,dispose}`. Construction does
7735
7888
  no I/O. Operations accept `{signal}`; a missing or empty destination clones once,
7736
7889
  while existing checkout files, branches, remotes and local changes remain intact.
7737
7890
  Same-directory calls are ordered within the process. Shutdown drains accepted
@@ -7739,6 +7892,14 @@ work and preserves the checkout. No CLI cwd, launch state, preference, snapshot
7739
7892
  cache or existing user-data location changes. See the [complete inputs, results,
7740
7893
  errors and lifecycle](core-repositories.md#one-connected-working-checkout).
7741
7894
 
7895
+ `write({files:[{path,content}],message,signal})` snapshots the exact selected
7896
+ strings, writes those repository-relative files, commits only their literal
7897
+ paths and performs an ordinary non-force push. It retains unrelated staged
7898
+ paths and existing Git filters/hooks. Results and causal failures distinguish
7899
+ confirmed local writes, commits, pushes and uncertain attempted side effects;
7900
+ complete process output is retained. See [the complete writer outcome
7901
+ contract](core-repositories.md#write-commit-and-push-selected-text).
7902
+
7742
7903
  ```javascript
7743
7904
  import {createRepositoryWorkspace} from 'arcane-os/core/repositories';
7744
7905
 
@@ -7955,6 +8116,95 @@ selected model and native engine.
7955
8116
  The default export from `arcane-os/core/image` is the same
7956
8117
  `createLocalImageService` factory and lifecycle described above.
7957
8118
 
8119
+ ## createWhisperRuntime()
8120
+
8121
+ ### Overview
8122
+
8123
+ Creates one retained native Whisper engine from a prepared runtime or a lazy
8124
+ preparation function. Construction starts no helper, model load or inference.
8125
+ Import the named factory from `arcane-os/local-ai/whisper`.
8126
+
8127
+ ### Syntax and inputs
8128
+
8129
+ ```javascript
8130
+ createWhisperRuntime({runtime, prepare, modelId, temporaryDirectory, onEvent}={})
8131
+ ```
8132
+
8133
+ Supply `runtime` or `prepare({signal,onEvent})`, and an application-owned
8134
+ `temporaryDirectory`. `modelId` selects a model from the prepared runtime;
8135
+ omitting it uses the runtime's selected model. `onEvent` receives complete
8136
+ native preparation/process diagnostics. The runtime describes matching helper,
8137
+ Whisper backend libraries, decoder and model files; see the complete
8138
+ [native Whisper preparation contract](local-whisper.md).
8139
+
8140
+ ### Returned members and lifetime
8141
+
8142
+ | Member | Contract |
8143
+ | --- | --- |
8144
+ | `current()` | Returns `providerId`, `modelId`, `state`, `loaded`, `busy`, `requestId`, `requestedBackend`, `observedBackend`, `backendEvidence` and `error`. State reports actual helper/model readiness. |
8145
+ | `subscribe(listener,{replay=true,signal}={})` | Observes the current snapshot immediately by default, then each state change; returns the unsubscribe closure. |
8146
+ | `load({modelId,signal}={})` | Prepares the selected runtime if necessary and retains its initialized helper/model. Same-model loading is shared; replacement remains explicit. |
8147
+ | `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. |
8148
+ | `unload()` | Cancels owned loading/transcription, joins decoder/helper work and releases the retained model before resolving its state snapshot. |
8149
+ | `close()` | Performs joined unload and disposes state observation. Repeated calls share the closing result. |
8150
+
8151
+ The decoder consumes the complete submitted media into native 16 kHz mono PCM
8152
+ without changing the caller's recording. The result preserves the engine's
8153
+ complete text and segment order. Temporary operation files remain until decoder
8154
+ and inference settle. The engine accepts one active recording; overlapping
8155
+ requests report `WHISPER_BUSY`, and unavailable/closing/model failures retain
8156
+ their explicit diagnostics. Caller cancellation retains its supplied reason.
8157
+
8158
+ With `backend: 'auto'`, an eligible accelerated native failure may drain that
8159
+ helper and retry the same complete recording and selected model once on the
8160
+ prepared CPU backend. Its snapshot is `recovering`, unloaded and busy during
8161
+ recovery. Cancellation, malformed input, observer/protocol failure and explicit
8162
+ backend selection do not cause that retry. `requestId` is the actual optional
8163
+ transport correlation, never a generated replacement or payload field; it
8164
+ remains through terminal error settlement and clears or changes on the next
8165
+ request/load/unload/close. The [shared speech adapter](native-speech.md) preserves
8166
+ only that continuing owned request, while new requests require actual readiness.
8167
+
8168
+ ### Availability and example
8169
+
8170
+ Node on Windows, Linux and macOS with matching native resources; Android needs
8171
+ the corresponding host/process adaptation. Prepared files establish resource
8172
+ availability, not execution on every platform. This uses an already prepared
8173
+ runtime and the application's own temporary directory:
8174
+
8175
+ ```javascript
8176
+ import {createWhisperRuntime} from 'arcane-os/local-ai/whisper';
8177
+ import {createSpeechService} from 'arcane-os/core/speech';
8178
+
8179
+ const stt = createWhisperRuntime({runtime, modelId: 'whisper-small', temporaryDirectory});
8180
+ const speech = createSpeechService({stt});
8181
+ // Register speech with the existing Core owner; it owns start/drain/dispose.
8182
+ ```
8183
+
8184
+ ## createWhisperRuntime default export
8185
+
8186
+ The default binding from `arcane-os/local-ai/whisper` is identical to the named
8187
+ [`createWhisperRuntime()`](#createwhisperruntime) factory and has the same inputs,
8188
+ members, readiness, cancellation and native-resource requirements.
8189
+
8190
+ ## buildWhisperHelper()
8191
+
8192
+ `buildWhisperHelper({runtime,outputRoot,cmake='cmake',generator,cmakeArgs=[],env,
8193
+ signal,onEvent}={})` from `arcane-os/local-ai/whisper/build` builds the first-party
8194
+ persistent helper using the installed native toolchain. `runtime.directory`
8195
+ selects the prepared libraries and `runtime.sourceDirectory` selects their
8196
+ matching headers; `outputRoot` owns build/install output. Optional CMake
8197
+ arguments, generator and environment remain caller-selected.
8198
+
8199
+ The operation runs configure, Release build and install in order. It returns
8200
+ `{platform,architecture,root,executable,runtimeDirectory,diagnostics}`, with
8201
+ complete `configure`, `compile` and `install` process results. Cancellation and
8202
+ process errors use the existing owned process lifetime. It installs neither a
8203
+ toolchain nor a model and performs no inference. Windows helper installation
8204
+ includes the selected compiler's redistributable runtime closure. Use a
8205
+ toolchain and prepared runtime matching the target platform/architecture; a
8206
+ source package alone is not a compiled helper. See the [helper build procedure](local-whisper.md).
8207
+
7958
8208
  ## createSpeechService()
7959
8209
 
7960
8210
  ### Overview and usage