@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.1`.
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 an experimental Agent Harness Descriptor surface. The core runtime remains compatibility-managed; descriptor shape and loader helpers may still evolve across `0.x` minor releases with explicit migration guidance.
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
- ## Experimental 0.2.x Surface
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` line as an experimental `aioc.agent_graph.v0` descriptor surface. Use it for controlled configuration, examples, and evaluation harnesses; keep executable tools, policies, providers, persistence, approvals, and deployment configuration in application code.
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` (`Experimental`)
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,EAEvB,MAAM,sBAAsB,CAAC;AAa9B,MAAM,MAAM,qBAAqB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAuC3D,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;AA+ND,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,MAAM,EACZ,kBAAkB,CAAC,EACf,iCAAiC,GACjC,qBAAqB,GACxB,sBAAsB,CAyBxB;AAED,wBAAsB,kCAAkC,CACtD,cAAc,EAAE,MAAM,EACtB,OAAO,GAAE,yCAA8C,GACtD,OAAO,CAAC,sBAAsB,CAAC,CAkBjC"}
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
- if (references.length > 0 && !promptMap) {
138
- throw new Error("loadAgentHarnessDescriptor(...) requires promptMap when the descriptor uses instructions_file or instructions_files. Use loadAgentHarnessDescriptorFromFile(...) to read prompt files from disk.");
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 (!promptMap) {
141
- return descriptor;
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 references) {
144
- const contents = readPromptMapContents(reference, promptMap);
145
- materializeInstructionReference(reference, contents);
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?: string;
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":"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"}
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) && !("instructions_files" 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, { optional: Boolean(reference.optional) });
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
- for (const path of promptPaths) {
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) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axiastudio/aioc",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "files": [