@tangle-network/agent-profile-materialize 0.6.0 → 0.8.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 +20 -0
- package/dist/index.js +34 -8
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -28,6 +28,26 @@ export interface PlanFile {
|
|
|
28
28
|
content: string;
|
|
29
29
|
/** chmod mode (e.g. 0o755 for hook scripts); default 0o644. */
|
|
30
30
|
mode?: number;
|
|
31
|
+
/**
|
|
32
|
+
* Provenance of this entry. `"generated"` = the materializer synthesized the
|
|
33
|
+
* content itself (native harness config, merged settings, lowered prompt/MCP/
|
|
34
|
+
* hooks/subagents). `"user"` = the exact bytes of a profile-declared resource
|
|
35
|
+
* ref (`resources.files`/`tools`/`agents`/`skills`/`commands`), passed through
|
|
36
|
+
* verbatim at the path the profile author chose.
|
|
37
|
+
*
|
|
38
|
+
* A consumer that needs to reinterpret a SPECIFIC generated file (e.g. parse
|
|
39
|
+
* `opencode.json` as the synthetic native config) MUST gate on
|
|
40
|
+
* `source === "generated"` at that relPath — matching by relPath alone cannot
|
|
41
|
+
* tell a harness-authored config apart from a user resource mount that
|
|
42
|
+
* happens to share the same conventional path, and treating arbitrary user
|
|
43
|
+
* content as trusted generated config is a fail-open bug, not a fallback.
|
|
44
|
+
*
|
|
45
|
+
* Optional: callers that hand-construct a {@link WorkspacePlan} directly for
|
|
46
|
+
* {@link applyWorkspacePlan} (tests, ad hoc writes) are not materializer
|
|
47
|
+
* output and have no provenance to report. `materializeProfile` always sets
|
|
48
|
+
* this on every file it emits.
|
|
49
|
+
*/
|
|
50
|
+
source?: "user" | "generated";
|
|
31
51
|
}
|
|
32
52
|
export interface Unsupported {
|
|
33
53
|
dimension: "files" | "skills" | "mcp" | "hooks" | "subagents" | "commands" | "instructions" | "systemPrompt" | "connections" | "confidential" | "permissions" | "tools" | "modes" | "extensions";
|
package/dist/index.js
CHANGED
|
@@ -25,15 +25,32 @@ import { createHash, randomUUID } from "node:crypto";
|
|
|
25
25
|
import { closeSync, fchmodSync, constants as fsConstants, fstatSync, linkSync, lstatSync, mkdirSync, openSync, readFileSync, unlinkSync, writeFileSync, } from "node:fs";
|
|
26
26
|
import { basename, dirname, isAbsolute, join, relative, resolve, sep, win32, } from "node:path";
|
|
27
27
|
import { agentCandidateProfileActivationSchema, agentCandidateProfilePlanEvidenceSchema, agentCandidateProfilePlanMaterialSchema, agentCandidateProfileSchema, agentProfileSchema, canonicalCandidateBytes, canonicalCandidateDigest, resolveNativeWebTools, } from "@tangle-network/agent-interface";
|
|
28
|
+
function describeUnresolvedRef(r) {
|
|
29
|
+
const name = typeof r.name === "string" ? r.name : undefined;
|
|
30
|
+
const path = typeof r.path === "string" ? r.path : undefined;
|
|
31
|
+
if (name && path)
|
|
32
|
+
return `"${name}" (${path})`;
|
|
33
|
+
if (name)
|
|
34
|
+
return `"${name}"`;
|
|
35
|
+
if (path)
|
|
36
|
+
return `"${path}"`;
|
|
37
|
+
return "resource";
|
|
38
|
+
}
|
|
28
39
|
/** Resolve inline bytes already present on the profile. Remote resources must
|
|
29
|
-
* be resolved by {@link resolveAgentProfileResources} before materialization.
|
|
40
|
+
* be resolved by {@link resolveAgentProfileResources} before materialization.
|
|
41
|
+
*
|
|
42
|
+
* FAIL-CLOSED: a ref that declares `kind: "inline"` is asserting its bytes are
|
|
43
|
+
* already resolved, so missing/non-string `content` is a caller bug, not an
|
|
44
|
+
* empty file — silently coercing it (`String(content ?? "")`) would produce a
|
|
45
|
+
* file that "looks provisioned, loads nothing", exactly the trap this module's
|
|
46
|
+
* docstring warns against. */
|
|
30
47
|
function resolveInlineRef(ref) {
|
|
31
48
|
const r = ref;
|
|
32
|
-
if (
|
|
33
|
-
return {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
};
|
|
49
|
+
if (typeof r.content === "string") {
|
|
50
|
+
return { name: String(r.name ?? r.path ?? "resource"), content: r.content };
|
|
51
|
+
}
|
|
52
|
+
if (r.kind === "inline") {
|
|
53
|
+
throw new Error(`Profile resource ${describeUnresolvedRef(r)} declares kind "inline" but has no string content`);
|
|
37
54
|
}
|
|
38
55
|
return null; // github / unresolved
|
|
39
56
|
}
|
|
@@ -424,14 +441,23 @@ export function materializeProfile(inputProfile, harness, opts = {}) {
|
|
|
424
441
|
flags: [],
|
|
425
442
|
unsupported: [],
|
|
426
443
|
};
|
|
427
|
-
|
|
444
|
+
// Every plan file is tagged by provenance at the single push site: `add`
|
|
445
|
+
// (harness-generated native config) vs `addExplicitResource` (a profile
|
|
446
|
+
// resource ref's bytes, passed through verbatim). Downstream consumers that
|
|
447
|
+
// reinterpret a specific generated file by relPath (e.g. opencode.json as
|
|
448
|
+
// the synthetic native config) must gate on `source === "generated"` —
|
|
449
|
+
// otherwise a user resource mounted at the same conventional path is
|
|
450
|
+
// silently misread as trusted harness config.
|
|
451
|
+
const pushPlanFile = (relPath, content, source, mode) => {
|
|
428
452
|
const normalizedPath = normalizeWorkspaceRelativePath(relPath);
|
|
429
453
|
plan.files.push({
|
|
430
454
|
relPath: normalizedPath,
|
|
431
455
|
content,
|
|
456
|
+
source,
|
|
432
457
|
...(mode === undefined ? {} : { mode: validateFileMode(mode) }),
|
|
433
458
|
});
|
|
434
459
|
};
|
|
460
|
+
const add = (relPath, content, mode) => pushPlanFile(relPath, content, "generated", mode);
|
|
435
461
|
const unsupported = (dimension, reason) => plan.unsupported.push({ dimension, reason });
|
|
436
462
|
const explicitResourcePaths = new Map();
|
|
437
463
|
const addExplicitResource = (dimension, relPath, content, mode) => {
|
|
@@ -441,7 +467,7 @@ export function materializeProfile(inputProfile, harness, opts = {}) {
|
|
|
441
467
|
throw new Error(`Duplicate profile resource path: ${normalizedPath} (${previousDimension}, ${dimension})`);
|
|
442
468
|
}
|
|
443
469
|
explicitResourcePaths.set(normalizedPath, dimension);
|
|
444
|
-
|
|
470
|
+
pushPlanFile(normalizedPath, content, "user", mode);
|
|
445
471
|
};
|
|
446
472
|
// 1) GENERIC FILES → exact workspace-relative paths. Inline refs are
|
|
447
473
|
// deterministic; remote refs must be resolved by an async caller first.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/agent-profile-materialize",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.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.33.0"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"@types/node": "25.6.0",
|