arcane-os 0.5.9 → 0.5.11

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 (55) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +117 -26
  3. package/browser-runtime/ai/browser-speech-providers.mjs +1 -1
  4. package/browser-runtime/ai/browser-wasm-llm-provider.mjs +63 -39
  5. package/docs/architecture.md +303 -0
  6. package/docs/compatibility.md +38 -0
  7. package/docs/event-manager.md +263 -0
  8. package/docs/platform-targets.md +104 -0
  9. package/docs/publishing.md +126 -0
  10. package/docs/reference/README.md +206 -0
  11. package/docs/reference/ai/browser-speech.md +813 -0
  12. package/docs/reference/ai/browser-wasm.md +637 -0
  13. package/docs/reference/ai/twin-cloud.md +156 -0
  14. package/docs/reference/arcane-ollama.md +288 -0
  15. package/docs/reference/availability-and-normalization.md +224 -0
  16. package/docs/reference/behavioral-testing.md +129 -0
  17. package/docs/reference/cli.md +820 -0
  18. package/docs/reference/core/README.md +61 -0
  19. package/docs/reference/core/arcane-ai-contracts.md +907 -0
  20. package/docs/reference/core/arcane-api.md +601 -0
  21. package/docs/reference/core/arcane-entities.md +59 -0
  22. package/docs/reference/core/arcane-events.md +134 -0
  23. package/docs/reference/core/ollama-module.md +181 -0
  24. package/docs/reference/core/reference/arcane-api/ai-and-ollama.md +1909 -0
  25. package/docs/reference/core/reference/arcane-api/applications-terminal-capabilities.md +1057 -0
  26. package/docs/reference/core/reference/arcane-api/core-and-events.md +320 -0
  27. package/docs/reference/core/reference/arcane-api/filesystem-storage-preferences-appearance.md +610 -0
  28. package/docs/reference/core/reference/arcane-api/namespaces.md +1157 -0
  29. package/docs/reference/core/reference/arcane-api/platform-installation-users-system.md +1423 -0
  30. package/docs/reference/core/reference/arcane-api/session-provisioning-diagnostics-development.md +315 -0
  31. package/docs/reference/event-manager.md +1409 -0
  32. package/docs/reference/inventory/package-api.json +3194 -0
  33. package/docs/reference/inventory/runtime-components.json +1015 -0
  34. package/docs/reference/inventory/runtime-entities.json +25 -0
  35. package/docs/reference/inventory/runtime-modules.json +1367 -0
  36. package/docs/reference/mail.md +309 -0
  37. package/docs/reference/protocols.md +749 -0
  38. package/docs/reference/runtime-components.md +1529 -0
  39. package/docs/reference/runtime-entities.md +305 -0
  40. package/docs/reference/runtime-modules.md +3275 -0
  41. package/docs/reference/sdk-api.md +6733 -0
  42. package/docs/roadmap.md +79 -0
  43. package/docs/work-amplification.md +66 -0
  44. package/examples/wasm-ai-demo/README.md +80 -0
  45. package/examples/wasm-ai-demo/app.js +787 -0
  46. package/examples/wasm-ai-demo/index.html +343 -0
  47. package/examples/wasm-ai-demo/profile-tools.js +217 -0
  48. package/examples/wasm-ai-demo/profiles/BOSS.Modelfile +106 -0
  49. package/examples/wasm-ai-demo/profiles/PreCrisis.Modelfile +693 -0
  50. package/examples/wasm-ai-demo/rag/boss-library.json +3006 -0
  51. package/examples/wasm-ai-demo/rag.js +295 -0
  52. package/examples/wasm-ai-demo/server.mjs +71 -0
  53. package/package.json +11 -2
  54. package/runtime/arcane/modules/AI.js +1 -1
  55. package/runtime/arcane/modules/AIProviderRuntime.js +26 -5
