Capability first · transport second

Protocol and host architecture

This is the deep reference behind the compact availability notes elsewhere. Application developers should start with the availability and normalization guide, call one documented API, and treat the protocols below as implementation detail.

SDK 0.97.1Runtime 0.8.12Protocol arcane/1
On this page

This is the deep reference behind the compact availability notes elsewhere. Application developers should start with the availability and normalization guide, call one documented API, and treat the protocols below as implementation detail.

Layer map

Code
application code
  |-- Node SDK API ----------------- arcane-cli-events/1 + SDK results
  |-- EventManager ----------------- synchronous bus + arcane-event-stack/1
  |-- browser-local AI ------------- WebGPU/WASM/Workers/DBOPFS; no Core grant
  `-- globalThis.Arcane
        |-- development host -------- development HTTP bridge
        |-- Microsoft NT native ----- WebView2 host bridge
        |-- Linux native ------------ WebKitGTK host bridge
        `-- Android native ---------- Android WebView message bridge
                                       |
                                       `-- Arcane Core/provider boundary
                                             |-- platform services
                                             |-- ArcaneOllama loopback service
                                             `-- explicitly selected cloud APIs

Each downward boundary can add authority and platform capability. None can be inferred merely from a function existing in shared JavaScript.

SDK package and CLI protocols

The npm package API is ordinary Node.js ESM. The CLI and programmatic toolchain share one headless operation implementation. Long-running operations accept before blocking work, own their task, stream structured events with bounded backpressure, emit progress or heartbeats, support cancellation where the underlying operation can do so safely, and surface failure through rejection or a nonzero CLI status.

Machine output uses arcane-cli-events/1. Native planning and providers use:

  • arcane-target-adapter/1 for target adapters;
  • arcane-native-build-plan/1 for native plans with complete selected release inputs;
  • arcane-native-builder/1 for injected native builders;
  • arcane-integrated-toolchain/1 for the fixed integrated shared/Core provider.

These protocols normalize orchestration and results. They do not normalize a Windows EXE, Linux DEB, Android APK, and portable directory into the same artifact kind.

Portable CLI operations load arcane-os/native/portable-provider; Windows x64 loads arcane-os/native/windows-provider, both from the installed SDK by default. The Windows provider assembles the matching precompiled WebView2/SEA host release asset. Explicit --arcane-root selection retains the checkout route; Linux and Android still need their platform provider/toolchain. arcane-os/core/host composes the reusable native runtime with explicitly supplied app services. These Node entries remain separate from browser imports; portable itself remains a non-executable payload. The synchronous classic facade and browser-safe client retain their separate public entries. Integrated shared checks keep their existing Arcane OS development provider.

App-scoped managed browser imports have three supported control-plane entrypoints. The CLI uses arcane import-map; Node callers use executeOperation('import-map', options) or createToolchain(defaults).importMap(options). These are three routes to the same app-scoped operation, not three import-map formats. Each route uses the application document selection shared by inspection, development and packaging, and writes one canonical map to the artifact plus every selected document. Each selected page retains the existing path-relative base requirement. Included component fragments remain complete package files and keep their existing component processing. Public lowercase runtime subpaths resolve directly to canonical runtime-module namespaces from both Node package exports and managed browser keys. These include arcane-os/preference-store, arcane-os/speech-playback, arcane-os/ai-preference-tuple, arcane-os/ai-preference-runtime, arcane-os/ai-provider-runtime, arcane-os/ai-runtime-state, arcane-os/model-definition, arcane-os/conversation-timebox, arcane-os/conversation-action-items, arcane-os/conversation-closing-report, and arcane-os/chat-records. Physical, nested and virtual layouts also retain the established arcane/PreferenceStore and arcane/SpeechPlayback browser names. Direct installed-package root apps use the package-namespaced imports. The additional portable arcane-os/speech-text subpath owns shared speech-input cleanup and has the same managed browser key. arcane-os/mail remains the existing Mail aggregation. There is no exported importMapApplication() function, generateImportMap() function, or arcane-os/import-map package subpath. Explicit host document lists use the separate root-exported generateDocumentImportMaps() API described below; they do not enter app discovery.

The application dependency boundary is conditional. Only an application that actually consumes Arcane declares arcane-os in its own package.json and resolves it through that project's normal install into its own node_modules. During development the declaration tracks latest; the lockfile and installed package record the exact selected version. An explicitly selected production build may declare an exact version. An application that does not currently consume Arcane adds no arcane-os dependency solely because of this rule; the user or governing task may direct that application to adopt Arcane later, at which point the same app-owned package, install, projection, and artifact boundary applies. An Arcane consumer never depends on a global install, link or symlink, or another Arcane OS/SDK checkout. The explicit --sdk-runtime-source checkout is a temporary development server input only: its path and mutable authority are not persisted in application package metadata, lockfiles, managed import maps, or dist. Distribution instead uses the app's installed package and materializes the complete required SDK runtime, browser-runtime, and managed-import-map closure inside that app's own artifact. No application polls for SDK changes.

Application document selection

An authored schema-2 arcane-app.json may select the HTML pages that receive application-document processing through package.documents:

json
{
  "package": {
    "entry": "index.html",
    "include": ["index.html", "pages", "originals", "modules", "css"],
    "documents": ["index.html", "pages/viewer.html"]
  }
}

This is a package excerpt. Each documents member is a literal app-root-relative .html or .htm file path, not a directory or glob. Every selected document must already be present in the app's included, non-excluded files. The entry is always selected first, followed by the other selected paths in deterministic lexical order without duplicates. An empty list selects only the entry. The authored documents declaration remains as supplied, including [].

Omitting documents preserves automatic discovery: the SDK selects the entry and included secondary HTML pages with an active <base>. A meta[name="arcane-app-id"] marker does not select a page. Selected pages retain the existing requirement for one path-correct active base.

The schema-1 arcane-package.json field is documents at the top level. Descriptor projection and synthesis preserve an omitted property as omitted and an authored empty list as []. Package-only applications may author that schema-1 field directly. Ordinary inspect, import-map, dev and package operations use the same declaration; there is no document-selection CLI flag.

Document selection controls application-document scanning, base handling, managed import maps and PWA injection. It does not change include/exclude, serving, package membership or offline resource selection. With an explicit list, other included HTML documents retain their exact content, even when a selected page references them through an iframe. They remain available as included resources without receiving application-document processing. Component processing and shared runtime-resource traversal retain their existing behavior. Offline inclusion still follows the app's PWA resource selection.

Native-only application resources

An application's schema-2 arcane-app.json can explicitly select server modules and their complete app-owned source closure without adding them to its browser or PWA output:

