@agentproto/runtime 5.5.0 → 5.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config-schema.mjs +14 -1
- package/dist/config-schema.mjs.map +1 -1
- package/dist/config.mjs +14 -1
- package/dist/config.mjs.map +1 -1
- package/dist/index.d.ts +320 -1
- package/dist/index.mjs +973 -212
- package/dist/index.mjs.map +1 -1
- package/dist/session-presence.mjs +14 -1
- package/dist/session-presence.mjs.map +1 -1
- package/package.json +12 -12
package/dist/index.mjs
CHANGED
|
@@ -19,9 +19,12 @@ import { resolvePairPageUrl, DEFAULT_PAIR_PAGE } from '@agentproto/secrets/pairi
|
|
|
19
19
|
import { ListToolsRequestSchema, CallToolRequestSchema, SUPPORTED_PROTOCOL_VERSIONS, LATEST_PROTOCOL_VERSION } from '@modelcontextprotocol/sdk/types.js';
|
|
20
20
|
import { makeCredsStore, makeAdapterResolver, makeAdapterLister, discoverAdapterPackages, makeSetupLedger, makeListTool, makeSetupTool } from '@agentproto/provider-kit';
|
|
21
21
|
import { syncPiModels, getConfiguredEndpoints, checkHarnessFit, connectorById } from '@agentproto/llm-endpoint';
|
|
22
|
+
import { createServer } from 'http';
|
|
23
|
+
import { createServer as createServer$1 } from 'net';
|
|
24
|
+
import { Readable } from 'stream';
|
|
25
|
+
import { pipeline } from 'stream/promises';
|
|
22
26
|
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
23
27
|
import { StreamableHTTPError, StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
|
24
|
-
import { createServer as createServer$1 } from 'net';
|
|
25
28
|
import { SandboxSpecSchema, resolveLifecyclePolicy, createSandboxAgentSessionHost, isSandboxBoxGoneError, SandboxHostBootFailedError } from '@agentproto/sandbox';
|
|
26
29
|
import { APP_UI_DISCOVERY_TOOLS, RUNNER_SELECT_SCRIPT } from '@agentproto/app-client/runner-select';
|
|
27
30
|
import { DISPLAY_MODE_SCRIPT } from '@agentproto/app-client/display-mode';
|
|
@@ -35,8 +38,6 @@ import { createBrainManager, parseKnowledgeConfig } from '@agentproto/workspace-
|
|
|
35
38
|
import { browserAdapters, getBrowserAdapter } from '@agentproto/adapter-browser';
|
|
36
39
|
import { defineHttpDriver } from '@agentproto/driver-http';
|
|
37
40
|
import matter2 from 'gray-matter';
|
|
38
|
-
import { createServer } from 'http';
|
|
39
|
-
import { Readable } from 'stream';
|
|
40
41
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
41
42
|
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
42
43
|
import { WebSocketServer } from 'ws';
|
|
@@ -67,7 +68,8 @@ import { createRequire } from 'module';
|
|
|
67
68
|
import { signArtifact, recordAuditEvent } from '@agentproto/governance-engine';
|
|
68
69
|
import { parseRoutineManifest } from '@agentproto/routine';
|
|
69
70
|
import { navigateOptionsSchema, evaluateOptionsSchema, clickOptionsSchema, fillOptionsSchema, screenshotOptionsSchema, assertToolSupported } from '@agentproto/driver-browser';
|
|
70
|
-
import {
|
|
71
|
+
import { lookup } from 'dns/promises';
|
|
72
|
+
import { request } from 'https';
|
|
71
73
|
import { identityFingerprint } from '@agentproto/secrets/identity';
|
|
72
74
|
|
|
73
75
|
/**
|
|
@@ -4874,16 +4876,16 @@ var init_workspaces_config = __esm({
|
|
|
4874
4876
|
function normalizeWorktreeField(field) {
|
|
4875
4877
|
if (field === void 0 || field === false) return void 0;
|
|
4876
4878
|
if (field === true) return {};
|
|
4877
|
-
const
|
|
4878
|
-
if (field.slug !== void 0)
|
|
4879
|
-
if (field.base !== void 0)
|
|
4880
|
-
if (field.async !== void 0)
|
|
4881
|
-
return
|
|
4879
|
+
const request2 = {};
|
|
4880
|
+
if (field.slug !== void 0) request2.slug = field.slug;
|
|
4881
|
+
if (field.base !== void 0) request2.base = field.base;
|
|
4882
|
+
if (field.async !== void 0) request2.async = field.async;
|
|
4883
|
+
return request2;
|
|
4882
4884
|
}
|
|
4883
4885
|
function decideWorktreeIsolation(input) {
|
|
4884
|
-
const
|
|
4886
|
+
const request2 = normalizeWorktreeField(input.field);
|
|
4885
4887
|
if (input.depth > 0) {
|
|
4886
|
-
if (
|
|
4888
|
+
if (request2 !== void 0) {
|
|
4887
4889
|
return {
|
|
4888
4890
|
action: "reject",
|
|
4889
4891
|
message: `agent_start: \`worktree\` isolation was requested on a nested spawn (depth ${input.depth}), but a spawn made through an orchestrator inherits its parent's working tree and cannot provision a worktree of its own. Remove the \`worktree\` field; for an isolated child use \`sandbox\` (the box already isolates) instead.`
|
|
@@ -4899,7 +4901,7 @@ function decideWorktreeIsolation(input) {
|
|
|
4899
4901
|
}
|
|
4900
4902
|
switch (input.mode) {
|
|
4901
4903
|
case "never":
|
|
4902
|
-
if (
|
|
4904
|
+
if (request2 !== void 0) {
|
|
4903
4905
|
return {
|
|
4904
4906
|
action: "reject",
|
|
4905
4907
|
message: `agent_start: \`worktree\` was requested but this daemon's \`worktrees.isolation\` policy is set to "never". Remove the \`worktree\` field, or change the policy (config \`worktrees.isolation\` / env ${WORKTREE_ISOLATION_ENV}).`
|
|
@@ -4907,9 +4909,9 @@ function decideWorktreeIsolation(input) {
|
|
|
4907
4909
|
}
|
|
4908
4910
|
return { action: "spawn-in-place" };
|
|
4909
4911
|
case "on-request":
|
|
4910
|
-
return
|
|
4912
|
+
return request2 !== void 0 ? { action: "provision", request: request2, implicit: false } : { action: "spawn-in-place" };
|
|
4911
4913
|
case "always":
|
|
4912
|
-
return { action: "provision", request:
|
|
4914
|
+
return { action: "provision", request: request2 ?? {}, implicit: request2 === void 0 };
|
|
4913
4915
|
}
|
|
4914
4916
|
}
|
|
4915
4917
|
function parseWorktreeIsolationMode(raw) {
|
|
@@ -4942,16 +4944,16 @@ function normalizeAttachField(field) {
|
|
|
4942
4944
|
if (field === void 0) return void 0;
|
|
4943
4945
|
if (field === false) return { detached: true };
|
|
4944
4946
|
if (field === true) return { optIn: true };
|
|
4945
|
-
const
|
|
4946
|
-
if (field.parent !== void 0)
|
|
4947
|
-
return
|
|
4947
|
+
const request2 = { optIn: true };
|
|
4948
|
+
if (field.parent !== void 0) request2.parent = field.parent;
|
|
4949
|
+
return request2;
|
|
4948
4950
|
}
|
|
4949
4951
|
function decideSpawnAttach(input) {
|
|
4950
|
-
const
|
|
4951
|
-
if (
|
|
4952
|
-
if (
|
|
4952
|
+
const request2 = normalizeAttachField(input.field);
|
|
4953
|
+
if (request2?.detached) return { parent: void 0, detached: true };
|
|
4954
|
+
if (request2?.parent) return { parent: request2.parent, detached: false };
|
|
4953
4955
|
const candidate = input.hint ?? input.autoParent;
|
|
4954
|
-
if (
|
|
4956
|
+
if (request2?.optIn) return { parent: candidate, detached: false };
|
|
4955
4957
|
switch (input.mode) {
|
|
4956
4958
|
case "always":
|
|
4957
4959
|
return { parent: candidate, detached: false };
|
|
@@ -5676,8 +5678,8 @@ function pathSegmentsMatch(entryPath, concretePath) {
|
|
|
5676
5678
|
return entrySegs.every((seg, i) => seg === "*" || seg === concreteSegs[i]);
|
|
5677
5679
|
}
|
|
5678
5680
|
function findConfigKey(path) {
|
|
5679
|
-
const
|
|
5680
|
-
return CONFIG_KEYS.find((entry) => pathSegmentsMatch(entry.path,
|
|
5681
|
+
const lookup2 = path.startsWith("models.") && path.length > "models.".length ? "models.*" : path;
|
|
5682
|
+
return CONFIG_KEYS.find((entry) => pathSegmentsMatch(entry.path, lookup2));
|
|
5681
5683
|
}
|
|
5682
5684
|
function validateConfig(raw) {
|
|
5683
5685
|
const result = agentprotoConfigSchema.safeParse(raw);
|
|
@@ -9243,6 +9245,139 @@ var init_sandbox_agent_session_proxy = __esm({
|
|
|
9243
9245
|
]);
|
|
9244
9246
|
}
|
|
9245
9247
|
});
|
|
9248
|
+
function collectHeaders(raw) {
|
|
9249
|
+
const headers = {};
|
|
9250
|
+
for (const [k, v] of Object.entries(raw)) {
|
|
9251
|
+
if (v === void 0 || HOP_BY_HOP.has(k.toLowerCase())) continue;
|
|
9252
|
+
headers[k] = Array.isArray(v) ? v.join(", ") : v;
|
|
9253
|
+
}
|
|
9254
|
+
return headers;
|
|
9255
|
+
}
|
|
9256
|
+
function isPrematureCloseError(err) {
|
|
9257
|
+
return typeof err === "object" && err !== null && "code" in err && err.code === "ERR_STREAM_PREMATURE_CLOSE";
|
|
9258
|
+
}
|
|
9259
|
+
async function readBody(req) {
|
|
9260
|
+
const chunks = [];
|
|
9261
|
+
for await (const chunk of req) chunks.push(chunk);
|
|
9262
|
+
if (chunks.length === 0) return void 0;
|
|
9263
|
+
return new Uint8Array(Buffer.concat(chunks));
|
|
9264
|
+
}
|
|
9265
|
+
async function startDeviceSandboxBridge(opts) {
|
|
9266
|
+
const { hostRegistry, target } = opts;
|
|
9267
|
+
const server = createServer((req, res) => {
|
|
9268
|
+
void handleRequest(req, res).catch((err) => {
|
|
9269
|
+
if (res.writableEnded) return;
|
|
9270
|
+
if (!res.headersSent) {
|
|
9271
|
+
res.writeHead(502, { "content-type": "application/json" });
|
|
9272
|
+
res.end(
|
|
9273
|
+
JSON.stringify({
|
|
9274
|
+
error: "device_sandbox_bridge_failed",
|
|
9275
|
+
message: err instanceof Error ? err.message : String(err)
|
|
9276
|
+
})
|
|
9277
|
+
);
|
|
9278
|
+
return;
|
|
9279
|
+
}
|
|
9280
|
+
res.end();
|
|
9281
|
+
});
|
|
9282
|
+
});
|
|
9283
|
+
async function handleRequest(req, res) {
|
|
9284
|
+
const body = await readBody(req);
|
|
9285
|
+
let upstream;
|
|
9286
|
+
try {
|
|
9287
|
+
upstream = await hostRegistry.forwardHttpStream(target, {
|
|
9288
|
+
method: req.method ?? "GET",
|
|
9289
|
+
path: `/device-spawn${req.url ?? "/"}`,
|
|
9290
|
+
headers: collectHeaders(req.headers),
|
|
9291
|
+
...body ? { body } : {}
|
|
9292
|
+
});
|
|
9293
|
+
} catch (err) {
|
|
9294
|
+
res.writeHead(502, { "content-type": "application/json" });
|
|
9295
|
+
res.end(
|
|
9296
|
+
JSON.stringify({
|
|
9297
|
+
error: "device_unreachable",
|
|
9298
|
+
message: err instanceof Error ? err.message : String(err)
|
|
9299
|
+
})
|
|
9300
|
+
);
|
|
9301
|
+
return;
|
|
9302
|
+
}
|
|
9303
|
+
const resHeaders = {};
|
|
9304
|
+
for (const [k, v] of Object.entries(upstream.headers)) {
|
|
9305
|
+
if (HOP_BY_HOP.has(k.toLowerCase()) || k.toLowerCase() === "content-length") continue;
|
|
9306
|
+
resHeaders[k] = v;
|
|
9307
|
+
}
|
|
9308
|
+
res.writeHead(upstream.status, resHeaders);
|
|
9309
|
+
try {
|
|
9310
|
+
await pipeline(
|
|
9311
|
+
Readable.fromWeb(upstream.body),
|
|
9312
|
+
res
|
|
9313
|
+
);
|
|
9314
|
+
} catch (err) {
|
|
9315
|
+
if (!isPrematureCloseError(err)) throw err;
|
|
9316
|
+
}
|
|
9317
|
+
}
|
|
9318
|
+
const port = await getFreePort();
|
|
9319
|
+
await new Promise((resolve41, reject) => {
|
|
9320
|
+
server.once("error", reject);
|
|
9321
|
+
server.listen(port, "127.0.0.1", () => resolve41());
|
|
9322
|
+
});
|
|
9323
|
+
return {
|
|
9324
|
+
mcpUrl: `http://127.0.0.1:${port}/mcp`,
|
|
9325
|
+
async close() {
|
|
9326
|
+
await new Promise((resolve41) => server.close(() => resolve41()));
|
|
9327
|
+
}
|
|
9328
|
+
};
|
|
9329
|
+
}
|
|
9330
|
+
async function getFreePort() {
|
|
9331
|
+
return new Promise((resolve41, reject) => {
|
|
9332
|
+
const srv = createServer$1();
|
|
9333
|
+
srv.once("error", reject);
|
|
9334
|
+
srv.listen(0, "127.0.0.1", () => {
|
|
9335
|
+
const address = srv.address();
|
|
9336
|
+
if (address === null || typeof address === "string") {
|
|
9337
|
+
srv.close(() => reject(new Error("failed to allocate a free port")));
|
|
9338
|
+
return;
|
|
9339
|
+
}
|
|
9340
|
+
const { port } = address;
|
|
9341
|
+
srv.close(() => resolve41(port));
|
|
9342
|
+
});
|
|
9343
|
+
});
|
|
9344
|
+
}
|
|
9345
|
+
var HOP_BY_HOP;
|
|
9346
|
+
var init_device_sandbox_bridge = __esm({
|
|
9347
|
+
"src/device-sandbox-bridge.ts"() {
|
|
9348
|
+
HOP_BY_HOP = /* @__PURE__ */ new Set(["connection", "keep-alive", "transfer-encoding", "upgrade", "host"]);
|
|
9349
|
+
}
|
|
9350
|
+
});
|
|
9351
|
+
function isDeviceSandboxTarget(sandbox) {
|
|
9352
|
+
const slug = typeof sandbox === "string" ? sandbox : sandbox?.provider ?? "";
|
|
9353
|
+
return slug.startsWith("device:");
|
|
9354
|
+
}
|
|
9355
|
+
function createDeviceSandboxProvider(target, hostRegistry) {
|
|
9356
|
+
return {
|
|
9357
|
+
async boot(_spec, _opts) {
|
|
9358
|
+
const hosts = await hostRegistry.list();
|
|
9359
|
+
const known = hosts.some((h) => h.fingerprint === target || h.name === target);
|
|
9360
|
+
if (!known) {
|
|
9361
|
+
throw new Error(
|
|
9362
|
+
`device sandbox: no paired host device matches "${target}" \u2014 run \`agentproto devices add <offer-url>\` here first (the offer must come from \`agentproto pair offer --host\` run ON that device). Check \`agentproto devices list\` for known devices.`
|
|
9363
|
+
);
|
|
9364
|
+
}
|
|
9365
|
+
const bridge = await startDeviceSandboxBridge({ hostRegistry, target });
|
|
9366
|
+
return {
|
|
9367
|
+
mcpUrl: bridge.mcpUrl,
|
|
9368
|
+
sandboxId: `device-${target}-${randomUUID()}`,
|
|
9369
|
+
async stop() {
|
|
9370
|
+
await bridge.close();
|
|
9371
|
+
}
|
|
9372
|
+
};
|
|
9373
|
+
}
|
|
9374
|
+
};
|
|
9375
|
+
}
|
|
9376
|
+
var init_device = __esm({
|
|
9377
|
+
"src/sandbox-providers/device.ts"() {
|
|
9378
|
+
init_device_sandbox_bridge();
|
|
9379
|
+
}
|
|
9380
|
+
});
|
|
9246
9381
|
async function connectBoxToolClient(mcpUrl) {
|
|
9247
9382
|
const client = new Client(
|
|
9248
9383
|
{ name: "agentproto-sandbox-app-serve", version: "0.1.0" },
|
|
@@ -9471,7 +9606,7 @@ var init_sandbox_app_serve = __esm({
|
|
|
9471
9606
|
REQUIRED_ALLOWLIST_BASENAMES = ["sh", "agentproto"];
|
|
9472
9607
|
}
|
|
9473
9608
|
});
|
|
9474
|
-
async function
|
|
9609
|
+
async function getFreePort2() {
|
|
9475
9610
|
return new Promise((resolve41, reject) => {
|
|
9476
9611
|
const srv = createServer$1();
|
|
9477
9612
|
srv.once("error", reject);
|
|
@@ -9505,7 +9640,7 @@ var init_local = __esm({
|
|
|
9505
9640
|
POLL_INTERVAL_MS = 250;
|
|
9506
9641
|
localSandboxProvider = {
|
|
9507
9642
|
async boot(_spec, opts) {
|
|
9508
|
-
const port = await
|
|
9643
|
+
const port = await getFreePort2();
|
|
9509
9644
|
const workspace = await mkdtemp(join(tmpdir(), "agentproto-sandbox-local-"));
|
|
9510
9645
|
const child = spawn(
|
|
9511
9646
|
"agentproto",
|
|
@@ -10159,6 +10294,9 @@ ${callerPrompt}`;
|
|
|
10159
10294
|
const pre = composed.slice(0, composed.length - tail.length);
|
|
10160
10295
|
return pre || void 0;
|
|
10161
10296
|
}
|
|
10297
|
+
function deviceContractLine(device) {
|
|
10298
|
+
return `This session was spawned remotely on paired device "${device}" by the controlling daemon \u2014 its own workspace (and any AGENTS.md along that path) is on ANOTHER machine and has not been injected here. If your resolved cwd has an AGENTS.md, treat it as binding on your next tool call (read it first); if you \`cd\` somewhere else (a nested repo included), re-resolve and re-read the AGENTS.md of wherever you end up.`;
|
|
10299
|
+
}
|
|
10162
10300
|
async function spawnAgentSession(deps2, input) {
|
|
10163
10301
|
if (input.inference) {
|
|
10164
10302
|
if (input.model || input.route || input.access) {
|
|
@@ -10972,22 +11110,30 @@ async function spawnAgentSession(deps2, input) {
|
|
|
10972
11110
|
let effectivePrompt = input.prompt;
|
|
10973
11111
|
let agentsMdResolution;
|
|
10974
11112
|
const resolveAgentsMdForSpawn = resolveAgentsMd2 ?? (async (cwdArg, _kb, o) => resolveAgentsMd(cwdArg, await loadAgentsMdInlineMaxKb(), void 0, o?.stopAt));
|
|
10975
|
-
|
|
10976
|
-
|
|
10977
|
-
|
|
10978
|
-
agentsMdResolution = { mode: "absent", contractLine:
|
|
11113
|
+
const isDeviceSandboxSpawn = isDeviceSandboxTarget(input.sandbox);
|
|
11114
|
+
const deviceTargetName = typeof input.sandbox === "string" ? input.sandbox.slice("device:".length) : (input.sandbox?.provider ?? "").slice("device:".length);
|
|
11115
|
+
if (isDeviceSandboxSpawn) {
|
|
11116
|
+
agentsMdResolution = { mode: "absent", contractLine: deviceContractLine(deviceTargetName) };
|
|
11117
|
+
} else {
|
|
11118
|
+
try {
|
|
11119
|
+
agentsMdResolution = appBoundary ? await resolveAgentsMdForSpawn(cwd, void 0, { stopAt: appBoundary.root }) : await resolveAgentsMdForSpawn(cwd);
|
|
11120
|
+
} catch {
|
|
11121
|
+
agentsMdResolution = { mode: "absent", contractLine: cdContractLine };
|
|
11122
|
+
}
|
|
10979
11123
|
}
|
|
10980
11124
|
const agentsMdParts = [agentsMdResolution.block, agentsMdResolution.contractLine].filter(
|
|
10981
11125
|
(p) => !!p
|
|
10982
11126
|
);
|
|
10983
|
-
const agentsMdReadPaths = additionalReadPathsForAgentsMd(agentsMdResolution, cwd);
|
|
11127
|
+
const agentsMdReadPaths = isDeviceSandboxSpawn ? void 0 : additionalReadPathsForAgentsMd(agentsMdResolution, cwd);
|
|
10984
11128
|
const additionalReadPaths = [...agentsMdReadPaths ?? [], ...browserReadPaths];
|
|
10985
11129
|
let workspaceRulesResolution = {};
|
|
10986
11130
|
const resolveWorkspaceRulesForSpawn = resolveWorkspaceRules2 ?? resolveWorkspaceRules;
|
|
10987
|
-
|
|
10988
|
-
|
|
10989
|
-
|
|
10990
|
-
|
|
11131
|
+
if (!isDeviceSandboxSpawn) {
|
|
11132
|
+
try {
|
|
11133
|
+
workspaceRulesResolution = await resolveWorkspaceRulesForSpawn(resolvedSlug);
|
|
11134
|
+
} catch {
|
|
11135
|
+
workspaceRulesResolution = {};
|
|
11136
|
+
}
|
|
10991
11137
|
}
|
|
10992
11138
|
const rulesMdParts = workspaceRulesResolution.block ? [workspaceRulesResolution.block] : [];
|
|
10993
11139
|
if (input.prompt && typeof input.prompt === "string") {
|
|
@@ -11941,6 +12087,7 @@ var init_session_spawn = __esm({
|
|
|
11941
12087
|
init_inference_binding();
|
|
11942
12088
|
init_mcp_credential_deps();
|
|
11943
12089
|
init_sandbox_agent_session_proxy();
|
|
12090
|
+
init_device();
|
|
11944
12091
|
init_sandbox_ledger();
|
|
11945
12092
|
init_sandbox_app_serve();
|
|
11946
12093
|
init_sandbox_adapters();
|
|
@@ -12667,21 +12814,21 @@ function injectStandaloneAppBridge(html, baseUrl) {
|
|
|
12667
12814
|
return injectAfterStructuralTag(html, script);
|
|
12668
12815
|
}
|
|
12669
12816
|
function createUiHtmlCache() {
|
|
12670
|
-
const
|
|
12817
|
+
const cache3 = /* @__PURE__ */ new Map();
|
|
12671
12818
|
return {
|
|
12672
12819
|
async get(path, version) {
|
|
12673
12820
|
const st = await stat(path);
|
|
12674
12821
|
const stamp = `${version}:${st.mtimeMs}:${st.size}`;
|
|
12675
|
-
const cached =
|
|
12822
|
+
const cached = cache3.get(path);
|
|
12676
12823
|
if (cached && cached.stamp === stamp) return cached.html;
|
|
12677
12824
|
const raw = await readFile(path, "utf8");
|
|
12678
12825
|
const html = injectMcpAppBridge(raw);
|
|
12679
|
-
|
|
12826
|
+
cache3.set(path, { stamp, html });
|
|
12680
12827
|
return html;
|
|
12681
12828
|
}
|
|
12682
12829
|
};
|
|
12683
12830
|
}
|
|
12684
|
-
async function makeInstalledAppUiApps(appRegistry,
|
|
12831
|
+
async function makeInstalledAppUiApps(appRegistry, cache3, existingToolNames) {
|
|
12685
12832
|
const apps = [];
|
|
12686
12833
|
const seen = new Set(existingToolNames);
|
|
12687
12834
|
for (const app of appRegistry.listApps()) {
|
|
@@ -12714,7 +12861,7 @@ async function makeInstalledAppUiApps(appRegistry, cache2, existingToolNames) {
|
|
|
12714
12861
|
});
|
|
12715
12862
|
}
|
|
12716
12863
|
try {
|
|
12717
|
-
return await
|
|
12864
|
+
return await cache3.get(ui.path, app.updatedAt);
|
|
12718
12865
|
} catch (err) {
|
|
12719
12866
|
return renderAppUiErrorHtml({
|
|
12720
12867
|
appName,
|
|
@@ -24217,7 +24364,7 @@ function createDefaultHostProbes() {
|
|
|
24217
24364
|
);
|
|
24218
24365
|
if (child.pid !== void 0) own.add(child.pid);
|
|
24219
24366
|
});
|
|
24220
|
-
const
|
|
24367
|
+
const sleep3 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
24221
24368
|
const probes = {
|
|
24222
24369
|
platform,
|
|
24223
24370
|
table: (t) => createProcessTableSource(t)(),
|
|
@@ -24245,7 +24392,7 @@ function createDefaultHostProbes() {
|
|
|
24245
24392
|
const read = () => Promise.all([readFile("/proc/stat", "utf8"), readFile("/proc/diskstats", "utf8").catch(() => "")]);
|
|
24246
24393
|
const t0 = Date.now();
|
|
24247
24394
|
const [statBefore, diskBefore] = await read();
|
|
24248
|
-
await
|
|
24395
|
+
await sleep3(400);
|
|
24249
24396
|
const [statAfter, diskAfter] = await read();
|
|
24250
24397
|
return { statBefore, statAfter, diskBefore, diskAfter, intervalSec: (Date.now() - t0) / 1e3 };
|
|
24251
24398
|
};
|
|
@@ -24790,7 +24937,7 @@ async function callJevSystemOne(state, questions, opts) {
|
|
|
24790
24937
|
const fetchImpl = opts.fetchImpl ?? fetch;
|
|
24791
24938
|
const url = (opts.baseUrl ?? JEV_DEFAULT_URL).replace(/\/+$/, "");
|
|
24792
24939
|
const maxRetries = opts.maxRetries ?? 3;
|
|
24793
|
-
const
|
|
24940
|
+
const sleep3 = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
24794
24941
|
const model = opts.model ?? JEV_DEFAULT_MODEL;
|
|
24795
24942
|
const timeoutMs = opts.timeoutMs ?? 3e4;
|
|
24796
24943
|
let attempt = 0;
|
|
@@ -24831,7 +24978,7 @@ async function callJevSystemOne(state, questions, opts) {
|
|
|
24831
24978
|
}
|
|
24832
24979
|
if (RETRYABLE_STATUSES.has(res.status) && attempt < maxRetries) {
|
|
24833
24980
|
attempt++;
|
|
24834
|
-
await
|
|
24981
|
+
await sleep3(backoffDelayMs(attempt, res.headers.get("retry-after")));
|
|
24835
24982
|
continue;
|
|
24836
24983
|
}
|
|
24837
24984
|
const body = await res.text().catch(() => "");
|
|
@@ -35218,7 +35365,8 @@ function createHostRegistry(deps2) {
|
|
|
35218
35365
|
const attempts = [epoch, epoch - 1];
|
|
35219
35366
|
let lastErr;
|
|
35220
35367
|
let hungUpOnHello = 0;
|
|
35221
|
-
for (
|
|
35368
|
+
for (let attempt = 1; attempt <= attempts.length; attempt++) {
|
|
35369
|
+
const e = attempts[attempt - 1];
|
|
35222
35370
|
const { route, auth } = await deriveEpochTokens(record2.pairRoot, e);
|
|
35223
35371
|
let raw;
|
|
35224
35372
|
try {
|
|
@@ -35228,32 +35376,45 @@ function createHostRegistry(deps2) {
|
|
|
35228
35376
|
continue;
|
|
35229
35377
|
}
|
|
35230
35378
|
try {
|
|
35231
|
-
|
|
35232
|
-
|
|
35233
|
-
|
|
35234
|
-
authToken: auth,
|
|
35235
|
-
clientName: record2.name
|
|
35379
|
+
let rawCloseReason;
|
|
35380
|
+
const offRawClose = raw.onClose((reason) => {
|
|
35381
|
+
rawCloseReason = reason ?? "unknown";
|
|
35236
35382
|
});
|
|
35237
|
-
|
|
35238
|
-
|
|
35239
|
-
|
|
35240
|
-
|
|
35241
|
-
|
|
35242
|
-
|
|
35243
|
-
|
|
35244
|
-
|
|
35383
|
+
let client;
|
|
35384
|
+
try {
|
|
35385
|
+
const started = await startClientHandshake({
|
|
35386
|
+
daemonX25519Pub: record2.daemonX25519Pub,
|
|
35387
|
+
daemonEd25519Pub: record2.daemonEd25519Pub,
|
|
35388
|
+
authToken: auth,
|
|
35389
|
+
clientName: record2.name
|
|
35390
|
+
});
|
|
35391
|
+
const wrapped = await clientHandshakeOverSink(
|
|
35392
|
+
raw,
|
|
35393
|
+
encodePairingMessage(started.hello),
|
|
35394
|
+
(replyBytes) => started.complete(decodePairingReply(replyBytes)),
|
|
35395
|
+
{ timeoutMs: handshakeTimeoutMs }
|
|
35396
|
+
);
|
|
35397
|
+
client = createTunnelClient({ sink: wrapped });
|
|
35398
|
+
await awaitTunnelHello(client, wrapped, record2, () => rawCloseReason);
|
|
35399
|
+
} finally {
|
|
35400
|
+
offRawClose();
|
|
35401
|
+
}
|
|
35245
35402
|
await markReached(record2);
|
|
35246
35403
|
return client;
|
|
35247
35404
|
} catch (err) {
|
|
35248
35405
|
lastErr = err;
|
|
35249
35406
|
if (err instanceof Error && /transport closed during handshake/.test(err.message)) hungUpOnHello++;
|
|
35407
|
+
log(
|
|
35408
|
+
`[hosts] ${record2.name} (${record2.fingerprint}) attempt ${attempt}/${attempts.length} failed: ${errMsg3(err)}`
|
|
35409
|
+
);
|
|
35250
35410
|
try {
|
|
35251
35411
|
raw.close("handshake failed");
|
|
35252
35412
|
} catch {
|
|
35253
35413
|
}
|
|
35254
35414
|
}
|
|
35255
35415
|
}
|
|
35256
|
-
const
|
|
35416
|
+
const helloTimedOut = lastErr instanceof Error && /did not send hello/.test(lastErr.message);
|
|
35417
|
+
const hint = hungUpOnHello === attempts.length || helloTimedOut ? ` \u2014 the host hung up on (or never sent) the post-handshake tunnel hello; if it runs an older agentproto (pair/v1), upgrade it and re-pair: run \`agentproto pair offer --host\` on the host, then \`agentproto devices add\` here. (${PAIRING_PROTOCOL_OUTDATED_MESSAGE})` : "";
|
|
35257
35418
|
const failure = `could not reach host ${record2.fingerprint} via ${record2.rendezvousUrl}: ${lastErr instanceof Error ? lastErr.message : String(lastErr)}${hint}`;
|
|
35258
35419
|
await markUnreachable(record2, errMsg3(lastErr));
|
|
35259
35420
|
throw new Error(failure);
|
|
@@ -35490,6 +35651,35 @@ function createHostRegistry(deps2) {
|
|
|
35490
35651
|
start: sweep
|
|
35491
35652
|
};
|
|
35492
35653
|
}
|
|
35654
|
+
async function awaitTunnelHello(client, sink, record2, closeReason) {
|
|
35655
|
+
return new Promise((resolve41, reject) => {
|
|
35656
|
+
if (!sink.isOpen) {
|
|
35657
|
+
reject(
|
|
35658
|
+
new Error(
|
|
35659
|
+
`host ${record2.fingerprint} (${record2.name}) closed the channel after handshake: ${closeReason() ?? "unknown"}`
|
|
35660
|
+
)
|
|
35661
|
+
);
|
|
35662
|
+
return;
|
|
35663
|
+
}
|
|
35664
|
+
const offClose = sink.onClose((reason) => {
|
|
35665
|
+
reject(
|
|
35666
|
+
new Error(
|
|
35667
|
+
`host ${record2.fingerprint} (${record2.name}) closed the channel after handshake: ${reason ?? "unknown"}`
|
|
35668
|
+
)
|
|
35669
|
+
);
|
|
35670
|
+
});
|
|
35671
|
+
client.ready().then(
|
|
35672
|
+
() => {
|
|
35673
|
+
offClose();
|
|
35674
|
+
resolve41();
|
|
35675
|
+
},
|
|
35676
|
+
(err) => {
|
|
35677
|
+
offClose();
|
|
35678
|
+
reject(err);
|
|
35679
|
+
}
|
|
35680
|
+
);
|
|
35681
|
+
});
|
|
35682
|
+
}
|
|
35493
35683
|
function wrapStreamWithCleanup(source, cleanup) {
|
|
35494
35684
|
const reader = source.getReader();
|
|
35495
35685
|
let cleanedUp = false;
|
|
@@ -35752,6 +35942,114 @@ function createDeviceRegistry(pairing, hosts) {
|
|
|
35752
35942
|
}
|
|
35753
35943
|
};
|
|
35754
35944
|
}
|
|
35945
|
+
var DEVICE_PROMPT_PATH_PREFIX = "/device-prompt";
|
|
35946
|
+
function isValidPrompt(prompt) {
|
|
35947
|
+
if (typeof prompt === "string") return prompt.length > 0;
|
|
35948
|
+
if (Array.isArray(prompt)) {
|
|
35949
|
+
return prompt.length > 0 && prompt.every((b) => b !== null && typeof b === "object");
|
|
35950
|
+
}
|
|
35951
|
+
return prompt !== null && typeof prompt === "object";
|
|
35952
|
+
}
|
|
35953
|
+
function sleep(ms) {
|
|
35954
|
+
return new Promise((resolve41) => setTimeout(resolve41, ms));
|
|
35955
|
+
}
|
|
35956
|
+
function httpErrorMessage(body) {
|
|
35957
|
+
const raw = Buffer.from(body).toString("utf8");
|
|
35958
|
+
try {
|
|
35959
|
+
const parsed = JSON.parse(raw);
|
|
35960
|
+
if (parsed !== null && typeof parsed === "object" && typeof parsed["message"] === "string") {
|
|
35961
|
+
return parsed["message"];
|
|
35962
|
+
}
|
|
35963
|
+
} catch {
|
|
35964
|
+
}
|
|
35965
|
+
return raw;
|
|
35966
|
+
}
|
|
35967
|
+
async function promptHostSession(hosts, target, sessionId, input) {
|
|
35968
|
+
if (!hosts) {
|
|
35969
|
+
throw new Error(
|
|
35970
|
+
"this daemon has no host registry wired \u2014 device prompt is unavailable (internal configuration issue, not a user error)"
|
|
35971
|
+
);
|
|
35972
|
+
}
|
|
35973
|
+
if (!isValidPrompt(input.prompt)) {
|
|
35974
|
+
return {
|
|
35975
|
+
ok: false,
|
|
35976
|
+
message: "`prompt` must be a non-empty string, a content block object, or an array of content blocks."
|
|
35977
|
+
};
|
|
35978
|
+
}
|
|
35979
|
+
const pollMs = Math.min(Math.max(input.pollMs ?? 1e3, 10), 1e4);
|
|
35980
|
+
const delay2 = input.sleep ?? sleep;
|
|
35981
|
+
const enqueueBody = { prompt: input.prompt, queue: true };
|
|
35982
|
+
if (input.interrupt) enqueueBody.interrupt = true;
|
|
35983
|
+
if (input.force) enqueueBody.force = true;
|
|
35984
|
+
const startedAt = Date.now();
|
|
35985
|
+
let enqueueRes;
|
|
35986
|
+
try {
|
|
35987
|
+
enqueueRes = await hosts.forwardHttp(target, {
|
|
35988
|
+
method: "POST",
|
|
35989
|
+
path: `${DEVICE_PROMPT_PATH_PREFIX}/${encodeURIComponent(sessionId)}?wait=false`,
|
|
35990
|
+
headers: { "content-type": "application/json" },
|
|
35991
|
+
body: new Uint8Array(Buffer.from(JSON.stringify(enqueueBody), "utf8"))
|
|
35992
|
+
});
|
|
35993
|
+
} catch (err) {
|
|
35994
|
+
return { ok: false, message: err instanceof Error ? err.message : String(err) };
|
|
35995
|
+
}
|
|
35996
|
+
if (enqueueRes.status < 200 || enqueueRes.status >= 300) {
|
|
35997
|
+
return { ok: false, status: enqueueRes.status, message: httpErrorMessage(enqueueRes.body) };
|
|
35998
|
+
}
|
|
35999
|
+
let enqueueParsed = {};
|
|
36000
|
+
try {
|
|
36001
|
+
const parsed = JSON.parse(Buffer.from(enqueueRes.body).toString("utf8"));
|
|
36002
|
+
if (parsed !== null && typeof parsed === "object") enqueueParsed = parsed;
|
|
36003
|
+
} catch {
|
|
36004
|
+
}
|
|
36005
|
+
if (!input.wait) {
|
|
36006
|
+
const pending = enqueueParsed["pending"] === true;
|
|
36007
|
+
return {
|
|
36008
|
+
ok: true,
|
|
36009
|
+
...pending ? {
|
|
36010
|
+
pending: true,
|
|
36011
|
+
...typeof enqueueParsed["queueId"] === "string" ? { queueId: enqueueParsed["queueId"] } : {},
|
|
36012
|
+
...typeof enqueueParsed["queuePosition"] === "number" ? { queuePosition: enqueueParsed["queuePosition"] } : {}
|
|
36013
|
+
} : {}
|
|
36014
|
+
};
|
|
36015
|
+
}
|
|
36016
|
+
for (; ; ) {
|
|
36017
|
+
await delay2(pollMs);
|
|
36018
|
+
const waitedMs = Date.now() - startedAt;
|
|
36019
|
+
let descRes;
|
|
36020
|
+
try {
|
|
36021
|
+
descRes = await hosts.forwardHttp(target, {
|
|
36022
|
+
method: "GET",
|
|
36023
|
+
path: `/sessions/${encodeURIComponent(sessionId)}`
|
|
36024
|
+
});
|
|
36025
|
+
} catch (err) {
|
|
36026
|
+
return { ok: false, waitedMs, message: err instanceof Error ? err.message : String(err) };
|
|
36027
|
+
}
|
|
36028
|
+
if (descRes.status === 404) {
|
|
36029
|
+
return { ok: false, waitedMs, status: 404, message: `session ${sessionId} not found on host ${target}` };
|
|
36030
|
+
}
|
|
36031
|
+
if (descRes.status < 200 || descRes.status >= 300) {
|
|
36032
|
+
return { ok: false, waitedMs, status: descRes.status, message: httpErrorMessage(descRes.body) };
|
|
36033
|
+
}
|
|
36034
|
+
let descriptor = {};
|
|
36035
|
+
try {
|
|
36036
|
+
const parsed = JSON.parse(Buffer.from(descRes.body).toString("utf8"));
|
|
36037
|
+
if (parsed !== null && typeof parsed === "object") descriptor = parsed;
|
|
36038
|
+
} catch {
|
|
36039
|
+
return { ok: false, waitedMs, status: descRes.status, message: "host returned a non-JSON session descriptor" };
|
|
36040
|
+
}
|
|
36041
|
+
if (descriptor["alive"] === false) {
|
|
36042
|
+
return { ok: false, waitedMs, message: `session ${sessionId} ended while waiting for its turn to drain` };
|
|
36043
|
+
}
|
|
36044
|
+
const queue = Array.isArray(descriptor["promptQueue"]) ? descriptor["promptQueue"] : [];
|
|
36045
|
+
if (descriptor["busy"] !== true && queue.length === 0) {
|
|
36046
|
+
return { ok: true, waitedMs };
|
|
36047
|
+
}
|
|
36048
|
+
if (input.maxWaitMs !== void 0 && input.maxWaitMs > 0 && waitedMs >= input.maxWaitMs) {
|
|
36049
|
+
return { ok: false, waitedMs, message: `timed out after ${waitedMs}ms waiting for the turn to drain` };
|
|
36050
|
+
}
|
|
36051
|
+
}
|
|
36052
|
+
}
|
|
35755
36053
|
|
|
35756
36054
|
// src/app-tools.ts
|
|
35757
36055
|
init_app_ui_build();
|
|
@@ -37339,7 +37637,7 @@ function createLedgerAppender(app, appRunId, runId, item) {
|
|
|
37339
37637
|
};
|
|
37340
37638
|
return { append, flush: () => chain };
|
|
37341
37639
|
}
|
|
37342
|
-
async function executeRunWorkflow(state, runtimeWf, agents, signal, sessionEvents,
|
|
37640
|
+
async function executeRunWorkflow(state, runtimeWf, agents, signal, sessionEvents, cache3, cacheKey, input, persist, appRegistry, eventLog, lease, runsRoot) {
|
|
37343
37641
|
if (lease) {
|
|
37344
37642
|
const renew = () => {
|
|
37345
37643
|
state.run.lease = { ownerId: lease.ownerId, heartbeatAt: lease.now().toISOString() };
|
|
@@ -37371,7 +37669,7 @@ async function executeRunWorkflow(state, runtimeWf, agents, signal, sessionEvent
|
|
|
37371
37669
|
workspaceSlug: state.workspaceSlug,
|
|
37372
37670
|
...workspacePaths ? { workspace: workspacePaths.scratch, artifactsDir: workspacePaths.artifactsDir, runId: state.run.runId, onArtifact } : {},
|
|
37373
37671
|
input,
|
|
37374
|
-
...
|
|
37672
|
+
...cache3 ? { cache: cache3, cacheKey } : {},
|
|
37375
37673
|
// WP-S — human-resolved approval steps: a `kind: "approval"` step parks
|
|
37376
37674
|
// the run as "awaiting-approval" with a pending inbox entry, instead of
|
|
37377
37675
|
// the engine's silent auto-approve. A human answers through
|
|
@@ -38057,9 +38355,9 @@ function createWorkflowRunner(opts) {
|
|
|
38057
38355
|
}
|
|
38058
38356
|
);
|
|
38059
38357
|
state.agents = agents;
|
|
38060
|
-
const
|
|
38358
|
+
const cache3 = input.cacheKey ? journal ? teeIntoInternalJournal(createFileStepCache(input.cacheKey), journal) : createFileStepCache(input.cacheKey) : journal?.cache;
|
|
38061
38359
|
const effectiveCacheKey = input.cacheKey ?? journal?.cacheKey;
|
|
38062
|
-
void executeRunWorkflow(state, workflow, agents, abort.signal, sessionEvents,
|
|
38360
|
+
void executeRunWorkflow(state, workflow, agents, abort.signal, sessionEvents, cache3, effectiveCacheKey, void 0, persist, opts.appRegistry, eventLog, leaseOpts, workspace).then(() => {
|
|
38063
38361
|
for (const [sid, binding] of sessionToRun) {
|
|
38064
38362
|
if (binding.runId === runId) sessionToRun.delete(sid);
|
|
38065
38363
|
}
|
|
@@ -38190,7 +38488,7 @@ function createWorkflowRunner(opts) {
|
|
|
38190
38488
|
);
|
|
38191
38489
|
state.agents = agents;
|
|
38192
38490
|
const journal = internalJournal(workspaceRoot, runId);
|
|
38193
|
-
const
|
|
38491
|
+
const cache3 = args.cacheKey ? journal ? teeIntoInternalJournal(createFileStepCache(args.cacheKey), journal) : createFileStepCache(args.cacheKey) : journal?.cache;
|
|
38194
38492
|
const effectiveCacheKey = args.cacheKey ?? journal?.cacheKey;
|
|
38195
38493
|
const execWorkflow = journal && args.cacheKey === void 0 ? forceCacheableWorkflow(workflow) : workflow;
|
|
38196
38494
|
void executeRunWorkflow(
|
|
@@ -38199,7 +38497,7 @@ function createWorkflowRunner(opts) {
|
|
|
38199
38497
|
agents,
|
|
38200
38498
|
abort.signal,
|
|
38201
38499
|
sessionEvents,
|
|
38202
|
-
|
|
38500
|
+
cache3,
|
|
38203
38501
|
effectiveCacheKey,
|
|
38204
38502
|
args.input,
|
|
38205
38503
|
persist,
|
|
@@ -39663,7 +39961,7 @@ function createTaskLedger(opts) {
|
|
|
39663
39961
|
conflict: false,
|
|
39664
39962
|
error: `task "${taskId}" not found`
|
|
39665
39963
|
});
|
|
39666
|
-
const
|
|
39964
|
+
const lookup2 = (taskId, caller) => {
|
|
39667
39965
|
const task = tasks.get(taskId);
|
|
39668
39966
|
if (!task) return void 0;
|
|
39669
39967
|
if (!canAccessBoard(caller, task.boardId)) return void 0;
|
|
@@ -39808,7 +40106,7 @@ function createTaskLedger(opts) {
|
|
|
39808
40106
|
return Array.from(tasks.values()).filter((t) => t.boardId === boardId).filter((t) => filter?.status !== void 0 ? t.status === filter.status : true).filter((t) => includeClosed || !isClosedTaskStatus(t.status)).sort((a, b) => a.createdAt.localeCompare(b.createdAt));
|
|
39809
40107
|
};
|
|
39810
40108
|
const claim = (input, caller) => {
|
|
39811
|
-
const task =
|
|
40109
|
+
const task = lookup2(input.taskId, caller);
|
|
39812
40110
|
if (!task) return notFound(input.taskId);
|
|
39813
40111
|
if (input.rev !== task.rev || task.owner !== void 0) {
|
|
39814
40112
|
return { ok: false, conflict: true, current: task };
|
|
@@ -39831,7 +40129,7 @@ function createTaskLedger(opts) {
|
|
|
39831
40129
|
return { ok: true, task };
|
|
39832
40130
|
};
|
|
39833
40131
|
const update = (input, caller) => {
|
|
39834
|
-
const task =
|
|
40132
|
+
const task = lookup2(input.taskId, caller);
|
|
39835
40133
|
if (!task) return notFound(input.taskId);
|
|
39836
40134
|
if (input.rev !== task.rev) {
|
|
39837
40135
|
return { ok: false, conflict: true, current: task };
|
|
@@ -40014,7 +40312,7 @@ function createTaskLedger(opts) {
|
|
|
40014
40312
|
return { ok: true, task };
|
|
40015
40313
|
};
|
|
40016
40314
|
const linkApproval = (taskId, approvalId, caller) => {
|
|
40017
|
-
const task =
|
|
40315
|
+
const task = lookup2(taskId, caller);
|
|
40018
40316
|
if (!task) return notFound(taskId);
|
|
40019
40317
|
const manager = isManager(caller, task);
|
|
40020
40318
|
const owner = isOwner(caller, task);
|
|
@@ -40059,7 +40357,7 @@ function createTaskLedger(opts) {
|
|
|
40059
40357
|
return {
|
|
40060
40358
|
create,
|
|
40061
40359
|
list,
|
|
40062
|
-
get: (taskId, caller) =>
|
|
40360
|
+
get: (taskId, caller) => lookup2(taskId, caller),
|
|
40063
40361
|
claim,
|
|
40064
40362
|
update,
|
|
40065
40363
|
linkApproval,
|
|
@@ -42506,7 +42804,7 @@ function createRemoteCatalogClient(options = {}) {
|
|
|
42506
42804
|
const timeoutMs = options.timeoutMs ?? DEFAULT_REMOTE_CATALOG_TIMEOUT_MS;
|
|
42507
42805
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
42508
42806
|
const now = options.now ?? Date.now;
|
|
42509
|
-
const
|
|
42807
|
+
const cache3 = /* @__PURE__ */ new Map();
|
|
42510
42808
|
async function fetchOne(url) {
|
|
42511
42809
|
let body;
|
|
42512
42810
|
try {
|
|
@@ -42538,13 +42836,13 @@ function createRemoteCatalogClient(options = {}) {
|
|
|
42538
42836
|
async fetchSources(sources, opts = {}) {
|
|
42539
42837
|
const results = await Promise.all(
|
|
42540
42838
|
sources.map(async ({ url }) => {
|
|
42541
|
-
const hit =
|
|
42839
|
+
const hit = cache3.get(url);
|
|
42542
42840
|
if (!opts.refresh && hit && now() - hit.at < ttlMs) return hit.result;
|
|
42543
42841
|
const result = await fetchOne(url);
|
|
42544
42842
|
if (result.entries.length > 0 || result.warnings.length === 0) {
|
|
42545
|
-
|
|
42843
|
+
cache3.set(url, { at: now(), result });
|
|
42546
42844
|
} else {
|
|
42547
|
-
|
|
42845
|
+
cache3.delete(url);
|
|
42548
42846
|
}
|
|
42549
42847
|
return result;
|
|
42550
42848
|
})
|
|
@@ -43880,7 +44178,7 @@ function send(res, status, body) {
|
|
|
43880
44178
|
res.writeHead(status, { "content-type": "application/json" });
|
|
43881
44179
|
res.end(JSON.stringify(body));
|
|
43882
44180
|
}
|
|
43883
|
-
async function
|
|
44181
|
+
async function readBody2(req) {
|
|
43884
44182
|
const chunks = [];
|
|
43885
44183
|
let size = 0;
|
|
43886
44184
|
for await (const chunk of req) {
|
|
@@ -43917,7 +44215,7 @@ async function handleA2aTaskRequest(req, res, path, deps2) {
|
|
|
43917
44215
|
return true;
|
|
43918
44216
|
}
|
|
43919
44217
|
if (appId.includes("/.well-known/")) return false;
|
|
43920
|
-
const body = await
|
|
44218
|
+
const body = await readBody2(req);
|
|
43921
44219
|
if ("tooLarge" in body) {
|
|
43922
44220
|
send(res, 413, { jsonrpc: "2.0", id: null, error: { code: JSON_RPC_ERROR.invalidRequest, message: "request body too large" } });
|
|
43923
44221
|
return true;
|
|
@@ -45303,8 +45601,8 @@ async function startHttpServer(opts) {
|
|
|
45303
45601
|
}))
|
|
45304
45602
|
};
|
|
45305
45603
|
});
|
|
45306
|
-
server2.server.setRequestHandler(CallToolRequestSchema, async (
|
|
45307
|
-
const out = await proxy.callTool(importId,
|
|
45604
|
+
server2.server.setRequestHandler(CallToolRequestSchema, async (request2) => {
|
|
45605
|
+
const out = await proxy.callTool(importId, request2.params.name, request2.params.arguments ?? {});
|
|
45308
45606
|
if (!out.ok) {
|
|
45309
45607
|
return { content: [{ type: "text", text: out.error }], isError: true };
|
|
45310
45608
|
}
|
|
@@ -46993,6 +47291,16 @@ async function startHttpServer(opts) {
|
|
|
46993
47291
|
);
|
|
46994
47292
|
if (handled) return;
|
|
46995
47293
|
}
|
|
47294
|
+
if (path.startsWith("/device-prompt/")) {
|
|
47295
|
+
const handled = await handleDevicePrompt(
|
|
47296
|
+
req,
|
|
47297
|
+
res,
|
|
47298
|
+
path,
|
|
47299
|
+
opts.port,
|
|
47300
|
+
opts.deviceSpawnAllow === true
|
|
47301
|
+
);
|
|
47302
|
+
if (handled) return;
|
|
47303
|
+
}
|
|
46996
47304
|
if (opts.remote && path.startsWith("/remote")) {
|
|
46997
47305
|
const handled = await handleRemoteControl(req, res, path, opts.remote);
|
|
46998
47306
|
if (handled) return;
|
|
@@ -49623,6 +49931,79 @@ async function handleDeviceSpawn(req, res, path, port, spawnAllowed) {
|
|
|
49623
49931
|
Readable.fromWeb(upstreamRes.body).pipe(res);
|
|
49624
49932
|
return true;
|
|
49625
49933
|
}
|
|
49934
|
+
async function handleDevicePrompt(req, res, path, port, spawnAllowed) {
|
|
49935
|
+
const json3 = (status, body2) => {
|
|
49936
|
+
res.writeHead(status, { "content-type": "application/json" });
|
|
49937
|
+
res.end(JSON.stringify(body2));
|
|
49938
|
+
};
|
|
49939
|
+
const promptMatch = path.match(/^\/device-prompt\/([^/?]+)(\?.*)?$/);
|
|
49940
|
+
if (!promptMatch) return false;
|
|
49941
|
+
const sessionId = decodeURIComponent(promptMatch[1] ?? "");
|
|
49942
|
+
if (!sessionId) return false;
|
|
49943
|
+
if (req.headers[HOST_SCOPE_HEADER] !== "1") {
|
|
49944
|
+
json3(403, {
|
|
49945
|
+
error: "host_scope_required",
|
|
49946
|
+
message: "device-prompt is reachable only over a host-scoped pairing (the far end must have registered this daemon via `agentproto pair offer --host` + `agentproto devices add`)"
|
|
49947
|
+
});
|
|
49948
|
+
return true;
|
|
49949
|
+
}
|
|
49950
|
+
if (!spawnAllowed) {
|
|
49951
|
+
json3(403, {
|
|
49952
|
+
error: "spawn_disabled",
|
|
49953
|
+
message: "this host has not opted in \u2014 run `agentproto devices allow-spawn on` on it"
|
|
49954
|
+
});
|
|
49955
|
+
return true;
|
|
49956
|
+
}
|
|
49957
|
+
if (req.method !== "POST") {
|
|
49958
|
+
json3(405, { error: "method_not_allowed", message: "POST /device-prompt/:sessionId" });
|
|
49959
|
+
return true;
|
|
49960
|
+
}
|
|
49961
|
+
const chunks = [];
|
|
49962
|
+
for await (const chunk of req) chunks.push(chunk);
|
|
49963
|
+
const body = Buffer.concat(chunks);
|
|
49964
|
+
const headers = {};
|
|
49965
|
+
for (const [k, v] of Object.entries(req.headers)) {
|
|
49966
|
+
if (v === void 0) continue;
|
|
49967
|
+
const lower = k.toLowerCase();
|
|
49968
|
+
if (lower === "connection" || lower === "keep-alive" || lower === "transfer-encoding") continue;
|
|
49969
|
+
if (lower === "upgrade" || lower === "host" || lower === "content-length") continue;
|
|
49970
|
+
if (lower === HOST_SCOPE_HEADER) continue;
|
|
49971
|
+
headers[k] = Array.isArray(v) ? v.join(", ") : v;
|
|
49972
|
+
}
|
|
49973
|
+
const rawUrl = req.url ?? "";
|
|
49974
|
+
const query = rawUrl.includes("?") ? rawUrl.slice(rawUrl.indexOf("?")) : "";
|
|
49975
|
+
let upstreamRes;
|
|
49976
|
+
try {
|
|
49977
|
+
upstreamRes = await fetch(
|
|
49978
|
+
`http://127.0.0.1:${port}/sessions/${encodeURIComponent(sessionId)}/prompt${query}`,
|
|
49979
|
+
{
|
|
49980
|
+
method: "POST",
|
|
49981
|
+
headers,
|
|
49982
|
+
...body.length > 0 ? { body } : {}
|
|
49983
|
+
// Cast rationale — see `handleDeviceSpawn`'s identical cast above.
|
|
49984
|
+
}
|
|
49985
|
+
);
|
|
49986
|
+
} catch (err) {
|
|
49987
|
+
json3(502, {
|
|
49988
|
+
error: "self_proxy_unreachable",
|
|
49989
|
+
message: err instanceof Error ? err.message : String(err)
|
|
49990
|
+
});
|
|
49991
|
+
return true;
|
|
49992
|
+
}
|
|
49993
|
+
const resHeaders = {};
|
|
49994
|
+
upstreamRes.headers.forEach((v, k) => {
|
|
49995
|
+
if (k === "content-length" || k === "connection") return;
|
|
49996
|
+
resHeaders[k] = v;
|
|
49997
|
+
});
|
|
49998
|
+
res.writeHead(upstreamRes.status, resHeaders);
|
|
49999
|
+
if (!upstreamRes.body) {
|
|
50000
|
+
res.end();
|
|
50001
|
+
return true;
|
|
50002
|
+
}
|
|
50003
|
+
const resBuf = Buffer.from(await upstreamRes.arrayBuffer());
|
|
50004
|
+
res.end(resBuf);
|
|
50005
|
+
return true;
|
|
50006
|
+
}
|
|
49626
50007
|
async function handleRemoteControl(req, res, path, controller) {
|
|
49627
50008
|
const json3 = (status, body) => {
|
|
49628
50009
|
res.writeHead(status, { "content-type": "application/json" });
|
|
@@ -50386,6 +50767,33 @@ async function handleDevices(req, res, path, registry, hostRegistry, joinTokens)
|
|
|
50386
50767
|
}
|
|
50387
50768
|
return true;
|
|
50388
50769
|
}
|
|
50770
|
+
const deviceSessionPromptMatch = path.match(/^\/devices\/([^/]+)\/sessions\/([^/]+)\/prompt$/);
|
|
50771
|
+
if (deviceSessionPromptMatch && req.method === "POST") {
|
|
50772
|
+
if (!hostRegistry) {
|
|
50773
|
+
json3(404, { error: "no_host_registry", message: "this daemon has no host registry wired" });
|
|
50774
|
+
return true;
|
|
50775
|
+
}
|
|
50776
|
+
const target = decodeURIComponent(deviceSessionPromptMatch[1] ?? "");
|
|
50777
|
+
const sessionId = decodeURIComponent(deviceSessionPromptMatch[2] ?? "");
|
|
50778
|
+
const body = await readJsonBody2(req);
|
|
50779
|
+
const b = body && typeof body === "object" ? body : {};
|
|
50780
|
+
const reqUrl = req.url ?? "";
|
|
50781
|
+
const qs = new URLSearchParams(reqUrl.includes("?") ? reqUrl.slice(reqUrl.indexOf("?") + 1) : "");
|
|
50782
|
+
try {
|
|
50783
|
+
const result = await promptHostSession(hostRegistry, target, sessionId, {
|
|
50784
|
+
prompt: b["prompt"],
|
|
50785
|
+
interrupt: b["interrupt"] === true,
|
|
50786
|
+
force: b["force"] === true,
|
|
50787
|
+
wait: ["1", "true"].includes(qs.get("wait") ?? ""),
|
|
50788
|
+
...qs.get("waitPollMs") ? { pollMs: Number(qs.get("waitPollMs")) } : {},
|
|
50789
|
+
...qs.get("maxWaitMs") ? { maxWaitMs: Number(qs.get("maxWaitMs")) } : {}
|
|
50790
|
+
});
|
|
50791
|
+
json3(result.ok ? 200 : result.status ?? 400, result);
|
|
50792
|
+
} catch (err) {
|
|
50793
|
+
json3(502, { error: "prompt_forward_failed", message: err instanceof Error ? err.message : String(err) });
|
|
50794
|
+
}
|
|
50795
|
+
return true;
|
|
50796
|
+
}
|
|
50389
50797
|
const idMatch = path.match(/^\/devices\/([^/]+)$/);
|
|
50390
50798
|
if (idMatch && req.method === "PATCH") {
|
|
50391
50799
|
const target = decodeURIComponent(idMatch[1] ?? "");
|
|
@@ -55793,7 +56201,7 @@ function waitingOnKey(w) {
|
|
|
55793
56201
|
var PR_SETTLE_SWEEP_INTERVAL_MS = 5 * 6e4;
|
|
55794
56202
|
var PR_SETTLE_SWEEP_MAX_URLS = 10;
|
|
55795
56203
|
function createActivityProjector(opts) {
|
|
55796
|
-
const
|
|
56204
|
+
const cache3 = /* @__PURE__ */ new Map();
|
|
55797
56205
|
const prStates = /* @__PURE__ */ new Map();
|
|
55798
56206
|
const projectOwnerRaw = (owner) => {
|
|
55799
56207
|
switch (owner) {
|
|
@@ -55854,9 +56262,9 @@ function createActivityProjector(opts) {
|
|
|
55854
56262
|
const seen = /* @__PURE__ */ new Set();
|
|
55855
56263
|
for (const rec of next) {
|
|
55856
56264
|
seen.add(rec.id);
|
|
55857
|
-
const prev =
|
|
56265
|
+
const prev = cache3.get(rec.id);
|
|
55858
56266
|
const changed = !prev || prev.state !== rec.state || prev.taskId !== rec.taskId || waitingOnKey(prev.waitingOn) !== waitingOnKey(rec.waitingOn);
|
|
55859
|
-
|
|
56267
|
+
cache3.set(rec.id, rec);
|
|
55860
56268
|
if (changed && announce) {
|
|
55861
56269
|
opts.sessionEvents.emit({
|
|
55862
56270
|
type: "activity:changed",
|
|
@@ -55865,8 +56273,8 @@ function createActivityProjector(opts) {
|
|
|
55865
56273
|
});
|
|
55866
56274
|
}
|
|
55867
56275
|
}
|
|
55868
|
-
for (const [id, rec] of
|
|
55869
|
-
if (OWNER_OF_SOURCE[rec.source] === owner && !seen.has(id))
|
|
56276
|
+
for (const [id, rec] of cache3) {
|
|
56277
|
+
if (OWNER_OF_SOURCE[rec.source] === owner && !seen.has(id)) cache3.delete(id);
|
|
55870
56278
|
}
|
|
55871
56279
|
};
|
|
55872
56280
|
const ownersTouchedBy = (type) => {
|
|
@@ -55978,7 +56386,7 @@ function createActivityProjector(opts) {
|
|
|
55978
56386
|
dispose() {
|
|
55979
56387
|
unsubscribe();
|
|
55980
56388
|
if (sweepTimer) clearInterval(sweepTimer);
|
|
55981
|
-
|
|
56389
|
+
cache3.clear();
|
|
55982
56390
|
prStates.clear();
|
|
55983
56391
|
}
|
|
55984
56392
|
};
|
|
@@ -56163,7 +56571,7 @@ function createApprovalsEngine(opts) {
|
|
|
56163
56571
|
const record2 = approvals.get(id);
|
|
56164
56572
|
return record2 ? checkExpiry(record2) : void 0;
|
|
56165
56573
|
};
|
|
56166
|
-
const
|
|
56574
|
+
const request2 = (input, requester) => {
|
|
56167
56575
|
const id = `apr_${randomUUID().replace(/-/g, "").slice(0, 20)}`;
|
|
56168
56576
|
const requestedChannels = input.channels && input.channels.length > 0 ? input.channels.filter((c) => enabledChannels.includes(c)) : enabledChannels;
|
|
56169
56577
|
const canonicalJson2 = canonicalPayloadFor(input.payload);
|
|
@@ -56329,7 +56737,7 @@ function createApprovalsEngine(opts) {
|
|
|
56329
56737
|
};
|
|
56330
56738
|
return {
|
|
56331
56739
|
enabledChannels,
|
|
56332
|
-
request,
|
|
56740
|
+
request: request2,
|
|
56333
56741
|
get,
|
|
56334
56742
|
list,
|
|
56335
56743
|
wait,
|
|
@@ -58235,127 +58643,9 @@ function registerPairingTools(server, opts) {
|
|
|
58235
58643
|
}
|
|
58236
58644
|
);
|
|
58237
58645
|
}
|
|
58238
|
-
var HOP_BY_HOP = /* @__PURE__ */ new Set(["connection", "keep-alive", "transfer-encoding", "upgrade", "host"]);
|
|
58239
|
-
function collectHeaders(raw) {
|
|
58240
|
-
const headers = {};
|
|
58241
|
-
for (const [k, v] of Object.entries(raw)) {
|
|
58242
|
-
if (v === void 0 || HOP_BY_HOP.has(k.toLowerCase())) continue;
|
|
58243
|
-
headers[k] = Array.isArray(v) ? v.join(", ") : v;
|
|
58244
|
-
}
|
|
58245
|
-
return headers;
|
|
58246
|
-
}
|
|
58247
|
-
function isPrematureCloseError(err) {
|
|
58248
|
-
return typeof err === "object" && err !== null && "code" in err && err.code === "ERR_STREAM_PREMATURE_CLOSE";
|
|
58249
|
-
}
|
|
58250
|
-
async function readBody2(req) {
|
|
58251
|
-
const chunks = [];
|
|
58252
|
-
for await (const chunk of req) chunks.push(chunk);
|
|
58253
|
-
if (chunks.length === 0) return void 0;
|
|
58254
|
-
return new Uint8Array(Buffer.concat(chunks));
|
|
58255
|
-
}
|
|
58256
|
-
async function startDeviceSandboxBridge(opts) {
|
|
58257
|
-
const { hostRegistry, target } = opts;
|
|
58258
|
-
const server = createServer((req, res) => {
|
|
58259
|
-
void handleRequest(req, res).catch((err) => {
|
|
58260
|
-
if (res.writableEnded) return;
|
|
58261
|
-
if (!res.headersSent) {
|
|
58262
|
-
res.writeHead(502, { "content-type": "application/json" });
|
|
58263
|
-
res.end(
|
|
58264
|
-
JSON.stringify({
|
|
58265
|
-
error: "device_sandbox_bridge_failed",
|
|
58266
|
-
message: err instanceof Error ? err.message : String(err)
|
|
58267
|
-
})
|
|
58268
|
-
);
|
|
58269
|
-
return;
|
|
58270
|
-
}
|
|
58271
|
-
res.end();
|
|
58272
|
-
});
|
|
58273
|
-
});
|
|
58274
|
-
async function handleRequest(req, res) {
|
|
58275
|
-
const body = await readBody2(req);
|
|
58276
|
-
let upstream;
|
|
58277
|
-
try {
|
|
58278
|
-
upstream = await hostRegistry.forwardHttpStream(target, {
|
|
58279
|
-
method: req.method ?? "GET",
|
|
58280
|
-
path: `/device-spawn${req.url ?? "/"}`,
|
|
58281
|
-
headers: collectHeaders(req.headers),
|
|
58282
|
-
...body ? { body } : {}
|
|
58283
|
-
});
|
|
58284
|
-
} catch (err) {
|
|
58285
|
-
res.writeHead(502, { "content-type": "application/json" });
|
|
58286
|
-
res.end(
|
|
58287
|
-
JSON.stringify({
|
|
58288
|
-
error: "device_unreachable",
|
|
58289
|
-
message: err instanceof Error ? err.message : String(err)
|
|
58290
|
-
})
|
|
58291
|
-
);
|
|
58292
|
-
return;
|
|
58293
|
-
}
|
|
58294
|
-
const resHeaders = {};
|
|
58295
|
-
for (const [k, v] of Object.entries(upstream.headers)) {
|
|
58296
|
-
if (HOP_BY_HOP.has(k.toLowerCase()) || k.toLowerCase() === "content-length") continue;
|
|
58297
|
-
resHeaders[k] = v;
|
|
58298
|
-
}
|
|
58299
|
-
res.writeHead(upstream.status, resHeaders);
|
|
58300
|
-
try {
|
|
58301
|
-
await pipeline(
|
|
58302
|
-
Readable.fromWeb(upstream.body),
|
|
58303
|
-
res
|
|
58304
|
-
);
|
|
58305
|
-
} catch (err) {
|
|
58306
|
-
if (!isPrematureCloseError(err)) throw err;
|
|
58307
|
-
}
|
|
58308
|
-
}
|
|
58309
|
-
const port = await getFreePort2();
|
|
58310
|
-
await new Promise((resolve41, reject) => {
|
|
58311
|
-
server.once("error", reject);
|
|
58312
|
-
server.listen(port, "127.0.0.1", () => resolve41());
|
|
58313
|
-
});
|
|
58314
|
-
return {
|
|
58315
|
-
mcpUrl: `http://127.0.0.1:${port}/mcp`,
|
|
58316
|
-
async close() {
|
|
58317
|
-
await new Promise((resolve41) => server.close(() => resolve41()));
|
|
58318
|
-
}
|
|
58319
|
-
};
|
|
58320
|
-
}
|
|
58321
|
-
async function getFreePort2() {
|
|
58322
|
-
return new Promise((resolve41, reject) => {
|
|
58323
|
-
const srv = createServer$1();
|
|
58324
|
-
srv.once("error", reject);
|
|
58325
|
-
srv.listen(0, "127.0.0.1", () => {
|
|
58326
|
-
const address = srv.address();
|
|
58327
|
-
if (address === null || typeof address === "string") {
|
|
58328
|
-
srv.close(() => reject(new Error("failed to allocate a free port")));
|
|
58329
|
-
return;
|
|
58330
|
-
}
|
|
58331
|
-
const { port } = address;
|
|
58332
|
-
srv.close(() => resolve41(port));
|
|
58333
|
-
});
|
|
58334
|
-
});
|
|
58335
|
-
}
|
|
58336
58646
|
|
|
58337
|
-
// src/
|
|
58338
|
-
|
|
58339
|
-
return {
|
|
58340
|
-
async boot(_spec, _opts) {
|
|
58341
|
-
const hosts = await hostRegistry.list();
|
|
58342
|
-
const known = hosts.some((h) => h.fingerprint === target || h.name === target);
|
|
58343
|
-
if (!known) {
|
|
58344
|
-
throw new Error(
|
|
58345
|
-
`device sandbox: no paired host device matches "${target}" \u2014 run \`agentproto devices add <offer-url>\` here first (the offer must come from \`agentproto pair offer --host\` run ON that device). Check \`agentproto devices list\` for known devices.`
|
|
58346
|
-
);
|
|
58347
|
-
}
|
|
58348
|
-
const bridge = await startDeviceSandboxBridge({ hostRegistry, target });
|
|
58349
|
-
return {
|
|
58350
|
-
mcpUrl: bridge.mcpUrl,
|
|
58351
|
-
sandboxId: `device-${target}-${randomUUID()}`,
|
|
58352
|
-
async stop() {
|
|
58353
|
-
await bridge.close();
|
|
58354
|
-
}
|
|
58355
|
-
};
|
|
58356
|
-
}
|
|
58357
|
-
};
|
|
58358
|
-
}
|
|
58647
|
+
// src/index.ts
|
|
58648
|
+
init_device();
|
|
58359
58649
|
function text9(value) {
|
|
58360
58650
|
return {
|
|
58361
58651
|
content: [
|
|
@@ -58367,7 +58657,7 @@ function text9(value) {
|
|
|
58367
58657
|
};
|
|
58368
58658
|
}
|
|
58369
58659
|
function registerDeviceTools(server, opts) {
|
|
58370
|
-
const { registry, joinTokens } = opts;
|
|
58660
|
+
const { registry, hosts: registryHosts, joinTokens } = opts;
|
|
58371
58661
|
server.tool(
|
|
58372
58662
|
"device_list",
|
|
58373
58663
|
"List devices known to this daemon: fingerprint, name, role (client|host), kind (browser|cli|daemon, best-effort from the client's self-reported name; always daemon for a host), rendezvous, createdAt, lastSeen, scope (host, when granted via `pair offer --host`), online (a channel/forward is active right now or a recent contact succeeded), and for hosts lastProbeAt/lastError (last contact attempt and why it failed). Joined CI hosts that are gone (said goodbye, or unreachable past the TTL) are hidden unless includeEnded is set; a manually added host unreachable past the TTL shows stale: true. Read-only.",
|
|
@@ -58449,6 +58739,37 @@ function registerDeviceTools(server, opts) {
|
|
|
58449
58739
|
}
|
|
58450
58740
|
}
|
|
58451
58741
|
);
|
|
58742
|
+
if (registryHosts) {
|
|
58743
|
+
server.tool(
|
|
58744
|
+
"device_prompt",
|
|
58745
|
+
"Send a prompt (a follow-up turn) to a session on a registered HOST device \u2014 the write counterpart of device_sessions, forwarded live over the host's E2E channel. Queueing rules are identical to a local agent_prompt: fire-and-forget by default, queued behind the session's in-flight turn, `interrupt`/`force` to redirect or jump the queue. wait: true blocks until the prompted turn drains. Requires the HOST daemon to have opted in (agentproto devices allow-spawn on) over a host-scoped pairing \u2014 the same gate as device spawn; refused for a plain remote-control pairing or an unknown target.",
|
|
58746
|
+
{
|
|
58747
|
+
target: z.string().describe("The host's fingerprint or name (see device_list)."),
|
|
58748
|
+
sessionId: z.string().min(1).describe("The session id ON the host to prompt."),
|
|
58749
|
+
prompt: z.union([z.string(), z.record(z.string(), z.unknown()), z.array(z.record(z.string(), z.unknown()))]).describe("Non-empty string, a content block, or an array of content blocks."),
|
|
58750
|
+
wait: z.boolean().optional().describe("Block until the prompted turn drains. Default false."),
|
|
58751
|
+
interrupt: z.boolean().optional().describe("Mid-turn: redirect instead of queueing (agent_prompt's interrupt). Default false."),
|
|
58752
|
+
force: z.boolean().optional().describe("Mid-turn: jump the FRONT of the FIFO (agent_prompt's force). Default false."),
|
|
58753
|
+
pollMs: z.number().int().min(10).max(1e4).optional().describe("Poll cadence while waiting, ms. Default 1000. Each poll is a fresh E2E dial."),
|
|
58754
|
+
maxWaitMs: z.number().int().positive().optional().describe("Give up waiting after this long (ms). Default: wait forever.")
|
|
58755
|
+
},
|
|
58756
|
+
async ({ target, sessionId, prompt, wait, interrupt, force, pollMs, maxWaitMs }) => {
|
|
58757
|
+
try {
|
|
58758
|
+
const result = await promptHostSession(registryHosts, target, sessionId, {
|
|
58759
|
+
prompt,
|
|
58760
|
+
...wait ? { wait: true } : {},
|
|
58761
|
+
...interrupt ? { interrupt: true } : {},
|
|
58762
|
+
...force ? { force: true } : {},
|
|
58763
|
+
...pollMs !== void 0 ? { pollMs } : {},
|
|
58764
|
+
...maxWaitMs !== void 0 ? { maxWaitMs } : {}
|
|
58765
|
+
});
|
|
58766
|
+
return text9(result);
|
|
58767
|
+
} catch (err) {
|
|
58768
|
+
return text9({ ok: false, message: err instanceof Error ? err.message : String(err) });
|
|
58769
|
+
}
|
|
58770
|
+
}
|
|
58771
|
+
);
|
|
58772
|
+
}
|
|
58452
58773
|
if (joinTokens) {
|
|
58453
58774
|
server.tool(
|
|
58454
58775
|
"join_token_create",
|
|
@@ -60116,6 +60437,440 @@ function createWorkspaceFs(opts) {
|
|
|
60116
60437
|
|
|
60117
60438
|
// src/index.ts
|
|
60118
60439
|
init_role();
|
|
60440
|
+
var WHSEC_PREFIX = "whsec_";
|
|
60441
|
+
function encodeWhsecSecret(raw) {
|
|
60442
|
+
return WHSEC_PREFIX + Buffer.from(raw).toString("base64");
|
|
60443
|
+
}
|
|
60444
|
+
function decodeWhsecSecret(secret) {
|
|
60445
|
+
if (typeof secret !== "string" || !secret.startsWith(WHSEC_PREFIX)) return null;
|
|
60446
|
+
const encoded = secret.slice(WHSEC_PREFIX.length);
|
|
60447
|
+
if (encoded.length === 0 || !/^[A-Za-z0-9+/]*={0,2}$/.test(encoded) || encoded.length % 4 !== 0) {
|
|
60448
|
+
return null;
|
|
60449
|
+
}
|
|
60450
|
+
let key;
|
|
60451
|
+
try {
|
|
60452
|
+
key = Buffer.from(encoded, "base64");
|
|
60453
|
+
} catch {
|
|
60454
|
+
return null;
|
|
60455
|
+
}
|
|
60456
|
+
if (key.length < 24 || key.length > 64) return null;
|
|
60457
|
+
return key;
|
|
60458
|
+
}
|
|
60459
|
+
function signWebhook(input) {
|
|
60460
|
+
if (input.secrets.length === 0) throw new Error("signWebhook: at least one secret is required");
|
|
60461
|
+
const prefix = Buffer.from(`${input.msgId}.${input.timestamp}.`, "utf8");
|
|
60462
|
+
const payload = Buffer.from(input.payload.buffer, input.payload.byteOffset, input.payload.byteLength);
|
|
60463
|
+
const segments = input.secrets.map((secret) => {
|
|
60464
|
+
const key = decodeWhsecSecret(secret);
|
|
60465
|
+
if (!key) throw new Error(`signWebhook: invalid webhooks secret format: ${redactPrefix(secret)}`);
|
|
60466
|
+
const mac = createHmac("sha256", key).update(prefix).update(payload).digest();
|
|
60467
|
+
return `v1,${mac.toString("base64")}`;
|
|
60468
|
+
});
|
|
60469
|
+
return {
|
|
60470
|
+
"webhook-id": input.msgId,
|
|
60471
|
+
"webhook-timestamp": String(input.timestamp),
|
|
60472
|
+
"webhook-signature": segments.join(" ")
|
|
60473
|
+
};
|
|
60474
|
+
}
|
|
60475
|
+
function redactPrefix(secret) {
|
|
60476
|
+
return `${WHSEC_PREFIX}<${secret.length - WHSEC_PREFIX.length} b64 chars, invalid>`;
|
|
60477
|
+
}
|
|
60478
|
+
var SsrfFetchError = class extends Error {
|
|
60479
|
+
constructor(reason, message) {
|
|
60480
|
+
super(message);
|
|
60481
|
+
this.reason = reason;
|
|
60482
|
+
this.name = "SsrfFetchError";
|
|
60483
|
+
}
|
|
60484
|
+
reason;
|
|
60485
|
+
};
|
|
60486
|
+
var MAX_REQUEST_BODY_BYTES = 262144;
|
|
60487
|
+
var MAX_RESPONSE_DIAGNOSTIC_BYTES = 4096;
|
|
60488
|
+
function isPubliclyRoutable(ipRaw) {
|
|
60489
|
+
const ip = ipRaw.includes("%") ? ipRaw.slice(0, ipRaw.indexOf("%")) : ipRaw;
|
|
60490
|
+
const v4 = parseIpv4(ip);
|
|
60491
|
+
if (v4) return ipv4Public(v4[0], v4[1], v4[2], v4[3]);
|
|
60492
|
+
const v6 = parseIpv6Bytes(ip);
|
|
60493
|
+
if (v6) return ipv6Public(v6);
|
|
60494
|
+
return false;
|
|
60495
|
+
}
|
|
60496
|
+
function ipv4Public(a, b, c, d) {
|
|
60497
|
+
if (a === 0) return false;
|
|
60498
|
+
if (a === 10) return false;
|
|
60499
|
+
if (a === 127) return false;
|
|
60500
|
+
if (a === 100 && b >= 64 && b <= 127) return false;
|
|
60501
|
+
if (a === 169 && b === 254) return false;
|
|
60502
|
+
if (a === 172 && b >= 16 && b <= 31) return false;
|
|
60503
|
+
if (a === 192 && b === 168) return false;
|
|
60504
|
+
if (a === 192 && b === 0 && c === 0) return false;
|
|
60505
|
+
if (a === 192 && b === 0 && c === 2) return false;
|
|
60506
|
+
if (a === 198 && b === 51 && c === 100) return false;
|
|
60507
|
+
if (a === 198 && (b === 18 || b === 19)) return false;
|
|
60508
|
+
if (a === 203 && b === 0 && c === 113) return false;
|
|
60509
|
+
if (a >= 224) return false;
|
|
60510
|
+
return true;
|
|
60511
|
+
}
|
|
60512
|
+
function ipv6Public(v6) {
|
|
60513
|
+
if (v6.mapped) return ipv4Public(v6.mapped[0], v6.mapped[1], v6.mapped[2], v6.mapped[3]);
|
|
60514
|
+
const b = v6.bytes;
|
|
60515
|
+
const v = new DataView(b.buffer, b.byteOffset, b.byteLength);
|
|
60516
|
+
const b0 = v.getUint8(0);
|
|
60517
|
+
const b1 = v.getUint8(1);
|
|
60518
|
+
const b2 = v.getUint8(2);
|
|
60519
|
+
const b3 = v.getUint8(3);
|
|
60520
|
+
const b15 = v.getUint8(15);
|
|
60521
|
+
const tailZero = b.slice(1, 15).every((x) => x === 0);
|
|
60522
|
+
if (b.every((x) => x === 0)) return false;
|
|
60523
|
+
if (tailZero && b0 === 0 && b15 === 1) return false;
|
|
60524
|
+
if ((b0 & 254) === 252) return false;
|
|
60525
|
+
if (b0 === 254 && (b1 & 192) === 128) return false;
|
|
60526
|
+
if (b0 === 255) return false;
|
|
60527
|
+
if (b0 === 32 && b1 === 1 && b2 === 13 && b3 === 184) return false;
|
|
60528
|
+
return true;
|
|
60529
|
+
}
|
|
60530
|
+
function parseIpv4(ip) {
|
|
60531
|
+
const parts = ip.split(".");
|
|
60532
|
+
if (parts.length !== 4) return null;
|
|
60533
|
+
const o = [];
|
|
60534
|
+
for (const part of parts) {
|
|
60535
|
+
if (!/^(?:0|[1-9]\d{0,2})$/.test(part)) return null;
|
|
60536
|
+
const n = Number(part);
|
|
60537
|
+
if (n > 255) return null;
|
|
60538
|
+
o.push(n);
|
|
60539
|
+
}
|
|
60540
|
+
const first = o[0];
|
|
60541
|
+
const second = o[1];
|
|
60542
|
+
const third = o[2];
|
|
60543
|
+
const fourth = o[3];
|
|
60544
|
+
if (first === void 0 || second === void 0 || third === void 0 || fourth === void 0) return null;
|
|
60545
|
+
return [first, second, third, fourth];
|
|
60546
|
+
}
|
|
60547
|
+
function parseIpv6Bytes(rawIp) {
|
|
60548
|
+
const ip = rawIp.replace(/^\[|\]$/g, "");
|
|
60549
|
+
if (!ip.includes(":")) return null;
|
|
60550
|
+
if (ip.lastIndexOf("::") !== ip.indexOf("::")) return null;
|
|
60551
|
+
const v4Match = ip.match(/^(?:.*:)(\d{1,3}(?:\.\d{1,3}){3})$/);
|
|
60552
|
+
const compressed = ip.includes("::");
|
|
60553
|
+
if (compressed) {
|
|
60554
|
+
const halves = ip.split("::");
|
|
60555
|
+
const before = halves[0] ?? "";
|
|
60556
|
+
const after = halves[1] ?? "";
|
|
60557
|
+
const head = before === "" ? [] : before.split(":");
|
|
60558
|
+
if (head.some((g) => !isHexGroup(g))) return null;
|
|
60559
|
+
const tail = after === "" ? [] : after.replace(/(\d{1,3}(?:\.\d{1,3}){3})$/, "").split(":").filter((g) => g !== "");
|
|
60560
|
+
if (tail.some((g) => !isHexGroup(g))) return null;
|
|
60561
|
+
return fromGroups(head, tail, v4Match?.[1]);
|
|
60562
|
+
}
|
|
60563
|
+
const all = ip.split(":");
|
|
60564
|
+
const v4 = v4Match ? [all[all.length - 1]] : [];
|
|
60565
|
+
const hexGroups = all.slice(0, all.length - (v4Match ? 1 : 0));
|
|
60566
|
+
if (all.length !== (v4Match ? 6 : 8) + (v4Match ? 1 : 0)) return null;
|
|
60567
|
+
if (hexGroups.some((g) => !isHexGroup(g))) return null;
|
|
60568
|
+
return fromGroups(hexGroups, [], v4 ? v4[0] : void 0);
|
|
60569
|
+
}
|
|
60570
|
+
function isHexGroup(g) {
|
|
60571
|
+
return /^[\da-fA-F]{1,4}$/.test(g);
|
|
60572
|
+
}
|
|
60573
|
+
function fromGroups(head, tail, v4) {
|
|
60574
|
+
if (head.length + tail.length > (v4 ? 6 : 8)) return null;
|
|
60575
|
+
const bytes = new Uint8Array(16);
|
|
60576
|
+
const writeGroup = (g, offset) => {
|
|
60577
|
+
if (g === void 0) return false;
|
|
60578
|
+
const v = parseInt(g, 16);
|
|
60579
|
+
bytes[offset] = v >> 8;
|
|
60580
|
+
bytes[offset + 1] = v & 255;
|
|
60581
|
+
return true;
|
|
60582
|
+
};
|
|
60583
|
+
head.forEach((g, i) => writeGroup(g, i * 2));
|
|
60584
|
+
const tailStart = 16 - tail.length * 2 - (v4 ? 4 : 0);
|
|
60585
|
+
tail.forEach((g, i) => writeGroup(g, tailStart + i * 2));
|
|
60586
|
+
if (v4) {
|
|
60587
|
+
const o = parseIpv4(v4);
|
|
60588
|
+
if (!o) return null;
|
|
60589
|
+
bytes[12] = o[0];
|
|
60590
|
+
bytes[13] = o[1];
|
|
60591
|
+
bytes[14] = o[2];
|
|
60592
|
+
bytes[15] = o[3];
|
|
60593
|
+
const mapped = bytes.slice(0, 10).every((x) => x === 0) && bytes[10] === 255 && bytes[11] === 255;
|
|
60594
|
+
return { bytes, mapped: mapped ? o : void 0 };
|
|
60595
|
+
}
|
|
60596
|
+
return { bytes };
|
|
60597
|
+
}
|
|
60598
|
+
async function resolveHost(hostname6, resolvers) {
|
|
60599
|
+
if (parseIpv4(hostname6)) return [hostname6];
|
|
60600
|
+
const bracketless = hostname6.replace(/^\[|\]$/g, "");
|
|
60601
|
+
if (bracketless.includes(":")) return [bracketless];
|
|
60602
|
+
if (resolvers.lookup) return (await resolvers.lookup(bracketless)).map((r) => r.address);
|
|
60603
|
+
const found2 = await lookup(bracketless, { all: true, verbatim: true });
|
|
60604
|
+
return found2.map((f) => f.address);
|
|
60605
|
+
}
|
|
60606
|
+
var testDispatcher = null;
|
|
60607
|
+
function setEgressDispatcherForTests(dispatcher) {
|
|
60608
|
+
testDispatcher = dispatcher;
|
|
60609
|
+
}
|
|
60610
|
+
async function ssrfFetch(url, init, io = {}) {
|
|
60611
|
+
let parsed;
|
|
60612
|
+
try {
|
|
60613
|
+
parsed = new URL(url);
|
|
60614
|
+
} catch {
|
|
60615
|
+
throw new SsrfFetchError("non_https", "unparseable callback URL");
|
|
60616
|
+
}
|
|
60617
|
+
if (parsed.protocol !== "https:") {
|
|
60618
|
+
throw new SsrfFetchError("non_https", `scheme ${parsed.protocol} \u2014 https only`);
|
|
60619
|
+
}
|
|
60620
|
+
if ((init.body?.byteLength ?? 0) > MAX_REQUEST_BODY_BYTES) {
|
|
60621
|
+
throw new SsrfFetchError("connect", `request body exceeds the ${MAX_REQUEST_BODY_BYTES}-byte clamp`);
|
|
60622
|
+
}
|
|
60623
|
+
if (testDispatcher) {
|
|
60624
|
+
const literal = parseIpv4(parsed.hostname);
|
|
60625
|
+
if (literal && !ipv4Public(literal[0], literal[1], literal[2], literal[3])) {
|
|
60626
|
+
throw new SsrfFetchError("private_target", `literal non-public address ${parsed.hostname}`);
|
|
60627
|
+
}
|
|
60628
|
+
return await testDispatcher({ url, headers: init.headers ?? {}, body: init.body, timeoutMs: init.timeoutMs });
|
|
60629
|
+
}
|
|
60630
|
+
const candidates = await resolveHost(parsed.hostname, io.resolvers ?? {});
|
|
60631
|
+
if (candidates.length === 0) {
|
|
60632
|
+
throw new SsrfFetchError("connect", `no addresses resolved for ${parsed.hostname}`);
|
|
60633
|
+
}
|
|
60634
|
+
const rejected = candidates.find((ip) => !isPubliclyRoutable(ip));
|
|
60635
|
+
if (rejected !== void 0) {
|
|
60636
|
+
throw new SsrfFetchError("private_target", `${parsed.hostname} resolves to non-public address ${rejected}`);
|
|
60637
|
+
}
|
|
60638
|
+
const connectIp = candidates[0] ?? candidates[candidates.length - 1];
|
|
60639
|
+
if (connectIp === void 0) throw new SsrfFetchError("connect", "no address available for connect");
|
|
60640
|
+
const port = parsed.port ? Number(parsed.port) : 443;
|
|
60641
|
+
return await rawPost({
|
|
60642
|
+
connectIp,
|
|
60643
|
+
servername: parsed.hostname,
|
|
60644
|
+
port,
|
|
60645
|
+
path: `${parsed.pathname}${parsed.search}`,
|
|
60646
|
+
headers: init.headers ?? {},
|
|
60647
|
+
body: init.body,
|
|
60648
|
+
timeoutMs: init.timeoutMs
|
|
60649
|
+
});
|
|
60650
|
+
}
|
|
60651
|
+
function rawPost(args) {
|
|
60652
|
+
return new Promise((resolve41, reject) => {
|
|
60653
|
+
const req = request({
|
|
60654
|
+
// Connect to the PRE-VALIDATED address; keep the original hostname in
|
|
60655
|
+
// `servername` so SNI and the certificate check never loosen.
|
|
60656
|
+
host: args.connectIp,
|
|
60657
|
+
port: args.port,
|
|
60658
|
+
path: args.path,
|
|
60659
|
+
method: "POST",
|
|
60660
|
+
servername: args.servername,
|
|
60661
|
+
headers: args.headers,
|
|
60662
|
+
rejectUnauthorized: true
|
|
60663
|
+
});
|
|
60664
|
+
let settled = false;
|
|
60665
|
+
const fail3 = (reason, message) => {
|
|
60666
|
+
if (settled) return;
|
|
60667
|
+
settled = true;
|
|
60668
|
+
req.destroy();
|
|
60669
|
+
reject(new SsrfFetchError(reason, message));
|
|
60670
|
+
};
|
|
60671
|
+
const ok3 = (status, body) => {
|
|
60672
|
+
if (settled) return;
|
|
60673
|
+
settled = true;
|
|
60674
|
+
resolve41({ status, body });
|
|
60675
|
+
};
|
|
60676
|
+
req.setTimeout(args.timeoutMs, () => fail3("timeout", `no response within ${args.timeoutMs}ms`));
|
|
60677
|
+
req.on("error", (err) => {
|
|
60678
|
+
fail3("connect", err.message);
|
|
60679
|
+
});
|
|
60680
|
+
req.on("response", (res) => {
|
|
60681
|
+
const status = res.statusCode ?? 0;
|
|
60682
|
+
let diags = Buffer.alloc(0);
|
|
60683
|
+
res.on("data", (chunk) => {
|
|
60684
|
+
if (diags.length < MAX_RESPONSE_DIAGNOSTIC_BYTES) {
|
|
60685
|
+
diags = Buffer.concat([diags, chunk.subarray(0, MAX_RESPONSE_DIAGNOSTIC_BYTES - diags.length)]);
|
|
60686
|
+
}
|
|
60687
|
+
if (diags.length >= MAX_RESPONSE_DIAGNOSTIC_BYTES) res.destroy();
|
|
60688
|
+
});
|
|
60689
|
+
res.on("end", () => {
|
|
60690
|
+
if (status >= 300 && status < 400) {
|
|
60691
|
+
const location = res.headers["location"];
|
|
60692
|
+
if (typeof location === "string" && !new URL(location, `https://${args.servername}`).protocol.startsWith("https")) {
|
|
60693
|
+
fail3("redirect", `redirect target ${location} is not https \u2014 never chased`);
|
|
60694
|
+
return;
|
|
60695
|
+
}
|
|
60696
|
+
}
|
|
60697
|
+
ok3(status, diags.toString("utf8"));
|
|
60698
|
+
});
|
|
60699
|
+
res.on("error", (err) => {
|
|
60700
|
+
fail3("connect", err.message);
|
|
60701
|
+
});
|
|
60702
|
+
});
|
|
60703
|
+
if (args.body) req.write(Buffer.from(args.body));
|
|
60704
|
+
req.end();
|
|
60705
|
+
});
|
|
60706
|
+
}
|
|
60707
|
+
function challengeReasonForSsrf(reason) {
|
|
60708
|
+
if (reason === "non_https") return "non_https";
|
|
60709
|
+
if (reason === "private_target" || reason === "redirect") return "ssrf_blocked";
|
|
60710
|
+
return "timeout";
|
|
60711
|
+
}
|
|
60712
|
+
var CHALLENGE_TTL_MS = 10 * 60 * 1e3;
|
|
60713
|
+
var encoder = new TextEncoder();
|
|
60714
|
+
var cache2 = /* @__PURE__ */ new Map();
|
|
60715
|
+
function challengeCacheSize() {
|
|
60716
|
+
return cache2.size;
|
|
60717
|
+
}
|
|
60718
|
+
function resetChallengeCacheForTests() {
|
|
60719
|
+
cache2.clear();
|
|
60720
|
+
}
|
|
60721
|
+
function normalizeCallbackUrl(url) {
|
|
60722
|
+
try {
|
|
60723
|
+
const u = new URL(url);
|
|
60724
|
+
const port = u.port === "443" ? "" : `:${u.port}`;
|
|
60725
|
+
return `${u.protocol.toLowerCase()}//${u.hostname.toLowerCase()}${port}${u.pathname}${u.search}`;
|
|
60726
|
+
} catch {
|
|
60727
|
+
return null;
|
|
60728
|
+
}
|
|
60729
|
+
}
|
|
60730
|
+
function hashSecret(secret) {
|
|
60731
|
+
return createHash("sha256").update(secret, "utf8").digest("hex");
|
|
60732
|
+
}
|
|
60733
|
+
function constantTimeEq(a, b) {
|
|
60734
|
+
const da = createHash("sha256").update(a, "utf8").digest();
|
|
60735
|
+
const db = createHash("sha256").update(b, "utf8").digest();
|
|
60736
|
+
return timingSafeEqual(da, db);
|
|
60737
|
+
}
|
|
60738
|
+
async function verifyCallback(input, deps2 = {}) {
|
|
60739
|
+
const now = deps2.now ?? (() => Date.now());
|
|
60740
|
+
const normalized = normalizeCallbackUrl(input.url);
|
|
60741
|
+
if (normalized === null || !normalized.startsWith("https://")) {
|
|
60742
|
+
return { ok: false, reason: "non_https", detail: "callback URL must be an https URL" };
|
|
60743
|
+
}
|
|
60744
|
+
if (decodeWhsecSecret(input.secret) === null) {
|
|
60745
|
+
return {
|
|
60746
|
+
ok: false,
|
|
60747
|
+
reason: "challenge_failed",
|
|
60748
|
+
detail: "invalid secret format \u2014 must be whsec_ + base64 decoding to 24..64 bytes (the caller validates before this; a bad secret is never cached)"
|
|
60749
|
+
};
|
|
60750
|
+
}
|
|
60751
|
+
const cacheKey = `${input.principal}|${normalized}|${hashSecret(input.secret)}`;
|
|
60752
|
+
const cached = cache2.get(cacheKey);
|
|
60753
|
+
if (cached && cached.expiresAt > now()) {
|
|
60754
|
+
return { ok: true, verificationBytes: cached.requestBytes };
|
|
60755
|
+
}
|
|
60756
|
+
if (cached) cache2.delete(cacheKey);
|
|
60757
|
+
const challenge = randomBytes(32).toString("hex");
|
|
60758
|
+
const requestBytes = encoder.encode(JSON.stringify({ type: "verification", challenge }));
|
|
60759
|
+
const msgId = `msg_verification_${randomBytes(12).toString("hex")}`;
|
|
60760
|
+
const timestamp = Math.floor(now() / 1e3);
|
|
60761
|
+
let headers;
|
|
60762
|
+
try {
|
|
60763
|
+
const signed = signWebhook({ msgId, timestamp, payload: requestBytes, secrets: [input.secret] });
|
|
60764
|
+
headers = {
|
|
60765
|
+
...signed,
|
|
60766
|
+
"content-type": "application/json",
|
|
60767
|
+
"X-MCP-Subscription-Id": input.subscriptionId
|
|
60768
|
+
};
|
|
60769
|
+
} catch (err) {
|
|
60770
|
+
return { ok: false, reason: "challenge_failed", detail: `signing failed: ${err.message}` };
|
|
60771
|
+
}
|
|
60772
|
+
let response;
|
|
60773
|
+
try {
|
|
60774
|
+
response = await (deps2.fetch ?? ssrfFetch)(input.url, {
|
|
60775
|
+
method: "POST",
|
|
60776
|
+
headers,
|
|
60777
|
+
body: requestBytes,
|
|
60778
|
+
timeoutMs: 1e4
|
|
60779
|
+
// default challenge timeout (delivery uses 15_000)
|
|
60780
|
+
});
|
|
60781
|
+
} catch (err) {
|
|
60782
|
+
if (err instanceof SsrfFetchError) {
|
|
60783
|
+
return { ok: false, reason: challengeReasonForSsrf(err.reason), detail: `${err.reason}: ${err.message}` };
|
|
60784
|
+
}
|
|
60785
|
+
return { ok: false, reason: "timeout", detail: err.message };
|
|
60786
|
+
}
|
|
60787
|
+
if (response.status < 200 || response.status > 299) {
|
|
60788
|
+
return { ok: false, reason: "non_2xx", detail: `callback responded with status ${response.status}` };
|
|
60789
|
+
}
|
|
60790
|
+
let parsedBody;
|
|
60791
|
+
try {
|
|
60792
|
+
parsedBody = JSON.parse(response.body);
|
|
60793
|
+
} catch {
|
|
60794
|
+
return { ok: false, reason: "challenge_failed", detail: "response body is not JSON" };
|
|
60795
|
+
}
|
|
60796
|
+
const echoed = parsedBody?.challenge;
|
|
60797
|
+
if (typeof echoed !== "string" || !constantTimeEq(echoed, challenge)) {
|
|
60798
|
+
return { ok: false, reason: "challenge_failed", detail: "response did not echo the fresh challenge" };
|
|
60799
|
+
}
|
|
60800
|
+
cache2.set(cacheKey, { requestBytes, expiresAt: now() + CHALLENGE_TTL_MS });
|
|
60801
|
+
return { ok: true, verificationBytes: requestBytes };
|
|
60802
|
+
}
|
|
60803
|
+
|
|
60804
|
+
// src/webhook-egress/delivery.ts
|
|
60805
|
+
var CLAMP_LIMIT_BYTES = 262144;
|
|
60806
|
+
var MAX_ATTEMPTS = 5;
|
|
60807
|
+
var BACKOFF_BASE_MS = 250;
|
|
60808
|
+
var BACKOFF_CAP_MS = 4e3;
|
|
60809
|
+
function deliveryBackoffDelay(attempt) {
|
|
60810
|
+
return Math.min(BACKOFF_BASE_MS * 2 ** attempt, BACKOFF_CAP_MS);
|
|
60811
|
+
}
|
|
60812
|
+
var TERMINAL_NO_RETRY_STATUSES = /* @__PURE__ */ new Set([410, 413]);
|
|
60813
|
+
function serializeEnvelope(envelope) {
|
|
60814
|
+
return new TextEncoder().encode(JSON.stringify(envelope));
|
|
60815
|
+
}
|
|
60816
|
+
function envelopeClampedBytes(event) {
|
|
60817
|
+
let bytes = serializeEnvelope(event);
|
|
60818
|
+
if (bytes.byteLength <= CLAMP_LIMIT_BYTES) return { bytes, clamped: false };
|
|
60819
|
+
const data = event.data;
|
|
60820
|
+
const clampedEvent = {
|
|
60821
|
+
...event,
|
|
60822
|
+
data: {
|
|
60823
|
+
summary: `Payload exceeded the ${CLAMP_LIMIT_BYTES}-byte wire clamp and was truncated`,
|
|
60824
|
+
subject: data && typeof data === "object" && "subject" in data ? data.subject : null
|
|
60825
|
+
},
|
|
60826
|
+
truncated: true
|
|
60827
|
+
};
|
|
60828
|
+
bytes = serializeEnvelope(clampedEvent);
|
|
60829
|
+
return { bytes, clamped: true };
|
|
60830
|
+
}
|
|
60831
|
+
async function deliverEventEnvelope(replay, event, deps2 = {}) {
|
|
60832
|
+
const now = deps2.now ?? (() => Date.now());
|
|
60833
|
+
const sleep3 = deps2.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
60834
|
+
const fetcher = deps2.fetch ?? ((url, init) => ssrfFetch(url, init));
|
|
60835
|
+
const { bytes: payloadBytes } = envelopeClampedBytes(event);
|
|
60836
|
+
const delivery = { attempts: 0 };
|
|
60837
|
+
for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
|
|
60838
|
+
if (attempt > 0) await sleep3(deliveryBackoffDelay(attempt));
|
|
60839
|
+
delivery.attempts += 1;
|
|
60840
|
+
delivery.lastAt = new Date(now()).toISOString();
|
|
60841
|
+
const timestamp = Math.floor(now() / 1e3);
|
|
60842
|
+
let signed;
|
|
60843
|
+
try {
|
|
60844
|
+
signed = signWebhook({ msgId: event.eventId, timestamp, payload: payloadBytes, secrets: replay.secrets });
|
|
60845
|
+
} catch (err) {
|
|
60846
|
+
delivery.lastError = `signing failed: ${err.message}`;
|
|
60847
|
+
return { ok: false, reason: "invalid_secret", delivery };
|
|
60848
|
+
}
|
|
60849
|
+
let response;
|
|
60850
|
+
try {
|
|
60851
|
+
response = await fetcher(replay.callbackUrl, {
|
|
60852
|
+
method: "POST",
|
|
60853
|
+
headers: { ...signed, "content-type": "application/json" },
|
|
60854
|
+
body: payloadBytes,
|
|
60855
|
+
timeoutMs: 15e3
|
|
60856
|
+
// delivery default (challenge uses 10_000)
|
|
60857
|
+
});
|
|
60858
|
+
} catch (err) {
|
|
60859
|
+
delivery.lastError = err instanceof Error ? err.message : String(err);
|
|
60860
|
+
if (attempt < MAX_ATTEMPTS - 1) continue;
|
|
60861
|
+
return { ok: false, reason: "network", delivery };
|
|
60862
|
+
}
|
|
60863
|
+
if (response.status >= 200 && response.status <= 299) {
|
|
60864
|
+
return { ok: true, delivery };
|
|
60865
|
+
}
|
|
60866
|
+
if (TERMINAL_NO_RETRY_STATUSES.has(response.status)) {
|
|
60867
|
+
delivery.lastError = `terminal status ${response.status}`;
|
|
60868
|
+
return { ok: false, reason: `http_${response.status}`, delivery };
|
|
60869
|
+
}
|
|
60870
|
+
delivery.lastError = `retryable status ${response.status}`;
|
|
60871
|
+
}
|
|
60872
|
+
return { ok: false, reason: "retries_exhausted", delivery };
|
|
60873
|
+
}
|
|
60119
60874
|
async function hasNamedLlmEndpoints() {
|
|
60120
60875
|
const override = process.env.LLM_ENDPOINT_ENDPOINTS_FILE?.trim();
|
|
60121
60876
|
const path = override && override.length > 0 ? override : join(homedir(), ".agentproto", "llm-endpoints.json");
|
|
@@ -60376,13 +61131,13 @@ function createJoinTokenRegistry(deps2) {
|
|
|
60376
61131
|
} catch (err) {
|
|
60377
61132
|
if (signal.aborted) break;
|
|
60378
61133
|
log(`[join-tokens] "${record2.name}": dial failed: ${errMsg4(err)}`);
|
|
60379
|
-
await
|
|
61134
|
+
await sleep2(backoff, signal);
|
|
60380
61135
|
backoff = Math.min(backoff * 2, reconnectMaxMs);
|
|
60381
61136
|
continue;
|
|
60382
61137
|
}
|
|
60383
61138
|
if (!sink.isOpen) {
|
|
60384
61139
|
if (signal.aborted) break;
|
|
60385
|
-
await
|
|
61140
|
+
await sleep2(backoff, signal);
|
|
60386
61141
|
backoff = Math.min(backoff * 2, reconnectMaxMs);
|
|
60387
61142
|
continue;
|
|
60388
61143
|
}
|
|
@@ -60411,12 +61166,17 @@ function createJoinTokenRegistry(deps2) {
|
|
|
60411
61166
|
capturedSession = result.session;
|
|
60412
61167
|
return { reply: encodePairingMessage(result.reply), keys: result.session };
|
|
60413
61168
|
},
|
|
60414
|
-
{
|
|
61169
|
+
{
|
|
61170
|
+
timeoutMs: handshakeTimeoutMs,
|
|
61171
|
+
// Post-handshake close reason (BOOTSTRAP P4 item 2) — same
|
|
61172
|
+
// diagnostic hook the pairing-host accept-loop wires.
|
|
61173
|
+
log: (line) => log(`[join-tokens] "${record2.name}": ${line}`)
|
|
61174
|
+
}
|
|
60415
61175
|
);
|
|
60416
61176
|
} catch (err) {
|
|
60417
61177
|
if (signal.aborted) break;
|
|
60418
61178
|
log(`[join-tokens] "${record2.name}": handshake failed: ${errMsg4(err)}`);
|
|
60419
|
-
await
|
|
61179
|
+
await sleep2(backoff, signal);
|
|
60420
61180
|
backoff = Math.min(backoff * 2, reconnectMaxMs);
|
|
60421
61181
|
continue;
|
|
60422
61182
|
}
|
|
@@ -60442,7 +61202,7 @@ function createJoinTokenRegistry(deps2) {
|
|
|
60442
61202
|
}
|
|
60443
61203
|
return { create, list, revoke, startAutoconnect, shutdown };
|
|
60444
61204
|
}
|
|
60445
|
-
function
|
|
61205
|
+
function sleep2(ms, signal) {
|
|
60446
61206
|
return new Promise((resolve41) => {
|
|
60447
61207
|
if (signal.aborted) return resolve41();
|
|
60448
61208
|
const timer = setTimeout(resolve41, ms);
|
|
@@ -61178,6 +61938,7 @@ async function createGateway(opts) {
|
|
|
61178
61938
|
registerPairingTools(server, { registry: opts.pairingRegistry });
|
|
61179
61939
|
registerDeviceTools(server, {
|
|
61180
61940
|
registry: createDeviceRegistry(opts.pairingRegistry, opts.hostRegistry),
|
|
61941
|
+
...opts.hostRegistry ? { hosts: opts.hostRegistry } : {},
|
|
61181
61942
|
...opts.joinTokens ? { joinTokens: opts.joinTokens } : {}
|
|
61182
61943
|
});
|
|
61183
61944
|
}
|
|
@@ -61804,6 +62565,6 @@ var export_readPairingsSnapshot = pairing_registry_exports.readPairingsSnapshot;
|
|
|
61804
62565
|
var export_removeProviderKey = providers_store_exports.removeProviderKey;
|
|
61805
62566
|
var export_setProviderKey = providers_store_exports.setProviderKey;
|
|
61806
62567
|
|
|
61807
|
-
export { AnthropicRemainingQuotaReader, AuthResolutionError, BUCKETS_ROOT, CLAUDE_CODE_OAUTH_SOURCE, CONTINUE_PROMPT_SOURCE, DEAD_SESSION_STATUSES, DEFAULT_ALWAYS_ON_TOOLS, DEFAULT_BUCKET, DEFAULT_CONTINUE_PROMPT, DEFAULT_HOST_LOAD_BUDGET_MS, DEFAULT_HOST_LOAD_THRESHOLDS, DEFAULT_MODEL_ROLES, DEFAULT_ORCHESTRATOR_TOOLS, DEFAULT_WORKTREE_ISOLATION, DEFAULT_WORKTREE_PROVISION_CONCURRENCY, GC_REAPABLE_STATES, HOSTS_VERSION, HOST_HANDSHAKE_REMEDIATION_HINT, HOST_SCOPE_HEADER, HarnessPresetValidationError, INBOUND_PROVIDERS, JOIN_TOKENS_VERSION, LEGACY_SESSIONS_FILE, MAX_AUTO_CONTINUE_ATTEMPTS, MAX_MESSAGE_DATA_BYTES, MCP_APP_BRIDGE_SCRIPT, MESSAGE_KINDS, MESSAGE_PREAMBLE, MESSAGE_TAG, MESSAGE_URGENCIES, MODEL_ROLE_REF_PREFIX, export_PAIRINGS_VERSION as PAIRINGS_VERSION, POSTURE_NATIVE_ALIASES, POSTURE_PREAMBLES, export_PROVIDER_ENV_VARS as PROVIDER_ENV_VARS, PrLookupError, ResumeDisabledError, SESSION_END_REASONS, SESSION_ID_ENV, SIGN_NAMESPACE, SubscriptionSourceError, TASK_STATUSES, WORKSPACE_SLUG_ENV, WORKTREE_ISOLATION_ENV, WORKTREE_PROVISION_CONCURRENCY_ENV, activityCounts, addHarnessPreset, allowedSignersLine, appendConversationRecord, attachSandbox, bucketDir, bucketSessionsFile, bucketTranscriptDir, buildCatalogModels, buildCatalogProviderModels, buildHostLoadReport, buildMcpConfigSnippet, buildRouteAwareLaunchConfig, canonicalForModeId, claudeProjectSlug, collectGcCandidates, collectHostSample, compactOutcome, composeMode, composeSessionObservers, composedFromRangeSha, computeHostWarnings, continueInterruptedSessions, continueSkipReason, conversationIndexPath, createActivityProjector, createDaemonReviewerHost, createDefaultHostProbes, createDeviceRegistry, createFileStepCache, createGateway, createHostLoadService, createHostRegistry, createInboundEndpointStore, createJoinTokenRegistry, createOrchestratorInjector, createOrchestratorMcpServerFactory, export_createPairingRegistry as createPairingRegistry, createPrProvenanceReconciler, createProcessStatsService, createReviewLaneExecutor, createReviewLedger, createReviewPackLoader, createReviewRunner, createScopeTokenRegistry, createSessionMessage, createSupervisorTaskGateRunner, createTaskLedger, createWorkspaceBrainSubscriber, createWorkspaceBrains, createWorkspaceFs, credentialFingerprint, daemonRegistryDir, decideWorktreeIsolation, declaredPresetToProviderPreset, decomposeMode, defaultBrowserAdapterIds, defaultBrowserAdapterResolution, defaultReviewKeysDir, defaultReviewLedgerRoot, defaultReviewPackCacheDir, defineBraveSearchHttpDriver, defineSerperHttpDriver, deleteUserPreset, deriveSessionOutcome, deriveSessionUsage, describeOwner, enrichRollupWithProviderQuota, enrichWithRemainingQuota, ensureReviewSigningKey, escapeHumanPrompt, escapeMessageBody, evaluateCostBudget, execGh, extractHelpSection, fetchPrStatus, fileConversationStore, fileExists3 as fileExists, filterActivities, findComposeCandidate, findConversationRecord, findNativeMode, findPrForCommit, formatToolCall, formatToolResult, getDefaultHarnessPreset, getHarnessPreset, getHostLoadService, getMcpCredentialDeps, getProcessStatsService, export_getProviderKey as getProviderKey, getToolHelp, getUserPreset, githubRepoOf, harnessPresetsPath, hostPlaceholders, injectMcpAppBridge, export_injectProviderKeysIntoEnv as injectProviderKeysIntoEnv, isAgentCliAuthConfigured, isClosedTaskStatus, isKnownSessionEndReason, isMessageAllowed, isProviderLimitError, isSafeBucketSlug, isTerminalActivityState, listBuckets, listHarnessPresets, listModelRoles, listPresets, listToolHelpTopics, listUserPresets, loadHarnessPresets, export_loadProviders as loadProviders, loadQuotaStore, loadUserPresets, loadWorkspaceModelRoles, loadWorktreeIsolation, locateConversationByNativePath, locateConversationBySessionId, makeBrowserAdapterLister, makeSandboxCredsStore, makeSandboxResolver, matchesMessageFilter, measureTool, measureToolList, messageFrom, migrateLegacySessionsFile, migrationMarkerPath, mintMessageId, monitorPolicyWait, monitorSessionWait, narrowOrchestratorTools, normalizeCommand, normalizeInbound, normalizeModeId, normalizeRemote, normalizeSkillsOption, normalizeWorktreeField, parseAnthropicRateLimitHeaders, parseDuration, parseModelRoleRef, parsePrUrl, parseTaskStatus, parseWindow, parseWorktreeIsolationMode, pickUsageDetail, policyToActivities, policyWatchesSession, prHeadSha, prToActivities, projectBrowserTools, projectSessionUsage, export_providerEnvVar as providerEnvVar, export_providersPath as providersPath, readConversationIndex, readDaemonRegistry, readHostsSnapshot, export_readPairingsSnapshot as readPairingsSnapshot, readProfileQuota, readRegisteredSlugs, readRuntimeMeta, readSandboxLedger, readSessionForBrain, readUsageSnapshots, reapGcEntry, reapOrphanedDescendants, reconcileSandboxLedger, recordProfileQuota, recordSandboxBoot, recordSandboxLiveness, recordSandboxOrigin, recordSandboxState, registerBrainTools, registerBuiltinRoutes, registerDeviceTools, registerPairingTools, registerReviewTools, registerSandboxAttachTool, registerWebSearchTools, removeHarnessPreset, export_removeProviderKey as removeProviderKey, removeSandboxLedgerEntry, renderInboxDigest, renderSessionMessage, renderSessionMessages, repoSlug, resolveAuthSpec, resolveBucketSlug, resolveDeferredToolsGatewayOption, resolveEffectiveLlmEndpointFlag, resolveModelRole, resolveNativeLink, resolvePosture, resolvePrincipal, resolveRelation, resolveReuseFromLedger, resolveReviewerPreset, resolveSpawnDefaults, resolveSubscriptionCredential, rollupSessionSubtree, rollupUsage, runContinueOnBootPass, runCrashDetectPass, runEagerResumePass, runIdleReapPass, runShellLane, runStallWatchdogPass, sandboxLedgerPath, sanitizeLabel, saveUserPreset, setDefaultPreset, setMcpCredentialDeps, export_setProviderKey as setProviderKey, signAttestation, sweepStaleDaemonRegistry, sweepStaleRuntimeMetas, toWorktreeStatusView, trackWorktreeProvision, turnToActivities, unlinkDaemonRegistryEntry, unlinkRuntimeMeta, updateHarnessPreset, upsertSandboxLedger, userPresetsPath, verifyInboundSignature, verifySignedAttestation, webSearchTool, withPr, withPrStatus, workflowToActivities, writeDaemonRegistryEntry };
|
|
62568
|
+
export { AnthropicRemainingQuotaReader, AuthResolutionError, BUCKETS_ROOT, CLAMP_LIMIT_BYTES, CLAUDE_CODE_OAUTH_SOURCE, CONTINUE_PROMPT_SOURCE, DEAD_SESSION_STATUSES, DEFAULT_ALWAYS_ON_TOOLS, DEFAULT_BUCKET, DEFAULT_CONTINUE_PROMPT, DEFAULT_HOST_LOAD_BUDGET_MS, DEFAULT_HOST_LOAD_THRESHOLDS, DEFAULT_MODEL_ROLES, DEFAULT_ORCHESTRATOR_TOOLS, DEFAULT_WORKTREE_ISOLATION, DEFAULT_WORKTREE_PROVISION_CONCURRENCY, DEVICE_PROMPT_PATH_PREFIX, GC_REAPABLE_STATES, HOSTS_VERSION, HOST_HANDSHAKE_REMEDIATION_HINT, HOST_SCOPE_HEADER, HarnessPresetValidationError, INBOUND_PROVIDERS, JOIN_TOKENS_VERSION, LEGACY_SESSIONS_FILE, MAX_ATTEMPTS, MAX_AUTO_CONTINUE_ATTEMPTS, MAX_MESSAGE_DATA_BYTES, MCP_APP_BRIDGE_SCRIPT, MESSAGE_KINDS, MESSAGE_PREAMBLE, MESSAGE_TAG, MESSAGE_URGENCIES, MODEL_ROLE_REF_PREFIX, export_PAIRINGS_VERSION as PAIRINGS_VERSION, POSTURE_NATIVE_ALIASES, POSTURE_PREAMBLES, export_PROVIDER_ENV_VARS as PROVIDER_ENV_VARS, PrLookupError, ResumeDisabledError, SESSION_END_REASONS, SESSION_ID_ENV, SIGN_NAMESPACE, SsrfFetchError, SubscriptionSourceError, TASK_STATUSES, WORKSPACE_SLUG_ENV, WORKTREE_ISOLATION_ENV, WORKTREE_PROVISION_CONCURRENCY_ENV, activityCounts, addHarnessPreset, allowedSignersLine, appendConversationRecord, attachSandbox, bucketDir, bucketSessionsFile, bucketTranscriptDir, buildCatalogModels, buildCatalogProviderModels, buildHostLoadReport, buildMcpConfigSnippet, buildRouteAwareLaunchConfig, canonicalForModeId, challengeCacheSize, challengeReasonForSsrf, claudeProjectSlug, collectGcCandidates, collectHostSample, compactOutcome, composeMode, composeSessionObservers, composedFromRangeSha, computeHostWarnings, continueInterruptedSessions, continueSkipReason, conversationIndexPath, createActivityProjector, createDaemonReviewerHost, createDefaultHostProbes, createDeviceRegistry, createFileStepCache, createGateway, createHostLoadService, createHostRegistry, createInboundEndpointStore, createJoinTokenRegistry, createOrchestratorInjector, createOrchestratorMcpServerFactory, export_createPairingRegistry as createPairingRegistry, createPrProvenanceReconciler, createProcessStatsService, createReviewLaneExecutor, createReviewLedger, createReviewPackLoader, createReviewRunner, createScopeTokenRegistry, createSessionMessage, createSupervisorTaskGateRunner, createTaskLedger, createWorkspaceBrainSubscriber, createWorkspaceBrains, createWorkspaceFs, credentialFingerprint, daemonRegistryDir, decideWorktreeIsolation, declaredPresetToProviderPreset, decodeWhsecSecret, decomposeMode, defaultBrowserAdapterIds, defaultBrowserAdapterResolution, defaultReviewKeysDir, defaultReviewLedgerRoot, defaultReviewPackCacheDir, defineBraveSearchHttpDriver, defineSerperHttpDriver, deleteUserPreset, deliverEventEnvelope, deliveryBackoffDelay, deriveSessionOutcome, deriveSessionUsage, describeOwner, encodeWhsecSecret, enrichRollupWithProviderQuota, enrichWithRemainingQuota, ensureReviewSigningKey, envelopeClampedBytes, escapeHumanPrompt, escapeMessageBody, evaluateCostBudget, execGh, extractHelpSection, fetchPrStatus, fileConversationStore, fileExists3 as fileExists, filterActivities, findComposeCandidate, findConversationRecord, findNativeMode, findPrForCommit, formatToolCall, formatToolResult, getDefaultHarnessPreset, getHarnessPreset, getHostLoadService, getMcpCredentialDeps, getProcessStatsService, export_getProviderKey as getProviderKey, getToolHelp, getUserPreset, githubRepoOf, harnessPresetsPath, hostPlaceholders, injectMcpAppBridge, export_injectProviderKeysIntoEnv as injectProviderKeysIntoEnv, isAgentCliAuthConfigured, isClosedTaskStatus, isKnownSessionEndReason, isMessageAllowed, isProviderLimitError, isPubliclyRoutable, isSafeBucketSlug, isTerminalActivityState, listBuckets, listHarnessPresets, listModelRoles, listPresets, listToolHelpTopics, listUserPresets, loadHarnessPresets, export_loadProviders as loadProviders, loadQuotaStore, loadUserPresets, loadWorkspaceModelRoles, loadWorktreeIsolation, locateConversationByNativePath, locateConversationBySessionId, makeBrowserAdapterLister, makeSandboxCredsStore, makeSandboxResolver, matchesMessageFilter, measureTool, measureToolList, messageFrom, migrateLegacySessionsFile, migrationMarkerPath, mintMessageId, monitorPolicyWait, monitorSessionWait, narrowOrchestratorTools, normalizeCallbackUrl, normalizeCommand, normalizeInbound, normalizeModeId, normalizeRemote, normalizeSkillsOption, normalizeWorktreeField, parseAnthropicRateLimitHeaders, parseDuration, parseModelRoleRef, parsePrUrl, parseTaskStatus, parseWindow, parseWorktreeIsolationMode, pickUsageDetail, policyToActivities, policyWatchesSession, prHeadSha, prToActivities, projectBrowserTools, projectSessionUsage, promptHostSession, export_providerEnvVar as providerEnvVar, export_providersPath as providersPath, readConversationIndex, readDaemonRegistry, readHostsSnapshot, export_readPairingsSnapshot as readPairingsSnapshot, readProfileQuota, readRegisteredSlugs, readRuntimeMeta, readSandboxLedger, readSessionForBrain, readUsageSnapshots, reapGcEntry, reapOrphanedDescendants, reconcileSandboxLedger, recordProfileQuota, recordSandboxBoot, recordSandboxLiveness, recordSandboxOrigin, recordSandboxState, registerBrainTools, registerBuiltinRoutes, registerDeviceTools, registerPairingTools, registerReviewTools, registerSandboxAttachTool, registerWebSearchTools, removeHarnessPreset, export_removeProviderKey as removeProviderKey, removeSandboxLedgerEntry, renderInboxDigest, renderSessionMessage, renderSessionMessages, repoSlug, resetChallengeCacheForTests, resolveAuthSpec, resolveBucketSlug, resolveDeferredToolsGatewayOption, resolveEffectiveLlmEndpointFlag, resolveModelRole, resolveNativeLink, resolvePosture, resolvePrincipal, resolveRelation, resolveReuseFromLedger, resolveReviewerPreset, resolveSpawnDefaults, resolveSubscriptionCredential, rollupSessionSubtree, rollupUsage, runContinueOnBootPass, runCrashDetectPass, runEagerResumePass, runIdleReapPass, runShellLane, runStallWatchdogPass, sandboxLedgerPath, sanitizeLabel, saveUserPreset, serializeEnvelope, setDefaultPreset, setEgressDispatcherForTests, setMcpCredentialDeps, export_setProviderKey as setProviderKey, signAttestation, signWebhook, ssrfFetch, sweepStaleDaemonRegistry, sweepStaleRuntimeMetas, toWorktreeStatusView, trackWorktreeProvision, turnToActivities, unlinkDaemonRegistryEntry, unlinkRuntimeMeta, updateHarnessPreset, upsertSandboxLedger, userPresetsPath, verifyCallback, verifyInboundSignature, verifySignedAttestation, webSearchTool, withPr, withPrStatus, workflowToActivities, writeDaemonRegistryEntry };
|
|
61808
62569
|
//# sourceMappingURL=index.mjs.map
|
|
61809
62570
|
//# sourceMappingURL=index.mjs.map
|