@ariaflowagents/config 0.5.3 → 0.8.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.
Files changed (56) hide show
  1. package/README.md +93 -5
  2. package/dist/agent-architect/index.d.ts +10 -0
  3. package/dist/agent-architect/index.d.ts.map +1 -0
  4. package/dist/agent-architect/index.js +131 -0
  5. package/dist/agent-architect/index.js.map +1 -0
  6. package/dist/agent-architect/prompt-generator.d.ts +9 -0
  7. package/dist/agent-architect/prompt-generator.d.ts.map +1 -0
  8. package/dist/agent-architect/prompt-generator.js +155 -0
  9. package/dist/agent-architect/prompt-generator.js.map +1 -0
  10. package/dist/agent-architect/rfc-emitter.d.ts +18 -0
  11. package/dist/agent-architect/rfc-emitter.d.ts.map +1 -0
  12. package/dist/agent-architect/rfc-emitter.js +350 -0
  13. package/dist/agent-architect/rfc-emitter.js.map +1 -0
  14. package/dist/agent-architect/tool-recommender.d.ts +8 -0
  15. package/dist/agent-architect/tool-recommender.d.ts.map +1 -0
  16. package/dist/agent-architect/tool-recommender.js +87 -0
  17. package/dist/agent-architect/tool-recommender.js.map +1 -0
  18. package/dist/agent-architect/types.d.ts +904 -0
  19. package/dist/agent-architect/types.d.ts.map +1 -0
  20. package/dist/agent-architect/types.js +89 -0
  21. package/dist/agent-architect/types.js.map +1 -0
  22. package/dist/agent-architect/use-case-analyzer.d.ts +8 -0
  23. package/dist/agent-architect/use-case-analyzer.d.ts.map +1 -0
  24. package/dist/agent-architect/use-case-analyzer.js +76 -0
  25. package/dist/agent-architect/use-case-analyzer.js.map +1 -0
  26. package/dist/cli.d.ts +3 -0
  27. package/dist/cli.d.ts.map +1 -1
  28. package/dist/cli.js +1226 -3
  29. package/dist/cli.js.map +1 -1
  30. package/dist/flow-codegen.d.ts +6 -0
  31. package/dist/flow-codegen.d.ts.map +1 -0
  32. package/dist/flow-codegen.js +31 -0
  33. package/dist/flow-codegen.js.map +1 -0
  34. package/dist/flow-generator.d.ts +17 -0
  35. package/dist/flow-generator.d.ts.map +1 -0
  36. package/dist/flow-generator.js +202 -0
  37. package/dist/flow-generator.js.map +1 -0
  38. package/dist/index.d.ts +9 -1
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +4 -0
  41. package/dist/index.js.map +1 -1
  42. package/dist/llm-flow-generator.d.ts +10 -0
  43. package/dist/llm-flow-generator.d.ts.map +1 -0
  44. package/dist/llm-flow-generator.js +244 -0
  45. package/dist/llm-flow-generator.js.map +1 -0
  46. package/dist/loader.d.ts +1 -1
  47. package/dist/loader.d.ts.map +1 -1
  48. package/dist/loader.js +175 -45
  49. package/dist/loader.js.map +1 -1
  50. package/dist/prompt-lint.d.ts +18 -0
  51. package/dist/prompt-lint.d.ts.map +1 -0
  52. package/dist/prompt-lint.js +180 -0
  53. package/dist/prompt-lint.js.map +1 -0
  54. package/dist/types.d.ts +19 -3
  55. package/dist/types.d.ts.map +1 -1
  56. package/package.json +12 -11
