@tangle-network/agent-profile-materialize 1.1.6 → 1.1.8

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.
Files changed (95) hide show
  1. package/dist/index.d.ts +1226 -29
  2. package/dist/index.js +4987 -23
  3. package/package.json +7 -5
  4. package/dist/candidate-types.d.ts +0 -36
  5. package/dist/candidate-types.js +0 -1
  6. package/dist/candidate.d.ts +0 -9
  7. package/dist/candidate.js +0 -283
  8. package/dist/commands.d.ts +0 -2
  9. package/dist/commands.js +0 -32
  10. package/dist/control-maps.d.ts +0 -58
  11. package/dist/control-maps.js +0 -277
  12. package/dist/execution-controls.d.ts +0 -5
  13. package/dist/execution-controls.js +0 -199
  14. package/dist/harness-prompt.d.ts +0 -22
  15. package/dist/harness-prompt.js +0 -64
  16. package/dist/harness.d.ts +0 -131
  17. package/dist/harness.js +0 -416
  18. package/dist/hooks.d.ts +0 -6
  19. package/dist/hooks.js +0 -250
  20. package/dist/managed-workspace-bind.d.ts +0 -4
  21. package/dist/managed-workspace-bind.js +0 -112
  22. package/dist/managed-workspace-stage.d.ts +0 -25
  23. package/dist/managed-workspace-stage.js +0 -199
  24. package/dist/managed-workspace-types.d.ts +0 -38
  25. package/dist/managed-workspace-types.js +0 -1
  26. package/dist/managed-workspace.d.ts +0 -37
  27. package/dist/managed-workspace.js +0 -290
  28. package/dist/mcp-config-ownership.d.ts +0 -13
  29. package/dist/mcp-config-ownership.js +0 -79
  30. package/dist/mcp-opencode.d.ts +0 -25
  31. package/dist/mcp-opencode.js +0 -52
  32. package/dist/mcp-timeouts.d.ts +0 -7
  33. package/dist/mcp-timeouts.js +0 -48
  34. package/dist/mcp.d.ts +0 -4
  35. package/dist/mcp.js +0 -246
  36. package/dist/model-id.d.ts +0 -55
  37. package/dist/model-id.js +0 -67
  38. package/dist/pi-models-config.d.ts +0 -32
  39. package/dist/pi-models-config.js +0 -65
  40. package/dist/plan-application.d.ts +0 -10
  41. package/dist/plan-application.js +0 -190
  42. package/dist/plan-digest.d.ts +0 -18
  43. package/dist/plan-digest.js +0 -116
  44. package/dist/plan-json.d.ts +0 -13
  45. package/dist/plan-json.js +0 -91
  46. package/dist/plan-normalize.d.ts +0 -12
  47. package/dist/plan-normalize.js +0 -168
  48. package/dist/plan-prompt.d.ts +0 -38
  49. package/dist/plan-prompt.js +0 -49
  50. package/dist/plan-support.d.ts +0 -9
  51. package/dist/plan-support.js +0 -22
  52. package/dist/plan-types.d.ts +0 -341
  53. package/dist/plan-types.js +0 -105
  54. package/dist/prime-agent-dir.d.ts +0 -64
  55. package/dist/prime-agent-dir.js +0 -173
  56. package/dist/prime-fork.d.ts +0 -155
  57. package/dist/prime-fork.js +0 -262
  58. package/dist/prime-profile.d.ts +0 -88
  59. package/dist/prime-profile.js +0 -297
  60. package/dist/profile-bundle-cache.d.ts +0 -37
  61. package/dist/profile-bundle-cache.js +0 -320
  62. package/dist/profile-bundle-key.d.ts +0 -69
  63. package/dist/profile-bundle-key.js +0 -62
  64. package/dist/profile-bundle-lru.d.ts +0 -56
  65. package/dist/profile-bundle-lru.js +0 -220
  66. package/dist/profile-materializer.d.ts +0 -24
  67. package/dist/profile-materializer.js +0 -114
  68. package/dist/profile-plan.d.ts +0 -16
  69. package/dist/profile-plan.js +0 -90
  70. package/dist/profile-prompt.d.ts +0 -4
  71. package/dist/profile-prompt.js +0 -107
  72. package/dist/profile-support.d.ts +0 -8
  73. package/dist/profile-support.js +0 -6
  74. package/dist/resource-lowering.d.ts +0 -6
  75. package/dist/resource-lowering.js +0 -103
  76. package/dist/resource-resolution.d.ts +0 -27
  77. package/dist/resource-resolution.js +0 -265
  78. package/dist/secret-concurrent-revision.d.ts +0 -30
  79. package/dist/secret-concurrent-revision.js +0 -291
  80. package/dist/secret-session-view.d.ts +0 -36
  81. package/dist/secret-session-view.js +0 -179
  82. package/dist/subagents.d.ts +0 -6
  83. package/dist/subagents.js +0 -137
  84. package/dist/tool-bin-path.d.ts +0 -23
  85. package/dist/tool-bin-path.js +0 -61
  86. package/dist/workspace-apply-files.d.ts +0 -57
  87. package/dist/workspace-apply-files.js +0 -193
  88. package/dist/workspace-apply-publish.d.ts +0 -22
  89. package/dist/workspace-apply-publish.js +0 -178
  90. package/dist/workspace-apply.d.ts +0 -11
  91. package/dist/workspace-apply.js +0 -124
  92. package/dist/workspace-files.d.ts +0 -8
  93. package/dist/workspace-files.js +0 -46
  94. package/dist/workspace-launch.d.ts +0 -3
  95. package/dist/workspace-launch.js +0 -13
