arcane-os 0.29.0 → 0.30.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 +25 -0
- package/README.md +8 -4
- package/browser-runtime/ai/twin-cloud.mjs +230 -0
- package/docs/architecture.md +20 -5
- package/docs/reference/ai/twin-cloud.md +120 -3
- package/docs/reference/asset-versioning.md +7 -6
- package/docs/reference/availability-and-normalization.md +9 -0
- package/docs/reference/inventory/package-api.json +102 -4
- package/docs/reference/pwa.md +4 -3
- package/docs/reference/sdk-api.md +286 -4
- package/package.json +3 -2
- package/runtime/arcane/modules/AI.js +14 -154
- package/schemas/arcane-app-bundle.schema.json +1 -1
- package/src/import-map.mjs +6 -8
- package/src/release-bundle.mjs +100 -12
package/docs/reference/pwa.md
CHANGED
|
@@ -150,9 +150,10 @@ The same bootstrap starts one initially hidden `pwa-install.html` component with
|
|
|
150
150
|
the generated manifest's app name. Component loading and worker registration
|
|
151
151
|
proceed independently.
|
|
152
152
|
|
|
153
|
-
The selected PWA browser delivery removes
|
|
154
|
-
local resource references, including the managed import map.
|
|
155
|
-
|
|
153
|
+
The selected PWA browser delivery removes only SDK-owned `arcaneVersion` fields
|
|
154
|
+
from actual local resource references, including the managed import map. Authored
|
|
155
|
+
fields such as `v`, encoded and repeated fields, empty query segments, fragments,
|
|
156
|
+
source spelling and unrelated payloads are preserved. The offline
|
|
156
157
|
manifest now carries release information. Non-PWA and native delivery retain
|
|
157
158
|
the [existing asset version contract](asset-versioning.md).
|
|
158
159
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
The npm package exposes a Node.js ESM control plane, the portable
|
|
4
4
|
`arcane-os/event-manager`, `arcane-os/logging`, `arcane-os/mail`,
|
|
5
5
|
`arcane-os/preference-store`, `arcane-os/speech-playback`,
|
|
6
|
-
`arcane-os/speech-text`, `arcane-os/ai/tool-text-stream`, and `arcane-os/browser-device` entrypoints, and the browser-only
|
|
6
|
+
`arcane-os/speech-text`, `arcane-os/ai/tool-text-stream`, `arcane-os/ai/twin-cloud`, and `arcane-os/browser-device` entrypoints, and the browser-only
|
|
7
7
|
`arcane-os/pwa`, `arcane-os/ai/browser-wasm` and `arcane-os/ai/browser-speech` entrypoints.
|
|
8
8
|
The `arcane-os/modules/<filename>` and `arcane-os/entities/<filename>` paths
|
|
9
9
|
resolve directly to the existing runtime files, with their actual extension.
|
|
@@ -66,6 +66,7 @@ runtime layouts.
|
|
|
66
66
|
| `arcane-os/pwa` | Nonblocking PWA registration, worker updates, native installation state and a dismissible installation component. |
|
|
67
67
|
| `arcane-os/ai/browser-wasm` | Caller-selected browser-local Wllama inference, complete DBOPFS model storage, streaming, cancellation, and structural tool-call results. |
|
|
68
68
|
| `arcane-os/ai/tool-text-stream` | Shared selected tool-argument text observer for provider integration. |
|
|
69
|
+
| `arcane-os/ai/twin-cloud` | Complete TWiN Cloud requests from Node or a browser with an explicit key/model and shared retry/cancellation behavior. |
|
|
69
70
|
| `arcane-os/ai/browser-speech` | Caller-selected browser-local Whisper STT and Kokoro TTS provider mechanisms, ordinary upstream assets, materialized/native routing, Workers, and cancellation. |
|
|
70
71
|
| `arcane-os/mail` | Portable Mail runtime, durable outbox, complete transport responses, and provider-neutral acceptance contracts. |
|
|
71
72
|
|
|
@@ -121,6 +122,13 @@ browser map are cataloged separately in [Runtime modules](runtime-modules.md).
|
|
|
121
122
|
|
|
122
123
|
| Member | Kind | Import | Group | Availability |
|
|
123
124
|
| --- | --- | --- | --- | --- |
|
|
125
|
+
| `fetchRequest()` | function | `arcane-os/ai/twin-cloud` | TWiN Cloud requests | Node and Browser; remote HTTPS provider |
|
|
126
|
+
| `fetchHTTPResponse()` | function | `arcane-os/ai/twin-cloud` | Shared AI transport integration | Node and Browser |
|
|
127
|
+
| `fetchJSONResponse()` | function | `arcane-os/ai/twin-cloud` | Shared AI transport integration | Node and Browser |
|
|
128
|
+
| `structuredOutputFormat()` | function | `arcane-os/ai/twin-cloud` | Shared AI transport integration | Node and Browser |
|
|
129
|
+
| `openAIResponseFormat()` | function | `arcane-os/ai/twin-cloud` | Shared AI transport integration | Node and Browser |
|
|
130
|
+
| `isAIRequestAbort()` | function | `arcane-os/ai/twin-cloud` | Shared AI transport integration | Node and Browser |
|
|
131
|
+
| `normalizeAIRequestAbort()` | function | `arcane-os/ai/twin-cloud` | Shared AI transport integration | Node and Browser |
|
|
124
132
|
| `APP_BUNDLE_DESCRIPTOR_NAME` | constant | `arcane-os` | Packaging and release bundles | Node |
|
|
125
133
|
| `APP_BUNDLE_EXTENSION` | constant | `arcane-os` | Packaging and release bundles | Node |
|
|
126
134
|
| `APP_BUNDLE_FORMAT` | constant | `arcane-os` | Packaging and release bundles | Node |
|
|
@@ -629,12 +637,17 @@ async function previewNextPrerelease(workspaceRoot) {
|
|
|
629
637
|
|
|
630
638
|
### Overview
|
|
631
639
|
|
|
632
|
-
Writes one deterministic USTAR+gzip external application bundle from the selected
|
|
640
|
+
Writes one deterministic USTAR+gzip external application bundle from the selected
|
|
641
|
+
authored release state. Long or non-ASCII filenames use per-file POSIX PAX `path`
|
|
642
|
+
extensions, preserving their complete UTF-8 spelling and file content. Ordinary
|
|
643
|
+
USTAR entries remain unchanged. Metadata framing is excluded from the manifest's
|
|
644
|
+
logical file inventory. Existing portable-path rules, schema 1, and the
|
|
645
|
+
`ustar+gzip` format identifier remain unchanged.
|
|
633
646
|
|
|
634
647
|
### Signature and result
|
|
635
648
|
|
|
636
649
|
```text
|
|
637
|
-
async createAppReleaseBundle({
|
|
650
|
+
async createAppReleaseBundle({releaseRoot, appDescriptor, outputPath, overwrite=false, signal, onEvent}={})
|
|
638
651
|
```
|
|
639
652
|
|
|
640
653
|
Import it from `arcane-os` or `arcane-os/release-bundle`. The signature above states whether settlement is synchronous or promise-based. The overview and owning group define result authority, side effects, callbacks, events, cancellation, and lifecycle.
|
|
@@ -658,6 +671,9 @@ async function usecreateAppReleaseBundle(...arguments_) {
|
|
|
658
671
|
### Overview
|
|
659
672
|
|
|
660
673
|
Builds the exact 512-byte canonical USTAR header for one validated bundle entry.
|
|
674
|
+
This low-level helper remains a single regular-file header with the USTAR field
|
|
675
|
+
limits. Use `createAppReleaseBundle()` to encode complete long or Unicode paths;
|
|
676
|
+
the bundle writer supplies PAX framing where needed.
|
|
661
677
|
|
|
662
678
|
### Signature and result
|
|
663
679
|
|
|
@@ -1104,7 +1120,14 @@ async function useverifyApp(...arguments_) {
|
|
|
1104
1120
|
|
|
1105
1121
|
### Overview
|
|
1106
1122
|
|
|
1107
|
-
Parses
|
|
1123
|
+
Parses one deterministic app bundle without extraction and checks its structural
|
|
1124
|
+
manifest and complete logical file inventory. It supports ordinary USTAR entries
|
|
1125
|
+
and per-file POSIX PAX `path` extensions. The complete extended path is used by
|
|
1126
|
+
`files` and `readFile()`; the metadata and regular-header placeholder are not
|
|
1127
|
+
payload files. Malformed or orphaned PAX records produce `ARCANE_BUNDLE_INVALID`.
|
|
1128
|
+
Other PAX attributes and archive member types remain unsupported. The existing
|
|
1129
|
+
same-SDK-version condition remains unchanged. An older reader without PAX support
|
|
1130
|
+
must be updated before importing a bundle that requires these path extensions.
|
|
1108
1131
|
|
|
1109
1132
|
### Signature and result
|
|
1110
1133
|
|
|
@@ -7621,6 +7644,265 @@ import {readArcaneDeveloperMode} from 'arcane-os/logging';
|
|
|
7621
7644
|
const developerMode=readArcaneDeveloperMode();
|
|
7622
7645
|
```
|
|
7623
7646
|
|
|
7647
|
+
## fetchRequest()
|
|
7648
|
+
|
|
7649
|
+
### Overview
|
|
7650
|
+
|
|
7651
|
+
Makes one complete TWiN Cloud request with explicit caller-owned credentials
|
|
7652
|
+
and model selection. This named function is independent of the browser
|
|
7653
|
+
`AI` instance method with the same name.
|
|
7654
|
+
|
|
7655
|
+
### Signature and result
|
|
7656
|
+
|
|
7657
|
+
```text
|
|
7658
|
+
async fetchRequest(options={})
|
|
7659
|
+
```
|
|
7660
|
+
|
|
7661
|
+
Import the named function from `arcane-os/ai/twin-cloud`. Supply `twinKey`,
|
|
7662
|
+
`model`, and complete `messages`. The function requires the explicit model;
|
|
7663
|
+
it reads no browser preference or default-model selection. Optional
|
|
7664
|
+
`structuredOutput:true` or `'json'` sends `response_format:{type:'json_object'}`.
|
|
7665
|
+
A supplied JSON Schema sends `response_format:{type:'json_schema',
|
|
7666
|
+
json_schema:{name:'structured_response',strict:true,schema:...}}`. Omission
|
|
7667
|
+
leaves structured output off. Optional `tools`, `toolChoice`,
|
|
7668
|
+
`parallelToolCalls`, and `reasoningEffort` map to their existing TWiN wire
|
|
7669
|
+
fields. Tool fields are sent when `tools` is nonempty; a nonempty
|
|
7670
|
+
`reasoningEffort` is forwarded without selecting a default. No default output
|
|
7671
|
+
cap, tool execution, or provider-response envelope validation is added.
|
|
7672
|
+
|
|
7673
|
+
The return value is the entire parsed provider JSON, not only one choice or
|
|
7674
|
+
message. Optional callbacks are `onRequest(request,id,metadata)` before
|
|
7675
|
+
dispatch and `onResponse(response,id,false)` before successful return;
|
|
7676
|
+
`id` defaults to `Date.now()` and may be supplied by the caller. Request metadata
|
|
7677
|
+
is `{operation:'fetch',transport:'http',destination:'https://inference.do-ai.run/v1/chat/completions'}`.
|
|
7678
|
+
Both callbacks are awaited, and callback
|
|
7679
|
+
failures propagate. Credentials are supplied to transport rather than added to
|
|
7680
|
+
the message or response callback payload.
|
|
7681
|
+
|
|
7682
|
+
### Availability and normalization
|
|
7683
|
+
|
|
7684
|
+
**Node and Browser; Cloud transport.** Uses standard Fetch and cancellation,
|
|
7685
|
+
without DOM, browser profiles, user singletons, or storage initialization.
|
|
7686
|
+
Import starts no request. HTTP `429` whose message contains `overload`
|
|
7687
|
+
(case-insensitive) waits
|
|
7688
|
+
`3000` milliseconds and retries the complete request; other HTTP failures
|
|
7689
|
+
throw their complete parsed JSON or text bodies. A missing key throws
|
|
7690
|
+
`AI_PROVIDER_NOT_CONFIGURED`; a missing model throws `TypeError`. `signal`
|
|
7691
|
+
cancellation during transport, body reading, retry waiting,
|
|
7692
|
+
or callback settlement prevents successful return and uses
|
|
7693
|
+
`ARCANE_AI_REQUEST_ABORTED`. No request history, DBOPFS write, or recurring
|
|
7694
|
+
model context is retained. The caller owns persistence and key configuration.
|
|
7695
|
+
See [TWiN Cloud](ai/twin-cloud.md) for a complete Node JSON-schema example and
|
|
7696
|
+
the unchanged browser AI interface.
|
|
7697
|
+
|
|
7698
|
+
### Example
|
|
7699
|
+
|
|
7700
|
+
```javascript
|
|
7701
|
+
import serverConfig from './server-config.json' with {type: 'json'};
|
|
7702
|
+
import {fetchRequest} from 'arcane-os/ai/twin-cloud';
|
|
7703
|
+
|
|
7704
|
+
const response = await fetchRequest({
|
|
7705
|
+
twinKey: serverConfig.twinKey,
|
|
7706
|
+
model: 'openai-gpt-oss-20b',
|
|
7707
|
+
messages: [{role: 'user', content: 'Describe a moon-powered toaster.'}]
|
|
7708
|
+
});
|
|
7709
|
+
|
|
7710
|
+
console.log(response);
|
|
7711
|
+
```
|
|
7712
|
+
|
|
7713
|
+
`server-config.json` is an application-owned, ignored configuration file;
|
|
7714
|
+
never commit its key or print it in diagnostics. The SDK does not read it.
|
|
7715
|
+
|
|
7716
|
+
## fetchHTTPResponse()
|
|
7717
|
+
|
|
7718
|
+
### Overview
|
|
7719
|
+
|
|
7720
|
+
Shared low-level HTTP owner used by browser AI and the focused TWiN API.
|
|
7721
|
+
Ordinary TWiN callers use `fetchRequest()` instead.
|
|
7722
|
+
|
|
7723
|
+
### Signature and result
|
|
7724
|
+
|
|
7725
|
+
```text
|
|
7726
|
+
async fetchHTTPResponse(url,options)
|
|
7727
|
+
```
|
|
7728
|
+
|
|
7729
|
+
Returns the successful Fetch `Response` without consuming its body. Both URL
|
|
7730
|
+
and Fetch options are caller-supplied; this helper adds no key, model, or
|
|
7731
|
+
request envelope. Non-success responses are read completely as JSON when the
|
|
7732
|
+
content type contains `application/json`, otherwise as text. Only status 429
|
|
7733
|
+
with an overload message repeats after 3000 ms; other error bodies are thrown.
|
|
7734
|
+
|
|
7735
|
+
### Availability and normalization
|
|
7736
|
+
|
|
7737
|
+
**Node and Browser.** Exported from `arcane-os/ai/twin-cloud` for shared SDK
|
|
7738
|
+
integration. `options.signal` cancels Fetch and the overload wait and is checked
|
|
7739
|
+
after response/error-body reads. Cancellation uses
|
|
7740
|
+
`ARCANE_AI_REQUEST_ABORTED`. Overload warnings use the existing shared logger.
|
|
7741
|
+
|
|
7742
|
+
### Example
|
|
7743
|
+
|
|
7744
|
+
```javascript
|
|
7745
|
+
import {fetchHTTPResponse} from 'arcane-os/ai/twin-cloud';
|
|
7746
|
+
|
|
7747
|
+
// The integration supplies its selected endpoint and complete Fetch options.
|
|
7748
|
+
const response = await fetchHTTPResponse(endpoint, requestOptions);
|
|
7749
|
+
```
|
|
7750
|
+
|
|
7751
|
+
## fetchJSONResponse()
|
|
7752
|
+
|
|
7753
|
+
### Overview
|
|
7754
|
+
|
|
7755
|
+
Shared complete JSON-body reader built on `fetchHTTPResponse()`.
|
|
7756
|
+
|
|
7757
|
+
### Signature and result
|
|
7758
|
+
|
|
7759
|
+
```text
|
|
7760
|
+
async fetchJSONResponse(url,options)
|
|
7761
|
+
```
|
|
7762
|
+
|
|
7763
|
+
Returns the full parsed JSON value. A successful response whose content type
|
|
7764
|
+
does not contain `application/json` throws `TypeError`; JSON parser failures
|
|
7765
|
+
propagate. The helper does not select choices or validate a provider envelope.
|
|
7766
|
+
|
|
7767
|
+
### Availability and normalization
|
|
7768
|
+
|
|
7769
|
+
**Node and Browser.** Exported from `arcane-os/ai/twin-cloud` for shared SDK
|
|
7770
|
+
integration; it retains the HTTP owner's retry/cancellation behavior and checks
|
|
7771
|
+
cancellation again after parsing. Ordinary callers use `fetchRequest()`.
|
|
7772
|
+
|
|
7773
|
+
### Example
|
|
7774
|
+
|
|
7775
|
+
```javascript
|
|
7776
|
+
import {fetchJSONResponse} from 'arcane-os/ai/twin-cloud';
|
|
7777
|
+
|
|
7778
|
+
// The integration supplies its selected endpoint and complete Fetch options.
|
|
7779
|
+
const completion = await fetchJSONResponse(endpoint, requestOptions);
|
|
7780
|
+
```
|
|
7781
|
+
|
|
7782
|
+
## structuredOutputFormat()
|
|
7783
|
+
|
|
7784
|
+
### Overview
|
|
7785
|
+
|
|
7786
|
+
Normalizes the existing AI structured-output option without rewriting a schema.
|
|
7787
|
+
|
|
7788
|
+
### Signature and result
|
|
7789
|
+
|
|
7790
|
+
```text
|
|
7791
|
+
structuredOutputFormat(value=false)
|
|
7792
|
+
```
|
|
7793
|
+
|
|
7794
|
+
False, null, and undefined return null. True and `'json'` return `'json'`.
|
|
7795
|
+
A plain object with `Object.prototype` or a null prototype is returned
|
|
7796
|
+
unchanged. Other values throw `AI_STRUCTURED_OUTPUT_INVALID`.
|
|
7797
|
+
|
|
7798
|
+
### Availability and normalization
|
|
7799
|
+
|
|
7800
|
+
**Node and Browser.** Synchronous helper exported from
|
|
7801
|
+
`arcane-os/ai/twin-cloud`; performs no network or storage operation.
|
|
7802
|
+
|
|
7803
|
+
### Example
|
|
7804
|
+
|
|
7805
|
+
```javascript
|
|
7806
|
+
import {structuredOutputFormat} from 'arcane-os/ai/twin-cloud';
|
|
7807
|
+
|
|
7808
|
+
const format = structuredOutputFormat({
|
|
7809
|
+
type: 'object',
|
|
7810
|
+
properties: {text: {type: 'string'}}
|
|
7811
|
+
});
|
|
7812
|
+
```
|
|
7813
|
+
|
|
7814
|
+
## openAIResponseFormat()
|
|
7815
|
+
|
|
7816
|
+
### Overview
|
|
7817
|
+
|
|
7818
|
+
Maps a normalized structured-output choice to chat-completion wire fields.
|
|
7819
|
+
|
|
7820
|
+
### Signature and result
|
|
7821
|
+
|
|
7822
|
+
```text
|
|
7823
|
+
openAIResponseFormat(format)
|
|
7824
|
+
```
|
|
7825
|
+
|
|
7826
|
+
`'json'` becomes `{type:'json_object'}`; a supplied schema becomes
|
|
7827
|
+
`{type:'json_schema',json_schema:{name:'structured_response',strict:true,schema:format}}`.
|
|
7828
|
+
A disabled format returns null. Use `structuredOutputFormat()` first to
|
|
7829
|
+
normalize the public option; this mapper does not validate schema contents.
|
|
7830
|
+
|
|
7831
|
+
### Availability and normalization
|
|
7832
|
+
|
|
7833
|
+
**Node and Browser.** Synchronous helper exported from
|
|
7834
|
+
`arcane-os/ai/twin-cloud`, shared with browser AI and performing no I/O.
|
|
7835
|
+
|
|
7836
|
+
### Example
|
|
7837
|
+
|
|
7838
|
+
```javascript
|
|
7839
|
+
import {structuredOutputFormat, openAIResponseFormat} from 'arcane-os/ai/twin-cloud';
|
|
7840
|
+
|
|
7841
|
+
const responseFormat = openAIResponseFormat(structuredOutputFormat(true));
|
|
7842
|
+
// {type: 'json_object'}
|
|
7843
|
+
```
|
|
7844
|
+
|
|
7845
|
+
## isAIRequestAbort()
|
|
7846
|
+
|
|
7847
|
+
### Overview
|
|
7848
|
+
|
|
7849
|
+
Recognizes the existing AI request cancellation forms.
|
|
7850
|
+
|
|
7851
|
+
### Signature and result
|
|
7852
|
+
|
|
7853
|
+
```text
|
|
7854
|
+
isAIRequestAbort(error,signal)
|
|
7855
|
+
```
|
|
7856
|
+
|
|
7857
|
+
Returns a boolean: true when the signal is aborted, the error name is
|
|
7858
|
+
`AbortError`, or its code is `ARCANE_REQUEST_ABORTED`,
|
|
7859
|
+
`ARCANE_AI_REQUEST_ABORTED`, or `AI_REQUEST_ABORTED`.
|
|
7860
|
+
|
|
7861
|
+
### Availability and normalization
|
|
7862
|
+
|
|
7863
|
+
**Node and Browser.** Synchronous helper exported from
|
|
7864
|
+
`arcane-os/ai/twin-cloud`; it neither changes nor aborts the supplied operation.
|
|
7865
|
+
|
|
7866
|
+
### Example
|
|
7867
|
+
|
|
7868
|
+
```javascript
|
|
7869
|
+
import {isAIRequestAbort} from 'arcane-os/ai/twin-cloud';
|
|
7870
|
+
|
|
7871
|
+
const controller = new AbortController();
|
|
7872
|
+
controller.abort();
|
|
7873
|
+
const cancelled = isAIRequestAbort(undefined, controller.signal);
|
|
7874
|
+
```
|
|
7875
|
+
|
|
7876
|
+
## normalizeAIRequestAbort()
|
|
7877
|
+
|
|
7878
|
+
### Overview
|
|
7879
|
+
|
|
7880
|
+
Keeps cancellation under the shared AI error code while preserving its cause.
|
|
7881
|
+
|
|
7882
|
+
### Signature and result
|
|
7883
|
+
|
|
7884
|
+
```text
|
|
7885
|
+
normalizeAIRequestAbort(error)
|
|
7886
|
+
```
|
|
7887
|
+
|
|
7888
|
+
An existing `ARCANE_AI_REQUEST_ABORTED` error is returned unchanged. Otherwise
|
|
7889
|
+
the result is an Error named `AbortError`, with code
|
|
7890
|
+
`ARCANE_AI_REQUEST_ABORTED`, message `The AI request was cancelled.`, and the
|
|
7891
|
+
supplied value as its cause. The helper returns the error; it does not throw it.
|
|
7892
|
+
|
|
7893
|
+
### Availability and normalization
|
|
7894
|
+
|
|
7895
|
+
**Node and Browser.** Synchronous helper exported from
|
|
7896
|
+
`arcane-os/ai/twin-cloud`; creates no request, storage, or user state.
|
|
7897
|
+
|
|
7898
|
+
### Example
|
|
7899
|
+
|
|
7900
|
+
```javascript
|
|
7901
|
+
import {normalizeAIRequestAbort} from 'arcane-os/ai/twin-cloud';
|
|
7902
|
+
|
|
7903
|
+
const cancelled = normalizeAIRequestAbort(new DOMException('Cancelled', 'AbortError'));
|
|
7904
|
+
```
|
|
7905
|
+
|
|
7624
7906
|
## Data export subpaths
|
|
7625
7907
|
|
|
7626
7908
|
The package also exposes eight JSON Schemas (including `arcane-os/schemas/event-stack.json`) and its package manifest. These are data contracts, not callable JavaScript members. See [schema contracts](../architecture.md) and the files under `schemas/`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "arcane-os",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.30.0",
|
|
4
4
|
"description": "Arcane OS JavaScript SDK, project-local CLI, browser runtime, and repository-portable application packager.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.mjs",
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
"./speech-text": "./browser-runtime/speech-text.mjs",
|
|
39
39
|
"./ai/browser-wasm": "./browser-runtime/ai/browser-wasm.mjs",
|
|
40
40
|
"./ai/tool-text-stream": "./browser-runtime/ai/tool-text-stream.mjs",
|
|
41
|
+
"./ai/twin-cloud": "./browser-runtime/ai/twin-cloud.mjs",
|
|
41
42
|
"./ai/browser-speech": "./browser-runtime/ai/browser-speech.mjs",
|
|
42
43
|
"./mail": "./src/mail-api.mjs",
|
|
43
44
|
"./testing": "./src/testing.mjs",
|
|
@@ -85,7 +86,7 @@
|
|
|
85
86
|
"test": "npm run test:unit && npm run test:functional && npm run test:integration && npm run test:regression",
|
|
86
87
|
"test:release": "node ./bin/arcane-test.mjs test/npm-release.test.mjs",
|
|
87
88
|
"test:unit": "node ./bin/arcane-test.mjs test/app-descriptor.test.mjs test/app-schema.test.mjs test/app-selection.test.mjs test/contracts.test.mjs test/doctor.test.mjs test/mail-credentials.test.mjs test/mail-outbox.test.mjs test/mail-public-api.test.mjs test/mail-send.test.mjs test/mail-transport.test.mjs test/targets.test.mjs test/workspace-operation-lock.test.mjs",
|
|
88
|
-
"test:functional": "node ./bin/arcane-test.mjs test/browser-speech-providers.test.mjs test/browser-wasm-gpu-notice.test.mjs test/browser-wasm-download-resume.test.mjs test/cli.test.mjs test/dbopfs-document-library.test.mjs test/dbopfs.test.mjs test/dev-server.test.mjs test/dev-pwa.test.mjs test/dom-event-instrumentation.test.mjs test/event-manager.test.mjs test/events.test.mjs test/import-map.test.mjs test/mail-cli.test.mjs test/mail-runtime.test.mjs test/mail-server.test.mjs test/modal.test.mjs test/packaging.test.mjs test/pwa-packaging.test.mjs test/pwa-client.test.mjs test/pwa-install.test.mjs test/pwa-worker.test.mjs test/persistent-ai-chat-session.test.mjs test/reference-completeness.test.mjs test/runtime-api-behavior.test.mjs test/runtime.test.mjs test/scaffold.test.mjs test/speech-playback.test.mjs test/site.test.mjs test/update-check.test.mjs",
|
|
89
|
+
"test:functional": "node ./bin/arcane-test.mjs test/browser-speech-providers.test.mjs test/browser-wasm-gpu-notice.test.mjs test/browser-wasm-download-resume.test.mjs test/cli.test.mjs test/dbopfs-document-library.test.mjs test/dbopfs.test.mjs test/dev-server.test.mjs test/dev-pwa.test.mjs test/dom-event-instrumentation.test.mjs test/event-manager.test.mjs test/events.test.mjs test/import-map.test.mjs test/mail-cli.test.mjs test/mail-runtime.test.mjs test/mail-server.test.mjs test/modal.test.mjs test/packaging.test.mjs test/pwa-packaging.test.mjs test/pwa-client.test.mjs test/pwa-install.test.mjs test/pwa-worker.test.mjs test/persistent-ai-chat-session.test.mjs test/reference-completeness.test.mjs test/runtime-api-behavior.test.mjs test/runtime.test.mjs test/scaffold.test.mjs test/speech-playback.test.mjs test/site.test.mjs test/twin-cloud.test.mjs test/update-check.test.mjs",
|
|
89
90
|
"test:integration": "node ./bin/arcane-test.mjs test/installed-package-runtime.test.mjs test/root-app-layout.test.mjs test/integrated-shared.test.mjs test/integrated-workspace.test.mjs test/mail-browser.test.mjs test/native-plan.test.mjs test/native-provider-loader.test.mjs test/npm-release.test.mjs test/release-bundle.test.mjs test/release-capability-smoke.test.mjs test/shared-payload-batch.test.mjs test/tarball.test.mjs test/browser-wasm-cpu.test.mjs test/wllama-webgpu-runtime.test.mjs",
|
|
90
91
|
"test:regression": "node ./bin/arcane-test.mjs test/channel-workflows.test.mjs test/html-import-registration.test.mjs test/logging-regression.test.mjs test/markdown-speech.test.mjs test/prepared-speech.test.mjs test/native-provider-generation.test.mjs test/speech-queue-regression.test.mjs test/testing.test.mjs test/test-sets.test.mjs",
|
|
91
92
|
"check": "node tools/check-source.mjs && npm test",
|
|
@@ -16,6 +16,14 @@ import {
|
|
|
16
16
|
} from './AIProviderRuntime.js';
|
|
17
17
|
import {normalizeOllamaModelIdentifier} from './OllamaModelIdentifier.js';
|
|
18
18
|
import {arcaneLogging} from 'arcane-os/logging';
|
|
19
|
+
import {
|
|
20
|
+
fetchHTTPResponse,
|
|
21
|
+
fetchJSONResponse,
|
|
22
|
+
isAIRequestAbort,
|
|
23
|
+
normalizeAIRequestAbort,
|
|
24
|
+
structuredOutputFormat as normalizeStructuredOutput,
|
|
25
|
+
openAIResponseFormat
|
|
26
|
+
} from 'arcane-os/ai/twin-cloud';
|
|
19
27
|
import {MarkdownSpeech,stripSpeechFormatting} from 'arcane-os/speech-text';
|
|
20
28
|
import {prepareSpeech} from './PreparedSpeech.js';
|
|
21
29
|
import {createToolTextObserver} from 'arcane-os/ai/tool-text-stream';
|
|
@@ -121,24 +129,6 @@ function aiInitializationError(code,reason,message){
|
|
|
121
129
|
return error;
|
|
122
130
|
}
|
|
123
131
|
|
|
124
|
-
function isAIRequestAbort(error,signal){
|
|
125
|
-
return signal?.aborted
|
|
126
|
-
||error?.name==='AbortError'
|
|
127
|
-
||error?.code==='ARCANE_REQUEST_ABORTED'
|
|
128
|
-
||error?.code==='ARCANE_AI_REQUEST_ABORTED'
|
|
129
|
-
||error?.code==='AI_REQUEST_ABORTED';
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
function normalizeAIRequestAbort(error){
|
|
133
|
-
if(error?.code==='ARCANE_AI_REQUEST_ABORTED'){
|
|
134
|
-
return error;
|
|
135
|
-
}
|
|
136
|
-
const normalized=new Error('The AI request was cancelled.',{cause:error});
|
|
137
|
-
normalized.name='AbortError';
|
|
138
|
-
normalized.code='ARCANE_AI_REQUEST_ABORTED';
|
|
139
|
-
return normalized;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
132
|
function normalizeAIReasoningEffort(value){
|
|
143
133
|
if(value===undefined||value===null||value===''){
|
|
144
134
|
return '';
|
|
@@ -3776,69 +3766,6 @@ class AI {
|
|
|
3776
3766
|
return true;
|
|
3777
3767
|
}
|
|
3778
3768
|
|
|
3779
|
-
async #fetchHTTPResponse(url,options){
|
|
3780
|
-
const {signal}=options;
|
|
3781
|
-
const retryDelayMs=3000;
|
|
3782
|
-
try{
|
|
3783
|
-
while(true){
|
|
3784
|
-
if(signal?.aborted){
|
|
3785
|
-
throw normalizeAIRequestAbort(signal.reason);
|
|
3786
|
-
}
|
|
3787
|
-
const response=await fetch(url,options);
|
|
3788
|
-
if(signal?.aborted){
|
|
3789
|
-
throw normalizeAIRequestAbort(signal.reason);
|
|
3790
|
-
}
|
|
3791
|
-
if(response.ok){
|
|
3792
|
-
return response;
|
|
3793
|
-
}
|
|
3794
|
-
|
|
3795
|
-
const contentType=response.headers.get('content-type')||'';
|
|
3796
|
-
const error=contentType.includes('application/json')
|
|
3797
|
-
?await response.json()
|
|
3798
|
-
:await response.text();
|
|
3799
|
-
if(signal?.aborted){
|
|
3800
|
-
throw normalizeAIRequestAbort(signal.reason);
|
|
3801
|
-
}
|
|
3802
|
-
const message=is.string(error)
|
|
3803
|
-
?error
|
|
3804
|
-
:error?.error?.message??error?.message;
|
|
3805
|
-
if(
|
|
3806
|
-
response.status!==429
|
|
3807
|
-
||!is.string(message)
|
|
3808
|
-
||!message.toLowerCase().includes('overload')
|
|
3809
|
-
){
|
|
3810
|
-
throw error;
|
|
3811
|
-
}
|
|
3812
|
-
|
|
3813
|
-
arcaneLogging.warn(
|
|
3814
|
-
`${message}\nRetrying in ${retryDelayMs / 1000} seconds`,
|
|
3815
|
-
error
|
|
3816
|
-
);
|
|
3817
|
-
await new Promise(function waitForOverloadRetry(resolve,reject){
|
|
3818
|
-
function finishRetryDelay(){
|
|
3819
|
-
signal?.removeEventListener('abort',cancelRetryDelay);
|
|
3820
|
-
resolve();
|
|
3821
|
-
}
|
|
3822
|
-
function cancelRetryDelay(){
|
|
3823
|
-
clearTimeout(timer);
|
|
3824
|
-
signal.removeEventListener('abort',cancelRetryDelay);
|
|
3825
|
-
reject(normalizeAIRequestAbort(signal.reason));
|
|
3826
|
-
}
|
|
3827
|
-
const timer=setTimeout(finishRetryDelay,retryDelayMs);
|
|
3828
|
-
signal?.addEventListener('abort',cancelRetryDelay,{once:true});
|
|
3829
|
-
if(signal?.aborted){
|
|
3830
|
-
cancelRetryDelay();
|
|
3831
|
-
}
|
|
3832
|
-
});
|
|
3833
|
-
}
|
|
3834
|
-
}catch(error){
|
|
3835
|
-
if(isAIRequestAbort(error,signal)){
|
|
3836
|
-
throw normalizeAIRequestAbort(error);
|
|
3837
|
-
}
|
|
3838
|
-
throw error;
|
|
3839
|
-
}
|
|
3840
|
-
}
|
|
3841
|
-
|
|
3842
3769
|
#nativeOllama(){
|
|
3843
3770
|
const client=globalThis.Arcane?.ollama;
|
|
3844
3771
|
|
|
@@ -4171,48 +4098,6 @@ class AI {
|
|
|
4171
4098
|
];
|
|
4172
4099
|
}
|
|
4173
4100
|
|
|
4174
|
-
#structuredOutputFormat(value=false){
|
|
4175
|
-
if(value===false||value===null||value===undefined){
|
|
4176
|
-
return null;
|
|
4177
|
-
}
|
|
4178
|
-
if(value===true||value==='json'){
|
|
4179
|
-
return 'json';
|
|
4180
|
-
}
|
|
4181
|
-
if(
|
|
4182
|
-
is.object(value)
|
|
4183
|
-
&&!is.array(value)
|
|
4184
|
-
&&(
|
|
4185
|
-
Object.getPrototypeOf(value)===Object.prototype
|
|
4186
|
-
||Object.getPrototypeOf(value)===null
|
|
4187
|
-
)
|
|
4188
|
-
){
|
|
4189
|
-
return value;
|
|
4190
|
-
}
|
|
4191
|
-
|
|
4192
|
-
const error=new TypeError(
|
|
4193
|
-
'AI structured output must be enabled with true, json, or a JSON Schema object.'
|
|
4194
|
-
);
|
|
4195
|
-
error.code='AI_STRUCTURED_OUTPUT_INVALID';
|
|
4196
|
-
throw error;
|
|
4197
|
-
}
|
|
4198
|
-
|
|
4199
|
-
#openAIResponseFormat(structuredOutputFormat){
|
|
4200
|
-
if(structuredOutputFormat==='json'){
|
|
4201
|
-
return {type:'json_object'};
|
|
4202
|
-
}
|
|
4203
|
-
if(structuredOutputFormat){
|
|
4204
|
-
return {
|
|
4205
|
-
type:'json_schema',
|
|
4206
|
-
json_schema:{
|
|
4207
|
-
name:'structured_response',
|
|
4208
|
-
strict:true,
|
|
4209
|
-
schema:structuredOutputFormat
|
|
4210
|
-
}
|
|
4211
|
-
};
|
|
4212
|
-
}
|
|
4213
|
-
return null;
|
|
4214
|
-
}
|
|
4215
|
-
|
|
4216
4101
|
async #reportRequest(requestHandler,request,id,metadata){
|
|
4217
4102
|
if(!is.function(requestHandler)){
|
|
4218
4103
|
throw new TypeError('AI onRequest callback must be a function.');
|
|
@@ -4762,7 +4647,7 @@ class AI {
|
|
|
4762
4647
|
if(signal?.aborted){
|
|
4763
4648
|
throw normalizeAIRequestAbort();
|
|
4764
4649
|
}
|
|
4765
|
-
const structuredOutputFormat=
|
|
4650
|
+
const structuredOutputFormat=normalizeStructuredOutput(
|
|
4766
4651
|
structuredOutput
|
|
4767
4652
|
);
|
|
4768
4653
|
|
|
@@ -4776,7 +4661,7 @@ class AI {
|
|
|
4776
4661
|
}
|
|
4777
4662
|
|
|
4778
4663
|
if(structuredOutputFormat){
|
|
4779
|
-
request.response_format=
|
|
4664
|
+
request.response_format=openAIResponseFormat(
|
|
4780
4665
|
structuredOutputFormat
|
|
4781
4666
|
);
|
|
4782
4667
|
}
|
|
@@ -4970,7 +4855,7 @@ class AI {
|
|
|
4970
4855
|
destination:this.url
|
|
4971
4856
|
});
|
|
4972
4857
|
const body = JSON.stringify(request);
|
|
4973
|
-
const response=await
|
|
4858
|
+
const response=await fetchHTTPResponse(
|
|
4974
4859
|
this.url,
|
|
4975
4860
|
{
|
|
4976
4861
|
method:'POST',
|
|
@@ -5622,7 +5507,7 @@ class AI {
|
|
|
5622
5507
|
if(signal?.aborted){
|
|
5623
5508
|
throw normalizeAIRequestAbort();
|
|
5624
5509
|
}
|
|
5625
|
-
const structuredOutputFormat=
|
|
5510
|
+
const structuredOutputFormat=normalizeStructuredOutput(structuredOutput);
|
|
5626
5511
|
|
|
5627
5512
|
const normalizedReasoningEffort=normalizeAIReasoningEffort(
|
|
5628
5513
|
reasoningEffort===undefined?this.reasoningEffort:reasoningEffort
|
|
@@ -5634,7 +5519,7 @@ class AI {
|
|
|
5634
5519
|
}
|
|
5635
5520
|
|
|
5636
5521
|
if(structuredOutputFormat){
|
|
5637
|
-
request.response_format=
|
|
5522
|
+
request.response_format=openAIResponseFormat(
|
|
5638
5523
|
structuredOutputFormat
|
|
5639
5524
|
);
|
|
5640
5525
|
}
|
|
@@ -5718,7 +5603,7 @@ class AI {
|
|
|
5718
5603
|
destination:this.url
|
|
5719
5604
|
});
|
|
5720
5605
|
const body = JSON.stringify(request);
|
|
5721
|
-
const
|
|
5606
|
+
const responseJSON=await fetchJSONResponse(
|
|
5722
5607
|
this.url,
|
|
5723
5608
|
{
|
|
5724
5609
|
method:'POST',
|
|
@@ -5729,36 +5614,11 @@ class AI {
|
|
|
5729
5614
|
}
|
|
5730
5615
|
);
|
|
5731
5616
|
|
|
5732
|
-
const contentType=response.headers.get('content-type')||'';
|
|
5733
|
-
|
|
5734
|
-
if(!contentType.includes('application/json')){
|
|
5735
|
-
throw new TypeError(
|
|
5736
|
-
`AI request returned ${contentType||'an unknown content type'} instead of JSON.`
|
|
5737
|
-
);
|
|
5738
|
-
}
|
|
5739
|
-
|
|
5740
|
-
let responseJSON;
|
|
5741
|
-
try{
|
|
5742
|
-
responseJSON=await response.json();
|
|
5743
|
-
}catch(error){
|
|
5744
|
-
if(isAIRequestAbort(error,signal)){
|
|
5745
|
-
throw normalizeAIRequestAbort(error);
|
|
5746
|
-
}
|
|
5747
|
-
throw error;
|
|
5748
|
-
}
|
|
5749
5617
|
if(signal?.aborted){
|
|
5750
5618
|
throw normalizeAIRequestAbort();
|
|
5751
5619
|
}
|
|
5752
|
-
|
|
5753
|
-
if(!response.id){
|
|
5754
|
-
response.id=id;
|
|
5755
|
-
}
|
|
5756
|
-
|
|
5757
|
-
//console.log(responseJSON);
|
|
5758
|
-
//async
|
|
5759
5620
|
normalizeAICompletionToolCalls(responseJSON);
|
|
5760
5621
|
await responseHandler(responseJSON,id,false);
|
|
5761
|
-
//sync
|
|
5762
5622
|
return responseJSON;
|
|
5763
5623
|
}
|
|
5764
5624
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "https://github.com/TheWizardNexus/arcane-os-sdk/schemas/arcane-app-bundle.schema.json",
|
|
4
4
|
"title": "Arcane external application release bundle",
|
|
5
|
-
"description": "Structural manifest for one complete USTAR+gzip application bundle.",
|
|
5
|
+
"description": "Structural manifest for one complete USTAR+gzip application bundle, with per-file POSIX PAX path extensions for long or non-ASCII filenames. PAX framing is excluded from the logical file inventory.",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"required": [
|
|
8
8
|
"schemaVersion",
|