@opengeni/runtime 0.7.1 → 0.13.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.
Files changed (61) hide show
  1. package/THIRD_PARTY_NOTICES +32 -0
  2. package/dist/agent-UMUIWWD2.js +8 -0
  3. package/dist/agent-UMUIWWD2.js.map +1 -0
  4. package/dist/chunk-4YJ5Z52Z.js +1789 -0
  5. package/dist/chunk-4YJ5Z52Z.js.map +1 -0
  6. package/dist/chunk-65UDUFUO.js +22133 -0
  7. package/dist/chunk-65UDUFUO.js.map +1 -0
  8. package/dist/chunk-DPULLAJG.js +215 -0
  9. package/dist/chunk-DPULLAJG.js.map +1 -0
  10. package/dist/chunk-HAN5D27J.js +292 -0
  11. package/dist/chunk-HAN5D27J.js.map +1 -0
  12. package/dist/chunk-K4V4QYCL.js +37683 -0
  13. package/dist/chunk-K4V4QYCL.js.map +1 -0
  14. package/dist/chunk-PZ5AY32C.js +10 -0
  15. package/dist/chunk-PZ5AY32C.js.map +1 -0
  16. package/dist/{index-DIiiwQY2.d.ts → index-DGUrY_yR.d.ts} +171 -18
  17. package/dist/index.d.ts +404 -125
  18. package/dist/index.js +8857 -412
  19. package/dist/index.js.map +1 -1
  20. package/dist/localSources-T3HZ6KY4.js +15 -0
  21. package/dist/localSources-T3HZ6KY4.js.map +1 -0
  22. package/dist/mcp-network.d.ts +78 -0
  23. package/dist/mcp-network.js +62 -0
  24. package/dist/mcp-network.js.map +1 -0
  25. package/dist/sandbox/index.d.ts +1 -1
  26. package/dist/sandbox/index.js +42 -5
  27. package/dist/skill-library.d.ts +74 -0
  28. package/dist/skill-library.js +18 -0
  29. package/dist/skill-library.js.map +1 -0
  30. package/package.json +18 -5
  31. package/src/bundled_hashicorp_terraform_skills/README.md +3 -3
  32. package/src/bundled_hashicorp_terraform_skills/checkov/SKILL.md +1 -1
  33. package/src/codex-tool-search.ts +20 -1
  34. package/src/context-compaction.ts +421 -24
  35. package/src/history-sanitizer.ts +14 -5
  36. package/src/index.ts +1806 -347
  37. package/src/mcp-network.ts +330 -0
  38. package/src/sandbox/channel-a.ts +526 -113
  39. package/src/sandbox/display-stack.ts +860 -23
  40. package/src/sandbox/exec-banner.ts +67 -0
  41. package/src/sandbox/index.ts +43 -2
  42. package/src/sandbox/providers/modal.ts +29 -2
  43. package/src/sandbox/routing/routing-session.ts +10 -2
  44. package/src/sandbox/run-credentials.ts +818 -0
  45. package/src/sandbox/select.ts +3 -3
  46. package/src/sandbox/selfhosted/capabilities.ts +1 -1
  47. package/src/sandbox/selfhosted/op-stream.ts +40 -0
  48. package/src/sandbox/selfhosted/op-testing.ts +50 -3
  49. package/src/sandbox/selfhosted/session.ts +17 -4
  50. package/src/sandbox/selfhosted/testing.ts +1 -1
  51. package/src/sandbox/stream-port.ts +1 -1
  52. package/src/sandbox/stream-token.ts +1 -1
  53. package/src/sandbox/terminal-server.ts +6 -2
  54. package/src/sandbox/toolspace-token.ts +147 -0
  55. package/src/sandbox/turn-tool-cancellation.ts +940 -0
  56. package/src/sandbox-computer.ts +3 -1
  57. package/src/skill-library.ts +323 -0
  58. package/src/usage-telemetry.ts +495 -51
  59. package/dist/chunk-RYDCVTMW.js +0 -5531
  60. package/dist/chunk-RYDCVTMW.js.map +0 -1
  61. /package/src/{bundled_hashicorp_terraform_skills → bundled_skill_library}/azure-verified-modules/SKILL.md +0 -0
