arcane-os 0.2.2 → 0.3.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.
- package/CHANGELOG.md +29 -0
- package/README.md +8 -8
- package/browser-runtime/ARCANE_SDK_BROWSER_RELEASE.json +29 -22
- package/browser-runtime/ai/ARCANE_AI_BROWSER_SPEECH_COMPONENTS.json +203 -0
- package/browser-runtime/ai/browser-kokoro-worker.mjs +11 -2
- package/browser-runtime/ai/browser-speech-artifacts.mjs +3230 -397
- package/browser-runtime/ai/browser-speech-providers.mjs +1141 -157
- package/browser-runtime/ai/browser-speech.mjs +2 -0
- package/browser-runtime/ai/browser-whisper-worker.mjs +11 -2
- package/browser-runtime/ai/model-controller.mjs +285 -95
- package/browser-runtime/ai/speech-worker-client.mjs +247 -32
- package/browser-runtime/ai/speech-worker-runtime.mjs +2310 -167
- package/browser-runtime/event-manager.mjs +1097 -1
- package/docs/architecture.md +2 -2
- package/docs/event-manager.md +155 -27
- package/docs/reference/README.md +27 -27
- package/docs/reference/ai/browser-speech-package-authority.json +835 -0
- package/docs/reference/ai/browser-speech.md +1162 -246
- package/docs/reference/ai/browser-wasm.md +18 -7
- package/docs/reference/availability-and-normalization.md +6 -3
- package/docs/reference/behavioral-testing.md +29 -6
- package/docs/reference/cli.md +117 -9
- package/docs/reference/core/arcane-ai-contracts.md +1 -1
- package/docs/reference/event-manager.md +577 -32
- package/docs/reference/inventory/package-api.json +478 -2
- package/docs/reference/inventory/runtime-components.json +108 -44
- package/docs/reference/inventory/runtime-modules.json +131 -53
- package/docs/reference/mail.md +316 -0
- package/docs/reference/protocols.md +157 -43
- package/docs/reference/runtime-components.md +258 -83
- package/docs/reference/runtime-modules.md +613 -77
- package/docs/reference/sdk-api.md +1014 -25
- package/package.json +5 -4
- package/runtime/ARCANE_RUNTIME_RELEASE.json +145 -140
- package/runtime/arcane/components/app-bar.html +34 -13
- package/runtime/arcane/components/assistant-panel.html +110 -57
- package/runtime/arcane/components/calculator.html +7 -4
- package/runtime/arcane/components/chart.html +58 -17
- package/runtime/arcane/components/chat.html +606 -136
- package/runtime/arcane/components/conversation-view.html +13 -6
- package/runtime/arcane/components/dashboard-config.html +96 -59
- package/runtime/arcane/components/data-maintenance.html +69 -14
- package/runtime/arcane/components/data-view.html +53 -7
- package/runtime/arcane/components/directory-picker.html +118 -32
- package/runtime/arcane/components/document-inspector.html +47 -10
- package/runtime/arcane/components/file-drop.html +81 -35
- package/runtime/arcane/components/file-inspector.html +72 -22
- package/runtime/arcane/components/file-manager.html +374 -79
- package/runtime/arcane/components/integration-settings.html +12 -5
- package/runtime/arcane/components/local-ai-status.html +48 -19
- package/runtime/arcane/components/markdown-document.html +161 -68
- package/runtime/arcane/components/markdown-editor.html +110 -33
- package/runtime/arcane/components/media-embed.html +8 -5
- package/runtime/arcane/components/modal.html +15 -5
- package/runtime/arcane/components/output-panel.html +28 -23
- package/runtime/arcane/components/preferences-form.html +22 -4
- package/runtime/arcane/components/record-timeline.html +18 -2
- package/runtime/arcane/components/relationship-board.html +23 -3
- package/runtime/arcane/components/screen-capture.html +10 -4
- package/runtime/arcane/components/source-code-viewer.html +76 -9
- package/runtime/arcane/components/source-explanation.html +23 -3
- package/runtime/arcane/components/speech.html +462 -384
- package/runtime/arcane/components/summary-strip.html +22 -11
- package/runtime/arcane/components/table.html +39 -21
- package/runtime/arcane/components/task-progress.html +79 -21
- package/runtime/arcane/components/terminal-workspace.html +7 -4
- package/runtime/arcane/components/theme-editor.html +7 -3
- package/runtime/arcane/components/unified-inbox.html +9 -4
- package/runtime/arcane/components/voice-transcription.html +639 -98
- package/runtime/arcane/components/weather-widget.html +5 -3
- package/runtime/arcane/components/web-navigator.html +48 -8
- package/runtime/arcane/entities/Chat.js +1 -1
- package/runtime/arcane/entities/User.js +110 -23
- package/runtime/arcane/modules/AI.js +2109 -130
- package/runtime/arcane/modules/AIProviderRuntime.js +720 -13
- package/runtime/arcane/modules/AIRuntimeState.js +109 -52
- package/runtime/arcane/modules/ApiModelDatabase.js +390 -17
- package/runtime/arcane/modules/BrowserTestSuite.js +205 -28
- package/runtime/arcane/modules/CalculatorEngine.js +63 -3
- package/runtime/arcane/modules/CommunicationAppController.js +588 -28
- package/runtime/arcane/modules/CommunicationHub.js +590 -10
- package/runtime/arcane/modules/ComponentContracts.js +470 -0
- package/runtime/arcane/modules/ConversationTimebox.js +152 -33
- package/runtime/arcane/modules/DBLS.js +40 -7
- package/runtime/arcane/modules/DBOPFS.js +35 -11
- package/runtime/arcane/modules/DataMaintenance.js +12 -2
- package/runtime/arcane/modules/Errors.js +65 -7
- package/runtime/arcane/modules/HTMLImport.js +198 -14
- package/runtime/arcane/modules/LocalAIReadinessController.js +208 -29
- package/runtime/arcane/modules/Mail.js +738 -115
- package/runtime/arcane/modules/MailOutbox.mjs +1395 -0
- package/runtime/arcane/modules/MailTransport.mjs +197 -39
- package/runtime/arcane/modules/Ollama.js +36 -1
- package/runtime/arcane/modules/OpenMeteoWeatherProvider.js +583 -7
- package/runtime/arcane/modules/PreferenceStore.js +367 -33
- package/runtime/arcane/modules/RecordReviewStore.js +322 -23
- package/runtime/arcane/modules/ScreenCapture.js +1397 -15
- package/runtime/arcane/modules/SpeechPlayback.js +438 -41
- package/runtime/arcane/modules/TerminalClient.js +277 -12
- package/runtime/arcane/modules/ThemeBootstrap.js +80 -6
- package/runtime/arcane/modules/ThemeManager.js +39 -7
- package/runtime/arcane/modules/TimeGuard.js +131 -20
- package/runtime/arcane/modules/WaitForComponent.js +386 -33
- package/schemas/arcane-lock.schema.json +2 -2
- package/src/cli/main.mjs +435 -9
- package/src/event-manager.mjs +1097 -1
- package/src/import-map.mjs +21 -3
- package/src/index.mjs +13 -0
- package/src/installed-sdk-runtime.mjs +112 -0
- package/src/mail-api.mjs +22 -0
- package/src/mail-credentials.mjs +667 -0
- package/src/mail-server.mjs +1769 -0
- package/src/mail.mjs +261 -0
- package/src/sdk-browser-runtime.mjs +85 -41
- package/src/testing-loader.mjs +7 -0
- package/src/toolchain.mjs +3 -0
- package/src/workspace.mjs +1 -1
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
# Mail gateway and durable outbox
|
|
2
|
+
|
|
3
|
+
Arcane Mail is a pure-JavaScript SDK path. It does not use WebAssembly: mail is
|
|
4
|
+
bounded network and durable-state work, while the Resend credential belongs in
|
|
5
|
+
the local Node gateway rather than in browser or WASM bytes.
|
|
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 immutable 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 bounded 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, enforces the app/Origin/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 credential operations fail closed; there is no
|
|
21
|
+
plaintext fallback.
|
|
22
|
+
|
|
23
|
+
## Public npm import
|
|
24
|
+
|
|
25
|
+
The 0.3.0 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`,
|
|
40
|
+
`MAIL_OUTBOX_STATES`, and `MAIL_OUTBOX_ACCEPTANCE_AUTHORITIES`; and
|
|
41
|
+
- `MailTransportError`, `normalizeMailEndpoint`, `serializeMailReport`,
|
|
42
|
+
`sendMailReport`, `DEFAULT_MAIL_REQUEST_TIMEOUT_MS`, and
|
|
43
|
+
`MAX_MAIL_RESPONSE_BYTES`.
|
|
44
|
+
|
|
45
|
+
This entrypoint contains only the portable browser/WebView runtime, outbox, and
|
|
46
|
+
transport contract. It does not import the Node HTTP gateway or Windows
|
|
47
|
+
Credential Manager adapter. Programmatic developer tooling reaches those
|
|
48
|
+
host-owned operations through the existing `createToolchain().mail(...)`
|
|
49
|
+
boundary; ordinary operators use `arcane mail send`, `arcane mail serve`, and
|
|
50
|
+
`arcane mail key ...`. This keeps Node credential and server authority out of a
|
|
51
|
+
browser import while preserving one shared CLI/toolchain implementation.
|
|
52
|
+
|
|
53
|
+
## Two separate credential boundaries
|
|
54
|
+
|
|
55
|
+
Arcane Mail deliberately separates two credentials:
|
|
56
|
+
|
|
57
|
+
- The **Resend API key** is provider authority. `arcane mail key set <profile>`
|
|
58
|
+
stores it in Windows Credential Manager. `mail send --profile <profile>` and
|
|
59
|
+
`mail serve --profile <profile>` read it only inside the owning Node process.
|
|
60
|
+
- The **mail app key** authenticates one browser/application caller to the
|
|
61
|
+
loopback gateway. It is supplied to `mail serve` through hidden terminal
|
|
62
|
+
input, or through redirected input with `--app-key-stdin`, and must match the
|
|
63
|
+
browser's `arcane.config.mail.appKey`. It is a 16–512 character printable
|
|
64
|
+
ASCII bearer-like local admission value, never the Resend API key.
|
|
65
|
+
|
|
66
|
+
Do not put either secret on the command line. Command-line arguments may be
|
|
67
|
+
recorded by the operating system or shell history. Structured CLI output
|
|
68
|
+
requires the matching explicit redirected-input flag and rejects TTY input so
|
|
69
|
+
the terminal cannot echo a secret.
|
|
70
|
+
|
|
71
|
+
The mail app key is not confidential from scripts executing in the same page:
|
|
72
|
+
same-runtime script or XSS can read browser configuration and issue the same
|
|
73
|
+
request. Inject it at runtime, never hardcode it in shipped assets, protect the
|
|
74
|
+
page's script boundary, restrict the exact gateway destination, and rotate it
|
|
75
|
+
when page or process trust is lost. It protects the loopback server from
|
|
76
|
+
unadmitted local callers; it is not provider authority or a replacement for
|
|
77
|
+
browser application security.
|
|
78
|
+
|
|
79
|
+
## Configure the browser runtime
|
|
80
|
+
|
|
81
|
+
One application declares an exact app id, gateway endpoint, and local app key:
|
|
82
|
+
|
|
83
|
+
```javascript
|
|
84
|
+
globalThis.arcane = globalThis.arcane || {};
|
|
85
|
+
globalThis.arcane.config = globalThis.arcane.config || {};
|
|
86
|
+
globalThis.arcane.config.mail = {
|
|
87
|
+
appName: 'arcane-dev',
|
|
88
|
+
appKey: localMailAppKey,
|
|
89
|
+
endpoint: 'http://127.0.0.1:8025/v1/mail'
|
|
90
|
+
};
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
The transport endpoint must be HTTPS or loopback HTTP at `localhost`,
|
|
94
|
+
`127.0.0.1`, or `[::1]`. The SDK CLI gateway itself binds numeric loopback only. An
|
|
95
|
+
explicit HTTPS endpoint receives the app key as `X-Mail-Key`, so its ownership
|
|
96
|
+
and trust must be verified before configuration. A hosted default is derived
|
|
97
|
+
only when the page declares an admitted Arcane mail base domain; otherwise
|
|
98
|
+
configuration fails rather than selecting an arbitrary remote host.
|
|
99
|
+
|
|
100
|
+
The browser request timeout must be 1,000–600,000 milliseconds. Its default is
|
|
101
|
+
590,000 milliseconds. If overridden, keep it longer than the gateway's
|
|
102
|
+
provider timeout plus response margin; equal client/provider deadlines can
|
|
103
|
+
discard the gateway's normalized result and force an uncertain retry path. The
|
|
104
|
+
transport reads at most 64 KiB of gateway response data.
|
|
105
|
+
|
|
106
|
+
## Durable send semantics
|
|
107
|
+
|
|
108
|
+
`Mail.send(to, subject, payload, messageStyle, messageType)` preserves the
|
|
109
|
+
existing signature. `messageType` is `error`, `report`, or `crisis_detected`.
|
|
110
|
+
Report and crisis mail require at least one recipient; error mail may use the
|
|
111
|
+
gateway's configured allowlisted fallback recipients.
|
|
112
|
+
|
|
113
|
+
In a browser, the module owns the one `window.mail` singleton. An explicit
|
|
114
|
+
`new Mail(config, options)` may configure that owned singleton only before its
|
|
115
|
+
durable lifecycle or outbox has begun; later reconfiguration fails with
|
|
116
|
+
`MAIL_CONFIGURATION_LOCKED`. `dispose()` clears the global registration only
|
|
117
|
+
when that exact instance owns it, so a later construction creates a fresh
|
|
118
|
+
instance instead of returning stale disposed state. A truthy `window.mail`
|
|
119
|
+
owned by another implementation fails closed with `MAIL_SINGLETON_CONFLICT`;
|
|
120
|
+
the SDK never returns or silently adopts an unverified foreign singleton.
|
|
121
|
+
|
|
122
|
+
Runtime context enrichment is off by default. With the explicit constructor
|
|
123
|
+
option `{includeContext:true}`, every message also captures
|
|
124
|
+
`location.pathname` as `source_path`; report and crisis messages load the
|
|
125
|
+
current User entity and add its `username`, `email`, `language`, and `phone`
|
|
126
|
+
values to the locally rendered content. Those fields are then stored and sent
|
|
127
|
+
unencrypted as part of the message, so the application owns consent, purpose,
|
|
128
|
+
recipient scope, retention, and disclosure. Without that option, Mail neither
|
|
129
|
+
loads the User profile nor adds the path/profile fields. The generated
|
|
130
|
+
`source_at` timestamp, caller-supplied payload, subject, type, and recipients
|
|
131
|
+
remain part of the requested report in either mode.
|
|
132
|
+
|
|
133
|
+
The public `Mail` integration requires its verified DBOPFS adapter. Before the
|
|
134
|
+
first delivery attempt, it serializes the exact provider-neutral report and
|
|
135
|
+
commits it to DBOPFS table `mail_outbox`. A generated report key
|
|
136
|
+
contains only time/random identity and never includes the subject or an email
|
|
137
|
+
address. Its random identity requires platform Web Crypto (`randomUUID()` or
|
|
138
|
+
`getRandomValues()`); Mail fails with `MAIL_CRYPTO_UNAVAILABLE` instead of
|
|
139
|
+
falling back to `Math.random()`. A compatible injected `crypto` provider is a
|
|
140
|
+
host/test boundary and must supply cryptographically secure randomness.
|
|
141
|
+
Delivery receives the stored serialized bytes and the same report key on every
|
|
142
|
+
safe retry.
|
|
143
|
+
|
|
144
|
+
`MailTransport.mjs` is also a lower-level public transport and does not persist
|
|
145
|
+
raw caller requests by itself. A directly constructed `MailOutbox` can accept
|
|
146
|
+
another injected storage adapter plus a Web Locks compatible `lockManager`;
|
|
147
|
+
durable claims then belong to that adapter's verified `get`, `set`,
|
|
148
|
+
`getAllKeys`, and shared-lock semantics rather than to DBOPFS. The browser
|
|
149
|
+
default uses `navigator.locks`; absence of that cross-context authority fails
|
|
150
|
+
closed with `MAIL_OUTBOX_LOCK_UNAVAILABLE`.
|
|
151
|
+
|
|
152
|
+
Call `await mail.start()` during application startup so pre-existing records
|
|
153
|
+
are scanned even when the application does not send a new report. The first
|
|
154
|
+
`send()` also starts the lifecycle if needed.
|
|
155
|
+
|
|
156
|
+
| Mail method/property | Contract |
|
|
157
|
+
| --- | --- |
|
|
158
|
+
| `start({signal})` | Idempotently scans/drains startup work and installs one owned online listener. |
|
|
159
|
+
| `send(to, subject, payload, style, type)` | Formats, persists, then conditionally attempts one new report and returns a privacy-safe send summary. |
|
|
160
|
+
| `drain({reason, signal})` | Runs or joins the one bounded FIFO drain. |
|
|
161
|
+
| `listOutbox()` / `getOutboxRecord(reportKey)` | Returns valid durable records, including unredacted serialized report content. Invalid files do not hide valid records. |
|
|
162
|
+
| `auditOutbox()` / `invalidOutboxRecords` | Returns the bounded valid inventory and filename/code/repairability metadata for invalid files; it never publishes that metadata as a semantic event. |
|
|
163
|
+
| `repairInvalidOutbox(fileName, record)` | Replaces one invalid, correctly named file only after the replacement passes the full record contract. |
|
|
164
|
+
| `deleteInvalidOutbox(fileName)` | Explicitly deletes one invalid file after bounded inventory confirmation; it requires a storage adapter with `delete`. |
|
|
165
|
+
| `quarantineInvalidOutbox({limit})` | Moves at most 64 confirmed invalid files per call into `mail_outbox_quarantine`; it must retain a bounded serialized snapshot before deleting each original. |
|
|
166
|
+
| `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. |
|
|
167
|
+
| `dispose()` | Idempotently stops lifecycle and releases the singleton event source. |
|
|
168
|
+
| `events` | Read-only event-source handle for `mail-outbox-state`, `mail-outbox-delivery`, and `mail-outbox-drain`. |
|
|
169
|
+
|
|
170
|
+
The returned durable record has one of these states:
|
|
171
|
+
|
|
172
|
+
| State | Meaning |
|
|
173
|
+
| --- | --- |
|
|
174
|
+
| `queued` | Persisted, but no attempt was made, normally because the device is offline. |
|
|
175
|
+
| `sending` | An attempt was durably recorded before calling the transport. An interrupted instance recovers this state on the next drain. |
|
|
176
|
+
| `retry_wait` | A retryable or uncertain result is retained inside Resend's 24-hour idempotency window. |
|
|
177
|
+
| `accepted` | Direct gateway delivery returned both its request id and a Resend provider id, or the exact native `mail-send-v1` bridge returned accepted under named `arcane-core-mail-send-v1` authority without fabricating a provider id. This is API acceptance, not an inbox-delivery claim. |
|
|
178
|
+
| `failed` | A permanent failure or expired non-ambiguous retry cannot be retried automatically. |
|
|
179
|
+
| `reconciliation_required` | An ambiguous attempt reached the end of the idempotency window. Automatic retry stops to avoid a duplicate send. |
|
|
180
|
+
|
|
181
|
+
The outbox owns one bounded FIFO drain per instance and attempts at most 16
|
|
182
|
+
records in one default drain. A shared Web Lock extends that single-drain
|
|
183
|
+
authority across MailOutbox instances and browser contexts for the same origin
|
|
184
|
+
and table. Startup, the browser's `online` event, and explicit calls can trigger
|
|
185
|
+
a drain; there are no polling/retry timers. A
|
|
186
|
+
future-due `retry_wait` record and records beyond the per-drain bound therefore
|
|
187
|
+
require a later startup, connectivity transition, or host-owned manual drain.
|
|
188
|
+
Every successful durable write publishes its privacy-safe record transition,
|
|
189
|
+
including transitions produced by startup, manual, and online drains.
|
|
190
|
+
`dispose()` aborts owned in-flight work, removes the online listener, and
|
|
191
|
+
releases the singleton-event registration. A provider attempt interrupted after
|
|
192
|
+
it began is retained as an uncertain same-key retry rather than being discarded.
|
|
193
|
+
Cancellation that arrives while the durable `sending` transition is being
|
|
194
|
+
written restores the prior non-attempted state before returning and never calls
|
|
195
|
+
the transport. A restart after `stop()` waits for the cancelled start generation
|
|
196
|
+
to settle, then begins a distinct lifecycle generation.
|
|
197
|
+
|
|
198
|
+
Each durable record contains the exact unredacted serialized message and the
|
|
199
|
+
public list/get APIs return that content. Never place credentials in a report.
|
|
200
|
+
Protect the application's OPFS origin and any code allowed to inspect it. The
|
|
201
|
+
default outbox cap is 512 records, including terminal records, and enqueue fails
|
|
202
|
+
closed at capacity. The outbox exposes no implicit retention/deletion policy;
|
|
203
|
+
the owning application must explicitly remove terminal DBOPFS records under an
|
|
204
|
+
appropriate audited policy.
|
|
205
|
+
|
|
206
|
+
Malformed or unreadable files are excluded from that logical 512-record
|
|
207
|
+
capacity, reported through `audit()` / `invalidRecords` on `MailOutbox` and the
|
|
208
|
+
Mail proxies above, and skipped without aborting valid listing or draining.
|
|
209
|
+
Inventory processing is bounded to `maxRecords + maxInvalidRecords` (512 + 128
|
|
210
|
+
by default). If more physical files exist, audit marks `truncated:true` and
|
|
211
|
+
enqueue fails with `MAIL_OUTBOX_MAINTENANCE_REQUIRED` until an operator repairs,
|
|
212
|
+
deletes, or quarantines bounded batches. This prevents corrupted files from
|
|
213
|
+
permanently consuming logical capacity without turning maintenance into
|
|
214
|
+
unbounded background work. Quarantine remains local and may contain unredacted
|
|
215
|
+
message bytes; protect and retain the quarantine table accordingly. Transient
|
|
216
|
+
storage read failures propagate as `MAIL_OUTBOX_STORAGE_FAILED` and never
|
|
217
|
+
authorize destructive maintenance. Each maintenance target is revalidated and
|
|
218
|
+
serialized against record writes through one origin-wide exclusive table lock;
|
|
219
|
+
a file that became valid in another MailOutbox instance or browser context is
|
|
220
|
+
preserved, and quarantine refuses deletion when it cannot capture the bounded
|
|
221
|
+
source snapshot. Same-key admission, immutable-body comparison, and capacity
|
|
222
|
+
checks execute under that table lock before the queued record is committed.
|
|
223
|
+
Injected adapters that can share a table must share the same Web Locks
|
|
224
|
+
compatible manager and must not mutate MailOutbox-owned records behind that
|
|
225
|
+
boundary.
|
|
226
|
+
|
|
227
|
+
Mail publishes privacy-minimal semantic events through the SDK singleton event
|
|
228
|
+
authority. Public event detail can contain only report identity, state,
|
|
229
|
+
attempt/count information, and normalized code/reason fields. Addresses,
|
|
230
|
+
subject/body content, headers, credentials, attachment bytes, provider error
|
|
231
|
+
text or stacks, provider ids, and private paths are never public event detail.
|
|
232
|
+
Listener exceptions are observational and cannot change a committed mail
|
|
233
|
+
operation result.
|
|
234
|
+
|
|
235
|
+
When both an explicit endpoint and native `Arcane.mail.send` exist, Mail uses
|
|
236
|
+
the configured HTTP endpoint so the authenticated SDK gateway can return its
|
|
237
|
+
provider acceptance id. The native bridge remains a fallback when no endpoint
|
|
238
|
+
is configured; its exact Core result is recorded with the named acceptance
|
|
239
|
+
authority above. A malformed or unreadable native response is retained as an
|
|
240
|
+
uncertain same-key retry, while temporary native transport unavailability is a
|
|
241
|
+
non-ambiguous retryable failure. Once a valid accepted result has returned, a
|
|
242
|
+
racing lifecycle cancellation cannot erase that authoritative acceptance.
|
|
243
|
+
|
|
244
|
+
## Operate the CLI and local gateway
|
|
245
|
+
|
|
246
|
+
Store one Resend key under a local profile:
|
|
247
|
+
|
|
248
|
+
```text
|
|
249
|
+
arcane mail key set arcane-dev
|
|
250
|
+
arcane mail key status arcane-dev
|
|
251
|
+
arcane mail key delete arcane-dev
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
`key set` prompts with hidden input. `--secret-stdin` is the explicit
|
|
255
|
+
non-interactive alternative and rejects a TTY.
|
|
256
|
+
|
|
257
|
+
Perform one provider attempt directly from the SDK CLI:
|
|
258
|
+
|
|
259
|
+
```text
|
|
260
|
+
arcane mail send --profile arcane-dev --from "Arcane <verified@example.com>" --report-key <stable-id> --report-stdin
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
The redirected UTF-8 JSON input is bounded to 52 MiB and uses the exact closed
|
|
264
|
+
report shape admitted by the gateway: `type`, `to`, `subject`, and at least one
|
|
265
|
+
of `text` or `html`. Direct CLI sends require one to 50 explicit recipients,
|
|
266
|
+
including for `error` reports. Message content is not accepted in argv and is
|
|
267
|
+
never copied into progress events, results, or normalized errors.
|
|
268
|
+
|
|
269
|
+
The caller must create and retain the 8–128 character `--report-key` before the
|
|
270
|
+
attempt. It is the Resend idempotency key and may be reused only with the same
|
|
271
|
+
byte-equivalent logical report for an intentional retry or reconciliation. The
|
|
272
|
+
CLI performs exactly one attempt and never retries automatically. Exit zero
|
|
273
|
+
requires a successful Resend response containing a valid provider id; that is
|
|
274
|
+
provider acceptance, not an inbox-delivery claim. Timeout, connection loss, or
|
|
275
|
+
cancellation after the provider attempt begins is returned as an ambiguous
|
|
276
|
+
nonzero outcome because the provider may already have accepted the request.
|
|
277
|
+
Cancellation before the attempt exits 130 without sending.
|
|
278
|
+
|
|
279
|
+
Start the authenticated gateway:
|
|
280
|
+
|
|
281
|
+
```text
|
|
282
|
+
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
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
Human output prompts for the separate mail app key with hidden input.
|
|
286
|
+
Non-interactive structured output requires `--app-key-stdin` and redirected
|
|
287
|
+
stdin. The server binds numeric loopback only; the default is
|
|
288
|
+
`127.0.0.1:8025/v1/mail`.
|
|
289
|
+
|
|
290
|
+
The gateway admits only:
|
|
291
|
+
|
|
292
|
+
- its exact numeric-loopback `Host` authority and `/v1/mail` route;
|
|
293
|
+
- an exact configured `Origin`, app id, and constant-time app-key match;
|
|
294
|
+
- JSON requests within the configured byte limit;
|
|
295
|
+
- one to 50 allowlisted recipients, with server-owned fallback recipients for
|
|
296
|
+
error reports (the CLI default sends a recipientless error to every address
|
|
297
|
+
in `--allow-to`);
|
|
298
|
+
- bounded request-body, send-count, aggregate send-byte, and provider-response
|
|
299
|
+
work; and
|
|
300
|
+
- one fixed Resend endpoint with the stable Arcane report key forwarded as
|
|
301
|
+
`Idempotency-Key`.
|
|
302
|
+
|
|
303
|
+
The gateway returns `202` only after Resend returns a valid provider id.
|
|
304
|
+
Transport loss, timeout, an invalid success body, or an unreadable/oversized
|
|
305
|
+
provider response returns an explicit uncertain result and never claims
|
|
306
|
+
delivery. Rate limits, concurrent idempotency requests, permanent validation
|
|
307
|
+
failures, and provider failures are mapped to structured retryable/permanent
|
|
308
|
+
results without returning provider message text.
|
|
309
|
+
|
|
310
|
+
## Operational verification
|
|
311
|
+
|
|
312
|
+
The focused SDK tests use only synthetic keys, addresses, responses, storage,
|
|
313
|
+
and loopback requests. They do not contact Resend or send email. A live
|
|
314
|
+
acceptance send is a separate operational boundary: use a disposable message,
|
|
315
|
+
the real allowlist, and the selected credential profile, then verify both the
|
|
316
|
+
gateway's provider-acceptance id and the intended inbox outcome.
|
|
@@ -104,12 +104,12 @@ import ollama from 'arcane/Ollama';
|
|
|
104
104
|
```
|
|
105
105
|
|
|
106
106
|
The authenticated physical-v1 tree lives entirely beneath `arcane/`. SDK
|
|
107
|
-
`0.
|
|
107
|
+
`0.3.0` projects it from two canonical release receipts:
|
|
108
108
|
|
|
109
109
|
| Canonical receipt | Source authority and protocol | Receipt inventory |
|
|
110
110
|
| --- | --- | --- |
|
|
111
|
-
| `runtime/ARCANE_RUNTIME_RELEASE.json` | `sdk-canonical`; `arcane/1`; builder `arcane-sdk-runtime-v1` |
|
|
112
|
-
| `browser-runtime/ARCANE_SDK_BROWSER_RELEASE.json` | `arcane-os-sdk`; `arcane-sdk-browser-runtime/1`; builder `arcane-sdk-browser-runtime-v1` |
|
|
111
|
+
| `runtime/ARCANE_RUNTIME_RELEASE.json` | `sdk-canonical`; `arcane/1`; builder `arcane-sdk-runtime-v1` | 161 files; 4,159,000 bytes; content SHA-256 `5dab0c9cadd9e5ca97f90ab63ce755940318198d5b1efc0b7b666de4075302e9` |
|
|
112
|
+
| `browser-runtime/ARCANE_SDK_BROWSER_RELEASE.json` | `arcane-os-sdk`; `arcane-sdk-browser-runtime/1`; builder `arcane-sdk-browser-runtime-v1` | 26 files; 9,548,478 bytes; content SHA-256 `0d41531e9a2d6ce97a357eeeebde5fbac8af59639a52f4f682717097b13dc6dc` |
|
|
113
113
|
|
|
114
114
|
The runtime receipt is the current byte authority. Its Arcane OS
|
|
115
115
|
`c540014afe69f14cf5ae60493b7295f36dbcec64` / bundle `0.8.12` record is
|
|
@@ -121,12 +121,12 @@ browser receipt binds `event-pubsub` `6.1.0`, `strong-type` `2.0.0`, and
|
|
|
121
121
|
`arcane/sdk/`. This URL-key separation prevents runtime and SDK dependency
|
|
122
122
|
versions from aliasing one another.
|
|
123
123
|
|
|
124
|
-
Those two receipt inventories contain
|
|
124
|
+
Those two receipt inventories contain 187 entries in total. That sum is a
|
|
125
125
|
release-inventory fact, not an import-map entry count and not an assertion about
|
|
126
|
-
one maintained example. The `0.
|
|
126
|
+
one maintained example. The `0.3.0` map deterministically roots every admitted
|
|
127
127
|
top-level runtime ESM plus the authenticated SDK browser roots, then follows
|
|
128
128
|
those roots for runtime entities and dependency compatibility. Application
|
|
129
|
-
source imports do not select
|
|
129
|
+
source imports do not select a fixed entry count. Its public operation receipt is the
|
|
130
130
|
authority for the exact `imports`, `entryCount`, and `excludedModules`;
|
|
131
131
|
reached-file traversal is internal and is not exposed in that receipt. The
|
|
132
132
|
managed graph exposes `arcane-os/event-manager`, `arcane-os/ai/browser-wasm`,
|
|
@@ -188,8 +188,8 @@ authority with exactly `dependencyName`, `packageSource`,
|
|
|
188
188
|
`canonicalPackageRoot`, `packageName`, `packageVersion`, `runtimeRoot`,
|
|
189
189
|
`browserRuntimeRoot`, `runtimeManifest`, and `browserRuntimeManifest`. A
|
|
190
190
|
workspace may use the canonical dependency name or one exact npm alias such as
|
|
191
|
-
`npm:arcane-os@0.
|
|
192
|
-
exactly as `arcane-os@0.
|
|
191
|
+
`npm:arcane-os@0.3.0`; the physical package manifest must still identify
|
|
192
|
+
exactly as `arcane-os@0.3.0`. Canonical-plus-alias duplicates, multiple aliases,
|
|
193
193
|
links/junctions, indirect package roots, or version drift fail closed.
|
|
194
194
|
|
|
195
195
|
The imported module can be pure browser logic, standard-Web-API logic, or a
|
|
@@ -225,19 +225,19 @@ heartbeat is event telemetry only and never regenerates browser state.
|
|
|
225
225
|
</details>
|
|
226
226
|
|
|
227
227
|
<details>
|
|
228
|
-
<summary>SDK 0.
|
|
228
|
+
<summary>SDK 0.3.0 browser-runtime admission and exact receipt fields</summary>
|
|
229
229
|
|
|
230
230
|
`arcane.lock.json.sdkBrowserRuntime` persists the trusted manifest path,
|
|
231
231
|
`manifestSha256`, `contentSha256`, `builder`, `sdkVersion`, and `source` record.
|
|
232
|
-
For SDK `0.
|
|
232
|
+
For SDK `0.3.0`, the manifest itself records:
|
|
233
233
|
|
|
234
234
|
```text
|
|
235
235
|
manifest: node_modules/arcane-os/browser-runtime/ARCANE_SDK_BROWSER_RELEASE.json
|
|
236
|
-
fileCount:
|
|
237
|
-
totalBytes:
|
|
238
|
-
contentSha256:
|
|
236
|
+
fileCount: 26
|
|
237
|
+
totalBytes: 9548478
|
|
238
|
+
contentSha256: 0d41531e9a2d6ce97a357eeeebde5fbac8af59639a52f4f682717097b13dc6dc
|
|
239
239
|
builder: arcane-sdk-browser-runtime-v1
|
|
240
|
-
sdkVersion: 0.
|
|
240
|
+
sdkVersion: 0.3.0
|
|
241
241
|
source.protocol: arcane-sdk-browser-runtime/1
|
|
242
242
|
source.browserEntry: arcane-os/event-manager
|
|
243
243
|
```
|
|
@@ -263,9 +263,8 @@ Application code should select a normalized role, not an internal protocol.
|
|
|
263
263
|
The exported
|
|
264
264
|
[`getAIProviderRuntime()` singleton](runtime-modules.md#aiproviderruntimejs)
|
|
265
265
|
comes from authenticated runtime bytes and owns independent `llm`, `stt`, and
|
|
266
|
-
`tts` selections.
|
|
267
|
-
speech provider/2 adapters
|
|
268
|
-
below. Current source also adapts selected legacy OpenAI LLM/STT/TTS,
|
|
266
|
+
`tts` selections. SDK `0.3.0` ships browser-WASM LLM and browser
|
|
267
|
+
speech provider/2 adapters and also adapts selected legacy OpenAI LLM/STT/TTS,
|
|
269
268
|
Core-backed Ollama LLM, and admitted Core speech STT/TTS routes into provider/2;
|
|
270
269
|
other native, Core, or cloud routes require an externally supplied compatible
|
|
271
270
|
adapter. The singleton itself is not an authentication or capability token. It
|
|
@@ -301,7 +300,7 @@ compatible completed cache, otherwise it rejects with
|
|
|
301
300
|
`ARCANE_AI_MODEL_OFFLINE_MISS`. Unload releases the active Wllama session but
|
|
302
301
|
does not silently delete the app-owned cache.
|
|
303
302
|
|
|
304
|
-
SDK `0.
|
|
303
|
+
SDK `0.3.0` requires WebGPU. Load requests full offload with exactly 99,999 GPU
|
|
305
304
|
layers and admits the model only after observing an adapter, full layer offload,
|
|
306
305
|
buffer and queue work, and a settled fence. `navigator.gpu` presence alone is
|
|
307
306
|
not readiness. There is no CPU fallback, partial-offload success mode, or
|
|
@@ -310,15 +309,115 @@ silent switch to native/Core/cloud inference.
|
|
|
310
309
|
### Browser speech lifecycle
|
|
311
310
|
|
|
312
311
|
The browser-speech package contains plain-JavaScript authority, DBOPFS store,
|
|
313
|
-
provider, client, and Worker machinery. It
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
312
|
+
provider, client, and Worker machinery. It redistributes no Whisper, Kokoro,
|
|
313
|
+
ONNX, model, voice, third-party license, or corresponding-source payload.
|
|
314
|
+
Default warn-first integrations use `createBrowserSpeechAuthority()` with a
|
|
315
|
+
version-pinned npm/package runtime entry and optional upstream `wasmPaths`;
|
|
316
|
+
the selected runtime then downloads models and voices through its normal
|
|
317
|
+
provider fetch and browser cache behavior after explicit `load()`.
|
|
318
|
+
|
|
319
|
+
`createBrowserSpeechArtifactGraph()` remains the explicit secure/offline option.
|
|
320
|
+
It declares one caller-selected immutable closure with an explicit entrypoint
|
|
321
|
+
and every auxiliary ESM, WASM, model, data, and voice file bound by canonical
|
|
322
|
+
path, materialized media type, optional source media type, byte length, SHA-256,
|
|
323
|
+
immutable starting source/revision, optional redirect-final-origin inventory,
|
|
324
|
+
license declaration, and canonical graph identity.
|
|
325
|
+
|
|
326
|
+
Graph construction rejects ambiguous paths and routes, mutable source
|
|
327
|
+
authorities, undeclared or unmatched static imports, dynamic imports, fetches,
|
|
328
|
+
Cache Storage opens, module Workers, undeclared executable-string construction,
|
|
329
|
+
and incomplete file reachability. `edges.cacheOpens[]` binds the exact module,
|
|
330
|
+
occurrence, policy, cache name, and readable non-JavaScript target paths. The
|
|
331
|
+
two admitted transforms are the exact audited `Function("return this")()`
|
|
332
|
+
compatibility site and typed-array constructor sites later bound to intrinsic
|
|
333
|
+
typed-array prototypes.
|
|
334
|
+
|
|
335
|
+
A source download rejects redirects by default. A file may opt in with a
|
|
336
|
+
nonempty, graph-identity-bound `redirectFinalOrigins` inventory; only that file
|
|
337
|
+
uses Fetch redirect following, and the final response must expose one declared
|
|
338
|
+
HTTPS origin without credentials or a fragment. The immutable starting URL
|
|
339
|
+
remains the source authority, and the final path, query, or signed/expiring URL
|
|
340
|
+
is never persisted or admitted as authority. Fetch exposes only the final CORS
|
|
341
|
+
response, so browser code cannot inspect or authenticate intermediate redirect
|
|
342
|
+
hops. The store then checks the declared source media type, exact length, and
|
|
343
|
+
SHA-256, persists and rehashes every file, rescans the closed module graph, and
|
|
344
|
+
commits the completion manifest last.
|
|
345
|
+
|
|
346
|
+
A valid warm admission performs no source request; it rehashes and rescans every
|
|
347
|
+
cached file and returns `artifact-graph-dbopfs-cache-verified`. Strict
|
|
348
|
+
`offline:true` never calls the source fetch function and returns only
|
|
349
|
+
`artifact-graph-offline-dbopfs-cache-verified`; a miss rejects with
|
|
350
|
+
`ARCANE_AI_ARTIFACT_GRAPH_OFFLINE_CACHE_MISS` /
|
|
351
|
+
`artifact-graph-offline-cache-miss`. Cold and warm admissions are exactly
|
|
352
|
+
`artifact-graph-network-dbopfs-verified` and
|
|
353
|
+
`artifact-graph-dbopfs-cache-verified`. Both cached paths bind redirect origins
|
|
354
|
+
and source media type through graph/manifest identity but never reuse a prior
|
|
355
|
+
final URL.
|
|
356
|
+
|
|
357
|
+
Every admission then uses module-captured native Blob URL functions, ignoring
|
|
358
|
+
the legacy caller `objectUrlFactory`, and reads back each unique `blob:` URL to
|
|
359
|
+
verify its exact identity, media type, byte length, and SHA-256 before
|
|
360
|
+
execution. A fresh cryptographic guard capability binds every rewritten graph
|
|
361
|
+
call for that materialization; it is not caller input, persisted authority, or
|
|
362
|
+
part of the graph identity.
|
|
363
|
+
|
|
364
|
+
The speech Worker establishes a private `MessageChannel` on its first load and
|
|
365
|
+
routes subsequent request, progress, and cancellation settlement through that
|
|
366
|
+
port. In explicit `secure:true` graph mode, scanned runtime edges are rewritten
|
|
367
|
+
through one authenticated guard.
|
|
368
|
+
Fetch and each declared cache-open edge can read only exact graph routes backed
|
|
369
|
+
by already verified object URLs; raw fetch/cache calls and cache writes reject.
|
|
370
|
+
The Worker also denies Function-family constructor escape, string timers,
|
|
371
|
+
IndexedDB, OPFS, and raw `BroadcastChannel`, `EventSource`, `RTCPeerConnection`,
|
|
372
|
+
`ShadowRealm`, `SharedWorker`, `WebSocket`, `WebSocketStream`, `WebTransport`,
|
|
373
|
+
`Worker`, `XMLHttpRequest`, `eval`, and `importScripts` capability. Declared
|
|
374
|
+
nested module Workers start through the SDK role Worker and receive the same
|
|
375
|
+
authenticated graph. Default warn-first operation uses the direct runtime/model
|
|
376
|
+
authority instead; these capability restrictions are not installed and the
|
|
377
|
+
selected upstream runtime keeps ordinary browser fetch/cache behavior. An exact
|
|
378
|
+
secure-graph runtime request alias, including Kokoro's audited
|
|
379
|
+
mutable voice request, is a local route to caller-authenticated bytes and is
|
|
380
|
+
never a source or network authority.
|
|
381
|
+
|
|
382
|
+
Worker operations use `arcane-ai-speech-worker/1`. The public Worker client
|
|
383
|
+
admits only `load`, `use`, `status`, `unload`, and `dispose`; the transport host
|
|
384
|
+
additionally admits only its internal `cancel` control. Every other operation
|
|
385
|
+
rejects with code `ARCANE_AI_INVALID_REQUEST`, message
|
|
386
|
+
`The speech worker operation is not part of its protocol.`, and role-specific
|
|
387
|
+
reason `stt-worker-operation-unknown` or `tts-worker-operation-unknown`.
|
|
388
|
+
Failures use the separate
|
|
389
|
+
`arcane-ai-speech-worker-error/1` envelope. Its exact own-key set is
|
|
390
|
+
`code,message,protocol,reason`, all four must be data properties, and its
|
|
391
|
+
registered code, fixed message, reason, role, and operation must agree. A
|
|
392
|
+
foreign, incomplete, extra-keyed, accessor-bearing, cross-role, or
|
|
393
|
+
cross-operation error envelope is rejected and terminates that role Worker.
|
|
394
|
+
Nested module Workers use
|
|
395
|
+
`arcane-ai-browser-speech-artifact-module-worker/1` and report bootstrap
|
|
396
|
+
rejection only as `artifact-module-worker-bootstrap-rejected`.
|
|
397
|
+
|
|
398
|
+
The exact redirect and source-media error registry is published in the
|
|
399
|
+
[browser-speech reference](ai/browser-speech.md#graph-reasoncode-rule); graph
|
|
400
|
+
errors retain the mechanical exact code pairing
|
|
401
|
+
`ARCANE_AI_` plus the uppercased, underscore-normalized reason.
|
|
402
|
+
|
|
403
|
+
Kokoro is configured through `namespace.env.wasmPaths`; Transformers is
|
|
404
|
+
configured through `namespace.env.backends.onnx.wasm.wasmPaths`. Warn-first
|
|
405
|
+
mode may use a caller-selected version-pinned upstream directory and preserves
|
|
406
|
+
the runtime's browser cache. Secure graph mode uses materialized runtime files
|
|
407
|
+
and its verified outer cache fields. Optional `numThreads` is caller-owned and
|
|
408
|
+
Transformers-STT-only; a Kokoro declaration rejects with
|
|
409
|
+
`ARCANE_AI_KOKORO_ENV_NUM_THREADS_FIELD_NOT_EXPOSED` /
|
|
410
|
+
`kokoro-env-num-threads-field-not-exposed`. Missing or rejected namespace
|
|
411
|
+
shapes fail closed with distinct `*-unavailable` and
|
|
412
|
+
`*-assignment-rejected` reasons for each verified setting; the Worker never
|
|
413
|
+
substitutes a different namespace. The caller also owns dtype, STT input sample
|
|
414
|
+
rate, TTS output sample rate, default voice, and the complete voice inventory;
|
|
415
|
+
the SDK selects no hardware default, runtime, model, or fallback.
|
|
416
|
+
|
|
417
|
+
The SDK is not the distributor of the selected upstream speech packages or
|
|
418
|
+
provider assets and does not republish their legal/source payloads. The
|
|
419
|
+
component record at `browser-runtime/ai/ARCANE_AI_BROWSER_SPEECH_COMPONENTS.json`
|
|
420
|
+
documents resolution only; it is not an execution or publication gate.
|
|
322
421
|
|
|
323
422
|
Whisper `stt` and Kokoro `tts` each own catalog, inspect, status, load, request,
|
|
324
423
|
unload, and dispose state. They load, cancel, unload, fail, and recover
|
|
@@ -327,7 +426,12 @@ begins terminates that role's Worker slot and returns the provider to unloaded;
|
|
|
327
426
|
a later use must load it again. If shared STT `Blob` decoding is cancelled
|
|
328
427
|
before Worker use, the request rejects while the loaded provider remains ready.
|
|
329
428
|
Speech failure neither disables text chat nor retries through another local,
|
|
330
|
-
native, or cloud provider.
|
|
429
|
+
native, or cloud provider. The provider/Worker layer is event-neutral: it
|
|
430
|
+
exposes promises, `AbortSignal`, precise lifecycle/status records, and one
|
|
431
|
+
caller progress callback, but owns no event bus or listener registry. Progress
|
|
432
|
+
is the provider-neutral record
|
|
433
|
+
`{phase,completed,total,unit,heartbeat}`; role is encoded in Worker phase names,
|
|
434
|
+
not added as a second field.
|
|
331
435
|
|
|
332
436
|
### Persistent chat and document context
|
|
333
437
|
|
|
@@ -378,14 +482,19 @@ STT. It declines to request a startup STT load; it does not unload a role alread
|
|
|
378
482
|
started through another explicit lifecycle action. A selected unloaded
|
|
379
483
|
transcription provider remains selected and unloaded until a user lifecycle
|
|
380
484
|
intent or explicit `startTranscription:true` opt-in asks the provider owner to
|
|
381
|
-
load it. Neither state observation nor
|
|
382
|
-
model or selects a fallback.
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
485
|
+
load it. Neither state observation nor either shared speech component imports a
|
|
486
|
+
model or selects a fallback. `speech.html` and `voice-transcription.html` consume
|
|
487
|
+
one shared `createSTTActivationController()` contract for selected, unloaded,
|
|
488
|
+
loading, unloading, error, and ready presentation plus cancelable user intent.
|
|
489
|
+
Both keep capture fail-closed until sticky STT state is exactly ready.
|
|
490
|
+
|
|
491
|
+
Each shared speech component owns an `AbortController` for its STT request and
|
|
492
|
+
passes its signal through `AI.fetchSTT()`. `voice-transcription.html` also adds
|
|
493
|
+
that signal to the existing injected `transcribe(file,context)` callback
|
|
494
|
+
context. Cancel, readiness loss, superseding capture, and component teardown
|
|
495
|
+
abort the owned signal and suppress late delivery. Whether the provider's
|
|
496
|
+
underlying computation stops remains governed by its own cancellation contract.
|
|
497
|
+
User TTS unmute calls `AI.setSpeechMuted(false)` before
|
|
389
498
|
or with its load intent so the runtime records the unmuted lifecycle preference;
|
|
390
499
|
mute calls `AI.setSpeechMuted(true)`, cancels active synthesis, and unloads TTS.
|
|
391
500
|
The selected TTS model catalog owns `defaultVoice`. AI.js uses a saved OpenAI
|
|
@@ -399,13 +508,18 @@ policy, dispatch, and the matching tool-result turn.
|
|
|
399
508
|
<summary>Portable AI protocol disclosure</summary>
|
|
400
509
|
|
|
401
510
|
The normalized runtime protocol is `arcane-ai-runtime/2`; registered adapters
|
|
402
|
-
implement `arcane-ai-provider/2` and must prove matching
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
`
|
|
406
|
-
|
|
407
|
-
`arcane-ai-browser-speech-artifacts/1
|
|
408
|
-
|
|
511
|
+
implement `arcane-ai-provider/2` and must prove matching model authority before
|
|
512
|
+
load. The browser-WASM component receipt is `arcane-ai-browser-wasm/2`; its
|
|
513
|
+
direct controller adapter uses `arcane-ai-adapter/1`, and
|
|
514
|
+
`adaptV1LlmProvider()` projects that surface into the provider/2 LLM role.
|
|
515
|
+
Browser speech stores identify themselves as
|
|
516
|
+
`arcane-ai-browser-speech-artifacts/1`. Legacy authorities retain
|
|
517
|
+
`arcane-ai-model-authority/1`. Authenticated browser-speech graphs use
|
|
518
|
+
`arcane-ai-browser-speech-artifact-graph/1`, kind
|
|
519
|
+
`browser-speech-authenticated-artifact-graph`, and a canonical SHA-256 graph
|
|
520
|
+
identity. Those identifiers describe validation and lifecycle contracts; none
|
|
521
|
+
is by itself a capability grant, publisher-authenticity claim, or complete
|
|
522
|
+
cache receipt.
|
|
409
523
|
|
|
410
524
|
These identifiers normalize lifecycle records. They do not erase provider
|
|
411
525
|
availability: browser providers still require their browser capabilities,
|