@@ -0,0 +1,244 @@
1
+ import { generateObject } from 'ai';
2
+ import { openai } from '@ai-sdk/openai';
3
+ import { z } from 'zod';
4
+ import { generateFlowTemplate } from './flow-generator.js';
5
+ const fullPromptBlocksSchema = z.object({
6
+ globalPrompt: z
7
+ .string()
8
+ .min(1)
9
+ .describe('Global role prompt with sections for overall goal, language/style rules, tool+transition rules, and reliability rules.'),
10
+ mainAgendaPrompt: z
11
+ .string()
12
+ .min(1)
13
+ .describe('Main agenda prompt with concrete stage tasks, required data, relevant questions, and strict transition success criteria.'),
14
+ });
15
+ const taskBlockSchema = z.object({
16
+ title: z.string().min(1).max(120),
17
+ details: z.array(z.string().min(1).max(280)).min(1).max(6),
18
+ questions: z.array(z.string().min(1).max(280)).min(1).max(6),
19
+ finalActions: z.array(z.string().min(1).max(280)).min(1).max(6),
20
+ });
21
+ const hybridDraftSchema = z.object({
22
+ goalDescription: z.string().min(1).max(420),
23
+ useCases: z.array(z.string().min(1).max(280)).min(1).max(8),
24
+ fallbackOptions: z.array(z.string().min(1).max(280)).min(1).max(6),
25
+ taskBlocks: z.array(taskBlockSchema).min(1).max(6),
26
+ summaryMoveCriteria: z.array(z.string().min(1).max(280)).min(1).max(8),
27
+ });
28
+ function buildFullLlmPrompt(options) {
29
+ const contextSection = options.contextText && options.contextText.trim().length > 0
30
+ ? `\n## Additional Context\n${options.contextText.trim()}\n`
31
+ : '';
32
+ return `Generate production-grade flow prompts for a voice-first guided agent.
33
+
34
+ ## Use Case
35
+ - agent name: ${options.agentName}
36
+ - direction: ${options.direction}
37
+ - use case: ${options.useCase}
38
+ ${contextSection}
39
+ ## Output Requirements
40
+ - Return ONLY structured fields via the schema.
41
+ - globalPrompt must include:
42
+ - OVERALL GOAL section
43
+ - response language/style rules
44
+ - tool/function transition discipline
45
+ - reliability / non-hallucination rules
46
+ - mainAgendaPrompt must include:
47
+ - "MAIN ACTION POINT AT THIS STEP"
48
+ - 3-5 task blocks
49
+ - each task block has details/data + relevant questions + final actions
50
+ - explicit success criteria for moving to summary stage
51
+ - Make questions concrete and domain-specific, not generic filler.
52
+ - Enforce deterministic behavior: one question at a time, no mixing text+tool transition output.
53
+ - Keep wording clear for spoken conversations.
54
+ - Do not include markdown code fences.`;
55
+ }
56
+ function buildHybridLlmPrompt(options) {
57
+ const contextSection = options.contextText && options.contextText.trim().length > 0
58
+ ? `\n## Additional Context\n${options.contextText.trim()}\n`
59
+ : '';
60
+ return `Create domain-specific variable blocks for a voice flow.
61
+ Do NOT output full prompts. Return only fields in schema.
62
+
63
+ ## Use Case
64
+ - agent name: ${options.agentName}
65
+ - direction: ${options.direction}
66
+ - use case: ${options.useCase}
67
+ ${contextSection}
68
+ ## Constraints
69
+ - Keep content specific and realistic for the use case.
70
+ - Avoid generic filler.
71
+ - Questions should be voice-friendly and one-at-a-time.
72
+ - Include only facts/steps that can be asked or verified in conversation.
73
+ - No markdown code fences.`;
74
+ }
75
+ function composeHybridGlobalPrompt(options, draft) {
76
+ const useCases = draft.useCases.map(item => item.trim()).filter(Boolean);
77
+ const fallbacks = draft.fallbackOptions.map(item => item.trim()).filter(Boolean);
78
+ const effectiveUseCases = useCases.length > 0 ? useCases : ['addressing user requests and collecting required details'];
79
+ const effectiveFallbacks = fallbacks.length > 0 ? fallbacks : ['providing a concise summary and a follow-up option'];
80
+ const goal = draft.goalDescription.trim().length > 0
81
+ ? draft.goalDescription.trim()
82
+ : 'handling user requests accurately and moving toward clear resolution';
83
+ const useCaseLines = effectiveUseCases.map(item => `- ${item}`).join('\n');
84
+ const fallbackLines = effectiveFallbacks.map(item => `- ${item}`).join('\n');
85
+ return `# OVERALL GOAL
86
+
87
+ You are ${options.agentName}. You are handling ${options.direction} calls for this use case.
88
+ Primary goal: ${goal}
89
+
90
+ Support these common scenarios:
91
+ ${useCaseLines}
92
+
93
+ If the caller is hesitant, confused, or does not wish to continue, offer alternatives such as:
94
+ ${fallbackLines}
95
+
96
+ ## Response Language
97
+ YOU MUST only REPLY IN the language of the latest user input. Use simple words, keep tone informal but professional, and keep answers concise for voice.
98
+
99
+ ## Critical Rules
100
+ - If the last message is not a user message, do not make a function/tool call.
101
+ - Never mix text and tool calls in the same output.
102
+ - Ask one focused question at a time and wait for the user response.
103
+ - If a transition is required, end the turn with the correct tool call or a question, never both.
104
+
105
+ ## Reliability Rules
106
+ - Do not invent facts. If uncertain, ask a clarifying question or use a verification tool.
107
+ - Keep collected details consistent across turns and avoid contradictions.
108
+ - Prefer deterministic next actions over vague responses.`;
109
+ }
110
+ function composeHybridMainAgendaPrompt(draft) {
111
+ const blocks = draft.taskBlocks
112
+ .map((block, index) => {
113
+ const detailsList = block.details.map(item => item.trim()).filter(Boolean);
114
+ const questionList = block.questions.map(item => item.trim()).filter(Boolean);
115
+ const actionList = block.finalActions.map(item => item.trim()).filter(Boolean);
116
+ const details = (detailsList.length > 0 ? detailsList : ['Collect the required data for this step.'])
117
+ .map(item => ` - ${item}`).join('\n');
118
+ const questions = (questionList.length > 0 ? questionList : ['What is the key detail needed to proceed?'])
119
+ .map(item => ` - ${item}`).join('\n');
120
+ const actions = (actionList.length > 0 ? actionList : ['Summarize captured inputs and proceed deterministically.'])
121
+ .map(item => ` - ${item}`).join('\n');
122
+ const title = block.title.trim().length > 0 ? block.title.trim() : `Step ${index + 1}`;
123
+ return `### Task ${index + 1}: ${title}
124
+ Details and Data:
125
+ ${details}
126
+ Relevant Questions:
127
+ ${questions}
128
+ Final Step and Related Actions:
129
+ ${actions}`;
130
+ })
131
+ .join('\n\n');
132
+ const successCriteriaList = draft.summaryMoveCriteria
133
+ .map(item => item.trim())
134
+ .filter(Boolean);
135
+ const successCriteria = (successCriteriaList.length > 0
136
+ ? successCriteriaList
137
+ : ['Required details are collected and validated', 'A clear next action is identified'])
138
+ .map(item => `- ${item}`)
139
+ .join('\n');
140
+ return `# MAIN ACTION POINT AT THIS STEP
141
+ ## Usable details and Main Agenda
142
+
143
+ ## USABLE DETAILS and GOALS AT THIS STAGE:
144
+ ${blocks}
145
+
146
+ ## Flow of call
147
+ Here’s how the flow should work:
148
+ - Ask one question at a time.
149
+ - Handle objections briefly, then continue the agenda.
150
+ - Do not loop or repeat already-confirmed questions.
151
+ - Use transition tools/events to move stages.
152
+
153
+ # Success criteria WHEN you can move to next step
154
+ ${successCriteria}
155
+ - Transition to Summary using the designated transition tool/event only.`;
156
+ }
157
+ function runWithTimeout(promise, timeoutMs, timeoutMessage) {
158
+ let timeoutHandle;
159
+ const timeout = new Promise((_, reject) => {
160
+ timeoutHandle = setTimeout(() => reject(new Error(timeoutMessage)), timeoutMs);
161
+ });
162
+ return Promise.race([promise, timeout]).finally(() => {
163
+ if (timeoutHandle) {
164
+ clearTimeout(timeoutHandle);
165
+ }
166
+ });
167
+ }
168
+ async function generateObjectStrict({ model, schema, system, prompt, }) {
169
+ const result = await generateObject({
170
+ model,
171
+ schema,
172
+ system,
173
+ prompt,
174
+ });
175
+ return result.object;
176
+ }
177
+ function resolveModelSpec(modelInput) {
178
+ const requested = modelInput?.trim() || 'gpt-5-mini';
179
+ const modelId = requested.startsWith('openai:') ? requested.slice('openai:'.length).trim() : requested;
180
+ if (!modelId) {
181
+ throw new Error('Model id cannot be empty');
182
+ }
183
+ if (!process.env.OPENAI_API_KEY || process.env.OPENAI_API_KEY.trim().length === 0) {
184
+ throw new Error('OPENAI_API_KEY is required for LLM flow generation');
185
+ }
186
+ return {
187
+ modelId,
188
+ label: `openai:${modelId}`,
189
+ model: openai(modelId),
190
+ };
191
+ }
192
+ export async function generateFlowTemplateWithLLM(options) {
193
+ const useCase = options.useCase.trim();
194
+ const direction = options.direction ?? 'inbound';
195
+ const agentName = options.agentName?.trim() || 'Sam';
196
+ const modelSpec = resolveModelSpec(options.model);
197
+ const timeoutMs = options.timeoutMs ?? 45000;
198
+ const llmMode = options.llmMode ?? 'hybrid';
199
+ if (llmMode === 'full') {
200
+ const generate = generateObjectStrict({
201
+ model: modelSpec.model,
202
+ schema: fullPromptBlocksSchema,
203
+ system: 'You are an expert prompt engineer for deterministic conversational flow agents. Produce concise, robust prompts only.',
204
+ prompt: buildFullLlmPrompt({
205
+ useCase,
206
+ direction,
207
+ agentName,
208
+ contextText: options.contextText,
209
+ }),
210
+ });
211
+ const object = await runWithTimeout(generate, timeoutMs, `LLM flow generation timed out after ${timeoutMs}ms (${modelSpec.label})`);
212
+ return generateFlowTemplate({
213
+ ...options,
214
+ promptBlocks: {
215
+ ...options.promptBlocks,
216
+ globalPrompt: object.globalPrompt.trim(),
217
+ mainAgendaPrompt: object.mainAgendaPrompt.trim(),
218
+ },
219
+ });
220
+ }
221
+ let draft;
222
+ draft = await runWithTimeout(generateObjectStrict({
223
+ model: modelSpec.model,
224
+ schema: hybridDraftSchema,
225
+ system: 'You generate domain-specific variable blocks for conversational flow prompts. Keep content concrete and deterministic.',
226
+ prompt: buildHybridLlmPrompt({
227
+ useCase,
228
+ direction,
229
+ agentName,
230
+ contextText: options.contextText,
231
+ }),
232
+ }), timeoutMs, `LLM flow generation timed out after ${timeoutMs}ms (${modelSpec.label})`);
233
+ const globalPrompt = composeHybridGlobalPrompt({ direction, agentName }, draft);
234
+ const mainAgendaPrompt = composeHybridMainAgendaPrompt(draft);
235
+ return generateFlowTemplate({
236
+ ...options,
237
+ promptBlocks: {
238
+ ...options.promptBlocks,
239
+ globalPrompt,
240
+ mainAgendaPrompt,
241
+ },
242
+ });
243
+ }
244
+ //# sourceMappingURL=llm-flow-generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"llm-flow-generator.js","sourceRoot":"","sources":["../src/llm-flow-generator.ts"],"names":[],"mappings":"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"}
package/dist/loader.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { type HarnessConfig, type Runtime } from '@ariaflowagents/core';
2
2
  import type { LoadedConfig, LoadOptions } from './types.js';