@@ -0,0 +1,215 @@
1
+ // src/skill-library.ts
2
+ import { createHash } from "crypto";
3
+ import { lstatSync, readFileSync, readdirSync } from "fs";
4
+ import { dirname, isAbsolute, join, relative } from "path";
5
+ import { TextDecoder } from "util";
6
+ import { fileURLToPath } from "url";
7
+ var skillLibraryEntries = Object.freeze([
8
+ Object.freeze({
9
+ id: "azure-verified-modules",
10
+ name: "azure-verified-modules",
11
+ version: "1.0.0",
12
+ description: "Azure Verified Modules (AVM) requirements and best practices for certified Terraform modules.",
13
+ category: "infrastructure",
14
+ tags: Object.freeze(["skill", "infrastructure", "terraform", "azure", "opt-in"]),
15
+ contentSha256: "bbc029412fd4893c35cf2a4df6e052efa5583d57d3c26e35d62869dcf4625699",
16
+ sourceCommit: "de4323afdfbc30d1387f287b55062fa8d82b62e8",
17
+ sourceUrl: "https://github.com/hashicorp/agent-skills/tree/de4323afdfbc30d1387f287b55062fa8d82b62e8/terraform/code-generation/skills/azure-verified-modules",
18
+ provenance: "Vendored from hashicorp/agent-skills; reviewed OpenGeni curated entry.",
19
+ license: "MPL-2.0",
20
+ documentationUrl: "https://azure.github.io/Azure-Verified-Modules/",
21
+ compatibility: Object.freeze({
22
+ runtime: "openai-agents-skills",
23
+ minimumSkillCapabilityVersion: "0.13.3"
24
+ }),
25
+ upgrade: Object.freeze({
26
+ policy: "immutable-replacement",
27
+ supersedes: null
28
+ }),
29
+ relativePath: "azure-verified-modules"
30
+ })
31
+ ]);
32
+ var skillLibraryRootCandidates = () => {
33
+ const moduleDir = dirname(fileURLToPath(import.meta.url));
34
+ return [
35
+ join(moduleDir, "bundled_skill_library"),
36
+ join(moduleDir, "..", "src", "bundled_skill_library")
37
+ ];
38
+ };
39
+ function skillLibraryRoot() {
40
+ return skillLibraryRootCandidates().find((candidate) => isRealDirectory(candidate)) ?? null;
41
+ }
42
+ function entryDirectory(entry) {
43
+ const root = skillLibraryRoot();
44
+ if (!root) return null;
45
+ const directory = join(root, entry.relativePath);
46
+ const withinRoot = relative(root, directory);
47
+ if (isAbsolute(withinRoot) || withinRoot === ".." || withinRoot.startsWith("../") || !isRealDirectory(directory)) {
48
+ return null;
49
+ }
50
+ return directory;
51
+ }
52
+ function listSkillLibraryEntries() {
53
+ return skillLibraryEntries.filter((entry) => reviewedArtifactIsAvailable(entry));
54
+ }
55
+ function isSkillLibraryEntryId(id) {
56
+ return skillLibraryEntries.some((entry) => entry.id === id);
57
+ }
58
+ function getSkillLibraryEntry(id, version) {
59
+ const entry = skillLibraryEntries.find(
60
+ (candidate) => candidate.id === id && (version === void 0 || candidate.version === version)
61
+ );
62
+ return entry && reviewedArtifactIsAvailable(entry) ? entry : null;
63
+ }
64
+ function loadSkillLibrarySkill(id, version) {
65
+ const entry = getSkillLibraryEntry(id, version);
66
+ if (!entry) {
67
+ throw new Error(
68
+ version ? `Skill library entry is unavailable: ${id}@${version}` : `Skill library entry is unavailable: ${id}`
69
+ );
70
+ }
71
+ const directory = entryDirectory(entry);
72
+ if (!directory) {
73
+ throw new Error(`Skill library entry is unavailable: ${entry.id}@${entry.version}`);
74
+ }
75
+ const artifact = verifySkillLibraryArtifact(directory, entry.contentSha256, entry);
76
+ const files = artifact.files;
77
+ const skillMarkdown = files.find((file) => file.path === "SKILL.md")?.content;
78
+ if (skillMarkdown === void 0) {
79
+ throw new Error(`Skill library entry is missing SKILL.md: ${entry.id}@${entry.version}`);
80
+ }
81
+ return {
82
+ entry,
83
+ skill: Object.freeze({
84
+ name: entry.name,
85
+ description: entry.description,
86
+ files: Object.freeze(files.map((file) => Object.freeze(file)))
87
+ })
88
+ };
89
+ }
90
+ function readSkillLibraryArtifact(root) {
91
+ const files = materializeSkillLibraryFiles(root);
92
+ const contentSha256 = skillLibraryArtifactSha256(files);
93
+ return Object.freeze({
94
+ files: Object.freeze(
95
+ files.map(
96
+ (file) => Object.freeze({
97
+ path: file.path,
98
+ content: decodeSkillLibraryFile(file.bytes, file.path)
99
+ })
100
+ )
101
+ ),
102
+ contentSha256
103
+ });
104
+ }
105
+ function verifySkillLibraryArtifact(root, expectedSha256, entry) {
106
+ const artifact = readSkillLibraryArtifact(root);
107
+ if (artifact.files.every((file) => file.path !== "SKILL.md")) {
108
+ throw new Error(
109
+ entry ? `Skill library entry is missing SKILL.md: ${entry.id}@${entry.version}` : "Skill library artifact is missing SKILL.md"
110
+ );
111
+ }
112
+ if (artifact.contentSha256 !== expectedSha256) {
113
+ const label = entry ? ` for ${entry.id}@${entry.version}` : "";
114
+ throw new Error(
115
+ `Skill library artifact hash mismatch${label}: expected ${expectedSha256}, got ${artifact.contentSha256}`
116
+ );
117
+ }
118
+ return artifact;
119
+ }
120
+ var utf8Decoder = new TextDecoder("utf-8", { fatal: true });
121
+ function materializeSkillLibraryFiles(root, current = "") {
122
+ const directory = current ? join(root, current) : root;
123
+ if (!isRealDirectory(directory)) {
124
+ throw new Error(`Skill library artifact root is not a real directory: ${directory}`);
125
+ }
126
+ return readdirSync(directory, { withFileTypes: true }).sort((a, b) => compareCanonicalPath(a.name, b.name)).flatMap((child) => {
127
+ const childName = normalizeSkillLibraryRelativePath(child.name, true);
128
+ const path = current ? `${current}/${childName}` : childName;
129
+ normalizeSkillLibraryRelativePath(path);
130
+ const childPath = join(root, path);
131
+ if (child.isSymbolicLink()) {
132
+ throw new Error(`Skill library artifact contains a symbolic link: ${path}`);
133
+ }
134
+ if (child.isDirectory()) {
135
+ return materializeSkillLibraryFiles(root, path);
136
+ }
137
+ if (!child.isFile() || !isRealFile(childPath)) {
138
+ throw new Error(`Skill library artifact contains a non-regular file: ${path}`);
139
+ }
140
+ return [{ path, bytes: Uint8Array.from(readFileSync(childPath)) }];
141
+ });
142
+ }
143
+ function reviewedArtifactIsAvailable(entry) {
144
+ const directory = entryDirectory(entry);
145
+ if (!directory) return false;
146
+ try {
147
+ verifySkillLibraryArtifact(directory, entry.contentSha256, entry);
148
+ return true;
149
+ } catch {
150
+ return false;
151
+ }
152
+ }
153
+ function skillLibraryArtifactSha256(files) {
154
+ const manifest = files.map((file) => {
155
+ const path = normalizeSkillLibraryRelativePath(file.path);
156
+ return [path, Buffer.from(file.bytes).toString("base64")];
157
+ }).sort((left, right) => compareCanonicalPath(left[0], right[0]));
158
+ const paths = /* @__PURE__ */ new Set();
159
+ for (const [path] of manifest) {
160
+ if (paths.has(path)) {
161
+ throw new Error(`Skill library artifact contains duplicate file path: ${path}`);
162
+ }
163
+ paths.add(path);
164
+ }
165
+ return createHash("sha256").update(JSON.stringify(manifest), "utf8").digest("hex");
166
+ }
167
+ function decodeSkillLibraryFile(bytes, path) {
168
+ try {
169
+ return utf8Decoder.decode(bytes);
170
+ } catch {
171
+ throw new Error(`Skill library artifact contains invalid UTF-8: ${path}`);
172
+ }
173
+ }
174
+ function normalizeSkillLibraryRelativePath(path, segment = false) {
175
+ if (path.length === 0 || path.includes("\\") || path.includes("\0") || path.startsWith("/") || /^[A-Za-z]:(?:\/|$)/u.test(path)) {
176
+ throw new Error(`Skill library artifact contains an unsafe path: ${path}`);
177
+ }
178
+ const parts = path.split("/");
179
+ if (parts.some((part) => part.length === 0 || part === "." || part === "..")) {
180
+ throw new Error(`Skill library artifact contains an unsafe path: ${path}`);
181
+ }
182
+ if (segment && parts.length !== 1) {
183
+ throw new Error(`Skill library artifact contains an unsafe path: ${path}`);
184
+ }
185
+ return parts.join("/");
186
+ }
187
+ function compareCanonicalPath(left, right) {
188
+ return left < right ? -1 : left > right ? 1 : 0;
189
+ }
190
+ function isRealDirectory(path) {
191
+ try {
192
+ const stats = lstatSync(path);
193
+ return stats.isDirectory() && !stats.isSymbolicLink();
194
+ } catch {
195
+ return false;
196
+ }
197
+ }
198
+ function isRealFile(path) {
199
+ try {
200
+ const stats = lstatSync(path);
201
+ return stats.isFile() && !stats.isSymbolicLink();
202
+ } catch {
203
+ return false;
204
+ }
205
+ }
206
+
207
+ export {
208
+ listSkillLibraryEntries,
209
+ isSkillLibraryEntryId,
210
+ getSkillLibraryEntry,
211
+ loadSkillLibrarySkill,
212
+ readSkillLibraryArtifact,
213
+ verifySkillLibraryArtifact
214
+ };
215
+ //# sourceMappingURL=chunk-DPULLAJG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/skill-library.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { lstatSync, readFileSync, readdirSync } from \"node:fs\";\nimport { dirname, isAbsolute, join, relative } from \"node:path\";\nimport { TextDecoder } from \"node:util\";\nimport { fileURLToPath } from \"node:url\";\n\n/**\n * Metadata for a platform-curated skill. The metadata is deliberately\n * provider-neutral: it describes guidance provenance and compatibility, not\n * credentials, tools, permissions, or a model route.\n *\n * Entries are immutable in the runtime. A new artifact gets a new version and\n * content hash; callers must not mutate the returned records.\n */\nexport type SkillLibraryEntry = Readonly<{\n id: string;\n name: string;\n version: string;\n description: string;\n category: string;\n tags: readonly string[];\n /** SHA-256 over the complete canonical artifact manifest, not just SKILL.md. */\n contentSha256: string;\n sourceCommit: string;\n sourceUrl: string;\n provenance: string;\n license: string;\n documentationUrl: string;\n compatibility: Readonly<{\n runtime: string;\n minimumSkillCapabilityVersion: string;\n }>;\n upgrade: Readonly<{\n policy: \"immutable-replacement\";\n supersedes: string | null;\n }>;\n relativePath: string;\n}>;\n\nexport type SkillLibraryFile = Readonly<{\n path: string;\n content: string;\n}>;\n\nexport type SkillLibraryArtifact = Readonly<{\n files: readonly SkillLibraryFile[];\n /** SHA-256 over the complete canonical artifact manifest, not just SKILL.md. */\n contentSha256: string;\n}>;\n\nexport type SkillLibrarySkill = Readonly<{\n name: string;\n description: string;\n files: readonly SkillLibraryFile[];\n}>;\n\nconst skillLibraryEntries: readonly SkillLibraryEntry[] = Object.freeze([\n Object.freeze({\n id: \"azure-verified-modules\",\n name: \"azure-verified-modules\",\n version: \"1.0.0\",\n description:\n \"Azure Verified Modules (AVM) requirements and best practices for certified Terraform modules.\",\n category: \"infrastructure\",\n tags: Object.freeze([\"skill\", \"infrastructure\", \"terraform\", \"azure\", \"opt-in\"]),\n contentSha256: \"bbc029412fd4893c35cf2a4df6e052efa5583d57d3c26e35d62869dcf4625699\",\n sourceCommit: \"de4323afdfbc30d1387f287b55062fa8d82b62e8\",\n sourceUrl:\n \"https://github.com/hashicorp/agent-skills/tree/de4323afdfbc30d1387f287b55062fa8d82b62e8/terraform/code-generation/skills/azure-verified-modules\",\n provenance: \"Vendored from hashicorp/agent-skills; reviewed OpenGeni curated entry.\",\n license: \"MPL-2.0\",\n documentationUrl: \"https://azure.github.io/Azure-Verified-Modules/\",\n compatibility: Object.freeze({\n runtime: \"openai-agents-skills\",\n minimumSkillCapabilityVersion: \"0.13.3\",\n }),\n upgrade: Object.freeze({\n policy: \"immutable-replacement\",\n supersedes: null,\n }),\n relativePath: \"azure-verified-modules\",\n }),\n]);\n\nconst skillLibraryRootCandidates = (): string[] => {\n const moduleDir = dirname(fileURLToPath(import.meta.url));\n return [\n join(moduleDir, \"bundled_skill_library\"),\n join(moduleDir, \"..\", \"src\", \"bundled_skill_library\"),\n ];\n};\n\nfunction skillLibraryRoot(): string | null {\n return skillLibraryRootCandidates().find((candidate) => isRealDirectory(candidate)) ?? null;\n}\n\nfunction entryDirectory(entry: SkillLibraryEntry): string | null {\n const root = skillLibraryRoot();\n if (!root) return null;\n const directory = join(root, entry.relativePath);\n const withinRoot = relative(root, directory);\n if (\n isAbsolute(withinRoot) ||\n withinRoot === \"..\" ||\n withinRoot.startsWith(\"../\") ||\n !isRealDirectory(directory)\n ) {\n return null;\n }\n return directory;\n}\n\n/** Return only entries whose reviewed artifact is present in this deployment. */\nexport function listSkillLibraryEntries(): readonly SkillLibraryEntry[] {\n return skillLibraryEntries.filter((entry) => reviewedArtifactIsAvailable(entry));\n}\n\n/** Return whether an id belongs to the immutable library, even if its artifact is unavailable. */\nexport function isSkillLibraryEntryId(id: string): boolean {\n return skillLibraryEntries.some((entry) => entry.id === id);\n}\n\n/** Resolve an exact immutable library entry by id and version. */\nexport function getSkillLibraryEntry(id: string, version?: string): SkillLibraryEntry | null {\n const entry = skillLibraryEntries.find(\n (candidate) => candidate.id === id && (version === undefined || candidate.version === version),\n );\n return entry && reviewedArtifactIsAvailable(entry) ? entry : null;\n}\n\n/**\n * Load a selected curated entry into the SDK's in-memory skill shape.\n * Selection is guidance-only: this function has no access to settings,\n * credentials, MCP declarations, or model-provider configuration.\n */\nexport function loadSkillLibrarySkill(\n id: string,\n version?: string,\n): { entry: SkillLibraryEntry; skill: SkillLibrarySkill } {\n const entry = getSkillLibraryEntry(id, version);\n if (!entry) {\n throw new Error(\n version\n ? `Skill library entry is unavailable: ${id}@${version}`\n : `Skill library entry is unavailable: ${id}`,\n );\n }\n const directory = entryDirectory(entry);\n if (!directory) {\n throw new Error(`Skill library entry is unavailable: ${entry.id}@${entry.version}`);\n }\n const artifact = verifySkillLibraryArtifact(directory, entry.contentSha256, entry);\n const files = artifact.files;\n const skillMarkdown = files.find((file) => file.path === \"SKILL.md\")?.content;\n if (skillMarkdown === undefined) {\n throw new Error(`Skill library entry is missing SKILL.md: ${entry.id}@${entry.version}`);\n }\n return {\n entry,\n skill: Object.freeze({\n name: entry.name,\n description: entry.description,\n files: Object.freeze(files.map((file) => Object.freeze(file))),\n }),\n };\n}\n\n/**\n * Read a curated artifact and calculate its canonical whole-artifact digest.\n *\n * The manifest is JSON encoded as sorted `[normalizedRelativePath, base64Bytes]`\n * tuples. Base64 preserves every byte, including bytes that would not survive\n * a lossy UTF-8 string round-trip. Files are decoded only after the digest\n * input has been captured because the SDK skill surface is text-based.\n */\nexport function readSkillLibraryArtifact(root: string): SkillLibraryArtifact {\n const files = materializeSkillLibraryFiles(root);\n const contentSha256 = skillLibraryArtifactSha256(files);\n return Object.freeze({\n files: Object.freeze(\n files.map((file) =>\n Object.freeze({\n path: file.path,\n content: decodeSkillLibraryFile(file.bytes, file.path),\n }),\n ),\n ),\n contentSha256,\n });\n}\n\n/** Verify a reviewed artifact against its immutable catalog digest. */\nexport function verifySkillLibraryArtifact(\n root: string,\n expectedSha256: string,\n entry?: Pick<SkillLibraryEntry, \"id\" | \"version\">,\n): SkillLibraryArtifact {\n const artifact = readSkillLibraryArtifact(root);\n if (artifact.files.every((file) => file.path !== \"SKILL.md\")) {\n throw new Error(\n entry\n ? `Skill library entry is missing SKILL.md: ${entry.id}@${entry.version}`\n : \"Skill library artifact is missing SKILL.md\",\n );\n }\n if (artifact.contentSha256 !== expectedSha256) {\n const label = entry ? ` for ${entry.id}@${entry.version}` : \"\";\n throw new Error(\n `Skill library artifact hash mismatch${label}: expected ${expectedSha256}, got ${artifact.contentSha256}`,\n );\n }\n return artifact;\n}\n\ntype MaterializedSkillLibraryFile = Readonly<{\n path: string;\n bytes: Uint8Array;\n}>;\n\nconst utf8Decoder = new TextDecoder(\"utf-8\", { fatal: true });\n\nfunction materializeSkillLibraryFiles(root: string, current = \"\"): MaterializedSkillLibraryFile[] {\n const directory = current ? join(root, current) : root;\n if (!isRealDirectory(directory)) {\n throw new Error(`Skill library artifact root is not a real directory: ${directory}`);\n }\n return readdirSync(directory, { withFileTypes: true })\n .sort((a, b) => compareCanonicalPath(a.name, b.name))\n .flatMap((child) => {\n const childName = normalizeSkillLibraryRelativePath(child.name, true);\n const path = current ? `${current}/${childName}` : childName;\n normalizeSkillLibraryRelativePath(path);\n const childPath = join(root, path);\n if (child.isSymbolicLink()) {\n throw new Error(`Skill library artifact contains a symbolic link: ${path}`);\n }\n if (child.isDirectory()) {\n return materializeSkillLibraryFiles(root, path);\n }\n if (!child.isFile() || !isRealFile(childPath)) {\n throw new Error(`Skill library artifact contains a non-regular file: ${path}`);\n }\n return [{ path, bytes: Uint8Array.from(readFileSync(childPath)) }];\n });\n}\n\nfunction reviewedArtifactIsAvailable(entry: SkillLibraryEntry): boolean {\n const directory = entryDirectory(entry);\n if (!directory) return false;\n try {\n verifySkillLibraryArtifact(directory, entry.contentSha256, entry);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction skillLibraryArtifactSha256(files: readonly MaterializedSkillLibraryFile[]): string {\n const manifest = files\n .map((file) => {\n const path = normalizeSkillLibraryRelativePath(file.path);\n return [path, Buffer.from(file.bytes).toString(\"base64\")] as const;\n })\n .sort((left, right) => compareCanonicalPath(left[0], right[0]));\n const paths = new Set<string>();\n for (const [path] of manifest) {\n if (paths.has(path)) {\n throw new Error(`Skill library artifact contains duplicate file path: ${path}`);\n }\n paths.add(path);\n }\n return createHash(\"sha256\").update(JSON.stringify(manifest), \"utf8\").digest(\"hex\");\n}\n\nfunction decodeSkillLibraryFile(bytes: Uint8Array, path: string): string {\n try {\n return utf8Decoder.decode(bytes);\n } catch {\n throw new Error(`Skill library artifact contains invalid UTF-8: ${path}`);\n }\n}\n\nfunction normalizeSkillLibraryRelativePath(path: string, segment = false): string {\n if (\n path.length === 0 ||\n path.includes(\"\\\\\") ||\n path.includes(\"\\0\") ||\n path.startsWith(\"/\") ||\n /^[A-Za-z]:(?:\\/|$)/u.test(path)\n ) {\n throw new Error(`Skill library artifact contains an unsafe path: ${path}`);\n }\n const parts = path.split(\"/\");\n if (parts.some((part) => part.length === 0 || part === \".\" || part === \"..\")) {\n throw new Error(`Skill library artifact contains an unsafe path: ${path}`);\n }\n if (segment && parts.length !== 1) {\n throw new Error(`Skill library artifact contains an unsafe path: ${path}`);\n }\n return parts.join(\"/\");\n}\n\nfunction compareCanonicalPath(left: string, right: string): number {\n return left < right ? -1 : left > right ? 1 : 0;\n}\n\nfunction isRealDirectory(path: string): boolean {\n try {\n const stats = lstatSync(path);\n return stats.isDirectory() && !stats.isSymbolicLink();\n } catch {\n return false;\n }\n}\n\nfunction isRealFile(path: string): boolean {\n try {\n const stats = lstatSync(path);\n return stats.isFile() && !stats.isSymbolicLink();\n } catch {\n return false;\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,292 @@
1
+ // src/mcp-network.ts
2
+ import {
3
+ isNonPublicAddress,
4
+ pinnedFetch,
5
+ resolvePinnedDestination
6
+ } from "@opengeni/network";
7
+ import { undiciFetch } from "@opengeni/network";
8
+ var MCP_MAX_RESPONSE_BYTES = 8 * 1024 * 1024;
9
+ var MCP_MAX_INBOUND_REQUEST_BYTES = 1024 * 1024;
10
+ var MCP_MAX_TOOL_DEFINITION_BYTES = 128 * 1024;
11
+ var MCP_MAX_TOOL_LIST_BYTES = 4 * 1024 * 1024;
12
+ var MCP_MAX_TOOL_LIST_ENTRIES = 1e3;
13
+ var MCP_MAX_TOOL_RESULT_BYTES = 1024 * 1024;
14
+ var MCP_MAX_TOOL_SEARCH_DISCLOSURE_BYTES = 256 * 1024;
15
+ var MCP_MAX_SELECTED_SERVERS = 64;
16
+ var MCP_MAX_CONCURRENT_SERVER_OPERATIONS = 8;
17
+ var MCP_MAX_AGGREGATE_TOOL_LIST_ENTRIES = 4096;
18
+ var MCP_MAX_AGGREGATE_TOOL_LIST_BYTES = 16 * 1024 * 1024;
19
+ var MCP_MAX_TOOL_SEARCH_SOURCES = 64;
20
+ var MCP_MAX_TOOL_SEARCH_SOURCE_LABEL_LENGTH = 120;
21
+ var MCP_MAX_TOOL_SEARCH_DESCRIPTION_BYTES = 16 * 1024;
22
+ var McpPayloadTooLargeError = class extends Error {
23
+ constructor(label, actualBytes, maxBytes) {
24
+ super(`${label} exceeds the ${maxBytes}-byte safety limit`);
25
+ this.label = label;
26
+ this.actualBytes = actualBytes;
27
+ this.maxBytes = maxBytes;
28
+ this.name = "McpPayloadTooLargeError";
29
+ }
30
+ };
31
+ function mcpSerializedSizeBytes(value) {
32
+ let serialized;
33
+ try {
34
+ serialized = JSON.stringify(value) ?? "null";
35
+ } catch {
36
+ throw new Error("MCP payload is not JSON serializable");
37
+ }
38
+ return Buffer.byteLength(serialized);
39
+ }
40
+ function assertMcpPayloadWithinBytes(value, maxBytes, label) {
41
+ const actualBytes = mcpSerializedSizeBytes(value);
42
+ if (actualBytes > maxBytes) {
43
+ throw new McpPayloadTooLargeError(label, actualBytes, maxBytes);
44
+ }
45
+ }
46
+ function assertMcpToolListWithinBounds(tools) {
47
+ if (tools.length > MCP_MAX_TOOL_LIST_ENTRIES) {
48
+ throw new McpPayloadTooLargeError("MCP tool list", tools.length, MCP_MAX_TOOL_LIST_ENTRIES);
49
+ }
50
+ for (const tool of tools) {
51
+ const toolBytes = mcpSerializedSizeBytes(tool);
52
+ if (toolBytes > MCP_MAX_TOOL_DEFINITION_BYTES) {
53
+ throw new McpPayloadTooLargeError(
54
+ "MCP tool definition",
55
+ toolBytes,
56
+ MCP_MAX_TOOL_DEFINITION_BYTES
57
+ );
58
+ }
59
+ }
60
+ const totalBytes = mcpSerializedSizeBytes(tools);
61
+ if (totalBytes > MCP_MAX_TOOL_LIST_BYTES) {
62
+ throw new McpPayloadTooLargeError("MCP tool list", totalBytes, MCP_MAX_TOOL_LIST_BYTES);
63
+ }
64
+ return tools;
65
+ }
66
+ function assertMcpServerSelectionWithinBounds(servers) {
67
+ if (servers.length > MCP_MAX_SELECTED_SERVERS) {
68
+ throw new McpPayloadTooLargeError(
69
+ "selected MCP server count",
70
+ servers.length,
71
+ MCP_MAX_SELECTED_SERVERS
72
+ );
73
+ }
74
+ return servers;
75
+ }
76
+ var McpAggregateToolListBudget = class {
77
+ constructor(label = "aggregate MCP tool list", maxEntries = MCP_MAX_AGGREGATE_TOOL_LIST_ENTRIES, maxBytes = MCP_MAX_AGGREGATE_TOOL_LIST_BYTES) {
78
+ this.label = label;
79
+ this.maxEntries = maxEntries;
80
+ this.maxBytes = maxBytes;
81
+ }
82
+ contributions = /* @__PURE__ */ new Map();
83
+ totalEntries = 0;
84
+ totalBytes = 0;
85
+ replace(sourceId, tools) {
86
+ assertMcpToolListWithinBounds(tools);
87
+ const contribution = {
88
+ entries: tools.length,
89
+ // Conservatively account each server as its complete serialized array.
90
+ // Summing these arrays slightly over-counts the brackets relative to one
91
+ // flattened array, which is intentional for a fail-closed hard ceiling.
92
+ bytes: mcpSerializedSizeBytes(tools)
93
+ };
94
+ const previous = this.contributions.get(sourceId) ?? { entries: 0, bytes: 0 };
95
+ const nextEntries = this.totalEntries - previous.entries + contribution.entries;
96
+ if (nextEntries > this.maxEntries) {
97
+ throw new McpPayloadTooLargeError(this.label, nextEntries, this.maxEntries);
98
+ }
99
+ const nextBytes = this.totalBytes - previous.bytes + contribution.bytes;
100
+ if (nextBytes > this.maxBytes) {
101
+ throw new McpPayloadTooLargeError(this.label, nextBytes, this.maxBytes);
102
+ }
103
+ this.contributions.set(sourceId, contribution);
104
+ this.totalEntries = nextEntries;
105
+ this.totalBytes = nextBytes;
106
+ return tools;
107
+ }
108
+ remove(sourceId) {
109
+ const previous = this.contributions.get(sourceId);
110
+ if (!previous) return;
111
+ this.contributions.delete(sourceId);
112
+ this.totalEntries -= previous.entries;
113
+ this.totalBytes -= previous.bytes;
114
+ }
115
+ snapshot() {
116
+ return { entries: this.totalEntries, bytes: this.totalBytes };
117
+ }
118
+ };
119
+ async function boundedParallelMap(values, concurrency, mapper) {
120
+ if (!Number.isInteger(concurrency) || concurrency < 1) {
121
+ throw new Error("bounded parallel map concurrency must be a positive integer");
122
+ }
123
+ if (values.length === 0) return [];
124
+ const results = new Array(values.length);
125
+ const errors = [];
126
+ let nextIndex = 0;
127
+ let stopped = false;
128
+ const worker = async () => {
129
+ while (!stopped) {
130
+ const index = nextIndex;
131
+ if (index >= values.length) return;
132
+ nextIndex += 1;
133
+ try {
134
+ results[index] = await mapper(values[index], index);
135
+ } catch (error) {
136
+ errors.push({ index, error });
137
+ stopped = true;
138
+ }
139
+ }
140
+ };
141
+ await Promise.all(Array.from({ length: Math.min(concurrency, values.length) }, () => worker()));
142
+ if (errors.length > 0) {
143
+ errors.sort((left, right) => left.index - right.index);
144
+ throw errors[0].error;
145
+ }
146
+ return results;
147
+ }
148
+ function guardedMcpFetch(settings, fetchImpl, options = {}) {
149
+ return async (input, init) => {
150
+ const response = await pinnedFetch(input, init, settings, {
151
+ fetchImpl,
152
+ ...options.dnsLookup ? { dnsLookup: options.dnsLookup } : {},
153
+ label: "MCP endpoint",
154
+ requireHttpsOutsideLocalTest: options.requireHttpsOutsideLocalTest ?? true
155
+ });
156
+ return boundMcpResponseBody(response, options.maxResponseBytes ?? MCP_MAX_RESPONSE_BYTES);
157
+ };
158
+ }
159
+ function boundMcpResponseBody(response, maxBytes) {
160
+ const declaredLength = Number(response.headers.get("content-length"));
161
+ if (Number.isFinite(declaredLength) && declaredLength > maxBytes) {
162
+ void response.body?.cancel().catch(() => void 0);
163
+ throw new McpPayloadTooLargeError("MCP response", declaredLength, maxBytes);
164
+ }
165
+ if (!response.body) {
166
+ return response;
167
+ }
168
+ const reader = response.body.getReader();
169
+ let receivedBytes = 0;
170
+ const body = new ReadableStream({
171
+ async pull(controller) {
172
+ try {
173
+ const chunk = await reader.read();
174
+ if (chunk.done) {
175
+ controller.close();
176
+ return;
177
+ }
178
+ receivedBytes += chunk.value.byteLength;
179
+ if (receivedBytes > maxBytes) {
180
+ await reader.cancel().catch(() => void 0);
181
+ controller.error(new McpPayloadTooLargeError("MCP response", receivedBytes, maxBytes));
182
+ return;
183
+ }
184
+ controller.enqueue(chunk.value);
185
+ } catch (error) {
186
+ controller.error(error);
187
+ }
188
+ },
189
+ async cancel(reason) {
190
+ await reader.cancel(reason).catch(() => void 0);
191
+ }
192
+ });
193
+ const bounded = new Response(body, {
194
+ status: response.status,
195
+ statusText: response.statusText,
196
+ headers: response.headers
197
+ });
198
+ Object.defineProperties(bounded, {
199
+ redirected: { value: response.redirected },
200
+ type: { value: response.type },
201
+ url: { value: response.url }
202
+ });
203
+ return bounded;
204
+ }
205
+ async function cancelMcpResponseBody(response) {
206
+ await response.body?.cancel().catch(() => void 0);
207
+ }
208
+ async function boundedMcpRequest(request, maxBytes = MCP_MAX_INBOUND_REQUEST_BYTES) {
209
+ const declared = request.headers.get("content-length");
210
+ if (declared !== null) {
211
+ const normalized = declared.trim();
212
+ if (!/^\d+$/.test(normalized)) {
213
+ await request.body?.cancel().catch(() => void 0);
214
+ throw new McpPayloadTooLargeError("MCP request", 0, maxBytes);
215
+ }
216
+ const declaredBytes = Number(normalized);
217
+ if (!Number.isSafeInteger(declaredBytes) || declaredBytes > maxBytes) {
218
+ await request.body?.cancel().catch(() => void 0);
219
+ throw new McpPayloadTooLargeError("MCP request", declaredBytes, maxBytes);
220
+ }
221
+ }
222
+ if (!request.body) {
223
+ return request;
224
+ }
225
+ const reader = request.body.getReader();
226
+ const chunks = [];
227
+ let receivedBytes = 0;
228
+ try {
229
+ for (; ; ) {
230
+ const result = await reader.read();
231
+ if (result.done) break;
232
+ receivedBytes += result.value.byteLength;
233
+ if (receivedBytes > maxBytes) {
234
+ await reader.cancel().catch(() => void 0);
235
+ throw new McpPayloadTooLargeError("MCP request", receivedBytes, maxBytes);
236
+ }
237
+ chunks.push(result.value);
238
+ }
239
+ } catch (error) {
240
+ await reader.cancel().catch(() => void 0);
241
+ throw error;
242
+ } finally {
243
+ reader.releaseLock();
244
+ }
245
+ const body = new Uint8Array(receivedBytes);
246
+ let offset = 0;
247
+ for (const chunk of chunks) {
248
+ body.set(chunk, offset);
249
+ offset += chunk.byteLength;
250
+ }
251
+ return new Request(request, { body, headers: request.headers });
252
+ }
253
+ async function assertMcpDestinationAllowed(rawUrl, settings, options = {}) {
254
+ await resolvePinnedDestination(rawUrl, settings, {
255
+ ...options.dnsLookup ? { dnsLookup: options.dnsLookup } : {},
256
+ label: "MCP endpoint",
257
+ requireHttpsOutsideLocalTest: true
258
+ });
259
+ }
260
+ var isNonPublicMcpAddress = isNonPublicAddress;
261
+
262
+ export {
263
+ MCP_MAX_RESPONSE_BYTES,
264
+ MCP_MAX_INBOUND_REQUEST_BYTES,
265
+ MCP_MAX_TOOL_DEFINITION_BYTES,
266
+ MCP_MAX_TOOL_LIST_BYTES,
267
+ MCP_MAX_TOOL_LIST_ENTRIES,
268
+ MCP_MAX_TOOL_RESULT_BYTES,
269
+ MCP_MAX_TOOL_SEARCH_DISCLOSURE_BYTES,
270
+ MCP_MAX_SELECTED_SERVERS,
271
+ MCP_MAX_CONCURRENT_SERVER_OPERATIONS,
272
+ MCP_MAX_AGGREGATE_TOOL_LIST_ENTRIES,
273
+ MCP_MAX_AGGREGATE_TOOL_LIST_BYTES,
274
+ MCP_MAX_TOOL_SEARCH_SOURCES,
275
+ MCP_MAX_TOOL_SEARCH_SOURCE_LABEL_LENGTH,
276
+ MCP_MAX_TOOL_SEARCH_DESCRIPTION_BYTES,
277
+ McpPayloadTooLargeError,
278
+ mcpSerializedSizeBytes,
279
+ assertMcpPayloadWithinBytes,
280
+ assertMcpToolListWithinBounds,
281
+ assertMcpServerSelectionWithinBounds,
282
+ McpAggregateToolListBudget,
283
+ boundedParallelMap,
284
+ guardedMcpFetch,
285
+ boundMcpResponseBody,
286
+ cancelMcpResponseBody,
287
+ boundedMcpRequest,
288
+ assertMcpDestinationAllowed,
289
+ isNonPublicMcpAddress,
290
+ undiciFetch
291
+ };
292
+ //# sourceMappingURL=chunk-HAN5D27J.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/mcp-network.ts"],"sourcesContent":["import type { Settings } from \"@opengeni/config\";\nimport {\n isNonPublicAddress,\n pinnedFetch,\n resolvePinnedDestination,\n type DnsLookup,\n type FetchLike,\n} from \"@opengeni/network\";\n\nexport { undiciFetch } from \"@opengeni/network\";\n\nexport const MCP_MAX_RESPONSE_BYTES = 8 * 1024 * 1024;\nexport const MCP_MAX_INBOUND_REQUEST_BYTES = 1024 * 1024;\nexport const MCP_MAX_TOOL_DEFINITION_BYTES = 128 * 1024;\nexport const MCP_MAX_TOOL_LIST_BYTES = 4 * 1024 * 1024;\nexport const MCP_MAX_TOOL_LIST_ENTRIES = 1_000;\nexport const MCP_MAX_TOOL_RESULT_BYTES = 1024 * 1024;\nexport const MCP_MAX_TOOL_SEARCH_DISCLOSURE_BYTES = 256 * 1024;\nexport const MCP_MAX_SELECTED_SERVERS = 64;\nexport const MCP_MAX_CONCURRENT_SERVER_OPERATIONS = 8;\nexport const MCP_MAX_AGGREGATE_TOOL_LIST_ENTRIES = 4_096;\nexport const MCP_MAX_AGGREGATE_TOOL_LIST_BYTES = 16 * 1024 * 1024;\nexport const MCP_MAX_TOOL_SEARCH_SOURCES = 64;\nexport const MCP_MAX_TOOL_SEARCH_SOURCE_LABEL_LENGTH = 120;\nexport const MCP_MAX_TOOL_SEARCH_DESCRIPTION_BYTES = 16 * 1024;\n\nexport class McpPayloadTooLargeError extends Error {\n constructor(\n readonly label: string,\n readonly actualBytes: number,\n readonly maxBytes: number,\n ) {\n super(`${label} exceeds the ${maxBytes}-byte safety limit`);\n this.name = \"McpPayloadTooLargeError\";\n }\n}\n\nexport function mcpSerializedSizeBytes(value: unknown): number {\n let serialized: string;\n try {\n serialized = JSON.stringify(value) ?? \"null\";\n } catch {\n throw new Error(\"MCP payload is not JSON serializable\");\n }\n return Buffer.byteLength(serialized);\n}\n\nexport function assertMcpPayloadWithinBytes(value: unknown, maxBytes: number, label: string): void {\n const actualBytes = mcpSerializedSizeBytes(value);\n if (actualBytes > maxBytes) {\n throw new McpPayloadTooLargeError(label, actualBytes, maxBytes);\n }\n}\n\nexport function assertMcpToolListWithinBounds<T>(tools: readonly T[]): readonly T[] {\n if (tools.length > MCP_MAX_TOOL_LIST_ENTRIES) {\n throw new McpPayloadTooLargeError(\"MCP tool list\", tools.length, MCP_MAX_TOOL_LIST_ENTRIES);\n }\n for (const tool of tools) {\n const toolBytes = mcpSerializedSizeBytes(tool);\n if (toolBytes > MCP_MAX_TOOL_DEFINITION_BYTES) {\n throw new McpPayloadTooLargeError(\n \"MCP tool definition\",\n toolBytes,\n MCP_MAX_TOOL_DEFINITION_BYTES,\n );\n }\n }\n // Measure the complete JSON array, including brackets and separators. Summing\n // definitions alone under-counts by one byte per separator plus the array\n // delimiters and makes the advertised serialized-byte ceiling porous.\n const totalBytes = mcpSerializedSizeBytes(tools);\n if (totalBytes > MCP_MAX_TOOL_LIST_BYTES) {\n throw new McpPayloadTooLargeError(\"MCP tool list\", totalBytes, MCP_MAX_TOOL_LIST_BYTES);\n }\n return tools;\n}\n\nexport function assertMcpServerSelectionWithinBounds<T>(servers: readonly T[]): readonly T[] {\n if (servers.length > MCP_MAX_SELECTED_SERVERS) {\n throw new McpPayloadTooLargeError(\n \"selected MCP server count\",\n servers.length,\n MCP_MAX_SELECTED_SERVERS,\n );\n }\n return servers;\n}\n\ntype McpToolListContribution = {\n entries: number;\n bytes: number;\n};\n\n/**\n * Atomic aggregate accounting shared by every exposed tools/list in one run.\n * A repeated list for the same source REPLACES its prior contribution, so\n * cache invalidation/relisting cannot ratchet the budget upward. Failed\n * replacements leave the previous accounting intact.\n */\nexport class McpAggregateToolListBudget {\n private readonly contributions = new Map<string, McpToolListContribution>();\n private totalEntries = 0;\n private totalBytes = 0;\n\n constructor(\n private readonly label = \"aggregate MCP tool list\",\n private readonly maxEntries = MCP_MAX_AGGREGATE_TOOL_LIST_ENTRIES,\n private readonly maxBytes = MCP_MAX_AGGREGATE_TOOL_LIST_BYTES,\n ) {}\n\n replace<T>(sourceId: string, tools: readonly T[]): readonly T[] {\n assertMcpToolListWithinBounds(tools);\n const contribution = {\n entries: tools.length,\n // Conservatively account each server as its complete serialized array.\n // Summing these arrays slightly over-counts the brackets relative to one\n // flattened array, which is intentional for a fail-closed hard ceiling.\n bytes: mcpSerializedSizeBytes(tools),\n };\n const previous = this.contributions.get(sourceId) ?? { entries: 0, bytes: 0 };\n const nextEntries = this.totalEntries - previous.entries + contribution.entries;\n if (nextEntries > this.maxEntries) {\n throw new McpPayloadTooLargeError(this.label, nextEntries, this.maxEntries);\n }\n const nextBytes = this.totalBytes - previous.bytes + contribution.bytes;\n if (nextBytes > this.maxBytes) {\n throw new McpPayloadTooLargeError(this.label, nextBytes, this.maxBytes);\n }\n this.contributions.set(sourceId, contribution);\n this.totalEntries = nextEntries;\n this.totalBytes = nextBytes;\n return tools;\n }\n\n remove(sourceId: string): void {\n const previous = this.contributions.get(sourceId);\n if (!previous) return;\n this.contributions.delete(sourceId);\n this.totalEntries -= previous.entries;\n this.totalBytes -= previous.bytes;\n }\n\n snapshot(): Readonly<McpToolListContribution> {\n return { entries: this.totalEntries, bytes: this.totalBytes };\n }\n}\n\n/** Stable-order parallel map with a hard in-flight ceiling and fail-stop drain. */\nexport async function boundedParallelMap<T, R>(\n values: readonly T[],\n concurrency: number,\n mapper: (value: T, index: number) => Promise<R>,\n): Promise<R[]> {\n if (!Number.isInteger(concurrency) || concurrency < 1) {\n throw new Error(\"bounded parallel map concurrency must be a positive integer\");\n }\n if (values.length === 0) return [];\n\n const results = new Array<R>(values.length);\n const errors: Array<{ index: number; error: unknown }> = [];\n let nextIndex = 0;\n let stopped = false;\n const worker = async () => {\n while (!stopped) {\n const index = nextIndex;\n if (index >= values.length) return;\n nextIndex += 1;\n try {\n results[index] = await mapper(values[index]!, index);\n } catch (error) {\n errors.push({ index, error });\n stopped = true;\n }\n }\n };\n await Promise.all(Array.from({ length: Math.min(concurrency, values.length) }, () => worker()));\n if (errors.length > 0) {\n errors.sort((left, right) => left.index - right.index);\n throw errors[0]!.error;\n }\n return results;\n}\n\n/**\n * Guard normal MCP traffic with the same deployment policy as OAuth traffic.\n * The pinned transport resolves and policy-checks the destination after the\n * caller has composed any credential headers, then performs the final network\n * call through the vetted Agent. It forces manual redirects, so an\n * Authorization-bearing request never follows a provider-controlled Location\n * to an unvalidated host.\n *\n * This module is deliberately agent-loop-free. API code imports the explicit\n * `@opengeni/runtime/mcp-network` leaf rather than the runtime root barrel.\n */\nexport function guardedMcpFetch<TInput extends string | URL | Request>(\n settings: Pick<Settings, \"environment\" | \"integrationsAllowPrivateNetworkTargets\">,\n fetchImpl: (input: TInput, init?: RequestInit) => Promise<Response>,\n options: {\n maxResponseBytes?: number;\n dnsLookup?: DnsLookup;\n requireHttpsOutsideLocalTest?: boolean;\n } = {},\n): (input: TInput, init?: RequestInit) => Promise<Response> {\n return async (input: TInput, init?: RequestInit) => {\n const response = await pinnedFetch(input, init, settings, {\n fetchImpl: fetchImpl as FetchLike,\n ...(options.dnsLookup ? { dnsLookup: options.dnsLookup } : {}),\n label: \"MCP endpoint\",\n requireHttpsOutsideLocalTest: options.requireHttpsOutsideLocalTest ?? true,\n });\n return boundMcpResponseBody(response, options.maxResponseBytes ?? MCP_MAX_RESPONSE_BYTES);\n };\n}\n\nexport function boundMcpResponseBody(response: Response, maxBytes: number): Response {\n const declaredLength = Number(response.headers.get(\"content-length\"));\n if (Number.isFinite(declaredLength) && declaredLength > maxBytes) {\n void response.body?.cancel().catch(() => undefined);\n throw new McpPayloadTooLargeError(\"MCP response\", declaredLength, maxBytes);\n }\n if (!response.body) {\n return response;\n }\n\n const reader = response.body.getReader();\n let receivedBytes = 0;\n const body = new ReadableStream<Uint8Array>({\n async pull(controller) {\n try {\n const chunk = await reader.read();\n if (chunk.done) {\n controller.close();\n return;\n }\n receivedBytes += chunk.value.byteLength;\n if (receivedBytes > maxBytes) {\n await reader.cancel().catch(() => undefined);\n controller.error(new McpPayloadTooLargeError(\"MCP response\", receivedBytes, maxBytes));\n return;\n }\n controller.enqueue(chunk.value);\n } catch (error) {\n controller.error(error);\n }\n },\n async cancel(reason) {\n await reader.cancel(reason).catch(() => undefined);\n },\n });\n const bounded = new Response(body, {\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n });\n Object.defineProperties(bounded, {\n redirected: { value: response.redirected },\n type: { value: response.type },\n url: { value: response.url },\n });\n return bounded;\n}\n\nexport async function cancelMcpResponseBody(response: Response): Promise<void> {\n await response.body?.cancel().catch(() => undefined);\n}\n\n/** Read and rebuild an inbound MCP request before the SDK parses it. */\nexport async function boundedMcpRequest(\n request: Request,\n maxBytes = MCP_MAX_INBOUND_REQUEST_BYTES,\n): Promise<Request> {\n const declared = request.headers.get(\"content-length\");\n if (declared !== null) {\n const normalized = declared.trim();\n if (!/^\\d+$/.test(normalized)) {\n await request.body?.cancel().catch(() => undefined);\n throw new McpPayloadTooLargeError(\"MCP request\", 0, maxBytes);\n }\n const declaredBytes = Number(normalized);\n if (!Number.isSafeInteger(declaredBytes) || declaredBytes > maxBytes) {\n await request.body?.cancel().catch(() => undefined);\n throw new McpPayloadTooLargeError(\"MCP request\", declaredBytes, maxBytes);\n }\n }\n if (!request.body) {\n return request;\n }\n const reader = request.body.getReader();\n const chunks: Uint8Array[] = [];\n let receivedBytes = 0;\n try {\n for (;;) {\n const result = await reader.read();\n if (result.done) break;\n receivedBytes += result.value.byteLength;\n if (receivedBytes > maxBytes) {\n await reader.cancel().catch(() => undefined);\n throw new McpPayloadTooLargeError(\"MCP request\", receivedBytes, maxBytes);\n }\n chunks.push(result.value);\n }\n } catch (error) {\n await reader.cancel().catch(() => undefined);\n throw error;\n } finally {\n reader.releaseLock();\n }\n const body = new Uint8Array(receivedBytes);\n let offset = 0;\n for (const chunk of chunks) {\n body.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return new Request(request, { body, headers: request.headers });\n}\n\nexport async function assertMcpDestinationAllowed(\n rawUrl: string,\n settings: Pick<Settings, \"environment\" | \"integrationsAllowPrivateNetworkTargets\">,\n options: { dnsLookup?: DnsLookup } = {},\n): Promise<void> {\n await resolvePinnedDestination(rawUrl, settings, {\n ...(options.dnsLookup ? { dnsLookup: options.dnsLookup } : {}),\n label: \"MCP endpoint\",\n requireHttpsOutsideLocalTest: true,\n });\n}\n\nexport const isNonPublicMcpAddress = isNonPublicAddress;\n"],"mappings":"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","names":[]}