@tangle-network/agent-interface 0.38.0 → 0.40.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/agent-execution-preparation.d.ts +2 -2
- package/dist/agent-execution-preparation.js +2 -59
- package/dist/agent-profile-canonical.d.ts +12 -0
- package/dist/agent-profile-canonical.js +79 -0
- package/dist/agent-profile-improvement-schema.d.ts +3 -6
- package/dist/agent-profile-improvement-schema.js +94 -56
- package/dist/agent-profile-improvement.d.ts +3 -3
- package/dist/agent-profile-snapshot.d.ts +9 -0
- package/dist/agent-profile-snapshot.js +24 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/profile-diff.d.ts +11 -0
- package/dist/profile-diff.js +123 -3
- package/package.json +1 -1
|
@@ -164,9 +164,9 @@ export declare const agentExecutionPreparationAxisResultSchema: z.ZodObject<{
|
|
|
164
164
|
harness: "harness";
|
|
165
165
|
confidential: "confidential";
|
|
166
166
|
extensions: "extensions";
|
|
167
|
-
instructions: "instructions";
|
|
168
167
|
skills: "skills";
|
|
169
168
|
commands: "commands";
|
|
169
|
+
instructions: "instructions";
|
|
170
170
|
systemPrompt: "systemPrompt";
|
|
171
171
|
modelDefault: "modelDefault";
|
|
172
172
|
modelSmall: "modelSmall";
|
|
@@ -308,9 +308,9 @@ export declare const agentExecutionPreparationReceiptSchema: z.ZodObject<{
|
|
|
308
308
|
harness: "harness";
|
|
309
309
|
confidential: "confidential";
|
|
310
310
|
extensions: "extensions";
|
|
311
|
-
instructions: "instructions";
|
|
312
311
|
skills: "skills";
|
|
313
312
|
commands: "commands";
|
|
313
|
+
instructions: "instructions";
|
|
314
314
|
systemPrompt: "systemPrompt";
|
|
315
315
|
modelDefault: "modelDefault";
|
|
316
316
|
modelSmall: "modelSmall";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { canonicalCandidateDigest, canonicalCandidateJson, isCanonicalJsonValue, isWellFormedUnicode, looksLikeCredential, omitTopLevelDigest, sha256DigestSchema, } from "./agent-candidate-schema-common.js";
|
|
3
|
+
import { canonicalAgentProfileValue } from "./agent-profile-canonical.js";
|
|
3
4
|
import { REASONING_EFFORTS, } from "./agent-profile.js";
|
|
4
5
|
import { AGENT_PROFILE_MATERIALIZATION_AXES, profileMaterializationRequests, } from "./agent-profile-materialization.js";
|
|
5
6
|
import { harnessTypeSchema } from "./harness.js";
|
|
@@ -190,7 +191,7 @@ export const agentExecutionPreparationReceiptSchema = z
|
|
|
190
191
|
*/
|
|
191
192
|
export function canonicalAgentProfileDigest(profile) {
|
|
192
193
|
const parsed = agentProfileSchema.parse(profile);
|
|
193
|
-
const material =
|
|
194
|
+
const material = canonicalAgentProfileValue(parsed);
|
|
194
195
|
if (material === undefined || !isCanonicalJsonValue(material)) {
|
|
195
196
|
throw new Error("AgentProfile must contain finite, acyclic RFC 8785 JSON values");
|
|
196
197
|
}
|
|
@@ -660,64 +661,6 @@ function cleanResolvedModel(model) {
|
|
|
660
661
|
}),
|
|
661
662
|
};
|
|
662
663
|
}
|
|
663
|
-
function canonicalProfileValue(value, path, ancestors) {
|
|
664
|
-
if (value === undefined)
|
|
665
|
-
return undefined;
|
|
666
|
-
if (value === null ||
|
|
667
|
-
typeof value === "boolean" ||
|
|
668
|
-
typeof value === "string") {
|
|
669
|
-
return value;
|
|
670
|
-
}
|
|
671
|
-
if (typeof value === "number") {
|
|
672
|
-
if (!Number.isFinite(value)) {
|
|
673
|
-
throw new Error(`AgentProfile ${renderPath(path)} must be finite`);
|
|
674
|
-
}
|
|
675
|
-
return value;
|
|
676
|
-
}
|
|
677
|
-
if (typeof value !== "object") {
|
|
678
|
-
throw new Error(`AgentProfile ${renderPath(path)} is not JSON serializable`);
|
|
679
|
-
}
|
|
680
|
-
if (ancestors.has(value)) {
|
|
681
|
-
throw new Error(`AgentProfile ${renderPath(path)} must be acyclic`);
|
|
682
|
-
}
|
|
683
|
-
const prototype = Object.getPrototypeOf(value);
|
|
684
|
-
if (!Array.isArray(value) && prototype !== Object.prototype && prototype !== null) {
|
|
685
|
-
throw new Error(`AgentProfile ${renderPath(path)} must be a plain JSON object`);
|
|
686
|
-
}
|
|
687
|
-
const nextAncestors = new Set(ancestors).add(value);
|
|
688
|
-
if (Array.isArray(value)) {
|
|
689
|
-
return Array.from({ length: value.length }, (_, index) => {
|
|
690
|
-
if (!Object.prototype.hasOwnProperty.call(value, index)) {
|
|
691
|
-
throw new Error(`AgentProfile ${renderPath([...path, index])} cannot be a sparse array hole`);
|
|
692
|
-
}
|
|
693
|
-
const entry = value[index];
|
|
694
|
-
const normalized = canonicalProfileValue(entry, [...path, index], nextAncestors);
|
|
695
|
-
if (normalized === undefined) {
|
|
696
|
-
throw new Error(`AgentProfile ${renderPath([...path, index])} cannot be undefined`);
|
|
697
|
-
}
|
|
698
|
-
return normalized;
|
|
699
|
-
});
|
|
700
|
-
}
|
|
701
|
-
const material = Object.create(null);
|
|
702
|
-
for (const [key, entry] of Object.entries(value)) {
|
|
703
|
-
if (!isWellFormedUnicode(key)) {
|
|
704
|
-
throw new Error(`AgentProfile ${renderPath(path)} has a record key that is not valid Unicode`);
|
|
705
|
-
}
|
|
706
|
-
const normalized = canonicalProfileValue(entry, [...path, key], nextAncestors);
|
|
707
|
-
if (normalized !== undefined) {
|
|
708
|
-
Object.defineProperty(material, key, {
|
|
709
|
-
value: normalized,
|
|
710
|
-
enumerable: true,
|
|
711
|
-
configurable: true,
|
|
712
|
-
writable: true,
|
|
713
|
-
});
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
|
-
return material;
|
|
717
|
-
}
|
|
718
|
-
function renderPath(path) {
|
|
719
|
-
return path.length === 0 ? "root" : path.join(".");
|
|
720
|
-
}
|
|
721
664
|
function readJsonPointer(root, pointer) {
|
|
722
665
|
let value = root;
|
|
723
666
|
for (const encoded of pointer.slice(1).split("/")) {
|
|
@@ -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[]):
|
|
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
|
|
113
|
+
message: "profile improvement patch must change the agent profile",
|
|
117
114
|
});
|
|
118
115
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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
|
-
|
|
158
|
-
|
|
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: ["
|
|
163
|
-
message: "profile
|
|
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
|
-
|
|
424
|
-
|
|
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
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
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
|
|
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.
|
|
56
|
-
*
|
|
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,9 @@
|
|
|
1
|
+
import type { AgentProfile } from "./agent-profile.js";
|
|
2
|
+
/**
|
|
3
|
+
* Detach, validate, and recursively freeze one AgentProfile at an intake boundary.
|
|
4
|
+
*
|
|
5
|
+
* The returned value is exactly the existing schema output: this function adds no
|
|
6
|
+
* defaults and applies no provider, model, or execution policy. Values outside the
|
|
7
|
+
* existing canonical profile JSON domain fail instead of becoming mutable state.
|
|
8
|
+
*/
|
|
9
|
+
export declare function snapshotAgentProfile(value: unknown): AgentProfile;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { canonicalAgentProfileValue } from "./agent-profile-canonical.js";
|
|
2
|
+
import { agentProfileSchema } from "./profile-schema.js";
|
|
3
|
+
/**
|
|
4
|
+
* Detach, validate, and recursively freeze one AgentProfile at an intake boundary.
|
|
5
|
+
*
|
|
6
|
+
* The returned value is exactly the existing schema output: this function adds no
|
|
7
|
+
* defaults and applies no provider, model, or execution policy. Values outside the
|
|
8
|
+
* existing canonical profile JSON domain fail instead of becoming mutable state.
|
|
9
|
+
*/
|
|
10
|
+
export function snapshotAgentProfile(value) {
|
|
11
|
+
const parsed = agentProfileSchema.parse(structuredClone(value));
|
|
12
|
+
canonicalAgentProfileValue(parsed);
|
|
13
|
+
return deepFreeze(parsed);
|
|
14
|
+
}
|
|
15
|
+
function deepFreeze(value, seen = new Set()) {
|
|
16
|
+
if (value === null || typeof value !== "object" || seen.has(value)) {
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
seen.add(value);
|
|
20
|
+
for (const child of Object.values(value)) {
|
|
21
|
+
deepFreeze(child, seen);
|
|
22
|
+
}
|
|
23
|
+
return Object.freeze(value);
|
|
24
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -623,6 +623,7 @@ export * from "./agent-profile-improvement.js";
|
|
|
623
623
|
export * from "./agent-profile-improvement-schema.js";
|
|
624
624
|
export * from "./agent-execution-limits.js";
|
|
625
625
|
export * from "./agent-profile.js";
|
|
626
|
+
export * from "./agent-profile-snapshot.js";
|
|
626
627
|
export * from "./agent-profile-activation.js";
|
|
627
628
|
export * from "./agent-profile-materialization.js";
|
|
628
629
|
export * from "./agent-execution-preparation.js";
|
package/dist/index.js
CHANGED
|
@@ -135,6 +135,7 @@ export * from "./agent-profile-improvement.js";
|
|
|
135
135
|
export * from "./agent-profile-improvement-schema.js";
|
|
136
136
|
export * from "./agent-execution-limits.js";
|
|
137
137
|
export * from "./agent-profile.js";
|
|
138
|
+
export * from "./agent-profile-snapshot.js";
|
|
138
139
|
export * from "./agent-profile-activation.js";
|
|
139
140
|
export * from "./agent-profile-materialization.js";
|
|
140
141
|
export * from "./agent-execution-preparation.js";
|
package/dist/profile-diff.d.ts
CHANGED
|
@@ -57,6 +57,17 @@ export interface AgentProfileDiff {
|
|
|
57
57
|
metadata?: Record<string, unknown>;
|
|
58
58
|
}
|
|
59
59
|
export declare function defineAgentProfileDiff<T extends AgentProfileDiff>(diff: T): T;
|
|
60
|
+
/**
|
|
61
|
+
* Construct deterministic ordered profile patches that reproduce `candidate`'s
|
|
62
|
+
* canonical profile value exactly when applied to `baseline`.
|
|
63
|
+
*
|
|
64
|
+
* Profile overlays append arrays and merge records, so changed fields are reset
|
|
65
|
+
* first and then replaced. Comparison and copied values follow the same
|
|
66
|
+
* undefined-entry normalization as profile identity. The returned values use
|
|
67
|
+
* the existing {@link AgentProfileDiff} contract; an unchanged profile returns
|
|
68
|
+
* no steps.
|
|
69
|
+
*/
|
|
70
|
+
export declare function diffAgentProfiles(baseline: AgentProfile, candidate: AgentProfile): AgentProfileDiff[];
|
|
60
71
|
export declare function applyAgentProfileDiff(base: AgentProfile, diff: AgentProfileDiff): AgentProfile;
|
|
61
72
|
export declare function changedAgentProfileAxes(diff: AgentProfileDiff): AgentProfileDiffAxis[];
|
|
62
73
|
export declare function pruneAgentProfileDiff(diff: AgentProfileDiff, axesToRemove: readonly AgentProfileDiffAxis[]): AgentProfileDiff;
|
package/dist/profile-diff.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { mergeAgentProfiles } from "./agent-profile.js";
|
|
2
|
+
import { canonicalAgentProfileJson } from "./agent-profile-canonical.js";
|
|
2
3
|
const agentProfileDiffPropertyAxes = [
|
|
3
4
|
"prompt",
|
|
4
5
|
"model",
|
|
@@ -20,6 +21,110 @@ void _agentProfileDiffPropertyAxesAreExhaustive;
|
|
|
20
21
|
export function defineAgentProfileDiff(diff) {
|
|
21
22
|
return diff;
|
|
22
23
|
}
|
|
24
|
+
const agentProfileResourceDiffPropertyAxes = [
|
|
25
|
+
"files",
|
|
26
|
+
"tools",
|
|
27
|
+
"skills",
|
|
28
|
+
"agents",
|
|
29
|
+
"commands",
|
|
30
|
+
"instructions",
|
|
31
|
+
"failOnError",
|
|
32
|
+
];
|
|
33
|
+
const _agentProfileResourceDiffPropertyAxesAreExhaustive = true;
|
|
34
|
+
void _agentProfileResourceDiffPropertyAxesAreExhaustive;
|
|
35
|
+
/**
|
|
36
|
+
* Construct deterministic ordered profile patches that reproduce `candidate`'s
|
|
37
|
+
* canonical profile value exactly when applied to `baseline`.
|
|
38
|
+
*
|
|
39
|
+
* Profile overlays append arrays and merge records, so changed fields are reset
|
|
40
|
+
* first and then replaced. Comparison and copied values follow the same
|
|
41
|
+
* undefined-entry normalization as profile identity. The returned values use
|
|
42
|
+
* the existing {@link AgentProfileDiff} contract; an unchanged profile returns
|
|
43
|
+
* no steps.
|
|
44
|
+
*/
|
|
45
|
+
export function diffAgentProfiles(baseline, candidate) {
|
|
46
|
+
const remove = {};
|
|
47
|
+
const set = {};
|
|
48
|
+
if (profileValuesDiffer(baseline.name, candidate.name) ||
|
|
49
|
+
profileValuesDiffer(baseline.description, candidate.description) ||
|
|
50
|
+
profileValuesDiffer(baseline.version, candidate.version)) {
|
|
51
|
+
remove.identity = true;
|
|
52
|
+
if (candidate.name !== undefined)
|
|
53
|
+
set.name = candidate.name;
|
|
54
|
+
if (candidate.description !== undefined) {
|
|
55
|
+
set.description = candidate.description;
|
|
56
|
+
}
|
|
57
|
+
if (candidate.version !== undefined)
|
|
58
|
+
set.version = candidate.version;
|
|
59
|
+
}
|
|
60
|
+
if (profileValuesDiffer(baseline.tags, candidate.tags)) {
|
|
61
|
+
remove.tags = true;
|
|
62
|
+
if (candidate.tags !== undefined) {
|
|
63
|
+
set.tags = canonicalProfileValue(candidate.tags);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
for (const axis of agentProfileDiffPropertyAxes) {
|
|
67
|
+
if (axis === "resources") {
|
|
68
|
+
replaceChangedProfileResources(baseline.resources, candidate.resources, remove, set);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (!profileValuesDiffer(baseline[axis], candidate[axis]))
|
|
72
|
+
continue;
|
|
73
|
+
Object.assign(remove, { [axis]: true });
|
|
74
|
+
const value = candidate[axis];
|
|
75
|
+
if (value !== undefined) {
|
|
76
|
+
Object.assign(set, { [axis]: canonicalProfileValue(value) });
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (Object.keys(remove).length === 0)
|
|
80
|
+
return [];
|
|
81
|
+
const reset = {
|
|
82
|
+
kind: "agent-profile-diff",
|
|
83
|
+
remove,
|
|
84
|
+
};
|
|
85
|
+
if (Object.keys(set).length === 0)
|
|
86
|
+
return [reset];
|
|
87
|
+
return [reset, { kind: "agent-profile-diff", set }];
|
|
88
|
+
}
|
|
89
|
+
function replaceChangedProfileResources(baseline, candidate, remove, set) {
|
|
90
|
+
if (!profileValuesDiffer(baseline, candidate))
|
|
91
|
+
return;
|
|
92
|
+
const resourceRemove = {};
|
|
93
|
+
const resourceSet = {};
|
|
94
|
+
let changedSubfields = 0;
|
|
95
|
+
for (const axis of agentProfileResourceDiffPropertyAxes) {
|
|
96
|
+
if (!profileValuesDiffer(baseline?.[axis], candidate?.[axis]))
|
|
97
|
+
continue;
|
|
98
|
+
changedSubfields += 1;
|
|
99
|
+
Object.assign(resourceRemove, { [axis]: true });
|
|
100
|
+
const value = candidate?.[axis];
|
|
101
|
+
if (value !== undefined) {
|
|
102
|
+
Object.assign(resourceSet, { [axis]: canonicalProfileValue(value) });
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// Distinguish an absent resources object from an explicitly empty one.
|
|
106
|
+
if (changedSubfields === 0) {
|
|
107
|
+
remove.resources = true;
|
|
108
|
+
if (candidate !== undefined) {
|
|
109
|
+
set.resources = canonicalProfileValue(candidate);
|
|
110
|
+
}
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
remove.resources = resourceRemove;
|
|
114
|
+
const canonicalCandidate = canonicalProfileValue(candidate);
|
|
115
|
+
if (Object.keys(resourceSet).length > 0 ||
|
|
116
|
+
(canonicalCandidate !== undefined &&
|
|
117
|
+
Object.keys(canonicalCandidate).length === 0)) {
|
|
118
|
+
set.resources = resourceSet;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function profileValuesDiffer(baseline, candidate) {
|
|
122
|
+
return (canonicalAgentProfileJson(baseline) !== canonicalAgentProfileJson(candidate));
|
|
123
|
+
}
|
|
124
|
+
function canonicalProfileValue(value) {
|
|
125
|
+
const json = canonicalAgentProfileJson(value);
|
|
126
|
+
return (json === undefined ? undefined : JSON.parse(json));
|
|
127
|
+
}
|
|
23
128
|
function asMutable(value) {
|
|
24
129
|
return value ? [...value] : undefined;
|
|
25
130
|
}
|
|
@@ -174,12 +279,26 @@ export function applyAgentProfileDiff(base, diff) {
|
|
|
174
279
|
const merged = mergeAgentProfiles(base, diff.set) ?? {};
|
|
175
280
|
return applyRemoval(merged, diff.remove);
|
|
176
281
|
}
|
|
282
|
+
function hasRemovalOperation(value) {
|
|
283
|
+
if (value === true)
|
|
284
|
+
return true;
|
|
285
|
+
if (Array.isArray(value))
|
|
286
|
+
return value.length > 0;
|
|
287
|
+
if (value && typeof value === "object") {
|
|
288
|
+
return Object.values(value).some(hasRemovalOperation);
|
|
289
|
+
}
|
|
290
|
+
return false;
|
|
291
|
+
}
|
|
177
292
|
export function changedAgentProfileAxes(diff) {
|
|
178
293
|
const axes = new Set();
|
|
179
294
|
const set = diff.set;
|
|
180
295
|
if (set) {
|
|
181
|
-
if (set.name
|
|
296
|
+
if (set.name !== undefined ||
|
|
297
|
+
set.description !== undefined ||
|
|
298
|
+
set.version !== undefined ||
|
|
299
|
+
set.tags !== undefined) {
|
|
182
300
|
axes.add("identity");
|
|
301
|
+
}
|
|
183
302
|
for (const axis of agentProfileDiffPropertyAxes) {
|
|
184
303
|
if (set[axis] !== undefined)
|
|
185
304
|
axes.add(axis);
|
|
@@ -187,10 +306,11 @@ export function changedAgentProfileAxes(diff) {
|
|
|
187
306
|
}
|
|
188
307
|
const remove = diff.remove;
|
|
189
308
|
if (remove) {
|
|
190
|
-
if (remove.identity || remove.tags)
|
|
309
|
+
if (hasRemovalOperation(remove.identity) || hasRemovalOperation(remove.tags)) {
|
|
191
310
|
axes.add("identity");
|
|
311
|
+
}
|
|
192
312
|
for (const axis of agentProfileDiffPropertyAxes) {
|
|
193
|
-
if (remove[axis]
|
|
313
|
+
if (hasRemovalOperation(remove[axis]))
|
|
194
314
|
axes.add(axis);
|
|
195
315
|
}
|
|
196
316
|
}
|