@volter/world-core 2.0.37 → 3.0.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/README.md +4 -5
- package/app-route.cjs +12 -6
- package/app-route.d.cts +1 -1
- package/dist/app-route.cjs +12 -6
- package/dist/app-route.d.cts +1 -1
- package/dist/generated/pack-facts.json +1410 -3069
- package/dist/inject.cjs +64 -9
- package/dist/pack-facts.cjs +44 -0
- package/dist/src/actions.d.ts +3 -3
- package/dist/src/actions.js +22 -16
- package/dist/src/ancestry.d.ts +14 -2
- package/dist/src/ancestry.js +92 -2
- package/dist/src/anthropic-wire.d.ts +39 -0
- package/dist/src/anthropic-wire.js +136 -0
- package/dist/src/bytes.d.ts +7 -0
- package/dist/src/bytes.js +35 -0
- package/dist/src/changeset.d.ts +1 -1
- package/dist/src/changeset.js +0 -0
- package/dist/src/clickhouse/index.d.ts +3 -0
- package/dist/src/clickhouse/index.js +6 -0
- package/dist/src/clickhouse/sql.d.ts +233 -0
- package/dist/src/clickhouse/sql.js +4329 -0
- package/dist/src/clickhouse/types.d.ts +18 -0
- package/dist/src/clickhouse/types.js +47 -0
- package/dist/src/clickhouse/values.d.ts +146 -0
- package/dist/src/clickhouse/values.js +858 -0
- package/dist/src/client-bundle.js +2 -3
- package/dist/src/cors.d.ts +15 -0
- package/dist/src/cors.js +31 -0
- package/dist/src/derived-core.d.ts +487 -24
- package/dist/src/derived-core.js +788 -144
- package/dist/src/derived-real.d.ts +13 -0
- package/dist/src/derived-real.js +518 -0
- package/dist/src/derived.d.ts +35 -1
- package/dist/src/derived.js +61 -9
- package/dist/src/emit.js +1 -2
- package/dist/src/events.d.ts +206 -0
- package/dist/src/events.js +341 -0
- package/dist/src/executor.d.ts +3 -0
- package/dist/src/executor.js +19 -2
- package/dist/src/file-response.d.ts +6 -0
- package/dist/src/file-response.js +30 -0
- package/dist/src/fork.js +3 -2
- package/dist/src/git/history.d.ts +7 -0
- package/dist/src/git/history.js +24 -0
- package/dist/src/git/index.d.ts +1 -0
- package/dist/src/git/index.js +1 -0
- package/dist/src/git/lfs.d.ts +28 -0
- package/dist/src/git/lfs.js +66 -0
- package/dist/src/git/objects.js +3 -8
- package/dist/src/git/smart-http.d.ts +3 -1
- package/dist/src/git/smart-http.js +67 -6
- package/dist/src/graphql-wire.d.ts +29 -0
- package/dist/src/graphql-wire.js +101 -0
- package/dist/src/grpc-wire.d.ts +67 -0
- package/dist/src/grpc-wire.js +170 -0
- package/dist/src/h2.d.ts +40 -0
- package/dist/src/h2.js +656 -0
- package/dist/src/head.d.ts +32 -3
- package/dist/src/head.js +161 -40
- package/dist/src/history.d.ts +1 -1
- package/dist/src/history.js +6 -6
- package/dist/src/hpack.json +1 -0
- package/dist/src/index.d.ts +64 -75
- package/dist/src/index.js +58 -101
- package/dist/src/log.js +28 -19
- package/dist/src/machines.d.ts +50 -0
- package/dist/src/machines.js +151 -0
- package/dist/src/managed-database.d.ts +86 -0
- package/dist/src/managed-database.js +283 -0
- package/dist/src/multipart.d.ts +11 -0
- package/dist/src/multipart.js +51 -0
- package/dist/src/observe.d.ts +15 -5
- package/dist/src/observe.js +23 -9
- package/dist/src/openai-wire.d.ts +108 -0
- package/dist/src/openai-wire.js +337 -0
- package/dist/src/pack-assets.d.ts +3 -4
- package/dist/src/pack-assets.js +15 -10
- package/dist/src/pack-fetch.d.ts +77 -0
- package/dist/src/pack-fetch.js +449 -0
- package/dist/src/pack-paths.d.ts +12 -0
- package/dist/src/pack-paths.js +86 -0
- package/dist/src/packRegistry.d.ts +69 -162
- package/dist/src/packRegistry.js +55 -20
- package/dist/src/people.d.ts +13 -0
- package/dist/src/people.js +18 -0
- package/dist/src/placeholder-image.d.ts +5 -0
- package/dist/src/placeholder-image.js +114 -0
- package/dist/src/protobuf.d.ts +28 -0
- package/dist/src/protobuf.js +332 -0
- package/dist/src/redis/engine.js +1 -1
- package/dist/src/request-scope.d.ts +1 -1
- package/dist/src/request-scope.js +6 -4
- package/dist/src/resource-blob.d.ts +5 -0
- package/dist/src/resource-blob.js +11 -0
- package/dist/src/runtime.d.ts +85 -0
- package/dist/src/runtime.js +104 -0
- package/dist/src/s3/wire.d.ts +60 -0
- package/dist/src/s3/wire.js +157 -0
- package/dist/src/scenario.d.ts +3 -0
- package/dist/src/scenario.js +2 -0
- package/dist/src/schema-sample.d.ts +1 -0
- package/dist/src/schema-sample.js +21 -0
- package/dist/src/sealed-box.d.ts +14 -0
- package/dist/src/sealed-box.js +225 -0
- package/dist/src/serve-http.d.ts +14 -0
- package/dist/src/serve-http.js +27 -3
- package/dist/src/serve.d.ts +6 -0
- package/dist/src/serve.js +69 -14
- package/dist/src/signing.d.ts +135 -0
- package/dist/src/signing.js +222 -0
- package/dist/src/sigv4.d.ts +48 -0
- package/dist/src/sigv4.js +167 -0
- package/dist/src/smtp.d.ts +16 -0
- package/dist/src/smtp.js +72 -0
- package/dist/src/sockets.d.ts +51 -0
- package/dist/src/sockets.js +90 -0
- package/dist/src/state-system.d.ts +1 -0
- package/dist/src/state-system.js +1 -1
- package/dist/src/storage.d.ts +1 -1
- package/dist/src/storage.js +3 -3
- package/dist/src/trace-context.js +1 -1
- package/dist/src/twin-fetch.d.ts +0 -7
- package/dist/src/twin-fetch.js +0 -14
- package/dist/src/vendor-call.d.ts +6 -0
- package/dist/src/vendor-call.js +41 -0
- package/dist/src/world-store.js +1 -1
- package/dist/vendor-hosts.cjs +36 -125
- package/dist/vendor-hosts.d.cts +8 -0
- package/generated/pack-facts.json +1410 -3069
- package/inject.cjs +64 -9
- package/pack-facts.cjs +44 -0
- package/package.json +17 -3
- package/src/actions.ts +23 -16
- package/src/ancestry.ts +74 -2
- package/src/anthropic-wire.ts +137 -0
- package/src/bytes.ts +42 -0
- package/src/changeset.ts +5 -5
- package/src/clickhouse/index.ts +6 -0
- package/src/clickhouse/sql.ts +3059 -0
- package/src/clickhouse/types.ts +44 -0
- package/src/clickhouse/values.ts +697 -0
- package/src/client-bundle.ts +2 -3
- package/src/cors.ts +34 -0
- package/src/derived-core.ts +1013 -146
- package/src/derived-real.ts +434 -0
- package/src/derived.ts +73 -3
- package/src/emit.ts +1 -2
- package/src/events.ts +449 -0
- package/src/executor.ts +24 -2
- package/src/file-response.ts +27 -0
- package/src/fork.ts +3 -2
- package/src/git/history.ts +19 -0
- package/src/git/index.ts +1 -0
- package/src/git/lfs.ts +67 -0
- package/src/git/objects.ts +3 -5
- package/src/git/smart-http.ts +56 -6
- package/src/graphql-wire.ts +106 -0
- package/src/grpc-wire.ts +159 -0
- package/src/h2.ts +627 -0
- package/src/head.ts +132 -41
- package/src/history.ts +6 -6
- package/src/hpack.json +1 -0
- package/src/index.ts +82 -329
- package/src/log.ts +27 -18
- package/src/machines.ts +151 -0
- package/src/managed-database.ts +299 -0
- package/src/multipart.ts +51 -0
- package/src/observe.ts +31 -15
- package/src/openai-wire.ts +371 -0
- package/src/pack-assets.ts +15 -11
- package/src/pack-fetch.ts +458 -0
- package/src/pack-paths.ts +72 -0
- package/src/packRegistry.ts +79 -167
- package/src/people.ts +31 -0
- package/src/placeholder-image.ts +88 -0
- package/src/protobuf.ts +251 -0
- package/src/redis/engine.ts +1 -1
- package/src/request-scope.ts +8 -4
- package/src/resource-blob.ts +13 -0
- package/src/runtime.ts +344 -0
- package/src/s3/wire.ts +172 -0
- package/src/scenario.ts +4 -0
- package/src/schema-sample.ts +24 -0
- package/src/sealed-box.ts +182 -0
- package/src/serve-http.ts +31 -3
- package/src/serve.ts +58 -14
- package/src/signing.ts +231 -0
- package/src/sigv4.ts +158 -0
- package/src/smtp.ts +76 -0
- package/src/sockets.ts +140 -0
- package/src/state-system.ts +2 -2
- package/src/storage.ts +3 -3
- package/src/trace-context.ts +1 -1
- package/src/twin-fetch.ts +0 -20
- package/src/vendor-call.ts +41 -0
- package/src/world-store.ts +1 -1
- package/vendor-hosts.cjs +36 -125
- package/vendor-hosts.d.cts +8 -0
- package/dist/src/mirror-shell.d.ts +0 -2
- package/dist/src/mirror-shell.js +0 -13
- package/dist/src/v1-removed.d.ts +0 -159
- package/dist/src/v1-removed.js +0 -124
- package/src/mirror-shell.ts +0 -15
- package/src/v1-removed.ts +0 -172
package/dist/inject.cjs
CHANGED
|
@@ -13,8 +13,7 @@
|
|
|
13
13
|
// e.g. STRIPE_TWIN_URL=http://127.0.0.1:12111 or a hosted skin URL
|
|
14
14
|
// {twinsUrl}/{org}/{world}/{vendor} with VOLTER_TWINS_KEY set (R7c). Host claims come
|
|
15
15
|
// from pack-facts at runtime — this file carries no per-vendor roster to rot.
|
|
16
|
-
//
|
|
17
|
-
// the consolidated `aws` twin, same as S3_TWIN_URL.
|
|
16
|
+
// The aws pack's service keys (S3_TWIN_URL, SECRETSMANAGER_TWIN_URL) both point at its one twin.
|
|
18
17
|
// …or combined: TWIN_INJECT="stripe=http://127.0.0.1:12111,github=http://127.0.0.1:12120"
|
|
19
18
|
//
|
|
20
19
|
// It patches http/https `request`/`get` and global `fetch` at the process
|
|
@@ -44,6 +43,7 @@ const { createPublicKey, createVerify } = require('crypto');
|
|
|
44
43
|
// serves it) has its own home, shared with the application route
|
|
45
44
|
const { VENDOR_HOSTS, twinEnvStem, twinOrigins } = require('./vendor-hosts.cjs');
|
|
46
45
|
const dns = require('dns');
|
|
46
|
+
const http2 = require('http2');
|
|
47
47
|
|
|
48
48
|
function decodeBase64UrlJson(part) {
|
|
49
49
|
try {
|
|
@@ -130,7 +130,7 @@ installClerkFastifyTwinShim();
|
|
|
130
130
|
// twin that is attached AND whose URL variable is set; the first such pack wins.
|
|
131
131
|
function installPrismaAdapterShim() {
|
|
132
132
|
if (Module.__volterPrismaAdapterShimInstalled) return;
|
|
133
|
-
const { packs } = require('./
|
|
133
|
+
const { packs } = require('./pack-facts.cjs').packFacts();
|
|
134
134
|
const candidates = Object.keys(packs).filter((vendor) => packs[vendor].prismaAdapter).sort();
|
|
135
135
|
if (candidates.length === 0) return;
|
|
136
136
|
Module.__volterPrismaAdapterShimInstalled = true;
|
|
@@ -225,7 +225,7 @@ function resolveTwin(hostname, map, pathname) {
|
|
|
225
225
|
const base = worldBase();
|
|
226
226
|
// a name a vendor's host rule gives a twin running here is that twin's, never an application's (a vendor with no
|
|
227
227
|
// twin here can be one of the World's applications: the product itself)
|
|
228
|
-
const app = appRouteFor(map[APP_INSTANCE], bare, map);
|
|
228
|
+
const app = appRouteFor(map[APP_INSTANCE], bare, map, claimedAppHosts(map));
|
|
229
229
|
if (app && !isTwinOriginHost(bare) && (!base || new URL(base.origin).hostname !== bare)) {
|
|
230
230
|
return { vendor: 'app', origin: app.origin };
|
|
231
231
|
}
|
|
@@ -245,17 +245,18 @@ const { appRouteFor, appForwardHeaders, appRequestOptions, sessionCa } = require
|
|
|
245
245
|
// to a vendor, so each claiming twin lists its own at its door, read here every five seconds from the moment the
|
|
246
246
|
// injector installs (or, in a process that only resolves, the proxy, from its first resolve). A fetch that would be
|
|
247
247
|
// refused while a first read is still in flight waits for it; a name connected moments ago is refused until the next
|
|
248
|
-
// read, as a new DNS record takes a moment.
|
|
248
|
+
// read, as a new DNS record takes a moment. The door's `appHosts` are the names a hosting twin serves as the
|
|
249
|
+
// customer's own application (a domain added to a Vercel project): those route to the application, not the twin.
|
|
249
250
|
const CLAIMERS = {};
|
|
250
251
|
{
|
|
251
|
-
const { packs } = require('./
|
|
252
|
+
const { packs } = require('./pack-facts.cjs').packFacts();
|
|
252
253
|
for (const vendor of Object.keys(packs)) if (packs[vendor].hostsClaimed) CLAIMERS[vendor] = packs[vendor].hostsClaimed.door;
|
|
253
254
|
}
|
|
254
255
|
const CLAIMS = new Map(); // cache: origin -> the hosts its twin last listed; refreshed by the poll below
|
|
255
256
|
function claimsAt(origin, door) {
|
|
256
257
|
const held = CLAIMS.get(origin);
|
|
257
258
|
if (held) return held;
|
|
258
|
-
const entry = { hosts: new Set(), loaded: false, ready: null };
|
|
259
|
+
const entry = { hosts: new Set(), appHosts: new Set(), loaded: false, ready: null };
|
|
259
260
|
let settle;
|
|
260
261
|
entry.ready = new Promise((resolve) => { settle = resolve; });
|
|
261
262
|
CLAIMS.set(origin, entry);
|
|
@@ -266,7 +267,10 @@ function claimsAt(origin, door) {
|
|
|
266
267
|
Promise.resolve()
|
|
267
268
|
.then(() => f(`${origin}${door}`, { headers: attachTwinsKey({}) }))
|
|
268
269
|
.then((r) => (r.ok ? r.json() : null))
|
|
269
|
-
.then((body) => {
|
|
270
|
+
.then((body) => {
|
|
271
|
+
if (body && Array.isArray(body.hosts)) entry.hosts = new Set(body.hosts.map((h) => String(h).toLowerCase()));
|
|
272
|
+
if (body) entry.appHosts = new Set(Array.isArray(body.appHosts) ? body.appHosts.map((h) => String(h).toLowerCase()) : []);
|
|
273
|
+
})
|
|
270
274
|
.catch(() => {})
|
|
271
275
|
.then(() => { entry.loaded = true; settle(); });
|
|
272
276
|
};
|
|
@@ -277,6 +281,15 @@ function claimsAt(origin, door) {
|
|
|
277
281
|
if (timer.unref) timer.unref();
|
|
278
282
|
return entry;
|
|
279
283
|
}
|
|
284
|
+
/** The names the claiming twins of this map serve as the application (their doors' `appHosts`), or null for none. */
|
|
285
|
+
function claimedAppHosts(map) {
|
|
286
|
+
let out = null;
|
|
287
|
+
for (const vendor of Object.keys(CLAIMERS)) {
|
|
288
|
+
if (!map[vendor]) continue;
|
|
289
|
+
for (const h of claimsAt(map[vendor], CLAIMERS[vendor]).appHosts) (out ??= new Set()).add(h);
|
|
290
|
+
}
|
|
291
|
+
return out;
|
|
292
|
+
}
|
|
280
293
|
/** The first read of a claiming twin's door still in flight for this map, or null when every one has landed. */
|
|
281
294
|
function claimsPending(map) {
|
|
282
295
|
const pending = Object.keys(CLAIMERS).filter((v) => map[v]).map((v) => claimsAt(map[v], CLAIMERS[v])).filter((e) => !e.loaded);
|
|
@@ -737,7 +750,7 @@ function socketTargetHosts(args, isTls) {
|
|
|
737
750
|
|
|
738
751
|
function endpointEnvFor(vendor) {
|
|
739
752
|
try {
|
|
740
|
-
const facts = require('./
|
|
753
|
+
const facts = require('./pack-facts.cjs').packFacts().packs[vendor];
|
|
741
754
|
const endpoint = facts && facts.endpointEnv;
|
|
742
755
|
if (!endpoint) return null;
|
|
743
756
|
const templates = Object.keys(endpoint.templates || {});
|
|
@@ -868,6 +881,33 @@ function patchedConnect(originalConnect, isTls) {
|
|
|
868
881
|
let MAP = {};
|
|
869
882
|
let installed = false;
|
|
870
883
|
|
|
884
|
+
/** A request asking to switch protocols (a WebSocket's `Upgrade: websocket`): a vendor's socket a twin serves (Slack's
|
|
885
|
+
* Socket Mode link, Discord's Gateway), which the fetch-backed stand-in cannot carry. */
|
|
886
|
+
function isUpgrade(headers) {
|
|
887
|
+
for (const [k, v] of Object.entries(headers || {})) if (k.toLowerCase() === 'upgrade' && v) return true;
|
|
888
|
+
return false;
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
/** An upgrade to a twinned host, sent as a real request to the twin's own listener (its origin's scheme, host and port,
|
|
892
|
+
* the vendor's path under it), the vendor's host kept in `Host`: the client's upgrade, its socket and its frames are the
|
|
893
|
+
* twin's. The caller's own connection options (a TLS `createConnection`, an agent, a servername) name the vendor and
|
|
894
|
+
* are left behind. Where the evidence stops: a hosted World's front upgrades nothing yet, so this carries a local
|
|
895
|
+
* World's twins only. */
|
|
896
|
+
function upgradeToTwin(origin, url, method, headers, args, callback) {
|
|
897
|
+
const target = new URL(origin);
|
|
898
|
+
const given = args.find((a) => a && typeof a === 'object' && !(a instanceof URL)) || {};
|
|
899
|
+
const options = {};
|
|
900
|
+
for (const [k, v] of Object.entries(given)) {
|
|
901
|
+
if (!['protocol', 'host', 'hostname', 'port', 'path', 'agent', 'createConnection', 'servername', 'defaultPort', 'socketPath', 'headers', 'method', 'href', 'origin', 'search', 'pathname'].includes(k)) options[k] = v;
|
|
902
|
+
}
|
|
903
|
+
Object.assign(options, {
|
|
904
|
+
protocol: target.protocol, hostname: target.hostname, port: target.port || (target.protocol === 'https:' ? 443 : 80),
|
|
905
|
+
path: `${target.pathname.replace(/\/+$/, '')}${url.pathname}${url.search}`, method, headers,
|
|
906
|
+
});
|
|
907
|
+
const request = target.protocol === 'https:' ? ORIGINALS.httpsRequest : ORIGINALS.httpRequest;
|
|
908
|
+
return callback ? request(options, callback) : request(options);
|
|
909
|
+
}
|
|
910
|
+
|
|
871
911
|
function patchedFactory(originalRequest, defaultProtocol) {
|
|
872
912
|
return function patchedRequest(...args) {
|
|
873
913
|
const { url, method, headers, callback } = describe(args, defaultProtocol);
|
|
@@ -879,6 +919,7 @@ function patchedFactory(originalRequest, defaultProtocol) {
|
|
|
879
919
|
if (twin) {
|
|
880
920
|
const fwdHeaders = Object.assign({}, headers);
|
|
881
921
|
if (!fwdHeaders.host && !fwdHeaders.Host) fwdHeaders.host = url.host;
|
|
922
|
+
if (isUpgrade(headers)) return upgradeToTwin(twin.origin, url, method, fwdHeaders, args, callback);
|
|
882
923
|
return fakeClientRequest(twin.origin, method, url.pathname + url.search, fwdHeaders, callback);
|
|
883
924
|
}
|
|
884
925
|
// No twin claims this path — but if configured twins claim the HOST, the path is an unclaimed
|
|
@@ -926,6 +967,7 @@ function install(mapOverride) {
|
|
|
926
967
|
tlsConnect: tls.connect,
|
|
927
968
|
dnsLookup: dns.lookup,
|
|
928
969
|
dnsPromisesLookup: dns.promises.lookup,
|
|
970
|
+
http2Connect: http2.connect,
|
|
929
971
|
};
|
|
930
972
|
|
|
931
973
|
http.request = patchedFactory(ORIGINALS.httpRequest, 'http:');
|
|
@@ -937,6 +979,18 @@ function install(mapOverride) {
|
|
|
937
979
|
tls.connect = patchedConnect(ORIGINALS.tlsConnect, true);
|
|
938
980
|
dns.lookup = patchedLookup(ORIGINALS.dnsLookup);
|
|
939
981
|
dns.promises.lookup = patchedPromisesLookup(ORIGINALS.dnsPromisesLookup);
|
|
982
|
+
// gRPC (architecture, "Other wires": gRPC): an HTTP/2 session to a host a twin serves (grpc-js on google-gax: TLS to
|
|
983
|
+
// webrisk.googleapis.com) connects to that twin's origin in plain text, where the kernel's HTTP/2 server answers it;
|
|
984
|
+
// the client carries on unchanged. Any other authority is the real one's.
|
|
985
|
+
http2.connect = function patchedHttp2Connect(authority, options, listener) {
|
|
986
|
+
let url = null;
|
|
987
|
+
try { url = new URL(typeof authority === 'string' ? authority : authority && authority.href ? authority.href : `https://${authority.hostname || authority.host}`); } catch { url = null; }
|
|
988
|
+
const twin = url && resolveTwin(url.hostname, MAP, '/');
|
|
989
|
+
if (!twin) return ORIGINALS.http2Connect.apply(this, arguments);
|
|
990
|
+
const cb = typeof options === 'function' ? options : listener;
|
|
991
|
+
const opts = typeof options === 'function' || !options ? {} : Object.fromEntries(Object.entries(options).filter(([k]) => !['secureContext', 'servername', 'ca', 'cert', 'key', 'checkServerIdentity', 'ALPNProtocols', 'createConnection', 'rejectUnauthorized'].includes(k)));
|
|
992
|
+
return ORIGINALS.http2Connect.call(this, new URL(twin.origin).origin, opts, cb);
|
|
993
|
+
};
|
|
940
994
|
|
|
941
995
|
if (ORIGINALS.fetch) {
|
|
942
996
|
const originalFetch = ORIGINALS.fetch;
|
|
@@ -1084,6 +1138,7 @@ function restore() {
|
|
|
1084
1138
|
tls.connect = ORIGINALS.tlsConnect;
|
|
1085
1139
|
dns.lookup = ORIGINALS.dnsLookup;
|
|
1086
1140
|
dns.promises.lookup = ORIGINALS.dnsPromisesLookup;
|
|
1141
|
+
http2.connect = ORIGINALS.http2Connect;
|
|
1087
1142
|
installed = false;
|
|
1088
1143
|
MAP = {};
|
|
1089
1144
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// THE PACK FACTS a process routes by (docs/contributing/architecture.md, "The catalog: where twins come from"): the facts
|
|
2
|
+
// this kernel was built with (generated/pack-facts.json, compiled from the packs of its checkout), overlaid by the facts
|
|
3
|
+
// of the packs the World resolved from installed packages. Each released pack carries its own
|
|
4
|
+
// (`generated/pack-facts.json`), and the World's instance file (VOLTER_WORLD_INSTANCE, which every World process already
|
|
5
|
+
// has) names them as `packFacts`. A pack's own facts replace the built-in ones for its vendor. Plain CommonJS, no
|
|
6
|
+
// dependency: the injector preloads it. Read once per process.
|
|
7
|
+
'use strict';
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
|
|
10
|
+
let cached;
|
|
11
|
+
function packFacts() {
|
|
12
|
+
if (cached) return cached;
|
|
13
|
+
const base = require('./generated/pack-facts.json');
|
|
14
|
+
const packs = Object.assign({}, base.packs);
|
|
15
|
+
// the vendors whose facts came from an installed pack (the host rules let those replace a hand entry)
|
|
16
|
+
const installed = [];
|
|
17
|
+
const instance = process.env.VOLTER_WORLD_INSTANCE;
|
|
18
|
+
if (instance) {
|
|
19
|
+
try {
|
|
20
|
+
const files = JSON.parse(fs.readFileSync(instance, 'utf8')).packFacts || [];
|
|
21
|
+
for (const file of files) {
|
|
22
|
+
try { const own = JSON.parse(fs.readFileSync(file, 'utf8')).packs || {}; Object.assign(packs, own); installed.push(...Object.keys(own)); } catch (_) { /* a pack without readable facts keeps the built-in ones */ }
|
|
23
|
+
}
|
|
24
|
+
} catch (_) { /* no instance yet: the built-in facts */ }
|
|
25
|
+
}
|
|
26
|
+
cached = Object.assign({}, base, { packs, installed });
|
|
27
|
+
return cached;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Facts files a process knows before any World instance names them (the runtime booting a World it has not written
|
|
31
|
+
* the instance of): merged as the instance's are, and into the host rules already built. */
|
|
32
|
+
function extendPackFacts(files) {
|
|
33
|
+
const facts = packFacts();
|
|
34
|
+
const added = {};
|
|
35
|
+
for (const file of files || []) {
|
|
36
|
+
try { Object.assign(added, JSON.parse(fs.readFileSync(file, 'utf8')).packs || {}); } catch (_) { /* the facts it has stand */ }
|
|
37
|
+
}
|
|
38
|
+
Object.assign(facts.packs, added);
|
|
39
|
+
// the host rules already built from the facts learn these packs too
|
|
40
|
+
require('./vendor-hosts.cjs').addPackHosts(added);
|
|
41
|
+
return facts;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
module.exports = { packFacts, extendPackFacts };
|
package/dist/src/actions.d.ts
CHANGED
|
@@ -124,7 +124,7 @@ export declare function appendActionIfAbsent(action: TwinAction, root?: string):
|
|
|
124
124
|
* Append `build(n)` as an OCCURRENCE: the nth time this exact write has been made.
|
|
125
125
|
*
|
|
126
126
|
* A local vendor write is an occurrence, not a replay — two calls are two actions, even
|
|
127
|
-
* byte-identical in the same instant (docs/contributing/
|
|
127
|
+
* byte-identical in the same instant (docs/contributing/architecture.md, "The state kernel",
|
|
128
128
|
* "A local write is an occurrence"). `appendActionIfAbsent` cannot express that: its identity is content, and
|
|
129
129
|
* content provably cannot separate "the same request delivered twice" from "the same change made
|
|
130
130
|
* twice". So a caller wanting at-most-once passes an explicit key and uses that function; a caller
|
|
@@ -205,8 +205,8 @@ export declare class OwnerStoreAmbiguousError extends Error {
|
|
|
205
205
|
});
|
|
206
206
|
}
|
|
207
207
|
/**
|
|
208
|
-
* ANOTHER pack's rows, read by contract (architecture A3: one vendor's store split across two packs,
|
|
209
|
-
* read by
|
|
208
|
+
* ANOTHER pack's rows, read by contract (architecture A3: one vendor's store split across two packs, googleoauth's tokens
|
|
209
|
+
* read by webrisk; the reader's manifest declares the pair, `ownerReads`): the
|
|
210
210
|
* owner's tree, from wherever the World keeps the owner's store (`ownerStoreRoots`). Read only: the rows are frozen and
|
|
211
211
|
* shared. It claims no journal identity (the twin answering is the reader).
|
|
212
212
|
*
|
package/dist/src/actions.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
//
|
|
12
12
|
// Projection = observed mirror, then apply each `set` transaction in order,
|
|
13
13
|
// skipping any transaction that was reverted or confirmed.
|
|
14
|
-
import {
|
|
14
|
+
import { withHistoryLock } from "./ancestry.js";
|
|
15
15
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
16
16
|
import { randomUUID } from 'node:crypto';
|
|
17
17
|
import { dirname, join } from 'node:path';
|
|
@@ -91,13 +91,18 @@ function actionIds(service, root) {
|
|
|
91
91
|
memos.set(path, { key, ids });
|
|
92
92
|
return ids;
|
|
93
93
|
}
|
|
94
|
+
/** A service's projection lock, under its history lock: one directory's locks are taken history, projection,
|
|
95
|
+
* actions, events, in that order (architecture, the history lock). */
|
|
96
|
+
function withProjection(service, root, fn) {
|
|
97
|
+
return withHistoryLock(worldPaths(service, root).dir, () => withFileLock(projectionLock(service, root), fn));
|
|
98
|
+
}
|
|
94
99
|
function appendActionRaw(action, root) {
|
|
95
100
|
const path = actionsPath(action.service, root);
|
|
96
101
|
getActiveWorldStore().mkdir(dirname(path));
|
|
97
102
|
const memos = idMemoSlot(path);
|
|
98
103
|
const held = memos.get(path);
|
|
99
104
|
const current = held !== undefined && held.key === logKey(path);
|
|
100
|
-
|
|
105
|
+
withHistoryLock(worldPaths(action.service, root).dir, () => appendDurable(path, `${JSON.stringify(action)}\n`));
|
|
101
106
|
if (current) {
|
|
102
107
|
held.ids.add(action.id);
|
|
103
108
|
held.key = logKey(path);
|
|
@@ -144,9 +149,10 @@ function existingExactAction(action, root) {
|
|
|
144
149
|
}
|
|
145
150
|
return existing;
|
|
146
151
|
}
|
|
147
|
-
/** The request-scoped correlation id (D3):
|
|
148
|
-
*
|
|
149
|
-
|
|
152
|
+
/** The request-scoped correlation id (D3): for the duration of one handler call, the id a pack fetch mints for the
|
|
153
|
+
* request (a caller's `x-twins-request-id` is recorded with each write as provenance, never as the group), or, under
|
|
154
|
+
* the kernel fetch adapter, the wire's `x-twins-request-id`; read by appendAction as the default. */
|
|
155
|
+
// created on first use, never at import: a browser bundle carrying the kernel carries this module and has
|
|
150
156
|
// no AsyncLocalStorage (see serve.ts identitySlot)
|
|
151
157
|
let correlationStore;
|
|
152
158
|
const correlationScope = () => (correlationStore ??= new AsyncLocalStorage());
|
|
@@ -158,7 +164,7 @@ export function appendAction(action, root) {
|
|
|
158
164
|
// compare-and-set, not an advisory validation: checking outside either lock would let another
|
|
159
165
|
// writer invalidate it before this action lands. The shadow basis is stamped under the same
|
|
160
166
|
// lock so the recorded merge base is the mirror the preconditions were checked against.
|
|
161
|
-
return
|
|
167
|
+
return withProjection(action.service, root, () => withFileLock(actionsLock(action.service, root), () => {
|
|
162
168
|
const stamped = withCorrelationId(action);
|
|
163
169
|
assertPreconditions(stamped, root);
|
|
164
170
|
const placeholder = landIfPlaceholderPull(stamped, root);
|
|
@@ -174,7 +180,7 @@ export function appendAction(action, root) {
|
|
|
174
180
|
* writes both land). A reused id with different content fails loudly. */
|
|
175
181
|
export function appendActionIfAbsent(action, root) {
|
|
176
182
|
refuseReadOnlyWrite(action.service);
|
|
177
|
-
return
|
|
183
|
+
return withProjection(action.service, root, () => withFileLock(actionsLock(action.service, root), () => {
|
|
178
184
|
const identified = withCorrelationId(action);
|
|
179
185
|
// An exact retry is already committed. Resolve it before re-evaluating author-time
|
|
180
186
|
// preconditions against the state that first commit intentionally changed — and
|
|
@@ -195,7 +201,7 @@ export function appendActionIfAbsent(action, root) {
|
|
|
195
201
|
* Append `build(n)` as an OCCURRENCE: the nth time this exact write has been made.
|
|
196
202
|
*
|
|
197
203
|
* A local vendor write is an occurrence, not a replay — two calls are two actions, even
|
|
198
|
-
* byte-identical in the same instant (docs/contributing/
|
|
204
|
+
* byte-identical in the same instant (docs/contributing/architecture.md, "The state kernel",
|
|
199
205
|
* "A local write is an occurrence"). `appendActionIfAbsent` cannot express that: its identity is content, and
|
|
200
206
|
* content provably cannot separate "the same request delivered twice" from "the same change made
|
|
201
207
|
* twice". So a caller wanting at-most-once passes an explicit key and uses that function; a caller
|
|
@@ -209,7 +215,7 @@ export function appendActionIfAbsent(action, root) {
|
|
|
209
215
|
*/
|
|
210
216
|
export function appendActionOccurrence(base, root) {
|
|
211
217
|
refuseReadOnlyWrite(base.service);
|
|
212
|
-
return
|
|
218
|
+
return withProjection(base.service, root, () => withFileLock(actionsLock(base.service, root), () => {
|
|
213
219
|
const stamped = occurrenceOf(base, actionIds(base.service, root), root);
|
|
214
220
|
assertPreconditions(stamped, root);
|
|
215
221
|
const placeholder = landIfPlaceholderPull(stamped, root);
|
|
@@ -247,7 +253,7 @@ export function occurrenceId(base, ordinal) {
|
|
|
247
253
|
* appends before releasing the lock.
|
|
248
254
|
*/
|
|
249
255
|
export function decideAndAppendAction(service, decide, root) {
|
|
250
|
-
return
|
|
256
|
+
return withProjection(service, root, () => withFileLock(actionsLock(service, root), () => {
|
|
251
257
|
const resources = projectResources(service, root);
|
|
252
258
|
const decision = decide(resources);
|
|
253
259
|
if (decision.kind === 'skip')
|
|
@@ -368,8 +374,8 @@ function ownerIndex(owner, root) {
|
|
|
368
374
|
return index;
|
|
369
375
|
}
|
|
370
376
|
/**
|
|
371
|
-
* ANOTHER pack's rows, read by contract (architecture A3: one vendor's store split across two packs,
|
|
372
|
-
* read by
|
|
377
|
+
* ANOTHER pack's rows, read by contract (architecture A3: one vendor's store split across two packs, googleoauth's tokens
|
|
378
|
+
* read by webrisk; the reader's manifest declares the pair, `ownerReads`): the
|
|
373
379
|
* owner's tree, from wherever the World keeps the owner's store (`ownerStoreRoots`). Read only: the rows are frozen and
|
|
374
380
|
* shared. It claims no journal identity (the twin answering is the reader).
|
|
375
381
|
*
|
|
@@ -424,13 +430,13 @@ export function confirmAction(opts) {
|
|
|
424
430
|
...(opts.additionalObservations ?? []).map((o) => landedCopy(base, receipt, { subject: o.subject, fields: o.fields })),
|
|
425
431
|
landedCopy(base, receipt, { subject: opts.subject, fields: opts.fields, ...(opts.vendorSubjectId ? { vendorSubjectId: opts.vendorSubjectId } : {}) }),
|
|
426
432
|
];
|
|
427
|
-
|
|
428
|
-
|
|
433
|
+
withProjection(opts.service, opts.root, () => {
|
|
434
|
+
withFileLock(eventsLockPath(paths), () => {
|
|
429
435
|
const held = landedIds(parentEntries(opts.service, opts.root));
|
|
430
436
|
for (const copy of copies)
|
|
431
437
|
if (!held.has(copy.id))
|
|
432
438
|
appendDurable(paths.events, `${JSON.stringify(copy)}\n`);
|
|
433
|
-
})
|
|
439
|
+
});
|
|
434
440
|
dropCheckpoint(opts.service, opts.root);
|
|
435
441
|
});
|
|
436
442
|
const observedEventIds = copies.map((c) => c.id);
|
|
@@ -445,7 +451,7 @@ export function confirmAction(opts) {
|
|
|
445
451
|
*/
|
|
446
452
|
export function revertAction(opts) {
|
|
447
453
|
refuseReadOnlyWrite(opts.service);
|
|
448
|
-
return
|
|
454
|
+
return withProjection(opts.service, opts.root, () => withFileLock(actionsLock(opts.service, opts.root), () => {
|
|
449
455
|
const all = listActions(opts.service, opts.root);
|
|
450
456
|
const target = all.find((a) => a.id === opts.actionId);
|
|
451
457
|
if (target === undefined)
|
package/dist/src/ancestry.d.ts
CHANGED
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
export declare const canonicalStatePath: (path: string) => string;
|
|
2
2
|
/** Synchronous only; use exclusive acquisition without age-stealing a live owner.
|
|
3
|
-
* The coordinator lives outside service, instance and hosted World deletion trees.
|
|
3
|
+
* The coordinator lives outside service, instance and hosted World deletion trees. It is held only for ancestry
|
|
4
|
+
* bookkeeping (generations, pins, branch pointers, removal receipts) and the not-being-removed check: every World
|
|
5
|
+
* sharing the VOLTER_HOME waits on it, so no append, fold or caller's critical section runs inside it, and no state lock
|
|
6
|
+
* is taken inside it (`withStateLock` takes its lock first). */
|
|
4
7
|
export declare function withAncestryLock<T>(fn: () => T): T;
|
|
8
|
+
/** Run `fn` holding the cross-process lock at `lockPath`, a lock of the directory it sits in: taken first, then the
|
|
9
|
+
* coordinator only to check that the directory is not being removed. Re-entrant within a process. */
|
|
10
|
+
export declare function withStateLock<T>(lockPath: string, fn: () => T): T;
|
|
11
|
+
/** A state directory's history lock: its appends, captures, observation batches and folds. A branch's is taken before
|
|
12
|
+
* its ancestors'. */
|
|
13
|
+
export declare function withHistoryLock<T>(directory: string, fn: () => T): T;
|
|
14
|
+
/** Several sibling directories' history locks at once (a World's services, a changeset's targets), in one order. */
|
|
15
|
+
export declare function withHistoryLocks<T>(directories: string[], fn: () => T): T;
|
|
5
16
|
/** Create an identity only for an existing parent. A recreated path gets a new identity. */
|
|
6
17
|
export declare function stateGeneration(dir: string): string;
|
|
7
18
|
/** Must be called in the same critical section as child pointer publication. A crash before
|
|
@@ -18,5 +29,6 @@ export declare function assertNotBeingRemoved(path: string): void;
|
|
|
18
29
|
/** force on scrub bypasses shape checking only, never another branch's ownership. The coordinator is held to decide
|
|
19
30
|
* and record the removal (`check`, the caller's own precondition, runs there too) and to release it, not through the
|
|
20
31
|
* deletion: every World on the machine shares the coordinator, and a large tree's deletion outlasts their writes'
|
|
21
|
-
* patience. The receipt, written first, keeps the path from being pinned or
|
|
32
|
+
* patience. The receipt, written first, keeps the path from being pinned or locked while it is deleted; the deletion
|
|
33
|
+
* then waits on each lock inside the tree in turn, so a holder that checked before the receipt finishes first. */
|
|
22
34
|
export declare function withStateRemoval<T>(path: string, remove: () => T, check?: () => void): T;
|
package/dist/src/ancestry.js
CHANGED
|
@@ -10,7 +10,10 @@ export const canonicalStatePath = (path) => getActiveWorldStore().canonicalPath?
|
|
|
10
10
|
const metaPath = (dir) => join(dir, 'ancestry.json');
|
|
11
11
|
const inside = (base, path) => { const rel = relative(canonicalStatePath(base), canonicalStatePath(path)); return rel === '' || (rel !== '..' && !rel.startsWith(`..${sep}`) && !rel.startsWith(sep)); };
|
|
12
12
|
/** Synchronous only; use exclusive acquisition without age-stealing a live owner.
|
|
13
|
-
* The coordinator lives outside service, instance and hosted World deletion trees.
|
|
13
|
+
* The coordinator lives outside service, instance and hosted World deletion trees. It is held only for ancestry
|
|
14
|
+
* bookkeeping (generations, pins, branch pointers, removal receipts) and the not-being-removed check: every World
|
|
15
|
+
* sharing the VOLTER_HOME waits on it, so no append, fold or caller's critical section runs inside it, and no state lock
|
|
16
|
+
* is taken inside it (`withStateLock` takes its lock first). */
|
|
14
17
|
export function withAncestryLock(fn) {
|
|
15
18
|
const store = getActiveWorldStore();
|
|
16
19
|
const active = entered ??= new WeakSet();
|
|
@@ -28,6 +31,76 @@ export function withAncestryLock(fn) {
|
|
|
28
31
|
}
|
|
29
32
|
}, { retainLiveOwner: true });
|
|
30
33
|
}
|
|
34
|
+
/** The state locks this process holds, per store: taken again inside one, a lock is already ours. */
|
|
35
|
+
let heldLocks;
|
|
36
|
+
const heldBy = (store) => { const map = heldLocks ??= new WeakMap(); let set = map.get(store); if (!set) {
|
|
37
|
+
set = new Set();
|
|
38
|
+
map.set(store, set);
|
|
39
|
+
} return set; };
|
|
40
|
+
/** Run `fn` holding the cross-process lock at `lockPath`, a lock of the directory it sits in: taken first, then the
|
|
41
|
+
* coordinator only to check that the directory is not being removed. Re-entrant within a process. */
|
|
42
|
+
export function withStateLock(lockPath, fn) {
|
|
43
|
+
const store = getActiveWorldStore();
|
|
44
|
+
const held = heldBy(store);
|
|
45
|
+
if (held.has(lockPath))
|
|
46
|
+
return fn();
|
|
47
|
+
if (entered?.has(store))
|
|
48
|
+
throw new Error(`State lock ${lockPath} taken inside the ancestry coordinator: a state lock is taken before it, never inside it`);
|
|
49
|
+
return store.withLock(lockPath, () => {
|
|
50
|
+
withAncestryLock(() => assertNotBeingRemoved(dirname(lockPath)));
|
|
51
|
+
held.add(lockPath);
|
|
52
|
+
try {
|
|
53
|
+
return fn();
|
|
54
|
+
}
|
|
55
|
+
finally {
|
|
56
|
+
held.delete(lockPath);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
/** A state directory's history lock: its appends, captures, observation batches and folds. A branch's is taken before
|
|
61
|
+
* its ancestors'. */
|
|
62
|
+
export function withHistoryLock(directory, fn) {
|
|
63
|
+
return withStateLock(join(canonicalStatePath(directory), 'history.lock'), fn);
|
|
64
|
+
}
|
|
65
|
+
/** A lock file a holder has recorded itself in (`{pid, hostname}`, written before the holder runs anything): a file
|
|
66
|
+
* that only ends in `.lock` (a yarn.lock) is not one. */
|
|
67
|
+
function isHeldLock(text) {
|
|
68
|
+
try {
|
|
69
|
+
const holder = JSON.parse(text ?? '');
|
|
70
|
+
return Number.isInteger(holder.pid);
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/** Several sibling directories' history locks at once (a World's services, a changeset's targets), in one order. */
|
|
77
|
+
export function withHistoryLocks(directories, fn) {
|
|
78
|
+
const sorted = [...new Set(directories.map((d) => canonicalStatePath(d)))].sort();
|
|
79
|
+
const take = (i) => (i === sorted.length ? fn() : withHistoryLock(sorted[i], () => take(i + 1)));
|
|
80
|
+
return take(0);
|
|
81
|
+
}
|
|
82
|
+
/** The locks inside a tree a removal waits on before deleting it. */
|
|
83
|
+
function locksUnder(path) {
|
|
84
|
+
const store = getActiveWorldStore();
|
|
85
|
+
const out = [];
|
|
86
|
+
const walk = (dir) => {
|
|
87
|
+
for (const name of store.list(dir)) {
|
|
88
|
+
if (name === 'node_modules' || name === '.git')
|
|
89
|
+
continue;
|
|
90
|
+
const child = join(dir, name);
|
|
91
|
+
const stat = store.stat(child);
|
|
92
|
+
if (!stat || stat.isSymbolicLink)
|
|
93
|
+
continue;
|
|
94
|
+
if (stat.isDirectory)
|
|
95
|
+
walk(child);
|
|
96
|
+
else if (name.endsWith('.lock') && isHeldLock(store.read(child)))
|
|
97
|
+
out.push(child);
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
if (store.stat(path)?.isDirectory)
|
|
101
|
+
walk(path);
|
|
102
|
+
return out.sort();
|
|
103
|
+
}
|
|
31
104
|
function metadata(dir) {
|
|
32
105
|
const raw = getActiveWorldStore().read(metaPath(dir));
|
|
33
106
|
if (raw === null)
|
|
@@ -209,7 +282,8 @@ export function assertNotBeingRemoved(path) {
|
|
|
209
282
|
/** force on scrub bypasses shape checking only, never another branch's ownership. The coordinator is held to decide
|
|
210
283
|
* and record the removal (`check`, the caller's own precondition, runs there too) and to release it, not through the
|
|
211
284
|
* deletion: every World on the machine shares the coordinator, and a large tree's deletion outlasts their writes'
|
|
212
|
-
* patience. The receipt, written first, keeps the path from being pinned or
|
|
285
|
+
* patience. The receipt, written first, keeps the path from being pinned or locked while it is deleted; the deletion
|
|
286
|
+
* then waits on each lock inside the tree in turn, so a holder that checked before the receipt finishes first. */
|
|
213
287
|
export function withStateRemoval(path, remove, check) {
|
|
214
288
|
const store = getActiveWorldStore();
|
|
215
289
|
const { removal, receipt } = withAncestryLock(() => {
|
|
@@ -225,6 +299,22 @@ export function withStateRemoval(path, remove, check) {
|
|
|
225
299
|
beingRemoved = [...beingRemoved, removal.path];
|
|
226
300
|
return { removal, receipt };
|
|
227
301
|
});
|
|
302
|
+
// Each lock is waited on alone, taken and let go: a holder that checked before the receipt finishes, and a taker
|
|
303
|
+
// after it refuses once it holds the lock (withStateLock), so the removal never holds one lock while waiting on
|
|
304
|
+
// another and no lock order can deadlock it. A wait that fails leaves nothing deleted: the receipt is released.
|
|
305
|
+
const held = heldBy(store);
|
|
306
|
+
try {
|
|
307
|
+
for (const lock of locksUnder(path).filter((l) => !held.has(l)))
|
|
308
|
+
store.withLock(lock, () => undefined);
|
|
309
|
+
}
|
|
310
|
+
catch (error) {
|
|
311
|
+
withAncestryLock(() => {
|
|
312
|
+
if (store.exists(receipt))
|
|
313
|
+
store.remove(receipt);
|
|
314
|
+
beingRemoved = beingRemoved.filter((p) => p !== removal.path);
|
|
315
|
+
});
|
|
316
|
+
throw error;
|
|
317
|
+
}
|
|
228
318
|
const result = remove();
|
|
229
319
|
withAncestryLock(() => {
|
|
230
320
|
if (store.exists(path))
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type WireRequest, type WireTurn } from './openai-wire.js';
|
|
2
|
+
type Row = Record<string, unknown>;
|
|
3
|
+
/** A message's content as text: a string, or its text blocks (and the text of its tool results) joined. */
|
|
4
|
+
export declare const messagesText: (content: unknown) => string;
|
|
5
|
+
/** The last user message's text. */
|
|
6
|
+
export declare function messagesLastUserText(messages: Row[]): string;
|
|
7
|
+
/** The tool results the last message carries, by the names of the tool_use blocks they answer. */
|
|
8
|
+
export declare function messagesToolResults(messages: Row[]): string[];
|
|
9
|
+
/** A Messages request as a scenario reads it (the OpenAI wire's `WireRequest`): its model, its system and messages'
|
|
10
|
+
* text, its last user text, the tools it offers and the tool results it ends with. */
|
|
11
|
+
export declare function messagesWireRequest(body: Row): WireRequest;
|
|
12
|
+
/** The labeled stub for a Messages request: with tools offered (`tool_choice` not `none`) and the last message not a
|
|
13
|
+
* tool's result, it calls the tool the choice names, else the first, with input its schema admits; with a JSON schema
|
|
14
|
+
* for the output, it answers a value the schema admits; otherwise it echoes the last user message. `label` names the
|
|
15
|
+
* twin. */
|
|
16
|
+
export declare function messagesStubTurn(body: Row, label: string): WireTurn;
|
|
17
|
+
export type MessageShape = {
|
|
18
|
+
id: string;
|
|
19
|
+
model: string;
|
|
20
|
+
turn: WireTurn;
|
|
21
|
+
body: Row;
|
|
22
|
+
/** each tool_use block's id, and the thinking block's signature */
|
|
23
|
+
toolId: (i: number) => string;
|
|
24
|
+
signature?: (thinking: string) => string;
|
|
25
|
+
/** usage fields the vendor adds (service_tier, the prompt cache's counts) */
|
|
26
|
+
usage?: Row;
|
|
27
|
+
};
|
|
28
|
+
/** A message (`type: message`): a thinking block when the request turns thinking on, then the
|
|
29
|
+
* text, then the tool_use blocks; its stop reason, and its usage estimated from the request and the answer. A text
|
|
30
|
+
* longer than `max_tokens` is cut there and stops `max_tokens`. */
|
|
31
|
+
export declare function message(s: MessageShape): Row;
|
|
32
|
+
/** A message as the stream's events: message_start (its content empty), per block its start, deltas and stop (a ping
|
|
33
|
+
* after the first start), message_delta with the stop reason and the output usage, message_stop. `extra` adds fields
|
|
34
|
+
* a vendor puts on the two closing events (Merge's `routing`). */
|
|
35
|
+
export declare function messageSSE(m: Row, extra?: Row, options?: {
|
|
36
|
+
ping?: boolean;
|
|
37
|
+
completeToolInput?: boolean;
|
|
38
|
+
}): string;
|
|
39
|
+
export {};
|