3
- type WarningType = 'invalid_frontmatter' | 'missing_prompt' | 'missing_template' | 'name_mismatch' | 'missing_ref' | 'invalid_tool_entry' | 'duplicate_agent' | 'duplicate_flow' | 'unresolved_flow_ref' | 'tool_not_found';
3
+ type WarningType = 'invalid_frontmatter' | 'invalid_flow' | 'missing_prompt' | 'missing_template' | 'name_mismatch' | 'missing_ref' | 'invalid_tool_entry' | 'duplicate_agent' | 'duplicate_flow' | 'unresolved_flow_ref' | 'tool_not_found';
4
4
  interface ConfigWarning {
5
5
  type: WarningType;
6
6
  file: string;
@@ -1 +1 @@
1
- {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"AAWA,OAAO,EAUL,KAAK,aAAa,EAElB,KAAK,OAAO,EAGb,MAAM,sBAAsB,CAAC;AAI9B,OAAO,KAAK,EAKV,YAAY,EACZ,WAAW,EAMZ,MAAM,YAAY,CAAC;AAqDpB,KAAK,WAAW,GACZ,qBAAqB,GACrB,gBAAgB,GAChB,kBAAkB,GAClB,eAAe,GACf,aAAa,GACb,oBAAoB,GACpB,iBAAiB,GACjB,gBAAgB,GAChB,qBAAqB,GACrB,gBAAgB,CAAC;AAErB,UAAU,aAAa;IACrB,IAAI,EAAE,WAAW,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;CAC5B;AAmgCD,wBAAsB,kBAAkB,CAAC,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,YAAY,CAAC,CA+QzF;AAED,wBAAsB,4BAA4B,CAAC,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC;IACrF,MAAM,EAAE,YAAY,CAAC;IACrB,OAAO,EAAE;QACP,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,EAAE,CAAC;QACjB,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,EAAE,MAAM,CAAC;QACrB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,QAAQ,EAAE,aAAa,EAAE,CAAC;CAC3B,CAAC,CAoSD;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,4BAA4B,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,CAanH;AA6FD,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,YAAY,EACpB,SAAS,GAAE,OAAO,CAAC,IAAI,CAAC,aAAa,EAAE,QAAQ,GAAG,gBAAgB,GAAG,cAAc,CAAC,CAAM,GACzF,OAAO,CAYT"}
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"AAWA,OAAO,EAUL,KAAK,aAAa,EAElB,KAAK,OAAO,EAGb,MAAM,sBAAsB,CAAC;AAI9B,OAAO,KAAK,EAKV,YAAY,EACZ,WAAW,EAMZ,MAAM,YAAY,CAAC;AAqDpB,KAAK,WAAW,GACZ,qBAAqB,GACrB,cAAc,GACd,gBAAgB,GAChB,kBAAkB,GAClB,eAAe,GACf,aAAa,GACb,oBAAoB,GACpB,iBAAiB,GACjB,gBAAgB,GAChB,qBAAqB,GACrB,gBAAgB,CAAC;AAErB,UAAU,aAAa;IACrB,IAAI,EAAE,WAAW,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;CAC5B;AA8rCD,wBAAsB,kBAAkB,CAAC,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,YAAY,CAAC,CAuQzF;AAED,wBAAsB,4BAA4B,CAAC,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC;IACrF,MAAM,EAAE,YAAY,CAAC;IACrB,OAAO,EAAE;QACP,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,EAAE,CAAC;QACjB,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,EAAE,MAAM,CAAC;QACrB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,QAAQ,EAAE,aAAa,EAAE,CAAC;CAC3B,CAAC,CA4RD;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,4BAA4B,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,CAanH;AA+FD,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,YAAY,EACpB,SAAS,GAAE,OAAO,CAAC,IAAI,CAAC,aAAa,EAAE,QAAQ,GAAG,gBAAgB,GAAG,cAAc,CAAC,CAAM,GACzF,OAAO,CAYT"}
package/dist/loader.js CHANGED
@@ -108,6 +108,160 @@ function deepMerge(base, override) {
108
108
  }
