@tangle-network/agent-interface 0.37.0 → 0.39.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.
@@ -0,0 +1,41 @@
1
+ import { z } from "zod";
2
+ import type { Sha256Digest } from "./agent-candidate.js";
3
+ /** One exact native file applied before an agent process starts. */
4
+ export interface AgentProfileActivationFileEvidence {
5
+ path: string;
6
+ mode: number;
7
+ content: string;
8
+ }
9
+ /**
10
+ * Shared evidence carried by every exact profile activation.
11
+ *
12
+ * The plan type remains owned by the producer because ordinary agent runs and
13
+ * sealed benchmark runs have different plan contracts. The applied file bytes
14
+ * and activation identity are shared so those producers cannot invent
15
+ * competing activation evidence.
16
+ */
17
+ export interface AgentProfileActivationEvidence<TProfilePlan = unknown> {
18
+ profilePlan: TProfilePlan;
19
+ files: AgentProfileActivationFileEvidence[];
20
+ digest: Sha256Digest;
21
+ }
22
+ /** Runtime validator for one exact native profile file. */
23
+ export declare const agentProfileActivationFileEvidenceSchema: z.ZodObject<{
24
+ path: z.ZodString;
25
+ mode: z.ZodNumber;
26
+ content: z.ZodString;
27
+ }, z.core.$strict>;
28
+ /**
29
+ * Compose the shared activation evidence with a producer-owned exact plan.
30
+ * Candidate materialization uses this factory and adds its stronger plan/file
31
+ * consistency checks around the resulting schema.
32
+ */
33
+ export declare function createAgentProfileActivationEvidenceSchema<TProfilePlan>(profilePlanSchema: z.ZodType<TProfilePlan>): z.ZodObject<{
34
+ profilePlan: z.ZodType<TProfilePlan, unknown, z.core.$ZodTypeInternals<TProfilePlan, unknown>>;
35
+ files: z.ZodArray<z.ZodObject<{
36
+ path: z.ZodString;
37
+ mode: z.ZodNumber;
38
+ content: z.ZodString;
39
+ }, z.core.$strict>>;
40
+ digest: z.ZodType<`sha256:${string}`, unknown, z.core.$ZodTypeInternals<`sha256:${string}`, unknown>>;
41
+ }, z.core.$strict>;
@@ -0,0 +1,24 @@
1
+ import { z } from "zod";
2
+ import { isSafeRelativePath, isWellFormedUnicode, sha256DigestSchema, } from "./agent-candidate-schema-common.js";
3
+ /** Runtime validator for one exact native profile file. */
4
+ export const agentProfileActivationFileEvidenceSchema = z.strictObject({
5
+ path: z
6
+ .string()
7
+ .refine((value) => isSafeRelativePath(value, false), "profile activation file must use a canonical relative path"),
8
+ mode: z.number().int().min(0).max(0o777),
9
+ content: z
10
+ .string()
11
+ .refine(isWellFormedUnicode, "profile activation content must be valid Unicode"),
12
+ });
13
+ /**
14
+ * Compose the shared activation evidence with a producer-owned exact plan.
15
+ * Candidate materialization uses this factory and adds its stronger plan/file
16
+ * consistency checks around the resulting schema.
17
+ */
18
+ export function createAgentProfileActivationEvidenceSchema(profilePlanSchema) {
19
+ return z.strictObject({
20
+ profilePlan: profilePlanSchema,
21
+ files: z.array(agentProfileActivationFileEvidenceSchema),
22
+ digest: sha256DigestSchema,
23
+ });
24
+ }
@@ -0,0 +1,12 @@
1
+ import type { AgentCandidateJsonValue } from "./agent-candidate.js";
2
+ /**
3
+ * Normalize profile-shaped data into the canonical JSON domain used for public
4
+ * profile identity. Undefined object entries are omitted; unsupported values,
5
+ * sparse arrays, and cycles are rejected with their profile path.
6
+ *
7
+ * This module is intentionally internal. Public callers should use the
8
+ * profile-level operations that own validation, identity, or diff semantics.
9
+ */
10
+ export declare function canonicalAgentProfileValue(value: unknown): AgentCandidateJsonValue | undefined;
11
+ /** Canonical serialization after applying AgentProfile normalization rules. */
12
+ export declare function canonicalAgentProfileJson(value: unknown): string | undefined;
@@ -0,0 +1,79 @@
1
+ import { canonicalCandidateJson, isWellFormedUnicode, } from "./agent-candidate-schema-common.js";
2
+ /**
3
+ * Normalize profile-shaped data into the canonical JSON domain used for public
4
+ * profile identity. Undefined object entries are omitted; unsupported values,
5
+ * sparse arrays, and cycles are rejected with their profile path.
6
+ *
7
+ * This module is intentionally internal. Public callers should use the
8
+ * profile-level operations that own validation, identity, or diff semantics.
9
+ */
10
+ export function canonicalAgentProfileValue(value) {
11
+ return normalizeAgentProfileValue(value, [], new Set());
12
+ }
13
+ /** Canonical serialization after applying AgentProfile normalization rules. */
14
+ export function canonicalAgentProfileJson(value) {
15
+ const normalized = canonicalAgentProfileValue(value);
16
+ return normalized === undefined
17
+ ? undefined
18
+ : canonicalCandidateJson(normalized);
19
+ }
20
+ function normalizeAgentProfileValue(value, path, ancestors) {
21
+ if (value === undefined)
22
+ return undefined;
23
+ if (value === null ||
24
+ typeof value === "boolean" ||
25
+ typeof value === "string") {
26
+ return value;
27
+ }
28
+ if (typeof value === "number") {
29
+ if (!Number.isFinite(value)) {
30
+ throw new Error(`AgentProfile ${renderPath(path)} must be finite`);
31
+ }
32
+ return value;
33
+ }
34
+ if (typeof value !== "object") {
35
+ throw new Error(`AgentProfile ${renderPath(path)} is not JSON serializable`);
36
+ }
37
+ if (ancestors.has(value)) {
38
+ throw new Error(`AgentProfile ${renderPath(path)} must be acyclic`);
39
+ }
40
+ const prototype = Object.getPrototypeOf(value);
41
+ if (!Array.isArray(value) &&
42
+ prototype !== Object.prototype &&
43
+ prototype !== null) {
44
+ throw new Error(`AgentProfile ${renderPath(path)} must be a plain JSON object`);
45
+ }
46
+ const nextAncestors = new Set(ancestors).add(value);
47
+ if (Array.isArray(value)) {
48
+ return Array.from({ length: value.length }, (_, index) => {
49
+ if (!Object.prototype.hasOwnProperty.call(value, index)) {
50
+ throw new Error(`AgentProfile ${renderPath([...path, index])} cannot be a sparse array hole`);
51
+ }
52
+ const entry = value[index];
53
+ const normalized = normalizeAgentProfileValue(entry, [...path, index], nextAncestors);
54
+ if (normalized === undefined) {
55
+ throw new Error(`AgentProfile ${renderPath([...path, index])} cannot be undefined`);
56
+ }
57
+ return normalized;
58
+ });
59
+ }
60
+ const material = Object.create(null);
61
+ for (const [key, entry] of Object.entries(value)) {
62
+ if (!isWellFormedUnicode(key)) {
63
+ throw new Error(`AgentProfile ${renderPath(path)} has a record key that is not valid Unicode`);
64
+ }
65
+ const normalized = normalizeAgentProfileValue(entry, [...path, key], nextAncestors);
66
+ if (normalized !== undefined) {
67
+ Object.defineProperty(material, key, {
68
+ value: normalized,
69
+ enumerable: true,
70
+ configurable: true,
71
+ writable: true,
72
+ });
73
+ }
74
+ }
75
+ return material;
76
+ }
77
+ function renderPath(path) {
78
+ return path.length === 0 ? "root" : path.join(".");
79
+ }
@@ -1,4 +1,5 @@
1
1
  import { z } from "zod";