json
{
  "package": {
    "include": ["index.html", "modules", "css"],
    "nativeResources": {
      "include": ["server", "src", "package.json"],
      "exclude": ["server/drafts"]
    }
  }
}

This is a package excerpt. Each path is literal and relative to the application root; a directory includes its descendants. The app selects the full required closure explicitly. The SDK does not crawl imports to discover server source, filter it by extension, install app dependencies or execute a service. nativeResources.include is required and may be empty; exclude defaults to []. Descriptor projection writes the same record at the top level of schema-1 arcane-package.json, and package-only apps may author it there directly. Omission remains omission. The native registry descriptor is unchanged.

packageApp({target}) and inspectApp({target}) default to 'browser'. A nonbrowser SDK target adds these resources to the ordinary app/shared selection. Native exclusions apply only to the additional selection, not the browser selection. A file selected by both is emitted once through its ordinary browser processing. Files selected only as native resources retain their complete original content, including imports, HTML, line endings and data. They are copied after browser document discovery, module projection, asset rewriting and PWA processing, and appear in the complete release inventory. A collision with generated output reports an error instead of replacing that output.

Target and module representation are independent: moduleFormat:'native' controls browser module/script packaging and does not select native resources. Nonbrowser targets use effective browserPwa:false; ordinary browser calls keep their configured PWA behavior. An app adapter receives target and effective browserPwa in its existing buildArcanePackage() context. Its copyBase() copies ordinary browser/shared files; the packager appends native-only files after the adapter and browser transformations finish.

A nonbrowser target with a nonempty native include list writes its app input to dist/.native/<target>/<app-id>, even when the saved browser outputDirectory is custom. This keeps an existing browser/PWA output separate. An explicit low-level outputDirectory override takes precedence; it must name the dedicated output the caller intends to replace. Omitted or empty native resources preserve the existing output rules. inspectApp() reports the effective output and complete selected files without writing them.

Paired native buildApplication() applies the same selected target and output resolution to the selected app and every explicitly bundled app. Each app keeps its own declaration; the provider receives actual release roots and complete release records. Native artifact outputRoot remains a different directory. Use verifyApp({workspaceRoot,appId,outputDirectory:release.output}) to inspect that emitted app release; verification does not launch its services or host. See the portable service composition guide.

App-selected package output

For browser selection, an app may set "outputDirectory": "ai" in its schema-1 arcane-package.json. This selects the final directory relative to the workspace: <workspace>/ai, not <workspace>/ai/<app-id>. Omitting the field retains dist/<app-id>. The root arcane-packager.json configuration and simple scaffold defaults remain unchanged. The destination is local to arcane-package.json, including apps with a schema-2 arcane-app.json. Descriptor refresh preserves it; no matching descriptor property or identity change is required.

This field controls the destination of the complete packaged release. It does not relocate application source, rename the app, change its identity or OPFS scope, or alter the package's internal file paths. Source development continues to serve the same source tree. There is no migration of existing source, saved data, or previously generated output.

The selected directory is wholly replaced through the packager's staged replacement operation, so it must be dedicated to that release. The prior contents remain until the replacement is ready; this is not an in-place merge. Changing the configured destination leaves the former output directory and unrelated siblings untouched. Output that would replace the app's source root, selected inputs, or required control files fails with ARCANE_PACKAGE_INVALID before output writes rather than overwriting those inputs. If an included source directory contains the output directory, use the existing exclude selection to keep that generated subtree outside the package inputs.

Inspection and dry runs report this selected destination. Packaging, verification, and arcane run --target browser use the same release root. The normal bundle destination is beside that release directory as <app-id>-<version>.arcane-app.tar.gz; an explicit artifact path retains its existing meaning. Native packaging supplies the selected release root to the native provider, while native executable outputRoot remains a separate target-output setting. Selected app package directories and native artifact output must remain separate; a conflicting native assembly fails before it packages any app. App launch paths, resource URLs, and enabled PWA representations retain their package-relative layout.

The native-only resource selection above adds a separate default native input directory when its include list is nonempty. An explicit low-level output override still selects the exact directory to replace.

Native-module package output

The public packageApp operation accepts moduleFormat:'native'. Omission, or 'import-map', retains ordinary browser output. Optional operation-level outputDirectory overrides only this invocation's destination; verifyApp accepts that same override. Configuration, source layout, app identity, storage scope and previously generated sibling outputs remain unchanged.

This module format does not select a native target. Use the separate target option for native-only resources.

The native transformation runs in the selected package stage after application adapter work, before the final inventory and any enabled PWA artifacts. It uses the existing selected document/runtime records and shared lexical/HTML owners. Each selected document supplies its saved maps and authored base. Native mode uses the static layout with map declarations before executable scripts; it does not reproduce late-map insertion or its already-resolved-module timing rules. Exact keys, longest matching prefixes and most-specific importer scopes resolve to package URLs, including URL aliases used by direct installed-SDK layouts. Static imports, re-exports, string-literal dynamic imports and string-literal import.meta.resolve() become native relative references. Query strings and fragments retain their meaning. Equivalent graphs share output modules; documents whose maps resolve differently receive distinct context files beside the original module, including when their graphs contain cycles.

Selected document import-map elements are consumed in the output only. Executable inline document scripts move into complete external files while their script elements retain position, type and effective scheduling. The packager removes ineffective async/defer attributes only from formerly inline classic scripts, whose external form otherwise would activate those attributes. Module scripts and already-external document scripts retain their attributes. Imported component scripts use one canonical classic wrapper, with an explicit data-arcane-packaged-script marker consumed by HTMLImport. That path loads the external script directly and retains per-instance host binding, ordered asynchronous completion, cancellation, teardown and ready/error events. Ordinary browser components keep their existing inline path. Application documents not selected for document processing remain unchanged unless they are explicitly loaded as components.

The selected payload is preserved in full, including runtime files, assets, workers and licenses. Graph processing follows each document's executable and resource references, including literal URLs to selected shared component HTML. It does not assign an unused shared file to another document's map. Unreferenced selected files retain their complete content. Referenced scripts, component HTML and stylesheets are processed at their existing owners; resource URLs are rebased for deployment under a directory such as app/. The operation adds generated scripts to the complete release inventory. It does not bundle, prune, minify or rewrite application prose, data or saved history. The existing stage replacement preserves prior output if packaging fails or is cancelled.

This is a static module/script packaging capability, not a replacement browser or a runtime import-map interpreter. Computed import expressions remain authored; applications supply native-resolvable URLs for runtime-selected imports. It does not convert event-handler attributes, dynamically evaluated source, or provider-created executable blobs. An inline document script beneath an authored remote effective base cannot address a package-local generated file; native mode reports that incompatible transformation and preserves the original output. Already-external scripts retain their remote-base behavior. Remote modules/providers and WebAssembly still depend on their actual host capabilities. Resolving package references is distinct from executing the complete application on Chromium or another host.