@@ -1,107 +0,0 @@
1
- import { appendSystemPromptRefusal, CODEX_SYSTEM_PROMPT_FILE, CONTEXT_FILE, markMaterializerContextFile, markMaterializerSubagent, OPENCODE_APPEND_SYSTEM_PROMPT_FILE, OPENCODE_CONFIG_FILE, OPENCODE_CONFIG_SCHEMA, OPENCODE_PRIMARY_AGENT_PREFIX, systemPromptRefusal, } from "./harness.js";
2
- import { sha256 } from "./plan-digest.js";
3
- import { serializeWorkspacePlanJson } from "./plan-json.js";
4
- export function lowerProfilePrompt(profile, harness, binds, instructionsRoot, builder) {
5
- const { plan, add, addAtRoot, unsupported } = builder;
6
- if (profile.prompt?.systemPrompt !== undefined) {
7
- if (harness === "claude-code" || harness === "pi" || harness === "prime") {
8
- plan.systemPrompt = profile.prompt.systemPrompt;
9
- }
10
- else if (harness === "codex") {
11
- add(CODEX_SYSTEM_PROMPT_FILE, profile.prompt.systemPrompt);
12
- plan.flags.push("-c", `model_instructions_file=${CODEX_SYSTEM_PROMPT_FILE}`);
13
- }
14
- else if (harness === "gemini") {
15
- add(".gemini/system.md", profile.prompt.systemPrompt);
16
- plan.env.GEMINI_SYSTEM_MD = "1";
17
- }
18
- else if (harness === "opencode") {
19
- const systemPrompt = profile.prompt.systemPrompt;
20
- if (systemPrompt.trim().length === 0) {
21
- throw new Error("opencode cannot express an empty exact system-prompt replacement");
22
- }
23
- if (binds.has("opencode.agent-prompt")) {
24
- plan.systemPromptBinding = {
25
- intent: "replace",
26
- control: "opencode.agent-prompt",
27
- systemPrompt,
28
- };
29
- }
30
- else if (binds.has("systemPrompt")) {
31
- const primaryAgentName = `${OPENCODE_PRIMARY_AGENT_PREFIX}${sha256(systemPrompt).slice(0, 16)}`;
32
- const primaryAgentPath = `.opencode/agents/${primaryAgentName}.md`;
33
- if (profile.subagents?.[primaryAgentName] !== undefined) {
34
- throw new Error(`opencode: subagent name "${primaryAgentName}" is reserved for the profile primary agent`);
35
- }
36
- plan.systemPrompt = systemPrompt;
37
- add(primaryAgentPath, markMaterializerSubagent(primaryAgentPath, ["---", "mode: primary", "---", systemPrompt, ""].join("\n")));
38
- plan.flags.push("--agent", primaryAgentName);
39
- }
40
- else {
41
- unsupported("systemPrompt", systemPromptRefusal(harness));
42
- }
43
- }
44
- else {
45
- unsupported("systemPrompt", systemPromptRefusal(harness));
46
- }
47
- }
48
- const openCodeInstructionFiles = [];
49
- const openCodeConfigUnreachable = harness === "opencode" && binds.has("opencode.agent-prompt");
50
- if (profile.prompt?.appendSystemPrompt !== undefined) {
51
- const appendSystemPrompt = profile.prompt.appendSystemPrompt;
52
- if (appendSystemPrompt.trim().length === 0) {
53
- throw new Error("prompt.appendSystemPrompt must be non-empty text");
54
- }
55
- if (openCodeConfigUnreachable) {
56
- unsupported("appendSystemPrompt", "opencode: the launcher that binds opencode.agent-prompt composes opencode's config itself and loads no workspace opencode.json, so an addition registered there would never reach the model; compose it into the launcher's own instructions[]");
57
- }
58
- else if (harness === "opencode") {
59
- add(OPENCODE_APPEND_SYSTEM_PROMPT_FILE, appendSystemPrompt.endsWith("\n")
60
- ? appendSystemPrompt
61
- : `${appendSystemPrompt}\n`);
62
- openCodeInstructionFiles.push(OPENCODE_APPEND_SYSTEM_PROMPT_FILE);
63
- }
64
- else if (harness === "claude-code" ||
65
- harness === "pi" ||
66
- harness === "prime") {
67
- plan.appendSystemPrompt = appendSystemPrompt;
68
- }
69
- else {
70
- unsupported("appendSystemPrompt", appendSystemPromptRefusal(harness));
71
- }
72
- }
73
- const instructions = [...(profile.prompt?.instructions ?? [])];
74
- const resourceInstructions = profile.resources?.instructions;
75
- if (typeof resourceInstructions === "string") {
76
- instructions.push(resourceInstructions);
77
- }
78
- else if (resourceInstructions && resourceInstructions.kind === "inline") {
79
- instructions.push(resourceInstructions.content);
80
- }
81
- else if (resourceInstructions) {
82
- unsupported("instructions", "instruction ref is not inline (github refs need async pre-resolution)");
83
- }
84
- if (instructions.length) {
85
- if (harness === "codex" && binds.has("codex.developer-instructions")) {
86
- // Codex's developer channel composes profile context without replacing
87
- // repository AGENTS.md or the harness's own system instructions.
88
- plan.appendSystemPrompt = instructions.join("\n\n");
89
- }
90
- else if (openCodeConfigUnreachable) {
91
- unsupported("instructions", "opencode: the launcher that binds opencode.agent-prompt composes opencode's config itself and loads no workspace opencode.json, so an instruction file registered there would never reach the model; compose it into the launcher's own instructions[]");
92
- }
93
- else {
94
- const instructionPath = CONTEXT_FILE[harness];
95
- addAtRoot(instructionsRoot, instructionPath, markMaterializerContextFile(instructionPath, `${instructions.join("\n\n")}\n`));
96
- if (harness === "opencode") {
97
- openCodeInstructionFiles.push(instructionPath);
98
- }
99
- }
100
- }
101
- if (openCodeInstructionFiles.length > 0) {
102
- add(OPENCODE_CONFIG_FILE, serializeWorkspacePlanJson({
103
- $schema: OPENCODE_CONFIG_SCHEMA,
104
- instructions: openCodeInstructionFiles,
105
- }));
106
- }
107
- }
@@ -1,8 +0,0 @@
1
- import type { AgentProfile, AgentProfileMcpServer } from "@tangle-network/agent-interface";
2
- import type { HarnessId } from "./harness.js";
3
- type ActiveAgentProfileMcpServer = Exclude<AgentProfileMcpServer, {
4
- enabled: false;
5
- }>;
6
- export declare function activeMcpServers(servers: AgentProfile["mcp"]): Record<string, ActiveAgentProfileMcpServer>;
7
- export declare function extensionNamespaces(harness: HarnessId): string[];
8
- export {};
@@ -1,6 +0,0 @@
1
- export function activeMcpServers(servers) {
2
- return Object.fromEntries(Object.entries(servers ?? {}).filter((entry) => entry[1].enabled !== false));
3
- }
4
- export function extensionNamespaces(harness) {
5
- return [harness];
6
- }
@@ -1,6 +0,0 @@
1
- import type { AgentProfile } from "@tangle-network/agent-interface";
2
- import type { HarnessId } from "./harness.js";
3
- import type { ProfilePlanBuilder } from "./profile-plan.js";
4
- export declare function lowerProfileResources(profile: AgentProfile, harness: HarnessId, builder: ProfilePlanBuilder): void;
5
- export declare function lowerProfileSkills(profile: AgentProfile, harness: HarnessId, builder: ProfilePlanBuilder): void;
6
- export declare function lowerProfileCommands(profile: AgentProfile, harness: HarnessId, builder: ProfilePlanBuilder): void;
@@ -1,103 +0,0 @@
1
- import { materializeCommand } from "./commands.js";
2
- import { HARNESS_SKILL_DIRS } from "./harness.js";
3
- import { normalizeSkillMd, resolveInlineRef } from "./resource-resolution.js";
4
- import { codexAgentRoleName, registerCodexAgent } from "./subagents.js";
5
- import { DEFAULT_FILE_MODE, EXECUTABLE_FILE_MODE, normalizeWorkspaceRelativePath, } from "./workspace-files.js";
6
- export function lowerProfileResources(profile, harness, builder) {
7
- const { plan, addExplicitResource, unsupported } = builder;
8
- const fileMountPaths = new Set();
9
- for (const mount of profile.resources?.files ?? []) {
10
- const relPath = normalizeWorkspaceRelativePath(mount.path);
11
- if (fileMountPaths.has(relPath)) {
12
- throw new Error(`Duplicate workspace file mount: ${relPath}`);
13
- }
14
- fileMountPaths.add(relPath);
15
- if (mount.executable !== undefined &&
16
- typeof mount.executable !== "boolean") {
17
- throw new Error(`Workspace file executable must be a boolean: ${relPath}`);
18
- }
19
- const resource = resolveInlineRef(mount.resource);
20
- if (!resource) {
21
- unsupported("files", `file mount "${relPath}" is not inline (github refs need async pre-resolution)`);
22
- continue;
23
- }
24
- addExplicitResource("files", relPath, resource.content, mount.executable ? EXECUTABLE_FILE_MODE : DEFAULT_FILE_MODE);
25
- }
26
- for (const ref of profile.resources?.tools ?? []) {
27
- const resource = resolveInlineRef(ref);
28
- if (!resource) {
29
- unsupported("tools", "tool ref is not inline (github refs need async pre-resolution)");
30
- continue;
31
- }
32
- if (harness !== "opencode") {
33
- unsupported("tools", `${harness}: no verified cwd-native file tool path; use MCP or a native extension`);
34
- continue;
35
- }
36
- addExplicitResource("tools", `.opencode/tools/${resource.name}`, resource.content);
37
- }
38
- for (const ref of profile.resources?.agents ?? []) {
39
- const resource = resolveInlineRef(ref);
40
- if (!resource) {
41
- unsupported("subagents", "agent ref is not inline (github refs need async pre-resolution)");
42
- continue;
43
- }
44
- const directory = agentResourceDirectory(harness);
45
- if (!directory) {
46
- unsupported("subagents", `${harness}: agent definitions have no verified cwd-native resource path`);
47
- continue;
48
- }
49
- const relPath = `${directory}/${resource.name}`;
50
- addExplicitResource("subagents", relPath, resource.content);
51
- if (harness === "codex") {
52
- const roleName = codexAgentRoleName(resource.name);
53
- if (!roleName) {
54
- unsupported("subagents", `codex: agent resource "${resource.name}" must use a lowercase role name`);
55
- }
56
- else {
57
- registerCodexAgent(plan, roleName, relPath, roleName);
58
- }
59
- }
60
- }
61
- }
62
- export function lowerProfileSkills(profile, harness, builder) {
63
- const { addExplicitResource, unsupported } = builder;
64
- for (const ref of profile.resources?.skills ?? []) {
65
- const resource = resolveInlineRef(ref);
66
- if (!resource) {
67
- unsupported("skills", "skill ref is not inline (github refs need async pre-resolution)");
68
- continue;
69
- }
70
- const directory = HARNESS_SKILL_DIRS[harness];
71
- if (!directory) {
72
- unsupported("skills", `${harness}: no cwd skill dir (skills live in a user/global dir); skill "${resource.name}" not mounted`);
73
- continue;
74
- }
75
- addExplicitResource("skills", `${directory}/${resource.name}/SKILL.md`, normalizeSkillMd(resource.name, resource.content));
76
- }
77
- }
78
- export function lowerProfileCommands(profile, harness, builder) {
79
- const { addExplicitResource, unsupported } = builder;
80
- for (const ref of profile.resources?.commands ?? []) {
81
- const resource = resolveInlineRef(ref);
82
- if (!resource) {
83
- unsupported("commands", "command ref is not inline (github needs pre-resolution)");
84
- continue;
85
- }
86
- materializeCommand(harness, resource.name, resource.content, (path, content, mode) => addExplicitResource("commands", path, content, mode), unsupported);
87
- }
88
- }
89
- function agentResourceDirectory(harness) {
90
- switch (harness) {
91
- case "claude-code":
92
- case "nanoclaw":
93
- return ".claude/agents";
94
- case "codex":
95
- return ".codex/agents";
96
- case "opencode":
97
- return ".opencode/agents";
98
- case "gemini":
99
- return ".gemini/agents";
100
- default:
101
- return null;
102
- }
103
- }
@@ -1,27 +0,0 @@
1
- /** Resolve inline bytes already present on the profile. Remote resources must
2
- * be resolved by {@link resolveAgentProfileResources} before materialization.
3
- *
4
- * FAIL-CLOSED: a ref that declares `kind: "inline"` is asserting its bytes are
5
- * already resolved, so missing/non-string `content` is a caller bug, not an
6
- * empty file — silently coercing it (`String(content ?? "")`) would produce a
7
- * file that "looks provisioned, loads nothing", exactly the trap this module's
8
- * docstring warns against. */
9
- export declare function resolveInlineRef(ref: AgentProfileResourceRef): {
10
- name: string;
11
- content: string;
12
- } | null;
13
- export interface ResolveAgentProfileResourcesOptions {
14
- /** Repository used when a GitHub resource omits its own `repository`. */
15
- repository?: string;
16
- fetch?: typeof globalThis.fetch;
17
- timeoutMs?: number;
18
- }
19
- /**
20
- * Resolve every GitHub resource to exact inline bytes before materialization.
21
- * The profile's default harness does not affect resource resolution.
22
- */
23
- export declare function resolveAgentProfileResources(inputProfile: AgentProfile, options?: ResolveAgentProfileResourcesOptions): Promise<AgentProfile>;
24
- /** Normalize any skill markdown (VB frontmatter `skill:`/`description:`, or raw) into a
25
- * harness SKILL.md with `name:`/`description:` frontmatter the harnesses require. */
26
- export declare function normalizeSkillMd(name: string, raw: string): string;
27
- import type { AgentProfile, AgentProfileResourceRef } from "@tangle-network/agent-interface";
@@ -1,265 +0,0 @@
1
- function describeUnresolvedRef(r) {
2
- const name = typeof r.name === "string" ? r.name : undefined;
3
- const path = typeof r.path === "string" ? r.path : undefined;
4
- if (name && path)
5
- return `"${name}" (${path})`;
6
- if (name)
7
- return `"${name}"`;
8
- if (path)
9
- return `"${path}"`;
10
- return "resource";
11
- }
12
- /** Resolve inline bytes already present on the profile. Remote resources must
13
- * be resolved by {@link resolveAgentProfileResources} before materialization.
14
- *
15
- * FAIL-CLOSED: a ref that declares `kind: "inline"` is asserting its bytes are
16
- * already resolved, so missing/non-string `content` is a caller bug, not an
17
- * empty file — silently coercing it (`String(content ?? "")`) would produce a
18
- * file that "looks provisioned, loads nothing", exactly the trap this module's
19
- * docstring warns against. */
20
- export function resolveInlineRef(ref) {
21
- const r = ref;
22
- if (typeof r.content === "string") {
23
- return { name: String(r.name ?? r.path ?? "resource"), content: r.content };
24
- }
25
- if (r.kind === "inline") {
26
- throw new Error(`Profile resource ${describeUnresolvedRef(r)} declares kind "inline" but has no string content`);
27
- }
28
- return null; // github / unresolved
29
- }
30
- const FRONTMATTER_RE = /^---\n([\s\S]*?)\n---\n?([\s\S]*)$/;
31
- const collapse = (s) => s.replace(/\s+/g, " ").trim();
32
- function frontmatterScalar(value) {
33
- const trimmed = value.trim();
34
- if (trimmed.startsWith('"') && trimmed.endsWith('"')) {
35
- try {
36
- const parsed = JSON.parse(trimmed);
37
- if (typeof parsed === "string")
38
- return parsed;
39
- }
40
- catch {
41
- return trimmed;
42
- }
43
- }
44
- if (trimmed.startsWith("'") && trimmed.endsWith("'")) {
45
- return trimmed.slice(1, -1).replaceAll("''", "'");
46
- }
47
- return trimmed;
48
- }
49
- function frontmatterValue(frontmatter, field) {
50
- const lines = frontmatter.split("\n");
51
- const index = lines.findIndex((line) => line.startsWith(`${field}:`));
52
- if (index < 0)
53
- return undefined;
54
- const inline = (lines[index] ?? "").slice(field.length + 1).trim();
55
- if (inline && ![">", "|", ">-", "|-"].includes(inline)) {
56
- return frontmatterScalar(inline);
57
- }
58
- const block = [];
59
- for (let offset = index + 1; offset < lines.length; offset += 1) {
60
- const line = lines[offset] ?? "";
61
- if (/^\s+\S/.test(line))
62
- block.push(line.trim());
63
- else if (line.trim() !== "")
64
- break;
65
- }
66
- const value = collapse(block.join(" "));
67
- return value || undefined;
68
- }
69
- const DEFAULT_RESOURCE_FETCH_TIMEOUT_MS = 30_000;
70
- const MAX_RESOURCE_BYTES = 10 * 1024 * 1024;
71
- const MAX_PROFILE_RESOURCE_BYTES = 32 * 1024 * 1024;
72
- const MAX_PROFILE_RESOURCE_REFS = 128;
73
- const MAX_CONCURRENT_RESOURCE_FETCHES = 8;
74
- async function readResourceBody(response, description, consumeBytes) {
75
- if (!response.body)
76
- return "";
77
- const reader = response.body.getReader();
78
- const chunks = [];
79
- let totalBytes = 0;
80
- try {
81
- while (true) {
82
- const { done, value } = await reader.read();
83
- if (done)
84
- break;
85
- if (!value)
86
- continue;
87
- totalBytes += value.byteLength;
88
- if (totalBytes > MAX_RESOURCE_BYTES) {
89
- await reader.cancel().catch(() => undefined);
90
- throw new Error(`GitHub profile resource exceeds ${MAX_RESOURCE_BYTES} bytes: ${description}`);
91
- }
92
- try {
93
- consumeBytes(value.byteLength);
94
- }
95
- catch (error) {
96
- await reader.cancel().catch(() => undefined);
97
- throw error;
98
- }
99
- chunks.push(value);
100
- }
101
- }
102
- finally {
103
- reader.releaseLock();
104
- }
105
- return Buffer.concat(chunks.map((chunk) => Buffer.from(chunk.buffer, chunk.byteOffset, chunk.byteLength)), totalBytes).toString("utf8");
106
- }
107
- async function mapWithConcurrency(values, concurrency, controller, transform) {
108
- const results = new Array(values.length);
109
- let nextIndex = 0;
110
- let firstFailure;
111
- const worker = async () => {
112
- while (!firstFailure) {
113
- const index = nextIndex++;
114
- if (index >= values.length)
115
- return;
116
- try {
117
- results[index] = await transform(values[index]);
118
- }
119
- catch (error) {
120
- if (!firstFailure) {
121
- firstFailure = { error };
122
- controller.abort(error);
123
- }
124
- }
125
- }
126
- };
127
- await Promise.all(Array.from({ length: Math.min(concurrency, values.length) }, async () => worker()));
128
- if (firstFailure)
129
- throw firstFailure.error;
130
- return results;
131
- }
132
- /**
133
- * Resolve every GitHub resource to exact inline bytes before materialization.
134
- * The profile's default harness does not affect resource resolution.
135
- */
136
- export async function resolveAgentProfileResources(inputProfile, options = {}) {
137
- const profile = agentProfileSchema.parse(inputProfile);
138
- const resources = profile.resources;
139
- if (!resources)
140
- return profile;
141
- const refs = [
142
- ...(resources.files?.map((mount) => mount.resource) ?? []),
143
- ...(resources.tools ?? []),
144
- ...(resources.skills ?? []),
145
- ...(resources.agents ?? []),
146
- ...(resources.commands ?? []),
147
- ...(typeof resources.instructions === "object"
148
- ? [resources.instructions]
149
- : []),
150
- ];
151
- if (refs.length > MAX_PROFILE_RESOURCE_REFS) {
152
- throw new Error(`AgentProfile may contain at most ${MAX_PROFILE_RESOURCE_REFS} resource references`);
153
- }
154
- const batchController = new AbortController();
155
- let fetchedBytes = 0;
156
- const consumeFetchedBytes = (bytes) => {
157
- fetchedBytes += bytes;
158
- if (fetchedBytes > MAX_PROFILE_RESOURCE_BYTES) {
159
- throw new Error(`GitHub profile resources exceed ${MAX_PROFILE_RESOURCE_BYTES} total bytes`);
160
- }
161
- };
162
- const resolveRef = async (ref) => {
163
- if (ref.kind === "inline")
164
- return ref;
165
- const repository = ref.repository ?? options.repository;
166
- if (!repository) {
167
- throw new Error(`GitHub profile resource "${ref.path}" requires repository`);
168
- }
169
- const [owner, repo, ...extraRepositoryParts] = repository.split("/");
170
- if (!owner ||
171
- !repo ||
172
- extraRepositoryParts.length > 0 ||
173
- !/^[A-Za-z0-9_.-]+$/.test(owner) ||
174
- !/^[A-Za-z0-9_.-]+$/.test(repo)) {
175
- throw new Error(`GitHub profile resource repository must be owner/repo: ${repository}`);
176
- }
177
- const resourcePath = normalizeWorkspaceRelativePath(ref.path);
178
- const gitRef = ref.ref ?? "main";
179
- if (!gitRef ||
180
- hasControlCharacter(gitRef) ||
181
- gitRef.split("/").some((segment) => !segment || segment === "..")) {
182
- throw new Error(`Invalid GitHub profile resource ref: ${gitRef}`);
183
- }
184
- const encoded = (value) => value.split("/").map(encodeURIComponent).join("/");
185
- const url = `https://raw.githubusercontent.com/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/${encoded(gitRef)}/${encoded(resourcePath)}`;
186
- const controller = new AbortController();
187
- const abortRequest = () => controller.abort(batchController.signal.reason);
188
- batchController.signal.addEventListener("abort", abortRequest, {
189
- once: true,
190
- });
191
- const timeoutError = new Error(`GitHub profile resource timed out: ${repository}/${resourcePath}@${gitRef}`);
192
- const timeout = setTimeout(() => controller.abort(timeoutError), options.timeoutMs ?? DEFAULT_RESOURCE_FETCH_TIMEOUT_MS);
193
- try {
194
- const fetchResource = options.fetch ?? globalThis.fetch;
195
- const response = await fetchResource(url, { signal: controller.signal });
196
- if (!response.ok) {
197
- throw new Error(`Failed to fetch GitHub profile resource ${repository}/${resourcePath}@${gitRef}: ${response.status} ${response.statusText}`);
198
- }
199
- const contentLength = Number(response.headers.get("content-length"));
200
- if (Number.isFinite(contentLength) &&
201
- contentLength > MAX_RESOURCE_BYTES) {
202
- throw new Error(`GitHub profile resource exceeds ${MAX_RESOURCE_BYTES} bytes: ${repository}/${resourcePath}`);
203
- }
204
- const content = await readResourceBody(response, `${repository}/${resourcePath}`, consumeFetchedBytes);
205
- return {
206
- kind: "inline",
207
- name: ref.name ?? basename(resourcePath),
208
- content,
209
- };
210
- }
211
- catch (error) {
212
- if (controller.signal.aborted && controller.signal.reason !== undefined) {
213
- throw controller.signal.reason;
214
- }
215
- throw error;
216
- }
217
- finally {
218
- clearTimeout(timeout);
219
- batchController.signal.removeEventListener("abort", abortRequest);
220
- }
221
- };
222
- const resolvedRefs = await mapWithConcurrency(refs, MAX_CONCURRENT_RESOURCE_FETCHES, batchController, resolveRef);
223
- let resolvedIndex = 0;
224
- const takeRef = () => resolvedRefs[resolvedIndex++];
225
- const takeRefs = (values) => values?.map(takeRef);
226
- const resolvedResources = {
227
- ...resources,
228
- files: resources.files === undefined
229
- ? undefined
230
- : resources.files.map((mount) => ({
231
- ...mount,
232
- resource: takeRef(),
233
- })),
234
- tools: takeRefs(resources.tools),
235
- skills: takeRefs(resources.skills),
236
- agents: takeRefs(resources.agents),
237
- commands: takeRefs(resources.commands),
238
- instructions: typeof resources.instructions === "object"
239
- ? takeRef()
240
- : resources.instructions,
241
- };
242
- return agentProfileSchema.parse({ ...profile, resources: resolvedResources });
243
- }
244
- /** Normalize any skill markdown (VB frontmatter `skill:`/`description:`, or raw) into a
245
- * harness SKILL.md with `name:`/`description:` frontmatter the harnesses require. */
246
- export function normalizeSkillMd(name, raw) {
247
- const m = raw.match(FRONTMATTER_RE);
248
- const fm = m?.[1] ?? "";
249
- const body = (m?.[2] ?? raw).trim();
250
- if (fm &&
251
- frontmatterValue(fm, "name") === name &&
252
- (frontmatterValue(fm, "description")?.trim().length ?? 0) > 0) {
253
- return raw;
254
- }
255
- let desc = `Skill ${name}. Use when the task matches its domain.`;
256
- const declaredDescription = fm
257
- ? frontmatterValue(fm, "description")
258
- : undefined;
259
- if (declaredDescription)
260
- desc = collapse(declaredDescription);
261
- return `---\nname: ${name}\ndescription: ${JSON.stringify(desc)}\n---\n\n${body}\n`;
262
- }
263
- import { basename } from "node:path";
264
- import { agentProfileSchema } from "@tangle-network/agent-interface";
265
- import { hasControlCharacter, normalizeWorkspaceRelativePath, } from "./workspace-files.js";
@@ -1,30 +0,0 @@
1
- import type { AgentProfileSecretProvider } from "@tangle-network/agent-interface";
2
- import { type ConcurrentProfileRevision } from "./managed-workspace.js";
3
- import type { ApplyWorkspacePlanOptions, WorkspacePlan } from "./plan-types.js";
4
- /**
5
- * Publish a resolved revision for exactly one native subprocess UID. The
6
- * executor-owned naming key hides low-entropy credential guesses from paths;
7
- * the resolved digest and manifest never leave this server-side result.
8
- */
9
- export declare function prepareSecretConcurrentProfileRevision(input: {
10
- plan: WorkspacePlan;
11
- /** Trusted executor transport only; never read from the mutable native HOME. */
12
- providerConfig?: Record<string, unknown>;
13
- managedRoot: string;
14
- launchRoot: string;
15
- sharedWorkspaceDir: string;
16
- ownerId: string;
17
- subprocessUid: number;
18
- namingKey: Uint8Array;
19
- secretProvider: AgentProfileSecretProvider;
20
- options?: Pick<ApplyWorkspacePlanOptions, "runtimeOnlyFiles">;
21
- }): Promise<ConcurrentProfileRevision & {
22
- /**
23
- * The value behind each `{env:NAME}` reference in the revision's config,
24
- * for the executor alone: the launch never receives them. An executor
25
- * that serves a referencing MCP server through its own loopback proxy
26
- * resolves the server's headers here.
27
- */
28
- secretReferences: Readonly<Record<string, string>>;
29
- }>;
30
- export declare function runSetfacl(args: string[], paths: readonly string[]): void;