sdlc-agent-4-enterprise-server 1.55.2 → 1.55.3

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 (97) hide show
  1. package/.kiro/agents/prompts/ba-agent.md +4 -4
  2. package/.kiro/agents/prompts/sa-agent.md +3 -3
  3. package/.kiro/agents/prompts/sm-agent.md +23 -22
  4. package/.kiro/agents/prompts/ta-agent.md +1 -1
  5. package/.kiro/agents/prompts/ui-agent.md +2 -2
  6. package/.kiro/agents/sm-agent.md +22 -21
  7. package/.kiro/agents/ui-agent.md +2 -2
  8. package/Dockerfile +28 -14
  9. package/README.md +5 -1
  10. package/dist/config/ContentProxy.d.ts +34 -0
  11. package/dist/config/ContentProxy.js +36 -0
  12. package/dist/config/ContentProxy.js.map +1 -0
  13. package/dist/modules/orchestration/McpClientManager.d.ts +12 -0
  14. package/dist/modules/orchestration/McpClientManager.js +47 -3
  15. package/dist/modules/orchestration/McpClientManager.js.map +1 -1
  16. package/dist/modules/orchestration/ProxiedToolRegistry.d.ts +9 -0
  17. package/dist/modules/orchestration/ProxiedToolRegistry.js +21 -3
  18. package/dist/modules/orchestration/ProxiedToolRegistry.js.map +1 -1
  19. package/dist/modules/orchestration/content-proxy/artifact-name.d.ts +10 -0
  20. package/dist/modules/orchestration/content-proxy/artifact-name.js +36 -0
  21. package/dist/modules/orchestration/content-proxy/artifact-name.js.map +1 -0
  22. package/dist/modules/orchestration/content-proxy/artifact-source.d.ts +30 -0
  23. package/dist/modules/orchestration/content-proxy/artifact-source.js +168 -0
  24. package/dist/modules/orchestration/content-proxy/artifact-source.js.map +1 -0
  25. package/dist/modules/orchestration/content-proxy/detect.d.ts +19 -0
  26. package/dist/modules/orchestration/content-proxy/detect.js +60 -0
  27. package/dist/modules/orchestration/content-proxy/detect.js.map +1 -0
  28. package/dist/modules/orchestration/content-proxy/invocation-adapter.d.ts +10 -0
  29. package/dist/modules/orchestration/content-proxy/invocation-adapter.js +52 -0
  30. package/dist/modules/orchestration/content-proxy/invocation-adapter.js.map +1 -0
  31. package/dist/modules/orchestration/content-proxy/result-adapter.d.ts +22 -0
  32. package/dist/modules/orchestration/content-proxy/result-adapter.js +131 -0
  33. package/dist/modules/orchestration/content-proxy/result-adapter.js.map +1 -0
  34. package/dist/modules/orchestration/content-proxy/schema-adapter.d.ts +13 -0
  35. package/dist/modules/orchestration/content-proxy/schema-adapter.js +45 -0
  36. package/dist/modules/orchestration/content-proxy/schema-adapter.js.map +1 -0
  37. package/dist/modules/orchestration/content-proxy/types.d.ts +21 -0
  38. package/dist/modules/orchestration/content-proxy/types.js +6 -0
  39. package/dist/modules/orchestration/content-proxy/types.js.map +1 -0
  40. package/eslint.config.js +9 -0
  41. package/package.json +1 -1
  42. package/src/config/ContentProxy.ts +52 -0
  43. package/src/modules/orchestration/McpClientManager.ts +45 -3
  44. package/src/modules/orchestration/ProxiedToolRegistry.ts +24 -3
  45. package/src/modules/orchestration/__tests__/McpClientManager.content-proxy.test.ts +132 -0
  46. package/src/modules/orchestration/content-proxy/__tests__/content-proxy.remote.test.ts +75 -0
  47. package/src/modules/orchestration/content-proxy/__tests__/content-proxy.test.ts +157 -0
  48. package/src/modules/orchestration/content-proxy/artifact-name.ts +39 -0
  49. package/src/modules/orchestration/content-proxy/artifact-source.ts +176 -0
  50. package/src/modules/orchestration/content-proxy/detect.ts +79 -0
  51. package/src/modules/orchestration/content-proxy/invocation-adapter.ts +57 -0
  52. package/src/modules/orchestration/content-proxy/result-adapter.ts +150 -0
  53. package/src/modules/orchestration/content-proxy/schema-adapter.ts +52 -0
  54. package/src/modules/orchestration/content-proxy/types.ts +23 -0
  55. package/tests/e2e/admin-api.auth.e2e.test.ts +80 -0
  56. package/tests/e2e/admin-api.config.e2e.test.ts +96 -0
  57. package/tests/e2e/admin-api.fixtures.ts +74 -0
  58. package/tests/e2e/admin-api.kb.e2e.test.ts +174 -0
  59. package/tests/e2e/admin-api.mcp-profile.e2e.test.ts +122 -0
  60. package/tests/e2e/admin-api.rbac.e2e.test.ts +105 -0
  61. package/tests/e2e/admin-api.unauth.e2e.test.ts +44 -0
  62. package/tests/e2e/admin-api.users-audit.e2e.test.ts +150 -0
  63. package/tests/e2e/admin-ui.audit-graph-profile.ui.test.ts +94 -0
  64. package/tests/e2e/admin-ui.config-search.ui.test.ts +79 -0
  65. package/tests/e2e/admin-ui.dashboard-nav.ui.test.ts +76 -0
  66. package/tests/e2e/admin-ui.fixtures.ts +31 -0
  67. package/tests/e2e/admin-ui.kb-mcp.ui.test.ts +116 -0
  68. package/tests/e2e/admin-ui.login.ui.test.ts +42 -0
  69. package/tests/e2e/admin-ui.multitab-logout.ui.test.ts +116 -0
  70. package/tests/e2e/admin-ui.quality-tags.ui.test.ts +81 -0
  71. package/tests/e2e/admin-ui.rbac-users.ui.test.ts +133 -0
  72. package/tests/e2e/mcp-api.health-tools.e2e.test.ts +85 -0
  73. package/tests/e2e/mcp-api.helpers.ts +79 -0
  74. package/tests/e2e/mcp-api.lifecycle.e2e.test.ts +58 -0
  75. package/tests/e2e/mcp-api.tool-call.e2e.test.ts +135 -0
  76. package/tests/e2e/multi-tenant.endpoint-permissions.e2e.test.ts +99 -0
  77. package/tests/e2e/multi-tenant.fixtures.ts +180 -0
  78. package/tests/e2e/multi-tenant.isolation-audit.e2e.test.ts +94 -0
  79. package/tests/e2e/multi-tenant.roledata.e2e.test.ts +78 -0
  80. package/tests/e2e/multi-tenant.setup.e2e.test.ts +187 -0
  81. package/tests/integration/auth.dual-token.test.ts +74 -0
  82. package/tests/integration/auth.lifecycle.test.ts +150 -0
  83. package/tests/integration/auth.users.test.ts +196 -0
  84. package/tests/integration/helpers.ts +81 -0
  85. package/tests/integration/mcp-tools.catalog.test.ts +156 -0
  86. package/tests/integration/mcp-tools.execution.test.ts +151 -0
  87. package/tests/integration/reindex.fixtures.ts +71 -0
  88. package/tests/integration/reindex.integrity.it.test.ts +79 -0
  89. package/tests/integration/reindex.isolation.it.test.ts +92 -0
  90. package/tests/integration/reindex.lifecycle.it.test.ts +73 -0
  91. package/tests/e2e/admin-api.e2e.test.ts +0 -704
  92. package/tests/e2e/admin-ui.e2e.test.ts +0 -702
  93. package/tests/e2e/mcp-api.e2e.test.ts +0 -311
  94. package/tests/e2e/multi-tenant.e2e.test.ts +0 -440
  95. package/tests/integration/auth.test.ts +0 -413
  96. package/tests/integration/mcp-tools.test.ts +0 -312
  97. package/tests/integration/reindex.it.test.ts +0 -260
