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.
@@ -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 `v` and `arcaneVersion` from actual
154
- local resource references, including the managed import map. Other query fields,
155
- fragments, source spelling and unrelated payloads are preserved. The offline
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 authored release state.
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({ receipt, releaseRoot, outputPath, overwrite=false, signal, onEvent }={})
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 and authenticates one deterministic app bundle without extraction.
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.29.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=this.#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=this.#openAIResponseFormat(
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 this.#fetchHTTPResponse(
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=this.#structuredOutputFormat(structuredOutput);
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=this.#openAIResponseFormat(
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 response=await this.#fetchHTTPResponse(
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",