2
+ import type { AgentImprovementSurface } from "./agent-candidate.js";
2
3
  import type { AgentProfileDiff } from "./profile-diff.js";
3
4
  export declare const agentProfileImprovementTaskSchema: z.ZodObject<{
4
5
  kind: z.ZodLiteral<"agent-profile-improvement-task">;
@@ -183,11 +184,7 @@ export declare const agentProfileImprovementExecutionRefSchema: z.ZodObject<{
183
184
  digest: z.ZodType<`sha256:${string}`, unknown, z.core.$ZodTypeInternals<`sha256:${string}`, unknown>>;
184
185
  kind: z.ZodLiteral<"agent-profile-improvement-execution-ref">;
185
186
  }, z.core.$strict>;
186
- /**
187
- * The first product path changes only prompt and skills, but it uses the
188
- * shared profile-diff language so execution and activation apply identical
189
- * ordered patches.
190
- */
187
+ /** One strict canonical profile patch in an ordered measured change. */
191
188
  export declare const agentProfileImprovementChangeStepSchema: z.ZodType<AgentProfileDiff, unknown, z.core.$ZodTypeInternals<AgentProfileDiff, unknown>>;
192
189
  export declare const agentProfileImprovementChangeSchema: z.ZodTuple<[z.ZodType<AgentProfileDiff, unknown, z.core.$ZodTypeInternals<AgentProfileDiff, unknown>>], z.ZodType<AgentProfileDiff, unknown, z.core.$ZodTypeInternals<AgentProfileDiff, unknown>>>;
193
190
  export declare const agentProfileImprovementExperimentSchema: z.ZodObject<{
@@ -1208,4 +1205,4 @@ export declare const agentProfileImprovementMeasuredComparisonSchema: z.ZodObjec
1208
1205
  }, z.core.$strict>;
1209
1206
  }, z.core.$strict>>;
