@tangle-network/agent-profile-materialize 0.26.5 → 0.26.7
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/plan-application.js
CHANGED
|
@@ -106,8 +106,10 @@ async function resolvePrivateWorkspacePlan(plan, secretProvider, fileSlotReferen
|
|
|
106
106
|
try {
|
|
107
107
|
value = await secretProvider.get(ref.key);
|
|
108
108
|
}
|
|
109
|
-
catch {
|
|
110
|
-
|
|
109
|
+
catch (cause) {
|
|
110
|
+
// The executor's refusal (a key it will not serve) rides along as
|
|
111
|
+
// the cause; a provider error carries no resolved value.
|
|
112
|
+
throw new Error(`AgentProfile secret provider failed for ${secretRefLabel(ref)}`, { cause });
|
|
111
113
|
}
|
|
112
114
|
return requireResolvedSecretValue(ref, value);
|
|
113
115
|
})();
|
|
@@ -50,6 +50,8 @@ export async function prepareSecretConcurrentProfileRevision(input) {
|
|
|
50
50
|
: file),
|
|
51
51
|
};
|
|
52
52
|
}
|
|
53
|
+
const secretReferences = {};
|
|
54
|
+
plan = referencePublicMcpHeaders(plan, secretReferences);
|
|
53
55
|
if (plan.files.some((file) => file.relPath.startsWith(".config/opencode/"))) {
|
|
54
56
|
throw new Error("Secret OpenCode reserves its native config directory");
|
|
55
57
|
}
|
|
@@ -66,7 +68,6 @@ export async function prepareSecretConcurrentProfileRevision(input) {
|
|
|
66
68
|
assertLaunchRoot(input.launchRoot);
|
|
67
69
|
// File slots reference their secrets, so an unchanged profile with a rotated
|
|
68
70
|
// per-turn secret reuses this revision.
|
|
69
|
-
const secretReferences = {};
|
|
70
71
|
const prepared = await prepareManagedWorkspacePlan(plan, input.managedRoot, input.sharedWorkspaceDir, input.secretProvider, { ...input.options, secretFileSlotReferences: secretReferences });
|
|
71
72
|
try {
|
|
72
73
|
// XDG_CONFIG_HOME is bound to this executor-owned tree at native launch.
|
|
@@ -137,6 +138,59 @@ export async function prepareSecretConcurrentProfileRevision(input) {
|
|
|
137
138
|
throw error;
|
|
138
139
|
}
|
|
139
140
|
}
|
|
141
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
142
|
+
/** How `servableMcpServer` recognizes a header value the executor holds. */
|
|
143
|
+
const HELD_VALUE_PREFIX = "TANGLE_PROFILE_SECRET_";
|
|
144
|
+
/**
|
|
145
|
+
* A remote MCP server's public header values never reach the sealed launch:
|
|
146
|
+
* the executor's loopback proxy replaces the server's url and headers there.
|
|
147
|
+
* They did reach the revision's bytes, so a value that changes every turn (a
|
|
148
|
+
* run id, a message id) gave each turn its own digest, revision directory,
|
|
149
|
+
* receipt and native health check. Each such value becomes a reference the
|
|
150
|
+
* executor resolves, as a secret slot already does, and the revision names
|
|
151
|
+
* only the header. The value stays as public as the profile declared it; it is
|
|
152
|
+
* held beside the secret references and nothing the native process can read
|
|
153
|
+
* carries it.
|
|
154
|
+
*
|
|
155
|
+
* A value that already looks like a reference, or is empty, stays in place.
|
|
156
|
+
*/
|
|
157
|
+
function referencePublicMcpHeaders(plan, references) {
|
|
158
|
+
const configFile = plan.files.find((file) => file.relPath === "opencode.json" &&
|
|
159
|
+
file.source === "generated" &&
|
|
160
|
+
(file.root ?? "workspace") === "workspace");
|
|
161
|
+
if (!configFile)
|
|
162
|
+
return plan;
|
|
163
|
+
const config = JSON.parse(configFile.content);
|
|
164
|
+
if (!isRecord(config) || !isRecord(config.mcp))
|
|
165
|
+
return plan;
|
|
166
|
+
let changed = false;
|
|
167
|
+
for (const [server, entry] of Object.entries(config.mcp)) {
|
|
168
|
+
if (!isRecord(entry) || entry.type !== "remote")
|
|
169
|
+
continue;
|
|
170
|
+
if (!isRecord(entry.headers))
|
|
171
|
+
continue;
|
|
172
|
+
for (const [header, value] of Object.entries(entry.headers)) {
|
|
173
|
+
if (typeof value !== "string" || value === "")
|
|
174
|
+
continue;
|
|
175
|
+
if (/\{(?:env|file):/.test(value))
|
|
176
|
+
continue;
|
|
177
|
+
const name = `${HELD_VALUE_PREFIX}${createHash("sha256")
|
|
178
|
+
.update(`public-mcp-header\0${server}\0${header}`, "utf8")
|
|
179
|
+
.digest("hex")
|
|
180
|
+
.slice(0, 24)
|
|
181
|
+
.toUpperCase()}`;
|
|
182
|
+
references[name] = value;
|
|
183
|
+
entry.headers[header] = `{env:${name}}`;
|
|
184
|
+
changed = true;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
if (!changed)
|
|
188
|
+
return plan;
|
|
189
|
+
return {
|
|
190
|
+
...plan,
|
|
191
|
+
files: plan.files.map((file) => file === configFile ? { ...file, content: JSON.stringify(config) } : file),
|
|
192
|
+
};
|
|
193
|
+
}
|
|
140
194
|
function secretRevisionPaths(root) {
|
|
141
195
|
const all = [root];
|
|
142
196
|
const exposed = [root];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/agent-profile-materialize",
|
|
3
|
-
"version": "0.26.
|
|
3
|
+
"version": "0.26.7",
|
|
4
4
|
"description": "Materialize an AgentProfile into the native per-harness workspace files (the one shared materializer for cli-bridge, agent-container, and VB).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -35,8 +35,8 @@
|
|
|
35
35
|
"@tangle-network/agent-interface": "^3.0.0 || ^4.0.0 || ^5.0.0"
|
|
36
36
|
},
|
|
37
37
|
"tangleRelease": {
|
|
38
|
-
"sourceSha": "
|
|
39
|
-
"mergedAt": "2026-10-
|
|
38
|
+
"sourceSha": "3a57bb381718597c9aac7fe2f0d2c60b35223474",
|
|
39
|
+
"mergedAt": "2026-10-19T11:40:06-06:00"
|
|
40
40
|
},
|
|
41
41
|
"scripts": {
|
|
42
42
|
"build": "tsc -p tsconfig.json",
|