@tangle-network/agent-profile-materialize 0.5.1 → 0.6.0
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/index.d.ts +49 -0
- package/dist/index.js +82 -13
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -74,6 +74,15 @@ export interface ApplyWorkspacePlanOptions {
|
|
|
74
74
|
}
|
|
75
75
|
/** Fail when a profile asks for controls the chosen harness cannot execute. */
|
|
76
76
|
export declare function assertWorkspacePlanSupported(plan: Pick<WorkspacePlan, "unsupported">): void;
|
|
77
|
+
/**
|
|
78
|
+
* Enforce plan supportability under the profile's `resources.failOnError`
|
|
79
|
+
* policy. Strict by default: `failOnError` absent or `true` throws on any
|
|
80
|
+
* unsupported dimension. An explicitly best-effort profile
|
|
81
|
+
* (`failOnError: false`) materializes the supported subset instead; the
|
|
82
|
+
* returned `"dimension: reason"` entries are what the caller must surface
|
|
83
|
+
* as a warning so the skipped dimensions stay visible to operators.
|
|
84
|
+
*/
|
|
85
|
+
export declare function enforceWorkspacePlanSupport(plan: Pick<WorkspacePlan, "unsupported">, failOnError: boolean | undefined): string[];
|
|
77
86
|
/**
|
|
78
87
|
* Candidate-safe workspace plan. Spawn values remain typed until the runtime
|
|
79
88
|
* resolves them; the materializer never joins argv into a shell command.
|
|
@@ -99,6 +108,42 @@ export interface MaterializeCandidateProfileOptions {
|
|
|
99
108
|
/** Exact text fetched from pinned resource refs, keyed by declared SHA-256. */
|
|
100
109
|
resolvedResources?: ReadonlyMap<Sha256Digest, string>;
|
|
101
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* Whether the harness LOADS its {@link CONTEXT_FILE} from cwd on its own.
|
|
113
|
+
*
|
|
114
|
+
* True for every harness whose CLI discovers the file by name (CLAUDE.md,
|
|
115
|
+
* AGENTS.md, …). opencode is the exception: it reads only what its
|
|
116
|
+
* `opencode.json` `instructions[]` names, so a caller writing context for
|
|
117
|
+
* opencode must register the path as well as write it — see the `instructions`
|
|
118
|
+
* lowering below, and the interactive TUI's persona fold.
|
|
119
|
+
*/
|
|
120
|
+
export declare function harnessAutoLoadsContextFile(harness: HarnessId): boolean;
|
|
121
|
+
/**
|
|
122
|
+
* The project context file this harness reads from cwd — its stable, documented
|
|
123
|
+
* ALWAYS-LOADED surface. Exported so a caller lowering additive context (e.g. a
|
|
124
|
+
* bare TUI's persona, which has no per-message system channel) targets the same
|
|
125
|
+
* file the materializer writes instructions to, instead of keeping its own copy
|
|
126
|
+
* of this per-harness map.
|
|
127
|
+
*/
|
|
128
|
+
export declare function harnessContextFile(harness: HarnessId): string;
|
|
129
|
+
/**
|
|
130
|
+
* cwd-relative native skill dir per harness, or null if the harness has no
|
|
131
|
+
* cwd skill primitive (skills live in a user/global dir the workspace plan
|
|
132
|
+
* cannot reach). Public so consumers composing a profile can (a) render the
|
|
133
|
+
* exact path the platform will mount a skill at into their prompts instead
|
|
134
|
+
* of hardcoding `~/.claude/skills/...`, and (b) branch on skill support
|
|
135
|
+
* ahead of materialization instead of catching the materialize-time throw.
|
|
136
|
+
*/
|
|
137
|
+
export declare const HARNESS_SKILL_DIRS: Readonly<Record<HarnessId, string | null>>;
|
|
138
|
+
/**
|
|
139
|
+
* Resolve the cwd-relative directory `resources.skills` refs materialize
|
|
140
|
+
* into on `harness` (after alias canonicalization: claude/claudish →
|
|
141
|
+
* claude-code, kimi → kimi-code). `null` means the harness cannot receive
|
|
142
|
+
* cwd skills — the same condition that surfaces as an `unsupported`
|
|
143
|
+
* "skills" entry (and an {@link assertWorkspacePlanSupported} throw) at
|
|
144
|
+
* materialize time, exposed here as branchable data.
|
|
145
|
+
*/
|
|
146
|
+
export declare function skillDirForHarness(harness: HarnessId): string | null;
|
|
102
147
|
export interface ResolveAgentProfileResourcesOptions {
|
|
103
148
|
/** Repository used when a GitHub resource omits its own `repository`. */
|
|
104
149
|
repository?: string;
|
|
@@ -154,6 +199,10 @@ export declare function applyAgentCandidateWorkspacePlan(plan: AgentCandidateWor
|
|
|
154
199
|
* agent-dev-container box, and VB. Returns `unsupported` so the caller can log/fail.
|
|
155
200
|
*/
|
|
156
201
|
export declare function applyWorkspacePlan(plan: WorkspacePlan, workspaceDir: string, options?: ApplyWorkspacePlanOptions): WorkspacePlanReceipt;
|
|
202
|
+
/** Tool IDs reported by the OpenCode binary shipped in the Sandbox image. */
|
|
203
|
+
export declare const OPENCODE_NATIVE_TOOL_IDS: readonly ["apply_patch", "bash", "codesearch", "edit", "glob", "grep", "question", "read", "skill", "task", "todowrite", "webfetch", "websearch", "write"];
|
|
204
|
+
/** Resolve portable and legacy profile names to current OpenCode tool IDs. */
|
|
205
|
+
export declare function openCodeNativeToolId(name: string): string | undefined;
|
|
157
206
|
/** OpenCode-native MCP configuration used by workspace and startup paths. */
|
|
158
207
|
export type OpenCodeMcpConfig = Record<string, {
|
|
159
208
|
type: "remote";
|
package/dist/index.js
CHANGED
|
@@ -54,7 +54,52 @@ export function assertWorkspacePlanSupported(plan) {
|
|
|
54
54
|
throw new Error(formatUnsupported(plan.unsupported));
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* Enforce plan supportability under the profile's `resources.failOnError`
|
|
59
|
+
* policy. Strict by default: `failOnError` absent or `true` throws on any
|
|
60
|
+
* unsupported dimension. An explicitly best-effort profile
|
|
61
|
+
* (`failOnError: false`) materializes the supported subset instead; the
|
|
62
|
+
* returned `"dimension: reason"` entries are what the caller must surface
|
|
63
|
+
* as a warning so the skipped dimensions stay visible to operators.
|
|
64
|
+
*/
|
|
65
|
+
export function enforceWorkspacePlanSupport(plan, failOnError) {
|
|
66
|
+
if (failOnError === false) {
|
|
67
|
+
return plan.unsupported.map(({ dimension, reason }) => `${dimension}: ${reason}`);
|
|
68
|
+
}
|
|
69
|
+
assertWorkspacePlanSupported(plan);
|
|
70
|
+
return [];
|
|
71
|
+
}
|
|
57
72
|
// ─── Verified per-harness conventions (capability matrix) ───────────────────
|
|
73
|
+
/**
|
|
74
|
+
* Whether the harness LOADS its {@link CONTEXT_FILE} from cwd on its own.
|
|
75
|
+
*
|
|
76
|
+
* True for every harness whose CLI discovers the file by name (CLAUDE.md,
|
|
77
|
+
* AGENTS.md, …). opencode is the exception: it reads only what its
|
|
78
|
+
* `opencode.json` `instructions[]` names, so a caller writing context for
|
|
79
|
+
* opencode must register the path as well as write it — see the `instructions`
|
|
80
|
+
* lowering below, and the interactive TUI's persona fold.
|
|
81
|
+
*/
|
|
82
|
+
export function harnessAutoLoadsContextFile(harness) {
|
|
83
|
+
return canonicalHarness(harness) !== "opencode";
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* The project context file this harness reads from cwd — its stable, documented
|
|
87
|
+
* ALWAYS-LOADED surface. Exported so a caller lowering additive context (e.g. a
|
|
88
|
+
* bare TUI's persona, which has no per-message system channel) targets the same
|
|
89
|
+
* file the materializer writes instructions to, instead of keeping its own copy
|
|
90
|
+
* of this per-harness map.
|
|
91
|
+
*/
|
|
92
|
+
export function harnessContextFile(harness) {
|
|
93
|
+
// The map is keyed by HarnessId, but callers reach this from runtime strings
|
|
94
|
+
// (a registry spec name, a request field). A miss would otherwise return
|
|
95
|
+
// undefined behind a `string` return type and surface as an undefined write
|
|
96
|
+
// path much later.
|
|
97
|
+
const path = CONTEXT_FILE[canonicalHarness(harness)];
|
|
98
|
+
if (!path) {
|
|
99
|
+
throw new Error(`No project context file is known for harness "${harness}"`);
|
|
100
|
+
}
|
|
101
|
+
return path;
|
|
102
|
+
}
|
|
58
103
|
/** Project context file each harness actually reads from cwd. NEVER a constant. */
|
|
59
104
|
const CONTEXT_FILE = {
|
|
60
105
|
"claude-code": "CLAUDE.md",
|
|
@@ -70,8 +115,15 @@ const CONTEXT_FILE = {
|
|
|
70
115
|
hermes: "HERMES.md", // highest-priority native name; only ONE context loads (first match)
|
|
71
116
|
openclaw: "AGENTS.md",
|
|
72
117
|
};
|
|
73
|
-
/**
|
|
74
|
-
|
|
118
|
+
/**
|
|
119
|
+
* cwd-relative native skill dir per harness, or null if the harness has no
|
|
120
|
+
* cwd skill primitive (skills live in a user/global dir the workspace plan
|
|
121
|
+
* cannot reach). Public so consumers composing a profile can (a) render the
|
|
122
|
+
* exact path the platform will mount a skill at into their prompts instead
|
|
123
|
+
* of hardcoding `~/.claude/skills/...`, and (b) branch on skill support
|
|
124
|
+
* ahead of materialization instead of catching the materialize-time throw.
|
|
125
|
+
*/
|
|
126
|
+
export const HARNESS_SKILL_DIRS = {
|
|
75
127
|
"claude-code": ".claude/skills",
|
|
76
128
|
claude: ".claude/skills",
|
|
77
129
|
claudish: ".claude/skills",
|
|
@@ -85,6 +137,18 @@ const SKILL_DIR = {
|
|
|
85
137
|
hermes: null, // skills are ~/.hermes/skills (user) only — no cwd discovery
|
|
86
138
|
openclaw: "skills", // <workspace>/skills/<name>/SKILL.md
|
|
87
139
|
};
|
|
140
|
+
const SKILL_DIR = HARNESS_SKILL_DIRS;
|
|
141
|
+
/**
|
|
142
|
+
* Resolve the cwd-relative directory `resources.skills` refs materialize
|
|
143
|
+
* into on `harness` (after alias canonicalization: claude/claudish →
|
|
144
|
+
* claude-code, kimi → kimi-code). `null` means the harness cannot receive
|
|
145
|
+
* cwd skills — the same condition that surfaces as an `unsupported`
|
|
146
|
+
* "skills" entry (and an {@link assertWorkspacePlanSupported} throw) at
|
|
147
|
+
* materialize time, exposed here as branchable data.
|
|
148
|
+
*/
|
|
149
|
+
export function skillDirForHarness(harness) {
|
|
150
|
+
return HARNESS_SKILL_DIRS[canonicalHarness(harness)];
|
|
151
|
+
}
|
|
88
152
|
function canonicalHarness(h) {
|
|
89
153
|
if (h === "claude" || h === "claudish")
|
|
90
154
|
return "claude-code";
|
|
@@ -1310,33 +1374,38 @@ function claudeToolName(name) {
|
|
|
1310
1374
|
return "WebFetch";
|
|
1311
1375
|
return CLAUDE_TOOL_NAMES[normalizedControlName(name)];
|
|
1312
1376
|
}
|
|
1313
|
-
|
|
1377
|
+
/** Tool IDs reported by the OpenCode binary shipped in the Sandbox image. */
|
|
1378
|
+
export const OPENCODE_NATIVE_TOOL_IDS = [
|
|
1379
|
+
"apply_patch",
|
|
1314
1380
|
"bash",
|
|
1315
|
-
"batch",
|
|
1316
1381
|
"codesearch",
|
|
1317
1382
|
"edit",
|
|
1318
1383
|
"glob",
|
|
1319
1384
|
"grep",
|
|
1320
|
-
"
|
|
1321
|
-
"lsp",
|
|
1322
|
-
"multiedit",
|
|
1323
|
-
"patch",
|
|
1385
|
+
"question",
|
|
1324
1386
|
"read",
|
|
1387
|
+
"skill",
|
|
1325
1388
|
"task",
|
|
1326
|
-
"todoread",
|
|
1327
1389
|
"todowrite",
|
|
1328
1390
|
"webfetch",
|
|
1329
1391
|
"websearch",
|
|
1330
1392
|
"write",
|
|
1331
|
-
]
|
|
1332
|
-
|
|
1393
|
+
];
|
|
1394
|
+
const OPENCODE_NATIVE_TOOL_ID_SET = new Set(OPENCODE_NATIVE_TOOL_IDS);
|
|
1395
|
+
/** Resolve portable and legacy profile names to current OpenCode tool IDs. */
|
|
1396
|
+
export function openCodeNativeToolId(name) {
|
|
1333
1397
|
const web = nativeWebToolKind(name);
|
|
1334
1398
|
if (web === "search")
|
|
1335
1399
|
return "websearch";
|
|
1336
1400
|
if (web === "fetch")
|
|
1337
1401
|
return "webfetch";
|
|
1338
1402
|
const normalized = normalizedControlName(name);
|
|
1339
|
-
|
|
1403
|
+
if (normalized === "patch")
|
|
1404
|
+
return "apply_patch";
|
|
1405
|
+
return OPENCODE_NATIVE_TOOL_ID_SET.has(normalized) ? normalized : undefined;
|
|
1406
|
+
}
|
|
1407
|
+
function openCodeToolName(name) {
|
|
1408
|
+
return openCodeNativeToolId(name);
|
|
1340
1409
|
}
|
|
1341
1410
|
function openCodePermissionName(name) {
|
|
1342
1411
|
const normalized = normalizedControlName(name);
|
|
@@ -1350,7 +1419,7 @@ function openCodePermissionName(name) {
|
|
|
1350
1419
|
if (["bash", "mcp", "doom_loop", "external_directory"].includes(normalized)) {
|
|
1351
1420
|
return normalized;
|
|
1352
1421
|
}
|
|
1353
|
-
return normalized !== "read" &&
|
|
1422
|
+
return normalized !== "read" && OPENCODE_NATIVE_TOOL_ID_SET.has(normalized)
|
|
1354
1423
|
? normalized
|
|
1355
1424
|
: undefined;
|
|
1356
1425
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/agent-profile-materialize",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
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
|
"main": "./dist/index.js",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"dist"
|
|
17
17
|
],
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@tangle-network/agent-interface": "0.
|
|
19
|
+
"@tangle-network/agent-interface": "0.31.0"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"@types/node": "25.6.0",
|