1210
1207
  }, z.core.$strict>;
1211
- export declare function changedProfileImprovementSurfaces(change: readonly AgentProfileDiff[]): string[];
1208
+ export declare function changedProfileImprovementSurfaces(change: readonly AgentProfileDiff[]): AgentImprovementSurface[];
@@ -1,4 +1,5 @@
1
1
  import { z } from "zod";
2
+ import { changedAgentProfileAxes } from "./profile-diff.js";
2
3
  import { agentCandidateLineageSchema } from "./agent-candidate-lineage-schema.js";
3
4
  import { canonicalCandidateDigest, isCanonicalJsonValue, omitTopLevelDigest, sha256DigestSchema, } from "./agent-candidate-schema-common.js";
4
5
  import { refineAgentExecutionWithinLimits } from "./agent-execution-limits.js";
@@ -103,67 +104,48 @@ export const agentProfileImprovementExecutionRefSchema = profileImprovementEvide
103
104
  kind: z.literal("agent-profile-improvement-execution-ref"),
104
105
  })
105
106
  .strict();
106
- /**
107
- * The first product path changes only prompt and skills, but it uses the
108
- * shared profile-diff language so execution and activation apply identical
109
- * ordered patches.
110
- */
107
+ /** One strict canonical profile patch in an ordered measured change. */
111
108
  export const agentProfileImprovementChangeStepSchema = agentProfileDiffSchema.superRefine((change, ctx) => {
112
109
  const changed = changedProfileImprovementSurfaces([change]);
113
110
  if (changed.length === 0) {
114
111
  ctx.addIssue({
115
112
  code: "custom",
116
- message: "profile improvement patch must change prompt or skills",
113
+ message: "profile improvement patch must change the agent profile",
117
114
  });
118
115
  }
119
- const setKeys = Object.keys(change.set ?? {});
120
- if (setKeys.some((key) => key !== "prompt" && key !== "resources")) {
121
- ctx.addIssue({
122
- code: "custom",
123
- path: ["set"],
124
- message: "profile improvement patches may set only prompt or skill resources",
125
- });
126
- }
127
- const setResourceKeys = Object.keys(change.set?.resources ?? {});
128
- if (setResourceKeys.some((key) => key !== "skills")) {
129
- ctx.addIssue({
130
- code: "custom",
131
- path: ["set", "resources"],
132
- message: "profile improvement patches may set only skill resources",
133
- });
134
- }
135
- if (change.set?.resources?.skills?.some((skill) => skill.kind !== "inline")) {
136
- ctx.addIssue({
137
- code: "custom",
138
- path: ["set", "resources", "skills"],
139
- message: "measured profile skill patches require inline content with exact bytes",
140
- });
141
- }
142
- const removeKeys = Object.keys(change.remove ?? {});
143
- if (removeKeys.some((key) => key !== "prompt" && key !== "resources")) {
144
- ctx.addIssue({
145
- code: "custom",
146
- path: ["remove"],
147
- message: "profile improvement patches may remove only prompt or skill resources",
148
- });
149
- }
150
- if (change.remove?.resources === true) {
151
- ctx.addIssue({
152
- code: "custom",
153
- path: ["remove", "resources"],
154
- message: "profile improvement patches may not remove unrelated resources",
155
- });
116
+ refineInlineProfileImprovementResources(change, ctx);
117
+ });
118
+ function refineInlineProfileImprovementResources(change, ctx) {
119
+ const resources = change.set?.resources;
120
+ if (resources === undefined)
121
+ return;
122
+ const groups = [
123
+ ["files", resources.files?.map((file) => file.resource)],
124
+ ["tools", resources.tools],
125
+ ["skills", resources.skills],
126
+ ["agents", resources.agents],
127
+ ["commands", resources.commands],
128
+ ];
129
+ for (const [name, refs] of groups) {
130
+ for (const [index, ref] of (refs ?? []).entries()) {
131
+ if (ref.kind !== "inline") {
132
+ ctx.addIssue({
133
+ code: "custom",
134
+ path: ["set", "resources", name, index],
135
+ message: "measured profile resource patches require inline content with exact bytes",
136
+ });
137
+ }
138
+ }
156
139
  }
157
- const removeResources = change.remove?.resources;
158
- const removeResourceKeys = Object.keys(typeof removeResources === "object" ? removeResources : {});
159
- if (removeResourceKeys.some((key) => key !== "skills")) {
140
+ if (typeof resources.instructions === "object" &&
141
+ resources.instructions.kind !== "inline") {
160
142
  ctx.addIssue({
161
143
  code: "custom",
162
- path: ["remove", "resources"],
163
- message: "profile improvement patches may remove only skill resources",
144
+ path: ["set", "resources", "instructions"],
145
+ message: "measured profile resource patches require inline content with exact bytes",
164
146
  });
165
147
  }
166
- });
148
+ }
167
149
  export const agentProfileImprovementChangeSchema = z
