@tangle-network/agent-profile-materialize 1.1.7 → 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 -106
  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 -31
  27. package/dist/managed-workspace.js +0 -271
  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 -249
  80. package/dist/secret-session-view.d.ts +0 -25
  81. package/dist/secret-session-view.js +0 -166
  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,341 +0,0 @@
1
- export type { HarnessId } from "./harness.js";
2
- /** The executor-owned root that receives a materialized file. */
3
- export type PlanFileRoot = "workspace" | "agent";
4
- export interface PlanFile {
5
- relPath: string;
6
- content: string;
7
- /** Defaults to the run workspace. `agent` is the harness's private agent directory. */
8
- root?: PlanFileRoot;
9
- /** chmod mode (e.g. 0o755 for hook scripts); default 0o644. */
10
- mode?: number;
11
- /**
12
- * Provenance of this entry. `"generated"` = the materializer synthesized the
13
- * content itself (native harness config, merged settings, lowered prompt/MCP/
14
- * hooks/subagents). `"user"` = the exact bytes of a profile-declared resource
15
- * ref (`resources.files`/`tools`/`agents`/`skills`/`commands`), passed through
16
- * verbatim at the path the profile author chose.
17
- *
18
- * A consumer that needs to reinterpret a SPECIFIC generated file (e.g. parse
19
- * `opencode.json` as the synthetic native config) MUST gate on
20
- * `source === "generated"` at that relPath — matching by relPath alone cannot
21
- * tell a harness-authored config apart from a user resource mount that
22
- * happens to share the same conventional path, and treating arbitrary user
23
- * content as trusted generated config is a fail-open bug, not a fallback.
24
- *
25
- * Optional: callers that hand-construct a {@link WorkspacePlan} directly for
26
- * {@link applyWorkspacePlan} (tests, ad hoc writes) are not materializer
27
- * output and have no provenance to report. `materializeProfile` always sets
28
- * this on every file it emits.
29
- */
30
- source?: "user" | "generated";
31
- /**
32
- * Who owns the PATH — which decides whether an existing file there may be
33
- * replaced.
34
- *
35
- * `"materializer"` = a path whose generated content is wholly this
36
- * package's output: a name this package invented, the generated context file
37
- * (`CLAUDE.md`, `AGENTS.md`, `GEMINI.md`, `HERMES.md`), or `opencode.json`,
38
- * which carries only the registrations generated from the profile. A file
39
- * already there is this materializer's own output from an earlier application
40
- * into the same workspace, and replacing it is how turn two of a resumed
41
- * session — or the first turn of a branched one — gets the current profile
42
- * instead of the previous one. Application replaces it only when the existing
43
- * bytes prove that provenance — by the name for an invented path, by a marker
44
- * line committing to the body digest for a context file, by the content for
45
- * `opencode.json`.
46
- *
47
- * `"shared"` (the default, and every other file) = a path the user or the
48
- * harness also owns. `.gemini/system.md` is a documented, user-authorable
49
- * name whose content this package cannot tell from its own, and so is a
50
- * context file mounted as an explicit profile resource rather than generated.
51
- * An existing file there may be the caller's own work, so it is never
52
- * replaced.
53
- *
54
- * Optional because hand-constructed plans have no path provenance to report;
55
- * absent reads as `"shared"`, the safe answer.
56
- */
57
- ownership?: "materializer" | "shared";
58
- /**
59
- * Secret leaves in a generated JSON document. `content` contains `null` at
60
- * each path until the private executor resolves the named reference. The
61
- * public plan, its digest, and its receipt retain only these references.
62
- */
63
- secretSlots?: PlanFileSecretSlot[];
64
- }
65
- export interface PlanFileSecretSlot {
66
- path: Array<string | number>;
67
- ref: AgentProfileSecretRef;
68
- }
69
- export interface WorkspacePlanSecretSegment {
70
- kind: "secret";
71
- ref: AgentProfileSecretRef;
72
- /** Encoding applied after private resolution. */
73
- encoding: "raw" | "json-string";
74
- }
75
- /** A launch argument whose private leaves are resolved immediately before spawn. */
76
- export interface WorkspacePlanArgumentTemplate {
77
- kind: "workspace-plan-argument-template";
78
- segments: Array<string | WorkspacePlanSecretSegment>;
79
- }
80
- export type WorkspacePlanArgument = string | WorkspacePlanArgumentTemplate;
81
- /** Public bytes stay strings; secret references stay opaque until application. */
82
- export type WorkspacePlanConfigValue = string | AgentProfileSecretRef;
83
- export interface Unsupported {
84
- dimension: "files" | "skills" | "mcp" | "hooks" | "subagents" | "commands" | "instructions" | "systemPrompt" | "appendSystemPrompt" | "connections" | "confidential" | "permissions" | "tools" | "modes" | "extensions";
85
- reason: string;
86
- }
87
- /**
88
- * The subset of {@link BINDABLE_LAUNCH_CONTROLS} the plan HANDS OVER rather than
89
- * executes: controls a caller reaches only by composing the harness's own
90
- * configuration, so no file, flag or env var the plan writes can touch them.
91
- *
92
- * - `opencode.agent-prompt` — opencode's `agent.<name>.prompt` config field,
93
- * the one control that replaces its built-in provider prompt. It binds to a
94
- * SINGLE agent, and the agent is chosen at launch. A caller that builds
95
- * opencode's config itself and picks the agent there reads neither
96
- * `plan.files` nor `plan.flags`, so for that one the plan names the control
97
- * and hands over the text in {@link WorkspacePlan.systemPromptBinding}.
98
- *
99
- * Every other bindable control names a capability the plan still lowers itself
100
- * once the caller has declared the plan's launch inputs really reach the spawn.
101
- */
102
- export declare const EXECUTOR_SYSTEM_PROMPT_BINDINGS: readonly ["opencode.agent-prompt"];
103
- export type ExecutorSystemPromptBinding = (typeof EXECUTOR_SYSTEM_PROMPT_BINDINGS)[number];
104
- /**
105
- * A system-prompt intent handed to the launcher that declared it owns the
106
- * control, instead of being lowered into the workspace.
107
- *
108
- * Distinct from {@link WorkspacePlan.systemPrompt} — a launch ARGUMENT the
109
- * executor hands to a harness flag — and from the file/flag/env lowerings the
110
- * plan performs on its own. This names the exact control that must receive the
111
- * text and which of the two intents it is, so the launcher cannot apply it to
112
- * the wrong channel. Never written into an additive channel and never dropped:
113
- * a caller that has not declared it owns `control` gets the plan's own lowering
114
- * (or a refusal where the harness has no control at all) instead of this field.
115
- */
116
- export interface WorkspacePlanSystemPromptBinding {
117
- /** Which of the two prompt intents this is — never inferred from presence. */
118
- intent: "replace";
119
- /** The exact launcher control that must receive `systemPrompt`. */
120
- control: ExecutorSystemPromptBinding;
121
- /** Exact text the control receives. */
122
- systemPrompt: string;
123
- }
124
- /** One file applied by a plan, with the executor root kept explicit. */
125
- export interface WorkspacePlanWrittenFile {
126
- root: PlanFileRoot;
127
- relPath: string;
128
- }
129
- /**
130
- * The dimensions `materializeProfile`'s `skip` option honors.
131
- *
132
- * Every other dimension is materialized unconditionally: generated plan files are pushed through
133
- * a single site that carries no dimension tag, so there is nothing to gate them on. Naming the
134
- * honored set keeps `skip: ["hooks"]` a loud rejection instead of a silent no-op.
135
- */
136
- export declare const SKIPPABLE_DIMENSIONS: readonly ["mcp", "extensions"];
137
- export type SkippableDimension = (typeof SKIPPABLE_DIMENSIONS)[number];
138
- export declare function isSkippableDimension(value: string): value is SkippableDimension;
139
- /** Reject a dimension `materializeProfile` would not actually skip, rather than ignoring it. */
140
- export declare function assertSkippableDimensions(skip: readonly string[] | undefined): readonly SkippableDimension[];
141
- /**
142
- * Launcher controls an executor can declare it OWNS via `materializeProfile`'s
143
- * `binds` option.
144
- *
145
- * Some harness controls live outside any workspace: opencode's exact
146
- * system-prompt replacement (`agent.<name>.prompt`) binds to the ONE agent
147
- * whoever starts the process selects, which a plan-forwarding executor cannot
148
- * guarantee — so the plan refuses the intent (harnessSystemPromptIntents in
149
- * @tangle-network/agent-interface answers `replace: false` for opencode).
150
- *
151
- * The replacement declarations describe the executor shapes that can reach it;
152
- * each gets the lowering it can apply. Codex launchers separately declare
153
- * `codex.developer-instructions` to consume ordinary instructions as additive
154
- * context; undeclared callers retain workspace files:
155
- *
156
- * - `systemPrompt` — the executor applies the plan's launch flags to the
157
- * spawn it owns, so the plan writes the generated `mode: primary` agent and
158
- * selects it with `--agent`.
159
- * - `opencode.agent-prompt` — the executor composes opencode's own config and
160
- * picks the agent there, reading neither plan files nor plan flags, so the
161
- * plan hands the text over in {@link WorkspacePlan.systemPromptBinding}
162
- * instead of writing a workspace agent that would never be selected.
163
- *
164
- * The replacement declarations are mutually exclusive: declaring both is a
165
- * caller bug, not a broader capability.
166
- */
167
- export declare const BINDABLE_LAUNCH_CONTROLS: readonly ["systemPrompt", "codex.developer-instructions", "opencode.agent-prompt"];
168
- export type BindableLaunchControl = (typeof BINDABLE_LAUNCH_CONTROLS)[number];
169
- /** Reject a binding declaration this materializer has no lowering for, rather than ignoring it. */
170
- export declare function assertBindableLaunchControls(binds: readonly string[] | undefined): ReadonlySet<BindableLaunchControl>;
171
- /** The plan a caller writes into the run cwd, then applies to the spawn. */
172
- export interface WorkspacePlan {
173
- harness: HarnessId;
174
- /** Files to write relative to the run workspace (cwd). */
175
- files: PlanFile[];
176
- /** Env vars to set on the spawned process (for harnesses needing them). */
177
- env: Record<string, WorkspacePlanConfigValue>;
178
- /** Extra CLI flags to pass (e.g. claude --mcp-config <path>). */
179
- flags: WorkspacePlanArgument[];
180
- /**
181
- * Exact text that REPLACES the harness's own system prompt, for harnesses
182
- * whose replacement control takes it as a launch argument the executor
183
- * supplies (claude-code `--system-prompt`, pi `--system-prompt`).
184
- *
185
- * Absent does NOT mean "no replacement was requested": a harness whose
186
- * control reads a file (codex, gemini) carries the replacement in
187
- * {@link WorkspacePlan.files} plus a flag or env var instead. Present means
188
- * the executor MUST bind it to that harness's replacement control — never to
189
- * an additive one, which would leave the built-in prompt in force.
190
- */
191
- systemPrompt?: string;
192
- /**
193
- * Text added to the harness context; its own system prompt stays intact.
194
- *
195
- * Claude Code and Pi bind their additive system-prompt control. Codex binds
196
- * ordinary profile instructions to developer_instructions, composed once
197
- * with host context; this is not a privileged system-prompt append. Authored
198
- * Codex appendSystemPrompt remains unsupported. Never fold this into an exact
199
- * system-prompt replacement, which carries the caller's unchanged bytes.
200
- */
201
- appendSystemPrompt?: string;
202
- /**
203
- * A prompt intent no workspace file, flag, or env var can execute, addressed
204
- * to the launcher control that can — see
205
- * {@link WorkspacePlanSystemPromptBinding}.
206
- *
207
- * Present ONLY when the caller declared it owns the named control, so a plan
208
- * built by a caller that forwards it to someone else's launcher never carries
209
- * one; that caller gets the intent in {@link WorkspacePlan.unsupported}
210
- * instead. Whoever reads it must bind it or throw — {@link
211
- * takeSystemPromptBinding} does both.
212
- */
213
- systemPromptBinding?: WorkspacePlanSystemPromptBinding;
214
- /** Dimensions requested but with NO cwd path on this harness — fail-closed record. */
215
- unsupported: Unsupported[];
216
- /**
217
- * Prerequisites the workspace files CANNOT establish on their own.
218
- *
219
- * A plan is a set of files, an env map, and flags. Some harness capabilities need more than
220
- * that to actually function: pi's MCP support is not built in, it arrives from an extension
221
- * that must be installed into the harness's own package directory and permitted to load. No
222
- * file written into a run workspace can make either true.
223
- *
224
- * Writing the config anyway and hoping is the failure this field exists to prevent: the
225
- * harness starts, silently has no MCP, and the agent runs without the tools it was configured
226
- * with while nothing reports a problem. So the plan states the requirement and the executor —
227
- * which knows where its harness is installed and how it spawns — verifies or refuses it.
228
- *
229
- * Optional because this is an additive field on a published type: a plan built before it
230
- * existed, or by a caller that does not construct one, is still a valid plan with no
231
- * prerequisites. Every plan this package builds sets it.
232
- */
233
- requires?: PlanRequirement[];
234
- }
235
- /**
236
- * One prerequisite a {@link WorkspacePlan} depends on but cannot create.
237
- *
238
- * Declarative on purpose: this package builds plans and never probes a filesystem, spawns a
239
- * process, or reaches the network. The executor holds that context and does the checking.
240
- */
241
- export interface PlanRequirement {
242
- /** The only kind so far: a harness extension/plugin that must be loaded for a dimension to work. */
243
- kind: "harness-extension";
244
- /** Dimension that produced the requirement, so a refusal can name what will not work. */
245
- dimension: Unsupported["dimension"];
246
- /** Package spec in the harness's own vocabulary, e.g. `npm:pi-mcp-adapter`. */
247
- id: string;
248
- /** Why the plan needs it — goes verbatim into the executor's refusal message. */
249
- reason: string;
250
- /** The exact command that satisfies it, so a failure is actionable without a doc lookup. */
251
- remedy: string;
252
- }
253
- /**
254
- * Proof of the profile workspace inputs applied before a harness is spawned.
255
- * `workspacePlanDigest` covers only the {@link WorkspacePlan}; it is not an
256
- * execution-plan digest and does not attest unrelated files in `workspaceDir`.
257
- */
258
- export interface WorkspacePlanReceipt {
259
- workspacePlanDigest: string;
260
- /** Public launch identity only; secret values never enter this receipt. */
261
- env: Record<string, WorkspacePlanConfigValue>;
262
- /** Public launch identity only; templates retain opaque secret references. */
263
- flags: WorkspacePlanArgument[];
264
- systemPrompt?: string;
265
- appendSystemPrompt?: string;
266
- /** The launcher-bound intent, so a receipt says which control owed the work. */
267
- systemPromptBinding?: WorkspacePlanSystemPromptBinding;
268
- unsupported: Unsupported[];
269
- /**
270
- * Workspace-root paths, for consumers that mount the workspace only.
271
- * Agent-root files are available in `writtenFiles` and are never encoded as
272
- * an `agent:` path because that spelling is legal in the workspace root.
273
- */
274
- written: string[];
275
- /** Every applied file, including its executor-owned root. */
276
- writtenFiles: WorkspacePlanWrittenFile[];
277
- }
278
- /** Private values handed directly to the provider spawn, never to receipts. */
279
- export interface WorkspacePlanSpawnInputs {
280
- env: Record<string, string>;
281
- flags: string[];
282
- systemPrompt?: string;
283
- appendSystemPrompt?: string;
284
- /** Read it with {@link takeSystemPromptBinding}, which refuses a foreign control. */
285
- systemPromptBinding?: WorkspacePlanSystemPromptBinding;
286
- /**
287
- * Absolute dirs of the profile's declared executable `reference/` tools in
288
- * the immutable revision the launch runs from. The provider puts them on
289
- * the launch PATH in place of the app's own tool dirs (bindLaunchToolBinDirs),
290
- * so a profile names its CLI bare.
291
- */
292
- toolBinDirs?: string[];
293
- }
294
- /** Result of applying a public plan at the private executor boundary. */
295
- export interface WorkspacePlanApplication {
296
- receipt: WorkspacePlanReceipt;
297
- spawn: WorkspacePlanSpawnInputs;
298
- /** Resolved generated files intentionally consumed in memory by the runtime. */
299
- runtimeOnlyFiles: Record<string, string>;
300
- }
301
- /** The plan was applied, but one or more private staging files remain. */
302
- export declare class WorkspacePlanCleanupError extends AggregateError {
303
- readonly receipt: WorkspacePlanReceipt;
304
- readonly temporaryPaths: readonly string[];
305
- constructor(errors: readonly unknown[], receipt: WorkspacePlanReceipt, temporaryPaths: readonly string[]);
306
- }
307
- export interface ApplyWorkspacePlanOptions {
308
- /** Plan files consumed in memory by the runtime and never persisted to cwd. */
309
- runtimeOnlyFiles?: readonly string[];
310
- /** Absolute private agent directory required by files whose root is `agent`. */
311
- agentDir?: string;
312
- /** Own newly written workspace files as the process that will read them. */
313
- ownership?: {
314
- uid: number;
315
- gid: number;
316
- };
317
- /** Existing workspace paths are either exact idempotent inputs or forbidden. */
318
- existingFiles?: "allow-identical" | "reject";
319
- /**
320
- * `workspacePlanDigest` of a plan a prior call already applied into this
321
- * workspace. When it equals this plan's digest, application changes no file
322
- * content. It can repair ownership on unchanged materializer-owned files.
323
- * A file the agent edited or removed stays exactly as the agent left it.
324
- * Env, flags, prompt intents, and the receipt are still produced. A different
325
- * digest applies normally, so a changed plan still meets the existing-file
326
- * refusal.
327
- */
328
- appliedPlanDigest?: string;
329
- /**
330
- * Write every JSON file secret slot as an `{env:NAME}` reference (OpenCode
331
- * config substitution) and never its value, so file bytes and every digest
332
- * over them stay identical when only a secret's value changes. The value
333
- * lands here under NAME for the executor alone: never put it in the launch
334
- * environment, argv or any file the native process reads, because that
335
- * process hands everything it holds to every tool it runs. A value that
336
- * could not be substituted safely is refused.
337
- */
338
- secretFileSlotReferences?: Record<string, string>;
339
- }
340
- import type { AgentProfileSecretRef } from "@tangle-network/agent-interface";
341
- import type { HarnessId } from "./harness.js";
@@ -1,105 +0,0 @@
1
- /**
2
- * The subset of {@link BINDABLE_LAUNCH_CONTROLS} the plan HANDS OVER rather than
3
- * executes: controls a caller reaches only by composing the harness's own
4
- * configuration, so no file, flag or env var the plan writes can touch them.
5
- *
6
- * - `opencode.agent-prompt` — opencode's `agent.<name>.prompt` config field,
7
- * the one control that replaces its built-in provider prompt. It binds to a
8
- * SINGLE agent, and the agent is chosen at launch. A caller that builds
9
- * opencode's config itself and picks the agent there reads neither
10
- * `plan.files` nor `plan.flags`, so for that one the plan names the control
11
- * and hands over the text in {@link WorkspacePlan.systemPromptBinding}.
12
- *
13
- * Every other bindable control names a capability the plan still lowers itself
14
- * once the caller has declared the plan's launch inputs really reach the spawn.
15
- */
16
- export const EXECUTOR_SYSTEM_PROMPT_BINDINGS = [
17
- "opencode.agent-prompt",
18
- ];
19
- /**
20
- * The dimensions `materializeProfile`'s `skip` option honors.
21
- *
22
- * Every other dimension is materialized unconditionally: generated plan files are pushed through
23
- * a single site that carries no dimension tag, so there is nothing to gate them on. Naming the
24
- * honored set keeps `skip: ["hooks"]` a loud rejection instead of a silent no-op.
25
- */
26
- export const SKIPPABLE_DIMENSIONS = [
27
- "mcp",
28
- "extensions",
29
- ];
30
- const SKIPPABLE_DIMENSION_SET = new Set(SKIPPABLE_DIMENSIONS);
31
- export function isSkippableDimension(value) {
32
- return SKIPPABLE_DIMENSION_SET.has(value);
33
- }
34
- /** Reject a dimension `materializeProfile` would not actually skip, rather than ignoring it. */
35
- export function assertSkippableDimensions(skip) {
36
- if (!skip)
37
- return [];
38
- const unsupportedSkips = skip.filter((dimension) => !isSkippableDimension(dimension));
39
- if (unsupportedSkips.length > 0) {
40
- throw new Error(`materializeProfile: cannot skip ${unsupportedSkips.join(", ")}. ` +
41
- `Skippable dimensions are: ${SKIPPABLE_DIMENSIONS.join(", ")}.`);
42
- }
43
- return skip;
44
- }
45
- /**
46
- * Launcher controls an executor can declare it OWNS via `materializeProfile`'s
47
- * `binds` option.
48
- *
49
- * Some harness controls live outside any workspace: opencode's exact
50
- * system-prompt replacement (`agent.<name>.prompt`) binds to the ONE agent
51
- * whoever starts the process selects, which a plan-forwarding executor cannot
52
- * guarantee — so the plan refuses the intent (harnessSystemPromptIntents in
53
- * @tangle-network/agent-interface answers `replace: false` for opencode).
54
- *
55
- * The replacement declarations describe the executor shapes that can reach it;
56
- * each gets the lowering it can apply. Codex launchers separately declare
57
- * `codex.developer-instructions` to consume ordinary instructions as additive
58
- * context; undeclared callers retain workspace files:
59
- *
60
- * - `systemPrompt` — the executor applies the plan's launch flags to the
61
- * spawn it owns, so the plan writes the generated `mode: primary` agent and
62
- * selects it with `--agent`.
63
- * - `opencode.agent-prompt` — the executor composes opencode's own config and
64
- * picks the agent there, reading neither plan files nor plan flags, so the
65
- * plan hands the text over in {@link WorkspacePlan.systemPromptBinding}
66
- * instead of writing a workspace agent that would never be selected.
67
- *
68
- * The replacement declarations are mutually exclusive: declaring both is a
69
- * caller bug, not a broader capability.
70
- */
71
- export const BINDABLE_LAUNCH_CONTROLS = [
72
- "systemPrompt",
73
- "codex.developer-instructions",
74
- ...EXECUTOR_SYSTEM_PROMPT_BINDINGS,
75
- ];
76
- const BINDABLE_LAUNCH_CONTROL_SET = new Set(BINDABLE_LAUNCH_CONTROLS);
77
- /** Reject a binding declaration this materializer has no lowering for, rather than ignoring it. */
78
- export function assertBindableLaunchControls(binds) {
79
- if (!binds)
80
- return new Set();
81
- const unknown = binds.filter((control) => !BINDABLE_LAUNCH_CONTROL_SET.has(control));
82
- if (unknown.length > 0) {
83
- throw new Error(`materializeProfile: cannot bind ${unknown.join(", ")}. ` +
84
- `Bindable launch controls are: ${BINDABLE_LAUNCH_CONTROLS.join(", ")}.`);
85
- }
86
- const declared = new Set(binds);
87
- const handedOver = [...EXECUTOR_SYSTEM_PROMPT_BINDINGS].filter((control) => declared.has(control));
88
- if (declared.has("systemPrompt") && handedOver.length > 0) {
89
- throw new Error(`materializeProfile: cannot bind systemPrompt together with ${handedOver.join(", ")}. ` +
90
- "The first says the plan's launch flags reach the spawn; the second says " +
91
- "the caller composes the harness config itself and reads neither.");
92
- }
93
- return declared;
94
- }
95
- /** The plan was applied, but one or more private staging files remain. */
96
- export class WorkspacePlanCleanupError extends AggregateError {
97
- receipt;
98
- temporaryPaths;
99
- constructor(errors, receipt, temporaryPaths) {
100
- super(errors, "Applied workspace plan but failed to remove temporary files");
101
- this.name = "WorkspacePlanCleanupError";
102
- this.receipt = receipt;
103
- this.temporaryPaths = Object.freeze([...temporaryPaths]);
104
- }
105
- }
@@ -1,64 +0,0 @@
1
- export declare function writePrimeAgentFile(path: string, data: string, mode?: number): void;
2
- /**
3
- * File access on a prime agent dir. The default reads and writes the local
4
- * filesystem as the calling process. An executor whose agent dir belongs to
5
- * another user (a root sidecar that demotes prime-agent) passes reads it took
6
- * as that user and replays the writes as that user, so it never touches the
7
- * agent's files with its own rights.
8
- */
9
- export interface PrimeAgentDirIo {
10
- /** The file's text, or undefined when it does not exist. */
11
- read(path: string): string | undefined;
12
- /** Replace the whole file: atomic and owner-only. */
13
- write(path: string, data: string): void;
14
- }
15
- /** The fork's continual-harness store, the only agent-dir file a materialization reads. */
16
- export declare function primeHarnessStatePath(agentDir: string): string;
17
- export interface PrimeMaterializeOptions {
18
- /**
19
- * Where the FLAG-NAMED files go — the prompt files and the skills root.
20
- * Defaults to `agentDir`. An executor that does not own the agent dir points
21
- * this at a per-run directory of its own, so binding a profile's prompts and
22
- * skills writes nothing into a dir it must not modify. The context file and
23
- * the harness store have no flag and always land in `agentDir`; refuse those
24
- * with {@link primeProfileNeedsAgentDir} instead.
25
- */
26
- flagFileRoot?: string;
27
- /**
28
- * Reports state the executor should see but that does not fail the run
29
- * (currently: adopting entries a pre-shared executor left behind).
30
- */
31
- warn?: (msg: string, meta?: Record<string, unknown>) => void;
32
- /** Agent-dir file access; the local filesystem when omitted. */
33
- io?: PrimeAgentDirIo;
34
- }
35
- /**
36
- * Materialize the validated controls and return the extra spawn args that bind
37
- * them. Runs before every spawn (same place models.json is materialized), so a
38
- * continual agent dir is re-pinned to the current profile on each fresh prime
39
- * process.
40
- */
41
- export declare function materializePrimeProfileControls(agentDir: string, controls: PrimeProfileControls, options?: PrimeMaterializeOptions): string[];
42
- /**
43
- * Remove the files a PREVIOUS materialization of this lowering wrote, so a
44
- * profile that shrank cannot leave a stale instruction file or skill behind for
45
- * the next run against the same dirs.
46
- *
47
- * Only the paths this lowering NAMES are removed. There is deliberately no
48
- * manifest: a manifest would live where the agent can write it, and reading a
49
- * delete list out of agent-writable state turns pruning into an arbitrary-path
50
- * delete.
51
- */
52
- export declare function prunePrimeProfileMaterial(agentDir: string, options?: Pick<PrimeMaterializeOptions, "flagFileRoot">): void;
53
- /**
54
- * Upsert profile subagent specs into the fork's global continual-harness
55
- * store and remove profile-sourced entries the current profile no longer
56
- * declares. Agent-written entries (`source: "agent"`) — and every top-level
57
- * key this lowering does not manage — are preserved untouched.
58
- *
59
- * A store that exists but no longer parses raises
60
- * {@link PrimeHarnessStateError}: the file is the agent's own self-modification
61
- * state, and overwriting it destroys what the run was supposed to observe.
62
- */
63
- export declare function seedPrimeProfileSubagents(agentDir: string, subagents: PrimeProfileSubagent[], warn?: (msg: string, meta?: Record<string, unknown>) => void, io?: PrimeAgentDirIo): void;
64
- import type { PrimeProfileControls, PrimeProfileSubagent } from "./prime-profile.js";