@axiastudio/aioc 0.2.1 → 0.2.2
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/README.md
CHANGED
|
@@ -8,7 +8,7 @@ Project home and documentation: [https://axiastudio.github.io/aioc](https://axia
|
|
|
8
8
|
|
|
9
9
|
## Release Status
|
|
10
10
|
|
|
11
|
-
Current stable release: `0.2.
|
|
11
|
+
Current stable release: `0.2.2`.
|
|
12
12
|
The stable line started with `0.1.0`.
|
|
13
13
|
The core runtime surface is compatibility-managed. Breaking changes to the stable surface should ship only with explicit migration guidance and release notes.
|
|
14
14
|
|
|
@@ -18,8 +18,9 @@ AIOC `0.1.0` stabilized the core runtime surface, public documentation aligned t
|
|
|
18
18
|
AIOC `0.1.1` adds thread-history utilities, the run-output stream adapter, and provider-specific instruction-role documentation without changing the stable governance model.
|
|
19
19
|
AIOC `0.1.2` adds approval evidence helpers for application-owned approval workflows.
|
|
20
20
|
|
|
21
|
-
AIOC `0.2.0` adds implemented runtime utilities and
|
|
21
|
+
AIOC `0.2.0` adds implemented runtime utilities and the Agent Harness Descriptor API. The core runtime remains compatibility-managed; descriptor shape and loader helpers are now part of the supported `0.2.x` surface and may evolve across `0.x` minor releases only with explicit migration guidance.
|
|
22
22
|
AIOC `0.2.1` realigns the `0.2.x` package line with the current `main` history after `0.2.0` was published from the descriptor release branch.
|
|
23
|
+
AIOC `0.2.2` completes the descriptor instruction-composition surface with reusable `instruction_parts`, ordered `instructions_sequence`, and boolean `where` gates.
|
|
23
24
|
|
|
24
25
|
- Release notes: `CHANGELOG.md`
|
|
25
26
|
- Historical beta contract snapshot: `docs/BETA-CONTRACT.md`
|
|
@@ -109,7 +110,7 @@ console.log(result.finalOutput);
|
|
|
109
110
|
- thread history utilities `toThreadHistory(...)`, `appendUserMessage(...)`, `replaceThreadHistory(...)`, `applyRunResultHistory(...)`
|
|
110
111
|
- JSON helper `toJsonValue(...)`
|
|
111
112
|
|
|
112
|
-
##
|
|
113
|
+
## Agent Harness Descriptor
|
|
113
114
|
|
|
114
115
|
- `buildAgentHarness(...)`
|
|
115
116
|
- `hashAgentHarnessDescriptor(...)`
|
|
@@ -117,7 +118,7 @@ console.log(result.finalOutput);
|
|
|
117
118
|
- `loadAgentHarnessDescriptorFromFile(...)`
|
|
118
119
|
- `AgentHarnessDescriptor` and related descriptor types
|
|
119
120
|
|
|
120
|
-
The Agent Harness Descriptor is included in the `0.2.x`
|
|
121
|
+
The Agent Harness Descriptor is included in the supported `0.2.x` API surface as the `aioc.agent_graph.v0` descriptor contract. Use it for controlled configuration, examples, and evaluation harnesses; keep executable tools, policies, providers, persistence, approvals, and deployment configuration in application code.
|
|
121
122
|
|
|
122
123
|
## Policy Gate (Minimal)
|
|
123
124
|
|
|
@@ -284,7 +285,7 @@ AIOC adopts the following non-negotiable principles:
|
|
|
284
285
|
- `docs/RFC-0008-run-stream-consumer-utilities.md` (`Accepted`)
|
|
285
286
|
- `docs/RFC-0009-governance-events-and-exporters.md` (`Experimental`)
|
|
286
287
|
- `docs/RFC-0010-policy-composition-helpers.md` (`Draft`)
|
|
287
|
-
- `docs/RFC-0011-agent-harness-descriptor.md` (`
|
|
288
|
+
- `docs/RFC-0011-agent-harness-descriptor.md` (`Accepted`)
|
|
288
289
|
- `docs/PRIVACY-BASELINE.md`
|
|
289
290
|
|
|
290
291
|
## Historical Snapshots
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"harness-descriptor-loader.d.ts","sourceRoot":"","sources":["../src/harness-descriptor-loader.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,sBAAsB,
|
|
1
|
+
{"version":3,"file":"harness-descriptor-loader.d.ts","sourceRoot":"","sources":["../src/harness-descriptor-loader.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,sBAAsB,EAIvB,MAAM,sBAAsB,CAAC;AAa9B,MAAM,MAAM,qBAAqB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAoE3D,MAAM,WAAW,iCAAiC;IAChD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,qBAAqB,CAAC;CACnC;AAED,MAAM,WAAW,yCAAyC;IACxD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAkeD,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,MAAM,EACZ,kBAAkB,CAAC,EACf,iCAAiC,GACjC,qBAAqB,GACxB,sBAAsB,CAuCxB;AAED,wBAAsB,kCAAkC,CACtD,cAAc,EAAE,MAAM,EACtB,OAAO,GAAE,yCAA8C,GACtD,OAAO,CAAC,sBAAsB,CAAC,CA0CjC"}
|
|
@@ -42,6 +42,23 @@ function collectInstructionFileReferences(descriptor, context) {
|
|
|
42
42
|
}
|
|
43
43
|
return references;
|
|
44
44
|
}
|
|
45
|
+
function collectInstructionSequenceReferences(descriptor, context) {
|
|
46
|
+
const references = [];
|
|
47
|
+
const defaultReference = createInstructionSequenceReference(descriptor.agent_defaults, "Harness descriptor agent_defaults", descriptor, context);
|
|
48
|
+
if (defaultReference) {
|
|
49
|
+
references.push(defaultReference);
|
|
50
|
+
}
|
|
51
|
+
if (!isPlainObject(descriptor.agents)) {
|
|
52
|
+
return references;
|
|
53
|
+
}
|
|
54
|
+
for (const [agentId, agentDescriptor] of Object.entries(descriptor.agents)) {
|
|
55
|
+
const agentReference = createInstructionSequenceReference(agentDescriptor, `Harness descriptor agent "${agentId}"`, descriptor, context);
|
|
56
|
+
if (agentReference) {
|
|
57
|
+
references.push(agentReference);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return references;
|
|
61
|
+
}
|
|
45
62
|
function createInstructionFileReference(value, label, context) {
|
|
46
63
|
if (!isPlainObject(value)) {
|
|
47
64
|
return undefined;
|
|
@@ -59,6 +76,102 @@ function createInstructionFileReference(value, label, context) {
|
|
|
59
76
|
appendInlineInstructions: input.appendInlineInstructions,
|
|
60
77
|
};
|
|
61
78
|
}
|
|
79
|
+
function createInstructionSequenceReference(value, label, descriptor, context) {
|
|
80
|
+
if (!isPlainObject(value) || !hasDefined(value, "instructions_sequence")) {
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
assertInstructionSequenceFields(value, label);
|
|
84
|
+
const rawItems = value.instructions_sequence;
|
|
85
|
+
if (!Array.isArray(rawItems) || rawItems.length === 0) {
|
|
86
|
+
throw new Error(`${label}.instructions_sequence must be a non-empty array.`);
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
target: value,
|
|
90
|
+
label,
|
|
91
|
+
items: rawItems.map((item, index) => readInstructionSequenceItem(item, `${label}.instructions_sequence[${index}]`, descriptor, context)),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function assertInstructionSequenceFields(value, label) {
|
|
95
|
+
for (const field of [
|
|
96
|
+
"instructions",
|
|
97
|
+
"instructions_file",
|
|
98
|
+
"instructions_files",
|
|
99
|
+
]) {
|
|
100
|
+
if (hasDefined(value, field)) {
|
|
101
|
+
throw new Error(`${label}.instructions_sequence is mutually exclusive with ${label}.${field}.`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function readInstructionSequenceItem(value, label, descriptor, context) {
|
|
106
|
+
if (!isPlainObject(value)) {
|
|
107
|
+
throw new Error(`${label} must be an object.`);
|
|
108
|
+
}
|
|
109
|
+
const hasFile = hasDefined(value, "file");
|
|
110
|
+
const hasRef = hasDefined(value, "ref");
|
|
111
|
+
const hasText = hasDefined(value, "text");
|
|
112
|
+
const definedKinds = [hasFile, hasRef, hasText].filter(Boolean).length;
|
|
113
|
+
if (definedKinds !== 1) {
|
|
114
|
+
throw new Error(`${label} must define exactly one of file, ref, or text.`);
|
|
115
|
+
}
|
|
116
|
+
const where = readInstructionWhere(value.where, label);
|
|
117
|
+
if (hasFile) {
|
|
118
|
+
const fileLabel = `${label}.file`;
|
|
119
|
+
(0, harness_descriptor_loader_paths_1.assertPromptFilePath)(value.file, fileLabel);
|
|
120
|
+
return {
|
|
121
|
+
kind: "file",
|
|
122
|
+
file: (0, harness_descriptor_loader_paths_1.resolvePromptFilePath)(value.file, context, fileLabel),
|
|
123
|
+
...(where ? { where } : {}),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
if (hasRef) {
|
|
127
|
+
const refLabel = `${label}.ref`;
|
|
128
|
+
const ref = readNonEmptyString(value.ref, refLabel);
|
|
129
|
+
return {
|
|
130
|
+
kind: "ref",
|
|
131
|
+
text: resolveInstructionPartReference(descriptor, ref, refLabel),
|
|
132
|
+
...(where ? { where } : {}),
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
return {
|
|
136
|
+
kind: "text",
|
|
137
|
+
text: readString(value.text, `${label}.text`),
|
|
138
|
+
...(where ? { where } : {}),
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
function readInstructionWhere(value, label) {
|
|
142
|
+
if (typeof value === "undefined") {
|
|
143
|
+
return undefined;
|
|
144
|
+
}
|
|
145
|
+
if (!isPlainObject(value)) {
|
|
146
|
+
throw new Error(`${label}.where must be an object.`);
|
|
147
|
+
}
|
|
148
|
+
return {
|
|
149
|
+
context: readNonEmptyString(value.context, `${label}.where.context`),
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
function readString(value, label) {
|
|
153
|
+
if (typeof value !== "string") {
|
|
154
|
+
throw new Error(`${label} must be a string.`);
|
|
155
|
+
}
|
|
156
|
+
return value;
|
|
157
|
+
}
|
|
158
|
+
function readNonEmptyString(value, label) {
|
|
159
|
+
const text = readString(value, label);
|
|
160
|
+
if (text.trim().length === 0) {
|
|
161
|
+
throw new Error(`${label} must be a non-empty string.`);
|
|
162
|
+
}
|
|
163
|
+
return text;
|
|
164
|
+
}
|
|
165
|
+
function resolveInstructionPartReference(descriptor, ref, label) {
|
|
166
|
+
const catalog = descriptor.instruction_parts;
|
|
167
|
+
if (!isPlainObject(catalog)) {
|
|
168
|
+
throw new Error(`${label} references instruction part "${ref}", but descriptor.instruction_parts is not defined.`);
|
|
169
|
+
}
|
|
170
|
+
if (!(ref in catalog)) {
|
|
171
|
+
throw new Error(`${label} references unknown instruction part "${ref}".`);
|
|
172
|
+
}
|
|
173
|
+
return readString(catalog[ref], `descriptor.instruction_parts.${ref}`);
|
|
174
|
+
}
|
|
62
175
|
function readInstructionFilesInput(value, label) {
|
|
63
176
|
const hasInstructionFile = hasDefined(value, "instructions_file");
|
|
64
177
|
const hasInstructionFiles = hasDefined(value, "instructions_files");
|
|
@@ -68,6 +181,7 @@ function readInstructionFilesInput(value, label) {
|
|
|
68
181
|
assertInstructionFileFields(value, label, {
|
|
69
182
|
hasInstructionFile,
|
|
70
183
|
hasInstructionFiles,
|
|
184
|
+
hasInstructionSequence: hasDefined(value, "instructions_sequence"),
|
|
71
185
|
});
|
|
72
186
|
if (hasInstructionFile) {
|
|
73
187
|
const fileLabel = `${label}.instructions_file`;
|
|
@@ -92,13 +206,16 @@ function hasDefined(value, key) {
|
|
|
92
206
|
return key in value && typeof value[key] !== "undefined";
|
|
93
207
|
}
|
|
94
208
|
function assertInstructionFileFields(value, label, fields) {
|
|
95
|
-
const { hasInstructionFile, hasInstructionFiles } = fields;
|
|
209
|
+
const { hasInstructionFile, hasInstructionFiles, hasInstructionSequence } = fields;
|
|
96
210
|
if (hasInstructionFile && hasInstructionFiles) {
|
|
97
211
|
throw new Error(`${label}.instructions_file and ${label}.instructions_files are mutually exclusive.`);
|
|
98
212
|
}
|
|
99
213
|
if (hasInstructionFile && typeof value.instructions !== "undefined") {
|
|
100
214
|
throw new Error(`${label}.instructions and ${label}.instructions_file are mutually exclusive.`);
|
|
101
215
|
}
|
|
216
|
+
if (hasInstructionSequence && (hasInstructionFile || hasInstructionFiles)) {
|
|
217
|
+
throw new Error(`${label}.instructions_sequence is mutually exclusive with ${label}.instructions_file/instructions_files.`);
|
|
218
|
+
}
|
|
102
219
|
if (hasInstructionFiles &&
|
|
103
220
|
typeof value.instructions !== "undefined" &&
|
|
104
221
|
typeof value.instructions !== "string") {
|
|
@@ -117,6 +234,21 @@ function materializeInstructionReference(reference, contents) {
|
|
|
117
234
|
delete reference.target.instructions_file;
|
|
118
235
|
delete reference.target.instructions_files;
|
|
119
236
|
}
|
|
237
|
+
function sequenceRequiresPromptFiles(reference) {
|
|
238
|
+
return reference.items.some((item) => item.kind === "file");
|
|
239
|
+
}
|
|
240
|
+
function materializeInstructionSequenceReference(reference, contents) {
|
|
241
|
+
let fileContentIndex = 0;
|
|
242
|
+
const parts = reference.items.map((item) => {
|
|
243
|
+
const text = item.kind === "file" ? contents[fileContentIndex++] : item.text;
|
|
244
|
+
return {
|
|
245
|
+
text: text ?? "",
|
|
246
|
+
...(item.where ? { where: item.where } : {}),
|
|
247
|
+
};
|
|
248
|
+
});
|
|
249
|
+
reference.target.instructions = parts;
|
|
250
|
+
delete reference.target.instructions_sequence;
|
|
251
|
+
}
|
|
120
252
|
function readPromptMapContents(reference, promptMap) {
|
|
121
253
|
return reference.files.map((file) => {
|
|
122
254
|
const content = promptMap.get(file.resolvedPath);
|
|
@@ -126,6 +258,17 @@ function readPromptMapContents(reference, promptMap) {
|
|
|
126
258
|
return content;
|
|
127
259
|
});
|
|
128
260
|
}
|
|
261
|
+
function readPromptMapSequenceContents(reference, promptMap) {
|
|
262
|
+
return reference.items
|
|
263
|
+
.filter((item) => item.kind === "file")
|
|
264
|
+
.map((item) => {
|
|
265
|
+
const content = promptMap.get(item.file.resolvedPath);
|
|
266
|
+
if (typeof content === "undefined") {
|
|
267
|
+
throw new Error(`Missing promptMap entry for ${reference.label} prompt file "${item.file.rawPath}".`);
|
|
268
|
+
}
|
|
269
|
+
return content;
|
|
270
|
+
});
|
|
271
|
+
}
|
|
129
272
|
function loadAgentHarnessDescriptor(yaml, optionsOrPromptMap) {
|
|
130
273
|
const options = normalizeOptions(optionsOrPromptMap);
|
|
131
274
|
const context = (0, harness_descriptor_loader_paths_1.createPathContext)(options);
|
|
@@ -134,15 +277,23 @@ function loadAgentHarnessDescriptor(yaml, optionsOrPromptMap) {
|
|
|
134
277
|
: undefined;
|
|
135
278
|
const descriptor = parseDescriptorYaml(yaml, context.descriptorLabel);
|
|
136
279
|
const references = collectInstructionFileReferences(descriptor, context);
|
|
137
|
-
|
|
138
|
-
|
|
280
|
+
const sequenceReferences = collectInstructionSequenceReferences(descriptor, context);
|
|
281
|
+
const hasPromptFileReferences = references.length > 0 ||
|
|
282
|
+
sequenceReferences.some((reference) => sequenceRequiresPromptFiles(reference));
|
|
283
|
+
if (hasPromptFileReferences && !promptMap) {
|
|
284
|
+
throw new Error("loadAgentHarnessDescriptor(...) requires promptMap when the descriptor uses instructions_file, instructions_files, or instructions_sequence file items. Use loadAgentHarnessDescriptorFromFile(...) to read prompt files from disk.");
|
|
139
285
|
}
|
|
140
|
-
if (
|
|
141
|
-
|
|
286
|
+
if (promptMap) {
|
|
287
|
+
for (const reference of references) {
|
|
288
|
+
const contents = readPromptMapContents(reference, promptMap);
|
|
289
|
+
materializeInstructionReference(reference, contents);
|
|
290
|
+
}
|
|
142
291
|
}
|
|
143
|
-
for (const reference of
|
|
144
|
-
const contents =
|
|
145
|
-
|
|
292
|
+
for (const reference of sequenceReferences) {
|
|
293
|
+
const contents = promptMap
|
|
294
|
+
? readPromptMapSequenceContents(reference, promptMap)
|
|
295
|
+
: [];
|
|
296
|
+
materializeInstructionSequenceReference(reference, contents);
|
|
146
297
|
}
|
|
147
298
|
return descriptor;
|
|
148
299
|
}
|
|
@@ -150,9 +301,19 @@ async function loadAgentHarnessDescriptorFromFile(descriptorPath, options = {})
|
|
|
150
301
|
const loaded = await (0, harness_descriptor_loader_paths_1.loadDescriptorFile)(descriptorPath, options);
|
|
151
302
|
const descriptor = parseDescriptorYaml(loaded.yaml, loaded.descriptorLabel);
|
|
152
303
|
const references = collectInstructionFileReferences(descriptor, loaded.context);
|
|
304
|
+
const sequenceReferences = collectInstructionSequenceReferences(descriptor, loaded.context);
|
|
153
305
|
for (const reference of references) {
|
|
154
306
|
const contents = await (0, harness_descriptor_loader_paths_1.readResolvedPromptFiles)(reference.files, reference.label, loaded.rootRealPath);
|
|
155
307
|
materializeInstructionReference(reference, contents);
|
|
156
308
|
}
|
|
309
|
+
for (const reference of sequenceReferences) {
|
|
310
|
+
const files = reference.items
|
|
311
|
+
.filter((item) => item.kind === "file")
|
|
312
|
+
.map((item) => item.file);
|
|
313
|
+
const contents = files.length > 0
|
|
314
|
+
? await (0, harness_descriptor_loader_paths_1.readResolvedPromptFiles)(files, reference.label, loaded.rootRealPath)
|
|
315
|
+
: [];
|
|
316
|
+
materializeInstructionSequenceReference(reference, contents);
|
|
317
|
+
}
|
|
157
318
|
return descriptor;
|
|
158
319
|
}
|
|
@@ -16,12 +16,20 @@ export interface HarnessToolDescriptor {
|
|
|
16
16
|
export interface HarnessAgentDescriptor {
|
|
17
17
|
name?: string;
|
|
18
18
|
handoffDescription?: string;
|
|
19
|
-
instructions?:
|
|
19
|
+
instructions?: HarnessInstructionsDescriptor;
|
|
20
20
|
model?: string;
|
|
21
21
|
modelSettings?: Record<string, unknown>;
|
|
22
22
|
tools?: string[];
|
|
23
23
|
handoffs?: string[];
|
|
24
24
|
}
|
|
25
|
+
export interface HarnessInstructionWhereDescriptor {
|
|
26
|
+
context: string;
|
|
27
|
+
}
|
|
28
|
+
export interface HarnessInstructionPartDescriptor {
|
|
29
|
+
text: string;
|
|
30
|
+
where?: HarnessInstructionWhereDescriptor;
|
|
31
|
+
}
|
|
32
|
+
export type HarnessInstructionsDescriptor = string | HarnessInstructionPartDescriptor[];
|
|
25
33
|
export interface HarnessContextReferenceDescriptor {
|
|
26
34
|
type?: string;
|
|
27
35
|
optional?: boolean;
|
|
@@ -45,6 +53,7 @@ export interface AgentHarnessDescriptor {
|
|
|
45
53
|
metadata?: HarnessDescriptorMetadata;
|
|
46
54
|
runtime: HarnessRuntimeDescriptor;
|
|
47
55
|
context?: HarnessContextDescriptor;
|
|
56
|
+
instruction_parts?: Record<string, string>;
|
|
48
57
|
tools?: Record<string, HarnessToolDescriptor>;
|
|
49
58
|
agent_defaults?: Pick<HarnessAgentDescriptor, "model" | "modelSettings" | "instructions">;
|
|
50
59
|
agents: Record<string, HarnessAgentDescriptor>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"harness-descriptor.d.ts","sourceRoot":"","sources":["../src/harness-descriptor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAA0B,MAAM,SAAS,CAAC;AAExD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AACnC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAEnD,MAAM,WAAW,yBAAyB;IACxC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,wBAAwB;IACvC,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,sBAAsB;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,YAAY,CAAC,EAAE,
|
|
1
|
+
{"version":3,"file":"harness-descriptor.d.ts","sourceRoot":"","sources":["../src/harness-descriptor.ts"],"names":[],"mappings":"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"}
|
|
@@ -40,6 +40,7 @@ function validateDescriptorShape(descriptor) {
|
|
|
40
40
|
}
|
|
41
41
|
assertOptionalPlainObject(descriptor.metadata, "Harness descriptor metadata");
|
|
42
42
|
assertOptionalPlainObject(descriptor.tools, "Harness descriptor tools");
|
|
43
|
+
assertOptionalPlainObject(descriptor.instruction_parts, "Harness descriptor instruction_parts");
|
|
43
44
|
assertOptionalPlainObject(descriptor.agent_defaults, "Harness descriptor agent_defaults");
|
|
44
45
|
assertOptionalPlainObject(descriptor.context, "Harness descriptor context");
|
|
45
46
|
assertOptionalPlainObject(descriptor.context?.fields, "Harness descriptor context.fields");
|
|
@@ -56,10 +57,12 @@ function validateDescriptorShape(descriptor) {
|
|
|
56
57
|
}
|
|
57
58
|
function assertNoUnresolvedInstructionFile(value, label) {
|
|
58
59
|
if (!isPlainObject(value) ||
|
|
59
|
-
(!("instructions_file" in value) &&
|
|
60
|
+
(!("instructions_file" in value) &&
|
|
61
|
+
!("instructions_files" in value) &&
|
|
62
|
+
!("instructions_sequence" in value))) {
|
|
60
63
|
return;
|
|
61
64
|
}
|
|
62
|
-
throw new Error(`${label}.instructions_file/instructions_files must be materialized by loadAgentHarnessDescriptor(...) before buildAgentHarness(...).`);
|
|
65
|
+
throw new Error(`${label}.instructions_file/instructions_files/instructions_sequence must be materialized by loadAgentHarnessDescriptor(...) before buildAgentHarness(...).`);
|
|
63
66
|
}
|
|
64
67
|
function normalizePromptPath(path, label) {
|
|
65
68
|
const segments = path
|
|
@@ -90,7 +93,10 @@ function createPromptReferenceRules(descriptor) {
|
|
|
90
93
|
if (!isPlainObject(reference)) {
|
|
91
94
|
throw new Error(`Context reference "${path}" must be true, false, or an object.`);
|
|
92
95
|
}
|
|
93
|
-
rules.set(normalizedPath, {
|
|
96
|
+
rules.set(normalizedPath, {
|
|
97
|
+
optional: Boolean(reference.optional),
|
|
98
|
+
type: typeof reference.type === "string" ? reference.type : undefined,
|
|
99
|
+
});
|
|
94
100
|
}
|
|
95
101
|
return rules;
|
|
96
102
|
}
|
|
@@ -147,19 +153,105 @@ function renderInstructionTemplate(template, context, promptAccessRules) {
|
|
|
147
153
|
return stringifyPromptValue(resolved.value);
|
|
148
154
|
});
|
|
149
155
|
}
|
|
156
|
+
function readWhereContextValue(context, path) {
|
|
157
|
+
return readPromptPath(context, path);
|
|
158
|
+
}
|
|
159
|
+
function assertInstructionWhere(where, label) {
|
|
160
|
+
if (!isPlainObject(where)) {
|
|
161
|
+
throw new Error(`${label}.where must be an object.`);
|
|
162
|
+
}
|
|
163
|
+
assertNonEmptyString(where.context, `${label}.where.context`);
|
|
164
|
+
}
|
|
165
|
+
function assertInstructionPart(part, label) {
|
|
166
|
+
if (!isPlainObject(part)) {
|
|
167
|
+
throw new Error(`${label} must be an object.`);
|
|
168
|
+
}
|
|
169
|
+
if (typeof part.text !== "string") {
|
|
170
|
+
throw new Error(`${label}.text must be a string.`);
|
|
171
|
+
}
|
|
172
|
+
if (typeof part.where !== "undefined") {
|
|
173
|
+
assertInstructionWhere(part.where, label);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
function assertInstructionParts(agentId, instructions) {
|
|
177
|
+
instructions.forEach((part, index) => assertInstructionPart(part, `Agent "${agentId}" instructions[${index}]`));
|
|
178
|
+
}
|
|
179
|
+
function collectInstructionPartPromptPaths(instructions) {
|
|
180
|
+
const paths = new Set();
|
|
181
|
+
for (const part of instructions) {
|
|
182
|
+
for (const path of collectContextPromptPaths(part.text)) {
|
|
183
|
+
paths.add(path);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return [...paths];
|
|
187
|
+
}
|
|
188
|
+
function collectInstructionWherePaths(instructions) {
|
|
189
|
+
const paths = new Set();
|
|
190
|
+
for (const part of instructions) {
|
|
191
|
+
if (!part.where) {
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
paths.add(normalizePromptPath(part.where.context, "Instruction where.context path"));
|
|
195
|
+
}
|
|
196
|
+
return [...paths];
|
|
197
|
+
}
|
|
198
|
+
function assertDeclaredPromptPaths(agentId, paths, promptAccessRules) {
|
|
199
|
+
for (const path of paths) {
|
|
200
|
+
if (!promptAccessRules.has(path)) {
|
|
201
|
+
throw new Error(`Agent "${agentId}" instruction references undeclared context path "${path}".`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
function assertDeclaredBooleanWherePaths(agentId, paths, promptAccessRules) {
|
|
206
|
+
for (const path of paths) {
|
|
207
|
+
const rule = promptAccessRules.get(path);
|
|
208
|
+
if (!rule) {
|
|
209
|
+
throw new Error(`Agent "${agentId}" instruction where references undeclared context path "${path}".`);
|
|
210
|
+
}
|
|
211
|
+
if (rule.type !== "boolean") {
|
|
212
|
+
throw new Error(`Agent "${agentId}" instruction where context path "${path}" must be declared with type "boolean".`);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
function shouldIncludeInstructionPart(part, context) {
|
|
217
|
+
if (!part.where) {
|
|
218
|
+
return true;
|
|
219
|
+
}
|
|
220
|
+
const path = normalizePromptPath(part.where.context, "Instruction where.context path");
|
|
221
|
+
const resolved = readWhereContextValue(context, path);
|
|
222
|
+
if (!resolved.exists || typeof resolved.value === "undefined") {
|
|
223
|
+
throw new Error(`Harness descriptor instruction where could not resolve context path "${path}".`);
|
|
224
|
+
}
|
|
225
|
+
if (typeof resolved.value !== "boolean") {
|
|
226
|
+
throw new Error(`Harness descriptor instruction where context path "${path}" must resolve to a boolean.`);
|
|
227
|
+
}
|
|
228
|
+
return resolved.value === true;
|
|
229
|
+
}
|
|
230
|
+
function renderInstructionParts(instructions, context, promptAccessRules) {
|
|
231
|
+
const parts = [];
|
|
232
|
+
for (const part of instructions) {
|
|
233
|
+
if (!shouldIncludeInstructionPart(part, context)) {
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
parts.push(renderInstructionTemplate(part.text, context, promptAccessRules));
|
|
237
|
+
}
|
|
238
|
+
return parts.join("\n\n");
|
|
239
|
+
}
|
|
150
240
|
function compileAgentInstructions(agentId, instructions, promptAccessRules) {
|
|
151
241
|
if (typeof instructions === "undefined") {
|
|
152
242
|
return undefined;
|
|
153
243
|
}
|
|
244
|
+
if (Array.isArray(instructions)) {
|
|
245
|
+
assertInstructionParts(agentId, instructions);
|
|
246
|
+
assertDeclaredPromptPaths(agentId, collectInstructionPartPromptPaths(instructions), promptAccessRules);
|
|
247
|
+
assertDeclaredBooleanWherePaths(agentId, collectInstructionWherePaths(instructions), promptAccessRules);
|
|
248
|
+
return (runContext) => renderInstructionParts(instructions, runContext.context, promptAccessRules);
|
|
249
|
+
}
|
|
154
250
|
const promptPaths = collectContextPromptPaths(instructions);
|
|
155
251
|
if (promptPaths.length === 0) {
|
|
156
252
|
return instructions;
|
|
157
253
|
}
|
|
158
|
-
|
|
159
|
-
if (!promptAccessRules.has(path)) {
|
|
160
|
-
throw new Error(`Agent "${agentId}" instruction references undeclared context path "${path}".`);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
254
|
+
assertDeclaredPromptPaths(agentId, promptPaths, promptAccessRules);
|
|
163
255
|
return (runContext) => renderInstructionTemplate(instructions, runContext.context, promptAccessRules);
|
|
164
256
|
}
|
|
165
257
|
function setPath(target, path, value) {
|