negotium 0.14.2 → 0.14.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-helpers.js +137 -7
- package/dist/agent-helpers.js.map +12 -10
- package/dist/background-bash.js +2 -2
- package/dist/background-bash.js.map +4 -4
- package/dist/browser-runtime.js +2 -2
- package/dist/browser-runtime.js.map +4 -4
- package/dist/{chunk-z1nb140t.js → chunk-er68mpja.js} +17 -4
- package/dist/{chunk-z1nb140t.js.map → chunk-er68mpja.js.map} +5 -5
- package/dist/{chunk-m6aw1tb3.js → chunk-p7tprsvc.js} +1 -1
- package/dist/{chunk-bqdya494.js → chunk-zcazbmcs.js} +3 -3
- package/dist/{chunk-bqdya494.js.map → chunk-zcazbmcs.js.map} +2 -2
- package/dist/hosted-agent.js +23 -7
- package/dist/hosted-agent.js.map +7 -7
- package/dist/main.js +557 -390
- package/dist/main.js.map +15 -14
- package/dist/mcp-factories.js +137 -7
- package/dist/mcp-factories.js.map +12 -10
- package/dist/media.js +2 -2
- package/dist/media.js.map +4 -4
- package/dist/prompts.js +2 -2
- package/dist/prompts.js.map +4 -4
- package/dist/query-runtime.js +2 -2
- package/dist/query-runtime.js.map +4 -4
- package/dist/registry.js +3 -3
- package/dist/rollout.js +2 -2
- package/dist/runtime/src/agents/codex-provider.ts +8 -2
- package/dist/runtime/src/application/submit-runtime-gateway-turn.ts +21 -2
- package/dist/runtime/src/node-host.ts +1 -0
- package/dist/runtime/src/platform/config.ts +1 -1
- package/dist/runtime/src/platform/mcp-config.ts +38 -16
- package/dist/runtime/src/runtime/turn-runner.ts +13 -0
- package/dist/runtime/src/runtime-gateway.ts +86 -0
- package/dist/runtime/src/storage/topic-host-mcp-grants.ts +54 -0
- package/dist/runtime/src/topics/lifecycle.ts +2 -0
- package/dist/runtime/src/types.ts +4 -0
- package/dist/runtime/src/version.ts +1 -1
- package/dist/runtime-gateway.js +73 -1
- package/dist/runtime-gateway.js.map +3 -3
- package/dist/runtime-helpers.js +2 -2
- package/dist/runtime-helpers.js.map +4 -4
- package/dist/storage.js.map +2 -2
- package/dist/types/packages/core/src/platform/config.d.ts +1 -1
- package/dist/types/packages/core/src/platform/mcp-config.d.ts +2 -0
- package/dist/types/packages/core/src/runtime/turn-runner.d.ts +3 -1
- package/dist/types/packages/core/src/runtime-gateway.d.ts +11 -0
- package/dist/types/packages/core/src/storage/topic-host-mcp-grants.d.ts +5 -0
- package/dist/types/packages/core/src/types.d.ts +3 -0
- package/dist/types/packages/core/src/version.d.ts +1 -1
- package/dist/vault.js +2 -2
- package/dist/vault.js.map +4 -4
- package/install-browser-rs.mjs +3 -3
- package/package.json +1 -1
- /package/dist/{chunk-m6aw1tb3.js.map → chunk-p7tprsvc.js.map} +0 -0
package/dist/main.js
CHANGED
|
@@ -281,7 +281,7 @@ function resolveWhisperBackend() {
|
|
|
281
281
|
function codexAuthFilePath() {
|
|
282
282
|
return process.env.NEGOTIUM_CODEX_AUTH_FILE || join(process.env.CODEX_HOME || join(homedir(), ".codex"), "auth.json");
|
|
283
283
|
}
|
|
284
|
-
var HOME, PROJECT_ROOT, STATE_DIR, WORKSPACE_DIR, TOPIC_WORKSPACE_DIR, SHARED_WIKI_DIR, CRON_WORKSPACE_DIR, BROWSER_DIR, BROWSER_PROFILES_DIR, BINARIES_DIR, SECRETS_DIR, DM_WORKSPACE_DIR, SESSION_WORKSPACE_DIR, CLAUDE_EXECUTABLE_ENV, CLAUDE_EXECUTABLE, DEFAULT_OUTPUT_LANGUAGE = "English", BROWSER_RS_VERSION = "v0.
|
|
284
|
+
var HOME, PROJECT_ROOT, STATE_DIR, WORKSPACE_DIR, TOPIC_WORKSPACE_DIR, SHARED_WIKI_DIR, CRON_WORKSPACE_DIR, BROWSER_DIR, BROWSER_PROFILES_DIR, BINARIES_DIR, SECRETS_DIR, DM_WORKSPACE_DIR, SESSION_WORKSPACE_DIR, CLAUDE_EXECUTABLE_ENV, CLAUDE_EXECUTABLE, DEFAULT_OUTPUT_LANGUAGE = "English", BROWSER_RS_VERSION = "v0.4.0", BROWSER_RS_MIN_SECURE_VERSION = "0.2.1", BROWSER_RS_VERSION_PROBE_TIMEOUT_MS = 15000, SNIPPETS_API_URL, BROWSER_RS_BIN, BASH_RS_VERSION = "v0.1.7", BASH_RS_BIN, PLAYWRIGHT_MCP_BIN, TSX_BIN, TSX_LOADER, TSCONFIG_PATH, SESSION_COMM_SERVER, TASK_SERVER, DECISION_SERVER, CANONICAL_MCP_PROXY_SERVER, WIKI_SERVER, TOKEN_STATS_SERVER, COMPACTION_LOG_SERVER, SYSTEM_HEALTH_SERVER, AGENT_HEALTH_SERVER, VAULT_SERVER, BG_BASH_BASE_PORT, BG_BASH_MAX_PORT, RUNTIME_MCP_SECRET, NODE_CONTROL_TOKEN, NODE_ID, VAULT_MASTER_KEY, NEGOTIUM_PORT, hostname, DATA_DIR, LOG_DIR, UPLOADS_DIR, VAULT_DIR, SESSIONS_DB, DEBUG_FILE, USERS_LOG_DIR, RUN_DIR, PROGRESS_DIR, DM_CMD_DIR, DM_RESP_DIR, SESSION_INBOX_DIR, SESSION_ASKS_DIR, BASHRS_SPILL_ROOT, PLAYWRIGHT_BASE_PORT, PLAYWRIGHT_MAX_PORT, PLAYWRIGHT_PORTS_DIR, ACTIVE_QUERY_STALE_MS, AGENTS_PROMPTS_DIR, RESOURCES_DIR, FILE_TAG_REGEX, MODEL_SONNET = "claude-sonnet-5", MODEL_OPUS = "claude-opus-5", MODEL_FABLE = "claude-fable-5-1", FALLBACK_AGENT, SESSION_AGENT, GATEWAY_AGENT, FALLBACK_MODEL, SESSION_MODEL, GATEWAY_MODEL, DEFAULT_TOPIC_EFFORT, FFMPEG_BIN, FFPROBE_BIN, PYTHON_BIN, FASTER_WHISPER_WRAPPER, WHISPER_MODEL, TESSERACT_BIN, PDFTOTEXT_BIN, WHISPER_BACKEND, MLX_WHISPER_BIN, WHISPER_MODEL_MLX, _envMaxTellDepth, MAX_TELL_DEPTH;
|
|
285
285
|
var init_config = __esm(() => {
|
|
286
286
|
init_config_helpers();
|
|
287
287
|
init_logger();
|
|
@@ -2777,6 +2777,19 @@ function setNodeMcpServers(entries) {
|
|
|
2777
2777
|
function getNodeMcpServers() {
|
|
2778
2778
|
return nodeMcpEntries;
|
|
2779
2779
|
}
|
|
2780
|
+
function isReservedRuntimeMcpServerName(name) {
|
|
2781
|
+
return Object.hasOwn(MCP_CATALOG, name) || nodeMcpEntries.some((entry) => entry.key === name);
|
|
2782
|
+
}
|
|
2783
|
+
function mergeHostMcpServers(base, hostMcpServers) {
|
|
2784
|
+
if (!hostMcpServers)
|
|
2785
|
+
return base;
|
|
2786
|
+
for (const name of Object.keys(hostMcpServers)) {
|
|
2787
|
+
if (Object.hasOwn(base, name) || isReservedRuntimeMcpServerName(name)) {
|
|
2788
|
+
throw new Error(`host MCP server name conflicts with node catalog: ${name}`);
|
|
2789
|
+
}
|
|
2790
|
+
}
|
|
2791
|
+
return { ...base, ...hostMcpServers };
|
|
2792
|
+
}
|
|
2780
2793
|
function buildNodeMcpSpecs(agent, filter) {
|
|
2781
2794
|
const out = {};
|
|
2782
2795
|
for (const entry of nodeMcpEntries) {
|
|
@@ -2953,7 +2966,7 @@ function getMcpServersForQuery(opts) {
|
|
|
2953
2966
|
});
|
|
2954
2967
|
}
|
|
2955
2968
|
if (opts.sessionType === "manager") {
|
|
2956
|
-
return getManagerMcpServers({
|
|
2969
|
+
return mergeHostMcpServers(getManagerMcpServers({
|
|
2957
2970
|
userId: opts.userId || "local",
|
|
2958
2971
|
session: opts.session,
|
|
2959
2972
|
topicId: opts.topicId,
|
|
@@ -2968,7 +2981,7 @@ function getMcpServersForQuery(opts) {
|
|
|
2968
2981
|
autoContinue: opts.autoContinue,
|
|
2969
2982
|
visualTools: opts.visualTools,
|
|
2970
2983
|
fileDeliveryTools: opts.fileDeliveryTools
|
|
2971
|
-
});
|
|
2984
|
+
}), opts.hostMcpServers);
|
|
2972
2985
|
}
|
|
2973
2986
|
return getForumMcpServers({
|
|
2974
2987
|
userId: opts.userId || "local",
|
|
@@ -3836,7 +3849,7 @@ var exports_version = {};
|
|
|
3836
3849
|
__export(exports_version, {
|
|
3837
3850
|
NEGOTIUM_VERSION: () => NEGOTIUM_VERSION
|
|
3838
3851
|
});
|
|
3839
|
-
var NEGOTIUM_VERSION = "0.14.
|
|
3852
|
+
var NEGOTIUM_VERSION = "0.14.4";
|
|
3840
3853
|
|
|
3841
3854
|
// ../../packages/core/src/agents/codex-native-multi-agent.ts
|
|
3842
3855
|
import { spawn as spawn2 } from "child_process";
|
|
@@ -6320,8 +6333,11 @@ function toCodexMcpServers(claudeShape) {
|
|
|
6320
6333
|
if (typeof s.url === "string" && s.type !== "sse") {
|
|
6321
6334
|
out[codexName] = withCodexMcpServerOverrides(name, {
|
|
6322
6335
|
url: s.url,
|
|
6323
|
-
...s.http_headers && typeof s.http_headers === "object"
|
|
6324
|
-
|
|
6336
|
+
...s.http_headers && typeof s.http_headers === "object" || s.headers && typeof s.headers === "object" ? {
|
|
6337
|
+
http_headers: s.http_headers ?? s.headers
|
|
6338
|
+
} : {},
|
|
6339
|
+
...s.env_http_headers && typeof s.env_http_headers === "object" ? { env_http_headers: s.env_http_headers } : {},
|
|
6340
|
+
...typeof s.timeout === "number" ? { tool_timeout_sec: Math.max(1, Math.ceil(s.timeout / 1000)) } : {}
|
|
6325
6341
|
});
|
|
6326
6342
|
}
|
|
6327
6343
|
}
|
|
@@ -17731,6 +17747,487 @@ body{font-family:system-ui,-apple-system,"Segoe UI",sans-serif;background:var(--
|
|
|
17731
17747
|
</style>`;
|
|
17732
17748
|
});
|
|
17733
17749
|
|
|
17750
|
+
// ../../packages/core/src/runtime-gateway.ts
|
|
17751
|
+
function parseHostMcpServers(value) {
|
|
17752
|
+
if (value === undefined)
|
|
17753
|
+
return;
|
|
17754
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
17755
|
+
throw new TypeError("hostMcpServers must be an object");
|
|
17756
|
+
}
|
|
17757
|
+
const parsedEntries = [];
|
|
17758
|
+
for (const [name, raw] of Object.entries(value)) {
|
|
17759
|
+
if (!HOST_MCP_SERVER_NAME.test(name)) {
|
|
17760
|
+
throw new TypeError(`invalid host MCP server name: ${name}`);
|
|
17761
|
+
}
|
|
17762
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
17763
|
+
throw new TypeError(`hostMcpServers.${name} must be an object`);
|
|
17764
|
+
}
|
|
17765
|
+
const spec = raw;
|
|
17766
|
+
const unsupported = Object.keys(spec).find((key) => !HOST_MCP_SERVER_KEYS.has(key));
|
|
17767
|
+
if (unsupported) {
|
|
17768
|
+
throw new TypeError(`hostMcpServers.${name}.${unsupported} is not allowed`);
|
|
17769
|
+
}
|
|
17770
|
+
if (spec.type !== "sse" && spec.type !== "http") {
|
|
17771
|
+
throw new TypeError(`hostMcpServers.${name}.type must be sse or http`);
|
|
17772
|
+
}
|
|
17773
|
+
if (typeof spec.url !== "string") {
|
|
17774
|
+
throw new TypeError(`hostMcpServers.${name}.url must be a string`);
|
|
17775
|
+
}
|
|
17776
|
+
let url;
|
|
17777
|
+
try {
|
|
17778
|
+
url = new URL(spec.url);
|
|
17779
|
+
} catch {
|
|
17780
|
+
throw new TypeError(`hostMcpServers.${name}.url must be a valid URL`);
|
|
17781
|
+
}
|
|
17782
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
17783
|
+
throw new TypeError(`hostMcpServers.${name}.url must use http or https`);
|
|
17784
|
+
}
|
|
17785
|
+
let headers;
|
|
17786
|
+
if (spec.headers !== undefined) {
|
|
17787
|
+
if (!spec.headers || typeof spec.headers !== "object" || Array.isArray(spec.headers)) {
|
|
17788
|
+
throw new TypeError(`hostMcpServers.${name}.headers must be an object`);
|
|
17789
|
+
}
|
|
17790
|
+
const headerEntries = [];
|
|
17791
|
+
for (const [key, headerValue] of Object.entries(spec.headers)) {
|
|
17792
|
+
if (!HTTP_HEADER_NAME.test(key) || typeof headerValue !== "string") {
|
|
17793
|
+
throw new TypeError(`hostMcpServers.${name}.headers must contain string values`);
|
|
17794
|
+
}
|
|
17795
|
+
try {
|
|
17796
|
+
new Headers([[key, headerValue]]);
|
|
17797
|
+
} catch {
|
|
17798
|
+
throw new TypeError(`hostMcpServers.${name}.headers contains an invalid header`);
|
|
17799
|
+
}
|
|
17800
|
+
headerEntries.push([key, headerValue]);
|
|
17801
|
+
}
|
|
17802
|
+
headers = Object.fromEntries(headerEntries);
|
|
17803
|
+
}
|
|
17804
|
+
if (spec.timeout !== undefined && (typeof spec.timeout !== "number" || !Number.isFinite(spec.timeout) || spec.timeout <= 0)) {
|
|
17805
|
+
throw new TypeError(`hostMcpServers.${name}.timeout must be a positive number`);
|
|
17806
|
+
}
|
|
17807
|
+
parsedEntries.push([
|
|
17808
|
+
name,
|
|
17809
|
+
{
|
|
17810
|
+
type: spec.type,
|
|
17811
|
+
url: url.toString(),
|
|
17812
|
+
...headers ? { headers } : {},
|
|
17813
|
+
...spec.timeout !== undefined ? { timeout: spec.timeout } : {}
|
|
17814
|
+
}
|
|
17815
|
+
]);
|
|
17816
|
+
}
|
|
17817
|
+
return Object.fromEntries(parsedEntries);
|
|
17818
|
+
}
|
|
17819
|
+
function record(value) {
|
|
17820
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
17821
|
+
}
|
|
17822
|
+
async function json(response) {
|
|
17823
|
+
try {
|
|
17824
|
+
return await response.json();
|
|
17825
|
+
} catch {
|
|
17826
|
+
throw new RuntimeGatewayError("Runtime Gateway returned invalid JSON", "protocol", response.status);
|
|
17827
|
+
}
|
|
17828
|
+
}
|
|
17829
|
+
function validHealth(value) {
|
|
17830
|
+
const body = record(value);
|
|
17831
|
+
return Boolean(body?.ok === true && body.v === RUNTIME_GATEWAY_VERSION && Array.isArray(body.capabilities) && typeof body.cursor === "number");
|
|
17832
|
+
}
|
|
17833
|
+
function validAcknowledgedMessage(value) {
|
|
17834
|
+
const message = record(value);
|
|
17835
|
+
return Boolean(message && typeof message.id === "string" && typeof message.topicId === "string" && typeof message.authorId === "string" && typeof message.text === "string" && typeof message.createdAt === "string");
|
|
17836
|
+
}
|
|
17837
|
+
function validTurnAck(value) {
|
|
17838
|
+
const body = record(value);
|
|
17839
|
+
return Boolean(body?.ok === true && body.v === RUNTIME_GATEWAY_VERSION && body.accepted === true && typeof body.deduplicated === "boolean" && typeof body.requestId === "string" && typeof body.clientMessageId === "string" && typeof body.topicId === "string" && typeof body.messageId === "string" && (body.message === undefined || validAcknowledgedMessage(body.message)) && typeof body.cursor === "number");
|
|
17840
|
+
}
|
|
17841
|
+
function validManagerTopic(value) {
|
|
17842
|
+
const topic = record(value);
|
|
17843
|
+
return Boolean(typeof topic?.id === "string" && typeof topic.title === "string" && topic.kind === "manager" && Array.isArray(topic.participants));
|
|
17844
|
+
}
|
|
17845
|
+
function validDeletedMessage(value) {
|
|
17846
|
+
const message = record(value);
|
|
17847
|
+
return Boolean(typeof message?.id === "string" && typeof message.topicId === "string" && typeof message.authorId === "string" && typeof message.text === "string" && message.deleted === true && typeof message.createdAt === "string");
|
|
17848
|
+
}
|
|
17849
|
+
function validCronJob(value) {
|
|
17850
|
+
const job = record(value);
|
|
17851
|
+
return Boolean(typeof job?.id === "string" && typeof job.name === "string" && typeof job.executionPrincipalUserId === "string" && typeof job.actorOwnerUserId === "string" && typeof job.topicId === "string" && typeof job.schedule === "string" && typeof job.enabled === "boolean" && typeof job.nextRunAt === "string" && typeof job.canMutate === "boolean");
|
|
17852
|
+
}
|
|
17853
|
+
async function* parseRuntimeGatewaySse(body) {
|
|
17854
|
+
const reader = body.getReader();
|
|
17855
|
+
const decoder = new TextDecoder;
|
|
17856
|
+
let buffer = "";
|
|
17857
|
+
let event = "message";
|
|
17858
|
+
let id;
|
|
17859
|
+
let data = [];
|
|
17860
|
+
const flush = () => {
|
|
17861
|
+
if (data.length === 0)
|
|
17862
|
+
return null;
|
|
17863
|
+
const raw = data.join(`
|
|
17864
|
+
`);
|
|
17865
|
+
data = [];
|
|
17866
|
+
try {
|
|
17867
|
+
return { event, id, data: JSON.parse(raw) };
|
|
17868
|
+
} catch {
|
|
17869
|
+
throw new RuntimeGatewayError("Runtime Gateway SSE event has invalid JSON", "protocol");
|
|
17870
|
+
} finally {
|
|
17871
|
+
event = "message";
|
|
17872
|
+
id = undefined;
|
|
17873
|
+
}
|
|
17874
|
+
};
|
|
17875
|
+
try {
|
|
17876
|
+
while (true) {
|
|
17877
|
+
const chunk = await reader.read();
|
|
17878
|
+
if (chunk.done)
|
|
17879
|
+
break;
|
|
17880
|
+
buffer += decoder.decode(chunk.value, { stream: true }).replace(/\r\n/g, `
|
|
17881
|
+
`);
|
|
17882
|
+
let newline = buffer.indexOf(`
|
|
17883
|
+
`);
|
|
17884
|
+
while (newline >= 0) {
|
|
17885
|
+
const line = buffer.slice(0, newline);
|
|
17886
|
+
buffer = buffer.slice(newline + 1);
|
|
17887
|
+
if (line === "") {
|
|
17888
|
+
const parsed2 = flush();
|
|
17889
|
+
if (parsed2)
|
|
17890
|
+
yield parsed2;
|
|
17891
|
+
} else if (line.startsWith("event:")) {
|
|
17892
|
+
event = line.slice(6).trim() || "message";
|
|
17893
|
+
} else if (line.startsWith("id:")) {
|
|
17894
|
+
const parsed2 = Number.parseInt(line.slice(3).trim(), 10);
|
|
17895
|
+
if (Number.isFinite(parsed2))
|
|
17896
|
+
id = parsed2;
|
|
17897
|
+
} else if (line.startsWith("data:")) {
|
|
17898
|
+
data.push(line.slice(5).trimStart());
|
|
17899
|
+
}
|
|
17900
|
+
newline = buffer.indexOf(`
|
|
17901
|
+
`);
|
|
17902
|
+
}
|
|
17903
|
+
}
|
|
17904
|
+
buffer += decoder.decode();
|
|
17905
|
+
if (buffer.startsWith("data:"))
|
|
17906
|
+
data.push(buffer.slice(5).trimStart());
|
|
17907
|
+
const parsed = flush();
|
|
17908
|
+
if (parsed)
|
|
17909
|
+
yield parsed;
|
|
17910
|
+
} finally {
|
|
17911
|
+
reader.releaseLock();
|
|
17912
|
+
}
|
|
17913
|
+
}
|
|
17914
|
+
|
|
17915
|
+
class RuntimeGatewayClient {
|
|
17916
|
+
options;
|
|
17917
|
+
pathPrefix;
|
|
17918
|
+
request;
|
|
17919
|
+
requestTimeoutMs;
|
|
17920
|
+
compactTimeoutMs;
|
|
17921
|
+
constructor(options) {
|
|
17922
|
+
this.options = options;
|
|
17923
|
+
this.pathPrefix = options.pathPrefix ?? RUNTIME_GATEWAY_CONTROL_PATH;
|
|
17924
|
+
this.request = options.fetch ?? fetch;
|
|
17925
|
+
this.requestTimeoutMs = options.requestTimeoutMs ?? 5000;
|
|
17926
|
+
this.compactTimeoutMs = options.compactTimeoutMs ?? 5 * 60000;
|
|
17927
|
+
}
|
|
17928
|
+
endpoint(path) {
|
|
17929
|
+
if (!this.options.baseUrl || !this.options.token) {
|
|
17930
|
+
throw new RuntimeGatewayError("Runtime Gateway is not configured", "config");
|
|
17931
|
+
}
|
|
17932
|
+
return `${this.options.baseUrl.replace(/\/+$/, "")}${this.pathPrefix}${path}`;
|
|
17933
|
+
}
|
|
17934
|
+
async authorization() {
|
|
17935
|
+
try {
|
|
17936
|
+
const token = typeof this.options.token === "function" ? await this.options.token() : this.options.token;
|
|
17937
|
+
return `Bearer ${token}`;
|
|
17938
|
+
} catch (error) {
|
|
17939
|
+
throw new RuntimeGatewayError(error instanceof Error ? error.message : "Runtime Gateway token resolution failed", "config");
|
|
17940
|
+
}
|
|
17941
|
+
}
|
|
17942
|
+
async send(path, init, timeoutMs) {
|
|
17943
|
+
const authorization = await this.authorization();
|
|
17944
|
+
try {
|
|
17945
|
+
return await this.request(this.endpoint(path), {
|
|
17946
|
+
...init,
|
|
17947
|
+
headers: { authorization, ...init.headers },
|
|
17948
|
+
signal: timeoutMs === undefined ? init.signal : AbortSignal.timeout(timeoutMs)
|
|
17949
|
+
});
|
|
17950
|
+
} catch (error) {
|
|
17951
|
+
if (error instanceof DOMException && error.name === "TimeoutError") {
|
|
17952
|
+
throw new RuntimeGatewayError("Runtime Gateway request timed out", "timeout");
|
|
17953
|
+
}
|
|
17954
|
+
throw new RuntimeGatewayError("Runtime Gateway request failed", "transport");
|
|
17955
|
+
}
|
|
17956
|
+
}
|
|
17957
|
+
async health() {
|
|
17958
|
+
const response = await this.send("/health", { method: "GET" }, this.requestTimeoutMs);
|
|
17959
|
+
if (!response.ok) {
|
|
17960
|
+
throw new RuntimeGatewayError("Runtime Gateway health request failed", "http", response.status);
|
|
17961
|
+
}
|
|
17962
|
+
const body = await json(response);
|
|
17963
|
+
if (!validHealth(body)) {
|
|
17964
|
+
throw new RuntimeGatewayError("Runtime Gateway health response is incompatible", "protocol");
|
|
17965
|
+
}
|
|
17966
|
+
return body;
|
|
17967
|
+
}
|
|
17968
|
+
async submitTurn(input) {
|
|
17969
|
+
const response = await this.send("/turns", {
|
|
17970
|
+
method: "POST",
|
|
17971
|
+
headers: { "content-type": "application/json" },
|
|
17972
|
+
body: JSON.stringify({ v: RUNTIME_GATEWAY_VERSION, ...input })
|
|
17973
|
+
}, this.requestTimeoutMs);
|
|
17974
|
+
if (response.status !== 202) {
|
|
17975
|
+
throw new RuntimeGatewayError("Runtime Gateway turn was not accepted", "http", response.status);
|
|
17976
|
+
}
|
|
17977
|
+
const body = await json(response);
|
|
17978
|
+
if (!validTurnAck(body)) {
|
|
17979
|
+
throw new RuntimeGatewayError("Runtime Gateway turn acknowledgement is incompatible", "protocol");
|
|
17980
|
+
}
|
|
17981
|
+
return body;
|
|
17982
|
+
}
|
|
17983
|
+
async deriveTopic(topicId, input) {
|
|
17984
|
+
const response = await this.send(`/topics/${encodeURIComponent(topicId)}/derive`, {
|
|
17985
|
+
method: "POST",
|
|
17986
|
+
headers: { "content-type": "application/json" },
|
|
17987
|
+
body: JSON.stringify({ v: RUNTIME_GATEWAY_VERSION, ...input })
|
|
17988
|
+
}, this.requestTimeoutMs);
|
|
17989
|
+
if (response.status !== 201) {
|
|
17990
|
+
throw new RuntimeGatewayError("Runtime Gateway topic derive failed", "http", response.status);
|
|
17991
|
+
}
|
|
17992
|
+
const body = await json(response);
|
|
17993
|
+
const topic = record(body)?.topic;
|
|
17994
|
+
const topicRecord = record(topic);
|
|
17995
|
+
if (!topicRecord || typeof topicRecord.id !== "string" || typeof topicRecord.title !== "string") {
|
|
17996
|
+
throw new RuntimeGatewayError("Runtime Gateway topic derive response is incompatible", "protocol");
|
|
17997
|
+
}
|
|
17998
|
+
return topic;
|
|
17999
|
+
}
|
|
18000
|
+
async ensureManagerTopic(userId) {
|
|
18001
|
+
const response = await this.send("/manager-topic", {
|
|
18002
|
+
method: "POST",
|
|
18003
|
+
headers: { "content-type": "application/json" },
|
|
18004
|
+
body: JSON.stringify({ v: RUNTIME_GATEWAY_VERSION, userId })
|
|
18005
|
+
}, this.requestTimeoutMs);
|
|
18006
|
+
if (!response.ok) {
|
|
18007
|
+
throw new RuntimeGatewayError("Runtime Gateway manager topic request failed", "http", response.status);
|
|
18008
|
+
}
|
|
18009
|
+
const body = record(await json(response));
|
|
18010
|
+
if (body?.ok !== true || body.v !== RUNTIME_GATEWAY_VERSION || !validManagerTopic(body.topic)) {
|
|
18011
|
+
throw new RuntimeGatewayError("Runtime Gateway manager topic response is incompatible", "protocol");
|
|
18012
|
+
}
|
|
18013
|
+
return body.topic;
|
|
18014
|
+
}
|
|
18015
|
+
async deleteMessage(topicId, messageId, actorUserId, allowAdmin = false) {
|
|
18016
|
+
const response = await this.send(`/topics/${encodeURIComponent(topicId)}/messages/${encodeURIComponent(messageId)}`, {
|
|
18017
|
+
method: "DELETE",
|
|
18018
|
+
headers: { "content-type": "application/json" },
|
|
18019
|
+
body: JSON.stringify({
|
|
18020
|
+
v: RUNTIME_GATEWAY_VERSION,
|
|
18021
|
+
actorUserId,
|
|
18022
|
+
...allowAdmin ? { allowAdmin: true } : {}
|
|
18023
|
+
})
|
|
18024
|
+
}, this.requestTimeoutMs);
|
|
18025
|
+
if (!response.ok) {
|
|
18026
|
+
throw new RuntimeGatewayError("Runtime Gateway message delete failed", "http", response.status);
|
|
18027
|
+
}
|
|
18028
|
+
const body = record(await json(response));
|
|
18029
|
+
if (body?.ok !== true || body.v !== RUNTIME_GATEWAY_VERSION || !validDeletedMessage(body.message)) {
|
|
18030
|
+
throw new RuntimeGatewayError("Runtime Gateway message delete response is incompatible", "protocol");
|
|
18031
|
+
}
|
|
18032
|
+
return body.message;
|
|
18033
|
+
}
|
|
18034
|
+
async cronRequest(path, init) {
|
|
18035
|
+
const response = await this.send(path, init, this.requestTimeoutMs);
|
|
18036
|
+
if (!response.ok) {
|
|
18037
|
+
throw new RuntimeGatewayError("Runtime Gateway cron request failed", "http", response.status);
|
|
18038
|
+
}
|
|
18039
|
+
const body = record(await json(response));
|
|
18040
|
+
if (body?.ok !== true || body.v !== RUNTIME_GATEWAY_VERSION) {
|
|
18041
|
+
throw new RuntimeGatewayError("Runtime Gateway cron response is incompatible", "protocol");
|
|
18042
|
+
}
|
|
18043
|
+
return body;
|
|
18044
|
+
}
|
|
18045
|
+
async listCronScripts() {
|
|
18046
|
+
const body = await this.cronRequest("/cron/scripts", { method: "GET" });
|
|
18047
|
+
if (!Array.isArray(body.scripts) || !body.scripts.every((value) => typeof value === "string")) {
|
|
18048
|
+
throw new RuntimeGatewayError("Runtime Gateway cron scripts are incompatible", "protocol");
|
|
18049
|
+
}
|
|
18050
|
+
return body.scripts;
|
|
18051
|
+
}
|
|
18052
|
+
async listCronJobs(userId, actorUserId, actorIsAdmin = false) {
|
|
18053
|
+
const search = new URLSearchParams({
|
|
18054
|
+
user: userId,
|
|
18055
|
+
actorUserId,
|
|
18056
|
+
actorIsAdmin: actorIsAdmin ? "1" : "0"
|
|
18057
|
+
});
|
|
18058
|
+
const body = await this.cronRequest(`/cron/jobs?${search}`, { method: "GET" });
|
|
18059
|
+
if (!Array.isArray(body.jobs) || !body.jobs.every(validCronJob)) {
|
|
18060
|
+
throw new RuntimeGatewayError("Runtime Gateway cron jobs are incompatible", "protocol");
|
|
18061
|
+
}
|
|
18062
|
+
return body.jobs;
|
|
18063
|
+
}
|
|
18064
|
+
async createCronJob(input) {
|
|
18065
|
+
const body = await this.cronRequest("/cron/jobs", {
|
|
18066
|
+
method: "POST",
|
|
18067
|
+
headers: { "content-type": "application/json" },
|
|
18068
|
+
body: JSON.stringify({ v: RUNTIME_GATEWAY_VERSION, ...input })
|
|
18069
|
+
});
|
|
18070
|
+
if (!validCronJob(body.job)) {
|
|
18071
|
+
throw new RuntimeGatewayError("Runtime Gateway cron job is incompatible", "protocol");
|
|
18072
|
+
}
|
|
18073
|
+
return body.job;
|
|
18074
|
+
}
|
|
18075
|
+
async updateCronJob(jobId, userId, actorUserId, patch, actorIsAdmin = false) {
|
|
18076
|
+
const body = await this.cronRequest(`/cron/jobs/${encodeURIComponent(jobId)}`, {
|
|
18077
|
+
method: "PATCH",
|
|
18078
|
+
headers: { "content-type": "application/json" },
|
|
18079
|
+
body: JSON.stringify({
|
|
18080
|
+
v: RUNTIME_GATEWAY_VERSION,
|
|
18081
|
+
userId,
|
|
18082
|
+
actorUserId,
|
|
18083
|
+
actorIsAdmin,
|
|
18084
|
+
patch
|
|
18085
|
+
})
|
|
18086
|
+
});
|
|
18087
|
+
if (!validCronJob(body.job)) {
|
|
18088
|
+
throw new RuntimeGatewayError("Runtime Gateway cron job is incompatible", "protocol");
|
|
18089
|
+
}
|
|
18090
|
+
return body.job;
|
|
18091
|
+
}
|
|
18092
|
+
async deleteCronJob(jobId, userId, actorUserId, actorIsAdmin = false) {
|
|
18093
|
+
const search = new URLSearchParams({
|
|
18094
|
+
user: userId,
|
|
18095
|
+
actorUserId,
|
|
18096
|
+
actorIsAdmin: actorIsAdmin ? "1" : "0"
|
|
18097
|
+
});
|
|
18098
|
+
const body = await this.cronRequest(`/cron/jobs/${encodeURIComponent(jobId)}?${search}`, {
|
|
18099
|
+
method: "DELETE"
|
|
18100
|
+
});
|
|
18101
|
+
if (typeof body.deleted !== "string") {
|
|
18102
|
+
throw new RuntimeGatewayError("Runtime Gateway cron delete is incompatible", "protocol");
|
|
18103
|
+
}
|
|
18104
|
+
return body.deleted;
|
|
18105
|
+
}
|
|
18106
|
+
async* events(after, topicId, signal) {
|
|
18107
|
+
const search = new URLSearchParams({ after: String(Math.max(0, after)) });
|
|
18108
|
+
if (topicId)
|
|
18109
|
+
search.set("topicId", topicId);
|
|
18110
|
+
const response = await this.send(`/events?${search}`, { method: "GET", signal });
|
|
18111
|
+
if (!response.ok || !response.body) {
|
|
18112
|
+
throw new RuntimeGatewayError("Runtime Gateway event stream failed", "http", response.status);
|
|
18113
|
+
}
|
|
18114
|
+
yield* parseRuntimeGatewaySse(response.body);
|
|
18115
|
+
}
|
|
18116
|
+
async getTopicUsage(topicId, userId) {
|
|
18117
|
+
const response = await this.send(`/topics/${encodeURIComponent(topicId)}/usage?user=${encodeURIComponent(userId)}`, { method: "GET" }, this.requestTimeoutMs);
|
|
18118
|
+
if (!response.ok) {
|
|
18119
|
+
throw new RuntimeGatewayError("Runtime Gateway topic usage request failed", "http", response.status);
|
|
18120
|
+
}
|
|
18121
|
+
const usage = record(record(await json(response))?.usage);
|
|
18122
|
+
const read = (key) => {
|
|
18123
|
+
const value = usage?.[key];
|
|
18124
|
+
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
18125
|
+
};
|
|
18126
|
+
return {
|
|
18127
|
+
queries: read("queries"),
|
|
18128
|
+
inputTokens: read("inputTokens"),
|
|
18129
|
+
outputTokens: read("outputTokens"),
|
|
18130
|
+
cacheCreationInputTokens: read("cacheCreationInputTokens"),
|
|
18131
|
+
cacheReadInputTokens: read("cacheReadInputTokens"),
|
|
18132
|
+
estimatedCostUsd: read("estimatedCostUsd")
|
|
18133
|
+
};
|
|
18134
|
+
}
|
|
18135
|
+
async abortTurn(topicId, userId) {
|
|
18136
|
+
const response = await this.send(`/topics/${encodeURIComponent(topicId)}/abort`, {
|
|
18137
|
+
method: "POST",
|
|
18138
|
+
headers: { "content-type": "application/json" },
|
|
18139
|
+
body: JSON.stringify({ v: RUNTIME_GATEWAY_VERSION, userId })
|
|
18140
|
+
}, this.requestTimeoutMs);
|
|
18141
|
+
if (!response.ok) {
|
|
18142
|
+
throw new RuntimeGatewayError("Runtime Gateway turn abort failed", "http", response.status);
|
|
18143
|
+
}
|
|
18144
|
+
const body = record(await json(response));
|
|
18145
|
+
if (body?.ok !== true || body.v !== RUNTIME_GATEWAY_VERSION || typeof body.aborted !== "boolean") {
|
|
18146
|
+
throw new RuntimeGatewayError("Runtime Gateway turn abort response is incompatible", "protocol");
|
|
18147
|
+
}
|
|
18148
|
+
return body.aborted;
|
|
18149
|
+
}
|
|
18150
|
+
async resetSession(topicId, userId, reason, actorUserId) {
|
|
18151
|
+
return this.sessionCommand(topicId, userId, "reset", reason, actorUserId);
|
|
18152
|
+
}
|
|
18153
|
+
async compactSession(topicId, userId, reason, actorUserId) {
|
|
18154
|
+
return this.sessionCommand(topicId, userId, "compact", reason, actorUserId);
|
|
18155
|
+
}
|
|
18156
|
+
async sessionCommand(topicId, userId, command, reason, actorUserId) {
|
|
18157
|
+
const response = await this.send(`/topics/${encodeURIComponent(topicId)}/session/${command}`, {
|
|
18158
|
+
method: "POST",
|
|
18159
|
+
headers: { "content-type": "application/json" },
|
|
18160
|
+
body: JSON.stringify({
|
|
18161
|
+
v: RUNTIME_GATEWAY_VERSION,
|
|
18162
|
+
userId,
|
|
18163
|
+
...actorUserId ? { actorUserId } : {},
|
|
18164
|
+
...reason ? { reason } : {}
|
|
18165
|
+
})
|
|
18166
|
+
}, command === "compact" ? this.compactTimeoutMs : this.requestTimeoutMs);
|
|
18167
|
+
if (!response.ok) {
|
|
18168
|
+
throw new RuntimeGatewayError(`Runtime Gateway session ${command} failed`, "http", response.status);
|
|
18169
|
+
}
|
|
18170
|
+
const body = record(await json(response));
|
|
18171
|
+
if (body?.ok !== true || body.v !== RUNTIME_GATEWAY_VERSION) {
|
|
18172
|
+
throw new RuntimeGatewayError(`Runtime Gateway session ${command} response is incompatible`, "protocol");
|
|
18173
|
+
}
|
|
18174
|
+
return typeof body.result === "string" ? body.result : undefined;
|
|
18175
|
+
}
|
|
18176
|
+
}
|
|
18177
|
+
var RUNTIME_GATEWAY_VERSION = 1, RUNTIME_GATEWAY_CONTROL_PATH = "/api/v1/control/runtime/v1", RuntimeGatewayError, HOST_MCP_SERVER_NAME, HOST_MCP_SERVER_KEYS, HTTP_HEADER_NAME;
|
|
18178
|
+
var init_runtime_gateway = __esm(() => {
|
|
18179
|
+
RuntimeGatewayError = class RuntimeGatewayError extends Error {
|
|
18180
|
+
kind;
|
|
18181
|
+
status;
|
|
18182
|
+
constructor(message, kind, status) {
|
|
18183
|
+
super(message);
|
|
18184
|
+
this.kind = kind;
|
|
18185
|
+
this.status = status;
|
|
18186
|
+
}
|
|
18187
|
+
};
|
|
18188
|
+
HOST_MCP_SERVER_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
18189
|
+
HOST_MCP_SERVER_KEYS = new Set(["type", "url", "headers", "timeout"]);
|
|
18190
|
+
HTTP_HEADER_NAME = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
|
|
18191
|
+
});
|
|
18192
|
+
|
|
18193
|
+
// ../../packages/core/src/storage/topic-host-mcp-grants.ts
|
|
18194
|
+
function getTopicHostMcpGrant(topicId) {
|
|
18195
|
+
const row = db.query("SELECT servers_json FROM api_topic_host_mcp_grants WHERE topic_id = ?").get(topicId);
|
|
18196
|
+
if (!row)
|
|
18197
|
+
return null;
|
|
18198
|
+
try {
|
|
18199
|
+
return parseHostMcpServers(JSON.parse(row.servers_json)) ?? null;
|
|
18200
|
+
} catch {
|
|
18201
|
+
return null;
|
|
18202
|
+
}
|
|
18203
|
+
}
|
|
18204
|
+
function recordTopicHostMcpGrant(topicId, servers) {
|
|
18205
|
+
const validated = parseHostMcpServers(servers) ?? {};
|
|
18206
|
+
if (Object.keys(validated).length === 0) {
|
|
18207
|
+
deleteTopicHostMcpGrant(topicId);
|
|
18208
|
+
return;
|
|
18209
|
+
}
|
|
18210
|
+
db.query(`INSERT INTO api_topic_host_mcp_grants (topic_id, servers_json, updated_at)
|
|
18211
|
+
VALUES (?, ?, ?)
|
|
18212
|
+
ON CONFLICT(topic_id) DO UPDATE SET
|
|
18213
|
+
servers_json = excluded.servers_json,
|
|
18214
|
+
updated_at = excluded.updated_at`).run(topicId, JSON.stringify(validated), new Date().toISOString());
|
|
18215
|
+
}
|
|
18216
|
+
function deleteTopicHostMcpGrant(topicId) {
|
|
18217
|
+
db.query("DELETE FROM api_topic_host_mcp_grants WHERE topic_id = ?").run(topicId);
|
|
18218
|
+
}
|
|
18219
|
+
var init_topic_host_mcp_grants = __esm(async () => {
|
|
18220
|
+
init_runtime_gateway();
|
|
18221
|
+
await init_forum_db();
|
|
18222
|
+
db.exec(`
|
|
18223
|
+
CREATE TABLE IF NOT EXISTS api_topic_host_mcp_grants (
|
|
18224
|
+
topic_id TEXT PRIMARY KEY,
|
|
18225
|
+
servers_json TEXT NOT NULL,
|
|
18226
|
+
updated_at TEXT NOT NULL
|
|
18227
|
+
)
|
|
18228
|
+
`);
|
|
18229
|
+
});
|
|
18230
|
+
|
|
17734
18231
|
// ../../packages/core/src/storage/topic-tool-capabilities.ts
|
|
17735
18232
|
function getTopicToolCapabilities(topicId) {
|
|
17736
18233
|
const row = db.query(`SELECT visual_tools, file_delivery_tools
|
|
@@ -17918,6 +18415,7 @@ async function deleteTopicCascadeImpl(topic, userId, options, deletingAncestorId
|
|
|
17918
18415
|
logger.warn({ err: err2, topicId, cwd }, "deleteTopicCascade: workspace cleanup failed");
|
|
17919
18416
|
}
|
|
17920
18417
|
deleteTopicVisuals(topicId);
|
|
18418
|
+
deleteTopicHostMcpGrant(topicId);
|
|
17921
18419
|
deleteTopicToolCapabilities(topicId);
|
|
17922
18420
|
deleteTopicArchiveState(topicId);
|
|
17923
18421
|
deleteMessagesForTopic(topicId);
|
|
@@ -17980,6 +18478,7 @@ var init_lifecycle = __esm(async () => {
|
|
|
17980
18478
|
await init_token_stats();
|
|
17981
18479
|
await init_topic_archive();
|
|
17982
18480
|
await init_topic_archive_state();
|
|
18481
|
+
await init_topic_host_mcp_grants();
|
|
17983
18482
|
await init_topic_tool_capabilities();
|
|
17984
18483
|
TopicArchiveRequiredError = class TopicArchiveRequiredError extends Error {
|
|
17985
18484
|
code = "TOPIC_ARCHIVE_FAILED";
|
|
@@ -18101,8 +18600,8 @@ function parseAttachmentNames(raw) {
|
|
|
18101
18600
|
return parsed.map((item) => {
|
|
18102
18601
|
if (!item || typeof item !== "object")
|
|
18103
18602
|
return;
|
|
18104
|
-
const
|
|
18105
|
-
const name =
|
|
18603
|
+
const record2 = item;
|
|
18604
|
+
const name = record2.filename ?? record2.name ?? record2.id;
|
|
18106
18605
|
return typeof name === "string" && name.trim() ? name.trim() : undefined;
|
|
18107
18606
|
}).filter((name) => Boolean(name));
|
|
18108
18607
|
} catch {
|
|
@@ -19481,6 +19980,7 @@ __export(exports_turn_runner, {
|
|
|
19481
19980
|
resolveWikiMemoryMirror: () => resolveWikiMemoryMirror,
|
|
19482
19981
|
resolveVisualMediaInput: () => resolveVisualMediaInput,
|
|
19483
19982
|
resolveTurnToolCapabilities: () => resolveTurnToolCapabilities,
|
|
19983
|
+
resolveTurnHostMcpServers: () => resolveTurnHostMcpServers,
|
|
19484
19984
|
resolveTopicTurnSession: () => resolveTopicTurnSession,
|
|
19485
19985
|
resolveTopicTurnExecution: () => resolveTopicTurnExecution,
|
|
19486
19986
|
resolveSessionRetry: () => resolveSessionRetry,
|
|
@@ -19837,6 +20337,12 @@ function resolveTurnToolCapabilities(topicId, params) {
|
|
|
19837
20337
|
fileDeliveryTools: params.fileDeliveryTools ?? granted?.fileDeliveryTools ?? false
|
|
19838
20338
|
};
|
|
19839
20339
|
}
|
|
20340
|
+
function resolveTurnHostMcpServers(topic, sessionType) {
|
|
20341
|
+
const effectiveSessionType = sessionType ?? (topic.kind === "manager" ? "manager" : "forum");
|
|
20342
|
+
if (topic.kind !== "manager" || effectiveSessionType !== "manager")
|
|
20343
|
+
return;
|
|
20344
|
+
return getTopicHostMcpGrant(topic.id) ?? undefined;
|
|
20345
|
+
}
|
|
19840
20346
|
function mergeSupersedingUserTurn(running, incoming) {
|
|
19841
20347
|
const userMessages = [
|
|
19842
20348
|
...running.userMessages ?? [legacyUserTurnEnvelope(running.prompt, running.attachments)],
|
|
@@ -20056,6 +20562,7 @@ function startAiTurn(params) {
|
|
|
20056
20562
|
const sessionName = params.sessionName ?? topic.title;
|
|
20057
20563
|
const sessionType = params.sessionType;
|
|
20058
20564
|
const { visualTools, fileDeliveryTools } = resolveTurnToolCapabilities(topicId, params);
|
|
20565
|
+
const hostMcpServers = resolveTurnHostMcpServers(topic, sessionType);
|
|
20059
20566
|
const onSessionId = params.onSessionId;
|
|
20060
20567
|
const onSessionReset = params.onSessionReset;
|
|
20061
20568
|
const bridgeSessionFromHistory = params.bridgeSessionFromHistory === true;
|
|
@@ -20555,6 +21062,7 @@ function startAiTurn(params) {
|
|
|
20555
21062
|
autoContinue: allowAutoContinue && !silent,
|
|
20556
21063
|
visualTools,
|
|
20557
21064
|
fileDeliveryTools,
|
|
21065
|
+
hostMcpServers,
|
|
20558
21066
|
...threadRootId ? { threadRootId } : {},
|
|
20559
21067
|
peerBridge
|
|
20560
21068
|
});
|
|
@@ -20898,6 +21406,7 @@ var init_turn_runner = __esm(async () => {
|
|
|
20898
21406
|
await init_runtime_leases();
|
|
20899
21407
|
await init_runtime_topic_state();
|
|
20900
21408
|
await init_runtime_turn_requests();
|
|
21409
|
+
await init_topic_host_mcp_grants();
|
|
20901
21410
|
await init_topic_tool_capabilities();
|
|
20902
21411
|
await init_wiki();
|
|
20903
21412
|
init_wiki_summary_names();
|
|
@@ -21921,6 +22430,14 @@ function gatewayPayloadHash(params, requestId, actorUserId) {
|
|
|
21921
22430
|
])).digest("hex");
|
|
21922
22431
|
}
|
|
21923
22432
|
function submitRuntimeGatewayTurn(params) {
|
|
22433
|
+
const hostMcpServers = parseHostMcpServers(params.hostMcpServers);
|
|
22434
|
+
if (hostMcpServers !== undefined && params.topic.kind !== "manager") {
|
|
22435
|
+
throw new TypeError("hostMcpServers is only allowed for manager topics");
|
|
22436
|
+
}
|
|
22437
|
+
const conflictingHostMcpName = Object.keys(hostMcpServers ?? {}).find(isReservedRuntimeMcpServerName);
|
|
22438
|
+
if (conflictingHostMcpName) {
|
|
22439
|
+
throw new TypeError(`host MCP server name conflicts with node catalog: ${conflictingHostMcpName}`);
|
|
22440
|
+
}
|
|
21924
22441
|
const requestId = params.requestId ?? params.clientMessageId;
|
|
21925
22442
|
const actorUserId = params.actorUserId ?? params.userId;
|
|
21926
22443
|
const sourceAdapter = params.sourceAdapter?.trim() || "runtime-gateway";
|
|
@@ -22001,6 +22518,9 @@ function submitRuntimeGatewayTurn(params) {
|
|
|
22001
22518
|
}
|
|
22002
22519
|
});
|
|
22003
22520
|
}
|
|
22521
|
+
if (hostMcpServers !== undefined) {
|
|
22522
|
+
recordTopicHostMcpGrant(params.topic.id, hostMcpServers);
|
|
22523
|
+
}
|
|
22004
22524
|
const acceptedEvent = appendRuntimeEvent("runtime-gateway-ingress", {
|
|
22005
22525
|
type: "ai-status",
|
|
22006
22526
|
topicId: params.topic.id,
|
|
@@ -22032,7 +22552,9 @@ function submitRuntimeGatewayTurn(params) {
|
|
|
22032
22552
|
}
|
|
22033
22553
|
var RuntimeGatewayIdempotencyConflictError;
|
|
22034
22554
|
var init_submit_runtime_gateway_turn = __esm(async () => {
|
|
22555
|
+
init_mcp_config();
|
|
22035
22556
|
init_file_hooks();
|
|
22557
|
+
init_runtime_gateway();
|
|
22036
22558
|
await init_api_messages();
|
|
22037
22559
|
await init_api_topics();
|
|
22038
22560
|
await init_forum_db();
|
|
@@ -22041,6 +22563,7 @@ var init_submit_runtime_gateway_turn = __esm(async () => {
|
|
|
22041
22563
|
await init_runtime_leases();
|
|
22042
22564
|
await init_runtime_topic_state();
|
|
22043
22565
|
await init_runtime_turn_requests();
|
|
22566
|
+
await init_topic_host_mcp_grants();
|
|
22044
22567
|
await init_topic_tool_capabilities();
|
|
22045
22568
|
RuntimeGatewayIdempotencyConflictError = class RuntimeGatewayIdempotencyConflictError extends Error {
|
|
22046
22569
|
constructor(message = "clientMessageId or requestId is already bound to another turn") {
|
|
@@ -25328,6 +25851,7 @@ __export(exports_node_host, {
|
|
|
25328
25851
|
killOwnedCodexTreesForShutdown: () => killOwnedCodexTreesForShutdown,
|
|
25329
25852
|
killAllPlaywright: () => killAllPlaywright,
|
|
25330
25853
|
killAllBgBash: () => killAllBgBash,
|
|
25854
|
+
isReservedRuntimeMcpServerName: () => isReservedRuntimeMcpServerName,
|
|
25331
25855
|
isParticipant: () => isParticipant,
|
|
25332
25856
|
getVisibleTopics: () => getVisibleTopics,
|
|
25333
25857
|
getTopicStats: () => getTopicStats,
|
|
@@ -26842,10 +27366,10 @@ var init_agent_health = __esm(async () => {
|
|
|
26842
27366
|
|
|
26843
27367
|
// ../../packages/core/src/mcp/session-comm/tell-permissions.ts
|
|
26844
27368
|
function resolveSubagentTellIdentity(currentTopicId, assertedParentTopicId) {
|
|
26845
|
-
const
|
|
26846
|
-
const identityInvalid = Boolean(assertedParentTopicId && (!
|
|
26847
|
-
const restricted = identityInvalid || Boolean(assertedParentTopicId) || Boolean(
|
|
26848
|
-
const parentTopicId = identityInvalid ? undefined : assertedParentTopicId || (
|
|
27369
|
+
const record2 = currentTopicId ? getTopic(currentTopicId) : undefined;
|
|
27370
|
+
const identityInvalid = Boolean(assertedParentTopicId && (!record2?.isSubagent || record2.parentTopicId !== assertedParentTopicId)) || Boolean(record2?.isSubagent && !record2.parentTopicId);
|
|
27371
|
+
const restricted = identityInvalid || Boolean(assertedParentTopicId) || Boolean(record2?.isSubagent);
|
|
27372
|
+
const parentTopicId = identityInvalid ? undefined : assertedParentTopicId || (record2?.isSubagent ? record2.parentTopicId : undefined);
|
|
26849
27373
|
return { currentTopicId, parentTopicId, restricted };
|
|
26850
27374
|
}
|
|
26851
27375
|
function canSubagentTellTarget(identity, targetTopicId) {
|
|
@@ -28561,12 +29085,12 @@ function wikiLastConversation(args) {
|
|
|
28561
29085
|
};
|
|
28562
29086
|
for (const line of lines) {
|
|
28563
29087
|
try {
|
|
28564
|
-
const
|
|
28565
|
-
if (
|
|
29088
|
+
const record2 = JSON.parse(line);
|
|
29089
|
+
if (record2.type === "message" && record2.role === "user") {
|
|
28566
29090
|
flush();
|
|
28567
|
-
currentUser =
|
|
28568
|
-
} else if (
|
|
28569
|
-
currentAssistant +=
|
|
29091
|
+
currentUser = record2.text ?? "";
|
|
29092
|
+
} else if (record2.type === "message" && record2.role === "assistant") {
|
|
29093
|
+
currentAssistant += record2.text ?? "";
|
|
28570
29094
|
}
|
|
28571
29095
|
} catch {}
|
|
28572
29096
|
}
|
|
@@ -31756,378 +32280,6 @@ var init_src3 = __esm(() => {
|
|
|
31756
32280
|
init_spec();
|
|
31757
32281
|
});
|
|
31758
32282
|
|
|
31759
|
-
// ../../packages/core/src/runtime-gateway.ts
|
|
31760
|
-
function record(value) {
|
|
31761
|
-
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
31762
|
-
}
|
|
31763
|
-
async function json(response) {
|
|
31764
|
-
try {
|
|
31765
|
-
return await response.json();
|
|
31766
|
-
} catch {
|
|
31767
|
-
throw new RuntimeGatewayError("Runtime Gateway returned invalid JSON", "protocol", response.status);
|
|
31768
|
-
}
|
|
31769
|
-
}
|
|
31770
|
-
function validHealth(value) {
|
|
31771
|
-
const body = record(value);
|
|
31772
|
-
return Boolean(body?.ok === true && body.v === RUNTIME_GATEWAY_VERSION && Array.isArray(body.capabilities) && typeof body.cursor === "number");
|
|
31773
|
-
}
|
|
31774
|
-
function validAcknowledgedMessage(value) {
|
|
31775
|
-
const message = record(value);
|
|
31776
|
-
return Boolean(message && typeof message.id === "string" && typeof message.topicId === "string" && typeof message.authorId === "string" && typeof message.text === "string" && typeof message.createdAt === "string");
|
|
31777
|
-
}
|
|
31778
|
-
function validTurnAck(value) {
|
|
31779
|
-
const body = record(value);
|
|
31780
|
-
return Boolean(body?.ok === true && body.v === RUNTIME_GATEWAY_VERSION && body.accepted === true && typeof body.deduplicated === "boolean" && typeof body.requestId === "string" && typeof body.clientMessageId === "string" && typeof body.topicId === "string" && typeof body.messageId === "string" && (body.message === undefined || validAcknowledgedMessage(body.message)) && typeof body.cursor === "number");
|
|
31781
|
-
}
|
|
31782
|
-
function validManagerTopic(value) {
|
|
31783
|
-
const topic = record(value);
|
|
31784
|
-
return Boolean(typeof topic?.id === "string" && typeof topic.title === "string" && topic.kind === "manager" && Array.isArray(topic.participants));
|
|
31785
|
-
}
|
|
31786
|
-
function validDeletedMessage(value) {
|
|
31787
|
-
const message = record(value);
|
|
31788
|
-
return Boolean(typeof message?.id === "string" && typeof message.topicId === "string" && typeof message.authorId === "string" && typeof message.text === "string" && message.deleted === true && typeof message.createdAt === "string");
|
|
31789
|
-
}
|
|
31790
|
-
function validCronJob(value) {
|
|
31791
|
-
const job = record(value);
|
|
31792
|
-
return Boolean(typeof job?.id === "string" && typeof job.name === "string" && typeof job.executionPrincipalUserId === "string" && typeof job.actorOwnerUserId === "string" && typeof job.topicId === "string" && typeof job.schedule === "string" && typeof job.enabled === "boolean" && typeof job.nextRunAt === "string" && typeof job.canMutate === "boolean");
|
|
31793
|
-
}
|
|
31794
|
-
async function* parseRuntimeGatewaySse(body) {
|
|
31795
|
-
const reader = body.getReader();
|
|
31796
|
-
const decoder = new TextDecoder;
|
|
31797
|
-
let buffer = "";
|
|
31798
|
-
let event = "message";
|
|
31799
|
-
let id;
|
|
31800
|
-
let data = [];
|
|
31801
|
-
const flush = () => {
|
|
31802
|
-
if (data.length === 0)
|
|
31803
|
-
return null;
|
|
31804
|
-
const raw = data.join(`
|
|
31805
|
-
`);
|
|
31806
|
-
data = [];
|
|
31807
|
-
try {
|
|
31808
|
-
return { event, id, data: JSON.parse(raw) };
|
|
31809
|
-
} catch {
|
|
31810
|
-
throw new RuntimeGatewayError("Runtime Gateway SSE event has invalid JSON", "protocol");
|
|
31811
|
-
} finally {
|
|
31812
|
-
event = "message";
|
|
31813
|
-
id = undefined;
|
|
31814
|
-
}
|
|
31815
|
-
};
|
|
31816
|
-
try {
|
|
31817
|
-
while (true) {
|
|
31818
|
-
const chunk = await reader.read();
|
|
31819
|
-
if (chunk.done)
|
|
31820
|
-
break;
|
|
31821
|
-
buffer += decoder.decode(chunk.value, { stream: true }).replace(/\r\n/g, `
|
|
31822
|
-
`);
|
|
31823
|
-
let newline = buffer.indexOf(`
|
|
31824
|
-
`);
|
|
31825
|
-
while (newline >= 0) {
|
|
31826
|
-
const line = buffer.slice(0, newline);
|
|
31827
|
-
buffer = buffer.slice(newline + 1);
|
|
31828
|
-
if (line === "") {
|
|
31829
|
-
const parsed2 = flush();
|
|
31830
|
-
if (parsed2)
|
|
31831
|
-
yield parsed2;
|
|
31832
|
-
} else if (line.startsWith("event:")) {
|
|
31833
|
-
event = line.slice(6).trim() || "message";
|
|
31834
|
-
} else if (line.startsWith("id:")) {
|
|
31835
|
-
const parsed2 = Number.parseInt(line.slice(3).trim(), 10);
|
|
31836
|
-
if (Number.isFinite(parsed2))
|
|
31837
|
-
id = parsed2;
|
|
31838
|
-
} else if (line.startsWith("data:")) {
|
|
31839
|
-
data.push(line.slice(5).trimStart());
|
|
31840
|
-
}
|
|
31841
|
-
newline = buffer.indexOf(`
|
|
31842
|
-
`);
|
|
31843
|
-
}
|
|
31844
|
-
}
|
|
31845
|
-
buffer += decoder.decode();
|
|
31846
|
-
if (buffer.startsWith("data:"))
|
|
31847
|
-
data.push(buffer.slice(5).trimStart());
|
|
31848
|
-
const parsed = flush();
|
|
31849
|
-
if (parsed)
|
|
31850
|
-
yield parsed;
|
|
31851
|
-
} finally {
|
|
31852
|
-
reader.releaseLock();
|
|
31853
|
-
}
|
|
31854
|
-
}
|
|
31855
|
-
|
|
31856
|
-
class RuntimeGatewayClient {
|
|
31857
|
-
options;
|
|
31858
|
-
pathPrefix;
|
|
31859
|
-
request;
|
|
31860
|
-
requestTimeoutMs;
|
|
31861
|
-
compactTimeoutMs;
|
|
31862
|
-
constructor(options) {
|
|
31863
|
-
this.options = options;
|
|
31864
|
-
this.pathPrefix = options.pathPrefix ?? RUNTIME_GATEWAY_CONTROL_PATH;
|
|
31865
|
-
this.request = options.fetch ?? fetch;
|
|
31866
|
-
this.requestTimeoutMs = options.requestTimeoutMs ?? 5000;
|
|
31867
|
-
this.compactTimeoutMs = options.compactTimeoutMs ?? 5 * 60000;
|
|
31868
|
-
}
|
|
31869
|
-
endpoint(path) {
|
|
31870
|
-
if (!this.options.baseUrl || !this.options.token) {
|
|
31871
|
-
throw new RuntimeGatewayError("Runtime Gateway is not configured", "config");
|
|
31872
|
-
}
|
|
31873
|
-
return `${this.options.baseUrl.replace(/\/+$/, "")}${this.pathPrefix}${path}`;
|
|
31874
|
-
}
|
|
31875
|
-
async authorization() {
|
|
31876
|
-
try {
|
|
31877
|
-
const token = typeof this.options.token === "function" ? await this.options.token() : this.options.token;
|
|
31878
|
-
return `Bearer ${token}`;
|
|
31879
|
-
} catch (error2) {
|
|
31880
|
-
throw new RuntimeGatewayError(error2 instanceof Error ? error2.message : "Runtime Gateway token resolution failed", "config");
|
|
31881
|
-
}
|
|
31882
|
-
}
|
|
31883
|
-
async send(path, init, timeoutMs) {
|
|
31884
|
-
const authorization = await this.authorization();
|
|
31885
|
-
try {
|
|
31886
|
-
return await this.request(this.endpoint(path), {
|
|
31887
|
-
...init,
|
|
31888
|
-
headers: { authorization, ...init.headers },
|
|
31889
|
-
signal: timeoutMs === undefined ? init.signal : AbortSignal.timeout(timeoutMs)
|
|
31890
|
-
});
|
|
31891
|
-
} catch (error2) {
|
|
31892
|
-
if (error2 instanceof DOMException && error2.name === "TimeoutError") {
|
|
31893
|
-
throw new RuntimeGatewayError("Runtime Gateway request timed out", "timeout");
|
|
31894
|
-
}
|
|
31895
|
-
throw new RuntimeGatewayError("Runtime Gateway request failed", "transport");
|
|
31896
|
-
}
|
|
31897
|
-
}
|
|
31898
|
-
async health() {
|
|
31899
|
-
const response = await this.send("/health", { method: "GET" }, this.requestTimeoutMs);
|
|
31900
|
-
if (!response.ok) {
|
|
31901
|
-
throw new RuntimeGatewayError("Runtime Gateway health request failed", "http", response.status);
|
|
31902
|
-
}
|
|
31903
|
-
const body = await json(response);
|
|
31904
|
-
if (!validHealth(body)) {
|
|
31905
|
-
throw new RuntimeGatewayError("Runtime Gateway health response is incompatible", "protocol");
|
|
31906
|
-
}
|
|
31907
|
-
return body;
|
|
31908
|
-
}
|
|
31909
|
-
async submitTurn(input) {
|
|
31910
|
-
const response = await this.send("/turns", {
|
|
31911
|
-
method: "POST",
|
|
31912
|
-
headers: { "content-type": "application/json" },
|
|
31913
|
-
body: JSON.stringify({ v: RUNTIME_GATEWAY_VERSION, ...input })
|
|
31914
|
-
}, this.requestTimeoutMs);
|
|
31915
|
-
if (response.status !== 202) {
|
|
31916
|
-
throw new RuntimeGatewayError("Runtime Gateway turn was not accepted", "http", response.status);
|
|
31917
|
-
}
|
|
31918
|
-
const body = await json(response);
|
|
31919
|
-
if (!validTurnAck(body)) {
|
|
31920
|
-
throw new RuntimeGatewayError("Runtime Gateway turn acknowledgement is incompatible", "protocol");
|
|
31921
|
-
}
|
|
31922
|
-
return body;
|
|
31923
|
-
}
|
|
31924
|
-
async deriveTopic(topicId, input) {
|
|
31925
|
-
const response = await this.send(`/topics/${encodeURIComponent(topicId)}/derive`, {
|
|
31926
|
-
method: "POST",
|
|
31927
|
-
headers: { "content-type": "application/json" },
|
|
31928
|
-
body: JSON.stringify({ v: RUNTIME_GATEWAY_VERSION, ...input })
|
|
31929
|
-
}, this.requestTimeoutMs);
|
|
31930
|
-
if (response.status !== 201) {
|
|
31931
|
-
throw new RuntimeGatewayError("Runtime Gateway topic derive failed", "http", response.status);
|
|
31932
|
-
}
|
|
31933
|
-
const body = await json(response);
|
|
31934
|
-
const topic = record(body)?.topic;
|
|
31935
|
-
const topicRecord = record(topic);
|
|
31936
|
-
if (!topicRecord || typeof topicRecord.id !== "string" || typeof topicRecord.title !== "string") {
|
|
31937
|
-
throw new RuntimeGatewayError("Runtime Gateway topic derive response is incompatible", "protocol");
|
|
31938
|
-
}
|
|
31939
|
-
return topic;
|
|
31940
|
-
}
|
|
31941
|
-
async ensureManagerTopic(userId) {
|
|
31942
|
-
const response = await this.send("/manager-topic", {
|
|
31943
|
-
method: "POST",
|
|
31944
|
-
headers: { "content-type": "application/json" },
|
|
31945
|
-
body: JSON.stringify({ v: RUNTIME_GATEWAY_VERSION, userId })
|
|
31946
|
-
}, this.requestTimeoutMs);
|
|
31947
|
-
if (!response.ok) {
|
|
31948
|
-
throw new RuntimeGatewayError("Runtime Gateway manager topic request failed", "http", response.status);
|
|
31949
|
-
}
|
|
31950
|
-
const body = record(await json(response));
|
|
31951
|
-
if (body?.ok !== true || body.v !== RUNTIME_GATEWAY_VERSION || !validManagerTopic(body.topic)) {
|
|
31952
|
-
throw new RuntimeGatewayError("Runtime Gateway manager topic response is incompatible", "protocol");
|
|
31953
|
-
}
|
|
31954
|
-
return body.topic;
|
|
31955
|
-
}
|
|
31956
|
-
async deleteMessage(topicId, messageId, actorUserId, allowAdmin = false) {
|
|
31957
|
-
const response = await this.send(`/topics/${encodeURIComponent(topicId)}/messages/${encodeURIComponent(messageId)}`, {
|
|
31958
|
-
method: "DELETE",
|
|
31959
|
-
headers: { "content-type": "application/json" },
|
|
31960
|
-
body: JSON.stringify({
|
|
31961
|
-
v: RUNTIME_GATEWAY_VERSION,
|
|
31962
|
-
actorUserId,
|
|
31963
|
-
...allowAdmin ? { allowAdmin: true } : {}
|
|
31964
|
-
})
|
|
31965
|
-
}, this.requestTimeoutMs);
|
|
31966
|
-
if (!response.ok) {
|
|
31967
|
-
throw new RuntimeGatewayError("Runtime Gateway message delete failed", "http", response.status);
|
|
31968
|
-
}
|
|
31969
|
-
const body = record(await json(response));
|
|
31970
|
-
if (body?.ok !== true || body.v !== RUNTIME_GATEWAY_VERSION || !validDeletedMessage(body.message)) {
|
|
31971
|
-
throw new RuntimeGatewayError("Runtime Gateway message delete response is incompatible", "protocol");
|
|
31972
|
-
}
|
|
31973
|
-
return body.message;
|
|
31974
|
-
}
|
|
31975
|
-
async cronRequest(path, init) {
|
|
31976
|
-
const response = await this.send(path, init, this.requestTimeoutMs);
|
|
31977
|
-
if (!response.ok) {
|
|
31978
|
-
throw new RuntimeGatewayError("Runtime Gateway cron request failed", "http", response.status);
|
|
31979
|
-
}
|
|
31980
|
-
const body = record(await json(response));
|
|
31981
|
-
if (body?.ok !== true || body.v !== RUNTIME_GATEWAY_VERSION) {
|
|
31982
|
-
throw new RuntimeGatewayError("Runtime Gateway cron response is incompatible", "protocol");
|
|
31983
|
-
}
|
|
31984
|
-
return body;
|
|
31985
|
-
}
|
|
31986
|
-
async listCronScripts() {
|
|
31987
|
-
const body = await this.cronRequest("/cron/scripts", { method: "GET" });
|
|
31988
|
-
if (!Array.isArray(body.scripts) || !body.scripts.every((value) => typeof value === "string")) {
|
|
31989
|
-
throw new RuntimeGatewayError("Runtime Gateway cron scripts are incompatible", "protocol");
|
|
31990
|
-
}
|
|
31991
|
-
return body.scripts;
|
|
31992
|
-
}
|
|
31993
|
-
async listCronJobs(userId, actorUserId, actorIsAdmin = false) {
|
|
31994
|
-
const search = new URLSearchParams({
|
|
31995
|
-
user: userId,
|
|
31996
|
-
actorUserId,
|
|
31997
|
-
actorIsAdmin: actorIsAdmin ? "1" : "0"
|
|
31998
|
-
});
|
|
31999
|
-
const body = await this.cronRequest(`/cron/jobs?${search}`, { method: "GET" });
|
|
32000
|
-
if (!Array.isArray(body.jobs) || !body.jobs.every(validCronJob)) {
|
|
32001
|
-
throw new RuntimeGatewayError("Runtime Gateway cron jobs are incompatible", "protocol");
|
|
32002
|
-
}
|
|
32003
|
-
return body.jobs;
|
|
32004
|
-
}
|
|
32005
|
-
async createCronJob(input) {
|
|
32006
|
-
const body = await this.cronRequest("/cron/jobs", {
|
|
32007
|
-
method: "POST",
|
|
32008
|
-
headers: { "content-type": "application/json" },
|
|
32009
|
-
body: JSON.stringify({ v: RUNTIME_GATEWAY_VERSION, ...input })
|
|
32010
|
-
});
|
|
32011
|
-
if (!validCronJob(body.job)) {
|
|
32012
|
-
throw new RuntimeGatewayError("Runtime Gateway cron job is incompatible", "protocol");
|
|
32013
|
-
}
|
|
32014
|
-
return body.job;
|
|
32015
|
-
}
|
|
32016
|
-
async updateCronJob(jobId, userId, actorUserId, patch, actorIsAdmin = false) {
|
|
32017
|
-
const body = await this.cronRequest(`/cron/jobs/${encodeURIComponent(jobId)}`, {
|
|
32018
|
-
method: "PATCH",
|
|
32019
|
-
headers: { "content-type": "application/json" },
|
|
32020
|
-
body: JSON.stringify({
|
|
32021
|
-
v: RUNTIME_GATEWAY_VERSION,
|
|
32022
|
-
userId,
|
|
32023
|
-
actorUserId,
|
|
32024
|
-
actorIsAdmin,
|
|
32025
|
-
patch
|
|
32026
|
-
})
|
|
32027
|
-
});
|
|
32028
|
-
if (!validCronJob(body.job)) {
|
|
32029
|
-
throw new RuntimeGatewayError("Runtime Gateway cron job is incompatible", "protocol");
|
|
32030
|
-
}
|
|
32031
|
-
return body.job;
|
|
32032
|
-
}
|
|
32033
|
-
async deleteCronJob(jobId, userId, actorUserId, actorIsAdmin = false) {
|
|
32034
|
-
const search = new URLSearchParams({
|
|
32035
|
-
user: userId,
|
|
32036
|
-
actorUserId,
|
|
32037
|
-
actorIsAdmin: actorIsAdmin ? "1" : "0"
|
|
32038
|
-
});
|
|
32039
|
-
const body = await this.cronRequest(`/cron/jobs/${encodeURIComponent(jobId)}?${search}`, {
|
|
32040
|
-
method: "DELETE"
|
|
32041
|
-
});
|
|
32042
|
-
if (typeof body.deleted !== "string") {
|
|
32043
|
-
throw new RuntimeGatewayError("Runtime Gateway cron delete is incompatible", "protocol");
|
|
32044
|
-
}
|
|
32045
|
-
return body.deleted;
|
|
32046
|
-
}
|
|
32047
|
-
async* events(after, topicId, signal) {
|
|
32048
|
-
const search = new URLSearchParams({ after: String(Math.max(0, after)) });
|
|
32049
|
-
if (topicId)
|
|
32050
|
-
search.set("topicId", topicId);
|
|
32051
|
-
const response = await this.send(`/events?${search}`, { method: "GET", signal });
|
|
32052
|
-
if (!response.ok || !response.body) {
|
|
32053
|
-
throw new RuntimeGatewayError("Runtime Gateway event stream failed", "http", response.status);
|
|
32054
|
-
}
|
|
32055
|
-
yield* parseRuntimeGatewaySse(response.body);
|
|
32056
|
-
}
|
|
32057
|
-
async getTopicUsage(topicId, userId) {
|
|
32058
|
-
const response = await this.send(`/topics/${encodeURIComponent(topicId)}/usage?user=${encodeURIComponent(userId)}`, { method: "GET" }, this.requestTimeoutMs);
|
|
32059
|
-
if (!response.ok) {
|
|
32060
|
-
throw new RuntimeGatewayError("Runtime Gateway topic usage request failed", "http", response.status);
|
|
32061
|
-
}
|
|
32062
|
-
const usage = record(record(await json(response))?.usage);
|
|
32063
|
-
const read = (key) => {
|
|
32064
|
-
const value = usage?.[key];
|
|
32065
|
-
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
32066
|
-
};
|
|
32067
|
-
return {
|
|
32068
|
-
queries: read("queries"),
|
|
32069
|
-
inputTokens: read("inputTokens"),
|
|
32070
|
-
outputTokens: read("outputTokens"),
|
|
32071
|
-
cacheCreationInputTokens: read("cacheCreationInputTokens"),
|
|
32072
|
-
cacheReadInputTokens: read("cacheReadInputTokens"),
|
|
32073
|
-
estimatedCostUsd: read("estimatedCostUsd")
|
|
32074
|
-
};
|
|
32075
|
-
}
|
|
32076
|
-
async abortTurn(topicId, userId) {
|
|
32077
|
-
const response = await this.send(`/topics/${encodeURIComponent(topicId)}/abort`, {
|
|
32078
|
-
method: "POST",
|
|
32079
|
-
headers: { "content-type": "application/json" },
|
|
32080
|
-
body: JSON.stringify({ v: RUNTIME_GATEWAY_VERSION, userId })
|
|
32081
|
-
}, this.requestTimeoutMs);
|
|
32082
|
-
if (!response.ok) {
|
|
32083
|
-
throw new RuntimeGatewayError("Runtime Gateway turn abort failed", "http", response.status);
|
|
32084
|
-
}
|
|
32085
|
-
const body = record(await json(response));
|
|
32086
|
-
if (body?.ok !== true || body.v !== RUNTIME_GATEWAY_VERSION || typeof body.aborted !== "boolean") {
|
|
32087
|
-
throw new RuntimeGatewayError("Runtime Gateway turn abort response is incompatible", "protocol");
|
|
32088
|
-
}
|
|
32089
|
-
return body.aborted;
|
|
32090
|
-
}
|
|
32091
|
-
async resetSession(topicId, userId, reason, actorUserId) {
|
|
32092
|
-
return this.sessionCommand(topicId, userId, "reset", reason, actorUserId);
|
|
32093
|
-
}
|
|
32094
|
-
async compactSession(topicId, userId, reason, actorUserId) {
|
|
32095
|
-
return this.sessionCommand(topicId, userId, "compact", reason, actorUserId);
|
|
32096
|
-
}
|
|
32097
|
-
async sessionCommand(topicId, userId, command, reason, actorUserId) {
|
|
32098
|
-
const response = await this.send(`/topics/${encodeURIComponent(topicId)}/session/${command}`, {
|
|
32099
|
-
method: "POST",
|
|
32100
|
-
headers: { "content-type": "application/json" },
|
|
32101
|
-
body: JSON.stringify({
|
|
32102
|
-
v: RUNTIME_GATEWAY_VERSION,
|
|
32103
|
-
userId,
|
|
32104
|
-
...actorUserId ? { actorUserId } : {},
|
|
32105
|
-
...reason ? { reason } : {}
|
|
32106
|
-
})
|
|
32107
|
-
}, command === "compact" ? this.compactTimeoutMs : this.requestTimeoutMs);
|
|
32108
|
-
if (!response.ok) {
|
|
32109
|
-
throw new RuntimeGatewayError(`Runtime Gateway session ${command} failed`, "http", response.status);
|
|
32110
|
-
}
|
|
32111
|
-
const body = record(await json(response));
|
|
32112
|
-
if (body?.ok !== true || body.v !== RUNTIME_GATEWAY_VERSION) {
|
|
32113
|
-
throw new RuntimeGatewayError(`Runtime Gateway session ${command} response is incompatible`, "protocol");
|
|
32114
|
-
}
|
|
32115
|
-
return typeof body.result === "string" ? body.result : undefined;
|
|
32116
|
-
}
|
|
32117
|
-
}
|
|
32118
|
-
var RUNTIME_GATEWAY_VERSION = 1, RUNTIME_GATEWAY_CONTROL_PATH = "/api/v1/control/runtime/v1", RuntimeGatewayError;
|
|
32119
|
-
var init_runtime_gateway = __esm(() => {
|
|
32120
|
-
RuntimeGatewayError = class RuntimeGatewayError extends Error {
|
|
32121
|
-
kind;
|
|
32122
|
-
status;
|
|
32123
|
-
constructor(message, kind, status) {
|
|
32124
|
-
super(message);
|
|
32125
|
-
this.kind = kind;
|
|
32126
|
-
this.status = status;
|
|
32127
|
-
}
|
|
32128
|
-
};
|
|
32129
|
-
});
|
|
32130
|
-
|
|
32131
32283
|
// ../../packages/core/src/cron-host.ts
|
|
32132
32284
|
var init_cron_host = __esm(async () => {
|
|
32133
32285
|
await init_topic_cleanup();
|
|
@@ -34504,6 +34656,7 @@ function createNodeControlHandler(options) {
|
|
|
34504
34656
|
aiName: getGlobalAiName(),
|
|
34505
34657
|
capabilities: [
|
|
34506
34658
|
"turn-submit-idempotent",
|
|
34659
|
+
"host-mcp-turn-injection",
|
|
34507
34660
|
"turn-events-sse-resume",
|
|
34508
34661
|
"canonical-topic-read",
|
|
34509
34662
|
"canonical-message-read",
|
|
@@ -34728,6 +34881,16 @@ function createNodeControlHandler(options) {
|
|
|
34728
34881
|
const requestId = body.requestId === undefined ? undefined : asRequestId(requiredText(body.requestId, "requestId"));
|
|
34729
34882
|
const threadRootId = body.threadRootId === undefined ? undefined : asMessageId(requiredText(body.threadRootId, "threadRootId"));
|
|
34730
34883
|
const parentId = body.parentId === undefined ? undefined : asMessageId(requiredText(body.parentId, "parentId"));
|
|
34884
|
+
let hostMcpServers;
|
|
34885
|
+
try {
|
|
34886
|
+
hostMcpServers = parseHostMcpServers(body.hostMcpServers);
|
|
34887
|
+
} catch (error2) {
|
|
34888
|
+
throw new ControlRequestError(error2 instanceof Error ? error2.message : "invalid hostMcpServers");
|
|
34889
|
+
}
|
|
34890
|
+
const conflictingHostMcpName = Object.keys(hostMcpServers ?? {}).find(isReservedRuntimeMcpServerName);
|
|
34891
|
+
if (conflictingHostMcpName) {
|
|
34892
|
+
return jsonError(400, `host MCP server name conflicts with node catalog: ${conflictingHostMcpName}`);
|
|
34893
|
+
}
|
|
34731
34894
|
const attachments2 = body.attachments === undefined ? [] : Array.isArray(body.attachments) && body.attachments.every((value) => typeof value === "string" && value.trim()) ? body.attachments : (() => {
|
|
34732
34895
|
throw new ControlRequestError("attachments must be an array of file ids");
|
|
34733
34896
|
})();
|
|
@@ -34740,6 +34903,9 @@ function createNodeControlHandler(options) {
|
|
|
34740
34903
|
if (!topic.participants.some((participant) => participant.userId === userId)) {
|
|
34741
34904
|
return jsonError(404, "Topic not found");
|
|
34742
34905
|
}
|
|
34906
|
+
if (hostMcpServers !== undefined && topic.kind !== "manager") {
|
|
34907
|
+
return jsonError(400, "hostMcpServers is only allowed for manager topics");
|
|
34908
|
+
}
|
|
34743
34909
|
if (attachments2.some((fileId) => !nodeFileStore.allows(fileId, { topicId, ownerUserId: userId }))) {
|
|
34744
34910
|
return jsonError(404, "Attachment not found");
|
|
34745
34911
|
}
|
|
@@ -34773,6 +34939,7 @@ function createNodeControlHandler(options) {
|
|
|
34773
34939
|
silent: body.silent === true,
|
|
34774
34940
|
visualTools: body.visualTools === true,
|
|
34775
34941
|
fileDeliveryTools: body.fileDeliveryTools === true,
|
|
34942
|
+
hostMcpServers,
|
|
34776
34943
|
...attachments2.length ? { attachments: attachments2 } : {},
|
|
34777
34944
|
...threadRootId ? { threadRootId } : {},
|
|
34778
34945
|
...parentId ? { parentId } : {}
|
|
@@ -51271,4 +51438,4 @@ switch (command) {
|
|
|
51271
51438
|
}
|
|
51272
51439
|
}
|
|
51273
51440
|
|
|
51274
|
-
//# debugId=
|
|
51441
|
+
//# debugId=F4E62DD52039241264756E2164756E21
|