@tangle-network/agent-profile-materialize 0.28.3 → 0.28.5

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.
@@ -1,4 +1,6 @@
1
1
  import { dirname, join } from "node:path";
2
+ import { CODEX_SYSTEM_PROMPT_FILE } from "./harness.js";
3
+ import { bindWorkspaceLaunchFlags } from "./workspace-launch.js";
2
4
  /**
3
5
  * Paths a secret OpenCode revision serves from its own immutable tree:
4
6
  * read-only profile content under `reference/`, skills, and OpenCode's
@@ -6,21 +8,6 @@ import { dirname, join } from "node:path";
6
8
  * `.opencode/{agent,agents}/**\/*.md` and `.opencode/{command,commands}/**\/*.md`).
7
9
  */
8
10
  const SECRET_REVISION_NATIVE_PATH = /^(?:reference\/|\.opencode\/skills\/|\.opencode\/(?:agents?|commands?)\/(?:[^./][^/]*\/)*[^./][^/]*\.md$)/u;
9
- /**
10
- * A secret OpenCode turn launches with its revision's `workspace` as cwd and
11
- * that tree's `.opencode` as its explicit config directory (the sealed launch
12
- * in sdk-provider-opencode). So skills, agents and commands load from the
13
- * digest-attested revision, and a cwd-relative `reference/` path resolves to
14
- * this revision's own read-only copy, never to the shared workspace that
15
- * concurrent revisions share. The binding stays limited to these paths, never
16
- * an app's own directory.
17
- */
18
- function ridesSecretRevision(plan, file, options) {
19
- return (options.secretOpenCodeRevision === true &&
20
- plan.harness === "opencode" &&
21
- (file.root ?? "workspace") === "workspace" &&
22
- SECRET_REVISION_NATIVE_PATH.test(file.relPath));
23
- }
24
11
  export function bindConcurrentLaunch(plan, application, revisionWorkspaceDir, options = {}) {
25
12
  if (plan.requires?.length) {
26
13
  throw new Error(`Concurrent ${plan.harness} profile needs a native extension that this launcher cannot attest`);
@@ -28,7 +15,7 @@ export function bindConcurrentLaunch(plan, application, revisionWorkspaceDir, op
28
15
  if (application.spawn.systemPromptBinding) {
29
16
  throw new Error(`Concurrent ${plan.harness} profile has an unbound system prompt control`);
30
17
  }
31
- const flags = [...application.spawn.flags];
18
+ const flags = bindWorkspaceLaunchFlags(plan, application.spawn.flags, revisionWorkspaceDir);
32
19
  const env = { ...application.spawn.env };
33
20
  const supportedFiles = new Set();
34
21
  const bindFlagFile = (flag, relPath) => {
@@ -50,14 +37,11 @@ export function bindConcurrentLaunch(plan, application, revisionWorkspaceDir, op
50
37
  }
51
38
  break;
52
39
  case "codex":
53
- if (plan.files.some((file) => file.relPath === ".codex/system-prompt.md")) {
54
- const original = "model_instructions_file=.codex/system-prompt.md";
55
- const index = flags.findIndex((value, at) => value === original && flags[at - 1] === "-c");
56
- if (index < 0)
40
+ if (plan.files.some((file) => file.relPath === CODEX_SYSTEM_PROMPT_FILE)) {
41
+ if (!plan.flags.some((value, at) => value === `model_instructions_file=${CODEX_SYSTEM_PROMPT_FILE}` &&
42
+ plan.flags[at - 1] === "-c"))
57
43
  throw new Error("Concurrent codex profile lacks model_instructions_file binding");
58
- flags[index] =
59
- `model_instructions_file=${join(revisionWorkspaceDir, ".codex/system-prompt.md")}`;
60
- supportedFiles.add(".codex/system-prompt.md");
44
+ supportedFiles.add(CODEX_SYSTEM_PROMPT_FILE);
61
45
  }
62
46
  break;
63
47
  // kimi-code has no case: kimi-code 2.x discovers MCP only from
@@ -91,7 +75,13 @@ export function bindConcurrentLaunch(plan, application, revisionWorkspaceDir, op
91
75
  // its directory, absolute in that revision, joins the launch PATH.
92
76
  const toolBinDirs = [];
93
77
  for (const file of plan.files) {
94
- if (ridesSecretRevision(plan, file, options)) {
78
+ // Secret OpenCode launches from its immutable revision workspace, with
79
+ // that revision's .opencode as config directory. These relative native
80
+ // resources therefore read the revision, never the shared workspace.
81
+ if (options.secretOpenCodeRevision === true &&
82
+ plan.harness === "opencode" &&
83
+ (file.root ?? "workspace") === "workspace" &&
84
+ SECRET_REVISION_NATIVE_PATH.test(file.relPath)) {
95
85
  if (file.relPath.startsWith("reference/") &&
96
86
  ((file.mode ?? 0) & 0o111) !== 0) {
97
87
  const dir = join(revisionWorkspaceDir, dirname(file.relPath));
@@ -4,6 +4,7 @@ import { basename, dirname, extname, join, resolve, sep } from "node:path";
4
4
  import { applyWorkspacePlanWithSecretProvider } from "./plan-application.js";
5
5
  import { hashWorkspacePlan } from "./plan-digest.js";
6
6
  import { normalizeWorkspaceRelativePath } from "./workspace-files.js";
7
+ import { bindWorkspaceLaunchFlags } from "./workspace-launch.js";
7
8
  export function assertPrivateManagedRoot(root) {
8
9
  if (!root || resolve(root) !== root) {
9
10
  throw new Error("Managed workspace root must be an absolute path");
@@ -88,7 +89,10 @@ export async function prepareManagedWorkspacePlan(plan, managedRoot, sharedWorks
88
89
  sha256: createHash("sha256").update(content).digest("hex"),
89
90
  }));
90
91
  const spawnDigest = createHash("sha256")
91
- .update(JSON.stringify(application.spawn))
92
+ .update(JSON.stringify({
93
+ ...application.spawn,
94
+ flags: bindWorkspaceLaunchFlags(plan, application.spawn.flags),
95
+ }))
92
96
  .digest("hex");
93
97
  const materializedDigest = `sha256:${createHash("sha256")
94
98
  .update(JSON.stringify({
@@ -94,7 +94,14 @@ export async function preparePublicConcurrentProfileRevision(input) {
94
94
  return {
95
95
  directory,
96
96
  manifest: prepared.manifest,
97
- application: { ...prepared.application, spawn },
97
+ application: {
98
+ ...prepared.application,
99
+ receipt: {
100
+ ...prepared.application.receipt,
101
+ flags: [...spawn.flags],
102
+ },
103
+ spawn,
104
+ },
98
105
  };
99
106
  }
100
107
  catch (error) {
@@ -1,7 +1,3 @@
1
- const receiptFile = (file) => ({
2
- root: file.root,
3
- relPath: file.relPath,
4
- });
5
1
  function readApplyInput(publicPlan, workspaceDir, options) {
6
2
  const workspaceRoot = resolve(workspaceDir);
7
3
  if (options.agentDir !== undefined && !isAbsolute(options.agentDir)) {
@@ -47,27 +43,42 @@ function selectResumeFiles(files, ownership, privateFilePaths) {
47
43
  return matches && !hasCurrentOwnership;
48
44
  });
49
45
  }
50
- function spawnFor(resolvedPlan) {
51
- return {
52
- env: { ...resolvedPlan.env },
53
- flags: [...resolvedPlan.flags],
54
- ...promptIntentFields(resolvedPlan, samePromptIntentValue),
55
- ...systemPromptBindingField(resolvedPlan),
56
- };
57
- }
58
46
  export function applyResolvedWorkspacePlan(publicPlan, resolvedPlan, workspaceDir, options, publicPlanDigest = hashWorkspacePlan(publicPlan)) {
59
47
  const { rootFor, runtimeOnlyFiles, ownership, privateFilePaths, existingFiles, } = readApplyInput(publicPlan, workspaceDir, options);
60
- const resolvedFiles = resolvedPlan.files;
61
- const privateRuntimeFiles = Object.fromEntries(resolvedFiles
48
+ const privateRuntimeFiles = Object.fromEntries(resolvedPlan.files
62
49
  .filter((file) => file.root === "workspace" && runtimeOnlyFiles.has(file.relPath))
63
50
  .map((file) => [file.relPath, file.content]));
64
- const files = resolvedFiles
51
+ const files = resolvedPlan.files
65
52
  .filter((file) => file.root !== "workspace" || !runtimeOnlyFiles.has(file.relPath))
66
53
  .map((file) => ({
67
54
  ...file,
68
55
  rootDir: rootFor(file.root),
69
56
  absPath: resolveWorkspaceFilePath(rootFor(file.root), file.relPath),
70
57
  }));
58
+ const application = {
59
+ receipt: {
60
+ workspacePlanDigest: publicPlanDigest,
61
+ env: Object.fromEntries(Object.entries(publicPlan.env).map(([name, value]) => [
62
+ name,
63
+ typeof value === "string" ? value : { ...value },
64
+ ])),
65
+ flags: bindWorkspaceLaunchFlags(publicPlan, publicPlan.flags, workspaceDir).map(normalizeWorkspacePlanArgument),
66
+ ...promptIntentFields(publicPlan, samePromptIntentValue),
67
+ ...systemPromptBindingField(publicPlan),
68
+ unsupported: publicPlan.unsupported.map((entry) => ({ ...entry })),
69
+ written: files
70
+ .filter((file) => file.root === "workspace")
71
+ .map((file) => file.relPath),
72
+ writtenFiles: files.map(({ root, relPath }) => ({ root, relPath })),
73
+ },
74
+ spawn: {
75
+ env: { ...resolvedPlan.env },
76
+ flags: bindWorkspaceLaunchFlags(publicPlan, resolvedPlan.flags, workspaceDir),
77
+ ...promptIntentFields(resolvedPlan, samePromptIntentValue),
78
+ ...systemPromptBindingField(resolvedPlan),
79
+ },
80
+ runtimeOnlyFiles: privateRuntimeFiles,
81
+ };
71
82
  // `reject` does not resume because it requires an empty target.
72
83
  const resumeSamePlan = existingFiles === "allow-identical" &&
73
84
  options.appliedPlanDigest !== undefined &&
@@ -76,11 +87,7 @@ export function applyResolvedWorkspacePlan(publicPlan, resolvedPlan, workspaceDi
76
87
  ? selectResumeFiles(files, ownership, privateFilePaths)
77
88
  : files;
78
89
  if (resumeSamePlan && filesToApply.length === 0) {
79
- return {
80
- receipt: safeWorkspacePlanReceipt(publicPlan, files.map(receiptFile), publicPlanDigest),
81
- spawn: spawnFor(resolvedPlan),
82
- runtimeOnlyFiles: privateRuntimeFiles,
83
- };
90
+ return application;
84
91
  }
85
92
  const context = {
86
93
  rootFor,
@@ -94,8 +101,6 @@ export function applyResolvedWorkspacePlan(publicPlan, resolvedPlan, workspaceDi
94
101
  // workspace state that this call does not own.
95
102
  for (const file of writes) {
96
103
  mkdirSync(dirname(file.absPath), { recursive: true });
97
- }
98
- for (const file of writes) {
99
104
  assertNoSymlinkPathComponents(file.rootDir, file.absPath);
100
105
  }
101
106
  const prepared = [];
@@ -105,30 +110,8 @@ export function applyResolvedWorkspacePlan(publicPlan, resolvedPlan, workspaceDi
105
110
  catch (error) {
106
111
  rollbackPublished(prepared, error);
107
112
  }
108
- const receipt = safeWorkspacePlanReceipt(publicPlan, files.map(receiptFile), publicPlanDigest);
109
- removeTemporaries(prepared, receipt);
110
- return {
111
- receipt,
112
- spawn: spawnFor(resolvedPlan),
113
- runtimeOnlyFiles: privateRuntimeFiles,
114
- };
115
- }
116
- function safeWorkspacePlanReceipt(plan, writtenFiles, workspacePlanDigest = hashWorkspacePlan(plan)) {
117
- return {
118
- workspacePlanDigest,
119
- env: Object.fromEntries(Object.entries(plan.env).map(([name, value]) => [
120
- name,
121
- typeof value === "string" ? value : { ...value },
122
- ])),
123
- flags: plan.flags.map(normalizeWorkspacePlanArgument),
124
- ...promptIntentFields(plan, samePromptIntentValue),
125
- ...systemPromptBindingField(plan),
126
- unsupported: plan.unsupported.map((entry) => ({ ...entry })),
127
- written: writtenFiles
128
- .filter((file) => file.root === "workspace")
129
- .map((file) => file.relPath),
130
- writtenFiles: writtenFiles.map((file) => ({ ...file })),
131
- };
113
+ removeTemporaries(prepared, application.receipt);
114
+ return application;
132
115
  }
133
116
  import { mkdirSync } from "node:fs";
134
117
  import { dirname, isAbsolute, resolve } from "node:path";
@@ -138,3 +121,4 @@ import { promptIntentFields, samePromptIntentValue, systemPromptBindingField, }
138
121
  import { assertNoSymlinkPathComponents, resolveWorkspaceFilePath, validateWorkspaceOwnership, workspaceFileExists, workspaceFileMatches, } from "./workspace-apply-files.js";
139
122
  import { fileIdentity, holdsCurrentOwnership, planWrites, publishWrites, removeTemporaries, rollbackPublished, } from "./workspace-apply-publish.js";
140
123
  import { normalizeWorkspaceRelativePath } from "./workspace-files.js";
124
+ import { bindWorkspaceLaunchFlags } from "./workspace-launch.js";
@@ -0,0 +1,3 @@
1
+ import type { WorkspacePlan } from "./plan-types.js";
2
+ /** Bind launch paths; omit the workspace to hash portable content identity. */
3
+ export declare function bindWorkspaceLaunchFlags<T>(plan: WorkspacePlan, flags: readonly T[], workspaceDir?: string): (T | string)[];
@@ -0,0 +1,13 @@
1
+ import { resolve } from "node:path";
2
+ import { CODEX_SYSTEM_PROMPT_FILE } from "./harness.js";
3
+ import { tomlStr } from "./plan-json.js";
4
+ /** Bind launch paths; omit the workspace to hash portable content identity. */
5
+ export function bindWorkspaceLaunchFlags(plan, flags, workspaceDir) {
6
+ return flags.map((flag, at) => plan.harness === "codex" &&
7
+ plan.flags[at - 1] === "-c" &&
8
+ plan.flags[at] === `model_instructions_file=${CODEX_SYSTEM_PROMPT_FILE}`
9
+ ? workspaceDir === undefined
10
+ ? `model_instructions_file=${CODEX_SYSTEM_PROMPT_FILE}`
11
+ : `model_instructions_file=${tomlStr(resolve(workspaceDir, CODEX_SYSTEM_PROMPT_FILE))}`
12
+ : flag);
13
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/agent-profile-materialize",
3
- "version": "0.28.3",
3
+ "version": "0.28.5",
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": "24a394c180d411cd9a0d639b32cbf4d2f128458d",
39
- "mergedAt": "2026-10-26T11:23:55-06:00"
38
+ "sourceSha": "02fcaa7cdd12f1cea201a8e929a7772232549e3e",
39
+ "mergedAt": "2026-10-26T14:54:59-06:00"
40
40
  },
41
41
  "scripts": {
42
42
  "build": "tsc -p tsconfig.json",