@tangle-network/agent-profile-materialize 0.22.0 → 0.22.1
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/control-maps.d.ts +5 -0
- package/dist/control-maps.js +32 -0
- package/dist/execution-controls.d.ts +1 -1
- package/dist/execution-controls.js +4 -3
- package/dist/profile-materializer.d.ts +1 -1
- package/dist/profile-materializer.js +2 -2
- package/dist/secret-concurrent-revision.d.ts +2 -0
- package/dist/secret-concurrent-revision.js +17 -1
- package/package.json +1 -1
package/dist/control-maps.d.ts
CHANGED
|
@@ -6,6 +6,11 @@ export declare const OPENCODE_NATIVE_TOOL_IDS: readonly ["apply_patch", "bash",
|
|
|
6
6
|
export declare function openCodeNativeToolId(name: string): string | undefined;
|
|
7
7
|
export declare function openCodeToolName(name: string): string | undefined;
|
|
8
8
|
export declare function openCodePermissionName(name: string): string | undefined;
|
|
9
|
+
/** OpenCode 1.x registers MCP tools as sanitized server + "_" + tool.
|
|
10
|
+
* Accept exact tools and a server-wide rule only for this profile's servers.
|
|
11
|
+
* A generic permission spelling must never become a grant to an undeclared MCP.
|
|
12
|
+
*/
|
|
13
|
+
export declare function openCodeDeclaredMcpPermissionName(name: string, aliases: readonly string[]): string | undefined;
|
|
9
14
|
export declare const PI_EXECUTION_CONTROL_EXTENSION_PATH = ".tangle/pi-execution-controls.ts";
|
|
10
15
|
/**
|
|
11
16
|
* Translate only portable spellings whose Pi equivalent differs.
|
package/dist/control-maps.js
CHANGED
|
@@ -68,6 +68,8 @@ export function openCodeToolName(name) {
|
|
|
68
68
|
return openCodeNativeToolId(name);
|
|
69
69
|
}
|
|
70
70
|
export function openCodePermissionName(name) {
|
|
71
|
+
if (name.includes("*") || name.includes("?"))
|
|
72
|
+
return undefined;
|
|
71
73
|
const normalized = normalizedControlName(name);
|
|
72
74
|
if (["write", "edit"].includes(normalized))
|
|
73
75
|
return "edit";
|
|
@@ -83,6 +85,36 @@ export function openCodePermissionName(name) {
|
|
|
83
85
|
? normalized
|
|
84
86
|
: undefined;
|
|
85
87
|
}
|
|
88
|
+
/** OpenCode 1.x registers MCP tools as sanitized server + "_" + tool.
|
|
89
|
+
* Accept exact tools and a server-wide rule only for this profile's servers.
|
|
90
|
+
* A generic permission spelling must never become a grant to an undeclared MCP.
|
|
91
|
+
*/
|
|
92
|
+
export function openCodeDeclaredMcpPermissionName(name, aliases) {
|
|
93
|
+
const servers = aliases.map((alias) => ({
|
|
94
|
+
alias,
|
|
95
|
+
native: alias.replace(/[^a-zA-Z0-9_-]/g, "_"),
|
|
96
|
+
}));
|
|
97
|
+
const matches = servers.filter(({ native }) => name.startsWith(`${native}_`));
|
|
98
|
+
const match = matches[0];
|
|
99
|
+
if (!match)
|
|
100
|
+
return undefined;
|
|
101
|
+
const { native } = match;
|
|
102
|
+
const tool = name.slice(native.length + 1);
|
|
103
|
+
if (tool !== "*" && !/^[a-zA-Z0-9_-]+$/.test(tool))
|
|
104
|
+
return undefined;
|
|
105
|
+
// Colliding or nested prefixes cannot identify one server's authority:
|
|
106
|
+
// foo_* also matches foo_bar_read, including a server named foo_bar.
|
|
107
|
+
if (matches.length !== 1 ||
|
|
108
|
+
servers.some((server) => server !== matches[0] &&
|
|
109
|
+
(server.native.startsWith(`${native}_`) ||
|
|
110
|
+
native.startsWith(`${server.native}_`)))) {
|
|
111
|
+
throw new Error(`opencode: ambiguous declared MCP permission "${name}"`);
|
|
112
|
+
}
|
|
113
|
+
if (tool !== "*" && openCodePermissionName(name) !== undefined) {
|
|
114
|
+
throw new Error(`opencode: permission "${name}" names both a native and MCP tool`);
|
|
115
|
+
}
|
|
116
|
+
return name;
|
|
117
|
+
}
|
|
86
118
|
export const PI_EXECUTION_CONTROL_EXTENSION_PATH = ".tangle/pi-execution-controls.ts";
|
|
87
119
|
/**
|
|
88
120
|
* Translate only portable spellings whose Pi equivalent differs.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare function materializeExecutionControls(h: HarnessId, tools: Record<string, boolean> | undefined, permissions: Record<string, AgentProfilePermission> | undefined, modes: AgentProfile["modes"], plan: WorkspacePlan, add: AddPlanFile, unsupported: (d: Unsupported["dimension"], r: string) => void): void;
|
|
1
|
+
export declare function materializeExecutionControls(h: HarnessId, tools: Record<string, boolean> | undefined, permissions: Record<string, AgentProfilePermission> | undefined, modes: AgentProfile["modes"], plan: WorkspacePlan, add: AddPlanFile, unsupported: (d: Unsupported["dimension"], r: string) => void, mcpAliases?: readonly string[]): void;
|
|
2
2
|
import type { AgentProfile, AgentProfilePermission } from "@tangle-network/agent-interface";
|
|
3
3
|
import type { HarnessId } from "./harness.js";
|
|
4
4
|
import type { AddPlanFile } from "./plan-json.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export function materializeExecutionControls(h, tools, permissions, modes, plan, add, unsupported) {
|
|
1
|
+
export function materializeExecutionControls(h, tools, permissions, modes, plan, add, unsupported, mcpAliases = []) {
|
|
2
2
|
const toolEntries = Object.entries(tools ?? {}).sort(([left], [right]) => compareStrings(left, right));
|
|
3
3
|
const permissionEntries = Object.entries(permissions ?? {}).sort(([left], [right]) => compareStrings(left, right));
|
|
4
4
|
switch (h) {
|
|
@@ -33,7 +33,8 @@ export function materializeExecutionControls(h, tools, permissions, modes, plan,
|
|
|
33
33
|
return false;
|
|
34
34
|
});
|
|
35
35
|
const nativeTools = mapSupportedBooleanEntries(enforceableTools, openCodeToolName, (name) => unsupported("tools", `opencode: no verified native tool mapping for "${name}"`));
|
|
36
|
-
const nativePermissions = mapSupportedPermissionEntries(enforceablePermissions,
|
|
36
|
+
const nativePermissions = mapSupportedPermissionEntries(enforceablePermissions, (name) => openCodeDeclaredMcpPermissionName(name, mcpAliases) ??
|
|
37
|
+
openCodePermissionName(name), (name) => unsupported("permissions", `opencode: no verified native permission mapping for "${name}"`), "opencode permission");
|
|
37
38
|
if (enforceableTools.length > 0 || permissionEntries.length > 0) {
|
|
38
39
|
add(OPENCODE_CONFIG_FILE, serializeWorkspacePlanJson({
|
|
39
40
|
$schema: OPENCODE_CONFIG_SCHEMA,
|
|
@@ -183,7 +184,7 @@ function piExecutionControlExtensionSource(enabledTools, excludedTools) {
|
|
|
183
184
|
].join("\n");
|
|
184
185
|
}
|
|
185
186
|
import { resolveNativeWebTools } from "@tangle-network/agent-interface";
|
|
186
|
-
import { claudePermissionRules, claudeToolName, mapSupportedBooleanEntries, mapSupportedPermissionEntries, normalizedControlName, openCodePermissionName, openCodeToolName, PI_EXECUTION_CONTROL_EXTENSION_PATH, permissionAllowsEverything, piToolId, sealClaudePermissionRules, } from "./control-maps.js";
|
|
187
|
+
import { claudePermissionRules, claudeToolName, mapSupportedBooleanEntries, mapSupportedPermissionEntries, normalizedControlName, openCodeDeclaredMcpPermissionName, openCodePermissionName, openCodeToolName, PI_EXECUTION_CONTROL_EXTENSION_PATH, permissionAllowsEverything, piToolId, sealClaudePermissionRules, } from "./control-maps.js";
|
|
187
188
|
import { CLAUDE_SETTINGS_PATH, OPENCODE_CONFIG_FILE, OPENCODE_CONFIG_SCHEMA, } from "./harness.js";
|
|
188
189
|
import { registerClaudeSettings } from "./hooks.js";
|
|
189
190
|
import { serializeWorkspacePlanJson } from "./plan-json.js";
|
|
@@ -11,7 +11,7 @@ export declare const UNAVAILABLE_AGENT_PROFILE_SECRET_PROVIDER: AgentProfileSecr
|
|
|
11
11
|
* user's turn materializes it. Controls a harness cannot enforce are not
|
|
12
12
|
* contradictions: `resources.failOnError` decides those per turn.
|
|
13
13
|
*/
|
|
14
|
-
export declare function executionControlConflict(profile: Pick<AgentProfile, "tools" | "permissions">, harness: HarnessId): string | undefined;
|
|
14
|
+
export declare function executionControlConflict(profile: Pick<AgentProfile, "tools" | "permissions" | "mcp">, harness: HarnessId): string | undefined;
|
|
15
15
|
/**
|
|
16
16
|
* Build a pure workspace plan for one canonical harness.
|
|
17
17
|
* The caller applies files, environment, and flags at its process boundary.
|
|
@@ -26,7 +26,7 @@ export const UNAVAILABLE_AGENT_PROFILE_SECRET_PROVIDER = Object.freeze({
|
|
|
26
26
|
export function executionControlConflict(profile, harness) {
|
|
27
27
|
const builder = createProfilePlanBuilder(harness);
|
|
28
28
|
try {
|
|
29
|
-
materializeExecutionControls(harness, profile.tools, profile.permissions, undefined, builder.plan, builder.add, builder.unsupported);
|
|
29
|
+
materializeExecutionControls(harness, profile.tools, profile.permissions, undefined, builder.plan, builder.add, builder.unsupported, Object.keys(activeMcpServers(profile.mcp)));
|
|
30
30
|
}
|
|
31
31
|
catch (error) {
|
|
32
32
|
return error instanceof Error ? error.message : String(error);
|
|
@@ -53,7 +53,7 @@ export function materializeProfile(inputProfile, harness, opts = {}) {
|
|
|
53
53
|
const builder = createProfilePlanBuilder(harness);
|
|
54
54
|
lowerProfileResources(profile, harness, builder);
|
|
55
55
|
lowerProfilePrompt(profile, harness, binds, instructionsRoot, builder);
|
|
56
|
-
materializeExecutionControls(harness, profile.tools, profile.permissions, profile.modes, builder.plan, builder.add, builder.unsupported);
|
|
56
|
+
materializeExecutionControls(harness, profile.tools, profile.permissions, profile.modes, builder.plan, builder.add, builder.unsupported, skip.has("mcp") ? [] : Object.keys(activeMcpServers(profile.mcp)));
|
|
57
57
|
if ((profile.connections?.length ?? 0) > 0) {
|
|
58
58
|
builder.unsupported("connections", `${harness}: Hub connections must be resolved to authorized MCP servers by the execution runtime`);
|
|
59
59
|
}
|
|
@@ -8,6 +8,8 @@ import type { ApplyWorkspacePlanOptions, WorkspacePlan } from "./plan-types.js";
|
|
|
8
8
|
*/
|
|
9
9
|
export declare function prepareSecretConcurrentProfileRevision(input: {
|
|
10
10
|
plan: WorkspacePlan;
|
|
11
|
+
/** Trusted executor transport only; never read from the mutable native HOME. */
|
|
12
|
+
providerConfig?: Record<string, unknown>;
|
|
11
13
|
managedRoot: string;
|
|
12
14
|
launchRoot: string;
|
|
13
15
|
sharedWorkspaceDir: string;
|
|
@@ -20,7 +20,23 @@ export async function prepareSecretConcurrentProfileRevision(input) {
|
|
|
20
20
|
!input.ownerId) {
|
|
21
21
|
throw new Error("Secret concurrent profile needs a root executor and reserved identity");
|
|
22
22
|
}
|
|
23
|
-
|
|
23
|
+
let plan = prepareExplicitOpenCodePlan(input.plan);
|
|
24
|
+
if (input.providerConfig) {
|
|
25
|
+
plan = {
|
|
26
|
+
...plan,
|
|
27
|
+
files: plan.files.map((file) => file.relPath === "opencode.json" &&
|
|
28
|
+
file.source === "generated" &&
|
|
29
|
+
(file.root ?? "workspace") === "workspace"
|
|
30
|
+
? {
|
|
31
|
+
...file,
|
|
32
|
+
content: JSON.stringify({
|
|
33
|
+
...JSON.parse(file.content),
|
|
34
|
+
provider: input.providerConfig,
|
|
35
|
+
}),
|
|
36
|
+
}
|
|
37
|
+
: file),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
24
40
|
if (plan.files.some((file) => file.relPath.startsWith(".config/opencode/"))) {
|
|
25
41
|
throw new Error("Secret OpenCode reserves its native config directory");
|
|
26
42
|
}
|
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.1",
|
|
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": {
|