109
109
  return result;
110
110
  }
111
+ const VALID_FLOW_NODE_TYPES = new Set(['start', 'agent', 'end', 'global']);
112
+ function isRecord(value) {
113
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
114
+ }
115
+ function normalizeAndValidateFlowInput(flowId, flowInput, sourcePath) {
116
+ const errors = [];
117
+ if (!Array.isArray(flowInput.nodes) || flowInput.nodes.length === 0) {
118
+ errors.push('must define at least one node');
119
+ }
120
+ const normalizedNodes = [];
121
+ const nodeIds = new Set();
122
+ let startNodeCount = 0;
123
+ let globalNodeCount = 0;
124
+ let detectedStartNodeId;
125
+ for (let i = 0; i < (Array.isArray(flowInput.nodes) ? flowInput.nodes.length : 0); i++) {
126
+ const rawNode = flowInput.nodes[i];
127
+ if (!isRecord(rawNode)) {
128
+ errors.push(`node[${i}] must be an object`);
129
+ continue;
130
+ }
131
+ const node = rawNode;
132
+ const nodeId = typeof node.id === 'string' ? node.id.trim() : '';
133
+ if (!nodeId) {
134
+ errors.push(`node[${i}] is missing a valid "id"`);
135
+ continue;
136
+ }
137
+ if (nodeIds.has(nodeId)) {
138
+ errors.push(`duplicate node id "${nodeId}"`);
139
+ continue;
140
+ }
141
+ nodeIds.add(nodeId);
142
+ if (typeof node.prompt !== 'string' || node.prompt.trim().length === 0) {
143
+ errors.push(`node "${nodeId}" is missing a non-empty "prompt"`);
144
+ }
145
+ const nodeType = typeof node.nodeType === 'string' ? node.nodeType : undefined;
146
+ if (nodeType && !VALID_FLOW_NODE_TYPES.has(nodeType)) {
147
+ errors.push(`node "${nodeId}" has invalid nodeType "${nodeType}" (allowed: start, agent, end, global)`);
148
+ }
149
+ if (nodeType === 'start') {
150
+ startNodeCount += 1;
151
+ detectedStartNodeId = detectedStartNodeId ?? nodeId;
152
+ }
153
+ if (nodeType === 'global') {
154
+ globalNodeCount += 1;
155
+ }
156
+ normalizedNodes.push({
157
+ ...node,
158
+ id: nodeId,
159
+ addGlobalPrompt: node.addGlobalPrompt !== false,
160
+ ...(nodeType
161
+ ? { nodeType: nodeType }
162
+ : {}),
163
+ });
164
+ }
165
+ if (startNodeCount > 1) {
166
+ errors.push('must have at most one node with nodeType="start"');
167
+ }
168
+ if (globalNodeCount > 1) {
169
+ errors.push('must have at most one node with nodeType="global"');
170
+ }
171
+ const normalizedTransitions = [];
172
+ if (flowInput.transitions !== undefined && !Array.isArray(flowInput.transitions)) {
173
+ errors.push('"transitions" must be an array when provided');
174
+ }
175
+ if (Array.isArray(flowInput.transitions)) {
176
+ for (let i = 0; i < flowInput.transitions.length; i++) {
177
+ const rawTransition = flowInput.transitions[i];
178
+ if (!isRecord(rawTransition)) {
179
+ errors.push(`transition[${i}] must be an object`);
180
+ continue;
181
+ }
182
+ const transition = rawTransition;
183
+ const from = typeof transition.from === 'string' ? transition.from.trim() : '';
184
+ const to = typeof transition.to === 'string' ? transition.to.trim() : '';
185
+ if (!from || !to) {
186
+ errors.push(`transition[${i}] must include non-empty "from" and "to"`);
187
+ continue;
188
+ }
189
+ if (!nodeIds.has(from)) {
190
+ errors.push(`transition[${i}] references unknown "from" node "${from}"`);
191
+ }
192
+ if (!nodeIds.has(to)) {
193
+ errors.push(`transition[${i}] references unknown "to" node "${to}"`);
194
+ }
195
+ const on = typeof transition.on === 'string' ? transition.on.trim() : transition.on;
196
+ if (transition.on !== undefined && (!on || typeof on !== 'string')) {
197
+ errors.push(`transition[${i}] has invalid "on" value`);
198
+ }
199
+ const hasConditionFn = typeof transition.condition === 'function';
200
+ if (transition.condition !== undefined && !hasConditionFn) {
201
+ errors.push(`transition[${i}] has non-function "condition"`);
202
+ }
203
+ if (!on && !hasConditionFn) {
204
+ errors.push(`transition[${i}] must define either "on" or "condition"`);
205
+ }
206
+ let contract;
207
+ if (transition.contract !== undefined) {
208
+ if (!isRecord(transition.contract)) {
209
+ errors.push(`transition[${i}] has invalid "contract" (must be an object)`);
210
+ }
211
+ else {
212
+ const rawContract = transition.contract;
213
+ contract = {
214
+ label: typeof rawContract.label === 'string' ? rawContract.label : undefined,
215
+ conditionText: typeof rawContract.conditionText === 'string'
216
+ ? rawContract.conditionText
217
+ : undefined,
218
+ toolOnly: typeof rawContract.toolOnly === 'boolean' ? rawContract.toolOnly : undefined,
219
+ requiresUserTurn: typeof rawContract.requiresUserTurn === 'boolean'
220
+ ? rawContract.requiresUserTurn
221
+ : undefined,
222
+ };
223
+ }
224
+ }
225
+ normalizedTransitions.push({
226
+ ...transition,
227
+ from,
228
+ to,
229
+ ...(typeof on === 'string' ? { on } : {}),
230
+ ...(contract ? { contract } : {}),
231
+ });
232
+ }
233
+ }
234
+ const explicitInitialNode = typeof flowInput.initialNode === 'string' && flowInput.initialNode.trim().length > 0
235
+ ? flowInput.initialNode.trim()
236
+ : typeof flowInput.entry === 'string' && flowInput.entry.trim().length > 0
237
+ ? flowInput.entry.trim()
238
+ : undefined;
239
+ const initialNode = explicitInitialNode ?? detectedStartNodeId ?? normalizedNodes[0]?.id;
240
+ if (!initialNode) {
241
+ errors.push('could not resolve initial node');
242
+ }
243
+ else if (!nodeIds.has(initialNode)) {
244
+ errors.push(`initial node "${initialNode}" is not present in nodes`);
245
+ }
246
+ if (errors.length > 0) {
247
+ const formatted = errors.map(err => `- ${err}`).join('\n');
248
+ warn('invalid_flow', sourcePath, `Flow "${flowId}" is invalid:\n${formatted}`, 'error');
249
+ throw new Error(`Flow "${flowId}" is invalid in ${sourcePath}:\n${formatted}`);
250
+ }
251
+ const flow = {
252
+ nodes: normalizedNodes,
253
+ transitions: normalizedTransitions.length > 0 ? normalizedTransitions : undefined,
254
+ defaultRolePrompt: flowInput.defaultRolePrompt,
255
+ contextStrategy: flowInput.contextStrategy,
256
+ summaryPrompt: flowInput.summaryPrompt,
257
+ summaryMaxTokens: flowInput.summaryMaxTokens,
258
+ maxSteps: flowInput.maxSteps,
259
+ ...(typeof flowInput.summaryTimeoutMs === 'number'
260
+ ? { summaryTimeoutMs: flowInput.summaryTimeoutMs }
261
+ : {}),
262
+ };
263
+ return { flow, initialNode: initialNode };
264
+ }
111
265
  function resolveConfigFile(target, baseDir) {
112
266
  const resolved = path.isAbsolute(target) ? target : path.resolve(baseDir, target);
113
267
  if (existsSync(resolved) && !resolved.endsWith('.json') && !resolved.endsWith('.jsonc')) {
@@ -444,17 +598,11 @@ async function loadFlowFiles(root, sources) {
444
598
  const content = await readFile(file, 'utf8');
445
599
  const data = parseJsoncContent(content, file);
446
600
  const id = data.id ?? path.basename(file, path.extname(file));
447
- const { id: _id, entry, initialNode, ...rest } = data;
448
- const flowConfig = {
449
- nodes: rest.nodes,
450
- transitions: rest.transitions,
451
- defaultRolePrompt: rest.defaultRolePrompt,
452
- contextStrategy: rest.contextStrategy,
453
- };
454
- flows[id] = flowConfig;
601
+ const normalized = normalizeAndValidateFlowInput(id, data, file);
602
+ flows[id] = normalized.flow;
455
603
  meta[id] = {
456
604
  id,
457
- initialNode: initialNode ?? entry,
605
+ initialNode: normalized.initialNode,
458
606
  path: file,
459
607
  };
460
608
  sources.flowFiles.push(file);
@@ -965,13 +1113,9 @@ export async function loadAriaflowConfig(options = {}) {
965
1113
  continue;
966
1114
  }
967
1115
  for (const [id, flowInput] of Object.entries(layer.config.flows)) {
968
- flows[id] = {
969
- nodes: flowInput.nodes,
970
- transitions: flowInput.transitions,
971
- defaultRolePrompt: flowInput.defaultRolePrompt,
972
- contextStrategy: flowInput.contextStrategy,
973
- };
974
- flowMeta[id] = { id, initialNode: flowInput.initialNode ?? flowInput.entry, path: layer.path };
1116
+ const normalized = normalizeAndValidateFlowInput(id, flowInput, layer.path);
1117
+ flows[id] = normalized.flow;
1118
+ flowMeta[id] = { id, initialNode: normalized.initialNode, path: layer.path };
975
1119
  }
976
1120
  }
977
1121
  for (const root of roots) {
@@ -981,13 +1125,9 @@ export async function loadAriaflowConfig(options = {}) {
981
1125
  }
982
1126
  if (inlineLayer?.config.flows) {
983
1127
  for (const [id, flowInput] of Object.entries(inlineLayer.config.flows)) {
984
- flows[id] = {
985
- nodes: flowInput.nodes,
986
- transitions: flowInput.transitions,
987
- defaultRolePrompt: flowInput.defaultRolePrompt,
988
- contextStrategy: flowInput.contextStrategy,
989
- };
990
- flowMeta[id] = { id, initialNode: flowInput.initialNode ?? flowInput.entry, path: inlineLayer.path };
1128
+ const normalized = normalizeAndValidateFlowInput(id, flowInput, inlineLayer.path);
1129
+ flows[id] = normalized.flow;
1130
+ flowMeta[id] = { id, initialNode: normalized.initialNode, path: inlineLayer.path };
991
1131
  }
992
1132
  }
993
1133
  const agentDefs = {};
@@ -1176,13 +1316,9 @@ export async function loadAriaflowConfigWithResult(options = {}) {
1176
1316
  if (!layer.config.flows)
1177
1317
  continue;
1178
1318
  for (const [id, flowInput] of Object.entries(layer.config.flows)) {
1179
- flows[id] = {
1180
- nodes: flowInput.nodes,
1181
- transitions: flowInput.transitions,
1182
- defaultRolePrompt: flowInput.defaultRolePrompt,
1183
- contextStrategy: flowInput.contextStrategy,
1184
- };
1185
- flowMeta[id] = { id, initialNode: flowInput.initialNode ?? flowInput.entry, path: layer.path };
1319
+ const normalized = normalizeAndValidateFlowInput(id, flowInput, layer.path);
1320
+ flows[id] = normalized.flow;
1321
+ flowMeta[id] = { id, initialNode: normalized.initialNode, path: layer.path };
1186
1322
  }
1187
1323
  }
1188
1324
  for (const root of roots) {
@@ -1192,13 +1328,9 @@ export async function loadAriaflowConfigWithResult(options = {}) {
1192
1328
  }
1193
1329
  if (inlineLayer?.config.flows) {
1194
1330
  for (const [id, flowInput] of Object.entries(inlineLayer.config.flows)) {
1195
- flows[id] = {
1196
- nodes: flowInput.nodes,
1197
- transitions: flowInput.transitions,
1198
- defaultRolePrompt: flowInput.defaultRolePrompt,
1199
- contextStrategy: flowInput.contextStrategy,
1200
- };
1201
- flowMeta[id] = { id, initialNode: flowInput.initialNode ?? flowInput.entry, path: inlineLayer.path };
1331
+ const normalized = normalizeAndValidateFlowInput(id, flowInput, inlineLayer.path);
1332
+ flows[id] = normalized.flow;
1333
+ flowMeta[id] = { id, initialNode: normalized.initialNode, path: inlineLayer.path };
1202
1334
  }
1203
1335
  }
1204
1336
  const agentDefs = {};
@@ -1396,13 +1528,11 @@ async function normalizeAgentInput(id, input, baseDir, flows, flowMeta, toolRegi
1396
1528
  ? flowInput.mode
1397
1529
  : undefined;
1398
1530
  const flowId = flowInput.flowRef ?? flowInput.flow?.id;
1399
- const flowConfig = flowInput.flow
1400
- ? {
1401
- nodes: flowInput.flow.nodes,
1402
- transitions: flowInput.flow.transitions,
1403
- defaultRolePrompt: flowInput.flow.defaultRolePrompt,
1404
- contextStrategy: flowInput.flow.contextStrategy,
1405
- }
1531
+ const inlineFlowNormalized = flowInput.flow
1532
+ ? normalizeAndValidateFlowInput(flowId ?? `${id}-inline-flow`, flowInput.flow, `${baseDir}#agent:${id}`)
1533
+ : null;
1534
+ const flowConfig = inlineFlowNormalized
1535
+ ? inlineFlowNormalized.flow
1406
1536
  : flowId
1407
1537
  ? flows[flowId]
1408
1538
  : undefined;
@@ -1410,7 +1540,7 @@ async function normalizeAgentInput(id, input, baseDir, flows, flowMeta, toolRegi
1410
1540
  throw new Error(`Flow agent "${id}" references missing flow`);
1411
1541
  }
1412
1542
  const initialNode = flowInput.initialNode ??
1413
- flowInput.flow?.entry ??
1543
+ inlineFlowNormalized?.initialNode ??
1414
1544
  flowMeta[flowId ?? '']?.initialNode ??
1415
1545
  flowConfig.nodes?.[0]?.id;
1416
1546
  if (!initialNode) {