@tangle-network/agent-profile-materialize 0.22.2 → 0.22.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/managed-workspace-bind.js +20 -10
- package/dist/mcp.d.ts +13 -3
- package/dist/mcp.js +23 -5
- package/package.json +2 -1
|
@@ -1,4 +1,22 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
|
+
/**
|
|
3
|
+
* Paths OpenCode discovers relative to its native cwd: content-only resources,
|
|
4
|
+
* skills, and its standard agent and command definitions (OpenCode reads
|
|
5
|
+
* `.opencode/{agent,agents}/**\/*.md` and `.opencode/{command,commands}/**\/*.md`).
|
|
6
|
+
*/
|
|
7
|
+
const SECRET_REVISION_NATIVE_PATH = /^(?:reference\/|\.opencode\/skills\/|\.opencode\/(?:agents?|commands?)\/(?:[^./][^/]*\/)*[^./][^/]*\.md$)/u;
|
|
8
|
+
/**
|
|
9
|
+
* A secret OpenCode revision runs OpenCode from its immutable, digest-attested
|
|
10
|
+
* revision tree, so a file at one of OpenCode's own discovery paths is visible
|
|
11
|
+
* there without a shared-workspace write. The binding stays limited to
|
|
12
|
+
* OpenCode's standard paths, never an app's own directory.
|
|
13
|
+
*/
|
|
14
|
+
function ridesSecretRevision(plan, file, options) {
|
|
15
|
+
return (options.secretOpenCodeRevisionCwd === true &&
|
|
16
|
+
plan.harness === "opencode" &&
|
|
17
|
+
(file.root ?? "workspace") === "workspace" &&
|
|
18
|
+
SECRET_REVISION_NATIVE_PATH.test(file.relPath));
|
|
19
|
+
}
|
|
2
20
|
export function bindConcurrentLaunch(plan, application, revisionWorkspaceDir, options = {}) {
|
|
3
21
|
if (plan.requires?.length) {
|
|
4
22
|
throw new Error(`Concurrent ${plan.harness} profile needs a native extension that this launcher cannot attest`);
|
|
@@ -67,17 +85,9 @@ export function bindConcurrentLaunch(plan, application, revisionWorkspaceDir, op
|
|
|
67
85
|
}
|
|
68
86
|
}
|
|
69
87
|
for (const file of plan.files) {
|
|
88
|
+
if (ridesSecretRevision(plan, file, options))
|
|
89
|
+
continue;
|
|
70
90
|
if (file.source !== "generated") {
|
|
71
|
-
if (options.secretOpenCodeRevisionCwd &&
|
|
72
|
-
plan.harness === "opencode" &&
|
|
73
|
-
(file.root ?? "workspace") === "workspace" &&
|
|
74
|
-
(file.relPath.startsWith("reference/") ||
|
|
75
|
-
file.relPath.startsWith(".opencode/skills/"))) {
|
|
76
|
-
// The secret launcher uses this immutable revision tree as native cwd.
|
|
77
|
-
// These content-only resources are therefore visible without writing
|
|
78
|
-
// into the shared workspace or changing native authority config.
|
|
79
|
-
continue;
|
|
80
|
-
}
|
|
81
91
|
throw new Error(`Concurrent ${plan.harness} profile resource ${file.relPath} needs a shared-workspace collision-safe copy`);
|
|
82
92
|
}
|
|
83
93
|
if ((file.root ?? "workspace") !== "workspace" ||
|
package/dist/mcp.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export declare function materializeMcp(h: HarnessId, mcp: Record<string, ActiveA
|
|
|
3
3
|
* The header a harness sends to a remote MCP server whose tool-call timeout it
|
|
4
4
|
* raised, carrying the timeout it applied in milliseconds. A server may hold a
|
|
5
5
|
* call for nearly that long. A server that does not see the header must assume
|
|
6
|
-
* the harness default, 60 s for Claude Code and
|
|
6
|
+
* the harness default, 60 s for Claude Code, Codex and OpenCode.
|
|
7
7
|
*/
|
|
8
8
|
export declare const MCP_TOOL_TIMEOUT_HEADER = "X-Mcp-Tool-Timeout-Ms";
|
|
9
9
|
/** OpenCode-native MCP configuration used by workspace and startup paths. */
|
|
@@ -12,14 +12,24 @@ export type OpenCodeMcpConfig = Record<string, {
|
|
|
12
12
|
url: string;
|
|
13
13
|
enabled: boolean;
|
|
14
14
|
headers?: Record<string, AgentProfileConfigValue>;
|
|
15
|
+
/** Milliseconds; OpenCode bounds this server's connect, tool listing and tool calls by it. */
|
|
16
|
+
timeout?: number;
|
|
17
|
+
/** `false` turns off OpenCode's OAuth discovery for this server. */
|
|
18
|
+
oauth?: false;
|
|
15
19
|
} | {
|
|
16
20
|
type: "local";
|
|
17
21
|
command: Array<string | AgentProfileConfigValue>;
|
|
18
22
|
enabled: boolean;
|
|
19
23
|
environment?: Record<string, AgentProfileConfigValue>;
|
|
24
|
+
timeout?: number;
|
|
20
25
|
}>;
|
|
21
|
-
/**
|
|
22
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Lower canonical MCP definitions to OpenCode's native startup configuration.
|
|
28
|
+
* `toolTimeouts` holds the validated `metadata.toolTimeoutMs` of each server
|
|
29
|
+
* that declared one; without it OpenCode applies the MCP SDK's 60 s default to
|
|
30
|
+
* every tool call.
|
|
31
|
+
*/
|
|
32
|
+
export declare function openCodeMcpConfig(mcp: Record<string, AgentProfileMcpServer>, toolTimeouts?: ReadonlyMap<string, number>): OpenCodeMcpConfig;
|
|
23
33
|
import type { AgentProfileConfigValue, AgentProfileMcpServer } from "@tangle-network/agent-interface";
|
|
24
34
|
import type { AddPlanFile } from "./plan-json.js";
|
|
25
35
|
import type { HarnessId, Unsupported, WorkspacePlan } from "./plan-types.js";
|
package/dist/mcp.js
CHANGED
|
@@ -58,7 +58,7 @@ export function materializeMcp(h, mcp, plan, add, unsupported) {
|
|
|
58
58
|
// opencode.json "mcp" block (local|remote).
|
|
59
59
|
add(OPENCODE_CONFIG_FILE, serializeWorkspacePlanJson({
|
|
60
60
|
$schema: OPENCODE_CONFIG_SCHEMA,
|
|
61
|
-
mcp: openCodeMcpConfig(mcp),
|
|
61
|
+
mcp: openCodeMcpConfig(mcp, mcpToolTimeouts(mcp, h, unsupported)),
|
|
62
62
|
}));
|
|
63
63
|
break;
|
|
64
64
|
case "gemini": {
|
|
@@ -156,7 +156,7 @@ function tomlInlineStringTable(values) {
|
|
|
156
156
|
* The header a harness sends to a remote MCP server whose tool-call timeout it
|
|
157
157
|
* raised, carrying the timeout it applied in milliseconds. A server may hold a
|
|
158
158
|
* call for nearly that long. A server that does not see the header must assume
|
|
159
|
-
* the harness default, 60 s for Claude Code and
|
|
159
|
+
* the harness default, 60 s for Claude Code, Codex and OpenCode.
|
|
160
160
|
*/
|
|
161
161
|
export const MCP_TOOL_TIMEOUT_HEADER = "X-Mcp-Tool-Timeout-Ms";
|
|
162
162
|
const MIN_MCP_TOOL_TIMEOUT_MS = 1_000;
|
|
@@ -270,21 +270,38 @@ function kimiMcpServer(server) {
|
|
|
270
270
|
...(server.cwd ? { cwd: server.cwd } : {}),
|
|
271
271
|
};
|
|
272
272
|
}
|
|
273
|
-
|
|
274
|
-
|
|
273
|
+
const carriesAuthorization = (headers) => Object.keys(headers ?? {}).some((header) => header.toLowerCase() === "authorization");
|
|
274
|
+
/**
|
|
275
|
+
* Lower canonical MCP definitions to OpenCode's native startup configuration.
|
|
276
|
+
* `toolTimeouts` holds the validated `metadata.toolTimeoutMs` of each server
|
|
277
|
+
* that declared one; without it OpenCode applies the MCP SDK's 60 s default to
|
|
278
|
+
* every tool call.
|
|
279
|
+
*/
|
|
280
|
+
export function openCodeMcpConfig(mcp, toolTimeouts = new Map()) {
|
|
275
281
|
const out = {};
|
|
276
282
|
for (const [name, s] of Object.entries(mcp)) {
|
|
277
283
|
if (s.enabled === false)
|
|
278
284
|
continue;
|
|
285
|
+
// OpenCode also bounds connect and tool listing by this value, so a server
|
|
286
|
+
// that hangs on connect holds startup that long; a refused connection still
|
|
287
|
+
// fails at once.
|
|
288
|
+
const timeout = toolTimeouts.get(name);
|
|
279
289
|
if (s.url) {
|
|
280
290
|
if (s.transport === "sse") {
|
|
281
291
|
throw new Error(`OpenCode MCP server "${name}" uses unsupported SSE transport`);
|
|
282
292
|
}
|
|
293
|
+
const headers = withAppliedToolTimeout(s.headers, timeout);
|
|
283
294
|
out[name] = {
|
|
284
295
|
type: "remote",
|
|
285
296
|
url: s.url,
|
|
286
297
|
enabled: true,
|
|
287
|
-
...(
|
|
298
|
+
...(headers ? { headers } : {}),
|
|
299
|
+
...(timeout !== undefined ? { timeout } : {}),
|
|
300
|
+
// A server that takes its credential from these headers has no OAuth
|
|
301
|
+
// flow. With OAuth on, OpenCode answers its 401 with OAuth discovery at
|
|
302
|
+
// the server's origin, and the agent sees that discovery's failure
|
|
303
|
+
// instead of the server's refusal.
|
|
304
|
+
...(carriesAuthorization(s.headers) ? { oauth: false } : {}),
|
|
288
305
|
};
|
|
289
306
|
continue;
|
|
290
307
|
}
|
|
@@ -299,6 +316,7 @@ export function openCodeMcpConfig(mcp) {
|
|
|
299
316
|
command: [s.command, ...(s.args ?? [])],
|
|
300
317
|
enabled: true,
|
|
301
318
|
...(s.env ? { environment: s.env } : {}),
|
|
319
|
+
...(timeout !== undefined ? { timeout } : {}),
|
|
302
320
|
};
|
|
303
321
|
}
|
|
304
322
|
return out;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/agent-profile-materialize",
|
|
3
|
-
"version": "0.22.
|
|
3
|
+
"version": "0.22.4",
|
|
4
4
|
"description": "Materialize an AgentProfile into the native per-harness workspace files (the one shared materializer for cli-bridge, agent-dev-container, and VB).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
"test": "pnpm run test:installed-peer && pnpm run test:current-peer",
|
|
42
42
|
"test:installed-peer": "vitest run",
|
|
43
43
|
"test:current-peer": "vitest run --config vitest.current-peer.config.ts",
|
|
44
|
+
"test:root": "node ../../scripts/ci/vitest-as-root.mjs tests/concurrent-subagents.test.ts tests/secret-provider-revision.test.ts",
|
|
44
45
|
"test:watch": "vitest"
|
|
45
46
|
}
|
|
46
47
|
}
|