@tangle-network/agent-profile-materialize 0.11.0 → 0.13.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 +220 -28
- package/dist/index.js +644 -239
- package/dist/prime-fork.d.ts +192 -0
- package/dist/prime-fork.js +303 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
* cli-bridge and agent-dev-container — this is the single source those re-export.
|
|
23
23
|
*/
|
|
24
24
|
export { modelIdsMatch, qualifyModelId } from "./model-id.js";
|
|
25
|
+
export { matchPrimeKernelDeadMarker, PRIME_AGENT_DIR_ENV_VAR, PRIME_AGENT_DIR_ENV_VARS, PRIME_FORK_INSTALL, PRIME_FORK_PIN, PRIME_HARNESS_STATE_ENV_DENYLIST, PRIME_KERNEL_DEAD_MARKERS, PRIME_KERNEL_ENV_PASSTHROUGH, PRIME_MCP_UNSUPPORTED_REASON, PRIME_THINKING_LEVELS, PrimeHarnessStateError, PrimeProfileError, type PrimeProfileErrorKind, type PrimeThinkingLevel, parsePrimeModelsJson, primeAgentDirEnv, primeApiKeyEnvNames, primeThinkingLevel, stripPrimeJsonComments, } from "./prime-fork.js";
|
|
25
26
|
import type { AgentCandidateConfigValue, AgentCandidateProfile, AgentCandidateProfileActivation, AgentCandidateProfileApplication, AgentCandidateProfilePlanEvidence, AgentCandidateProfilePlanMaterial, AgentProfile, AgentProfileConfigValue, AgentProfileMcpServer, AgentProfileSecretProvider, AgentProfileSecretRef, Sha256Digest } from "@tangle-network/agent-interface";
|
|
26
27
|
export declare const UNAVAILABLE_AGENT_PROFILE_SECRET_PROVIDER: AgentProfileSecretProvider;
|
|
27
28
|
/**
|
|
@@ -57,6 +58,25 @@ export interface PlanFile {
|
|
|
57
58
|
* this on every file it emits.
|
|
58
59
|
*/
|
|
59
60
|
source?: "user" | "generated";
|
|
61
|
+
/**
|
|
62
|
+
* Who owns the PATH — which decides whether an existing file there may be
|
|
63
|
+
* replaced.
|
|
64
|
+
*
|
|
65
|
+
* `"materializer"` = a path this package invented. No harness convention
|
|
66
|
+
* names it and no user authors it, so a file already there can only be this
|
|
67
|
+
* materializer's own output from an earlier application into the same
|
|
68
|
+
* workspace, and replacing it is how turn two of a resumed session gets the
|
|
69
|
+
* current profile instead of the previous one.
|
|
70
|
+
*
|
|
71
|
+
* `"shared"` (the default, and every other file) = a path the user or the
|
|
72
|
+
* harness also owns — `CLAUDE.md`, `AGENTS.md`, `opencode.json`,
|
|
73
|
+
* `.gemini/system.md` are all documented, user-authorable names. An existing
|
|
74
|
+
* file there may be the caller's own work, so it is never replaced.
|
|
75
|
+
*
|
|
76
|
+
* Optional because hand-constructed plans have no path provenance to report;
|
|
77
|
+
* absent reads as `"shared"`, the safe answer.
|
|
78
|
+
*/
|
|
79
|
+
ownership?: "materializer" | "shared";
|
|
60
80
|
/**
|
|
61
81
|
* Secret leaves in a generated JSON document. `content` contains `null` at
|
|
62
82
|
* each path until the private executor resolves the named reference. The
|
|
@@ -83,9 +103,47 @@ export type WorkspacePlanArgument = string | WorkspacePlanArgumentTemplate;
|
|
|
83
103
|
/** Public bytes stay strings; secret references stay opaque until application. */
|
|
84
104
|
export type WorkspacePlanConfigValue = string | AgentProfileSecretRef;
|
|
85
105
|
export interface Unsupported {
|
|
86
|
-
dimension: "files" | "skills" | "mcp" | "hooks" | "subagents" | "commands" | "instructions" | "systemPrompt" | "connections" | "confidential" | "permissions" | "tools" | "modes" | "extensions";
|
|
106
|
+
dimension: "files" | "skills" | "mcp" | "hooks" | "subagents" | "commands" | "instructions" | "systemPrompt" | "appendSystemPrompt" | "connections" | "confidential" | "permissions" | "tools" | "modes" | "extensions";
|
|
87
107
|
reason: string;
|
|
88
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* The subset of {@link BINDABLE_LAUNCH_CONTROLS} the plan HANDS OVER rather than
|
|
111
|
+
* executes: controls a caller reaches only by composing the harness's own
|
|
112
|
+
* configuration, so no file, flag or env var the plan writes can touch them.
|
|
113
|
+
*
|
|
114
|
+
* - `opencode.agent-prompt` — opencode's `agent.<name>.prompt` config field,
|
|
115
|
+
* the one control that replaces its built-in provider prompt. It binds to a
|
|
116
|
+
* SINGLE agent, and the agent is chosen at launch. A caller that builds
|
|
117
|
+
* opencode's config itself and picks the agent there reads neither
|
|
118
|
+
* `plan.files` nor `plan.flags`, so for that one the plan names the control
|
|
119
|
+
* and hands over the text in {@link WorkspacePlan.systemPromptBinding}.
|
|
120
|
+
*
|
|
121
|
+
* Every other bindable control names a capability the plan still lowers itself
|
|
122
|
+
* once the caller has declared the plan's launch inputs really reach the spawn.
|
|
123
|
+
*/
|
|
124
|
+
export declare const EXECUTOR_SYSTEM_PROMPT_BINDINGS: readonly ["opencode.agent-prompt"];
|
|
125
|
+
export type ExecutorSystemPromptBinding = (typeof EXECUTOR_SYSTEM_PROMPT_BINDINGS)[number];
|
|
126
|
+
export declare function isExecutorSystemPromptBinding(value: string): value is ExecutorSystemPromptBinding;
|
|
127
|
+
/**
|
|
128
|
+
* A system-prompt intent handed to the launcher that declared it owns the
|
|
129
|
+
* control, instead of being lowered into the workspace.
|
|
130
|
+
*
|
|
131
|
+
* Distinct from {@link WorkspacePlan.systemPrompt} — a launch ARGUMENT the
|
|
132
|
+
* executor hands to a harness flag — and from the file/flag/env lowerings the
|
|
133
|
+
* plan performs on its own. This names the exact control that must receive the
|
|
134
|
+
* text and which of the two intents it is, so the launcher cannot apply it to
|
|
135
|
+
* the wrong channel. Never written into an additive channel and never dropped:
|
|
136
|
+
* a caller that has not declared it owns `control` gets the plan's own lowering
|
|
137
|
+
* (or a refusal where the harness has no control at all) instead of this field.
|
|
138
|
+
*/
|
|
139
|
+
export interface WorkspacePlanSystemPromptBinding {
|
|
140
|
+
/** Which of the two prompt intents this is — never inferred from presence. */
|
|
141
|
+
intent: "replace";
|
|
142
|
+
/** The exact launcher control that must receive `systemPrompt`. */
|
|
143
|
+
control: ExecutorSystemPromptBinding;
|
|
144
|
+
/** Exact text the control receives. */
|
|
145
|
+
systemPrompt: string;
|
|
146
|
+
}
|
|
89
147
|
/**
|
|
90
148
|
* The dimensions `materializeProfile`'s `skip` option honors.
|
|
91
149
|
*
|
|
@@ -104,13 +162,23 @@ export declare function isSkippableDimension(value: string): value is SkippableD
|
|
|
104
162
|
* system-prompt replacement (`agent.<name>.prompt`) binds to the ONE agent
|
|
105
163
|
* whoever starts the process selects, which a plan-forwarding executor cannot
|
|
106
164
|
* guarantee — so the plan refuses the intent (harnessSystemPromptIntents in
|
|
107
|
-
* @tangle-network/agent-interface answers `replace: false` for opencode).
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
165
|
+
* @tangle-network/agent-interface answers `replace: false` for opencode).
|
|
166
|
+
*
|
|
167
|
+
* The two declarations describe the two executor shapes that CAN reach it, and
|
|
168
|
+
* each gets the lowering it can actually apply:
|
|
169
|
+
*
|
|
170
|
+
* - `systemPrompt` — the executor applies the plan's launch flags to the
|
|
171
|
+
* spawn it owns, so the plan writes the generated `mode: primary` agent and
|
|
172
|
+
* selects it with `--agent`.
|
|
173
|
+
* - `opencode.agent-prompt` — the executor composes opencode's own config and
|
|
174
|
+
* picks the agent there, reading neither plan files nor plan flags, so the
|
|
175
|
+
* plan hands the text over in {@link WorkspacePlan.systemPromptBinding}
|
|
176
|
+
* instead of writing a workspace agent that would never be selected.
|
|
177
|
+
*
|
|
178
|
+
* They are mutually exclusive claims about the same caller: declaring both is a
|
|
179
|
+
* caller bug, not a broader capability.
|
|
112
180
|
*/
|
|
113
|
-
export declare const BINDABLE_LAUNCH_CONTROLS: readonly ["systemPrompt"];
|
|
181
|
+
export declare const BINDABLE_LAUNCH_CONTROLS: readonly ["systemPrompt", "opencode.agent-prompt"];
|
|
114
182
|
export type BindableLaunchControl = (typeof BINDABLE_LAUNCH_CONTROLS)[number];
|
|
115
183
|
/** The plan a caller writes into the run cwd, then applies to the spawn. */
|
|
116
184
|
export interface WorkspacePlan {
|
|
@@ -121,18 +189,42 @@ export interface WorkspacePlan {
|
|
|
121
189
|
env: Record<string, WorkspacePlanConfigValue>;
|
|
122
190
|
/** Extra CLI flags to pass (e.g. kimi --mcp-config-file <path>). */
|
|
123
191
|
flags: WorkspacePlanArgument[];
|
|
124
|
-
/**
|
|
192
|
+
/**
|
|
193
|
+
* Exact text that REPLACES the harness's own system prompt, for harnesses
|
|
194
|
+
* whose replacement control takes it as a launch argument the executor
|
|
195
|
+
* supplies (claude-code `--system-prompt`, pi `--system-prompt`).
|
|
196
|
+
*
|
|
197
|
+
* Absent does NOT mean "no replacement was requested": a harness whose
|
|
198
|
+
* control reads a file (codex, gemini) carries the replacement in
|
|
199
|
+
* {@link WorkspacePlan.files} plus a flag or env var instead. Present means
|
|
200
|
+
* the executor MUST bind it to that harness's replacement control — never to
|
|
201
|
+
* an additive one, which would leave the built-in prompt in force.
|
|
202
|
+
*/
|
|
125
203
|
systemPrompt?: string;
|
|
126
204
|
/**
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
205
|
+
* Exact text ADDED to the harness's own system prompt, which stays intact.
|
|
206
|
+
*
|
|
207
|
+
* Separate from {@link WorkspacePlan.systemPrompt} because the two are
|
|
208
|
+
* different intents against the same channel, and every harness that has one
|
|
209
|
+
* control may lack the other. An executor binds this ONLY to an additive
|
|
210
|
+
* control (claude-code `--append-system-prompt`, pi
|
|
211
|
+
* `--append-system-prompt`); there is no harness for which folding it into
|
|
212
|
+
* the replacement is correct, because that would delete the built-in prompt
|
|
213
|
+
* the caller asked to keep.
|
|
134
214
|
*/
|
|
135
215
|
appendSystemPrompt?: string;
|
|
216
|
+
/**
|
|
217
|
+
* A prompt intent no workspace file, flag, or env var can execute, addressed
|
|
218
|
+
* to the launcher control that can — see
|
|
219
|
+
* {@link WorkspacePlanSystemPromptBinding}.
|
|
220
|
+
*
|
|
221
|
+
* Present ONLY when the caller declared it owns the named control, so a plan
|
|
222
|
+
* built by a caller that forwards it to someone else's launcher never carries
|
|
223
|
+
* one; that caller gets the intent in {@link WorkspacePlan.unsupported}
|
|
224
|
+
* instead. Whoever reads it must bind it or throw — {@link
|
|
225
|
+
* takeSystemPromptBinding} does both.
|
|
226
|
+
*/
|
|
227
|
+
systemPromptBinding?: WorkspacePlanSystemPromptBinding;
|
|
136
228
|
/** Dimensions requested but with NO cwd path on this harness — fail-closed record. */
|
|
137
229
|
unsupported: Unsupported[];
|
|
138
230
|
/**
|
|
@@ -185,6 +277,8 @@ export interface WorkspacePlanReceipt {
|
|
|
185
277
|
flags: WorkspacePlanArgument[];
|
|
186
278
|
systemPrompt?: string;
|
|
187
279
|
appendSystemPrompt?: string;
|
|
280
|
+
/** The launcher-bound intent, so a receipt says which control owed the work. */
|
|
281
|
+
systemPromptBinding?: WorkspacePlanSystemPromptBinding;
|
|
188
282
|
unsupported: Unsupported[];
|
|
189
283
|
written: string[];
|
|
190
284
|
}
|
|
@@ -194,6 +288,8 @@ export interface WorkspacePlanSpawnInputs {
|
|
|
194
288
|
flags: string[];
|
|
195
289
|
systemPrompt?: string;
|
|
196
290
|
appendSystemPrompt?: string;
|
|
291
|
+
/** Read it with {@link takeSystemPromptBinding}, which refuses a foreign control. */
|
|
292
|
+
systemPromptBinding?: WorkspacePlanSystemPromptBinding;
|
|
197
293
|
}
|
|
198
294
|
/** Result of applying a public plan at the private executor boundary. */
|
|
199
295
|
export interface WorkspacePlanApplication {
|
|
@@ -248,6 +344,18 @@ export interface AgentCandidateWorkspacePlanReceipt {
|
|
|
248
344
|
appendSystemPrompt?: AgentCandidateConfigValue;
|
|
249
345
|
unsupported: Unsupported[];
|
|
250
346
|
}
|
|
347
|
+
/**
|
|
348
|
+
* The replacement text this launcher must bind to `control`, or `undefined`
|
|
349
|
+
* when the plan carries no launcher-bound intent.
|
|
350
|
+
*
|
|
351
|
+
* Throws for a binding addressed to a DIFFERENT control: the reader cannot
|
|
352
|
+
* execute it, and answering `undefined` would start the agent with the
|
|
353
|
+
* harness's own prompt still in force while nothing reported a problem — the
|
|
354
|
+
* silent downgrade the binding exists to prevent. This is the seam where an
|
|
355
|
+
* executor that cannot bind an intent says so, by name, instead of running a
|
|
356
|
+
* profile whose prompt never took effect.
|
|
357
|
+
*/
|
|
358
|
+
export declare function takeSystemPromptBinding(spawn: Pick<WorkspacePlanSpawnInputs, "systemPromptBinding">, control: ExecutorSystemPromptBinding): string | undefined;
|
|
251
359
|
export interface MaterializeCandidateProfileOptions {
|
|
252
360
|
/** Exact text fetched from pinned resource refs, keyed by declared SHA-256. */
|
|
253
361
|
resolvedResources?: ReadonlyMap<Sha256Digest, string>;
|
|
@@ -308,7 +416,17 @@ export declare function normalizeSkillMd(name: string, raw: string): string;
|
|
|
308
416
|
*/
|
|
309
417
|
export declare function materializeProfile(inputProfile: AgentProfile, harness: HarnessId, opts?: {
|
|
310
418
|
skip?: SkippableDimension[];
|
|
311
|
-
/**
|
|
419
|
+
/**
|
|
420
|
+
* Launcher controls THIS caller owns and will bind itself — see
|
|
421
|
+
* {@link BINDABLE_LAUNCH_CONTROLS}. Each names a control no workspace file,
|
|
422
|
+
* flag or env var reaches on its own, so declaring one turns a plan-time
|
|
423
|
+
* refusal into the lowering (or the hand-over) that control can execute.
|
|
424
|
+
*
|
|
425
|
+
* A promise, not a hint: declare a control only from the code that starts
|
|
426
|
+
* the harness and writes that control. A caller that hands the plan to
|
|
427
|
+
* someone else's launcher declares nothing and keeps the refusal, which is
|
|
428
|
+
* what it can actually deliver.
|
|
429
|
+
*/
|
|
312
430
|
binds?: readonly BindableLaunchControl[];
|
|
313
431
|
}): WorkspacePlan;
|
|
314
432
|
/**
|
|
@@ -386,31 +504,105 @@ export interface PrimeProfileSubagent {
|
|
|
386
504
|
export interface PrimeProfileControls {
|
|
387
505
|
systemPrompt?: string;
|
|
388
506
|
appendSystemPrompt?: string;
|
|
507
|
+
/** Composed caller instructions, lowered to the agent dir's AGENTS.md. */
|
|
508
|
+
instructions?: string;
|
|
389
509
|
skills: PrimeProfileSkill[];
|
|
390
510
|
subagents: PrimeProfileSubagent[];
|
|
391
511
|
}
|
|
512
|
+
/** True when the controls materialize anything at all. */
|
|
513
|
+
export declare function hasPrimeProfileMaterial(controls: PrimeProfileControls): boolean;
|
|
514
|
+
/**
|
|
515
|
+
* True when the controls must WRITE INTO the agent dir itself.
|
|
516
|
+
*
|
|
517
|
+
* Prompt files and skill dirs are named by a flag, so they can live anywhere
|
|
518
|
+
* the agent can read (`--system-prompt` / `--append-system-prompt` resolve a
|
|
519
|
+
* path when it exists, fork resource-loader.ts:41-56; `--skill` takes an
|
|
520
|
+
* arbitrary dir, :437-442). The context file and the continual-harness store
|
|
521
|
+
* are located BY the agent dir and have no flag, so they cannot move.
|
|
522
|
+
*
|
|
523
|
+
* An executor pointed at an agent dir it does not own (an operator's shared
|
|
524
|
+
* dir) uses this to refuse exactly the controls it would have to write there,
|
|
525
|
+
* while still binding the ones it can materialize elsewhere.
|
|
526
|
+
*/
|
|
527
|
+
export declare function primeProfileNeedsAgentDir(controls: PrimeProfileControls): boolean;
|
|
392
528
|
/**
|
|
393
529
|
* Validate and extract the profile controls the prime agent-dir lowering
|
|
394
|
-
* materializes. Throws on any control (or control
|
|
395
|
-
* verified read path for — partial acceptance would
|
|
530
|
+
* materializes. Throws {@link PrimeProfileError} on any control (or control
|
|
531
|
+
* facet) the fork has no verified read path for — partial acceptance would
|
|
532
|
+
* silently drop the rest.
|
|
396
533
|
*/
|
|
397
534
|
export declare function readPrimeProfileControls(profile: AgentProfile | undefined): PrimeProfileControls;
|
|
535
|
+
/**
|
|
536
|
+
* Refuse a run whose selected model disagrees with the profile's own.
|
|
537
|
+
*
|
|
538
|
+
* One authority per run: a profile names the model it was written and scored
|
|
539
|
+
* against, so running it on a different one silently scores a different agent.
|
|
540
|
+
* Both sides go through {@link modelIdsMatch}, so the qualification level a
|
|
541
|
+
* caller happened to use is not a conflict.
|
|
542
|
+
*
|
|
543
|
+
* `provider` is the executor's PROVIDER SELECTION, and is compared only when
|
|
544
|
+
* the executor's provider is part of model identity. An executor whose
|
|
545
|
+
* provider key is merely a namespace inside the agent dir passes it as
|
|
546
|
+
* undefined and gets the model-name comparison alone.
|
|
547
|
+
*/
|
|
548
|
+
export declare function assertPrimeModelAgreement(profile: AgentProfile | undefined, selected: {
|
|
549
|
+
provider?: string;
|
|
550
|
+
model: string;
|
|
551
|
+
}): void;
|
|
398
552
|
/** Deterministic harness-state entry id for a profile subagent name. */
|
|
399
553
|
export declare function primeSubagentEntryId(name: string): string;
|
|
400
554
|
/**
|
|
401
|
-
*
|
|
402
|
-
*
|
|
403
|
-
*
|
|
404
|
-
*
|
|
555
|
+
* Write one agent-dir file atomically (temp + rename).
|
|
556
|
+
*
|
|
557
|
+
* Every file this lowering writes lands in a directory a LIVE agent may be
|
|
558
|
+
* reading: prime reloads models.json at model-resolution time and re-reads the
|
|
559
|
+
* harness store on every system-prompt build, so a partial write is observable.
|
|
560
|
+
* `mode` is applied to the temp file before the rename, so the final file is
|
|
561
|
+
* never briefly world-readable.
|
|
562
|
+
*/
|
|
563
|
+
export declare function writePrimeAgentFile(path: string, data: string, mode?: number): void;
|
|
564
|
+
export interface PrimeMaterializeOptions {
|
|
565
|
+
/**
|
|
566
|
+
* Where the FLAG-NAMED files go — the prompt files and the skills root.
|
|
567
|
+
* Defaults to `agentDir`. An executor that does not own the agent dir points
|
|
568
|
+
* this at a per-run directory of its own, so binding a profile's prompts and
|
|
569
|
+
* skills writes nothing into a dir it must not modify. The context file and
|
|
570
|
+
* the harness store have no flag and always land in `agentDir`; refuse those
|
|
571
|
+
* with {@link primeProfileNeedsAgentDir} instead.
|
|
572
|
+
*/
|
|
573
|
+
flagFileRoot?: string;
|
|
574
|
+
/**
|
|
575
|
+
* Reports state the executor should see but that does not fail the run
|
|
576
|
+
* (currently: adopting entries a pre-shared executor left behind).
|
|
577
|
+
*/
|
|
578
|
+
warn?: (msg: string, meta?: Record<string, unknown>) => void;
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* Materialize the validated controls and return the extra spawn args that bind
|
|
582
|
+
* them. Runs before every spawn (same place models.json is materialized), so a
|
|
583
|
+
* continual agent dir is re-pinned to the current profile on each fresh prime
|
|
584
|
+
* process.
|
|
585
|
+
*/
|
|
586
|
+
export declare function materializePrimeProfileControls(agentDir: string, controls: PrimeProfileControls, options?: PrimeMaterializeOptions): string[];
|
|
587
|
+
/**
|
|
588
|
+
* Remove the files a PREVIOUS materialization of this lowering wrote, so a
|
|
589
|
+
* profile that shrank cannot leave a stale instruction file or skill behind for
|
|
590
|
+
* the next run against the same dirs.
|
|
591
|
+
*
|
|
592
|
+
* Only the paths this lowering NAMES are removed. There is deliberately no
|
|
593
|
+
* manifest: a manifest would live where the agent can write it, and reading a
|
|
594
|
+
* delete list out of agent-writable state turns pruning into an arbitrary-path
|
|
595
|
+
* delete.
|
|
405
596
|
*/
|
|
406
|
-
export declare function
|
|
597
|
+
export declare function prunePrimeProfileMaterial(agentDir: string, options?: Pick<PrimeMaterializeOptions, "flagFileRoot">): void;
|
|
407
598
|
/**
|
|
408
599
|
* Upsert profile subagent specs into the fork's global continual-harness
|
|
409
600
|
* store and remove profile-sourced entries the current profile no longer
|
|
410
|
-
* declares. Agent-written entries (`source: "agent"`)
|
|
601
|
+
* declares. Agent-written entries (`source: "agent"`) — and every top-level
|
|
602
|
+
* key this lowering does not manage — are preserved untouched.
|
|
411
603
|
*
|
|
412
|
-
* A
|
|
413
|
-
*
|
|
414
|
-
*
|
|
604
|
+
* A store that exists but no longer parses raises
|
|
605
|
+
* {@link PrimeHarnessStateError}: the file is the agent's own self-modification
|
|
606
|
+
* state, and overwriting it destroys what the run was supposed to observe.
|
|
415
607
|
*/
|
|
416
|
-
export declare function seedPrimeProfileSubagents(agentDir: string, subagents: PrimeProfileSubagent[], warn
|
|
608
|
+
export declare function seedPrimeProfileSubagents(agentDir: string, subagents: PrimeProfileSubagent[], warn?: (msg: string, meta?: Record<string, unknown>) => void): void;
|