@volter/twin-xai 0.1.1 → 0.1.3
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/dist/src/xai-server.js +8 -4
- package/dist/src/xai-twin.js +5 -0
- package/package.json +3 -3
- package/src/xai-server.ts +8 -4
- package/src/xai-twin.ts +5 -0
package/dist/src/xai-server.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
// Scenario scripting (xai-scenario.ts): a JSON scenario file — via the scenarioPath option,
|
|
11
11
|
// the TWIN_XAI_SCENARIO env var, or `world-xai serve --scenario <path>` — scripts the exact
|
|
12
12
|
// assistant turns for POST /v1/chat/completions. One session per server (nthCall/once state).
|
|
13
|
-
import { serveHttp } from '@volter/world-core';
|
|
13
|
+
import { isReadOnlyRequest, serveHttp, withRequestScopes } from '@volter/world-core';
|
|
14
14
|
import { twinManifest, twinPublicBase, worldNow } from '@volter/world-core';
|
|
15
15
|
import { createXaiScenarioEngine, loadXaiScenarioDocument } from "./xai-scenario.js";
|
|
16
16
|
import { handleXaiTwinRequest } from "./xai-twin.js";
|
|
@@ -68,10 +68,14 @@ function html(body, status = 200) {
|
|
|
68
68
|
* (xai-device-auth-ui.ts), so the OAuth protocol UI is workerd-servable too.
|
|
69
69
|
*/
|
|
70
70
|
export function createXaiTwinFetch(options = {}) {
|
|
71
|
-
const readOnly = options.readOnly ?? false;
|
|
72
71
|
const scenarioPath = options.scenarioPath ?? process.env.TWIN_XAI_SCENARIO;
|
|
73
72
|
const scenarioEngine = scenarioPath ? createXaiScenarioEngine(loadXaiScenarioDocument(scenarioPath)) : undefined;
|
|
74
|
-
|
|
73
|
+
// a read-only request (x-volter-read-only, a World's read token) is a request to a read-only twin: no
|
|
74
|
+
// device grant is started or decided, no token minted, no completion run or usage recorded, and a
|
|
75
|
+
// deferred completion is only PEEKED (xai-twin.ts retrieveDeferredCompletion: the app's one retrieval
|
|
76
|
+
// is never spent by a viewer); the kernel refuses any write that slips past as xAI's own error shape
|
|
77
|
+
return withRequestScopes(async function xaiTwinFetch(request) {
|
|
78
|
+
const readOnly = (options.readOnly ?? false) || isReadOnlyRequest(request);
|
|
75
79
|
const url = new URL(request.url);
|
|
76
80
|
const path = url.pathname + (url.search || '');
|
|
77
81
|
const cleanPath = url.pathname.replace(/\/+$/, '');
|
|
@@ -220,7 +224,7 @@ export function createXaiTwinFetch(options = {}) {
|
|
|
220
224
|
...(scenarioEngine ? { scenarioEngine } : {}),
|
|
221
225
|
});
|
|
222
226
|
return new Response(JSON.stringify(out), { status, headers: { 'content-type': 'application/json', 'x-request-id': 'req_twin', ...(outHeaders ?? {}) } });
|
|
223
|
-
};
|
|
227
|
+
}, { refuse: () => new Response(JSON.stringify({ code: 'method_not_allowed', error: 'this request is read-only' }), { status: 405, headers: { 'content-type': 'application/json' } }) });
|
|
224
228
|
}
|
|
225
229
|
export async function createXaiTwinServer(options) {
|
|
226
230
|
const server = await serveHttp({
|
package/dist/src/xai-twin.js
CHANGED
|
@@ -543,6 +543,11 @@ async function retrieveDeferredCompletion(requestId, req) {
|
|
|
543
543
|
// consume is a WRITE, so it is skipped (the lifecycle simply cannot advance in a read-only
|
|
544
544
|
// mirror, and repeated read-only polls keep serving the still-unconsumed result). §9 found
|
|
545
545
|
// this consume unguarded — a read-only GET was consuming the row.
|
|
546
|
+
// A World's READ token (x-volter-read-only) arrives here as readOnly too, and PEEKS rather than
|
|
547
|
+
// being refused: xAI's own shapes already say everything a viewer may see without spending
|
|
548
|
+
// anything — 202 with an empty body while pending, the completion once ready — so a peek answers
|
|
549
|
+
// exactly the vendor's shape for the row's current state and advances nothing (neither the first
|
|
550
|
+
// poll nor the one-time retrieval). The app's own poll then still gets its one retrieval.
|
|
546
551
|
if (!req.readOnly) {
|
|
547
552
|
await applyTwinWrite(SERVICE, {
|
|
548
553
|
operation: 'deferred_completion.consume',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/twin-xai",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Local xAI (Grok) twin — a faithful, stateful local API plus the Grok Build device-OAuth browser surface. The model is stubbed (deterministic) and Live Search citations are labeled stubs, while the OAuth/account/usage world and API protocol envelopes remain coherent and vendor-shaped. Built on @volter/world-core.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"twin",
|
|
@@ -56,10 +56,10 @@
|
|
|
56
56
|
"react-dom": "^19.2.7"
|
|
57
57
|
},
|
|
58
58
|
"peerDependencies": {
|
|
59
|
-
"@volter/world-core": "2.0.
|
|
59
|
+
"@volter/world-core": "2.0.3"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
|
-
"@volter/world-core": "2.0.
|
|
62
|
+
"@volter/world-core": "2.0.3",
|
|
63
63
|
"@volter/world-tooling": "0.1.0",
|
|
64
64
|
"@ai-sdk/xai": "^3.0.111",
|
|
65
65
|
"ai": "^6.0.0",
|
package/src/xai-server.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
// Scenario scripting (xai-scenario.ts): a JSON scenario file — via the scenarioPath option,
|
|
11
11
|
// the TWIN_XAI_SCENARIO env var, or `world-xai serve --scenario <path>` — scripts the exact
|
|
12
12
|
// assistant turns for POST /v1/chat/completions. One session per server (nthCall/once state).
|
|
13
|
-
import { serveHttp } from '@volter/world-core';
|
|
13
|
+
import { isReadOnlyRequest, serveHttp, withRequestScopes } from '@volter/world-core';
|
|
14
14
|
import { twinManifest, twinPublicBase, worldNow } from '@volter/world-core';
|
|
15
15
|
import { createXaiScenarioEngine, loadXaiScenarioDocument, type XaiScenarioEngine } from './xai-scenario.ts';
|
|
16
16
|
import { handleXaiTwinRequest } from './xai-twin.ts';
|
|
@@ -89,10 +89,14 @@ export interface XaiTwinFetchOptions {
|
|
|
89
89
|
* (xai-device-auth-ui.ts), so the OAuth protocol UI is workerd-servable too.
|
|
90
90
|
*/
|
|
91
91
|
export function createXaiTwinFetch(options: XaiTwinFetchOptions = {}): (request: Request) => Promise<Response> {
|
|
92
|
-
const readOnly = options.readOnly ?? false;
|
|
93
92
|
const scenarioPath = options.scenarioPath ?? process.env.TWIN_XAI_SCENARIO;
|
|
94
93
|
const scenarioEngine: XaiScenarioEngine | undefined = scenarioPath ? createXaiScenarioEngine(loadXaiScenarioDocument(scenarioPath)) : undefined;
|
|
95
|
-
|
|
94
|
+
// a read-only request (x-volter-read-only, a World's read token) is a request to a read-only twin: no
|
|
95
|
+
// device grant is started or decided, no token minted, no completion run or usage recorded, and a
|
|
96
|
+
// deferred completion is only PEEKED (xai-twin.ts retrieveDeferredCompletion: the app's one retrieval
|
|
97
|
+
// is never spent by a viewer); the kernel refuses any write that slips past as xAI's own error shape
|
|
98
|
+
return withRequestScopes(async function xaiTwinFetch(request: Request): Promise<Response> {
|
|
99
|
+
const readOnly = (options.readOnly ?? false) || isReadOnlyRequest(request);
|
|
96
100
|
const url = new URL(request.url);
|
|
97
101
|
const path = url.pathname + (url.search || '');
|
|
98
102
|
const cleanPath = url.pathname.replace(/\/+$/, '');
|
|
@@ -245,7 +249,7 @@ export function createXaiTwinFetch(options: XaiTwinFetchOptions = {}): (request:
|
|
|
245
249
|
...(scenarioEngine ? { scenarioEngine } : {}),
|
|
246
250
|
});
|
|
247
251
|
return new Response(JSON.stringify(out), { status, headers: { 'content-type': 'application/json', 'x-request-id': 'req_twin', ...(outHeaders ?? {}) } });
|
|
248
|
-
};
|
|
252
|
+
}, { refuse: () => new Response(JSON.stringify({ code: 'method_not_allowed', error: 'this request is read-only' }), { status: 405, headers: { 'content-type': 'application/json' } }) });
|
|
249
253
|
}
|
|
250
254
|
|
|
251
255
|
export async function createXaiTwinServer(options: { root?: string; port?: number; readOnly?: boolean; scenarioPath?: string }): Promise<{ port: number; stop: () => void }> {
|
package/src/xai-twin.ts
CHANGED
|
@@ -629,6 +629,11 @@ async function retrieveDeferredCompletion(requestId: string, req: XaiRequest): P
|
|
|
629
629
|
// consume is a WRITE, so it is skipped (the lifecycle simply cannot advance in a read-only
|
|
630
630
|
// mirror, and repeated read-only polls keep serving the still-unconsumed result). §9 found
|
|
631
631
|
// this consume unguarded — a read-only GET was consuming the row.
|
|
632
|
+
// A World's READ token (x-volter-read-only) arrives here as readOnly too, and PEEKS rather than
|
|
633
|
+
// being refused: xAI's own shapes already say everything a viewer may see without spending
|
|
634
|
+
// anything — 202 with an empty body while pending, the completion once ready — so a peek answers
|
|
635
|
+
// exactly the vendor's shape for the row's current state and advances nothing (neither the first
|
|
636
|
+
// poll nor the one-time retrieval). The app's own poll then still gets its one retrieval.
|
|
632
637
|
if (!req.readOnly) {
|
|
633
638
|
await applyTwinWrite(SERVICE, {
|
|
634
639
|
operation: 'deferred_completion.consume',
|