@tangle-network/agent-profile-materialize 0.8.0 → 0.9.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/index.d.ts CHANGED
@@ -22,7 +22,9 @@
22
22
  * cli-bridge and agent-dev-container — this is the single source those re-export.
23
23
  */
24
24
  import type { AgentCandidateConfigValue, AgentCandidateProfile, AgentCandidateProfileActivation, AgentCandidateProfileApplication, AgentCandidateProfilePlanEvidence, AgentCandidateProfilePlanMaterial, AgentProfile, AgentProfileMcpServer, Sha256Digest } from "@tangle-network/agent-interface";
25
- export type HarnessId = "claude-code" | "claude" | "claudish" | "nanoclaw" | "codex" | "opencode" | "kimi-code" | "kimi" | "pi" | "gemini" | "hermes" | "openclaw";
25
+ export declare const MATERIALIZER_HARNESSES: readonly ["claude-code", "nanoclaw", "codex", "opencode", "kimi-code", "pi", "gemini", "hermes", "openclaw"];
26
+ export type HarnessId = (typeof MATERIALIZER_HARNESSES)[number];
27
+ export declare function isMaterializerHarness(harness: string): harness is HarnessId;
26
28
  export interface PlanFile {
27
29
  relPath: string;
28
30
  content: string;
@@ -157,9 +159,8 @@ export declare function harnessContextFile(harness: HarnessId): string;
157
159
  export declare const HARNESS_SKILL_DIRS: Readonly<Record<HarnessId, string | null>>;
158
160
  /**
159
161
  * Resolve the cwd-relative directory `resources.skills` refs materialize
160
- * into on `harness` (after alias canonicalization: claude/claudish →
161
- * claude-code, kimi → kimi-code). `null` means the harness cannot receive
162
- * cwd skills — the same condition that surfaces as an `unsupported`
162
+ * into on `harness`. `null` means the harness cannot receive cwd skills, the
163
+ * same condition that surfaces as an `unsupported`
163
164
  * "skills" entry (and an {@link assertWorkspacePlanSupported} throw) at
164
165
  * materialize time, exposed here as branchable data.
165
166
  */
@@ -170,7 +171,10 @@ export interface ResolveAgentProfileResourcesOptions {
170
171
  fetch?: typeof globalThis.fetch;
171
172
  timeoutMs?: number;
172
173
  }
173
- /** Resolve every GitHub resource to exact inline bytes before materialization. */
174
+ /**
175
+ * Resolve every GitHub resource to exact inline bytes before materialization.
176
+ * The profile's default harness does not affect resource resolution.
177
+ */
174
178
  export declare function resolveAgentProfileResources(inputProfile: AgentProfile, options?: ResolveAgentProfileResourcesOptions): Promise<AgentProfile>;
175
179
  /** Normalize any skill markdown (VB frontmatter `skill:`/`description:`, or raw) into a
176
180
  * harness SKILL.md with `name:`/`description:` frontmatter the harnesses require. */
@@ -178,6 +182,7 @@ export declare function normalizeSkillMd(name: string, raw: string): string;
178
182
  /**
179
183
  * Build the WorkspacePlan for `profile` on `harness`. Pure — no IO. The caller
180
184
  * writes plan.files into the run cwd and applies plan.env/plan.flags to the spawn.
185
+ * The explicit target harness takes precedence over the profile's default.
181
186
  */
182
187
  export declare function materializeProfile(inputProfile: AgentProfile, harness: HarnessId, opts?: {
183
188
  skip?: Unsupported["dimension"][];
package/dist/index.js CHANGED
@@ -54,6 +54,26 @@ function resolveInlineRef(ref) {
54
54
  }
55
55
  return null; // github / unresolved
56
56
  }
57
+ export const MATERIALIZER_HARNESSES = [
58
+ "claude-code",
59
+ "nanoclaw",
60
+ "codex",
61
+ "opencode",
62
+ "kimi-code",
63
+ "pi",
64
+ "gemini",
65
+ "hermes",
66
+ "openclaw",
67
+ ];
68
+ const MATERIALIZER_HARNESS_SET = new Set(MATERIALIZER_HARNESSES);
69
+ export function isMaterializerHarness(harness) {
70
+ return MATERIALIZER_HARNESS_SET.has(harness);
71
+ }
72
+ function assertMaterializerHarness(harness) {
73
+ if (!isMaterializerHarness(harness)) {
74
+ throw new Error(`Unsupported profile materialization harness "${harness}"`);
75
+ }
76
+ }
57
77
  /** The plan was applied, but one or more private staging files remain. */
58
78
  export class WorkspacePlanCleanupError extends AggregateError {
59
79
  receipt;
@@ -97,7 +117,8 @@ export function enforceWorkspacePlanSupport(plan, failOnError) {
97
117
  * lowering below, and the interactive TUI's persona fold.
98
118
  */
99
119
  export function harnessAutoLoadsContextFile(harness) {
100
- return canonicalHarness(harness) !== "opencode";
120
+ assertMaterializerHarness(harness);
121
+ return harness !== "opencode";
101
122
  }
102
123
  /**
103
124
  * The project context file this harness reads from cwd — its stable, documented
@@ -107,11 +128,12 @@ export function harnessAutoLoadsContextFile(harness) {
107
128
  * of this per-harness map.
108
129
  */
109
130
  export function harnessContextFile(harness) {
131
+ assertMaterializerHarness(harness);
110
132
  // The map is keyed by HarnessId, but callers reach this from runtime strings
111
133
  // (a registry spec name, a request field). A miss would otherwise return
112
134
  // undefined behind a `string` return type and surface as an undefined write
113
135
  // path much later.
114
- const path = CONTEXT_FILE[canonicalHarness(harness)];
136
+ const path = CONTEXT_FILE[harness];
115
137
  if (!path) {
116
138
  throw new Error(`No project context file is known for harness "${harness}"`);
117
139
  }
@@ -120,13 +142,10 @@ export function harnessContextFile(harness) {
120
142
  /** Project context file each harness actually reads from cwd. NEVER a constant. */
121
143
  const CONTEXT_FILE = {
122
144
  "claude-code": "CLAUDE.md",
123
- claude: "CLAUDE.md",
124
- claudish: "CLAUDE.md",
125
145
  nanoclaw: "CLAUDE.md",
126
146
  codex: "AGENTS.md",
127
147
  opencode: ".opencode/profile-instructions.md",
128
148
  "kimi-code": "AGENTS.md",
129
- kimi: "AGENTS.md",
130
149
  pi: "AGENTS.md",
131
150
  gemini: "GEMINI.md", // current default; AGENTS.md only via .gemini/settings.json context.fileName
132
151
  hermes: "HERMES.md", // highest-priority native name; only ONE context loads (first match)
@@ -142,13 +161,10 @@ const CONTEXT_FILE = {
142
161
  */
143
162
  export const HARNESS_SKILL_DIRS = {
144
163
  "claude-code": ".claude/skills",
145
- claude: ".claude/skills",
146
- claudish: ".claude/skills",
147
164
  nanoclaw: ".claude/skills",
148
165
  codex: ".codex/skills",
149
166
  opencode: ".opencode/skills",
150
167
  "kimi-code": ".kimi/skills",
151
- kimi: ".kimi/skills",
152
168
  pi: ".pi/skills",
153
169
  gemini: ".gemini/skills",
154
170
  hermes: null, // skills are ~/.hermes/skills (user) only — no cwd discovery
@@ -157,21 +173,14 @@ export const HARNESS_SKILL_DIRS = {
157
173
  const SKILL_DIR = HARNESS_SKILL_DIRS;
158
174
  /**
159
175
  * Resolve the cwd-relative directory `resources.skills` refs materialize
160
- * into on `harness` (after alias canonicalization: claude/claudish →
161
- * claude-code, kimi → kimi-code). `null` means the harness cannot receive
162
- * cwd skills — the same condition that surfaces as an `unsupported`
176
+ * into on `harness`. `null` means the harness cannot receive cwd skills, the
177
+ * same condition that surfaces as an `unsupported`
163
178
  * "skills" entry (and an {@link assertWorkspacePlanSupported} throw) at
164
179
  * materialize time, exposed here as branchable data.
165
180
  */
166
181
  export function skillDirForHarness(harness) {
167
- return HARNESS_SKILL_DIRS[canonicalHarness(harness)];
168
- }
169
- function canonicalHarness(h) {
170
- if (h === "claude" || h === "claudish")
171
- return "claude-code";
172
- if (h === "kimi")
173
- return "kimi-code";
174
- return h;
182
+ assertMaterializerHarness(harness);
183
+ return HARNESS_SKILL_DIRS[harness];
175
184
  }
176
185
  // ─── Helpers ────────────────────────────────────────────────────────────────
177
186
  const FRONTMATTER_RE = /^---\n([\s\S]*?)\n---\n?([\s\S]*)$/;
@@ -283,7 +292,10 @@ function validateFileMode(mode) {
283
292
  }
284
293
  return normalized;
285
294
  }
286
- /** Resolve every GitHub resource to exact inline bytes before materialization. */
295
+ /**
296
+ * Resolve every GitHub resource to exact inline bytes before materialization.
297
+ * The profile's default harness does not affect resource resolution.
298
+ */
287
299
  export async function resolveAgentProfileResources(inputProfile, options = {}) {
288
300
  const profile = agentProfileSchema.parse(inputProfile);
289
301
  const resources = profile.resources;
@@ -429,10 +441,12 @@ const tomlStr = (s) => JSON.stringify(s); // valid TOML basic string
429
441
  /**
430
442
  * Build the WorkspacePlan for `profile` on `harness`. Pure — no IO. The caller
431
443
  * writes plan.files into the run cwd and applies plan.env/plan.flags to the spawn.
444
+ * The explicit target harness takes precedence over the profile's default.
432
445
  */
433
446
  export function materializeProfile(inputProfile, harness, opts = {}) {
447
+ assertMaterializerHarness(harness);
434
448
  const profile = agentProfileSchema.parse(inputProfile);
435
- const h = canonicalHarness(harness);
449
+ const h = harness;
436
450
  const skip = new Set(opts.skip ?? []);
437
451
  const plan = {
438
452
  harness: h,
@@ -637,14 +651,7 @@ function activeMcpServers(servers) {
637
651
  return Object.fromEntries(Object.entries(servers ?? {}).filter((entry) => entry[1].enabled !== false));
638
652
  }
639
653
  function extensionNamespaces(harness) {
640
- switch (harness) {
641
- case "claude-code":
642
- return ["claude-code", "claude"];
643
- case "kimi-code":
644
- return ["kimi-code", "kimi"];
645
- default:
646
- return [harness];
647
- }
654
+ return [harness];
648
655
  }
649
656
  /**
650
657
  * Materialize a sealed candidate profile without weakening its process values.
@@ -652,6 +659,7 @@ function extensionNamespaces(harness) {
652
659
  * argv, and environment values separately. Other hook formats fail closed.
653
660
  */
654
661
  export function materializeCandidateProfile(profile, harness, opts = {}) {
662
+ assertMaterializerHarness(harness);
655
663
  const candidate = agentCandidateProfileSchema.parse(profile);
656
664
  verifyCandidateInlineResources(candidate);
657
665
  const hookLowering = lowerCandidateHooks(candidate.hooks, harness);
@@ -692,7 +700,7 @@ export function materializeCandidateProfile(profile, harness, opts = {}) {
692
700
  function lowerCandidateHooks(hooks, harness) {
693
701
  if (!hooks || Object.keys(hooks).length === 0)
694
702
  return { wrapperFiles: [] };
695
- const nativeHarness = canonicalHarness(harness);
703
+ const nativeHarness = harness;
696
704
  if (nativeHarness !== "codex" && nativeHarness !== "claude-code") {
697
705
  return {
698
706
  wrapperFiles: [],
@@ -939,7 +947,7 @@ export function hashWorkspacePlan(plan) {
939
947
  const env = Object.fromEntries(Object.entries(plan.env).sort(([a], [b]) => compareStrings(a, b)));
940
948
  const unsupported = [...plan.unsupported].sort((a, b) => compareStrings(`${a.dimension}\0${a.reason}`, `${b.dimension}\0${b.reason}`));
941
949
  const canonical = JSON.stringify({
942
- harness: canonicalHarness(plan.harness),
950
+ harness: plan.harness,
943
951
  files,
944
952
  env,
945
953
  flags: [...plan.flags],
@@ -954,7 +962,7 @@ export function hashWorkspacePlan(plan) {
954
962
  export function agentCandidateProfilePlanMaterial(plan) {
955
963
  const material = {
956
964
  sourceProfileDigest: plan.sourceProfileDigest,
957
- harness: canonicalHarness(plan.harness),
965
+ harness: plan.harness,
958
966
  files: normalizePlanFiles(plan.files).map((file) => ({
959
967
  relPath: file.relPath,
960
968
  mode: file.mode,
@@ -1884,7 +1892,7 @@ const GEMINI_HOOK_EVENTS = new Set([
1884
1892
  "SessionStart",
1885
1893
  ]);
1886
1894
  function hookEventSupported(harness, event) {
1887
- switch (canonicalHarness(harness)) {
1895
+ switch (harness) {
1888
1896
  case "claude-code":
1889
1897
  return CLAUDE_HOOK_EVENTS.has(event);
1890
1898
  case "codex":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/agent-profile-materialize",
3
- "version": "0.8.0",
3
+ "version": "0.9.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
  "main": "./dist/index.js",
@@ -16,12 +16,12 @@
16
16
  "dist"
17
17
  ],
18
18
  "dependencies": {
19
- "@tangle-network/agent-interface": "0.33.0"
19
+ "@tangle-network/agent-interface": "0.35.0"
20
20
  },
21
21
  "devDependencies": {
22
22
  "@types/node": "25.6.0",
23
23
  "typescript": "^6.0.3",
24
- "vitest": "^4.1.5"
24
+ "vitest": "^4.1.10"
25
25
  },
26
26
  "scripts": {
27
27
  "build": "tsc -p tsconfig.json",