For an extension, select browserPwa:false for that artifact and place the complete SDK-produced app beneath the app-owned extension root. The application owns its manifest, toolbar/background launcher and permissions. Ordinary browser PWA configuration and generated browser output remain independently available. Before native-module processing, the stage removes active data-arcane-pwa scripts and manifest relationships targeting the package's arcane.webmanifest from the existing selected PWA document set. Authored bases resolve those links; unrelated manifest URLs, other link relationships, comments, templates and other inactive HTML retain their content. Source documents and saved PWA settings remain unchanged; the operation does not sweep every packaged HTML file.

Installed-package browser routes

An external application can serve and package its installed SDK directly, without creating a top-level arcane/ directory or requiring arcane.lock.json. Select installed-v1 through these four ordered routes in the existing schema-1 arcane-packager.json; no additional layout field is needed:

json
{
  "schemaVersion": 1,
  "appsRoot": ".",
  "distRoot": "dist",
  "sharedPayloads": {
    "browser-runtime": [
      {
        "source": "node_modules/arcane-os/runtime/arcane",
        "destination": "node_modules/arcane-os/runtime/arcane",
        "include": ["components", "css", "entities", "img", "modules"],
        "exclude": []
      },
      {
        "source": "node_modules/arcane-os/browser-runtime",
        "destination": "node_modules/arcane-os/browser-runtime",
        "include": ["."],
        "exclude": []
      },
      {
        "source": "node_modules/arcane-os/runtime/strong-type",
        "destination": "node_modules/arcane-os/runtime/strong-type",
        "include": ["."],
        "exclude": []
      },
      {
        "source": "node_modules/arcane-os",
        "destination": "node_modules/arcane-os",
        "include": ["LICENSE", "COMMERCIAL-LICENSE.md", "NOTICE"],
        "exclude": []
      }
    ]
  }
}

The first include list also accepts a final security entry. An npm alias such as arcane-sdk substitutes node_modules/arcane-sdk for the package prefix in all four source and destination paths. Package selection and SDK version come from the installed dependency, not a generated runtime lock.

arcane import-map, arcane dev, packaging, and generated PWA inventories use the same actual npm destinations. For example, arcane-os/modules/HTMLImport.js resolves to ./node_modules/arcane-os/runtime/arcane/modules/HTMLImport.js, and arcane-os/event-manager resolves to ./node_modules/arcane-os/browser-runtime/event-manager.mjs. A normal static server can serve those files directly. Source serving and map generation read installed files without materializing them into the workspace. A selected portable package copies its complete selected resources inside the artifact, preserving the same browser URLs and saved managed import maps.

Existing physical-v1 configurations whose first source is arcane remain supported. The explicit materializer below still refreshes those projections, and an explicitly selected multi-app scaffold retains its physical layout. The earlier installed routes with virtual /arcane destinations also remain supported. Selecting installed-v1 does not delete any preexisting workspace files. The separate host-document generateDocumentImportMaps() API continues to accept an already materialized runtime; its input contract is unchanged.

Standalone root application

Each standalone app's root is its repository root. appsRoot: "." selects that layout: arcane-app.json, arcane-package.json, entry, and app-owned files occupy the workspace root. The declared application ID remains unchanged. appsRoot: "apps" continues to discover apps/<id> and supports the existing integrated and multi-app layouts. Entries and include/exclude paths remain relative to the application.

SDK-managed root HTML uses <base href="./">; nested navigable documents use their actual depth back to the workspace. Managed bare imports remain the public interface; their targets follow the selected npm routes. arcane import-map generates enabled static PWA records at the app root, so normal static hosting needs no SDK request handler or runtime copy. Managed refresh, development serving, inspection, dry run and packaging add no nested apps/<id>/ navigation pages, redirects, or duplicate PWA worker/inventory files. There is no output-retention switch for those retired records.

An authored source-host index.html suppresses the root app-entry redirect and generated PWA navigation alias even when that file is outside the application's include or offline selection. Development serves that host index at /. An index outside the selected app mapping uses an unchanged exact-root response; /index.html and other resource URLs retain their existing selection rules. A selected index follows the existing document classification. Root presence adds no page, assets, managed maps, or PWA bootstrap to the application. With no host index, development retains the app-entry redirect. In a package, only a selected authored output-root index suppresses the alias; an absent or excluded root retains the generated launcher and app-entry alias. Selected records that supply an authored output-root index add the optional generated release field app.rootDocument: "./index.html". Preview serves that document at /; absence retains the existing 302 to app.start or the older app.entry fallback with the request query preserved. This is a release fact, not an app setting or HTML content inference, and app.start remains unchanged. The worker can answer a directory navigation from an index only when that index already belongs to its current offline selection and has a cached response; otherwise the unchanged request remains host-owned. See authored host roots.

Application identity, stored data, the existing default installation ID and an authored pwa.manifest.id remain unchanged. Authored resources continue to follow the app's include/exclude selection; generation does not delete existing files. Published SDK internals stay package-owned under node_modules. Generated and offline app files are committed, and GitHub Actions consume those committed files. See root generated output.