168
150
  .tuple([agentProfileImprovementChangeStepSchema])
169
151
  .rest(agentProfileImprovementChangeStepSchema)
@@ -420,16 +402,72 @@ export const agentProfileImprovementMeasuredComparisonSchema = z
420
402
  export function changedProfileImprovementSurfaces(change) {
421
403
  const surfaces = new Set();
422
404
  for (const step of change) {
423
- if (step.set?.prompt !== undefined || step.remove?.prompt !== undefined) {
424
- surfaces.add("prompt");
405
+ for (const axis of changedAgentProfileAxes(step)) {
406
+ if (axis === "prompt" ||
407
+ axis === "tools" ||
408
+ axis === "mcp" ||
409
+ axis === "hooks" ||
410
+ axis === "subagents") {
411
+ surfaces.add(axis);
412
+ }
413
+ else if (axis === "resources") {
414
+ const resources = changedResourceSurfaces(step);
415
+ for (const surface of resources.granular)
416
+ surfaces.add(surface);
417
+ if (resources.other)
418
+ surfaces.add("agent-profile");
419
+ }
420
+ else {
421
+ surfaces.add("agent-profile");
422
+ }
425
423
  }
426
- if (step.set?.resources?.skills !== undefined ||
427
- (typeof step.remove?.resources === "object" &&
428
- step.remove.resources.skills !== undefined)) {
429
- surfaces.add("skills");
424
+ }
425
+ const canonicalOrder = [
426
+ "prompt",
427
+ "skills",
428
+ "tools",
429
+ "mcp",
430
+ "hooks",
431
+ "subagents",
432
+ "agent-profile",
433
+ ];
434
+ return canonicalOrder.filter((surface) => surfaces.has(surface));
435
+ }
436
+ function changedResourceSurfaces(change) {
437
+ const granular = new Set();
438
+ let other = false;
439
+ const setResources = change.set?.resources;
440
+ if (setResources !== undefined) {
441
+ const keys = Object.keys(setResources);
442
+ for (const key of keys) {
443
+ if (key === "skills" || key === "tools")
444
+ granular.add(key);
445
+ else if (key === "agents")
446
+ granular.add("subagents");
447
+ else
448
+ other = true;
449
+ }
450
+ other ||= keys.length === 0;
451
+ }
452
+ const removeResources = change.remove?.resources;
453
+ if (removeResources === true) {
454
+ granular.add("skills");
455
+ granular.add("tools");
456
+ granular.add("subagents");
457
+ other = true;
458
+ }
459
+ else if (removeResources !== undefined) {
460
+ const entries = Object.entries(removeResources).filter(([, value]) => Array.isArray(value) ? value.length > 0 : value === true);
461
+ for (const [key] of entries) {
462
+ if (key === "skills" || key === "tools")
463
+ granular.add(key);
464
+ else if (key === "agents")
465
+ granular.add("subagents");
466
+ else
467
+ other = true;
430
468
  }
431
469
  }
432
- return [...surfaces].sort();
470
+ return { granular, other };
433
471
  }
434
472
  function refineProfileImprovementComparison(comparison, ctx) {
435
473
  const { suite, tasks } = comparison.experiment.benchmark;
@@ -52,9 +52,9 @@ export interface AgentProfileImprovementExecutionRef extends AgentProfileImprove
52
52
  * Ordered portable profile patches retained for review and activation.
53
53
  *
54
54
  * Applying the steps in order matters: a full resource replacement is a reset
55
- * followed by a replacement step. Prompt and inline skill content can be
56
- * sensitive, so the product that persists this value owns access control and
57
- * redaction.
55
+ * followed by a replacement step. Complete profile diffs can contain sensitive
56
+ * host-owned configuration, so the product that persists this value owns access
57
+ * control and redaction.
58
58
  */
59
59
  export type AgentProfileImprovementChange = [AgentProfileDiff, ...AgentProfileDiff[]];
60
60
  /** A measured comparison of two states of one host-owned agent profile. */
@@ -0,0 +1,34 @@
1
+ import type { AgentProfile } from "./agent-profile.js";
2
+ /**
3
+ * The 29 canonical AgentProfile leaves that can affect one execution.
4
+ *
5
+ * Compound parents such as `model`, `prompt`, and `resources` are deliberately
6
+ * absent. A producer must report the exact requested leaf instead of claiming
7
+ * a parent while silently dropping one of its children.
8
+ */
9
+ export declare const AGENT_PROFILE_MATERIALIZATION_AXES: readonly ["name", "description", "version", "tags", "systemPrompt", "instructions", "modelDefault", "modelSmall", "modelProvider", "modelReasoningEffort", "modelMetadata", "harness", "permissions", "tools", "mcp", "connections", "subagents", "files", "resourceTools", "skills", "resourceAgents", "commands", "resourceInstructions", "resourceFailOnError", "hooks", "modes", "confidential", "metadata", "extensions"];
10
+ /** One exact leaf of the public AgentProfile contract. */
11
+ export type AgentProfileMaterializationAxis = (typeof AGENT_PROFILE_MATERIALIZATION_AXES)[number];
12
+ /** Compatibility name used by runtimes that distinguish canonical axes. */
13
+ export type CanonicalAgentProfileMaterializationAxis = AgentProfileMaterializationAxis;
14
+ /** One requested profile leaf and its canonical RFC 6901 JSON Pointer. */
15
+ export interface AgentProfileMaterializationRequest {
16
+ axis: AgentProfileMaterializationAxis;
17
+ path: string;
18
+ }
19
+ /**
20
+ * Return every canonical profile leaf that contains a meaningful request.
21
+ * Every explicit value is a request, including empty strings, empty
22
+ * collections, `null`, `false`, and `0`. Only an absent/undefined leaf is
23
+ * omitted.
24
+ */
25
+ export declare function profileMaterializationAxes(profile: AgentProfile): readonly AgentProfileMaterializationAxis[];
26
+ /**
27
+ * Expand requested axes into exact JSON Pointer paths.
28
+ *
29
+ * Compound maps and arrays produce one row per explicit scalar leaf. An empty
30
+ * compound value produces its axis-root path. This prevents an executor from
31
+ * acknowledging one tool, server, resource, or instruction while claiming the
32
+ * whole axis, without losing an explicit request to clear that axis.
33
+ */
34
+ export declare function profileMaterializationRequests(profile: AgentProfile): readonly AgentProfileMaterializationRequest[];
@@ -0,0 +1,243 @@
1
+ /**
2
+ * The 29 canonical AgentProfile leaves that can affect one execution.
3
+ *
4
+ * Compound parents such as `model`, `prompt`, and `resources` are deliberately
5
+ * absent. A producer must report the exact requested leaf instead of claiming
6
+ * a parent while silently dropping one of its children.
7
+ */
8
+ export const AGENT_PROFILE_MATERIALIZATION_AXES = [
9
+ "name",
10
+ "description",
11
+ "version",
12
+ "tags",
13
+ "systemPrompt",
14
+ "instructions",
15
+ "modelDefault",
16
+ "modelSmall",
17
+ "modelProvider",
18
+ "modelReasoningEffort",
19
+ "modelMetadata",
20
+ "harness",
21
+ "permissions",
22
+ "tools",
23
+ "mcp",
24
+ "connections",
25
+ "subagents",
26
+ "files",
27
+ "resourceTools",
28
+ "skills",
29
+ "resourceAgents",
30
+ "commands",
31
+ "resourceInstructions",
32
+ "resourceFailOnError",
33
+ "hooks",
34
+ "modes",
35
+ "confidential",
36
+ "metadata",
37
+ "extensions",
38
+ ];
39
+ const profileProperties = [
40
+ "prompt",
41
+ "model",
42
+ "harness",
43
+ "permissions",
44
+ "tools",
45
+ "mcp",
46
+ "connections",
47
+ "subagents",
48
+ "resources",
49
+ "hooks",
50
+ "modes",
51
+ "confidential",
52
+ "metadata",
53
+ "extensions",
54
+ ];
55
+ const profilePropertiesAreExhaustive = true;
56
+ void profilePropertiesAreExhaustive;
57
+ const AXIS_DESCRIPTORS = [
58
+ { axis: "name", rootPath: "/name", value: (profile) => profile.name },
59
+ {
60
+ axis: "description",
61
+ rootPath: "/description",
62
+ value: (profile) => profile.description,
63
+ },
64
+ { axis: "version", rootPath: "/version", value: (profile) => profile.version },
65
+ { axis: "tags", rootPath: "/tags", value: (profile) => profile.tags },
66
+ {
67
+ axis: "systemPrompt",
68
+ rootPath: "/prompt/systemPrompt",
69
+ value: (profile) => profile.prompt?.systemPrompt,
70
+ },
71
+ {
72
+ axis: "instructions",
73
+ rootPath: "/prompt/instructions",
74
+ value: (profile) => profile.prompt?.instructions,
75
+ },
76
+ {
77
+ axis: "modelDefault",
78
+ rootPath: "/model/default",
79
+ value: (profile) => profile.model?.default,
80
+ },
81
+ {
82
+ axis: "modelSmall",
83
+ rootPath: "/model/small",
84
+ value: (profile) => profile.model?.small,
85
+ },
86
+ {
87
+ axis: "modelProvider",
88
+ rootPath: "/model/provider",
89
+ value: (profile) => profile.model?.provider,
90
+ },
91
+ {
92
+ axis: "modelReasoningEffort",
93
+ rootPath: "/model/reasoningEffort",
94
+ value: (profile) => profile.model?.reasoningEffort,
95
+ },
96
+ {
97
+ axis: "modelMetadata",
98
+ rootPath: "/model/metadata",
99
+ value: (profile) => profile.model?.metadata,
100
+ },
101
+ { axis: "harness", rootPath: "/harness", value: (profile) => profile.harness },
102
+ {
103
+ axis: "permissions",
104
+ rootPath: "/permissions",
105
+ value: (profile) => profile.permissions,
106
+ },
107
+ { axis: "tools", rootPath: "/tools", value: (profile) => profile.tools },
108
+ { axis: "mcp", rootPath: "/mcp", value: (profile) => profile.mcp },
109
+ {
110
+ axis: "connections",
111
+ rootPath: "/connections",
112
+ value: (profile) => profile.connections,
113
+ },
114
+ {
115
+ axis: "subagents",
116
+ rootPath: "/subagents",
117
+ value: (profile) => profile.subagents,
118
+ },
119
+ {
120
+ axis: "files",
121
+ rootPath: "/resources/files",
122
+ value: (profile) => profile.resources?.files,
123
+ },
124
+ {
125
+ axis: "resourceTools",
126
+ rootPath: "/resources/tools",
127
+ value: (profile) => profile.resources?.tools,
128
+ },
129
+ {
130
+ axis: "skills",
131
+ rootPath: "/resources/skills",
132
+ value: (profile) => profile.resources?.skills,
133
+ },
134
+ {
135
+ axis: "resourceAgents",
136
+ rootPath: "/resources/agents",
137
+ value: (profile) => profile.resources?.agents,
138
+ },
139
+ {
140
+ axis: "commands",
141
+ rootPath: "/resources/commands",
142
+ value: (profile) => profile.resources?.commands,
143
+ },
144
+ {
145
+ axis: "resourceInstructions",
146
+ rootPath: "/resources/instructions",
147
+ value: (profile) => profile.resources?.instructions,
148
+ },
149
+ {
150
+ axis: "resourceFailOnError",
151
+ rootPath: "/resources/failOnError",
152
+ value: (profile) => profile.resources?.failOnError,
153
+ },
154
+ { axis: "hooks", rootPath: "/hooks", value: (profile) => profile.hooks },
155
+ { axis: "modes", rootPath: "/modes", value: (profile) => profile.modes },
156
+ {
157
+ axis: "confidential",
158
+ rootPath: "/confidential",
159
+ value: (profile) => profile.confidential,
160
+ },
161
+ {
162
+ axis: "metadata",
163
+ rootPath: "/metadata",
164
+ value: (profile) => profile.metadata,
165
+ },
166
+ {
167
+ axis: "extensions",
168
+ rootPath: "/extensions",
169
+ value: (profile) => profile.extensions,
170
+ },
171
+ ];
172
+ const axisDescriptorsAreExhaustive = true;
173
+ void axisDescriptorsAreExhaustive;
174
+ /**
175
+ * Return every canonical profile leaf that contains a meaningful request.
176
+ * Every explicit value is a request, including empty strings, empty
177
+ * collections, `null`, `false`, and `0`. Only an absent/undefined leaf is
178
+ * omitted.
179
+ */
180
+ export function profileMaterializationAxes(profile) {
181
+ const axes = [];
182
+ for (const descriptor of AXIS_DESCRIPTORS) {
183
+ if (descriptor.value(profile) !== undefined) {
184
+ axes.push(descriptor.axis);
185
+ }
186
+ }
187
+ return axes;
188
+ }
189
+ /**
190
+ * Expand requested axes into exact JSON Pointer paths.
191
+ *
192
+ * Compound maps and arrays produce one row per explicit scalar leaf. An empty
193
+ * compound value produces its axis-root path. This prevents an executor from
194
+ * acknowledging one tool, server, resource, or instruction while claiming the
195
+ * whole axis, without losing an explicit request to clear that axis.
196
+ */
197
+ export function profileMaterializationRequests(profile) {
198
+ const requests = [];
199
+ for (const descriptor of AXIS_DESCRIPTORS) {
200
+ const paths = materializationValuePaths(descriptor.value(profile), descriptor.rootPath);
201
+ for (const path of paths) {
202
+ requests.push({ axis: descriptor.axis, path });
203
+ }
204
+ }
205
+ return requests;
206
+ }
207
+ function materializationValuePaths(root, rootPath) {
208
+ const paths = [];
209
+ visitMaterializationValue(root, rootPath, [], paths);
210
+ return paths;
211
+ }
212
+ function visitMaterializationValue(value, path, ancestors, paths) {
213
+ if (value === undefined)
214
+ return false;
215
+ if (value === null || typeof value !== "object") {
216
+ paths.push(path);
217
+ return true;
218
+ }
219
+ if (ancestors.includes(value)) {
220
+ paths.push(path);
221
+ return true;
222
+ }
223
+ const nextAncestors = [...ancestors, value];
224
+ const entries = Array.isArray(value)
225
+ ? Array.from({ length: value.length }, (_, index) => [
226
+ String(index),
227
+ Object.prototype.hasOwnProperty.call(value, index)
228
+ ? value[index]
229
+ : undefined,
230
+ ])
231
+ : Object.entries(value).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0);
232
+ let childRequested = false;
233
+ for (const [key, child] of entries) {
234
+ childRequested =
235
+ visitMaterializationValue(child, `${path}/${escapeJsonPointerSegment(key)}`, nextAncestors, paths) || childRequested;
236
+ }
237
+ if (!childRequested)
238
+ paths.push(path);
239
+ return true;
240
+ }
241
+ function escapeJsonPointerSegment(value) {
242
+ return value.replace(/~/g, "~0").replace(/\//g, "~1");
243
+ }