@@ -0,0 +1,309 @@
1
+ # Mail gateway and durable outbox
2
+
3
+ Arcane Mail is a pure-JavaScript SDK path. It does not use WebAssembly: mail is
4
+ network and durable-state work, while the Resend credential belongs in the
5
+ local Node gateway rather than in browser or WebAssembly state.
6
+
7
+ ## Ownership and availability
8
+
9
+ | Surface | Runtime | Responsibility |
10
+ | --- | --- | --- |
11
+ | `Mail.js` | Browser or native WebView | Validates and formats reports, persists each exact outbound request in DBOPFS, and owns retry/drain lifecycle. |
12
+ | `MailOutbox.mjs` | Browser or compatible injected storage | Stores exact requests in the `mail_outbox` DBOPFS table before delivery and normalizes terminal, retry, and reconciliation states. |
13
+ | `MailTransport.mjs` | Browser, WebView, or compatible Fetch host | Sends one already-persisted request to the configured Arcane gateway with the stable report key as its idempotency key. |
14
+ | `arcane mail send` | Node on the local machine | Reads one complete provider-neutral report from redirected stdin and performs one explicit Resend attempt with a caller-owned idempotency key. |
15
+ | `arcane mail serve` | Node on the local machine | Authenticates the local caller, protects the provider credential, applies any explicitly configured recipient policy, and makes the single server-side Resend request. |
16
+ | `arcane mail key ...` | Node on Windows | Stores, inspects, or deletes a Resend API key in Windows Credential Manager. |
17
+
18
+ The browser never receives the Resend API key. The gateway never writes that
19
+ key to source, argv, logs, events, fixtures, browser storage, or its public
20
+ lifecycle result. Non-Windows hosts report credential operations as unavailable;
21
+ there is no plaintext fallback.
22
+
23
+ ## Public npm import
24
+
25
+ The portable programmatic contract is one subpath:
26
+
27
+ ```javascript
28
+ import Mail,{
29
+ MailOutbox,
30
+ createMailOutbox,
31
+ sendMailReport
32
+ } from 'arcane-os/mail';
33
+ ```
34
+
35
+ `arcane-os/mail` projects `src/mail-api.mjs` and has these exact exports:
36
+
37
+ - default and named `Mail`, plus `resolveMailConfig`;
38
+ - `MailOutbox`, `createMailOutbox`, `MAIL_OUTBOX_PROTOCOL`,
39
+ `MAIL_OUTBOX_TABLE`, `MAIL_OUTBOX_IDEMPOTENCY_WINDOW_MS`, and
40
+ `MAIL_OUTBOX_STATES`; and
41
+ - `MailTransportError`, `normalizeMailEndpoint`, `serializeMailReport`,
42
+ and `sendMailReport`.
43
+
44
+ This entrypoint contains only the portable browser/WebView runtime, outbox, and
45
+ transport contract. It does not import the Node HTTP gateway or Windows
46
+ Credential Manager adapter. Programmatic developer tooling reaches those
47
+ host-owned operations through the existing `createToolchain().mail(...)`
48
+ boundary; ordinary operators use `arcane mail send`, `arcane mail serve`, and
49
+ `arcane mail key ...`. This keeps Node credential and server authority out of a
50
+ browser import while preserving one shared CLI/toolchain implementation.
51
+
52
+ ## Two separate credential boundaries
53
+
54
+ Arcane Mail deliberately separates two credentials:
55
+
56
+ - The **Resend API key** is provider authority. `arcane mail key set <profile>`
57
+ stores it in Windows Credential Manager. `mail send --profile <profile>` and
58
+ `mail serve --profile <profile>` read it only inside the owning Node process.
59
+ - The **mail app key** authenticates one browser/application caller to the
60
+ loopback gateway. It is supplied to `mail serve` through hidden terminal
61
+ input, or through redirected input with `--app-key-stdin`, and must match the
62
+ browser's `arcane.config.mail.appKey`. It is a nonempty printable ASCII
63
+ bearer-like local authentication value, never the Resend API key.
64
+
65
+ Do not put either secret on the command line. Command-line arguments may be
66
+ recorded by the operating system or shell history. Structured CLI output
67
+ requires the matching explicit redirected-input flag and rejects TTY input so
68
+ the terminal cannot echo a secret.
69
+
70
+ The mail app key is not confidential from scripts executing in the same page:
71
+ same-runtime script or XSS can read browser configuration and issue the same
72
+ request. Inject it at runtime, never hardcode it in shipped assets, protect the
73
+ page's script boundary, restrict the exact gateway destination, and rotate it
74
+ when page or process trust is lost. It protects the loopback server from
75
+ unadmitted local callers; it is not provider authority or a replacement for
76
+ browser application security.
77
+
78
+ ## Configure the browser runtime
79
+
80
+ One application declares an exact app id, gateway endpoint, and local app key:
81
+
82
+ ```javascript
83
+ globalThis.arcane = globalThis.arcane || {};
84
+ globalThis.arcane.config = globalThis.arcane.config || {};
85
+ globalThis.arcane.config.mail = {
86
+ appName: 'arcane-dev',
87
+ appKey: localMailAppKey,
88
+ endpoint: 'http://127.0.0.1:8025/v1/mail'
89
+ };
90
+ ```
91
+
92
+ The transport endpoint must be HTTPS or loopback HTTP at `localhost`,
93
+ `127.0.0.1`, or `[::1]`. The SDK CLI gateway itself binds numeric loopback only. An
94
+ explicit HTTPS endpoint receives the app key as `X-Mail-Key`, so its ownership
95
+ and trust must be verified before configuration. A hosted default is derived
96
+ only when the page declares an admitted Arcane mail base domain; otherwise
97
+ configuration fails rather than selecting an arbitrary remote host.
98
+
99
+ The ordinary browser transport has no automatic request deadline and reads the
100
+ complete gateway response. A caller may explicitly supply a positive
101
+ `requestTimeout` when its own lifecycle requires a deadline; cancellation then
102
+ remains an uncertain delivery outcome because the provider may already have
103
+ accepted the request.
104
+
105
+ ## Durable send semantics
106
+
107
+ `Mail.send(to, subject, payload, messageStyle, messageType)` preserves the
108
+ existing signature. `messageType` is `error`, `report`, or `crisis_detected`.
109
+ Report and crisis mail require at least one recipient; error mail may use the
110
+ gateway's configured allowlisted fallback recipients.
111
+
112
+ In a browser, the module owns the one `window.mail` singleton. An explicit
113
+ `new Mail(config, options)` may configure that owned singleton only before its
114
+ durable lifecycle or outbox has begun; later reconfiguration fails with
115
+ `MAIL_CONFIGURATION_LOCKED`. `dispose()` clears the global registration only
116
+ when that exact instance owns it, so a later construction creates a fresh
117
+ instance instead of returning stale disposed state. A truthy `window.mail`
118
+ owned by another implementation reports `MAIL_SINGLETON_CONFLICT`; the SDK
119
+ never replaces another implementation's singleton.
120
+
121
+ Runtime context enrichment is off by default. With the explicit constructor
122
+ option `{includeContext:true}`, every message also captures
123
+ `location.pathname` as `source_path`; report and crisis messages load the
124
+ current User entity and add its `username`, `email`, `language`, and `phone`
125
+ values to the locally rendered content. Those fields are then stored and sent
126
+ unencrypted as part of the message, so the application owns consent, purpose,
127
+ recipient scope, retention, and disclosure. Without that option, Mail neither
128
+ loads the User profile nor adds the path/profile fields. The generated
129
+ `source_at` timestamp, caller-supplied payload, subject, type, and recipients
130
+ remain part of the requested report in either mode.
131
+
132
+ The public `Mail` integration requires its compatible DBOPFS adapter. Before the
133
+ first delivery attempt, it serializes the exact provider-neutral report and
134
+ commits it to DBOPFS table `mail_outbox`. A generated report key
135
+ contains only time/process identity and never includes the subject or an email
136
+ address. Mail uses `randomUUID()` when available and otherwise uses a local
137
+ time/sequence identity without blocking ordinary delivery. Delivery receives
138
+ the complete stored serialized content and the same report key on every retry.
139
+
140
+ `MailTransport.mjs` is also a lower-level public transport and does not persist
141
+ raw caller requests by itself. A directly constructed `MailOutbox` can accept
142
+ another injected storage adapter plus a Web Locks compatible `lockManager`;
143
+ durable claims then belong to that adapter's implemented `get`, `set`,
144
+ `getAllKeys`, and shared-lock semantics rather than to DBOPFS. The browser
145
+ default uses `navigator.locks`; when that cross-context authority is absent,
146
+ the outbox reports `MAIL_OUTBOX_LOCK_UNAVAILABLE` and does not start the drain.
147
+
148
+ Call `await mail.start()` during application startup so pre-existing records
149
+ are scanned even when the application does not send a new report. The first
150
+ `send()` also starts the lifecycle if needed.
151
+
152
+ | Mail method/property | Contract |
153
+ | --- | --- |
154
+ | `start({signal})` | Idempotently scans/drains startup work and installs one owned online listener. |
155
+ | `send(to, subject, payload, style, type)` | Formats, persists, then conditionally attempts one new report and returns the complete mutable durable record, report, delivery result, and convenience state fields. |
156
+ | `drain({reason, signal})` | Runs or joins one FIFO drain across the complete current inventory. |
157
+ | `listOutbox()` / `getOutboxRecord(reportKey)` | Returns valid durable records, including complete serialized report content. Invalid files do not hide valid records. |
158
+ | `auditOutbox()` / `invalidOutboxRecords` | Returns the complete valid inventory and filename/code/repairability metadata for every invalid file. |
159
+ | `repairInvalidOutbox(fileName, record)` | Replaces one invalid, correctly named file only after the replacement passes the full record contract. |
160
+ | `deleteInvalidOutbox(fileName)` | Explicitly deletes one invalid file after current inventory confirmation; it requires a storage adapter with `delete`. |
161
+ | `quarantineInvalidOutbox()` | Moves every confirmed invalid file in the current inventory into `mail_outbox_quarantine`; it retains the complete JSON-serializable source value before deleting each original. |
162
+ | `stop()` | Removes the online listener and aborts in-flight work owned by this Mail instance; persisted requests and uncertain attempt state remain available for a later restart. |
163
+ | `dispose()` | Idempotently stops lifecycle and releases the singleton event source. |
164
+ | `events` | Event-source handle for `mail-outbox-state`, `mail-outbox-delivery`, and `mail-outbox-drain`; details contain the complete mutable record, result, failure, inventory, and report content available at that transition. |
165
+
166
+ The returned durable record has one of these states:
167
+
168
+ | State | Meaning |
169
+ | --- | --- |
170
+ | `queued` | Persisted, but no attempt was made, normally because the device is offline. |
171
+ | `sending` | An attempt was durably recorded before calling the transport. An interrupted instance recovers this state on the next drain. |
172
+ | `retry_wait` | A retryable or uncertain result is retained inside Resend's 24-hour idempotency window. |
173
+ | `accepted` | The selected transport returned `accepted` with a valid request id. A provider id or acceptance authority may be preserved as optional transport metadata, but neither is required by the outbox. This is API acceptance, not an inbox-delivery claim. |
174
+ | `failed` | A permanent failure or expired non-ambiguous retry cannot be retried automatically. |
175
+ | `reconciliation_required` | An ambiguous attempt reached the end of the idempotency window. Automatic retry stops to avoid a duplicate send. |
176
+
177
+ The outbox owns one FIFO drain per instance and processes the complete current
178
+ inventory. A shared Web Lock extends that single-drain authority across
179
+ MailOutbox instances and browser contexts for the same origin and table.
180
+ Startup, the browser's `online` event, and explicit calls can trigger a drain;
181
+ there are no polling/retry timers. A future-due `retry_wait` record requires a
182
+ later startup, connectivity transition, or host-owned manual drain. Every
183
+ successful durable write publishes its complete record transition, including
184
+ transitions produced by startup, manual, and online drains.
185
+ `dispose()` aborts owned in-flight work, removes the online listener, and
186
+ releases the singleton-event registration. A provider attempt interrupted after
187
+ it began is retained as an uncertain same-key retry rather than being discarded.
188
+ Cancellation that arrives while the durable `sending` transition is being
189
+ written restores the prior non-attempted state before returning and never calls
190
+ the transport. A restart after `stop()` waits for the cancelled start generation
191
+ to settle, then begins a distinct lifecycle generation.
192
+
193
+ Each durable record contains the exact complete serialized message and the
194
+ public list/get APIs return that content. Never place credentials in a report.
195
+ Protect the application's OPFS origin and any code allowed to inspect it. The
196
+ outbox applies no record-count ceiling and exposes no implicit
197
+ retention/deletion policy; the owning application explicitly removes terminal
198
+ DBOPFS records when its own lifecycle requires that operation.
199
+
200
+ Malformed or unreadable files are reported through `audit()` /
201
+ `invalidRecords` on `MailOutbox` and the Mail proxies above, and skipped without
202
+ aborting valid listing or draining. Inventory and quarantine process every
203
+ physical file in the current inventory in one operation. Quarantine remains
204
+ local and contains the complete serialized
205
+ source value; protect and retain that table according to the application's own
206
+ data policy. Transient storage read failures propagate as
207
+ `MAIL_OUTBOX_STORAGE_FAILED` and never authorize destructive maintenance. Each
208
+ maintenance target is revalidated and serialized against record writes through
209
+ one origin-wide exclusive table lock; a file that became valid in another
210
+ MailOutbox instance or browser context is preserved, and quarantine refuses
211
+ deletion when it cannot capture the complete source value. Same-key exact
212
+ serialized-content comparison executes under that table lock before a queued
213
+ record is committed.
214
+ Injected adapters that can share a table must share the same Web Locks
215
+ compatible manager and must not mutate MailOutbox-owned records behind that
216
+ boundary.
217
+
218
+ Mail publishes complete semantic events through the SDK singleton event
219
+ authority. Public detail includes the full mutable durable record, serialized
220
+ report, result or failure, provider details, and complete drain inventory
221
+ available at that transition. It never includes the Resend API key or mail app
222
+ key. Listener exceptions are observational and cannot change a committed mail
223
+ operation result.
224
+
225
+ When both an explicit endpoint and native `Arcane.mail.send` exist, Mail uses
226
+ the configured HTTP endpoint so the authenticated SDK gateway can return its
227
+ complete provider result. The native bridge remains a fallback when no endpoint
228
+ is configured; an accepted Core result keeps its native acceptance-authority
229
+ metadata without an SDK allowlist. A malformed or unreadable native response
230
+ is retained as an uncertain same-key retry, while temporary native transport
231
+ unavailability is a non-ambiguous retryable failure. Once a valid accepted
232
+ result has returned, a racing lifecycle cancellation cannot erase that
233
+ committed acceptance result.
234
+
235
+ ## Operate the CLI and local gateway
236
+
237
+ Store one Resend key under a local profile:
238
+
239
+ ```text
240
+ arcane mail key set arcane-dev
241
+ arcane mail key status arcane-dev
242
+ arcane mail key delete arcane-dev
243
+ ```
244
+
245
+ `key set` prompts with hidden input. `--secret-stdin` is the explicit
246
+ non-interactive alternative and rejects a TTY.
247
+
248
+ Perform one provider attempt directly from the SDK CLI:
249
+
250
+ ```text
251
+ arcane mail send --profile arcane-dev --from "Arcane <verified@example.com>" --report-key <stable-id> --report-stdin
252
+ ```
253
+
254
+ The redirected UTF-8 JSON input is read completely. A report requires `type`,
255
+ `to`, `subject`, and at least one of `text` or `html`; additional
256
+ JSON-compatible provider fields are preserved. Direct CLI sends require at
257
+ least one explicit recipient, including for `error` reports. Message content is
258
+ not accepted in argv. Programmatic results and observer events preserve the
259
+ complete report, provider request, provider response, and error detail while
260
+ never exposing either credential.
261
+
262
+ The caller must create and retain a safe-character `--report-key` before the
263
+ attempt. It is the Resend idempotency key and may be reused only with the same
264
+ serialized report content for an intentional retry or reconciliation. The
265
+ CLI performs exactly one attempt and never retries automatically. Exit zero
266
+ requires a successful Resend response containing a valid provider id; that is
267
+ provider acceptance, not an inbox-delivery claim. Timeout, connection loss, or
268
+ cancellation after the provider attempt begins is returned as an ambiguous
269
+ nonzero outcome because the provider may already have accepted the request.
270
+ Cancellation before the attempt exits 130 without sending.
271
+ For both CLI mail operations, `--request-timeout` accepts 1 through 2147483647
272
+ milliseconds, the Node timer range. When omitted, the SDK adds no provider
273
+ deadline.
274
+
275
+ Start the authenticated gateway:
276
+
277
+ ```text
278
+ arcane mail serve --profile arcane-dev --from "Arcane <verified@example.com>" --app arcane-dev --origin http://127.0.0.1:8000 --allow-to recipient@example.com
279
+ ```
280
+
281
+ Human output prompts for the separate mail app key with hidden input.
282
+ Non-interactive structured output requires `--app-key-stdin` and redirected
283
+ stdin. The server binds numeric loopback only; the default is
284
+ `127.0.0.1:8025/v1/mail`.
285
+
286
+ The gateway protects the provider credential by requiring:
287
+
288
+ - its exact numeric-loopback `Host` authority and `/v1/mail` route;
289
+ - an exact configured `Origin`, app id, and constant-time app-key match;
290
+ - complete JSON requests with at least one recipient, or configured fallback
291
+ recipients for an `error` report;
292
+ - an optional explicit recipient allowlist when one is configured; and
293
+ - one fixed Resend endpoint with the stable Arcane report key forwarded as
294
+ `Idempotency-Key`.
295
+
296
+ The gateway returns `202` only after Resend returns a valid provider id.
297
+ Transport loss, an explicit caller-selected timeout, an invalid success body,
298
+ or an unreadable provider response returns an explicit uncertain result and never claims
299
+ delivery. Rate limits, concurrent idempotency requests, permanent validation
300
+ failures, and provider failures are mapped to structured retryable/permanent
301
+ results with the complete available provider response or error detail.
302
+
303
+ ## Operational verification
304
+
305
+ The focused SDK tests use only synthetic keys, addresses, responses, storage,
306
+ and loopback requests. They do not contact Resend or send email. A live
307
+ acceptance send is a separate operational boundary: use a disposable message,
308
+ the real allowlist, and the selected credential profile, then verify both the
309
+ gateway's provider-acceptance id and the intended inbox outcome.