openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
831 lines (830 loc) • 30.2 kB
JavaScript
import { c as isRecord } from "../../record-coerce-DItp3I4t.js";
import { i as normalizeBoundedOptionalString } from "../../string-coerce-CIXf7egm.js";
import { r as truncateUtf16Safe } from "../../utf16-slice-D_ngcYKd.js";
import { h as redactToolPayloadText } from "../../redact-BtvPPfTi.js";
import { b as resolveSessionAgentIdsStrict } from "../../agent-scope-DbtJyKUL.js";
import { t as resolveConfiguredSecretInputString } from "../../resolve-configured-secret-input-string-B8zlpQOU.js";
import { a as getPluginRuntimeGatewayRequestScope } from "../../gateway-request-scope-BCMYlsDI.js";
import { b as ssrfPolicyFromHttpBaseUrlAllowedOrigin } from "../../ssrf-0QyXWOVG.js";
import { i as fetchWithSsrFGuard, n as GuardedFetchRedirectError } from "../../fetch-guard-BMdGQhbb.js";
import "../../string-coerce-runtime-GQa0ehRA.js";
import "../../agent-scope-runtime-h57Nypqc.js";
import { t as definePluginEntry } from "../../plugin-entry-zfBGJaNO.js";
import "../../ssrf-runtime-Bum5C6NN.js";
import "../../plugin-runtime-Cl_BX05Z.js";
import "../../logging-core-yEitd9NN.js";
import { n as buildControlUiCatalogSharePath, r as isControlUiCatalogShareId, t as listActiveSessionCatalogs } from "../../session-catalog-runtime-CFbNhe8c.js";
import "../../secret-input-runtime-Clavxxg-.js";
import "../../text-utility-runtime-BjzvUG99.js";
import { o as createFixedWindowRateLimiter } from "../../webhook-ingress-CpRSOilv.js";
import { a as createWebhookInFlightLimiter, i as beginWebhookRequestPipelineOrReject, s as readJsonWebhookBodyOrReject } from "../../webhook-request-guards-BoKMzZgq.js";
import { createHash } from "node:crypto";
//#region extensions/beam/src/types.ts
const BEAM_HOST_ID = "gateway";
const BEAM_SESSION_SHARE_ROUTE = {
kind: "thread-id-prefix",
routeSegment: "beam",
hostId: BEAM_HOST_ID,
identifierAlphabet: "lowercase-hex",
fullLength: 32,
minPrefixLength: 12,
lookup: "catalog-list-search-by-thread-id-prefix",
ambiguity: "multiple-results-or-next-cursor"
};
const BEAM_MAX_BODY_BYTES = 57344;
const BEAM_RETENTION_MS = 6048e5;
const BEAM_MAX_ITEM_CHARS = 6e3;
const TOP_LEVEL_KEYS = /* @__PURE__ */ new Set([
"version",
"beamId",
"source",
"title",
"updatedAt",
"completed",
"truncated",
"hookEvent",
"sourceModel",
"items"
]);
const ITEM_KEYS = /* @__PURE__ */ new Set(["type", "text"]);
const SOURCE_MODEL_KEYS = /* @__PURE__ */ new Set(["provider", "model"]);
const ITEM_TYPES = /* @__PURE__ */ new Set([
"userMessage",
"agentMessage",
"other"
]);
function hasOnlyKeys(value, allowed) {
return Object.keys(value).every((key) => allowed.has(key));
}
function isIsoTimestamp(value) {
const match = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.\d{1,9})?(?:Z|([+-])(\d{2}):(\d{2}))$/.exec(value);
if (!match || !Number.isFinite(Date.parse(value))) return false;
return (/* @__PURE__ */ new Date(`${match[1]}-${match[2]}-${match[3]}T00:00:00Z`)).getUTCDate() === Number(match[3]) && Number(match[4]) < 24 && Number(match[5]) < 60 && Number(match[6]) < 60 && Number(match[8] ?? 0) < 24 && Number(match[9] ?? 0) < 60;
}
function parseBeamUpload(value) {
if (!isRecord(value) || !hasOnlyKeys(value, TOP_LEVEL_KEYS)) return {
ok: false,
error: "request body must be a closed Beam object"
};
if (value.version !== 1) return {
ok: false,
error: "version must be 1"
};
const beamId = normalizeBoundedOptionalString(value.beamId, 64);
if (!beamId || !/^[a-f0-9]{32}$/i.test(beamId)) return {
ok: false,
error: "beamId must be a 32-character hex id"
};
const source = normalizeBoundedOptionalString(value.source, 32);
if (!source || !/^[a-z0-9._-]+$/i.test(source)) return {
ok: false,
error: "source must be a short identifier"
};
const title = normalizeBoundedOptionalString(value.title, 160);
if (!title) return {
ok: false,
error: "title must be a non-empty string"
};
const updatedAt = normalizeBoundedOptionalString(value.updatedAt, 64);
if (!updatedAt || !isIsoTimestamp(updatedAt)) return {
ok: false,
error: "updatedAt must be an ISO timestamp"
};
if (typeof value.completed !== "boolean") return {
ok: false,
error: "completed must be a boolean"
};
if (value.truncated !== void 0 && typeof value.truncated !== "boolean") return {
ok: false,
error: "truncated must be a boolean"
};
const hookEvent = value.hookEvent === void 0 ? void 0 : normalizeBoundedOptionalString(value.hookEvent, 64);
if (value.hookEvent !== void 0 && !hookEvent) return {
ok: false,
error: "hookEvent must be a short string"
};
let sourceModel;
if (value.sourceModel !== void 0) {
if (!isRecord(value.sourceModel) || !hasOnlyKeys(value.sourceModel, SOURCE_MODEL_KEYS)) return {
ok: false,
error: "sourceModel must be a closed object"
};
const provider = normalizeBoundedOptionalString(value.sourceModel.provider, 64);
const model = normalizeBoundedOptionalString(value.sourceModel.model, 256);
if (!provider || !/^[a-z0-9._-]+$/i.test(provider) || !model || !/^\S+$/u.test(model)) return {
ok: false,
error: "sourceModel must contain a provider and model"
};
sourceModel = {
provider: provider.toLowerCase(),
model
};
}
if (!Array.isArray(value.items) || value.items.length === 0 || value.items.length > 200) return {
ok: false,
error: `items must contain 1-200 entries`
};
const items = [];
for (const rawItem of value.items) {
if (!isRecord(rawItem) || !hasOnlyKeys(rawItem, ITEM_KEYS)) return {
ok: false,
error: "each transcript item must be a closed object"
};
if (typeof rawItem.type !== "string" || !ITEM_TYPES.has(rawItem.type)) return {
ok: false,
error: "transcript item type is invalid"
};
const text = normalizeBoundedOptionalString(rawItem.text, BEAM_MAX_ITEM_CHARS);
if (!text) return {
ok: false,
error: `transcript item text must be 1-${BEAM_MAX_ITEM_CHARS} characters`
};
items.push({
type: rawItem.type,
text
});
}
return {
ok: true,
value: {
version: 1,
beamId: beamId.toLowerCase(),
source: source.toLowerCase(),
title,
updatedAt,
completed: value.completed,
...value.truncated === true ? { truncated: true } : {},
...hookEvent ? { hookEvent } : {},
...sourceModel ? { sourceModel } : {},
items
}
};
}
//#endregion
//#region extensions/beam/src/http.ts
function sendJson(res, status, value) {
res.statusCode = status;
res.setHeader("Cache-Control", "no-store");
res.setHeader("Content-Type", "application/json; charset=utf-8");
res.end(JSON.stringify(value));
}
function currentRequestClient(req) {
const client = getPluginRuntimeGatewayRequestScope()?.client;
return {
clientIp: client?.clientIp ?? req.socket.remoteAddress ?? "unknown",
scopes: client?.connect?.scopes ?? [],
profileId: client?.authenticatedUserProfile?.profileId
};
}
function canPublish(scopes) {
return scopes.includes("operator.write") || scopes.includes("operator.admin");
}
function beamTimestampEpochNanoseconds(value) {
const match = /\.(\d{1,9})(?=Z|[+-]\d{2}:\d{2}$)/.exec(value);
const fraction = (match?.[1] ?? "").padEnd(9, "0");
const millisecondTimestamp = match ? `${value.slice(0, match.index)}.${fraction.slice(0, 3)}${value.slice(match.index + match[0].length)}` : value;
return BigInt(Date.parse(millisecondTimestamp)) * 1000000n + BigInt(fraction.slice(3));
}
function compareBeamTimestamps(left, right) {
const leftNanoseconds = beamTimestampEpochNanoseconds(left);
const rightNanoseconds = beamTimestampEpochNanoseconds(right);
return leftNanoseconds < rightNanoseconds ? -1 : leftNanoseconds > rightNanoseconds ? 1 : 0;
}
function createBeamRequestHandler(params) {
const rateLimiter = createFixedWindowRateLimiter({
windowMs: 6e4,
maxRequests: 60,
maxTrackedKeys: 2048
});
const inFlightLimiter = createWebhookInFlightLimiter({
maxInFlightPerKey: 2,
maxTrackedKeys: 2048
});
return async (req, res) => {
const client = params.resolveClient?.(req) ?? currentRequestClient(req);
if (!canPublish(client.scopes)) {
sendJson(res, 403, {
ok: false,
error: "operator.write is required"
});
return true;
}
const pipeline = beginWebhookRequestPipelineOrReject({
req,
res,
allowMethods: ["POST"],
requireJsonContentType: true,
rateLimiter,
rateLimitKey: client.clientIp,
inFlightLimiter,
inFlightKey: client.clientIp
});
if (!pipeline.ok) return true;
try {
const body = await readJsonWebhookBodyOrReject({
req,
res,
maxBytes: BEAM_MAX_BODY_BYTES,
timeoutMs: 1e4,
emptyObjectOnEmpty: false,
invalidJsonMessage: "invalid Beam request body"
});
if (!body.ok) return true;
const parsed = parseBeamUpload(body.value);
if (!parsed.ok) {
sendJson(res, 400, {
ok: false,
error: parsed.error
});
return true;
}
const receivedAt = params.now?.() ?? Date.now();
await params.store.update(parsed.value.beamId, (existing) => {
const revisionOrder = existing ? compareBeamTimestamps(parsed.value.updatedAt, existing.updatedAt) : 1;
if (revisionOrder < 0 || revisionOrder === 0 && existing?.completed && !parsed.value.completed) return;
return {
...parsed.value,
...client.profileId ? { uploaderProfileId: client.profileId } : {},
createdAt: existing?.createdAt ?? receivedAt,
receivedAt
};
});
sendJson(res, 200, {
ok: true,
beamId: parsed.value.beamId,
url: buildControlUiCatalogSharePath({
shareRoute: BEAM_SESSION_SHARE_ROUTE,
threadId: parsed.value.beamId,
displayName: parsed.value.title,
basePath: params.resolveControlUiBasePath()
})
});
return true;
} finally {
pipeline.release();
}
};
}
//#endregion
//#region extensions/beam/src/mirror.ts
const MIRROR_CONFIG_PATH = "plugins.entries.beam.config.mirror";
const MIRROR_TOKEN_PATH = `${MIRROR_CONFIG_PATH}.token`;
const DEFAULT_POLL_SECONDS = 30;
const DEFAULT_ACTIVE_WINDOW_MINUTES = 180;
const MIRROR_LIST_LIMIT = 100;
const MIRROR_READ_LIMIT = 50;
const MIRROR_MAX_SESSIONS = 32;
const MIRROR_RETRY_CAP = 1e3;
const MIRROR_BODY_BUDGET_BYTES = 55296;
const MIRROR_WARN_INTERVAL_MS = 3e5;
const MIRROR_UPLOAD_TIMEOUT_MS = 15e3;
function isLoopbackHostname(hostname) {
const bare = hostname.replace(/^\[|\]$/g, "");
return bare === "localhost" || bare === "127.0.0.1" || bare === "::1";
}
const MIRROR_KEYS = /* @__PURE__ */ new Set([
"endpoint",
"token",
"catalogs",
"pollSeconds",
"activeWindowMinutes"
]);
function boundedNumber(value, fallback, min, max) {
if (typeof value !== "number" || !Number.isFinite(value)) return fallback;
return Math.min(max, Math.max(min, value));
}
/** Returns the mirror config, undefined when mirroring is not configured, or an error string. */
function parseBeamMirrorConfig(config) {
const mirror = config.plugins?.entries?.beam?.config?.mirror;
if (mirror === void 0) return;
if (!isRecord(mirror) || !Object.keys(mirror).every((key) => MIRROR_KEYS.has(key))) return `${MIRROR_CONFIG_PATH} must be a closed object with endpoint/token/catalogs/pollSeconds/activeWindowMinutes`;
const endpoint = typeof mirror.endpoint === "string" ? mirror.endpoint.trim() : "";
let parsedEndpoint;
try {
parsedEndpoint = new URL(endpoint);
} catch {
return `${MIRROR_CONFIG_PATH}.endpoint must be an absolute URL`;
}
if (parsedEndpoint.protocol === "http:") {
if (!isLoopbackHostname(parsedEndpoint.hostname)) return `${MIRROR_CONFIG_PATH}.endpoint must use https for non-loopback hosts`;
} else if (parsedEndpoint.protocol !== "https:") return `${MIRROR_CONFIG_PATH}.endpoint must use http(s)`;
if (!Array.isArray(mirror.catalogs) || mirror.catalogs.length === 0 || mirror.catalogs.some((id) => typeof id !== "string" || !id.trim())) return `${MIRROR_CONFIG_PATH}.catalogs must explicitly list the catalog ids to mirror`;
const catalogs = mirror.catalogs.map((id) => id.trim().toLowerCase());
return {
endpoint,
...mirror.token !== void 0 ? { token: mirror.token } : {},
catalogs,
pollSeconds: boundedNumber(mirror.pollSeconds, DEFAULT_POLL_SECONDS, 10, 3600),
activeWindowMinutes: boundedNumber(mirror.activeWindowMinutes, DEFAULT_ACTIVE_WINDOW_MINUTES, 1, 10080)
};
}
function beamMirrorId(catalogId, hostId, threadId) {
return createHash("sha256").update(`${catalogId}\0${hostId}\0${threadId}`).digest("hex").slice(0, 32);
}
/**
* Reduce newest-first catalog items to chronological Beam uploads. Only user/agent
* message text crosses the wire; reasoning, tool calls, tool results, and raw
* payloads collapse into compact counts. Redact credentials before clipping so
* the character boundary cannot hide the suffix needed to recognize a secret.
*/
function buildBeamMirrorItems(items) {
const out = [];
const dropped = /* @__PURE__ */ new Map();
let truncated = items.some((item) => item.truncated);
const flush = () => {
if (dropped.size > 0) {
const counts = [...dropped].map(([kind, count]) => `${count} ${kind}`).join(", ");
out.push({
type: "other",
text: `${counts}; raw content dropped`
});
dropped.clear();
}
};
const labels = {
toolCall: "tool calls",
toolResult: "tool results",
reasoning: "reasoning items"
};
for (const item of items.toReversed()) {
const text = item.text?.trim();
if ((item.type === "userMessage" || item.type === "agentMessage") && text) {
flush();
const redacted = redactToolPayloadText(text);
const clipped = truncateUtf16Safe(redacted, BEAM_MAX_ITEM_CHARS);
truncated ||= clipped.length < redacted.length;
out.push({
type: item.type,
text: clipped
});
continue;
}
const label = labels[item.type] ?? "other entries";
dropped.set(label, (dropped.get(label) ?? 0) + 1);
}
flush();
return {
items: out,
truncated
};
}
/** Drop oldest items until the payload fits the receiver's item and byte caps. */
function fitBeamMirrorUpload(upload) {
const items = upload.items.slice(-200);
const fitted = {
...upload,
items
};
let bytes = Buffer.byteLength(JSON.stringify(fitted), "utf8");
if (items.length < upload.items.length || bytes > MIRROR_BODY_BUDGET_BYTES) {
fitted.truncated = true;
bytes = Buffer.byteLength(JSON.stringify(fitted), "utf8");
}
let start = 0;
while (start + 1 < items.length && bytes > MIRROR_BODY_BUDGET_BYTES) bytes -= Buffer.byteLength(JSON.stringify(items[start++]), "utf8") + 1;
return {
...fitted,
items: items.slice(start)
};
}
function sourceModelForMirror(providerValue, items) {
const provider = providerValue?.trim().toLowerCase();
const rawModel = items.find((item) => item.type === "agentMessage" && item.model?.trim())?.model;
if (!provider || !/^[a-z0-9._-]+$/i.test(provider) || !rawModel) return;
const prefixed = rawModel.trim();
const model = truncateUtf16Safe(prefixed.startsWith(`${provider}/`) ? prefixed.slice(provider.length + 1) : prefixed, 256).trim();
return model && /^\S+$/u.test(model) ? {
provider,
model
} : void 0;
}
function mirrorCandidateKey(candidate) {
return `${candidate.catalogId}\0${candidate.hostId}\0${candidate.threadId}`;
}
function createBeamMirrorRunner(params) {
const env = params.env ?? process.env;
const now = params.now ?? Date.now;
const listCatalogs = params.listCatalogs ?? listActiveSessionCatalogs;
const tracked = /* @__PURE__ */ new Map();
const controller = new AbortController();
const { signal } = controller;
let lastWarnAt = 0;
let warnedProcessHomeIsolation = false;
let endpoint = "";
let redirectBlocked = false;
let activeTick;
let stopPromise;
const stopError = /* @__PURE__ */ new Error("Beam mirror stopped");
const raceCatalog = async (operation) => {
let rejectAbort;
const aborted = new Promise((_, reject) => {
rejectAbort = reject;
});
const abort = () => rejectAbort(stopError);
signal.addEventListener("abort", abort, { once: true });
try {
if (signal.aborted) abort();
return await Promise.race([operation, aborted]);
} finally {
signal.removeEventListener("abort", abort);
}
};
const trackSuccessfulUpload = (key, candidate, fingerprint) => {
tracked.set(key, {
candidate,
fingerprint,
expiresAt: now() + BEAM_RETENTION_MS
});
if (tracked.size <= MIRROR_RETRY_CAP) return;
let oldest;
for (const item of tracked) if (item[0] !== key && (!oldest || item[1].expiresAt < oldest[1].expiresAt)) oldest = item;
if (oldest) tracked.delete(oldest[0]);
};
const warnThrottled = (message) => {
if (now() - lastWarnAt >= MIRROR_WARN_INTERVAL_MS) {
lastWarnAt = now();
params.logger.warn(message);
}
};
const upload = async (token, payload) => {
if (signal.aborted || redirectBlocked) return false;
let guarded;
try {
guarded = await fetchWithSsrFGuard({
url: endpoint,
fetchImpl: params.fetchFn,
timeoutMs: MIRROR_UPLOAD_TIMEOUT_MS,
signal,
policy: ssrfPolicyFromHttpBaseUrlAllowedOrigin(endpoint),
auditContext: "beam.mirror_upload",
maxRedirects: 0,
init: {
method: "POST",
headers: {
"Content-Type": "application/json",
...token ? { Authorization: `Bearer ${token}` } : {}
},
body: JSON.stringify(payload)
}
});
} catch (error) {
signal.throwIfAborted();
if (error instanceof GuardedFetchRedirectError) {
redirectBlocked = true;
params.logger.warn(`beam mirror upload blocked for ${payload.source}: receiver returned redirect (${error.status}); redirects are not followed; configure the final endpoint`);
return false;
}
throw error;
}
const { response, release } = guarded;
try {
signal.throwIfAborted();
if (!response.ok) warnThrottled(`beam mirror upload failed (${response.status}) for ${payload.source}`);
return response.ok;
} finally {
await response.body?.cancel().catch(() => void 0);
await release();
}
};
const buildUpload = async (agentId, catalog, candidate, completed) => {
const transcript = await raceCatalog(catalog.read({
agentId,
hostId: candidate.hostId,
threadId: candidate.threadId,
limit: MIRROR_READ_LIMIT
}));
signal.throwIfAborted();
const reduced = buildBeamMirrorItems(transcript.items);
const sourceModel = sourceModelForMirror(candidate.modelProvider, transcript.items);
const items = reduced.items.length ? reduced.items : [{
type: "other",
text: "no shareable messages yet"
}];
return fitBeamMirrorUpload({
version: 1,
beamId: beamMirrorId(candidate.catalogId, candidate.hostId, candidate.threadId),
source: candidate.catalogId,
title: truncateUtf16Safe(redactToolPayloadText(candidate.title), 160),
updatedAt: new Date(candidate.recencyAt || now()).toISOString(),
completed,
...sourceModel ? { sourceModel } : {},
...reduced.truncated || transcript.nextCursor ? { truncated: true } : {},
items
});
};
const mirrorFingerprint = ({ updatedAt: _updatedAt, ...content }) => createHash("sha256").update(JSON.stringify(content)).digest("hex");
const scan = async () => {
try {
const config = params.runtime.config.current();
const mirror = parseBeamMirrorConfig(config);
if (mirror === void 0) return;
if (typeof mirror === "string") {
warnThrottled(`beam mirror disabled: ${mirror}`);
return;
}
if (endpoint !== mirror.endpoint) {
endpoint = mirror.endpoint;
tracked.clear();
redirectBlocked = false;
}
let agentId;
try {
agentId = resolveSessionAgentIdsStrict({ config }).defaultAgentId;
} catch (error) {
warnThrottled(`beam mirror disabled: ${String(error)}`);
return;
}
let token;
if (mirror.token !== void 0) {
const resolved = await resolveConfiguredSecretInputString({
config,
env,
value: mirror.token,
path: MIRROR_TOKEN_PATH
});
signal.throwIfAborted();
if (!resolved.value) {
warnThrottled(`beam mirror token unresolved${resolved.unresolvedRefReason ? `: ${resolved.unresolvedRefReason}` : ""}`);
return;
}
token = resolved.value;
}
const activeSinceMs = now() - mirror.activeWindowMinutes * 6e4;
const catalogs = listCatalogs().filter((catalog) => catalog.id !== "beam" && mirror.catalogs.includes(catalog.id));
if (!warnedProcessHomeIsolation && catalogs.some((catalog) => !catalog.processHomeFallbackAllowed)) {
warnedProcessHomeIsolation = true;
params.logger.warn("beam mirror process-HOME fallback disabled: isolated state; only explicit catalog roots can be mirrored");
}
const catalogById = new Map(catalogs.map((catalog) => [catalog.id, catalog]));
const observed = /* @__PURE__ */ new Map();
const fullyObservedHosts = /* @__PURE__ */ new Set();
for (const catalog of catalogs) {
signal.throwIfAborted();
try {
const hosts = await raceCatalog(catalog.list({
agentId,
limitPerHost: MIRROR_LIST_LIMIT
}));
signal.throwIfAborted();
for (const host of hosts) {
if (host.kind !== "gateway" || !host.connected || host.error) continue;
if (host.nextCursor === void 0) fullyObservedHosts.add(`${catalog.id}\0${host.hostId}`);
for (const session of host.sessions) {
const candidate = {
catalogId: catalog.id,
hostId: host.hostId,
modelProvider: session.modelProvider,
threadId: session.threadId,
title: session.name?.trim() || `${catalog.id} session`,
recencyAt: session.recencyAt ?? session.updatedAt ?? 0
};
observed.set(mirrorCandidateKey(candidate), candidate);
}
}
} catch (error) {
signal.throwIfAborted();
warnThrottled(`beam mirror list failed for ${catalog.id}: ${String(error)}`);
}
}
const selected = [...observed.values()].filter((candidate) => candidate.recencyAt >= activeSinceMs).toSorted((left, right) => right.recencyAt - left.recencyAt).slice(0, MIRROR_MAX_SESSIONS);
for (const candidate of selected) {
signal.throwIfAborted();
const key = mirrorCandidateKey(candidate);
const catalog = catalogById.get(candidate.catalogId);
if (!catalog) continue;
try {
const payload = await buildUpload(agentId, catalog, candidate, false);
signal.throwIfAborted();
const fingerprint = mirrorFingerprint(payload);
if (tracked.get(key)?.fingerprint === fingerprint) continue;
const uploaded = await upload(token, payload);
signal.throwIfAborted();
if (uploaded) trackSuccessfulUpload(key, candidate, fingerprint);
} catch (error) {
signal.throwIfAborted();
warnThrottled(`beam mirror upload failed for ${candidate.catalogId}: ${String(error)}`);
}
}
const tickNow = now();
const terminalEntries = [];
for (const [key, entry] of tracked) {
signal.throwIfAborted();
const candidate = observed.get(key);
if (!(candidate ? candidate.recencyAt < activeSinceMs : fullyObservedHosts.has(`${entry.candidate.catalogId}\0${entry.candidate.hostId}`))) continue;
if (entry.expiresAt <= tickNow) tracked.delete(key);
else if (terminalEntries.length < MIRROR_MAX_SESSIONS) terminalEntries.push([key, candidate ? {
...entry,
candidate
} : entry]);
}
for (const [key, entry] of terminalEntries) {
signal.throwIfAborted();
let uploaded = false;
const catalog = catalogById.get(entry.candidate.catalogId);
if (catalog) try {
const payload = await buildUpload(agentId, catalog, entry.candidate, true);
signal.throwIfAborted();
uploaded = await upload(token, payload);
signal.throwIfAborted();
} catch {
signal.throwIfAborted();
}
tracked.delete(key);
if (!uploaded) tracked.set(key, entry);
}
} catch (error) {
if (!signal.aborted) throw error;
}
};
return {
tick: () => {
if (signal.aborted) return stopPromise ?? Promise.resolve();
activeTick ??= scan().finally(() => {
activeTick = void 0;
});
return activeTick;
},
stop: () => {
if (!stopPromise) {
stopPromise = activeTick ?? Promise.resolve();
controller.abort();
}
return stopPromise;
}
};
}
function createBeamMirrorService(params) {
let interval;
let runner;
return {
id: "beam-mirror",
start(ctx) {
const mirror = parseBeamMirrorConfig(params.runtime.config.current());
if (mirror === void 0) return;
if (typeof mirror === "string") {
ctx.logger.warn(`beam mirror disabled: ${mirror}`);
return;
}
runner = createBeamMirrorRunner({
runtime: params.runtime,
logger: ctx.logger
});
interval = setInterval(() => {
runner?.tick();
}, mirror.pollSeconds * 1e3);
interval.unref?.();
ctx.logger.info(`beam mirror active: ${mirror.catalogs.join(", ")} -> ${mirror.endpoint}`);
runner.tick();
},
stop() {
if (interval) {
clearInterval(interval);
interval = void 0;
}
return runner?.stop() ?? Promise.resolve();
}
};
}
//#endregion
//#region extensions/beam/src/session-catalog.ts
const DEFAULT_LIMIT = 50;
const MAX_LIMIT = 100;
function boundedLimit(value) {
return Math.min(MAX_LIMIT, Math.max(1, value ?? DEFAULT_LIMIT));
}
function cursorOffset(value) {
if (!value || !/^\d+$/.test(value)) return 0;
const parsed = Number(value);
return Number.isSafeInteger(parsed) && parsed >= 0 ? parsed : 0;
}
function searchableText(session) {
return `${session.title}\n${session.source}`.toLowerCase();
}
function transcriptRevision(session) {
return createHash("sha256").update(JSON.stringify(session.items)).digest("base64url");
}
function encodeTranscriptCursor(cursor) {
return Buffer.from(JSON.stringify(cursor), "utf8").toString("base64url");
}
function decodeTranscriptCursor(value) {
try {
const parsed = JSON.parse(Buffer.from(value, "base64url").toString("utf8"));
if (isRecord(parsed) && typeof parsed.revision === "string" && /^[A-Za-z0-9_-]{43}$/.test(parsed.revision) && typeof parsed.end === "number" && Number.isSafeInteger(parsed.end) && parsed.end >= 0) return {
revision: parsed.revision,
end: parsed.end
};
} catch {}
throw new Error("invalid Beam transcript cursor");
}
function createBeamSessionCatalog(store) {
return {
id: "beam",
label: "Beam",
audience: "gateway-operators",
shareRoute: BEAM_SESSION_SHARE_ROUTE,
supportsProcessHomeIsolation: true,
async list(params) {
const search = params.search?.trim().toLowerCase();
const shareId = search && isControlUiCatalogShareId(BEAM_SESSION_SHARE_ROUTE, search) ? search : void 0;
const sessions = (await store.list()).filter((session) => !search || (shareId ? session.beamId.startsWith(shareId) : searchableText(session).includes(search))).toSorted((left, right) => right.receivedAt - left.receivedAt || left.beamId.localeCompare(right.beamId));
const offset = cursorOffset(params.cursors?.[BEAM_HOST_ID]);
const limit = boundedLimit(params.limitPerHost);
const page = sessions.slice(offset, offset + limit);
return [{
hostId: BEAM_HOST_ID,
label: "Beamed sessions",
kind: "gateway",
connected: true,
sessions: page.map((session) => ({
threadId: session.beamId,
name: session.title,
status: session.completed ? "completed" : "live",
createdAt: session.createdAt,
updatedAt: session.receivedAt,
recencyAt: session.receivedAt,
source: session.source,
archived: false,
canContinue: true,
canArchive: false
})),
...offset + page.length < sessions.length ? { nextCursor: String(offset + page.length) } : {}
}];
},
async read(params) {
if (params.hostId !== "gateway") throw new Error(`unknown Beam host: ${params.hostId}`);
const session = await store.get(params.threadId);
if (!session) throw new Error(`unknown Beam session: ${params.threadId}`);
const cursor = params.cursor === void 0 ? void 0 : decodeTranscriptCursor(params.cursor);
const revision = transcriptRevision(session);
if (cursor && cursor.revision !== revision) throw new Error("stale Beam transcript cursor");
const end = Math.min(session.items.length, cursor?.end ?? session.items.length);
const start = Math.max(0, end - boundedLimit(params.limit));
return {
hostId: BEAM_HOST_ID,
label: session.title,
threadId: session.beamId,
items: session.items.slice(start, end).map((item, index) => ({
id: `${session.beamId}:${start + index}`,
type: item.type,
text: item.text,
timestamp: session.updatedAt,
sender: item.type === "userMessage" && session.uploaderProfileId ? { identity: {
type: "profile",
id: session.uploaderProfileId
} } : void 0
})).toReversed(),
...start > 0 ? { nextCursor: encodeTranscriptCursor({
revision,
end: start
}) } : {}
};
},
async copyToGatewaySession(params) {
if (params.hostId !== "gateway") throw new Error(`unknown Beam host: ${params.hostId}`);
const session = await store.get(params.threadId);
if (!session) throw new Error(`unknown Beam session: ${params.threadId}`);
return {
displayName: session.title,
...session.sourceModel ? { preferredModel: `${session.sourceModel.provider}/${session.sourceModel.model}` } : {}
};
}
};
}
//#endregion
//#region extensions/beam/src/store.ts
function createBeamStore(runtime) {
const store = runtime.state.openKeyedStore({
namespace: "sessions",
maxEntries: 500,
overflowPolicy: "evict-oldest",
defaultTtlMs: BEAM_RETENTION_MS
});
return {
update: (beamId, updateValue) => store.update(beamId, updateValue),
get: (beamId) => store.lookup(beamId),
list: async () => (await store.entries()).map((entry) => entry.value)
};
}
//#endregion
//#region extensions/beam/index.ts
var beam_default = definePluginEntry({
id: "beam",
name: "Beam",
description: "Receive redacted coding-session snapshots for review or Team continuation",
register(api) {
const store = createBeamStore(api.runtime);
api.registerSessionCatalog(createBeamSessionCatalog(store));
api.registerHttpRoute({
path: "/api/v1/beam/sessions",
auth: "gateway",
match: "exact",
handler: createBeamRequestHandler({
store,
resolveControlUiBasePath: () => api.runtime.config.current().gateway?.controlUi?.basePath
})
});
api.registerService(createBeamMirrorService({ runtime: api.runtime }));
}
});
//#endregion
export { beam_default as default };