@ai-sdk/harness-codex 1.0.39 → 1.0.41
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/CHANGELOG.md +18 -0
- package/dist/bridge/index.mjs +2 -41
- package/dist/bridge/index.mjs.map +1 -1
- package/dist/bridge/package.json +2 -4
- package/dist/bridge/pnpm-lock.yaml +34 -803
- package/dist/index.js +5 -10
- package/dist/index.js.map +1 -1
- package/package.json +4 -5
- package/src/bridge/index.ts +16 -70
- package/src/bridge/package.json +2 -4
- package/src/bridge/pnpm-lock.yaml +34 -803
- package/src/codex-harness.ts +2 -7
- package/dist/bridge/host-tool-mcp.mjs +0 -103
- package/dist/bridge/host-tool-mcp.mjs.map +0 -1
- package/src/bridge/host-tool-mcp.ts +0 -160
package/src/codex-harness.ts
CHANGED
|
@@ -177,11 +177,10 @@ export function createCodex(
|
|
|
177
177
|
lifecycleStateSchema: codexResumeStateSchema,
|
|
178
178
|
getBootstrap: async () => {
|
|
179
179
|
if (cachedBootstrap != null) return cachedBootstrap;
|
|
180
|
-
const [pkg, lock, bridge
|
|
180
|
+
const [pkg, lock, bridge] = await Promise.all([
|
|
181
181
|
readBridgeAsset('package.json'),
|
|
182
182
|
readBridgeAsset('pnpm-lock.yaml'),
|
|
183
183
|
readBridgeAsset('index.mjs'),
|
|
184
|
-
readBridgeAsset('host-tool-mcp.mjs'),
|
|
185
184
|
]);
|
|
186
185
|
cachedBootstrap = {
|
|
187
186
|
harnessId: 'codex',
|
|
@@ -190,10 +189,6 @@ export function createCodex(
|
|
|
190
189
|
{ path: `${BOOTSTRAP_DIR}/package.json`, content: pkg },
|
|
191
190
|
{ path: `${BOOTSTRAP_DIR}/pnpm-lock.yaml`, content: lock },
|
|
192
191
|
{ path: `${BOOTSTRAP_DIR}/bridge.mjs`, content: bridge },
|
|
193
|
-
{
|
|
194
|
-
path: `${BOOTSTRAP_DIR}/host-tool-mcp.mjs`,
|
|
195
|
-
content: hostToolMcp,
|
|
196
|
-
},
|
|
197
192
|
],
|
|
198
193
|
commands: [
|
|
199
194
|
{ command: `mkdir -p ${BOOTSTRAP_DIR}` },
|
|
@@ -378,7 +373,7 @@ export function createCodex(
|
|
|
378
373
|
});
|
|
379
374
|
|
|
380
375
|
const proc = await session.spawn({
|
|
381
|
-
command: `node ${BOOTSTRAP_DIR}/bridge.mjs --workdir ${shellQuote(workDir)} --bridge-state-dir ${shellQuote(bridgeStateDir)} --
|
|
376
|
+
command: `node ${BOOTSTRAP_DIR}/bridge.mjs --workdir ${shellQuote(workDir)} --bridge-state-dir ${shellQuote(bridgeStateDir)} --cli-shim-dir ${shellQuote(cliShimDir)}`,
|
|
382
377
|
env,
|
|
383
378
|
abortSignal: startOpts.abortSignal,
|
|
384
379
|
});
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// src/bridge/host-tool-mcp.ts
|
|
4
|
-
import * as mcpServerModule from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
|
-
import * as mcpStdioModule from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
-
import { z } from "zod/v4";
|
|
7
|
-
var { McpServer } = mcpServerModule;
|
|
8
|
-
var { StdioServerTransport } = mcpStdioModule;
|
|
9
|
-
var schemas = JSON.parse(process.env.TOOL_SCHEMAS || "[]");
|
|
10
|
-
var relayUrl = process.env.TOOL_RELAY_URL || "";
|
|
11
|
-
if (!schemas.length || !relayUrl) {
|
|
12
|
-
process.stderr.write(
|
|
13
|
-
"[host-tool-mcp] Missing TOOL_SCHEMAS or TOOL_RELAY_URL; exiting\n"
|
|
14
|
-
);
|
|
15
|
-
process.exit(0);
|
|
16
|
-
}
|
|
17
|
-
var server = new McpServer({ name: "harness-tools", version: "1.0.0" });
|
|
18
|
-
for (const schema of schemas) {
|
|
19
|
-
const shape = toZodShape(schema.inputSchema);
|
|
20
|
-
server.tool(
|
|
21
|
-
schema.name,
|
|
22
|
-
schema.description ?? "",
|
|
23
|
-
shape,
|
|
24
|
-
async (input) => {
|
|
25
|
-
const requestId = crypto.randomUUID();
|
|
26
|
-
try {
|
|
27
|
-
const res = await fetch(relayUrl, {
|
|
28
|
-
method: "POST",
|
|
29
|
-
headers: {
|
|
30
|
-
"Content-Type": "application/json"
|
|
31
|
-
},
|
|
32
|
-
body: JSON.stringify({ requestId, toolName: schema.name, input })
|
|
33
|
-
});
|
|
34
|
-
if (!res.ok) {
|
|
35
|
-
const body = await res.text();
|
|
36
|
-
throw new Error(
|
|
37
|
-
`Tool relay ${schema.name} failed with ${res.status}: ${body.slice(0, 500)}`
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
|
-
const data = await res.json();
|
|
41
|
-
return {
|
|
42
|
-
content: [
|
|
43
|
-
{
|
|
44
|
-
type: "text",
|
|
45
|
-
text: JSON.stringify(data.result ?? null)
|
|
46
|
-
}
|
|
47
|
-
]
|
|
48
|
-
};
|
|
49
|
-
} catch (err) {
|
|
50
|
-
return {
|
|
51
|
-
content: [{ type: "text", text: `Error: ${String(err)}` }],
|
|
52
|
-
isError: true
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
);
|
|
57
|
-
}
|
|
58
|
-
function toZodShape(schema) {
|
|
59
|
-
if (!schema?.properties) return {};
|
|
60
|
-
const required = new Set(schema.required ?? []);
|
|
61
|
-
const shape = {};
|
|
62
|
-
for (const [key, propSchema] of Object.entries(schema.properties)) {
|
|
63
|
-
const propType = toZodType(propSchema);
|
|
64
|
-
shape[key] = required.has(key) ? propType : propType.optional();
|
|
65
|
-
}
|
|
66
|
-
return shape;
|
|
67
|
-
}
|
|
68
|
-
function toZodType(schema) {
|
|
69
|
-
if (!schema) return z.any();
|
|
70
|
-
const types = Array.isArray(schema.type) ? schema.type.filter((t) => t !== "null") : [schema.type].filter(Boolean);
|
|
71
|
-
let zType;
|
|
72
|
-
switch (types[0]) {
|
|
73
|
-
case "string":
|
|
74
|
-
zType = z.string();
|
|
75
|
-
break;
|
|
76
|
-
case "number":
|
|
77
|
-
zType = z.number();
|
|
78
|
-
break;
|
|
79
|
-
case "integer":
|
|
80
|
-
zType = z.number().int();
|
|
81
|
-
break;
|
|
82
|
-
case "boolean":
|
|
83
|
-
zType = z.boolean();
|
|
84
|
-
break;
|
|
85
|
-
case "array":
|
|
86
|
-
zType = z.array(toZodType(schema.items));
|
|
87
|
-
break;
|
|
88
|
-
case "object":
|
|
89
|
-
zType = z.object(toZodShape(schema));
|
|
90
|
-
break;
|
|
91
|
-
case "null":
|
|
92
|
-
zType = z.null();
|
|
93
|
-
break;
|
|
94
|
-
default:
|
|
95
|
-
zType = z.any();
|
|
96
|
-
}
|
|
97
|
-
if (schema.description) zType = zType.describe(schema.description);
|
|
98
|
-
if (schema.nullable) zType = zType.nullable();
|
|
99
|
-
return zType;
|
|
100
|
-
}
|
|
101
|
-
var transport = new StdioServerTransport();
|
|
102
|
-
await server.connect(transport);
|
|
103
|
-
//# sourceMappingURL=host-tool-mcp.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/bridge/host-tool-mcp.ts"],"sourcesContent":["#!/usr/bin/env node\n// MCP-stdio tool server spawned by the codex CLI when\n// `mcp_servers.harness-tools` is configured. Exposes host-defined tools\n// over MCP-stdio and round-trips each call to the bridge's HTTP relay.\n//\n// Env vars (set by the bridge when starting a turn):\n// TOOL_SCHEMAS — JSON array of { name, description, inputSchema }\n// TOOL_RELAY_URL — http://127.0.0.1:<port> of the bridge relay server\n// Relay authorization is issued by bridge runtime events, not an env token.\n\n/*\n * CONSTRAINT — the third-party imports below are NEVER bundled into the\n * compiled `bridge/host-tool-mcp.mjs`. They are declared `external` in\n * tsup.config.ts and resolved at runtime from the node_modules that the\n * bridge installs *inside the sandbox* from `src/bridge/package.json` (and\n * its pinned `pnpm-lock.yaml`). That bridge package.json — NOT this host\n * package — is the single source of truth for these packages and their\n * versions; the published `@ai-sdk/harness-codex` package does not provide\n * them at runtime.\n *\n * When adding or changing a third-party import here you MUST keep all three\n * in sync, or this server will either get the dependency bundled in or fail\n * to resolve it in the sandbox:\n * 1. the import statement below,\n * 2. the `external` array in tsup.config.ts, and\n * 3. the dependency entry in `src/bridge/package.json`.\n */\nimport * as mcpServerModule from '@modelcontextprotocol/sdk/server/mcp.js';\nimport * as mcpStdioModule from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { z } from 'zod/v4';\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst { McpServer } = mcpServerModule as any;\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst { StdioServerTransport } = mcpStdioModule as any;\n\ntype ToolSchema = {\n name: string;\n description?: string;\n inputSchema?: JsonSchemaObject;\n};\n\ntype JsonSchemaObject = {\n type?: string | string[];\n description?: string;\n properties?: Record<string, JsonSchemaObject>;\n required?: string[];\n items?: JsonSchemaObject;\n enum?: unknown[];\n const?: unknown;\n oneOf?: JsonSchemaObject[];\n anyOf?: JsonSchemaObject[];\n additionalProperties?: boolean | JsonSchemaObject;\n nullable?: boolean;\n};\n\nconst schemas: ToolSchema[] = JSON.parse(process.env.TOOL_SCHEMAS || '[]');\nconst relayUrl = process.env.TOOL_RELAY_URL || '';\n\nif (!schemas.length || !relayUrl) {\n process.stderr.write(\n '[host-tool-mcp] Missing TOOL_SCHEMAS or TOOL_RELAY_URL; exiting\\n',\n );\n process.exit(0);\n}\n\nconst server = new McpServer({ name: 'harness-tools', version: '1.0.0' });\n\nfor (const schema of schemas) {\n const shape = toZodShape(schema.inputSchema);\n server.tool(\n schema.name,\n schema.description ?? '',\n shape,\n async (input: Record<string, unknown>) => {\n const requestId = crypto.randomUUID();\n try {\n const res = await fetch(relayUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ requestId, toolName: schema.name, input }),\n });\n if (!res.ok) {\n const body = await res.text();\n throw new Error(\n `Tool relay ${schema.name} failed with ${res.status}: ${body.slice(0, 500)}`,\n );\n }\n const data = (await res.json()) as { result?: unknown };\n return {\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify(data.result ?? null),\n },\n ],\n };\n } catch (err) {\n return {\n content: [{ type: 'text' as const, text: `Error: ${String(err)}` }],\n isError: true,\n };\n }\n },\n );\n}\n\nfunction toZodShape(\n schema: JsonSchemaObject | undefined,\n): Record<string, z.ZodTypeAny> {\n if (!schema?.properties) return {};\n const required = new Set(schema.required ?? []);\n const shape: Record<string, z.ZodTypeAny> = {};\n for (const [key, propSchema] of Object.entries(schema.properties)) {\n const propType = toZodType(propSchema);\n shape[key] = required.has(key) ? propType : propType.optional();\n }\n return shape;\n}\n\nfunction toZodType(schema: JsonSchemaObject | undefined): z.ZodTypeAny {\n if (!schema) return z.any();\n const types = Array.isArray(schema.type)\n ? schema.type.filter((t): t is string => t !== 'null')\n : ([schema.type].filter(Boolean) as string[]);\n let zType: z.ZodTypeAny;\n switch (types[0]) {\n case 'string':\n zType = z.string();\n break;\n case 'number':\n zType = z.number();\n break;\n case 'integer':\n zType = z.number().int();\n break;\n case 'boolean':\n zType = z.boolean();\n break;\n case 'array':\n zType = z.array(toZodType(schema.items));\n break;\n case 'object':\n zType = z.object(toZodShape(schema));\n break;\n case 'null':\n zType = z.null();\n break;\n default:\n zType = z.any();\n }\n if (schema.description) zType = zType.describe(schema.description);\n if (schema.nullable) zType = zType.nullable();\n return zType;\n}\n\nconst transport = new StdioServerTransport();\nawait server.connect(transport);\n"],"mappings":"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","names":[]}
|
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// MCP-stdio tool server spawned by the codex CLI when
|
|
3
|
-
// `mcp_servers.harness-tools` is configured. Exposes host-defined tools
|
|
4
|
-
// over MCP-stdio and round-trips each call to the bridge's HTTP relay.
|
|
5
|
-
//
|
|
6
|
-
// Env vars (set by the bridge when starting a turn):
|
|
7
|
-
// TOOL_SCHEMAS — JSON array of { name, description, inputSchema }
|
|
8
|
-
// TOOL_RELAY_URL — http://127.0.0.1:<port> of the bridge relay server
|
|
9
|
-
// Relay authorization is issued by bridge runtime events, not an env token.
|
|
10
|
-
|
|
11
|
-
/*
|
|
12
|
-
* CONSTRAINT — the third-party imports below are NEVER bundled into the
|
|
13
|
-
* compiled `bridge/host-tool-mcp.mjs`. They are declared `external` in
|
|
14
|
-
* tsup.config.ts and resolved at runtime from the node_modules that the
|
|
15
|
-
* bridge installs *inside the sandbox* from `src/bridge/package.json` (and
|
|
16
|
-
* its pinned `pnpm-lock.yaml`). That bridge package.json — NOT this host
|
|
17
|
-
* package — is the single source of truth for these packages and their
|
|
18
|
-
* versions; the published `@ai-sdk/harness-codex` package does not provide
|
|
19
|
-
* them at runtime.
|
|
20
|
-
*
|
|
21
|
-
* When adding or changing a third-party import here you MUST keep all three
|
|
22
|
-
* in sync, or this server will either get the dependency bundled in or fail
|
|
23
|
-
* to resolve it in the sandbox:
|
|
24
|
-
* 1. the import statement below,
|
|
25
|
-
* 2. the `external` array in tsup.config.ts, and
|
|
26
|
-
* 3. the dependency entry in `src/bridge/package.json`.
|
|
27
|
-
*/
|
|
28
|
-
import * as mcpServerModule from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
29
|
-
import * as mcpStdioModule from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
30
|
-
import { z } from 'zod/v4';
|
|
31
|
-
|
|
32
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
33
|
-
const { McpServer } = mcpServerModule as any;
|
|
34
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
35
|
-
const { StdioServerTransport } = mcpStdioModule as any;
|
|
36
|
-
|
|
37
|
-
type ToolSchema = {
|
|
38
|
-
name: string;
|
|
39
|
-
description?: string;
|
|
40
|
-
inputSchema?: JsonSchemaObject;
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
type JsonSchemaObject = {
|
|
44
|
-
type?: string | string[];
|
|
45
|
-
description?: string;
|
|
46
|
-
properties?: Record<string, JsonSchemaObject>;
|
|
47
|
-
required?: string[];
|
|
48
|
-
items?: JsonSchemaObject;
|
|
49
|
-
enum?: unknown[];
|
|
50
|
-
const?: unknown;
|
|
51
|
-
oneOf?: JsonSchemaObject[];
|
|
52
|
-
anyOf?: JsonSchemaObject[];
|
|
53
|
-
additionalProperties?: boolean | JsonSchemaObject;
|
|
54
|
-
nullable?: boolean;
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
const schemas: ToolSchema[] = JSON.parse(process.env.TOOL_SCHEMAS || '[]');
|
|
58
|
-
const relayUrl = process.env.TOOL_RELAY_URL || '';
|
|
59
|
-
|
|
60
|
-
if (!schemas.length || !relayUrl) {
|
|
61
|
-
process.stderr.write(
|
|
62
|
-
'[host-tool-mcp] Missing TOOL_SCHEMAS or TOOL_RELAY_URL; exiting\n',
|
|
63
|
-
);
|
|
64
|
-
process.exit(0);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const server = new McpServer({ name: 'harness-tools', version: '1.0.0' });
|
|
68
|
-
|
|
69
|
-
for (const schema of schemas) {
|
|
70
|
-
const shape = toZodShape(schema.inputSchema);
|
|
71
|
-
server.tool(
|
|
72
|
-
schema.name,
|
|
73
|
-
schema.description ?? '',
|
|
74
|
-
shape,
|
|
75
|
-
async (input: Record<string, unknown>) => {
|
|
76
|
-
const requestId = crypto.randomUUID();
|
|
77
|
-
try {
|
|
78
|
-
const res = await fetch(relayUrl, {
|
|
79
|
-
method: 'POST',
|
|
80
|
-
headers: {
|
|
81
|
-
'Content-Type': 'application/json',
|
|
82
|
-
},
|
|
83
|
-
body: JSON.stringify({ requestId, toolName: schema.name, input }),
|
|
84
|
-
});
|
|
85
|
-
if (!res.ok) {
|
|
86
|
-
const body = await res.text();
|
|
87
|
-
throw new Error(
|
|
88
|
-
`Tool relay ${schema.name} failed with ${res.status}: ${body.slice(0, 500)}`,
|
|
89
|
-
);
|
|
90
|
-
}
|
|
91
|
-
const data = (await res.json()) as { result?: unknown };
|
|
92
|
-
return {
|
|
93
|
-
content: [
|
|
94
|
-
{
|
|
95
|
-
type: 'text' as const,
|
|
96
|
-
text: JSON.stringify(data.result ?? null),
|
|
97
|
-
},
|
|
98
|
-
],
|
|
99
|
-
};
|
|
100
|
-
} catch (err) {
|
|
101
|
-
return {
|
|
102
|
-
content: [{ type: 'text' as const, text: `Error: ${String(err)}` }],
|
|
103
|
-
isError: true,
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
},
|
|
107
|
-
);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
function toZodShape(
|
|
111
|
-
schema: JsonSchemaObject | undefined,
|
|
112
|
-
): Record<string, z.ZodTypeAny> {
|
|
113
|
-
if (!schema?.properties) return {};
|
|
114
|
-
const required = new Set(schema.required ?? []);
|
|
115
|
-
const shape: Record<string, z.ZodTypeAny> = {};
|
|
116
|
-
for (const [key, propSchema] of Object.entries(schema.properties)) {
|
|
117
|
-
const propType = toZodType(propSchema);
|
|
118
|
-
shape[key] = required.has(key) ? propType : propType.optional();
|
|
119
|
-
}
|
|
120
|
-
return shape;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
function toZodType(schema: JsonSchemaObject | undefined): z.ZodTypeAny {
|
|
124
|
-
if (!schema) return z.any();
|
|
125
|
-
const types = Array.isArray(schema.type)
|
|
126
|
-
? schema.type.filter((t): t is string => t !== 'null')
|
|
127
|
-
: ([schema.type].filter(Boolean) as string[]);
|
|
128
|
-
let zType: z.ZodTypeAny;
|
|
129
|
-
switch (types[0]) {
|
|
130
|
-
case 'string':
|
|
131
|
-
zType = z.string();
|
|
132
|
-
break;
|
|
133
|
-
case 'number':
|
|
134
|
-
zType = z.number();
|
|
135
|
-
break;
|
|
136
|
-
case 'integer':
|
|
137
|
-
zType = z.number().int();
|
|
138
|
-
break;
|
|
139
|
-
case 'boolean':
|
|
140
|
-
zType = z.boolean();
|
|
141
|
-
break;
|
|
142
|
-
case 'array':
|
|
143
|
-
zType = z.array(toZodType(schema.items));
|
|
144
|
-
break;
|
|
145
|
-
case 'object':
|
|
146
|
-
zType = z.object(toZodShape(schema));
|
|
147
|
-
break;
|
|
148
|
-
case 'null':
|
|
149
|
-
zType = z.null();
|
|
150
|
-
break;
|
|
151
|
-
default:
|
|
152
|
-
zType = z.any();
|
|
153
|
-
}
|
|
154
|
-
if (schema.description) zType = zType.describe(schema.description);
|
|
155
|
-
if (schema.nullable) zType = zType.nullable();
|
|
156
|
-
return zType;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
const transport = new StdioServerTransport();
|
|
160
|
-
await server.connect(transport);
|