@superatomai/sdk-node 0.0.92-dsp → 0.0.94-dsp
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 +942 -942
- package/dist/index.d.mts +212 -800
- package/dist/index.d.ts +212 -800
- package/dist/index.js +411 -10
- package/dist/index.mjs +407 -8
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -45,6 +45,8 @@ __export(index_exports, {
|
|
|
45
45
|
MainAgent: () => MainAgent,
|
|
46
46
|
MissingRequestContextError: () => MissingRequestContextError,
|
|
47
47
|
NUMERIC_KEYS: () => NUMERIC_KEYS,
|
|
48
|
+
PARCEL_THRESHOLD_BYTES: () => PARCEL_THRESHOLD_BYTES,
|
|
49
|
+
ParcelFile: () => ParcelFile,
|
|
48
50
|
STORAGE_CONFIG: () => STORAGE_CONFIG,
|
|
49
51
|
ScriptMatcher: () => ScriptMatcher,
|
|
50
52
|
ScriptStore: () => ScriptStore,
|
|
@@ -124,7 +126,7 @@ __export(index_exports, {
|
|
|
124
126
|
writeShims: () => writeShims
|
|
125
127
|
});
|
|
126
128
|
module.exports = __toCommonJS(index_exports);
|
|
127
|
-
var
|
|
129
|
+
var import_crypto8 = require("crypto");
|
|
128
130
|
|
|
129
131
|
// src/websocket.ts
|
|
130
132
|
var import_ws = __toESM(require("ws"));
|
|
@@ -3027,6 +3029,7 @@ async function handleDataRequestInner(data, collections, sendMessage, tools) {
|
|
|
3027
3029
|
let requestId;
|
|
3028
3030
|
let collection;
|
|
3029
3031
|
let op;
|
|
3032
|
+
const fromId = typeof data?.from?.id === "string" ? data.from.id : void 0;
|
|
3030
3033
|
try {
|
|
3031
3034
|
const dataRequest = DataRequestMessageSchema.parse(data);
|
|
3032
3035
|
const { id, payload } = dataRequest;
|
|
@@ -3037,13 +3040,13 @@ async function handleDataRequestInner(data, collections, sendMessage, tools) {
|
|
|
3037
3040
|
if (!collections[collection]) {
|
|
3038
3041
|
sendDataResponse(id, collection, op, null, {
|
|
3039
3042
|
error: `Collection '${collection}' not found`
|
|
3040
|
-
}, sendMessage);
|
|
3043
|
+
}, sendMessage, fromId);
|
|
3041
3044
|
return;
|
|
3042
3045
|
}
|
|
3043
3046
|
if (!collections[collection][op]) {
|
|
3044
3047
|
sendDataResponse(id, collection, op, null, {
|
|
3045
3048
|
error: `Operation '${op}' not found for collection '${collection}'`
|
|
3046
|
-
}, sendMessage);
|
|
3049
|
+
}, sendMessage, fromId);
|
|
3047
3050
|
return;
|
|
3048
3051
|
}
|
|
3049
3052
|
const startTime = performance.now();
|
|
@@ -3115,21 +3118,22 @@ async function handleDataRequestInner(data, collections, sendMessage, tools) {
|
|
|
3115
3118
|
logger.warn(`UIBlock ${uiBlockId} not found in threads`);
|
|
3116
3119
|
}
|
|
3117
3120
|
}
|
|
3118
|
-
sendDataResponse(id, collection, op, result, { executionMs }, sendMessage);
|
|
3121
|
+
sendDataResponse(id, collection, op, result, { executionMs }, sendMessage, fromId);
|
|
3119
3122
|
} catch (error) {
|
|
3120
3123
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
3121
3124
|
logger.error("Failed to handle data request:", error);
|
|
3122
3125
|
if (requestId) {
|
|
3123
3126
|
sendDataResponse(requestId, collection || "unknown", op || "unknown", null, {
|
|
3124
3127
|
error: errorMsg
|
|
3125
|
-
}, sendMessage);
|
|
3128
|
+
}, sendMessage, fromId);
|
|
3126
3129
|
}
|
|
3127
3130
|
}
|
|
3128
3131
|
}
|
|
3129
|
-
function sendDataResponse(id, collection, op, data, meta, sendMessage) {
|
|
3132
|
+
function sendDataResponse(id, collection, op, data, meta, sendMessage, fromId) {
|
|
3130
3133
|
const response = {
|
|
3131
3134
|
id,
|
|
3132
3135
|
from: { type: "data-agent" },
|
|
3136
|
+
to: fromId ? { id: fromId } : void 0,
|
|
3133
3137
|
type: "DATA_RES",
|
|
3134
3138
|
payload: {
|
|
3135
3139
|
collection,
|
|
@@ -24832,6 +24836,14 @@ function readCodexConfig(env) {
|
|
|
24832
24836
|
return "";
|
|
24833
24837
|
}
|
|
24834
24838
|
}
|
|
24839
|
+
var CODEX_PATH_ENV = "SA_CODEX_PATH";
|
|
24840
|
+
function codexPathOverride(env = process.env, exists = fs8.existsSync) {
|
|
24841
|
+
const p = env[CODEX_PATH_ENV]?.trim();
|
|
24842
|
+
if (!p) return void 0;
|
|
24843
|
+
if (exists(p)) return p;
|
|
24844
|
+
console.warn(`[codex] ${CODEX_PATH_ENV}=${p} does not exist; using the codex bundled with @openai/codex-sdk`);
|
|
24845
|
+
return void 0;
|
|
24846
|
+
}
|
|
24835
24847
|
function codexDefaultProvider(env = process.env, configToml) {
|
|
24836
24848
|
const choice = env.CODEX_MODEL_PROVIDER?.trim();
|
|
24837
24849
|
if (choice && choice !== "openrouter") return null;
|
|
@@ -24893,7 +24905,9 @@ function createCodexSession(opts) {
|
|
|
24893
24905
|
if (thread) return thread;
|
|
24894
24906
|
const { Codex } = await loadCodex();
|
|
24895
24907
|
const config2 = codexRunConfig(opts, provider);
|
|
24908
|
+
const codexPath = codexPathOverride();
|
|
24896
24909
|
const codex = new Codex({
|
|
24910
|
+
...codexPath ? { codexPathOverride: codexPath } : {},
|
|
24897
24911
|
...opts.apiKey ? { apiKey: opts.apiKey } : {},
|
|
24898
24912
|
...opts.baseUrl ? { baseUrl: opts.baseUrl } : {},
|
|
24899
24913
|
env: buildEnv(opts, provider),
|
|
@@ -28351,6 +28365,355 @@ var ReportManager = class {
|
|
|
28351
28365
|
}
|
|
28352
28366
|
};
|
|
28353
28367
|
|
|
28368
|
+
// src/parcels/index.ts
|
|
28369
|
+
var import_crypto7 = require("crypto");
|
|
28370
|
+
var PARCEL_THRESHOLD_BYTES = 256 * 1024;
|
|
28371
|
+
var MAX_INLINE_BYTES = 900 * 1024;
|
|
28372
|
+
var MAX_PARCEL_BYTES = 95 * 1024 * 1024;
|
|
28373
|
+
var NEVER_PARCEL = /* @__PURE__ */ new Set(["PING", "PONG", "BUNDLE_CHUNK", "BUNDLE_CSS_CHUNK"]);
|
|
28374
|
+
var RETRY_DELAYS_MS = [500, 1500];
|
|
28375
|
+
var REQUEST_TIMEOUT_MS = 6e4;
|
|
28376
|
+
var MAX_DEPTH = 32;
|
|
28377
|
+
var PARCEL_FIELD = "__parcel";
|
|
28378
|
+
var FILE_FIELD = "__parcelFile";
|
|
28379
|
+
var isRef = (ref) => !!ref && typeof ref === "object" && typeof ref.projectId === "string" && typeof ref.hash === "string" && typeof ref.ext === "string" && typeof ref.size === "number";
|
|
28380
|
+
function parcelRefOf(payload) {
|
|
28381
|
+
const ref = payload?.[PARCEL_FIELD];
|
|
28382
|
+
return isRef(ref) ? ref : null;
|
|
28383
|
+
}
|
|
28384
|
+
var fileRefOf = (value) => {
|
|
28385
|
+
const ref = value?.[FILE_FIELD];
|
|
28386
|
+
return typeof ref?.contentType === "string" && isRef(ref) ? ref : null;
|
|
28387
|
+
};
|
|
28388
|
+
var sha256Hex = (bytes) => (0, import_crypto7.createHash)("sha256").update(bytes).digest("hex");
|
|
28389
|
+
var sleep = (ms) => new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
28390
|
+
var ParcelHttpError = class extends Error {
|
|
28391
|
+
constructor(message, status) {
|
|
28392
|
+
super(message);
|
|
28393
|
+
this.status = status;
|
|
28394
|
+
}
|
|
28395
|
+
};
|
|
28396
|
+
var retryable = (err) => !(err instanceof ParcelHttpError) || err.status >= 500 || err.status === 408 || err.status === 429;
|
|
28397
|
+
async function withRetries(what, run) {
|
|
28398
|
+
let lastError;
|
|
28399
|
+
for (let attempt = 0; attempt <= RETRY_DELAYS_MS.length; attempt++) {
|
|
28400
|
+
try {
|
|
28401
|
+
return await run();
|
|
28402
|
+
} catch (err) {
|
|
28403
|
+
lastError = err;
|
|
28404
|
+
if (!retryable(err) || attempt === RETRY_DELAYS_MS.length) break;
|
|
28405
|
+
logger.warn(`[parcels] ${what} failed (attempt ${attempt + 1}), retrying: ${err?.message ?? err}`);
|
|
28406
|
+
await sleep(RETRY_DELAYS_MS[attempt] ?? 0);
|
|
28407
|
+
}
|
|
28408
|
+
}
|
|
28409
|
+
throw lastError;
|
|
28410
|
+
}
|
|
28411
|
+
var ParcelClient = class {
|
|
28412
|
+
constructor(opts) {
|
|
28413
|
+
this.opts = opts;
|
|
28414
|
+
this.base = opts.apiUrl.replace(/\/+$/, "");
|
|
28415
|
+
this.fetchImpl = opts.fetchImpl ?? fetch;
|
|
28416
|
+
this.timeoutMs = opts.timeoutMs ?? REQUEST_TIMEOUT_MS;
|
|
28417
|
+
}
|
|
28418
|
+
url(ref) {
|
|
28419
|
+
return `${this.base}/parcels/${encodeURIComponent(ref.projectId)}/${ref.hash}.${ref.ext}`;
|
|
28420
|
+
}
|
|
28421
|
+
async call(url, init) {
|
|
28422
|
+
const res = await this.fetchImpl(url, {
|
|
28423
|
+
...init,
|
|
28424
|
+
headers: { ...init.headers, Authorization: `Bearer ${this.opts.apiKey}` },
|
|
28425
|
+
signal: AbortSignal.timeout(this.timeoutMs)
|
|
28426
|
+
});
|
|
28427
|
+
if (!res.ok) {
|
|
28428
|
+
const text = await res.text().catch(() => "");
|
|
28429
|
+
throw new ParcelHttpError(`HTTP ${res.status}${text ? ` ${text.slice(0, 200)}` : ""}`, res.status);
|
|
28430
|
+
}
|
|
28431
|
+
return res;
|
|
28432
|
+
}
|
|
28433
|
+
/** Stores the bytes and returns the receipt for them. */
|
|
28434
|
+
async upload(bytes, ext = "json", contentType = "application/json") {
|
|
28435
|
+
if (bytes.byteLength > MAX_PARCEL_BYTES) {
|
|
28436
|
+
throw new Error(`Parcel too large (${bytes.byteLength} bytes, limit ${MAX_PARCEL_BYTES})`);
|
|
28437
|
+
}
|
|
28438
|
+
const ref = { projectId: this.opts.projectId, hash: sha256Hex(bytes), ext, size: bytes.byteLength };
|
|
28439
|
+
await withRetries(
|
|
28440
|
+
"upload",
|
|
28441
|
+
() => this.call(this.url(ref), { method: "PUT", headers: { "Content-Type": contentType }, body: bytes })
|
|
28442
|
+
);
|
|
28443
|
+
return ref;
|
|
28444
|
+
}
|
|
28445
|
+
/** The bytes behind a receipt, checked against its hash. */
|
|
28446
|
+
async download(ref) {
|
|
28447
|
+
return withRetries("download", async () => {
|
|
28448
|
+
const res = await this.call(this.url(ref), { method: "GET" });
|
|
28449
|
+
const bytes = new Uint8Array(await res.arrayBuffer());
|
|
28450
|
+
if (sha256Hex(bytes) !== ref.hash) {
|
|
28451
|
+
throw new ParcelHttpError("Parcel content does not match its hash", 502);
|
|
28452
|
+
}
|
|
28453
|
+
return bytes;
|
|
28454
|
+
});
|
|
28455
|
+
}
|
|
28456
|
+
};
|
|
28457
|
+
var ParcelFile = class {
|
|
28458
|
+
constructor(ref, client) {
|
|
28459
|
+
this.ref = ref;
|
|
28460
|
+
this.client = client;
|
|
28461
|
+
this.name = ref.name;
|
|
28462
|
+
this.size = ref.size;
|
|
28463
|
+
this.contentType = ref.contentType;
|
|
28464
|
+
}
|
|
28465
|
+
/** The file's bytes, checked against their hash. Throws if they cannot be fetched. */
|
|
28466
|
+
async download() {
|
|
28467
|
+
if (!this.client) throw new Error("Cannot download files: SA_API_URL or the API key is not set");
|
|
28468
|
+
return this.client.download(this.ref);
|
|
28469
|
+
}
|
|
28470
|
+
toJSON() {
|
|
28471
|
+
return { [FILE_FIELD]: this.ref };
|
|
28472
|
+
}
|
|
28473
|
+
};
|
|
28474
|
+
var isBinary = (v) => v instanceof Uint8Array || v instanceof ArrayBuffer;
|
|
28475
|
+
var isPlain = (v) => {
|
|
28476
|
+
if (!v || typeof v !== "object") return false;
|
|
28477
|
+
const proto = Object.getPrototypeOf(v);
|
|
28478
|
+
return proto === Object.prototype || proto === null;
|
|
28479
|
+
};
|
|
28480
|
+
function hasBinary(value, depth = 0) {
|
|
28481
|
+
if (isBinary(value)) return true;
|
|
28482
|
+
if (depth >= MAX_DEPTH) return false;
|
|
28483
|
+
if (Array.isArray(value)) return value.some((v) => hasBinary(v, depth + 1));
|
|
28484
|
+
if (isPlain(value)) return Object.values(value).some((v) => hasBinary(v, depth + 1));
|
|
28485
|
+
return false;
|
|
28486
|
+
}
|
|
28487
|
+
function hasFileRef(value, depth = 0) {
|
|
28488
|
+
if (fileRefOf(value)) return true;
|
|
28489
|
+
if (depth >= MAX_DEPTH) return false;
|
|
28490
|
+
if (Array.isArray(value)) return value.some((v) => hasFileRef(v, depth + 1));
|
|
28491
|
+
if (isPlain(value)) return Object.values(value).some((v) => hasFileRef(v, depth + 1));
|
|
28492
|
+
return false;
|
|
28493
|
+
}
|
|
28494
|
+
var extOf = (name) => typeof name === "string" && /\.([a-z0-9]{1,10})$/i.exec(name)?.[1]?.toLowerCase() || "bin";
|
|
28495
|
+
async function uploadFiles(value, client, parent, depth = 0) {
|
|
28496
|
+
if (isBinary(value)) {
|
|
28497
|
+
const bytes = value instanceof Uint8Array ? value : new Uint8Array(value);
|
|
28498
|
+
const name = parent?.name ?? parent?.fileName;
|
|
28499
|
+
const contentType = String(parent?.contentType ?? parent?.mimeType ?? "application/octet-stream");
|
|
28500
|
+
const ref = await client.upload(bytes, extOf(name), contentType);
|
|
28501
|
+
const fileRef = { ...ref, contentType, ...typeof name === "string" ? { name } : {} };
|
|
28502
|
+
return { [FILE_FIELD]: fileRef };
|
|
28503
|
+
}
|
|
28504
|
+
if (depth >= MAX_DEPTH) return value;
|
|
28505
|
+
if (Array.isArray(value)) {
|
|
28506
|
+
const out = [];
|
|
28507
|
+
for (const v of value) out.push(await uploadFiles(v, client, void 0, depth + 1));
|
|
28508
|
+
return out;
|
|
28509
|
+
}
|
|
28510
|
+
if (isPlain(value)) {
|
|
28511
|
+
const out = {};
|
|
28512
|
+
for (const [k, v] of Object.entries(value)) out[k] = await uploadFiles(v, client, value, depth + 1);
|
|
28513
|
+
return out;
|
|
28514
|
+
}
|
|
28515
|
+
return value;
|
|
28516
|
+
}
|
|
28517
|
+
function toFileHandles(value, client, depth = 0) {
|
|
28518
|
+
const ref = fileRefOf(value);
|
|
28519
|
+
if (ref) return new ParcelFile(ref, client);
|
|
28520
|
+
if (depth >= MAX_DEPTH) return value;
|
|
28521
|
+
if (Array.isArray(value)) return value.map((v) => toFileHandles(v, client, depth + 1));
|
|
28522
|
+
if (isPlain(value)) {
|
|
28523
|
+
const out = {};
|
|
28524
|
+
for (const [k, v] of Object.entries(value)) out[k] = toFileHandles(v, client, depth + 1);
|
|
28525
|
+
return out;
|
|
28526
|
+
}
|
|
28527
|
+
return value;
|
|
28528
|
+
}
|
|
28529
|
+
var asError = (message, reason) => ({
|
|
28530
|
+
...message,
|
|
28531
|
+
payload: { error: reason, parcelError: true }
|
|
28532
|
+
});
|
|
28533
|
+
var recipientOf = (message) => {
|
|
28534
|
+
const id = message.to?.id;
|
|
28535
|
+
return typeof id === "string" ? id : "";
|
|
28536
|
+
};
|
|
28537
|
+
var InOrder = class {
|
|
28538
|
+
/** `onIdle` runs each time the last waiting task finishes. */
|
|
28539
|
+
constructor(onIdle) {
|
|
28540
|
+
this.onIdle = onIdle;
|
|
28541
|
+
this.pending = 0;
|
|
28542
|
+
this.chain = Promise.resolve();
|
|
28543
|
+
}
|
|
28544
|
+
get busy() {
|
|
28545
|
+
return this.pending > 0;
|
|
28546
|
+
}
|
|
28547
|
+
run(task) {
|
|
28548
|
+
this.pending++;
|
|
28549
|
+
this.chain = this.chain.then(task).catch((err) => logger.error("[parcels] queue fault:", err)).finally(() => {
|
|
28550
|
+
if (--this.pending === 0) this.onIdle?.();
|
|
28551
|
+
});
|
|
28552
|
+
}
|
|
28553
|
+
idle() {
|
|
28554
|
+
return this.chain;
|
|
28555
|
+
}
|
|
28556
|
+
};
|
|
28557
|
+
var OutgoingParcels = class {
|
|
28558
|
+
constructor(client, jsonParcels, deliver, threshold = PARCEL_THRESHOLD_BYTES) {
|
|
28559
|
+
this.client = client;
|
|
28560
|
+
this.jsonParcels = jsonParcels;
|
|
28561
|
+
this.deliver = deliver;
|
|
28562
|
+
this.threshold = threshold;
|
|
28563
|
+
/** Only recipients with something waiting; a queue is dropped once it empties. */
|
|
28564
|
+
this.queues = /* @__PURE__ */ new Map();
|
|
28565
|
+
this.warnedNoClient = false;
|
|
28566
|
+
}
|
|
28567
|
+
/**
|
|
28568
|
+
* Same contract as the SDK's `send`: never throws, returns whether the message
|
|
28569
|
+
* went out (or, for a queued one, was accepted).
|
|
28570
|
+
*/
|
|
28571
|
+
send(message) {
|
|
28572
|
+
if (NEVER_PARCEL.has(message.type)) return this.now(message);
|
|
28573
|
+
let files = false;
|
|
28574
|
+
try {
|
|
28575
|
+
files = hasBinary(message.payload);
|
|
28576
|
+
} catch {
|
|
28577
|
+
}
|
|
28578
|
+
if (files && !this.client) {
|
|
28579
|
+
if (!this.warnedNoClient) {
|
|
28580
|
+
this.warnedNoClient = true;
|
|
28581
|
+
logger.warn("[parcels] a message carries a file but SA_API_URL or the API key is missing \u2014 sent as-is");
|
|
28582
|
+
}
|
|
28583
|
+
files = false;
|
|
28584
|
+
}
|
|
28585
|
+
if (!files) {
|
|
28586
|
+
if (!this.jsonParcels || !this.client) return this.now(message);
|
|
28587
|
+
let wire;
|
|
28588
|
+
try {
|
|
28589
|
+
wire = JSON.stringify(message);
|
|
28590
|
+
} catch {
|
|
28591
|
+
return this.now(message);
|
|
28592
|
+
}
|
|
28593
|
+
if (Buffer.byteLength(wire, "utf8") < this.threshold) return this.now(message, wire);
|
|
28594
|
+
}
|
|
28595
|
+
this.queueFor(message).run(async () => {
|
|
28596
|
+
const out = await this.prepare(message, files);
|
|
28597
|
+
this.deliver(out.message, out.wire);
|
|
28598
|
+
});
|
|
28599
|
+
return true;
|
|
28600
|
+
}
|
|
28601
|
+
/** Delivers now, unless something for the same recipient is already waiting — then behind it. */
|
|
28602
|
+
now(message, wire) {
|
|
28603
|
+
if (!this.queues.get(recipientOf(message))?.busy) return this.deliver(message, wire);
|
|
28604
|
+
this.queueFor(message).run(async () => {
|
|
28605
|
+
this.deliver(message, wire);
|
|
28606
|
+
});
|
|
28607
|
+
return true;
|
|
28608
|
+
}
|
|
28609
|
+
queueFor(message) {
|
|
28610
|
+
const key = recipientOf(message);
|
|
28611
|
+
let queue = this.queues.get(key);
|
|
28612
|
+
if (!queue) {
|
|
28613
|
+
queue = new InOrder(() => this.queues.delete(key));
|
|
28614
|
+
this.queues.set(key, queue);
|
|
28615
|
+
}
|
|
28616
|
+
return queue;
|
|
28617
|
+
}
|
|
28618
|
+
/** Resolves when everything queued so far has been handed to the socket. For tests and shutdown. */
|
|
28619
|
+
async idle() {
|
|
28620
|
+
await Promise.all(Array.from(this.queues.values(), (queue) => queue.idle()));
|
|
28621
|
+
}
|
|
28622
|
+
async prepare(message, files) {
|
|
28623
|
+
let current = message;
|
|
28624
|
+
if (files) {
|
|
28625
|
+
const started2 = Date.now();
|
|
28626
|
+
try {
|
|
28627
|
+
current = { ...message, payload: await uploadFiles(message.payload, this.client) };
|
|
28628
|
+
logger.info(`[parcels] ${message.type} ${message.id ?? ""}: file(s) uploaded (${Date.now() - started2}ms)`);
|
|
28629
|
+
} catch (err) {
|
|
28630
|
+
const reason = err?.message ?? String(err);
|
|
28631
|
+
logger.error(`[parcels] file upload failed for ${message.type}, sending an error instead: ${reason}`);
|
|
28632
|
+
return { message: asError(message, `Could not send a file: ${reason}`) };
|
|
28633
|
+
}
|
|
28634
|
+
}
|
|
28635
|
+
let wire;
|
|
28636
|
+
try {
|
|
28637
|
+
wire = JSON.stringify(current);
|
|
28638
|
+
} catch {
|
|
28639
|
+
return { message: current };
|
|
28640
|
+
}
|
|
28641
|
+
const size = Buffer.byteLength(wire, "utf8");
|
|
28642
|
+
if (!this.jsonParcels || size < this.threshold) return { message: current, wire };
|
|
28643
|
+
const started = Date.now();
|
|
28644
|
+
try {
|
|
28645
|
+
const bytes = Buffer.from(JSON.stringify(current.payload ?? null), "utf8");
|
|
28646
|
+
const ref = await this.client.upload(bytes);
|
|
28647
|
+
logger.info(
|
|
28648
|
+
`[parcels] ${message.type} ${message.id ?? ""} \u2192 parcel ${ref.hash.slice(0, 12)} (${ref.size} bytes, ${Date.now() - started}ms)`
|
|
28649
|
+
);
|
|
28650
|
+
return { message: { ...current, payload: { [PARCEL_FIELD]: ref } } };
|
|
28651
|
+
} catch (err) {
|
|
28652
|
+
const reason = err?.message ?? String(err);
|
|
28653
|
+
if (size <= MAX_INLINE_BYTES) {
|
|
28654
|
+
logger.warn(`[parcels] upload failed for ${message.type}, sending inline (${size} bytes): ${reason}`);
|
|
28655
|
+
return { message: current, wire };
|
|
28656
|
+
}
|
|
28657
|
+
logger.error(`[parcels] upload failed for ${message.type} (${size} bytes), sending an error instead: ${reason}`);
|
|
28658
|
+
return { message: asError(message, `The message was too large to deliver (${size} bytes): ${reason}`) };
|
|
28659
|
+
}
|
|
28660
|
+
}
|
|
28661
|
+
};
|
|
28662
|
+
var IncomingParcels = class {
|
|
28663
|
+
constructor(client, dispatch) {
|
|
28664
|
+
this.client = client;
|
|
28665
|
+
this.dispatch = dispatch;
|
|
28666
|
+
this.queue = new InOrder();
|
|
28667
|
+
}
|
|
28668
|
+
/**
|
|
28669
|
+
* `raw` is the message's text when it is at hand: a message that never
|
|
28670
|
+
* mentions a file placeholder is not searched for one.
|
|
28671
|
+
*/
|
|
28672
|
+
receive(message, raw) {
|
|
28673
|
+
const ref = parcelRefOf(message?.payload);
|
|
28674
|
+
if (!ref) {
|
|
28675
|
+
const withFiles = raw === void 0 || raw.includes(FILE_FIELD) ? this.withFiles(message) : message;
|
|
28676
|
+
if (this.queue.busy) {
|
|
28677
|
+
this.queue.run(async () => this.dispatch(withFiles));
|
|
28678
|
+
} else {
|
|
28679
|
+
this.dispatch(withFiles);
|
|
28680
|
+
}
|
|
28681
|
+
return;
|
|
28682
|
+
}
|
|
28683
|
+
this.queue.run(async () => this.dispatch(await this.restore(message, ref)));
|
|
28684
|
+
}
|
|
28685
|
+
idle() {
|
|
28686
|
+
return this.queue.idle();
|
|
28687
|
+
}
|
|
28688
|
+
withFiles(message) {
|
|
28689
|
+
try {
|
|
28690
|
+
return hasFileRef(message.payload) ? { ...message, payload: toFileHandles(message.payload, this.client) } : message;
|
|
28691
|
+
} catch {
|
|
28692
|
+
return message;
|
|
28693
|
+
}
|
|
28694
|
+
}
|
|
28695
|
+
async restore(message, ref) {
|
|
28696
|
+
if (!this.client) {
|
|
28697
|
+
logger.error(`[parcels] received a parcel for ${message.type} but SA_API_URL or the API key is not set`);
|
|
28698
|
+
return asError(message, "This engine cannot download parcels: SA_API_URL or the API key is not set.");
|
|
28699
|
+
}
|
|
28700
|
+
const started = Date.now();
|
|
28701
|
+
try {
|
|
28702
|
+
const bytes = await this.client.download(ref);
|
|
28703
|
+
const payload = JSON.parse(Buffer.from(bytes).toString("utf8"));
|
|
28704
|
+
logger.info(
|
|
28705
|
+
`[parcels] ${message.type} ${message.id ?? ""} \u2190 parcel ${ref.hash.slice(0, 12)} (${ref.size} bytes, ${Date.now() - started}ms)`
|
|
28706
|
+
);
|
|
28707
|
+
return this.withFiles({ ...message, payload });
|
|
28708
|
+
} catch (err) {
|
|
28709
|
+
const reason = err?.message ?? String(err);
|
|
28710
|
+
logger.error(`[parcels] download failed for ${message.type} (${ref.hash.slice(0, 12)}): ${reason}`);
|
|
28711
|
+
return asError(message, `Could not download the message content: ${reason}`);
|
|
28712
|
+
}
|
|
28713
|
+
}
|
|
28714
|
+
};
|
|
28715
|
+
var parcelsFlagFromEnv = () => !/^(off|false|0|no)$/i.test((process.env.SA_PARCELS ?? "").trim());
|
|
28716
|
+
|
|
28354
28717
|
// src/userResponse/scripts/fmt.ts
|
|
28355
28718
|
var DASH = "\u2014";
|
|
28356
28719
|
var DEFAULT_FMT_CONFIG = { locale: "en-IN", currency: "INR" };
|
|
@@ -29702,6 +30065,18 @@ var SuperatomSDK = class {
|
|
|
29702
30065
|
this.type = config2.type || "data-agent";
|
|
29703
30066
|
this.bundleDir = config2.bundleDir;
|
|
29704
30067
|
this.url = config2.url || process.env.SA_WEBSOCKET_URL || DEFAULT_WS_URL;
|
|
30068
|
+
const apiUrl = config2.apiUrl || process.env.SA_API_URL;
|
|
30069
|
+
this.parcelClient = apiUrl && this.apiKey ? new ParcelClient({ apiUrl, projectId: this.projectId, apiKey: this.apiKey }) : null;
|
|
30070
|
+
const jsonParcels = config2.parcels ?? parcelsFlagFromEnv();
|
|
30071
|
+
if (jsonParcels && !this.parcelClient) {
|
|
30072
|
+
logger.warn("[parcels] parcels are on but SA_API_URL or the API key is missing \u2014 large messages go inline");
|
|
30073
|
+
}
|
|
30074
|
+
this.outgoing = new OutgoingParcels(
|
|
30075
|
+
this.parcelClient,
|
|
30076
|
+
jsonParcels,
|
|
30077
|
+
(message, wire) => this.deliver(message, wire)
|
|
30078
|
+
);
|
|
30079
|
+
this.incoming = new IncomingParcels(this.parcelClient, (parsed) => this.dispatchMessage(parsed));
|
|
29705
30080
|
this.anthropicApiKey = config2.ANTHROPIC_API_KEY || process.env.ANTHROPIC_API_KEY || "";
|
|
29706
30081
|
this.groqApiKey = config2.GROQ_API_KEY || process.env.GROQ_API_KEY || "";
|
|
29707
30082
|
this.geminiApiKey = config2.GEMINI_API_KEY || process.env.GEMINI_API_KEY || "";
|
|
@@ -29853,11 +30228,24 @@ var SuperatomSDK = class {
|
|
|
29853
30228
|
});
|
|
29854
30229
|
}
|
|
29855
30230
|
/**
|
|
29856
|
-
* Handle incoming WebSocket messages
|
|
30231
|
+
* Handle incoming WebSocket messages.
|
|
30232
|
+
*
|
|
30233
|
+
* Parsed here, then handed to the parcel layer, which restores a parcel's
|
|
30234
|
+
* payload (and holds later messages back until it has) before routing.
|
|
29857
30235
|
*/
|
|
29858
30236
|
handleMessage(data) {
|
|
30237
|
+
let parsed;
|
|
30238
|
+
try {
|
|
30239
|
+
parsed = JSON.parse(data);
|
|
30240
|
+
} catch (error) {
|
|
30241
|
+
logger.error("Failed to parse incoming message:", error);
|
|
30242
|
+
return;
|
|
30243
|
+
}
|
|
30244
|
+
this.incoming.receive(parsed, data);
|
|
30245
|
+
}
|
|
30246
|
+
/** Routes one parsed message — everything `handleMessage` did after parsing. */
|
|
30247
|
+
dispatchMessage(parsed) {
|
|
29859
30248
|
try {
|
|
29860
|
-
const parsed = JSON.parse(data);
|
|
29861
30249
|
const message = IncomingMessageSchema.parse(parsed);
|
|
29862
30250
|
logger.debug("Received message:", message.type);
|
|
29863
30251
|
switch (message.type) {
|
|
@@ -30047,8 +30435,19 @@ var SuperatomSDK = class {
|
|
|
30047
30435
|
* Send a message to the Superatom service.
|
|
30048
30436
|
* Returns true if the message was sent, false if the WebSocket is not connected.
|
|
30049
30437
|
* Does NOT throw on closed connections — callers can check the return value if needed.
|
|
30438
|
+
*
|
|
30439
|
+
* Large payloads and files go as parcels (see ./parcels): a file is any
|
|
30440
|
+
* Buffer / Uint8Array / ArrayBuffer inside the payload, with an optional
|
|
30441
|
+
* `name` and `contentType` beside it — e.g. `payload: { file: { name, bytes } }`.
|
|
30442
|
+
* Such a message goes out once its upload is done (true = accepted), and later
|
|
30443
|
+
* messages keep their order. A received file arrives as a `ParcelFile` handle
|
|
30444
|
+
* whose `download()` fetches the bytes.
|
|
30050
30445
|
*/
|
|
30051
30446
|
send(message) {
|
|
30447
|
+
return this.outgoing.send(message);
|
|
30448
|
+
}
|
|
30449
|
+
/** Puts one message on the socket, or in the reconnect outbox. `wire` is its JSON, when already made. */
|
|
30450
|
+
deliver(message, wire) {
|
|
30052
30451
|
if (!this.ws || !this.connected) {
|
|
30053
30452
|
logger.warn("WebSocket is not connected, queueing message for reconnect:", message.type);
|
|
30054
30453
|
this.queuePendingMessage(message);
|
|
@@ -30060,7 +30459,7 @@ var SuperatomSDK = class {
|
|
|
30060
30459
|
return false;
|
|
30061
30460
|
}
|
|
30062
30461
|
try {
|
|
30063
|
-
const payload = JSON.stringify(message);
|
|
30462
|
+
const payload = wire ?? JSON.stringify(message);
|
|
30064
30463
|
this.ws.send(payload);
|
|
30065
30464
|
return true;
|
|
30066
30465
|
} catch (error) {
|
|
@@ -30313,7 +30712,7 @@ var SuperatomSDK = class {
|
|
|
30313
30712
|
waiters.push(finish);
|
|
30314
30713
|
this.registryWaiters.set(resType, waiters);
|
|
30315
30714
|
const sent = this.send({
|
|
30316
|
-
id: `${reqType.toLowerCase()}_${(0,
|
|
30715
|
+
id: `${reqType.toLowerCase()}_${(0, import_crypto8.randomUUID)()}`,
|
|
30317
30716
|
type: reqType,
|
|
30318
30717
|
from: { type: this.type },
|
|
30319
30718
|
to: { type: "runtime" },
|
|
@@ -30404,6 +30803,8 @@ var SuperatomSDK = class {
|
|
|
30404
30803
|
MainAgent,
|
|
30405
30804
|
MissingRequestContextError,
|
|
30406
30805
|
NUMERIC_KEYS,
|
|
30806
|
+
PARCEL_THRESHOLD_BYTES,
|
|
30807
|
+
ParcelFile,
|
|
30407
30808
|
STORAGE_CONFIG,
|
|
30408
30809
|
ScriptMatcher,
|
|
30409
30810
|
ScriptStore,
|