Direct-installed root maps expose arcane-os/modules/<filename> and arcane-os/entities/<filename> (including extensions), plus the existing focused lowercase package exports. They omit the old arcane/* and ./arcane/* keys. Real installed runtime/arcane/ directories are unchanged. Physical, nested and virtual layouts retain their established aliases and also support the package-namespaced module/entity keys. Relative module and component URLs still resolve from the actual selected file, not a reexport wrapper.

New root apps declare the SDK in dependencies because serving their npm resources requires the installed package at runtime. Root init promotes an existing SDK development declaration while preserving other dependencies. Existing runtime or optional declarations retain their classification across layouts; the default nested scaffold continues to use its development dependency.

Installed SDK runtime materialization

materializeInstalledSdkRuntime() is the Node entrypoint for refreshing one external workspace's checked-in arcane/ projection from its one declared and installed SDK dependency. Import the function through the dependency key that the workspace actually declares: arcane-os for the canonical package name or, for example, arcane-sdk for the development alias npm:arcane-os@latest. The lockfile and installed package retain the exact resolved version. No application- or Arcane OS-local copier owns this operation.

The operation resolves that declaration while holding the shared workspace-operation lock, copies the complete runtime and browser-runtime content, and returns the installed-package location plus the materialized workspace runtime paths. After the complete runtime replacement, it writes or replaces semantic arcane.lock.json metadata from the actual installed dependency name, package name, package version, and alias source.

Materialization writes a complete staging tree before the commit boundary, moves any prior runtime to an owned backup, installs the new tree, and restores the prior tree if commit fails. Cancellation remains effective through the last pre-commit check. Successful whole-tree replacement removes paths absent from the selected runtime content. The lock document describes the selected installation and projected roots; it is not a content-identity or admission record.

Both runtime materializers accept explicit browserDecisions: true to acquire the upstream bundled Transformers.js 4.3.0 entry, matching ONNX Runtime 1.31.0-dev.20260914-8d85527a0 asyncify module/WASM, and license files into arcane/sdk/ai/decisions-runtime/ before replacement. Default false performs no optional acquisition. The complete selected files remain unchanged; workspace.decisions.started/workspace.decisions.progress count completed files. Cancellation, HTTP failures and observer failures join acquisition cleanup and preserve the previous projection. Retain the option on each refresh that needs the closure. The optional browserDecisions result describes its directory, versions and filenames; installed materialization nests it under workspaceRuntime. Model/tokenizer data remain upstream and cache-owned. See extension-local decisions for the separate runtime.local: true browser selection.

upgradeApplication() and arcane upgrade run the application's ordinary npm upgrade command. They do not independently reconcile the installed SDK projection, semantic Arcane lock, or managed import map.

Central events and time-travel data

arcane-os/event-manager is host-neutral JavaScript. Its live event path is synchronous, in-process event-pubsub; it does not select WebView2, WebKitGTK, Android, HTTP, Core, or a kernel boundary. Optional history uses the arcane-event-stack/1 data format. That protocol names a diagnostic document, not a network channel and not the arcane/1 Core RPC protocol.

Live listeners receive the original arguments. Recording separately snapshots complete payloads and metadata while redacting credentials and explicitly protected private fields. Exported stacks can be moved between Node and browser hosts because parseEventStack() rejects genuinely malformed data before playback. Review mode emits records, events mode deliberately re-emits recorded event types, and neither mode restores external host side effects or DOM state.

Event-stack structure and privacy boundary

Each document carries protocol, sessionId, createdAt, and ordered events. Each record repeats the protocol/session and carries sequence, timing, nesting/causation, source/category, payload, metadata, status, and failure evidence. Recording retains the complete selected session until the caller clears it or disables recording. Import rejects genuinely malformed or unsafe structure and grants no Core capability or provider authority.

See EventManager and time-travel review for the callable surface, DOM privacy defaults, playback modes, and recovery behavior.

Explicit host documents

generateDocumentImportMaps() accepts one document root, an existing materialized runtime root, and explicit document-root-relative paths. It inventories the runtime and constructs the shared browser map once per call, then resolves each document's map against its first href-bearing base or its own URL. An absolute or root-relative base, or a relative base traversing above the document root, requires the caller's directory deploymentUrl; target-only bases leave URL resolution unchanged.

The operation preserves authored content, resource URLs, custom import maps, and script order, updating only SDK-managed inline map blocks. It prepares all documents before sequential writes and reports import-map.documents.started, import-map.write.progress, and import-map.documents.completed. Cancellation can leave completed document writes in place. Successful writes remain successful when event delivery fails, with a degraded-delivery record in the return value. Runtime materialization, serving, and writer coordination remain with the caller. See the API guide for the complete inputs, results, errors, and example. The app-scoped discovery and map-artifact lifecycle below remains separate.

Browser runtime delivery

External and modern integrated workspaces use a browser-standard import map. Each selected app owns modules/arcane.importmap.json relative to its app root: the repository root for a standalone app, or apps/<id>/ for an explicitly nested app. The exact canonical JSON is also embedded in its HTML entry as a managed <script type="importmap" data-arcane-import-map>. The map follows <base> and precedes module scripts, classic scripts, and module preloads, so application code can use stable named imports such as:

javascript
import ollama from 'arcane-os/modules/Ollama.js';

The supported physical-v1 tree lives entirely beneath arcane/. It projects the complete canonical runtime and browser runtime selected by the installed SDK package. Runtime dependencies stay under arcane/dependencies/; the SDK event and browser-AI closure stays under arcane/sdk/. This URL-key separation prevents runtime and SDK dependency versions from aliasing one another.

The physical workspace route keeps its established route count and order. The external route includes components, css, dependencies, entities, img, modules, and sdk, followed by optional trailing security when that directory exists; its external license route remains second. The modern integrated physical route uses the same ordered include list in its one route. Omitting only that final optional entry is compatible. Removing, reordering, or renaming any preceding entry changes the physical contract.

The map derives its complete entries from the selected runtime graph; application source imports do not select a fixed entry count. The operation result reports imports, optional dependency scopes, documentCount, and excludedModules; reached-file traversal remains internal. The managed graph exposes arcane-os/event-manager, arcane-os/ai/browser-wasm, arcane-os/ai/browser-speech, arcane-os/preference-store, and arcane-os/speech-playback plus arcane-os/speech-text; dependency compatibility mappings are added when runtime or SDK root traversal observes them.

The focused physical targets remain stable when their bindings are reached:

Browser specifier Physical target
arcane-os/event-manager ./arcane/sdk/event-manager.mjs
arcane-os/websocket-client ./arcane/sdk/websocket-client.mjs
ws-share ./arcane/sdk/dependencies/ws-share/WS.js
arcane-os/ai/browser-wasm ./arcane/sdk/ai/browser-wasm.mjs
arcane-os/ai/browser-speech ./arcane/sdk/ai/browser-speech.mjs
arcane-os/speech-text ./arcane/sdk/speech-text.mjs
arcane-os/preference-store ./arcane/modules/PreferenceStore.js
arcane-os/speech-playback ./arcane/modules/SpeechPlayback.js
event-pubsub ./arcane/sdk/dependencies/event-pubsub/index.js
strong-type ./arcane/dependencies/strong-type/index.js
./node_modules/strong-type/index.js ./arcane/dependencies/strong-type/index.js

When the selected runtime contains event-pubsub's own dependency projection, the ./arcane/sdk/dependencies/event-pubsub/ scope maps strong-type to ./arcane/sdk/dependencies/event-pubsub/dependencies/strong-type/index.js. This keeps its published 2.0.0 selection separate from the global SDK 2.0.1 binding. Scope keys and targets follow installed-package and document-base translation together. Older inventories without that nested projection retain their existing map shape. Internal managed test contexts accept and preserve optional scopes alongside imports; their loader selects the most-specific matching importer scope before the global map.

Direct installed routes translate these targets to their actual npm destinations, including aliases. There is no arcane-os package-root mapping or catch-all arcane/ prefix. Classic scripts, workers, stylesheets, and other non-ESM assets use their documented URL or host loading contract rather than invented package bindings. Development serves the selected app plus the complete selected tree. Packaging copies the same map, app entry, and physical content into the selected package output, defaulting to dist/<id>; physical targets stay beneath the selected arcane/ routes, while direct installed targets retain node_modules/<dependency>/... inside the output. The two lowercase static runtime package specifiers above and the shared speech-text helper are exact npm package exports, so Node and managed-browser source can share those specifiers without data URLs, copied modules, or a consumer-owned loader.

generateImportMap() is an internal toolchain operation, not a package export. The shared app-document owner derives the configured entry plus the deterministic admitted .html/.htm inventory from the descriptor's package include/exclude rules. import-map, development, and packaging all reuse that discovery. Import-map generation prepares the complete selection, then writes the artifact and the same managed JSON into each selected document in order. The result reports artifactPath, entryPath, documentPaths, documentCount, the complete imports record, and committed. documentPaths lists the configured entry first, followed by the other selected documents. The public CLI keeps its existing two-option command; callers never maintain a separate page list.

The managed import map derives its complete bindings from the selected runtime files. Enabled PWA delivery publishes its resource inventory in arcane-offline.json; packaging records its complete file inventory in ARCANE_APP_RELEASE.json. There is no generated ARCANE_RUNTIME_PROJECTION.json endpoint in the current serving or packaging contract.

External validateWorkspace() results also expose sdkInstallation with exactly dependencyName, packageSource, canonicalPackageRoot, packageName, packageVersion, runtimeRoot, and browserRuntimeRoot. A workspace may use the canonical dependency name or one npm alias such as npm:arcane-os@latest; the installed package manifest identifies arcane-os and supplies its actual exact version. The resolver requires one declaration and a direct physical installation with runtime and browser-runtime directories. The application's normal npm declaration and lockfile own version selection.

For external workspaces, arcane dev serves the selected direct npm, virtual installed or physical routes alongside the selected application. Integrated workspaces retain their configured physical routes. The explicit live-source development mapping remains a separate development path and does not rewrite the installed projection.

The imported module can be pure browser logic, standard-Web-API logic, or a client of globalThis.Arcane. Import-map resolution is not a new Arcane wire protocol, Core capability, network authority, or provider fallback. Import transport and host RPC remain separate layers.

Refresh lifecycle and managed-file commit behavior

Root scaffolding reports the map as pending until its SDK dependency is installed; root init with an installed SDK and explicit nested scaffolds generate it. dev refreshes once before binding. import-map refreshes every admitted document explicitly. package, browser build, and paired native packaging consume the saved source and managed maps. Refresh maps through import-map or ordinary dev startup when needed. Packaging does not run tests or checks automatically. Explicit test and check operations read the existing map without rewriting it; verify, bundle, and browser run do not refresh. Dry-run packaging/build validates an existing map without rewriting it, and import-map itself has no supported dry-run.

Generation prepares the artifact and every selected HTML document, checks their destinations, then writes the artifact followed by the documents. Cancellation or a write failure can leave completed writes in place. Success reports committed: true and the selected document paths. Event-delivery failure after successful writes is returned as degraded delivery; it does not undo those writes. There is no all-file rollback or durable crash journal.

When tests are explicitly selected, external app tests receive an arcane-test-import-map/1 context for that app's managed map. Each isolated Node child uses the existing loader for managed names and URL-like compatibility keys. Materialized modules such as arcane/TimeGuard and arcane/DBOPFS resolve their canonical arcane-os/event-manager self-import through this managed map; raw Node import of projected arcane/** files is not a separate public resolution contract. Unmapped arcane/* and #arcane/* names fail ARCANE_IMPORT_MAP_UNRESOLVED; ordinary Node exports and test runs without an app context retain their existing resolution.

No app watches, polls, downloads, or self-updates this map. An active operation's heartbeat is event telemetry only and never regenerates browser state.

Portable AI provider runtime

Application code should select a normalized role, not an internal protocol. The exported getAIProviderRuntime() singleton comes from the selected runtime and owns independent llm, stt, and tts selections. This SDK release ships browser-WASM LLM and browser speech provider/2 adapters and also adapts selected TWiN Cloud LLM, Core-backed Ollama LLM, on-device Whisper STT, and on-device Kokoro TTS routes into provider/2. Built-in speech selections use LOCAL_SPEACH with whisper-small for STT and kokoro for TTS; these local routes need no TWiN key. Applications may explicitly configure createTwinCloudTTSProvider() with their TWiN key for cloud TTS. Native browser recognition and synthesis providers are also available through arcane-os/ai/browser-speech; their browser-owned engines may use remote services. Other routes require a compatible provider adapter. The singleton itself is not an authentication or capability token. It normalizes inspection, model authority, load/unload/dispose, cancellation, stream cleanup, status, and startup barriers. Each selected provider retains its real execution requirements. localOnly creates no fallback, and failure in one role never authorizes a Core, cloud, or different-provider fallback.

For a browser-only LLM, arcane-os/ai/browser-wasm exposes createArcaneAI() and an adapter into the same provider-neutral lifecycle. For browser speech, arcane-os/ai/browser-speech creates independent Whisper STT, Kokoro TTS, native browser recognition/synthesis and explicit cloud TTS providers. These providers register directly with the normalized runtime. The SDK supplies mechanism; applications retain model/runtime choice, licenses, prompts, tools, voices, and disclosure policy.

Browser-WASM LLM lifecycle

The shipped browser runtime contains the Wllama JavaScript/WASM engine and its provider/cache/controller mechanism. It contains no model weights, default model catalog, CDN fallback, native provider, speech model, or application profile. The caller supplies each model as a source with a nonempty ordered file list, so monolithic and split GGUF models use the same contract. HTTPS redirects are followed and the final HTTPS URL is recorded. Each member may optionally declare positive bytes for progress reporting and HTTP Range planning. Declared and observed byte measures remain transfer-local telemetry; they never validate, admit, identify, hash, or decide cache reuse for model content.

On load, the DBOPFS store preserves descriptor order while one bounded worker pool fetches split members or ranges within the selected member. A split member may use one Range worker, allowing an interrupted shard to resume without multiplying the configured concurrency. Download progress retains its existing completed-file fields and adds raw aggregate bytes, remaining bytes, rolling bytes per second, ETA seconds, active transfer workers, the transfer limit, and the active transfer mode. Chunk-driven changes are coalesced on a 250 ms cadence; start, plan/total, active-worker, and completion boundaries may publish immediately. A source member that receives a redirected 200 probes the final URL directly before reusing the original response as its single-fetch fallback. Confirmed support uses deterministic OPFS Range parts of roughly 4 MB each, up to 4,096 parts per member, with bounded active workers; completed parts and split-model members survive interruption so a retry fetches only missing work. Only unfinished active parts restart after refresh. Without usable range support or an observable or declared total it falls back to one full fetch. A normal cache miss may fetch only the caller-supplied HTTPS sources; offline:true performs no model request and uses only a compatible completed cache, otherwise it rejects with ARCANE_AI_MODEL_OFFLINE_MISS. Unload releases the active Wllama session but does not silently delete the app-owned cache. A complete whole member supersedes its current resumable fragments. Cleanup failure is warned without hiding the usable model.

Browser-WASM loads default to full WebGPU offload. Explicit loadDefaults:{gpuLayers:0} or load({gpuLayers:0}) selects CPU through the same packaged Wllama runtime without GPU initialization or GPU-only requirements. Both routes wait for the runtime to report the complete model loaded. navigator.gpu presence alone is not readiness. There is no automatic CPU fallback or silent switch to native/Core/cloud inference.

Browser speech lifecycle

The browser-speech package contains plain-JavaScript authority, DBOPFS store, provider, client, and Worker machinery. It redistributes no Whisper, Kokoro, ONNX, model, voice, third-party license, or corresponding-source payload. Default upstream integrations use createBrowserSpeechAuthority() with a version-pinned npm/package runtime entry and optional upstream wasmPaths; the selected runtime then downloads models and voices through its normal provider fetch and browser cache behavior after explicit load().

createBrowserSpeechArtifactGraph() is an ordinary materialization descriptor. Browser speech uses the caller-selected upstream runtime, model, voice, Fetch, cache, and Worker behavior.

Ordinary module routing preserves bare import specifiers for native import-map resolution and leaves bare fetch, caches, and Worker calls unchanged when the module declares a shadowing binding with the same name. Explicit globalThis or self calls continue through the materialized-file router.

The speech Worker uses its ordinary global Worker message boundary directly for requests, progress, results, errors, and cancellation. It creates no private MessageChannel. During loading, {protocol,id,type:'progress',progress} messages retain the active request id without settling its pending result. Progress records describe the current phase, stage, message, file, and completed file count with total:null for dynamically discovered model files. Complete upstream callback content remains in the adjacent detail field. The ordinary final success or failure envelope ends the request and its progress stream.

Worker operations use arcane-ai-speech-worker/1. The public Worker client supports load, use, status, unload, and dispose; the transport host additionally supports its internal cancel control. Every other operation rejects with code ARCANE_AI_INVALID_REQUEST, message The speech worker operation is not part of its protocol., and role-specific reason stt-worker-operation-unknown or tts-worker-operation-unknown. Failures use the separate arcane-ai-speech-worker-error/1 envelope. Its cause-free form has the four data properties code,message,protocol,reason; the diagnostic form may add one cause data property containing the complete serialized diagnostic, including cycles. The protocol, registered code, nonempty message, reason, role, and operation must agree. A foreign, incomplete, extra-keyed, accessor-bearing, cross-role, or cross-operation error envelope is rejected and terminates that role Worker. Nested module Workers use arcane-ai-browser-speech-artifact-module-worker/1 and report bootstrap rejection only as artifact-module-worker-bootstrap-rejected.

If the platform cannot clone an exotic cause, the Worker keeps the complete raw failure in its console diagnostics and retries settlement with the cause-free four-field envelope. Nested Workers are terminated during role teardown.

Kokoro receives the exact selected webgpu or wasm device through KokoroTTS.from_pretrained() and retains the caller-selected dtype. Its WASM path is configured through namespace.env.wasmPaths; Transformers is configured through namespace.env.backends.onnx.wasm.wasmPaths. Ordinary mode may use a caller-selected version-pinned upstream directory and preserves the runtime's browser cache. Optional numThreads is caller-owned and Transformers-STT-only; a Kokoro declaration rejects with ARCANE_AI_KOKORO_ENV_NUM_THREADS_FIELD_NOT_EXPOSED / kokoro-env-num-threads-field-not-exposed. Missing or rejected namespace shapes report distinct *-unavailable and *-assignment-rejected reasons for each setting; the Worker never substitutes a different namespace. The caller also owns dtype, STT input sample rate, TTS output sample rate, default voice, and the complete voice inventory. Kokoro execution defaults to {device:'auto',maxConcurrentRequests:4} and may be overridden with webgpu or wasm and an integer capacity from 1 through 4. Automatic selection attempts a complete WebGPU pool only when WebGPU is exposed, then tears it down and forms a complete WASM pool if WebGPU loading rejects. It does not change the selected runtime, model, dtype, or voice.

Each capacity slot owns one Worker/model session. The provider-neutral runtime admits the first four requests and retains segment 5 and later in its FIFO queue without dropping content. Synthesis may finish out of order; high-level AI playback waits for earlier segments and schedules the contiguous ready prefix in exact input order. More sessions trade memory for latency, without promising physical GPU kernel overlap.

Applications using AI.configureBrowserSpeech() can read ai.providerRuntime.status('tts', {execution:true}).execution instead of reaching into provider internals. The optional provider snapshot exposes Kokoro's requestedDevice, selectedDevice, maxConcurrentRequests, and activeRequestCount. The default status() retains its original sticky-state behavior; explicit execution inspection does not load a provider or emit a state event. See browser speech for complete examples.

The SDK is not the distributor of the selected upstream speech packages or provider assets and does not republish their legal/source payloads. Package, model, voice, and version selection belongs to each consuming application, not to a shared SDK component ledger or execution/publication gate.

Whisper stt and Kokoro tts each own catalog, inspect, status, load, request, unload, and dispose state. They load, cancel, unload, fail, and recover independently from the LLM and from one another. Whisper owns one Worker and one request. Kokoro owns a bounded pool of distinct Workers and loaded model sessions because the selected browser adapter serializes inference inside one JavaScript isolate. Pool loading first completes one model session, then loads the remaining Worker sessions concurrently against the same prepared artifact selection. One Kokoro request cancellation suppresses only that result and sends the targeted Worker cancel control; upstream may still finish work already inside generate(). Kokoro unload terminates the whole pool. Whisper cancellation after Worker use begins retains destructive Worker teardown. If shared STT Blob decoding is cancelled before Worker use, the request rejects while the loaded provider remains ready. Speech failure neither disables text chat nor retries through another local, native, or cloud provider. The provider/Worker layer is event-neutral: it exposes promises, AbortSignal, and precise lifecycle/status records, but owns no event bus. The provider/2 load context accepts an optional progress callback that receives artifact preparation, upstream model loading, and session initialization records. Compatible concurrent observers share one load and receive its current progress; cancelling one observer leaves the others active. The shared AI runtime projects those records into sticky role state.

Persistent chat and document context

The SDK runtime owns DBOPFSDocumentLibrary, DocumentLexicalSearch, and PersistentAIChatSession. DBOPFS JSONL parsing preserves every nonblank unreadable row as its original string. Persistent chat can therefore expose the complete saved transcript for application-owned inspection and recovery while rejecting that row from provider-facing history with a coded persistence error. Document bootstrap is explicit and schema-driven, commits a completed generation last, and returns complete search results with partial read failures disclosed. evaluate() can instead score a caller-owned source set without persisting its bodies. A chat session never searches the corpus unless the application deliberately wires a document context builder into the request; generated document context remains labeled untrusted.

Persistent chat maintains recurring model context plus a sanitized ChatEntity transcript according to the caller's persistence choice. Structural calls, IDs, argument envelopes, and raw results exist only through the one active provider continuation that requires them. When that continuation settles, recurring model context replaces the protocol exchange with its complete user-facing call messages, any supplied public result messages, and the model's visible response. Reasoning, request metadata, raw protocol, and raw tool returns do not recur. Durable user and assistant turns retain only role, complete visible content, and timestamp. Durable tool turns retain only role, the required user-facing message as content, and optional public name and result status. System prompts remain in the live session only. Existing stored files are not rewritten on load. A turn with persist:false participates in its one request and response only, then remains absent from subsequent model context, the retained transcript, memory extraction, and DBOPFS.

For an automatic model-authored conversation opening, the application calls the persistent session's open() operation with its internal bootstrap message. That bootstrap participates in the one provider request only. The operation durably appends only the complete nonblank assistant response, so the opening survives reload without a fabricated or retained user turn. A system prompt by itself is not a retained conversation turn; an existing retained user, assistant, or tool sequence prevents another opening.

Cancellation and structural tools

Cancellation is part of the provider lifecycle, not just a UI decision. AbortSignal, the normalized role cancel operation, and stream-handle cancel(reason) propagate to the selected provider. Browser-WASM inference requires positive llama cancellation acknowledgement when cancellation is required. Browser Whisper cancellation terminates its Worker after Worker use begins. Browser Kokoro uses request-targeted cancellation so one synthesis does not terminate sibling pool slots; delivery suppression is immediate, while engine preemption remains upstream-cooperative. Cancellation during shared browser decoding leaves the loaded Worker ready. Unload always cancels every active role request before releasing that role's execution state, and superseded late results are rejected rather than committed or retried through another provider.

Interactive requests enter a FIFO lane independently for each role. Provider capacity defaults to 1. A TTS provider may declare a positive safe-integer maxConcurrentRequests; the runtime admits that many oldest requests and queues overflow without discarding content. LLM and STT remain capacity 1. A caller signal cancels only its own queued or active request, while explicit role cancellation targets the oldest active request. Request-specific ownership prevents canceled or superseded callbacks from clearing or restoring sibling state. The runtime revalidates selected-provider readiness and never reloads, switches, or falls back implicitly. Generic provider-promise settlement is not a claim that underlying work stopped; only a provider's documented positive acknowledgement or destructive worker teardown can prove that stronger fact.

High-level built-in selection has its own activation boundary: AI.setAI(...sixPreferences,{signal,startLanguageModel:false}), AI.transitionAI(...sixPreferences,{signal,startLanguageModel:false}), AI.configureProviders(selections,{signal,startLanguageModel:false}), and AI.transitionProviders(selections,{signal,startLanguageModel:false}) register the selected adapters and keep the AI tuple and runtime routes together without an implicit LLM load. The omitted option remains eager. Deferred or cancelled activation remains inactive through later readiness callbacks; explicit runtime load or startup can activate that selected route. Accepted transition cleanup settles independently, cancellation is checked before configuring/preloading the replacement, and the first owned load receives the caller signal. Newer accepted selections supersede older pending work without allowing older continuations to alter the newer selection. After settlement, later caller abort does not unload the completed model. This does not change same-route runtime-load join ownership.

startAIRuntime({startLanguageModel:false,startTranscription:false}) is the explicit startup boundary for browser-WASM LLM and default STT activation. It leaves the selected LLM unloaded until the shared chat control publishes an explicit user load intent; the startup barrier then reports chatReady:false and roles.llm.requested:false instead of beginning a browser-WASM download. The omitted/default startLanguageModel:true preserves existing provider startup behavior. The default startTranscription:false also declines to request a startup STT load and does not unload a role already started through another explicit lifecycle action. A selected unloaded transcription provider remains selected and unloaded until a user lifecycle intent or explicit startTranscription:true opt-in asks the provider owner to load it. Neither state observation nor either shared speech component imports a model or selects a fallback. speech.html and voice-transcription.html consume one shared createSTTActivationController() contract for selected, unloaded, loading, unloading, error, and ready presentation plus cancelable user intent. Both keep capture unavailable until sticky STT state is exactly ready. On mobile browsers, speech.html additionally omits transcription activation, capture, and transcription status/progress while retaining its TTS control and voice status. Its public activation callback returns false without dispatch. This uses the SDK's browser-device classification, including tablets, and does not change the dedicated voice-transcription.html workflow or provider APIs.

Each shared speech component owns an AbortController for its STT request and passes its signal through AI.fetchSTT(). voice-transcription.html also adds that signal to the existing injected transcribe(file,context) callback context. Cancel, readiness loss, superseding capture, and component teardown abort the owned signal and suppress late delivery. Whether the provider's underlying computation stops remains governed by its own cancellation contract. User TTS unmute calls AI.setSpeechMuted(false) before or with its load intent so the runtime records the unmuted lifecycle preference; an initialMuted:false component configuration preserves that intent across an unselected or loading TTS route and exposes unmuted state only after readiness; mute calls AI.setSpeechMuted(true), cancels active synthesis, and unloads TTS. The selected local TTS model catalog owns defaultVoice. AI.js never forwards a saved OpenAI-route voice to Core or browser Kokoro.

LLM tool calls are structural result data only. The SDK never executes a handler. The application owns schema validation, authorization, side-effect policy, dispatch, and the matching tool-result turn.

Ordinary streaming publishes complete nonstructural content and reasoning from every provider choice in order. Complete provider chunks and terminal records remain independently available through the data callbacks. Structural deltas stay private until terminal validation; terminal-only calls are valid, while any observed call must keep the same choice, order, identity, exact argument string, and extension fields at settlement. Awaiting a terminal result before iterating does not stall the private provider stream; projected chunks remain available to the later consumer.

Every first-party function-tool declaration includes function.parameters.properties.message as a required nonempty string, and every emitted call includes that plain-language user-facing value in the JSON encoded by function.arguments.message. The application uses message for the call's progress, question, or next step; it does not treat raw tool envelopes, arguments, internal sequencing state, or protocol failures as ordinary transcript content. An explicitly selected developer inspection surface may show the raw structure separately. This message does not replace domain fields such as a closing report's final_message. One assistant response may contain an ordered array of calls with unique IDs. Display does not settle any call: one atomic result batch must provide exactly one matching nonblank executed, declined, cancelled, or not-executed role:'tool' turn for every pending ID before another user or provider turn.

Portable AI protocol disclosure

The normalized runtime protocol is arcane-ai-runtime/2; registered adapters implement arcane-ai-provider/2 and select a model before load. The browser-WASM component protocol is arcane-ai-browser-wasm/2; its direct controller adapter uses arcane-ai-adapter/1, and adaptV1LlmProvider() projects that surface into the provider/2 LLM role. Browser speech stores identify themselves as arcane-ai-browser-speech-artifacts/1. Direct authorities use arcane-ai-model-authority/1. Optional browser-speech graphs use arcane-ai-browser-speech-artifact-graph/1, kind browser-speech-authenticated-artifact-graph. Those identifiers describe lifecycle contracts; none is by itself a capability grant.

These identifiers normalize lifecycle records. They do not erase provider availability: browser providers still require their browser capabilities, native providers still require an admitted host and Core method, and cloud providers still require explicit selection, network policy, and credentials.

Arcane application protocol

globalThis.Arcane.protocol is arcane/1. The shared API wraps transport selection, request ids, JSON-safe values, promise settlement, Arcane.Error, and renderer events. The current transport snapshot is synchronous:

javascript
const {connected, transport, native, managedLocalAI} =
    globalThis.Arcane.runtime.current();

Transport values are webview2, webkitgtk, android-webview, development-http, and standalone. connected means a callable transport was initialized. It does not prove that Core answered, the method is admitted, or a dependency is ready.

Native host transports

Microsoft NT / WebView2

The SDK Windows host mounts the selected app at a virtual HTTPS origin and injects the canonical classic facade before application scripts. Ordered arcane/1 requests and complete responses cross its owned WebView2/Core bridge; each connected document requests authoritative runtime/service replay. Its selected service composition supplies the actual methods. Closing the window cancels renderer requests and drains accepted service work before Core exits. See Windows executable packaging. Arcane OS can separately compose product policy and privileged services; the generic SDK host does not copy that product composition.

Linux / WebKitGTK

The renderer uses the WebKitGTK host bridge with the same application-facing Arcane protocol. Linux can implement the same normalized methods through different host code. Some administrator-owned service settings intentionally return unsupported/manual guidance rather than imitating Microsoft NT mutation.

Android / Android WebView

Android injects a main-frame, origin-bound bridge for the packaged application. Its generated registry binds application identity, package version, entry, grants, and method policy. Android exposes a narrower surface, including an admitted user-managed local-AI chat projection where configured. Desktop model management is not projected merely because a user-managed Ollama listener is reachable.

macOS

This version includes Foundation-only ArcaneCoreProcess source for an explicit executable, framed pipes and graceful child lifecycle. It supplies no macOS renderer bridge, executable target, artifact or run command. A separately composed macOS host may combine that source with the reusable Node runtime, stdio transport, host composition and portable payload. Source delivery does not establish compilation or execution on macOS. See the macOS process-source contract.

Development and remote HTTP transport

The development HTTP bridge makes the same application-facing request shape available without pretending the browser is a native host. It is a development transport, not a production isolation or authority boundary. A remotely operated client can use an expressly configured web transport only when the host, origin, application identity, and policy admit it; Arcane does not silently swap native calls to arbitrary remote HTTP endpoints.

When transport changes while the public method remains the same, request settlement and public errors stay normalized. Host-specific availability and result detail remain documented by the method.

Core dispatch and capability admission

The host binds the caller's application identity. Core checks the exact method, required capability, allowed application type/id, privilege, mutation exclusivity, request bounds, and relevant package policy. Renderer-supplied app ids or grants never replace that bound identity.

Arcane.capabilities.list() is the Core-side application preflight. It reports current grants and admitted methods but does not reserve authority for a later call. Each call is checked again.

Arcane Ollama protocol path

The safe application path is:

Code
renderer Ollama.js or Arcane.ollama
  -> arcane/1 host request
  -> Core capability and package-policy admission
  -> managed ArcaneOllama loopback service
  -> complete Ollama HTTP operation

Applications never connect directly to localhost:11434. Core owns loopback endpoint selection, method policy, stream ids, chunk events, model-policy checks, native resource admission, and managed mutation workflows.

Direct Ollama methods preserve the complete provider-native success envelope. Arcane normalizes the outer promise/error and stream lifecycle. chatText, generateText, and readiness are helper-level normalizers.

Explicit cloud provider path

arcane/AI can use an explicitly selected and configured cloud profile over HTTPS. That path is not Core transport fallback. The module adapts the selected provider into its high-level application behavior, while provider diagnostics and optional fields can remain provider-specific. Native policy and network policy still govern a native renderer's outbound access.

Local failure never authorizes cloud disclosure. The user or owning application must select and configure the cloud provider explicitly.

Events, streaming, and cancellation

Renderer-visible Core events are listed in the Arcane event reference. Durable transport.ready and core.ready completions can be observed after the fact with Arcane.events.when(). Ordinary progress, stream, terminal, and appearance events are future-only.

Ollama streaming correlates chunks to the originating request. A renderer abort or timeout can stop observation without proving that a non-cooperative host mutation stopped. Method guides state whether Core cooperatively cancels, whether partial provider state can remain, and which status call must be refreshed before retry.

Cross-kernel normalization boundary

The common contract ends where platform truth must remain different:

  • API names, promise/error behavior, host capability policy, and public operation events are normalized;
  • host availability, privilege model, installation/service mechanism, native artifact kind, and some diagnostic/result fields remain platform-specific;
  • unsupported platforms fail or return a documented unsupported state; they do not run an unrelated browser implementation as a substitute;
  • the portable target defaults to the installed SDK provider and assembles a non-executable payload without an Arcane OS checkout;
  • Windows x64 defaults to the installed SDK's matching precompiled host; Linux/Android retain explicit checkout/providers. Every composed host must supply the protocol, version, features, capabilities, methods and services that its application contract requires;
  • that current-build compatibility does not promise that a future SDK will accept this Core or that this SDK will accept a future Core.

Complete-content boundary

SDK runtime, app releases, import-map files, bundles, native plans, providers, and artifacts preserve their complete selected content. Required credential protection, genuinely malformed input rejection, applicable law, external protocol requirements, and host platform safety remain in effect.