@@ -2,10 +2,18 @@
2
2
  * ProxiedToolRegistry — tracks tools proxied from child MCP servers.
3
3
  * SA4E-218: reserved (locally-provided) tool names are never shadowed by child servers.
4
4
  * Extracted from McpClientManager (SA4E-223 line-count gate, <= 200 lines/file).
5
+ *
6
+ * Content proxy: file-producing child tools (`export_…` / `render_…` / `generate_…`)
7
+ * are registered with the wrapper markers (`content_base64`, `x-content-param`,
8
+ * `x-output-base64`) and their capability is kept for invocation/result adaptation.
5
9
  */
10
+ import { detectCapability } from './content-proxy/detect.js';
11
+ import { adaptToolDefinition } from './content-proxy/schema-adapter.js';
6
12
  export class ProxiedToolRegistry {
7
13
  toolsToServer = new Map();
8
14
  proxiedTools = [];
15
+ /** Content-proxy capability per adapted tool. */
16
+ capabilities = new Map();
9
17
  /** Tool names provided locally by the orchestrator/registry — child servers must never shadow these. */
10
18
  reservedToolNames = new Set();
11
19
  logger;
@@ -18,6 +26,8 @@ export class ProxiedToolRegistry {
18
26
  /** Returns the child server name owning a tool, or undefined if not proxied. */
19
27
  getOwningServer(toolName) { return this.toolsToServer.get(toolName); }
20
28
  getProxiedTools() { return this.proxiedTools; }
29
+ /** Content-proxy capability for a tool, or undefined for unadapted tools. */
30
+ getCapability(toolName) { return this.capabilities.get(toolName); }
21
31
  getToolCount(serverName) {
22
32
  let count = 0;
23
33
  for (const [, sn] of this.toolsToServer.entries()) {
@@ -35,10 +45,16 @@ export class ProxiedToolRegistry {
35
45
  continue;
36
46
  }
37
47
  this.toolsToServer.set(tool.name, name);
38
- this.proxiedTools.push({
48
+ const definition = {
39
49
  name: tool.name, description: tool.description ?? '',
40
50
  category: name, inputSchema: tool.inputSchema,
41
- });
51
+ };
52
+ const capability = detectCapability(definition);
53
+ if (capability) {
54
+ this.capabilities.set(tool.name, capability);
55
+ this.logger.info({ tool: tool.name, contentParam: capability.contentParam }, 'Content proxy enabled for child tool');
56
+ }
57
+ this.proxiedTools.push(capability ? adaptToolDefinition(definition, capability) : definition);
42
58
  }
43
59
  }
44
60
  clearServerTools(name) {
@@ -47,8 +63,10 @@ export class ProxiedToolRegistry {
47
63
  if (sn === name)
48
64
  names.push(tn);
49
65
  }
50
- for (const tn of names)
66
+ for (const tn of names) {
51
67
  this.toolsToServer.delete(tn);
68
+ this.capabilities.delete(tn);
69
+ }
52
70
  this.proxiedTools = this.proxiedTools.filter((t) => !names.includes(t.name));
53
71
  }
54
72
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ProxiedToolRegistry.js","sourceRoot":"","sources":["../../../src/modules/orchestration/ProxiedToolRegistry.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,MAAM,OAAO,mBAAmB;IACtB,aAAa,GAAwB,IAAI,GAAG,EAAE,CAAC;IAC/C,YAAY,GAAqB,EAAE,CAAC;IAC5C,wGAAwG;IAChG,iBAAiB,GAAgB,IAAI,GAAG,EAAE,CAAC;IAC3C,MAAM,CAAS;IAEvB,YAAY,MAAc;QACxB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,EAAE,SAAS,EAAE,qBAAqB,EAAE,CAAC,CAAC;IACnE,CAAC;IAED,0FAA0F;IAC1F,oBAAoB,CAAC,KAAkB,IAAU,IAAI,CAAC,iBAAiB,GAAG,KAAK,CAAC,CAAC,CAAC;IAElF,QAAQ,CAAC,QAAgB,IAAa,OAAO,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IAEhF,gFAAgF;IAChF,eAAe,CAAC,QAAgB,IAAwB,OAAO,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IAElG,eAAe,KAAuB,OAAO,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC;IAEjE,YAAY,CAAC,UAAkB;QAC7B,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,KAAK,MAAM,CAAC,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE,EAAE,CAAC;YAAC,IAAI,EAAE,KAAK,UAAU;gBAAE,KAAK,EAAE,CAAC;QAAC,CAAC;QACtF,OAAO,KAAK,CAAC;IACf,CAAC;IAED,KAAK,CAAC,mBAAmB,CAAC,IAAY,EAAE,MAAc;QACpD,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,SAAS,EAAE,CAAC;QAC7C,KAAK,MAAM,IAAI,IAAI,WAAW,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC;YAC3C,qFAAqF;YACrF,IAAI,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC1C,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,iHAAiH,CAAC,CAAC;gBACvK,SAAS;YACX,CAAC;YACD,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YACxC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC;gBACrB,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,IAAI,EAAE;gBACpD,QAAQ,EAAE,IAAkC,EAAE,WAAW,EAAE,IAAI,CAAC,WAAiD;aAClH,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,gBAAgB,CAAC,IAAY;QAC3B,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,KAAK,MAAM,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE,EAAE,CAAC;YAAC,IAAI,EAAE,KAAK,IAAI;gBAAE,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAAC,CAAC;QACzF,KAAK,MAAM,EAAE,IAAI,KAAK;YAAE,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACtD,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAC/E,CAAC;CACF"}
1
+ {"version":3,"file":"ProxiedToolRegistry.js","sourceRoot":"","sources":["../../../src/modules/orchestration/ProxiedToolRegistry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Artifact naming — derive a safe, bare file name for inlined bytes.
3
+ * No path fragments ever leave the backend: only `path.basename` survives.
4
+ */
5
+ /** Keep only a bare file name — never a path fragment from a child result. */
6
+ export declare function safeName(value: unknown): string | null;
7
+ /** File name from a location (path/URI) or, failing that, from the MIME type. */
8
+ export declare function nameFromLocation(location: string, mime?: string): string;
9
+ /** Stable fallback name when the child provides neither a name nor a usable location. */
10
+ export declare function nameFromMime(mime?: string): string;
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Artifact naming — derive a safe, bare file name for inlined bytes.
3
+ * No path fragments ever leave the backend: only `path.basename` survives.
4
+ */
5
+ import * as path from 'path';
6
+ /** Keep only a bare file name — never a path fragment from a child result. */
7
+ export function safeName(value) {
8
+ if (typeof value !== 'string')
9
+ return null;
10
+ const trimmed = value.trim().split(/[?#]/)[0];
11
+ const base = path.basename(trimmed);
12
+ return base.length > 0 && base !== '.' && base !== '..' ? base : null;
13
+ }
14
+ /** File name from a location (path/URI) or, failing that, from the MIME type. */
15
+ export function nameFromLocation(location, mime) {
16
+ return safeName(location) ?? nameFromMime(mime);
17
+ }
18
+ /** Stable fallback name when the child provides neither a name nor a usable location. */
19
+ export function nameFromMime(mime) {
20
+ return (mime && MIME_NAMES[mime.toLowerCase()]) || 'artifact.bin';
21
+ }
22
+ const MIME_NAMES = {
23
+ 'application/pdf': 'artifact.pdf',
24
+ 'application/json': 'artifact.json',
25
+ 'application/zip': 'artifact.zip',
26
+ 'text/html': 'artifact.html',
27
+ 'text/plain': 'artifact.txt',
28
+ 'text/markdown': 'artifact.md',
29
+ 'image/png': 'artifact.png',
30
+ 'image/jpeg': 'artifact.jpg',
31
+ 'image/svg+xml': 'artifact.svg',
32
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'artifact.docx',
33
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'artifact.xlsx',
34
+ 'application/vnd.openxmlformats-officedocument.presentationml.presentation': 'artifact.pptx',
35
+ };
36
+ //# sourceMappingURL=artifact-name.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"artifact-name.js","sourceRoot":"","sources":["../../../../src/modules/orchestration/content-proxy/artifact-name.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAE7B,8EAA8E;AAC9E,MAAM,UAAU,QAAQ,CAAC,KAAc;IACrC,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3C,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9C,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IACpC,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,KAAK,GAAG,IAAI,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;AACxE,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,gBAAgB,CAAC,QAAgB,EAAE,IAAa;IAC9D,OAAO,QAAQ,CAAC,QAAQ,CAAC,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC;AAClD,CAAC;AAED,yFAAyF;AACzF,MAAM,UAAU,YAAY,CAAC,IAAa;IACxC,OAAO,CAAC,IAAI,IAAI,UAAU,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,IAAI,cAAc,CAAC;AACpE,CAAC;AAED,MAAM,UAAU,GAA2B;IACzC,iBAAiB,EAAE,cAAc;IACjC,kBAAkB,EAAE,eAAe;IACnC,iBAAiB,EAAE,cAAc;IACjC,WAAW,EAAE,eAAe;IAC5B,YAAY,EAAE,cAAc;IAC5B,eAAe,EAAE,aAAa;IAC9B,WAAW,EAAE,cAAc;IAC3B,YAAY,EAAE,cAAc;IAC5B,eAAe,EAAE,cAAc;IAC/B,yEAAyE,EAAE,eAAe;IAC1F,mEAAmE,EAAE,eAAe;IACpF,2EAA2E,EAAE,eAAe;CAC7F,CAAC"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Artifact source resolution — turns whatever a child tool returns into bytes.
3
+ *
4
+ * Transport-agnostic by design: the backend may run next to the child (stdio)
5
+ * or far away from it (httpStream). Retrieval order per artifact:
6
+ * 1. inline payload (`output_base64` / `base64` / `data`)
7
+ * 2. location: data URI / http(s) URL / MCP resource URI / filesystem path
8
+ * 3. filesystem path (backend host) → MCP resources/read fallback (remote host)
9
+ *
10
+ * Remote servers that only return a bare local path cannot be read — the bytes
11
+ * live on another machine. That case is reported as a tool error by the caller,
12
+ * never silently downgraded to a path.
13
+ */
14
+ export interface ArtifactDeps {
15
+ /** Read a resource from the child server (works across hosts). */
16
+ readResource?: (uri: string) => Promise<Buffer | null>;
17
+ /** Fetch an http(s) URL. Overridable for tests; defaults to global fetch. */
18
+ fetchUrl?: (url: string) => Promise<Buffer | null>;
19
+ }
20
+ export interface FetchedArtifact {
21
+ buffer: Buffer;
22
+ file_name: string;
23
+ mime_type?: string;
24
+ }
25
+ /** Resolve one `artifacts[]` entry to bytes, or null when nothing is readable. */
26
+ export declare function resolveArtifact(artifact: any, deps: ArtifactDeps): Promise<FetchedArtifact | null>;
27
+ /** Resolve a standard MCP content block (resource / resource_link / image / audio). */
28
+ export declare function resolveContentBlock(block: any, deps: ArtifactDeps): Promise<FetchedArtifact | null>;
29
+ /** Read any supported location to bytes. */
30
+ export declare function readLocation(location: string, deps: ArtifactDeps): Promise<Buffer | null>;
@@ -0,0 +1,168 @@
1
+ /**
2
+ * Artifact source resolution — turns whatever a child tool returns into bytes.
3
+ *
4
+ * Transport-agnostic by design: the backend may run next to the child (stdio)
5
+ * or far away from it (httpStream). Retrieval order per artifact:
6
+ * 1. inline payload (`output_base64` / `base64` / `data`)
7
+ * 2. location: data URI / http(s) URL / MCP resource URI / filesystem path
8
+ * 3. filesystem path (backend host) → MCP resources/read fallback (remote host)
9
+ *
10
+ * Remote servers that only return a bare local path cannot be read — the bytes
11
+ * live on another machine. That case is reported as a tool error by the caller,
12
+ * never silently downgraded to a path.
13
+ */
14
+ import * as fs from 'fs';
15
+ import * as path from 'path';
16
+ import { fileURLToPath, pathToFileURL } from 'url';
17
+ import { MAX_ARTIFACT_BYTES } from '../../../config/ContentProxy.js';
18
+ import { nameFromLocation, nameFromMime, safeName } from './artifact-name.js';
19
+ /** Resolve one `artifacts[]` entry to bytes, or null when nothing is readable. */
20
+ export async function resolveArtifact(artifact, deps) {
21
+ if (!artifact || typeof artifact !== 'object')
22
+ return null;
23
+ const mime = typeof artifact.mime_type === 'string' ? artifact.mime_type : undefined;
24
+ const name = safeName(artifact.name);
25
+ const inline = firstString(artifact.output_base64, artifact.content_base64, artifact.base64, artifact.data);
26
+ if (inline)
27
+ return fromBase64(inline, name, mime);
28
+ for (const location of [artifact.url, artifact.uri, artifact.path]) {
29
+ if (typeof location !== 'string' || location.trim() === '')
30
+ continue;
31
+ const buffer = await readLocation(location.trim(), deps);
32
+ if (buffer)
33
+ return { buffer, file_name: name ?? nameFromLocation(location, mime), mime_type: mime };
34
+ }
35
+ return null;
36
+ }
37
+ /** Resolve a standard MCP content block (resource / resource_link / image / audio). */
38
+ export async function resolveContentBlock(block, deps) {
39
+ if (!block || typeof block !== 'object')
40
+ return null;
41
+ if (block.type === 'resource' && block.resource) {
42
+ const res = block.resource;
43
+ const mime = typeof res.mimeType === 'string' ? res.mimeType : undefined;
44
+ if (typeof res.blob === 'string')
45
+ return fromBase64(res.blob, safeName(res.uri), mime);
46
+ if (typeof res.text === 'string') {
47
+ const buffer = Buffer.from(res.text, 'utf8');
48
+ return { buffer, file_name: safeName(res.uri) ?? nameFromMime(mime), mime_type: mime };
49
+ }
50
+ if (typeof res.uri === 'string')
51
+ return locationArtifact(res.uri, deps, mime);
52
+ }
53
+ if (block.type === 'resource_link' && typeof block.uri === 'string') {
54
+ return locationArtifact(block.uri, deps, typeof block.mimeType === 'string' ? block.mimeType : undefined);
55
+ }
56
+ if ((block.type === 'image' || block.type === 'audio' || block.type === 'blob') && typeof block.data === 'string') {
57
+ const mime = typeof block.mimeType === 'string' ? block.mimeType : undefined;
58
+ return fromBase64(block.data, safeName(block.name), mime);
59
+ }
60
+ return null;
61
+ }
62
+ /** Read any supported location to bytes. */
63
+ export async function readLocation(location, deps) {
64
+ if (location.startsWith('data:'))
65
+ return decodeDataUri(location);
66
+ if (/^https?:\/\//i.test(location))
67
+ return (deps.fetchUrl ?? fetchHttp)(location);
68
+ const fileUri = location.startsWith('file:') ? location : toFileUri(location);
69
+ if (location.startsWith('file:')) {
70
+ const local = readLocalFileUri(location);
71
+ if (local)
72
+ return local;
73
+ return deps.readResource ? deps.readResource(location) : null;
74
+ }
75
+ if (/^[a-z][a-z0-9+.-]*:/i.test(location) && !/^[a-z]:[\\/]/i.test(location)) {
76
+ // Any other scheme (artifact://, mcp://, …) is server-owned — ask the server.
77
+ return deps.readResource ? deps.readResource(location) : null;
78
+ }
79
+ const local = readLocalFile(path.resolve(location));
80
+ if (local)
81
+ return local;
82
+ return deps.readResource && fileUri ? deps.readResource(fileUri) : null;
83
+ }
84
+ async function locationArtifact(location, deps, mime) {
85
+ const buffer = await readLocation(location, deps);
86
+ return buffer ? { buffer, file_name: nameFromLocation(location, mime), mime_type: mime } : null;
87
+ }
88
+ function fromBase64(value, name, mime) {
89
+ const compact = value.replace(/\s+/g, '');
90
+ if (!compact || compact.length % 4 !== 0 || !/^[A-Za-z0-9+/]+={0,2}$/.test(compact))
91
+ return null;
92
+ const buffer = Buffer.from(compact, 'base64');
93
+ if (buffer.length === 0 || buffer.length > MAX_ARTIFACT_BYTES)
94
+ return null;
95
+ return { buffer, file_name: name ?? nameFromMime(mime), mime_type: mime };
96
+ }
97
+ /** Read a file on the backend host: regular file only, size-capped, no symlinks. */
98
+ function readLocalFile(filePath) {
99
+ try {
100
+ const stat = fs.lstatSync(filePath);
101
+ if (!stat.isFile() || stat.isSymbolicLink() || stat.size > MAX_ARTIFACT_BYTES)
102
+ return null;
103
+ const buffer = fs.readFileSync(filePath);
104
+ return buffer.length <= MAX_ARTIFACT_BYTES ? buffer : null;
105
+ }
106
+ catch {
107
+ return null;
108
+ }
109
+ }
110
+ function readLocalFileUri(uri) {
111
+ try {
112
+ return readLocalFile(fileURLToPath(uri));
113
+ }
114
+ catch {
115
+ return null;
116
+ }
117
+ }
118
+ /** Cross-platform file:// URI — a POSIX path stays POSIX even on a Windows host. */
119
+ function toFileUri(location) {
120
+ try {
121
+ if (/^[A-Za-z]:[\\/]/.test(location) || location.startsWith('\\\\')) {
122
+ return pathToFileURL(location).href;
123
+ }
124
+ if (location.startsWith('/'))
125
+ return `file://${encodeURI(location).replace(/#/g, '%23')}`;
126
+ return pathToFileURL(path.resolve(location)).href;
127
+ }
128
+ catch {
129
+ return null;
130
+ }
131
+ }
132
+ async function fetchHttp(url) {
133
+ try {
134
+ const res = await fetch(url, { signal: AbortSignal.timeout(30_000), redirect: 'follow' });
135
+ if (!res.ok)
136
+ return null;
137
+ const declared = Number(res.headers.get('content-length') ?? '0');
138
+ if (declared > MAX_ARTIFACT_BYTES)
139
+ return null;
140
+ const buffer = Buffer.from(await res.arrayBuffer());
141
+ return buffer.length > 0 && buffer.length <= MAX_ARTIFACT_BYTES ? buffer : null;
142
+ }
143
+ catch {
144
+ return null;
145
+ }
146
+ }
147
+ function decodeDataUri(uri) {
148
+ const match = /^data:([^;,]*)(;base64)?,(.*)$/is.exec(uri);
149
+ if (!match)
150
+ return null;
151
+ try {
152
+ const buffer = match[2]
153
+ ? Buffer.from(match[3], 'base64')
154
+ : Buffer.from(decodeURIComponent(match[3]), 'utf8');
155
+ return buffer.length > 0 && buffer.length <= MAX_ARTIFACT_BYTES ? buffer : null;
156
+ }
157
+ catch {
158
+ return null;
159
+ }
160
+ }
161
+ function firstString(...values) {
162
+ for (const value of values) {
163
+ if (typeof value === 'string' && value.trim().length > 0)
164
+ return value;
165
+ }
166
+ return null;
167
+ }
168
+ //# sourceMappingURL=artifact-source.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"artifact-source.js","sourceRoot":"","sources":["../../../../src/modules/orchestration/content-proxy/artifact-source.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Capability detection — decides, from a child tool's name/description/schema,
3
+ * whether the content proxy should adapt it.
4
+ *
5
+ * Only file-producing tools are adapted. That gate keeps the adapter away from
6
+ * ordinary tools that also happen to have a `body`/`content` argument
7
+ * (jira_create_issue, confluence_create_page, mem_ingest, …).
8
+ */
9
+ import type { ContentCapability } from './types.js';
10
+ /** Minimal shape of a child tool (raw MCP listTools entry). */
11
+ export interface ToolLike {
12
+ name: string;
13
+ description?: string;
14
+ inputSchema?: Record<string, unknown>;
15
+ }
16
+ /** Derive the content capability for a child tool, or null when it is not a file tool. */
17
+ export declare function detectCapability(tool: ToolLike): ContentCapability | null;
18
+ /** Pick the argument that carries document content. */
19
+ export declare function detectContentParam(schema: Record<string, any>): string | undefined;
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Capability detection — decides, from a child tool's name/description/schema,
3
+ * whether the content proxy should adapt it.
4
+ *
5
+ * Only file-producing tools are adapted. That gate keeps the adapter away from
6
+ * ordinary tools that also happen to have a `body`/`content` argument
7
+ * (jira_create_issue, confluence_create_page, mem_ingest, …).
8
+ */
9
+ import { CONTENT_PARAM_ALIASES, CONTENT_PROXY_OVERRIDES, FILE_OUTPUT_DESCRIPTION, FILE_TOOL_NAME_PREFIXES, NON_CONTENT_PARAM, } from '../../../config/ContentProxy.js';
10
+ /** Derive the content capability for a child tool, or null when it is not a file tool. */
11
+ export function detectCapability(tool) {
12
+ const override = CONTENT_PROXY_OVERRIDES[tool.name];
13
+ if (override?.enabled === false)
14
+ return null;
15
+ const fileOutput = override?.fileOutput ?? looksLikeFileOutput(tool);
16
+ if (!fileOutput)
17
+ return null;
18
+ const schema = (tool.inputSchema ?? {});
19
+ const properties = (schema.properties ?? {});
20
+ // Already wrapper-native (declares content_base64 itself, e.g. drawio_export_png):
21
+ // the extension handles it directly — no adaptation, no double mapping.
22
+ if (properties.content_base64)
23
+ return null;
24
+ const contentParam = override?.contentParam !== undefined
25
+ ? (override.contentParam ?? undefined)
26
+ : detectContentParam(schema);
27
+ return {
28
+ contentParam,
29
+ fileOutput,
30
+ declaredParams: new Set(Object.keys(properties)),
31
+ };
32
+ }
33
+ /** Pick the argument that carries document content. */
34
+ export function detectContentParam(schema) {
35
+ const properties = (schema?.properties ?? {});
36
+ const required = Array.isArray(schema?.required) ? schema.required : [];
37
+ for (const alias of CONTENT_PARAM_ALIASES) {
38
+ if (properties[alias] && isStringLike(properties[alias]))
39
+ return alias;
40
+ }
41
+ const candidates = required.filter((name) => properties[name] && isStringLike(properties[name]) && !NON_CONTENT_PARAM.test(name));
42
+ return candidates.length === 1 ? candidates[0] : undefined;
43
+ }
44
+ function looksLikeFileOutput(tool) {
45
+ const name = tool.name.toLowerCase();
46
+ if (FILE_TOOL_NAME_PREFIXES.some((prefix) => name.startsWith(prefix)))
47
+ return true;
48
+ return FILE_OUTPUT_DESCRIPTION.test(tool.description ?? '');
49
+ }
50
+ /** JSON-Schema shape check that tolerates `anyOf`/`oneOf` unions (pydantic emits these). */
51
+ function isStringLike(property) {
52
+ if (!property || typeof property !== 'object')
53
+ return false;
54
+ const prop = property;
55
+ if (prop.type === 'string')
56
+ return true;
57
+ const variants = [prop.anyOf, prop.oneOf].find(Array.isArray);
58
+ return !!variants?.some((variant) => variant?.type === 'string');
59
+ }
60
+ //# sourceMappingURL=detect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect.js","sourceRoot":"","sources":["../../../../src/modules/orchestration/content-proxy/detect.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EACL,qBAAqB,EACrB,uBAAuB,EACvB,uBAAuB,EACvB,uBAAuB,EACvB,iBAAiB,GAClB,MAAM,iCAAiC,CAAC;AASzC,0FAA0F;AAC1F,MAAM,UAAU,gBAAgB,CAAC,IAAc;IAC7C,MAAM,QAAQ,GAAG,uBAAuB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACpD,IAAI,QAAQ,EAAE,OAAO,KAAK,KAAK;QAAE,OAAO,IAAI,CAAC;IAE7C,MAAM,UAAU,GAAG,QAAQ,EAAE,UAAU,IAAI,mBAAmB,CAAC,IAAI,CAAC,CAAC;IACrE,IAAI,CAAC,UAAU;QAAE,OAAO,IAAI,CAAC;IAE7B,MAAM,MAAM,GAAG,CAAC,IAAI,CAAC,WAAW,IAAI,EAAE,CAAwB,CAAC;IAC/D,MAAM,UAAU,GAAG,CAAC,MAAM,CAAC,UAAU,IAAI,EAAE,CAAwB,CAAC;IACpE,mFAAmF;IACnF,wEAAwE;IACxE,IAAI,UAAU,CAAC,cAAc;QAAE,OAAO,IAAI,CAAC;IAE3C,MAAM,YAAY,GAAG,QAAQ,EAAE,YAAY,KAAK,SAAS;QACvD,CAAC,CAAC,CAAC,QAAQ,CAAC,YAAY,IAAI,SAAS,CAAC;QACtC,CAAC,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC;IAE/B,OAAO;QACL,YAAY;QACZ,UAAU;QACV,cAAc,EAAE,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;KACjD,CAAC;AACJ,CAAC;AAED,uDAAuD;AACvD,MAAM,UAAU,kBAAkB,CAAC,MAA2B;IAC5D,MAAM,UAAU,GAAG,CAAC,MAAM,EAAE,UAAU,IAAI,EAAE,CAAwB,CAAC;IACrE,MAAM,QAAQ,GAAa,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;IAElF,KAAK,MAAM,KAAK,IAAI,qBAAqB,EAAE,CAAC;QAC1C,IAAI,UAAU,CAAC,KAAK,CAAC,IAAI,YAAY,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;YAAE,OAAO,KAAK,CAAC;IACzE,CAAC;IAED,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,CAChC,CAAC,IAAI,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,YAAY,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAC9F,CAAC;IACF,OAAO,UAAU,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAC7D,CAAC;AAED,SAAS,mBAAmB,CAAC,IAAc;IACzC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;IACrC,IAAI,uBAAuB,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACnF,OAAO,uBAAuB,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC;AAC9D,CAAC;AAED,4FAA4F;AAC5F,SAAS,YAAY,CAAC,QAAiB;IACrC,IAAI,CAAC,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC5D,MAAM,IAAI,GAAG,QAA+B,CAAC;IAC7C,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACxC,MAAM,QAAQ,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAA2C,CAAC;IACxG,OAAO,CAAC,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,EAAE,IAAI,KAAK,QAAQ,CAAC,CAAC;AACnE,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Invocation adapter — maps the wrapper's `content_base64` onto the child's own
3
+ * content argument (e.g. `markdown`) and strips adapter-only arguments.
4
+ *
5
+ * Wrapper mode is signalled by the presence of `content_base64` or `output_path`.
6
+ * Direct (non-wrapper) calls keep their original arguments and result shape.
7
+ */
8
+ import type { AdaptedInvocation, ContentCapability } from './types.js';
9
+ /** Adapt arguments before they reach the child server. */
10
+ export declare function adaptInvocation(capability: ContentCapability, args: unknown): AdaptedInvocation;
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Invocation adapter — maps the wrapper's `content_base64` onto the child's own
3
+ * content argument (e.g. `markdown`) and strips adapter-only arguments.
4
+ *
5
+ * Wrapper mode is signalled by the presence of `content_base64` or `output_path`.
6
+ * Direct (non-wrapper) calls keep their original arguments and result shape.
7
+ */
8
+ /** Arguments owned by the extension wrapper, never forwarded to a child. */
9
+ const WRAPPER_ONLY_KEYS = ['content_base64', 'output_path'];
10
+ /** Adapt arguments before they reach the child server. */
11
+ export function adaptInvocation(capability, args) {
12
+ const input = isRecord(args) ? args : {};
13
+ const wrapperMode = WRAPPER_ONLY_KEYS.some((key) => Object.prototype.hasOwnProperty.call(input, key));
14
+ if (!wrapperMode)
15
+ return { args: input, wrapperMode: false, capability };
16
+ const adapted = { ...input };
17
+ const encoded = adapted.content_base64;
18
+ delete adapted.output_path;
19
+ if (!capability.declaredParams.has('file_path'))
20
+ delete adapted.file_path;
21
+ if (capability.contentParam) {
22
+ delete adapted.content_base64;
23
+ if (typeof encoded === 'string' && encoded.length > 0) {
24
+ const decoded = decodeBase64Utf8(encoded);
25
+ if (decoded === null) {
26
+ return { args: adapted, wrapperMode: true, capability, error: 'Invalid content_base64 payload.' };
27
+ }
28
+ adapted[capability.contentParam] = decoded;
29
+ }
30
+ else if (typeof adapted[capability.contentParam] !== 'string') {
31
+ return {
32
+ args: adapted,
33
+ wrapperMode: true,
34
+ capability,
35
+ error: `Missing content: pass file_path so the extension wrapper can inject content_base64 ` +
36
+ `(or send ${capability.contentParam} directly).`,
37
+ };
38
+ }
39
+ }
40
+ return { args: adapted, wrapperMode: true, capability };
41
+ }
42
+ /** Strict UTF-8 base64 decode — rejects payloads that would silently corrupt the document. */
43
+ function decodeBase64Utf8(value) {
44
+ const compact = value.replace(/\s+/g, '');
45
+ if (!compact || compact.length % 4 !== 0 || !/^[A-Za-z0-9+/]+={0,2}$/.test(compact))
46
+ return null;
47
+ return Buffer.from(compact, 'base64').toString('utf8');
48
+ }
49
+ function isRecord(value) {
50
+ return !!value && typeof value === 'object' && !Array.isArray(value);
51
+ }
52
+ //# sourceMappingURL=invocation-adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invocation-adapter.js","sourceRoot":"","sources":["../../../../src/modules/orchestration/content-proxy/invocation-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,4EAA4E;AAC5E,MAAM,iBAAiB,GAAG,CAAC,gBAAgB,EAAE,aAAa,CAAU,CAAC;AAErE,0DAA0D;AAC1D,MAAM,UAAU,eAAe,CAAC,UAA6B,EAAE,IAAa;IAC1E,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;IACzC,MAAM,WAAW,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC;IACtG,IAAI,CAAC,WAAW;QAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC;IAEzE,MAAM,OAAO,GAA4B,EAAE,GAAG,KAAK,EAAE,CAAC;IACtD,MAAM,OAAO,GAAG,OAAO,CAAC,cAAc,CAAC;IACvC,OAAO,OAAO,CAAC,WAAW,CAAC;IAC3B,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,GAAG,CAAC,WAAW,CAAC;QAAE,OAAO,OAAO,CAAC,SAAS,CAAC;IAE1E,IAAI,UAAU,CAAC,YAAY,EAAE,CAAC;QAC5B,OAAO,OAAO,CAAC,cAAc,CAAC;QAC9B,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtD,MAAM,OAAO,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;YAC1C,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;gBACrB,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,iCAAiC,EAAE,CAAC;YACpG,CAAC;YACD,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,GAAG,OAAO,CAAC;QAC7C,CAAC;aAAM,IAAI,OAAO,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,KAAK,QAAQ,EAAE,CAAC;YAChE,OAAO;gBACL,IAAI,EAAE,OAAO;gBACb,WAAW,EAAE,IAAI;gBACjB,UAAU;gBACV,KAAK,EACH,qFAAqF;oBACrF,YAAY,UAAU,CAAC,YAAY,aAAa;aACnD,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;AAC1D,CAAC;AAED,8FAA8F;AAC9F,SAAS,gBAAgB,CAAC,KAAa;IACrC,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC1C,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,wBAAwB,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAC;IACjG,OAAO,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;AACzD,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,CAAC,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AACvE,CAAC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Result adapter — inlines child artifacts as base64 so the extension wrapper can
3
+ * write them to disk. Only runs in wrapper mode (content in → content out), so
4
+ * direct dynamic calls keep returning the child's lightweight path response.
5
+ *
6
+ * The child result text is usually a JSON document shaped like:
7
+ * { success, summary, artifacts: [{ path, name, mime_type, size }] }
8
+ * Bytes may also arrive as MCP resource blocks (remote servers). Absolute host
9
+ * paths are stripped from the response; bytes travel as base64.
10
+ *
11
+ * Wrapper-mode contract: content or a hard error — never a bare path. A path
12
+ * that the backend cannot read (remote server, same-host invariant broken) is
13
+ * reported as ARTIFACT_FETCH_FAILED so the caller cannot mistake it for success.
14
+ */
15
+ import type { AdaptedInvocation } from './types.js';
16
+ import type { ArtifactDeps } from './artifact-source.js';
17
+ export interface ResultAdapterDeps extends ArtifactDeps {
18
+ /** Child server name — included in retrieval errors. */
19
+ serverName?: string;
20
+ }
21
+ /** Adapt a child result for wrapper mode; unchanged for direct calls. */
22
+ export declare function adaptResult(invocation: AdaptedInvocation, result: any, deps?: ResultAdapterDeps): Promise<any>;