@tangle-network/agent-runtime 0.22.0 → 0.23.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/README.md +27 -0
- package/dist/chunk-7HN72MF3.js +200 -0
- package/dist/chunk-7HN72MF3.js.map +1 -0
- package/dist/chunk-IQHYOJU3.js +427 -0
- package/dist/chunk-IQHYOJU3.js.map +1 -0
- package/dist/{chunk-724Y4KHY.js → chunk-TZ53F7M7.js} +3 -2
- package/dist/chunk-TZ53F7M7.js.map +1 -0
- package/dist/{chunk-FTWTA6YE.js → chunk-UNQM6XQO.js} +33 -432
- package/dist/chunk-UNQM6XQO.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -1
- package/dist/loops.d.ts +2 -2
- package/dist/loops.js +1 -1
- package/dist/mcp/bin.js +3 -2
- package/dist/mcp/bin.js.map +1 -1
- package/dist/mcp/index.d.ts +52 -2
- package/dist/mcp/index.js +57 -8
- package/dist/mcp/index.js.map +1 -1
- package/dist/otel-export-B33Cy_60.d.ts +114 -0
- package/dist/profiles.d.ts +1 -1
- package/dist/{types-Ps7-2gJC.d.ts → types-DmkRGTBn.d.ts} +17 -0
- package/package.json +1 -1
- package/dist/chunk-724Y4KHY.js.map +0 -1
- package/dist/chunk-FTWTA6YE.js.map +0 -1
package/README.md
CHANGED
|
@@ -213,6 +213,33 @@ Or run the ready-made bin:
|
|
|
213
213
|
TANGLE_API_KEY=sk_sandbox_... agent-runtime-mcp
|
|
214
214
|
```
|
|
215
215
|
|
|
216
|
+
### Surfacing the tools through `createOpenAICompatibleBackend`
|
|
217
|
+
|
|
218
|
+
Sandbox callers discover MCP tools through the runtime mount. Callers that
|
|
219
|
+
route through the OpenAI-compat backend (tcloud, OpenRouter, cli-bridge,
|
|
220
|
+
OpenAI direct) must hand the model an explicit `tools[]` array — the
|
|
221
|
+
backend does not auto-discover. `mcpToolsForRuntimeMcp()` returns the
|
|
222
|
+
canonical projection so the model can call any of the 5 delegation tools
|
|
223
|
+
through the OpenAI-compat path:
|
|
224
|
+
|
|
225
|
+
```ts
|
|
226
|
+
import {
|
|
227
|
+
createOpenAICompatibleBackend,
|
|
228
|
+
mcpToolsForRuntimeMcp,
|
|
229
|
+
} from '@tangle-network/agent-runtime'
|
|
230
|
+
|
|
231
|
+
const backend = createOpenAICompatibleBackend({
|
|
232
|
+
apiKey,
|
|
233
|
+
baseUrl,
|
|
234
|
+
model,
|
|
235
|
+
tools: mcpToolsForRuntimeMcp(),
|
|
236
|
+
})
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Use `mcpToolsForRuntimeMcpSubset(['delegate_research', 'delegation_status'])`
|
|
240
|
+
when you want a curated subset (e.g. read-only research without the coder
|
|
241
|
+
queue).
|
|
242
|
+
|
|
216
243
|
The bin auto-wires the coder delegate and, when
|
|
217
244
|
`@tangle-network/agent-knowledge` is installed as a peer, the researcher
|
|
218
245
|
delegate. Environment knobs:
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DELEGATE_CODE_DESCRIPTION,
|
|
3
|
+
DELEGATE_CODE_INPUT_SCHEMA,
|
|
4
|
+
DELEGATE_CODE_TOOL_NAME,
|
|
5
|
+
DELEGATE_FEEDBACK_DESCRIPTION,
|
|
6
|
+
DELEGATE_FEEDBACK_INPUT_SCHEMA,
|
|
7
|
+
DELEGATE_FEEDBACK_TOOL_NAME,
|
|
8
|
+
DELEGATE_RESEARCH_DESCRIPTION,
|
|
9
|
+
DELEGATE_RESEARCH_INPUT_SCHEMA,
|
|
10
|
+
DELEGATE_RESEARCH_TOOL_NAME,
|
|
11
|
+
DELEGATION_HISTORY_DESCRIPTION,
|
|
12
|
+
DELEGATION_HISTORY_INPUT_SCHEMA,
|
|
13
|
+
DELEGATION_HISTORY_TOOL_NAME,
|
|
14
|
+
DELEGATION_STATUS_DESCRIPTION,
|
|
15
|
+
DELEGATION_STATUS_INPUT_SCHEMA,
|
|
16
|
+
DELEGATION_STATUS_TOOL_NAME
|
|
17
|
+
} from "./chunk-UNQM6XQO.js";
|
|
18
|
+
|
|
19
|
+
// src/mcp/openai-tools.ts
|
|
20
|
+
function buildTool(name, description, parameters) {
|
|
21
|
+
return {
|
|
22
|
+
type: "function",
|
|
23
|
+
function: { name, description, parameters: { ...parameters } }
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function mcpToolsForRuntimeMcp() {
|
|
27
|
+
return [
|
|
28
|
+
buildTool(
|
|
29
|
+
DELEGATE_CODE_TOOL_NAME,
|
|
30
|
+
DELEGATE_CODE_DESCRIPTION,
|
|
31
|
+
DELEGATE_CODE_INPUT_SCHEMA
|
|
32
|
+
),
|
|
33
|
+
buildTool(
|
|
34
|
+
DELEGATE_RESEARCH_TOOL_NAME,
|
|
35
|
+
DELEGATE_RESEARCH_DESCRIPTION,
|
|
36
|
+
DELEGATE_RESEARCH_INPUT_SCHEMA
|
|
37
|
+
),
|
|
38
|
+
buildTool(
|
|
39
|
+
DELEGATE_FEEDBACK_TOOL_NAME,
|
|
40
|
+
DELEGATE_FEEDBACK_DESCRIPTION,
|
|
41
|
+
DELEGATE_FEEDBACK_INPUT_SCHEMA
|
|
42
|
+
),
|
|
43
|
+
buildTool(
|
|
44
|
+
DELEGATION_STATUS_TOOL_NAME,
|
|
45
|
+
DELEGATION_STATUS_DESCRIPTION,
|
|
46
|
+
DELEGATION_STATUS_INPUT_SCHEMA
|
|
47
|
+
),
|
|
48
|
+
buildTool(
|
|
49
|
+
DELEGATION_HISTORY_TOOL_NAME,
|
|
50
|
+
DELEGATION_HISTORY_DESCRIPTION,
|
|
51
|
+
DELEGATION_HISTORY_INPUT_SCHEMA
|
|
52
|
+
)
|
|
53
|
+
];
|
|
54
|
+
}
|
|
55
|
+
function mcpToolsForRuntimeMcpSubset(names) {
|
|
56
|
+
const allowed = new Set(names);
|
|
57
|
+
return mcpToolsForRuntimeMcp().filter((tool) => allowed.has(tool.function.name));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// src/otel-export.ts
|
|
61
|
+
var SCOPE = { name: "@tangle-network/agent-runtime", version: "0.23.0" };
|
|
62
|
+
function createOtelExporter(config) {
|
|
63
|
+
const resolvedEndpoint = config?.endpoint ?? (typeof process !== "undefined" ? process.env.OTEL_EXPORTER_OTLP_ENDPOINT : void 0);
|
|
64
|
+
if (!resolvedEndpoint) return void 0;
|
|
65
|
+
const endpoint = resolvedEndpoint;
|
|
66
|
+
const headers = config?.headers ?? parseHeadersFromEnv();
|
|
67
|
+
const batchSize = config?.batchSize ?? 64;
|
|
68
|
+
const flushIntervalMs = config?.flushIntervalMs ?? 5e3;
|
|
69
|
+
const serviceName = config?.serviceName ?? "agent-runtime";
|
|
70
|
+
const resourceAttrs = config?.resourceAttributes ?? {};
|
|
71
|
+
const pending = [];
|
|
72
|
+
let timer;
|
|
73
|
+
let stopped = false;
|
|
74
|
+
const exporter = {
|
|
75
|
+
exportSpan(span) {
|
|
76
|
+
if (stopped) return;
|
|
77
|
+
pending.push(span);
|
|
78
|
+
if (pending.length >= batchSize) {
|
|
79
|
+
void doFlush();
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
async flush() {
|
|
83
|
+
await doFlush();
|
|
84
|
+
},
|
|
85
|
+
async shutdown() {
|
|
86
|
+
stopped = true;
|
|
87
|
+
if (timer !== void 0) {
|
|
88
|
+
clearInterval(timer);
|
|
89
|
+
timer = void 0;
|
|
90
|
+
}
|
|
91
|
+
await doFlush();
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
timer = setInterval(() => {
|
|
95
|
+
if (pending.length > 0) void doFlush();
|
|
96
|
+
}, flushIntervalMs);
|
|
97
|
+
if (typeof timer === "object" && "unref" in timer) {
|
|
98
|
+
;
|
|
99
|
+
timer.unref();
|
|
100
|
+
}
|
|
101
|
+
async function doFlush() {
|
|
102
|
+
if (pending.length === 0) return;
|
|
103
|
+
const batch = pending.splice(0);
|
|
104
|
+
const body = {
|
|
105
|
+
resourceSpans: [
|
|
106
|
+
{
|
|
107
|
+
resource: {
|
|
108
|
+
attributes: toAttributes({
|
|
109
|
+
"service.name": serviceName,
|
|
110
|
+
...resourceAttrs
|
|
111
|
+
})
|
|
112
|
+
},
|
|
113
|
+
scopeSpans: [{ scope: SCOPE, spans: batch }]
|
|
114
|
+
}
|
|
115
|
+
]
|
|
116
|
+
};
|
|
117
|
+
const url = endpoint.replace(/\/+$/, "") + "/v1/traces";
|
|
118
|
+
try {
|
|
119
|
+
await fetch(url, {
|
|
120
|
+
method: "POST",
|
|
121
|
+
headers: { "content-type": "application/json", ...headers },
|
|
122
|
+
body: JSON.stringify(body)
|
|
123
|
+
});
|
|
124
|
+
} catch {
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return exporter;
|
|
128
|
+
}
|
|
129
|
+
function loopEventToOtelSpan(event, traceId, parentSpanId) {
|
|
130
|
+
const spanId = generateSpanId();
|
|
131
|
+
const attrs = {
|
|
132
|
+
"loop.event_kind": event.kind,
|
|
133
|
+
"loop.run_id": event.runId
|
|
134
|
+
};
|
|
135
|
+
for (const [k, v] of Object.entries(event.payload)) {
|
|
136
|
+
if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") {
|
|
137
|
+
attrs[`loop.${k}`] = v;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
const ts = msToNs(event.timestamp);
|
|
141
|
+
return {
|
|
142
|
+
traceId: padTraceId(traceId),
|
|
143
|
+
spanId,
|
|
144
|
+
parentSpanId: parentSpanId ? padSpanId(parentSpanId) : void 0,
|
|
145
|
+
name: event.kind,
|
|
146
|
+
kind: 1,
|
|
147
|
+
startTimeUnixNano: ts,
|
|
148
|
+
endTimeUnixNano: ts,
|
|
149
|
+
attributes: toAttributes(attrs),
|
|
150
|
+
status: { code: 1 }
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
function parseHeadersFromEnv() {
|
|
154
|
+
if (typeof process === "undefined") return {};
|
|
155
|
+
const raw = process.env.OTEL_EXPORTER_OTLP_HEADERS;
|
|
156
|
+
if (!raw) return {};
|
|
157
|
+
const out = {};
|
|
158
|
+
for (const pair of raw.split(",")) {
|
|
159
|
+
const eq = pair.indexOf("=");
|
|
160
|
+
if (eq < 0) continue;
|
|
161
|
+
const key = pair.slice(0, eq).trim();
|
|
162
|
+
const value = pair.slice(eq + 1).trim();
|
|
163
|
+
if (key) out[key] = value;
|
|
164
|
+
}
|
|
165
|
+
return out;
|
|
166
|
+
}
|
|
167
|
+
function toAttributes(record) {
|
|
168
|
+
return Object.entries(record).map(([key, value]) => ({
|
|
169
|
+
key,
|
|
170
|
+
value: typeof value === "number" ? Number.isInteger(value) ? { intValue: value.toString() } : { doubleValue: value } : typeof value === "boolean" ? { boolValue: value } : { stringValue: value }
|
|
171
|
+
}));
|
|
172
|
+
}
|
|
173
|
+
function msToNs(ms) {
|
|
174
|
+
return (BigInt(Math.floor(ms)) * 1000000n).toString();
|
|
175
|
+
}
|
|
176
|
+
function padSpanId(id) {
|
|
177
|
+
const cleaned = id.replace(/-/g, "");
|
|
178
|
+
return cleaned.slice(0, 16).padEnd(16, "0");
|
|
179
|
+
}
|
|
180
|
+
function padTraceId(id) {
|
|
181
|
+
const cleaned = id.replace(/-/g, "");
|
|
182
|
+
return cleaned.slice(0, 32).padEnd(32, "0");
|
|
183
|
+
}
|
|
184
|
+
function generateSpanId() {
|
|
185
|
+
const bytes = new Uint8Array(8);
|
|
186
|
+
if (typeof globalThis.crypto?.getRandomValues === "function") {
|
|
187
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
188
|
+
} else {
|
|
189
|
+
for (let i = 0; i < 8; i++) bytes[i] = Math.floor(Math.random() * 256);
|
|
190
|
+
}
|
|
191
|
+
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export {
|
|
195
|
+
mcpToolsForRuntimeMcp,
|
|
196
|
+
mcpToolsForRuntimeMcpSubset,
|
|
197
|
+
createOtelExporter,
|
|
198
|
+
loopEventToOtelSpan
|
|
199
|
+
};
|
|
200
|
+
//# sourceMappingURL=chunk-7HN72MF3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/mcp/openai-tools.ts","../src/otel-export.ts"],"sourcesContent":["/**\n * @experimental\n *\n * OpenAI Chat Completions `tools[]` projection of the 5 agent-runtime MCP\n * delegation tools.\n *\n * Use when configuring `createOpenAICompatibleBackend({ tools: ... })` so the\n * model can call `delegate_code`, `delegate_research`, `delegate_feedback`,\n * `delegation_status`, and `delegation_history` through the OpenAI-compat\n * transport (tcloud, OpenRouter, OpenAI direct, cli-bridge). The runtime\n * surfaces tool calls as `tool_call` stream events — execution is the\n * caller's responsibility (typically the parent sandbox runtime's MCP\n * mount).\n *\n * Sandbox-SDK callers do NOT need this helper: the sandbox runtime mounts\n * MCP servers natively and the in-sandbox harness discovers tools via the\n * runtime, not via an OpenAI tools array.\n *\n * Tool name + description + JSON-schema are pulled from the canonical\n * `DELEGATE_*` constants exported by `./tools/*` so the projection cannot\n * drift from the server's own validators.\n */\n\nimport type { OpenAIChatTool } from '../types'\nimport {\n DELEGATE_CODE_DESCRIPTION,\n DELEGATE_CODE_INPUT_SCHEMA,\n DELEGATE_CODE_TOOL_NAME,\n} from './tools/delegate-code'\nimport {\n DELEGATE_FEEDBACK_DESCRIPTION,\n DELEGATE_FEEDBACK_INPUT_SCHEMA,\n DELEGATE_FEEDBACK_TOOL_NAME,\n} from './tools/delegate-feedback'\nimport {\n DELEGATE_RESEARCH_DESCRIPTION,\n DELEGATE_RESEARCH_INPUT_SCHEMA,\n DELEGATE_RESEARCH_TOOL_NAME,\n} from './tools/delegate-research'\nimport {\n DELEGATION_HISTORY_DESCRIPTION,\n DELEGATION_HISTORY_INPUT_SCHEMA,\n DELEGATION_HISTORY_TOOL_NAME,\n} from './tools/delegation-history'\nimport {\n DELEGATION_STATUS_DESCRIPTION,\n DELEGATION_STATUS_INPUT_SCHEMA,\n DELEGATION_STATUS_TOOL_NAME,\n} from './tools/delegation-status'\n\nfunction buildTool(\n name: string,\n description: string,\n parameters: Readonly<Record<string, unknown>>,\n): OpenAIChatTool {\n // `parameters` arrives as a deeply-readonly `as const` literal. The\n // OpenAI-compat backend JSON-serializes the body so a shallow copy\n // into a plain object is sufficient — and shields callers that mutate\n // the returned descriptor from corrupting the source constant.\n return {\n type: 'function',\n function: { name, description, parameters: { ...parameters } },\n }\n}\n\n/**\n * @experimental\n *\n * Returns the 5 delegation tools projected into OpenAI Chat Completions\n * `tools[]` shape. The order is stable: `delegate_code`,\n * `delegate_research`, `delegate_feedback`, `delegation_status`,\n * `delegation_history`.\n */\nexport function mcpToolsForRuntimeMcp(): OpenAIChatTool[] {\n return [\n buildTool(\n DELEGATE_CODE_TOOL_NAME,\n DELEGATE_CODE_DESCRIPTION,\n DELEGATE_CODE_INPUT_SCHEMA as Readonly<Record<string, unknown>>,\n ),\n buildTool(\n DELEGATE_RESEARCH_TOOL_NAME,\n DELEGATE_RESEARCH_DESCRIPTION,\n DELEGATE_RESEARCH_INPUT_SCHEMA as Readonly<Record<string, unknown>>,\n ),\n buildTool(\n DELEGATE_FEEDBACK_TOOL_NAME,\n DELEGATE_FEEDBACK_DESCRIPTION,\n DELEGATE_FEEDBACK_INPUT_SCHEMA as Readonly<Record<string, unknown>>,\n ),\n buildTool(\n DELEGATION_STATUS_TOOL_NAME,\n DELEGATION_STATUS_DESCRIPTION,\n DELEGATION_STATUS_INPUT_SCHEMA as Readonly<Record<string, unknown>>,\n ),\n buildTool(\n DELEGATION_HISTORY_TOOL_NAME,\n DELEGATION_HISTORY_DESCRIPTION,\n DELEGATION_HISTORY_INPUT_SCHEMA as Readonly<Record<string, unknown>>,\n ),\n ]\n}\n\n/**\n * @experimental\n *\n * Subset filter — return only the projected tools whose `function.name`\n * appears in `names`. Useful for curated mounts (e.g. only the queue-bound\n * delegation tools, omitting `delegate_feedback`). Unknown names are\n * silently ignored; pass an empty array to get an empty result.\n */\nexport function mcpToolsForRuntimeMcpSubset(names: ReadonlyArray<string>): OpenAIChatTool[] {\n const allowed = new Set(names)\n return mcpToolsForRuntimeMcp().filter((tool) => allowed.has(tool.function.name))\n}\n","/**\n * OTEL span exporter — streams LoopTraceEvents to an OTLP/HTTP collector.\n *\n * Reads OTEL_EXPORTER_OTLP_ENDPOINT + OTEL_EXPORTER_OTLP_HEADERS from env\n * when no explicit config is given. Keeps the runtime dep-free from\n * @opentelemetry/sdk-trace-base — minimal OTLP/JSON serializer.\n *\n * The exporter accepts both raw OtelSpan objects and LoopTraceEvents\n * (which get converted to OTLP spans automatically).\n */\n\nexport interface OtelExportConfig {\n /** OTLP endpoint. Reads OTEL_EXPORTER_OTLP_ENDPOINT env by default. */\n endpoint?: string\n /** OTLP headers. Reads OTEL_EXPORTER_OTLP_HEADERS env by default. */\n headers?: Record<string, string>\n /** Batch size before flush. Default 64. */\n batchSize?: number\n /** Flush interval ms. Default 5000. */\n flushIntervalMs?: number\n /** Resource attributes stamped on every export. */\n resourceAttributes?: Record<string, string | number | boolean>\n /** Service name. Default 'agent-runtime'. */\n serviceName?: string\n}\n\nexport interface OtelExporter {\n /** Export a span. */\n exportSpan(span: OtelSpan): void\n /** Force flush pending spans. */\n flush(): Promise<void>\n /** Shutdown cleanly. */\n shutdown(): Promise<void>\n}\n\nexport interface OtelSpan {\n traceId: string\n spanId: string\n parentSpanId?: string\n name: string\n kind?: number\n startTimeUnixNano: string\n endTimeUnixNano: string\n attributes?: OtelAttribute[]\n status?: { code: number; message?: string }\n}\n\nexport interface OtelAttribute {\n key: string\n value: { stringValue?: string; intValue?: string; doubleValue?: number; boolValue?: boolean }\n}\n\ninterface OtlpResourceSpans {\n resource: { attributes: OtelAttribute[] }\n scopeSpans: Array<{ scope: { name: string; version: string }; spans: OtelSpan[] }>\n}\n\ninterface OtlpExport {\n resourceSpans: OtlpResourceSpans[]\n}\n\nconst SCOPE = { name: '@tangle-network/agent-runtime', version: '0.23.0' }\n\n/**\n * Create an OTEL exporter. Returns undefined when no endpoint is configured.\n */\nexport function createOtelExporter(config?: OtelExportConfig): OtelExporter | undefined {\n const resolvedEndpoint =\n config?.endpoint ?? (typeof process !== 'undefined' ? process.env.OTEL_EXPORTER_OTLP_ENDPOINT : undefined)\n if (!resolvedEndpoint) return undefined\n const endpoint: string = resolvedEndpoint\n\n const headers = config?.headers ?? parseHeadersFromEnv()\n const batchSize = config?.batchSize ?? 64\n const flushIntervalMs = config?.flushIntervalMs ?? 5000\n const serviceName = config?.serviceName ?? 'agent-runtime'\n const resourceAttrs = config?.resourceAttributes ?? {}\n\n const pending: OtelSpan[] = []\n let timer: ReturnType<typeof setInterval> | undefined\n let stopped = false\n\n const exporter: OtelExporter = {\n exportSpan(span: OtelSpan): void {\n if (stopped) return\n pending.push(span)\n if (pending.length >= batchSize) {\n void doFlush()\n }\n },\n\n async flush(): Promise<void> {\n await doFlush()\n },\n\n async shutdown(): Promise<void> {\n stopped = true\n if (timer !== undefined) {\n clearInterval(timer)\n timer = undefined\n }\n await doFlush()\n },\n }\n\n timer = setInterval(() => {\n if (pending.length > 0) void doFlush()\n }, flushIntervalMs)\n if (typeof timer === 'object' && 'unref' in timer) {\n ;(timer as NodeJS.Timeout).unref()\n }\n\n async function doFlush(): Promise<void> {\n if (pending.length === 0) return\n const batch = pending.splice(0)\n const body: OtlpExport = {\n resourceSpans: [\n {\n resource: {\n attributes: toAttributes({\n 'service.name': serviceName,\n ...resourceAttrs,\n }),\n },\n scopeSpans: [{ scope: SCOPE, spans: batch }],\n },\n ],\n }\n const url = endpoint.replace(/\\/+$/, '') + '/v1/traces'\n try {\n await fetch(url, {\n method: 'POST',\n headers: { 'content-type': 'application/json', ...headers },\n body: JSON.stringify(body),\n })\n } catch {\n // Best-effort — telemetry export must not crash the runtime.\n }\n }\n\n return exporter\n}\n\n/**\n * Convert a LoopTraceEvent into an OtelSpan for export.\n */\nexport function loopEventToOtelSpan(\n event: {\n kind: string\n runId: string\n timestamp: number\n payload: object\n },\n traceId: string,\n parentSpanId?: string,\n): OtelSpan {\n const spanId = generateSpanId()\n const attrs: Record<string, string | number | boolean> = {\n 'loop.event_kind': event.kind,\n 'loop.run_id': event.runId,\n }\n for (const [k, v] of Object.entries(event.payload)) {\n if (typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean') {\n attrs[`loop.${k}`] = v\n }\n }\n const ts = msToNs(event.timestamp)\n return {\n traceId: padTraceId(traceId),\n spanId,\n parentSpanId: parentSpanId ? padSpanId(parentSpanId) : undefined,\n name: event.kind,\n kind: 1,\n startTimeUnixNano: ts,\n endTimeUnixNano: ts,\n attributes: toAttributes(attrs),\n status: { code: 1 },\n }\n}\n\nfunction parseHeadersFromEnv(): Record<string, string> {\n if (typeof process === 'undefined') return {}\n const raw = process.env.OTEL_EXPORTER_OTLP_HEADERS\n if (!raw) return {}\n const out: Record<string, string> = {}\n for (const pair of raw.split(',')) {\n const eq = pair.indexOf('=')\n if (eq < 0) continue\n const key = pair.slice(0, eq).trim()\n const value = pair.slice(eq + 1).trim()\n if (key) out[key] = value\n }\n return out\n}\n\nfunction toAttributes(record: Record<string, string | number | boolean>): OtelAttribute[] {\n return Object.entries(record).map(([key, value]) => ({\n key,\n value:\n typeof value === 'number'\n ? Number.isInteger(value)\n ? { intValue: value.toString() }\n : { doubleValue: value }\n : typeof value === 'boolean'\n ? { boolValue: value }\n : { stringValue: value },\n }))\n}\n\nfunction msToNs(ms: number): string {\n return (BigInt(Math.floor(ms)) * 1_000_000n).toString()\n}\n\nfunction padSpanId(id: string): string {\n const cleaned = id.replace(/-/g, '')\n return cleaned.slice(0, 16).padEnd(16, '0')\n}\n\nfunction padTraceId(id: string): string {\n const cleaned = id.replace(/-/g, '')\n return cleaned.slice(0, 32).padEnd(32, '0')\n}\n\nfunction generateSpanId(): string {\n const bytes = new Uint8Array(8)\n if (typeof globalThis.crypto?.getRandomValues === 'function') {\n globalThis.crypto.getRandomValues(bytes)\n } else {\n for (let i = 0; i < 8; i++) bytes[i] = Math.floor(Math.random() * 256)\n }\n return Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('')\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DELEGATE_CODE_DESCRIPTION,
|
|
3
|
+
DELEGATE_CODE_INPUT_SCHEMA,
|
|
4
|
+
DELEGATE_CODE_TOOL_NAME,
|
|
5
|
+
DELEGATE_FEEDBACK_DESCRIPTION,
|
|
6
|
+
DELEGATE_FEEDBACK_INPUT_SCHEMA,
|
|
7
|
+
DELEGATE_FEEDBACK_TOOL_NAME,
|
|
8
|
+
DELEGATE_RESEARCH_DESCRIPTION,
|
|
9
|
+
DELEGATE_RESEARCH_INPUT_SCHEMA,
|
|
10
|
+
DELEGATE_RESEARCH_TOOL_NAME,
|
|
11
|
+
DELEGATION_HISTORY_DESCRIPTION,
|
|
12
|
+
DELEGATION_HISTORY_INPUT_SCHEMA,
|
|
13
|
+
DELEGATION_HISTORY_TOOL_NAME,
|
|
14
|
+
DELEGATION_STATUS_DESCRIPTION,
|
|
15
|
+
DELEGATION_STATUS_INPUT_SCHEMA,
|
|
16
|
+
DELEGATION_STATUS_TOOL_NAME,
|
|
17
|
+
DelegationTaskQueue,
|
|
18
|
+
InMemoryFeedbackStore,
|
|
19
|
+
createDelegateCodeHandler,
|
|
20
|
+
createDelegateFeedbackHandler,
|
|
21
|
+
createDelegateResearchHandler,
|
|
22
|
+
createDelegationHistoryHandler,
|
|
23
|
+
createDelegationStatusHandler
|
|
24
|
+
} from "./chunk-UNQM6XQO.js";
|
|
25
|
+
import {
|
|
26
|
+
runLoop
|
|
27
|
+
} from "./chunk-TZ53F7M7.js";
|
|
28
|
+
import {
|
|
29
|
+
coderProfile,
|
|
30
|
+
multiHarnessCoderFanout
|
|
31
|
+
} from "./chunk-CBQVID7G.js";
|
|
32
|
+
|
|
33
|
+
// src/mcp/executor.ts
|
|
34
|
+
function createSiblingSandboxExecutor(options) {
|
|
35
|
+
const underlying = options.client;
|
|
36
|
+
const client = {
|
|
37
|
+
create(opts) {
|
|
38
|
+
return underlying.create(opts);
|
|
39
|
+
},
|
|
40
|
+
describePlacement(box) {
|
|
41
|
+
return { kind: "sibling", sandboxId: readId(box) };
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
return {
|
|
45
|
+
client,
|
|
46
|
+
describe() {
|
|
47
|
+
return "sibling-sandbox (each delegation = fresh sandbox via client.create)";
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function createFleetWorkspaceExecutor(options) {
|
|
52
|
+
const fleet = options.fleet;
|
|
53
|
+
const exclude = new Set(options.excludeMachineIds ?? []);
|
|
54
|
+
let callIndex = 0;
|
|
55
|
+
const placementBySandboxId = /* @__PURE__ */ new Map();
|
|
56
|
+
const client = {
|
|
57
|
+
async create() {
|
|
58
|
+
const ids = fleet.ids.filter((id) => !exclude.has(id));
|
|
59
|
+
if (ids.length === 0) {
|
|
60
|
+
throw new Error(
|
|
61
|
+
`agent-runtime: fleet ${fleet.fleetId} has no eligible worker machines (ids=[${fleet.ids.join(",")}], excluded=[${[...exclude].join(",")}])`
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
const selector = options.selectMachine;
|
|
65
|
+
const machineId = selector ? selector({ callIndex, ids }) : ids[callIndex % ids.length];
|
|
66
|
+
callIndex += 1;
|
|
67
|
+
if (typeof machineId !== "string" || machineId.length === 0) {
|
|
68
|
+
throw new Error("agent-runtime: fleet executor selectMachine returned an empty machine id");
|
|
69
|
+
}
|
|
70
|
+
const box = await fleet.sandbox(machineId);
|
|
71
|
+
const sandboxId = readId(box);
|
|
72
|
+
if (sandboxId) placementBySandboxId.set(sandboxId, { machineId });
|
|
73
|
+
return box;
|
|
74
|
+
},
|
|
75
|
+
describePlacement(box) {
|
|
76
|
+
const sandboxId = readId(box);
|
|
77
|
+
const recorded = sandboxId ? placementBySandboxId.get(sandboxId) : void 0;
|
|
78
|
+
return {
|
|
79
|
+
kind: "fleet",
|
|
80
|
+
sandboxId,
|
|
81
|
+
fleetId: fleet.fleetId,
|
|
82
|
+
machineId: recorded?.machineId
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
return {
|
|
87
|
+
client,
|
|
88
|
+
describe() {
|
|
89
|
+
const excluded = exclude.size > 0 ? ` (excluded=[${[...exclude].join(",")}])` : "";
|
|
90
|
+
return `fleet-workspace (fleetId=${fleet.fleetId}, machines=[${fleet.ids.join(",")}]${excluded})`;
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function readId(box) {
|
|
95
|
+
const raw = box.id;
|
|
96
|
+
return typeof raw === "string" && raw.length > 0 ? raw : void 0;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// src/mcp/bin-helpers.ts
|
|
100
|
+
async function detectExecutor(args) {
|
|
101
|
+
const env = args.env ?? process.env;
|
|
102
|
+
const fleetId = parseFleetId(env.TANGLE_FLEET_ID);
|
|
103
|
+
if (!fleetId) {
|
|
104
|
+
return createSiblingSandboxExecutor({ client: args.sandboxClient });
|
|
105
|
+
}
|
|
106
|
+
const resolveFleet = args.resolveFleet ?? defaultResolveFleet;
|
|
107
|
+
const fleet = await resolveFleet(args.sandboxClient, fleetId);
|
|
108
|
+
const excludeMachineIds = parseList(env.TANGLE_FLEET_EXCLUDE_MACHINES);
|
|
109
|
+
return createFleetWorkspaceExecutor({
|
|
110
|
+
fleet,
|
|
111
|
+
excludeMachineIds
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
async function defaultResolveFleet(sandboxClient, fleetId) {
|
|
115
|
+
const fleets = sandboxClient.fleets;
|
|
116
|
+
if (!fleets || typeof fleets.get !== "function") {
|
|
117
|
+
throw new Error(
|
|
118
|
+
"agent-runtime-mcp: the configured sandbox client does not expose `.fleets.get`; upgrade @tangle-network/sandbox to >= 0.2.1 or unset TANGLE_FLEET_ID."
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
const raw = await fleets.get(fleetId);
|
|
122
|
+
if (!raw || typeof raw !== "object") {
|
|
123
|
+
throw new Error(`agent-runtime-mcp: fleets.get(${fleetId}) returned no handle`);
|
|
124
|
+
}
|
|
125
|
+
const handle = raw;
|
|
126
|
+
if (typeof handle.fleetId !== "string" || !Array.isArray(handle.ids)) {
|
|
127
|
+
throw new Error(
|
|
128
|
+
`agent-runtime-mcp: fleet handle for ${fleetId} is missing fleetId/ids \u2014 incompatible sandbox SDK shape`
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
if (typeof handle.sandbox !== "function") {
|
|
132
|
+
throw new Error(
|
|
133
|
+
`agent-runtime-mcp: fleet handle for ${fleetId} is missing sandbox(machineId) \u2014 incompatible sandbox SDK shape`
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
return handle;
|
|
137
|
+
}
|
|
138
|
+
function parseFleetId(raw) {
|
|
139
|
+
if (typeof raw !== "string") return void 0;
|
|
140
|
+
const trimmed = raw.trim();
|
|
141
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
142
|
+
}
|
|
143
|
+
function parseList(raw) {
|
|
144
|
+
if (!raw) return void 0;
|
|
145
|
+
const list = raw.split(",").map((entry) => entry.trim()).filter(Boolean);
|
|
146
|
+
return list.length > 0 ? list : void 0;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// src/mcp/delegates.ts
|
|
150
|
+
function createDefaultCoderDelegate(options) {
|
|
151
|
+
const executor = resolveExecutor(options);
|
|
152
|
+
const sandboxClient = executor.client;
|
|
153
|
+
const fanoutHarnesses = options.fanoutHarnesses;
|
|
154
|
+
const maxConcurrency = options.maxConcurrency ?? 4;
|
|
155
|
+
return async (args, ctx) => {
|
|
156
|
+
const task = {
|
|
157
|
+
goal: buildCoderGoal(args),
|
|
158
|
+
repoRoot: args.repoRoot,
|
|
159
|
+
testCmd: args.config?.testCmd,
|
|
160
|
+
typecheckCmd: args.config?.typecheckCmd,
|
|
161
|
+
forbiddenPaths: args.config?.forbiddenPaths,
|
|
162
|
+
maxDiffLines: args.config?.maxDiffLines
|
|
163
|
+
};
|
|
164
|
+
const variants = Math.max(1, Math.trunc(args.variants ?? 1));
|
|
165
|
+
ctx.report({ iteration: 0, phase: "starting" });
|
|
166
|
+
if (variants <= 1) {
|
|
167
|
+
const { agentRunSpec, output, validator } = coderProfile({ task });
|
|
168
|
+
const result2 = await runLoop({
|
|
169
|
+
driver: singleShotDriver,
|
|
170
|
+
agentRun: agentRunSpec,
|
|
171
|
+
output,
|
|
172
|
+
validator,
|
|
173
|
+
task,
|
|
174
|
+
ctx: { sandboxClient, signal: ctx.signal },
|
|
175
|
+
maxIterations: 1,
|
|
176
|
+
maxConcurrency
|
|
177
|
+
});
|
|
178
|
+
const winner2 = result2.winner;
|
|
179
|
+
if (!winner2) {
|
|
180
|
+
throw new Error("coder delegate produced no winner");
|
|
181
|
+
}
|
|
182
|
+
ctx.report({ iteration: 1, phase: "completed" });
|
|
183
|
+
return winner2.output;
|
|
184
|
+
}
|
|
185
|
+
const fanout = multiHarnessCoderFanout(
|
|
186
|
+
fanoutHarnesses && fanoutHarnesses.length > 0 ? { harnesses: fanoutHarnesses.slice(0, variants) } : { harnesses: void 0 }
|
|
187
|
+
);
|
|
188
|
+
const agentRuns = fanout.agentRuns.slice(0, variants);
|
|
189
|
+
const result = await runLoop({
|
|
190
|
+
driver: fanout.driver,
|
|
191
|
+
agentRuns,
|
|
192
|
+
output: fanout.output,
|
|
193
|
+
validator: fanout.validator,
|
|
194
|
+
task,
|
|
195
|
+
ctx: { sandboxClient, signal: ctx.signal },
|
|
196
|
+
maxIterations: variants,
|
|
197
|
+
maxConcurrency: Math.min(maxConcurrency, variants)
|
|
198
|
+
});
|
|
199
|
+
const winner = result.winner;
|
|
200
|
+
if (!winner) {
|
|
201
|
+
throw new Error("coder delegate fanout produced no winner");
|
|
202
|
+
}
|
|
203
|
+
ctx.report({ iteration: agentRuns.length, phase: "completed" });
|
|
204
|
+
return winner.output;
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
function buildCoderGoal(args) {
|
|
208
|
+
if (!args.contextHint) return args.goal;
|
|
209
|
+
return [args.goal, "", "## Context", args.contextHint].join("\n");
|
|
210
|
+
}
|
|
211
|
+
function resolveExecutor(options) {
|
|
212
|
+
if (options.executor && options.sandboxClient) {
|
|
213
|
+
throw new Error("createDefaultCoderDelegate: pass exactly one of `executor` or `sandboxClient`");
|
|
214
|
+
}
|
|
215
|
+
if (options.executor) return options.executor;
|
|
216
|
+
if (options.sandboxClient) {
|
|
217
|
+
return createSiblingSandboxExecutor({ client: options.sandboxClient });
|
|
218
|
+
}
|
|
219
|
+
throw new Error("createDefaultCoderDelegate: `executor` or `sandboxClient` is required");
|
|
220
|
+
}
|
|
221
|
+
var singleShotDriver = {
|
|
222
|
+
name: "mcp-single-shot",
|
|
223
|
+
async plan(task, history) {
|
|
224
|
+
return history.length === 0 ? [task] : [];
|
|
225
|
+
},
|
|
226
|
+
decide(history) {
|
|
227
|
+
return history.length > 0 ? "pick-winner" : "fail";
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
// src/mcp/server.ts
|
|
232
|
+
import { createInterface } from "readline";
|
|
233
|
+
import { Readable, Writable } from "stream";
|
|
234
|
+
var PROTOCOL_VERSION = "2024-11-05";
|
|
235
|
+
var DEFAULT_SERVER_NAME = "agent-runtime-mcp";
|
|
236
|
+
var DEFAULT_SERVER_VERSION = "0.22.0";
|
|
237
|
+
function createMcpServer(options = {}) {
|
|
238
|
+
const queue = options.queue ?? new DelegationTaskQueue();
|
|
239
|
+
const feedbackStore = options.feedbackStore ?? new InMemoryFeedbackStore();
|
|
240
|
+
const serverName = options.serverName ?? DEFAULT_SERVER_NAME;
|
|
241
|
+
const serverVersion = options.serverVersion ?? DEFAULT_SERVER_VERSION;
|
|
242
|
+
const tools = /* @__PURE__ */ new Map();
|
|
243
|
+
if (options.coderDelegate) {
|
|
244
|
+
tools.set(DELEGATE_CODE_TOOL_NAME, {
|
|
245
|
+
name: DELEGATE_CODE_TOOL_NAME,
|
|
246
|
+
description: DELEGATE_CODE_DESCRIPTION,
|
|
247
|
+
inputSchema: DELEGATE_CODE_INPUT_SCHEMA,
|
|
248
|
+
handler: createDelegateCodeHandler({ queue, delegate: options.coderDelegate })
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
if (options.researcherDelegate) {
|
|
252
|
+
tools.set(DELEGATE_RESEARCH_TOOL_NAME, {
|
|
253
|
+
name: DELEGATE_RESEARCH_TOOL_NAME,
|
|
254
|
+
description: DELEGATE_RESEARCH_DESCRIPTION,
|
|
255
|
+
inputSchema: DELEGATE_RESEARCH_INPUT_SCHEMA,
|
|
256
|
+
handler: createDelegateResearchHandler({ queue, delegate: options.researcherDelegate })
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
tools.set(DELEGATE_FEEDBACK_TOOL_NAME, {
|
|
260
|
+
name: DELEGATE_FEEDBACK_TOOL_NAME,
|
|
261
|
+
description: DELEGATE_FEEDBACK_DESCRIPTION,
|
|
262
|
+
inputSchema: DELEGATE_FEEDBACK_INPUT_SCHEMA,
|
|
263
|
+
handler: createDelegateFeedbackHandler({ queue, store: feedbackStore })
|
|
264
|
+
});
|
|
265
|
+
tools.set(DELEGATION_STATUS_TOOL_NAME, {
|
|
266
|
+
name: DELEGATION_STATUS_TOOL_NAME,
|
|
267
|
+
description: DELEGATION_STATUS_DESCRIPTION,
|
|
268
|
+
inputSchema: DELEGATION_STATUS_INPUT_SCHEMA,
|
|
269
|
+
handler: createDelegationStatusHandler({ queue })
|
|
270
|
+
});
|
|
271
|
+
tools.set(DELEGATION_HISTORY_TOOL_NAME, {
|
|
272
|
+
name: DELEGATION_HISTORY_TOOL_NAME,
|
|
273
|
+
description: DELEGATION_HISTORY_DESCRIPTION,
|
|
274
|
+
inputSchema: DELEGATION_HISTORY_INPUT_SCHEMA,
|
|
275
|
+
handler: createDelegationHistoryHandler({ queue })
|
|
276
|
+
});
|
|
277
|
+
let stopped = false;
|
|
278
|
+
let activeReadline;
|
|
279
|
+
async function handle(message) {
|
|
280
|
+
if (stopped) {
|
|
281
|
+
return rpcError(message.id ?? null, -32099, "server stopped");
|
|
282
|
+
}
|
|
283
|
+
if (message.method === "initialize") {
|
|
284
|
+
return rpcResult(message.id ?? null, {
|
|
285
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
286
|
+
capabilities: { tools: {} },
|
|
287
|
+
serverInfo: { name: serverName, version: serverVersion }
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
if (message.method === "notifications/initialized") {
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
if (message.method === "tools/list") {
|
|
294
|
+
return rpcResult(message.id ?? null, {
|
|
295
|
+
tools: [...tools.values()].map((tool) => ({
|
|
296
|
+
name: tool.name,
|
|
297
|
+
description: tool.description,
|
|
298
|
+
inputSchema: tool.inputSchema
|
|
299
|
+
}))
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
if (message.method === "tools/call") {
|
|
303
|
+
const params = message.params ?? {};
|
|
304
|
+
const name = typeof params.name === "string" ? params.name : "";
|
|
305
|
+
const tool = tools.get(name);
|
|
306
|
+
if (!tool) {
|
|
307
|
+
return rpcError(message.id ?? null, -32601, `unknown tool: ${name}`);
|
|
308
|
+
}
|
|
309
|
+
try {
|
|
310
|
+
const output = await tool.handler(params.arguments ?? {});
|
|
311
|
+
return rpcResult(message.id ?? null, {
|
|
312
|
+
content: [{ type: "text", text: JSON.stringify(output) }],
|
|
313
|
+
structuredContent: output,
|
|
314
|
+
isError: false
|
|
315
|
+
});
|
|
316
|
+
} catch (err) {
|
|
317
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
318
|
+
const code = err instanceof TypeError || err instanceof RangeError ? -32602 : -32e3;
|
|
319
|
+
return rpcError(message.id ?? null, code, reason);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
if (message.id === void 0 || message.id === null) return null;
|
|
323
|
+
return rpcError(message.id, -32601, `unknown method: ${message.method}`);
|
|
324
|
+
}
|
|
325
|
+
async function serve(transport) {
|
|
326
|
+
const input = transport?.input ?? process.stdin;
|
|
327
|
+
const output = transport?.output ?? process.stdout;
|
|
328
|
+
const rl = createInterface({ input, crlfDelay: Number.POSITIVE_INFINITY });
|
|
329
|
+
activeReadline = rl;
|
|
330
|
+
return new Promise((resolve, reject) => {
|
|
331
|
+
rl.on("line", (line) => {
|
|
332
|
+
const trimmed = line.trim();
|
|
333
|
+
if (!trimmed) return;
|
|
334
|
+
let parsed;
|
|
335
|
+
try {
|
|
336
|
+
parsed = JSON.parse(trimmed);
|
|
337
|
+
} catch (err) {
|
|
338
|
+
writeResponse(output, rpcError(null, -32700, `parse error: ${err.message}`));
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
if (!parsed || parsed.jsonrpc !== "2.0" || typeof parsed.method !== "string") {
|
|
342
|
+
writeResponse(output, rpcError(parsed?.id ?? null, -32600, "invalid request"));
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
void handle(parsed).then((response) => {
|
|
346
|
+
if (response) writeResponse(output, response);
|
|
347
|
+
});
|
|
348
|
+
});
|
|
349
|
+
rl.on("close", () => resolve());
|
|
350
|
+
rl.on("error", (err) => reject(err));
|
|
351
|
+
if (stopped) {
|
|
352
|
+
rl.close();
|
|
353
|
+
resolve();
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
function stop() {
|
|
358
|
+
stopped = true;
|
|
359
|
+
activeReadline?.close();
|
|
360
|
+
activeReadline = void 0;
|
|
361
|
+
}
|
|
362
|
+
return {
|
|
363
|
+
tools,
|
|
364
|
+
queue,
|
|
365
|
+
feedbackStore,
|
|
366
|
+
handle,
|
|
367
|
+
serve,
|
|
368
|
+
stop
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
function rpcResult(id, result) {
|
|
372
|
+
return { jsonrpc: "2.0", id, result };
|
|
373
|
+
}
|
|
374
|
+
function rpcError(id, code, message, data) {
|
|
375
|
+
return {
|
|
376
|
+
jsonrpc: "2.0",
|
|
377
|
+
id,
|
|
378
|
+
error: data === void 0 ? { code, message } : { code, message, data }
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
function writeResponse(output, response) {
|
|
382
|
+
output.write(`${JSON.stringify(response)}
|
|
383
|
+
`);
|
|
384
|
+
}
|
|
385
|
+
function createInProcessTransport() {
|
|
386
|
+
const responses = [];
|
|
387
|
+
const input = new Readable({ read() {
|
|
388
|
+
} });
|
|
389
|
+
const output = new Writable({
|
|
390
|
+
write(chunk, _enc, cb) {
|
|
391
|
+
const text = chunk.toString("utf8");
|
|
392
|
+
for (const line of text.split("\n")) {
|
|
393
|
+
const trimmed = line.trim();
|
|
394
|
+
if (!trimmed) continue;
|
|
395
|
+
try {
|
|
396
|
+
responses.push(JSON.parse(trimmed));
|
|
397
|
+
} catch {
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
cb();
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
return {
|
|
404
|
+
transport: { input, output },
|
|
405
|
+
clientWrite(line) {
|
|
406
|
+
input.push(`${line}
|
|
407
|
+
`);
|
|
408
|
+
},
|
|
409
|
+
clientClose() {
|
|
410
|
+
input.push(null);
|
|
411
|
+
},
|
|
412
|
+
async readServer() {
|
|
413
|
+
for (let i = 0; i < 5; i += 1) await new Promise((r) => setImmediate(r));
|
|
414
|
+
return [...responses];
|
|
415
|
+
}
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
export {
|
|
420
|
+
createSiblingSandboxExecutor,
|
|
421
|
+
createFleetWorkspaceExecutor,
|
|
422
|
+
detectExecutor,
|
|
423
|
+
createDefaultCoderDelegate,
|
|
424
|
+
createMcpServer,
|
|
425
|
+
createInProcessTransport
|
|
426
|
+
};
|
|
427
|
+
//# sourceMappingURL=chunk-IQHYOJU3.js.map
|