@renseiai/agentfactory 0.8.7 → 0.8.8

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 (119) hide show
  1. package/dist/src/config/repository-config.d.ts +14 -0
  2. package/dist/src/config/repository-config.d.ts.map +1 -1
  3. package/dist/src/config/repository-config.js +20 -0
  4. package/dist/src/governor/event-types.d.ts +18 -1
  5. package/dist/src/governor/event-types.d.ts.map +1 -1
  6. package/dist/src/governor/event-types.js +4 -0
  7. package/dist/src/merge-queue/adapters/github-native.d.ts +22 -0
  8. package/dist/src/merge-queue/adapters/github-native.d.ts.map +1 -0
  9. package/dist/src/merge-queue/adapters/github-native.js +243 -0
  10. package/dist/src/merge-queue/adapters/github-native.test.d.ts +2 -0
  11. package/dist/src/merge-queue/adapters/github-native.test.d.ts.map +1 -0
  12. package/dist/src/merge-queue/adapters/github-native.test.js +384 -0
  13. package/dist/src/merge-queue/index.d.ts +18 -0
  14. package/dist/src/merge-queue/index.d.ts.map +1 -0
  15. package/dist/src/merge-queue/index.js +28 -0
  16. package/dist/src/merge-queue/merge-queue.integration.test.d.ts +2 -0
  17. package/dist/src/merge-queue/merge-queue.integration.test.d.ts.map +1 -0
  18. package/dist/src/merge-queue/merge-queue.integration.test.js +128 -0
  19. package/dist/src/merge-queue/types.d.ts +48 -0
  20. package/dist/src/merge-queue/types.d.ts.map +1 -0
  21. package/dist/src/merge-queue/types.js +8 -0
  22. package/dist/src/orchestrator/artifact-tracker.d.ts +93 -0
  23. package/dist/src/orchestrator/artifact-tracker.d.ts.map +1 -0
  24. package/dist/src/orchestrator/artifact-tracker.js +235 -0
  25. package/dist/src/orchestrator/artifact-tracker.test.d.ts +2 -0
  26. package/dist/src/orchestrator/artifact-tracker.test.d.ts.map +1 -0
  27. package/dist/src/orchestrator/artifact-tracker.test.js +189 -0
  28. package/dist/src/orchestrator/context-manager.d.ts +72 -0
  29. package/dist/src/orchestrator/context-manager.d.ts.map +1 -0
  30. package/dist/src/orchestrator/context-manager.js +120 -0
  31. package/dist/src/orchestrator/context-manager.test.d.ts +2 -0
  32. package/dist/src/orchestrator/context-manager.test.d.ts.map +1 -0
  33. package/dist/src/orchestrator/context-manager.test.js +137 -0
  34. package/dist/src/orchestrator/index.d.ts +8 -2
  35. package/dist/src/orchestrator/index.d.ts.map +1 -1
  36. package/dist/src/orchestrator/index.js +8 -1
  37. package/dist/src/orchestrator/orchestrator.d.ts +12 -0
  38. package/dist/src/orchestrator/orchestrator.d.ts.map +1 -1
  39. package/dist/src/orchestrator/orchestrator.js +258 -2
  40. package/dist/src/orchestrator/state-recovery.d.ts +21 -2
  41. package/dist/src/orchestrator/state-recovery.d.ts.map +1 -1
  42. package/dist/src/orchestrator/state-recovery.js +54 -2
  43. package/dist/src/orchestrator/state-recovery.test.js +106 -2
  44. package/dist/src/orchestrator/state-types.d.ts +62 -0
  45. package/dist/src/orchestrator/state-types.d.ts.map +1 -1
  46. package/dist/src/orchestrator/state-types.js +5 -1
  47. package/dist/src/orchestrator/summary-builder.d.ts +47 -0
  48. package/dist/src/orchestrator/summary-builder.d.ts.map +1 -0
  49. package/dist/src/orchestrator/summary-builder.js +240 -0
  50. package/dist/src/orchestrator/summary-builder.test.d.ts +2 -0
  51. package/dist/src/orchestrator/summary-builder.test.d.ts.map +1 -0
  52. package/dist/src/orchestrator/summary-builder.test.js +236 -0
  53. package/dist/src/orchestrator/types.d.ts +2 -0
  54. package/dist/src/orchestrator/types.d.ts.map +1 -1
  55. package/dist/src/orchestrator/work-types.d.ts +1 -1
  56. package/dist/src/orchestrator/work-types.d.ts.map +1 -1
  57. package/dist/src/templates/registry.test.js +2 -2
  58. package/dist/src/templates/types.d.ts +2 -0
  59. package/dist/src/templates/types.d.ts.map +1 -1
  60. package/dist/src/templates/types.js +1 -0
  61. package/dist/src/workflow/branching-router.d.ts +38 -0
  62. package/dist/src/workflow/branching-router.d.ts.map +1 -0
  63. package/dist/src/workflow/branching-router.js +52 -0
  64. package/dist/src/workflow/branching-router.test.d.ts +2 -0
  65. package/dist/src/workflow/branching-router.test.d.ts.map +1 -0
  66. package/dist/src/workflow/branching-router.test.js +209 -0
  67. package/dist/src/workflow/duration.d.ts +28 -0
  68. package/dist/src/workflow/duration.d.ts.map +1 -0
  69. package/dist/src/workflow/duration.js +57 -0
  70. package/dist/src/workflow/duration.test.d.ts +2 -0
  71. package/dist/src/workflow/duration.test.d.ts.map +1 -0
  72. package/dist/src/workflow/duration.test.js +74 -0
  73. package/dist/src/workflow/expression/ast.d.ts +53 -0
  74. package/dist/src/workflow/expression/ast.d.ts.map +1 -0
  75. package/dist/src/workflow/expression/ast.js +8 -0
  76. package/dist/src/workflow/expression/context.d.ts +40 -0
  77. package/dist/src/workflow/expression/context.d.ts.map +1 -0
  78. package/dist/src/workflow/expression/context.js +37 -0
  79. package/dist/src/workflow/expression/evaluator.d.ts +28 -0
  80. package/dist/src/workflow/expression/evaluator.d.ts.map +1 -0
  81. package/dist/src/workflow/expression/evaluator.js +165 -0
  82. package/dist/src/workflow/expression/evaluator.test.d.ts +2 -0
  83. package/dist/src/workflow/expression/evaluator.test.d.ts.map +1 -0
  84. package/dist/src/workflow/expression/evaluator.test.js +792 -0
  85. package/dist/src/workflow/expression/expression.test.d.ts +2 -0
  86. package/dist/src/workflow/expression/expression.test.d.ts.map +1 -0
  87. package/dist/src/workflow/expression/expression.test.js +516 -0
  88. package/dist/src/workflow/expression/helpers.d.ts +21 -0
  89. package/dist/src/workflow/expression/helpers.d.ts.map +1 -0
  90. package/dist/src/workflow/expression/helpers.js +56 -0
  91. package/dist/src/workflow/expression/index.d.ts +55 -0
  92. package/dist/src/workflow/expression/index.d.ts.map +1 -0
  93. package/dist/src/workflow/expression/index.js +71 -0
  94. package/dist/src/workflow/expression/lexer.d.ts +37 -0
  95. package/dist/src/workflow/expression/lexer.d.ts.map +1 -0
  96. package/dist/src/workflow/expression/lexer.js +166 -0
  97. package/dist/src/workflow/expression/parser.d.ts +23 -0
  98. package/dist/src/workflow/expression/parser.d.ts.map +1 -0
  99. package/dist/src/workflow/expression/parser.js +181 -0
  100. package/dist/src/workflow/index.d.ts +10 -3
  101. package/dist/src/workflow/index.d.ts.map +1 -1
  102. package/dist/src/workflow/index.js +6 -1
  103. package/dist/src/workflow/retry-resolver.d.ts +51 -0
  104. package/dist/src/workflow/retry-resolver.d.ts.map +1 -0
  105. package/dist/src/workflow/retry-resolver.js +70 -0
  106. package/dist/src/workflow/retry-resolver.test.d.ts +2 -0
  107. package/dist/src/workflow/retry-resolver.test.d.ts.map +1 -0
  108. package/dist/src/workflow/retry-resolver.test.js +149 -0
  109. package/dist/src/workflow/transition-engine.d.ts +3 -1
  110. package/dist/src/workflow/transition-engine.d.ts.map +1 -1
  111. package/dist/src/workflow/transition-engine.js +14 -7
  112. package/dist/src/workflow/transition-engine.test.js +123 -11
  113. package/dist/src/workflow/workflow-registry.d.ts +41 -0
  114. package/dist/src/workflow/workflow-registry.d.ts.map +1 -1
  115. package/dist/src/workflow/workflow-registry.js +66 -0
  116. package/dist/src/workflow/workflow-types.d.ts +181 -8
  117. package/dist/src/workflow/workflow-types.d.ts.map +1 -1
  118. package/dist/src/workflow/workflow-types.js +31 -6
  119. package/package.json +2 -2
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Expression Parser & Evaluator — Public API
3
+ *
4
+ * Parses Handlebars-style condition strings from WorkflowDefinition
5
+ * transitions into a typed AST, and evaluates them within a sandboxed
6
+ * context.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * import { parseExpression, evaluateCondition, buildEvaluationContext } from './expression/index.js'
11
+ *
12
+ * const ast = parseExpression("{{ hasLabel('bug') and priority gt 3 }}")
13
+ * // => BinaryOp(AND, FunctionCall("hasLabel", [StringLiteral("bug")]), BinaryOp(GT, ...))
14
+ *
15
+ * const ctx = buildEvaluationContext(issue, phaseState)
16
+ * const result = evaluateCondition("{{ hasLabel('bug') }}", ctx)
17
+ * // => true or false
18
+ * ```
19
+ */
20
+ import { tokenize } from './lexer.js';
21
+ import { parse } from './parser.js';
22
+ import { evaluate } from './evaluator.js';
23
+ export { ParseError, tokenize } from './lexer.js';
24
+ // ---------------------------------------------------------------------------
25
+ // Re-exports — Parser
26
+ // ---------------------------------------------------------------------------
27
+ export { parse } from './parser.js';
28
+ // ---------------------------------------------------------------------------
29
+ // Re-exports — Evaluator
30
+ // ---------------------------------------------------------------------------
31
+ export { evaluate, EvaluationError } from './evaluator.js';
32
+ export { buildEvaluationContext } from './context.js';
33
+ // ---------------------------------------------------------------------------
34
+ // Re-exports — Helpers
35
+ // ---------------------------------------------------------------------------
36
+ export { createBuiltinHelpers } from './helpers.js';
37
+ // ---------------------------------------------------------------------------
38
+ // Convenience entry points
39
+ // ---------------------------------------------------------------------------
40
+ /**
41
+ * Parse a condition string into an AST in a single call.
42
+ *
43
+ * Combines tokenization and parsing. Accepts strings with or without
44
+ * `{{ }}` delimiters.
45
+ *
46
+ * @param condition - The condition string to parse.
47
+ * @returns The root AST node.
48
+ * @throws {ParseError} on invalid input with position information.
49
+ */
50
+ export function parseExpression(condition) {
51
+ const tokens = tokenize(condition);
52
+ return parse(tokens);
53
+ }
54
+ /**
55
+ * Parse and evaluate a condition string, returning a boolean result.
56
+ *
57
+ * This is the primary entry point for workflow transition evaluation.
58
+ * It combines parsing and evaluation in one call, and coerces the
59
+ * result to a boolean.
60
+ *
61
+ * @param condition - The condition string (with or without `{{ }}`).
62
+ * @param context - The sandboxed evaluation context.
63
+ * @returns `true` if the condition is satisfied, `false` otherwise.
64
+ * @throws {ParseError} on syntax errors.
65
+ * @throws {EvaluationError} on runtime errors (unknown functions, type mismatches).
66
+ */
67
+ export function evaluateCondition(condition, context) {
68
+ const ast = parseExpression(condition);
69
+ const result = evaluate(ast, context);
70
+ return Boolean(result);
71
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Expression Lexer (Tokenizer)
3
+ *
4
+ * Converts a condition string (with optional `{{ }}` delimiters) into a
5
+ * flat token stream. Each token carries source-position info for error
6
+ * reporting downstream.
7
+ */
8
+ export type TokenType = 'Identifier' | 'BooleanLiteral' | 'StringLiteral' | 'NumberLiteral' | 'Operator' | 'LeftParen' | 'RightParen' | 'Comma' | 'EOF';
9
+ /** Source position within the *inner* expression (after delimiter stripping). */
10
+ export interface SourcePosition {
11
+ /** 0-based character offset */
12
+ readonly offset: number;
13
+ /** 1-based column (same as offset + 1 since expressions are single-line) */
14
+ readonly column: number;
15
+ }
16
+ export interface Token {
17
+ readonly type: TokenType;
18
+ readonly value: string;
19
+ readonly position: SourcePosition;
20
+ }
21
+ /**
22
+ * Error thrown when the lexer or parser encounters invalid input.
23
+ * Carries source-position information for precise error messages.
24
+ */
25
+ export declare class ParseError extends Error {
26
+ readonly position: SourcePosition;
27
+ constructor(message: string, position: SourcePosition);
28
+ }
29
+ /**
30
+ * Tokenize a condition string into a flat token array.
31
+ *
32
+ * @param input - The condition string, optionally wrapped in `{{ }}`.
33
+ * @returns An array of tokens ending with an `EOF` token.
34
+ * @throws {ParseError} on unexpected characters or unterminated strings.
35
+ */
36
+ export declare function tokenize(input: string): Token[];
37
+ //# sourceMappingURL=lexer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lexer.d.ts","sourceRoot":"","sources":["../../../../src/workflow/expression/lexer.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,MAAM,MAAM,SAAS,GACjB,YAAY,GACZ,gBAAgB,GAChB,eAAe,GACf,eAAe,GACf,UAAU,GACV,WAAW,GACX,YAAY,GACZ,OAAO,GACP,KAAK,CAAA;AAET,iFAAiF;AACjF,MAAM,WAAW,cAAc;IAC7B,+BAA+B;IAC/B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,4EAA4E;IAC5E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAA;CAClC;AAMD;;;GAGG;AACH,qBAAa,UAAW,SAAQ,KAAK;IACnC,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAA;gBAErB,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc;CAKtD;AAsED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,EAAE,CAsF/C"}
@@ -0,0 +1,166 @@
1
+ /**
2
+ * Expression Lexer (Tokenizer)
3
+ *
4
+ * Converts a condition string (with optional `{{ }}` delimiters) into a
5
+ * flat token stream. Each token carries source-position info for error
6
+ * reporting downstream.
7
+ */
8
+ // ---------------------------------------------------------------------------
9
+ // ParseError
10
+ // ---------------------------------------------------------------------------
11
+ /**
12
+ * Error thrown when the lexer or parser encounters invalid input.
13
+ * Carries source-position information for precise error messages.
14
+ */
15
+ export class ParseError extends Error {
16
+ position;
17
+ constructor(message, position) {
18
+ super(`${message} at column ${position.column}`);
19
+ this.name = 'ParseError';
20
+ this.position = position;
21
+ }
22
+ }
23
+ // ---------------------------------------------------------------------------
24
+ // Keyword sets
25
+ // ---------------------------------------------------------------------------
26
+ const OPERATORS = new Set([
27
+ 'and', 'or', 'not',
28
+ 'eq', 'neq', 'gt', 'lt', 'gte', 'lte',
29
+ ]);
30
+ const BOOLEAN_LITERALS = new Set(['true', 'false']);
31
+ // ---------------------------------------------------------------------------
32
+ // Helpers
33
+ // ---------------------------------------------------------------------------
34
+ function isWhitespace(ch) {
35
+ return ch === ' ' || ch === '\t' || ch === '\n' || ch === '\r';
36
+ }
37
+ function isDigit(ch) {
38
+ return ch >= '0' && ch <= '9';
39
+ }
40
+ function isIdentStart(ch) {
41
+ return (ch >= 'a' && ch <= 'z') ||
42
+ (ch >= 'A' && ch <= 'Z') ||
43
+ ch === '_';
44
+ }
45
+ function isIdentChar(ch) {
46
+ return isIdentStart(ch) || isDigit(ch);
47
+ }
48
+ function pos(offset) {
49
+ return { offset, column: offset + 1 };
50
+ }
51
+ // ---------------------------------------------------------------------------
52
+ // Strip delimiters
53
+ // ---------------------------------------------------------------------------
54
+ /**
55
+ * Remove the `{{ }}` Handlebars-style delimiters from a condition string.
56
+ * Returns the inner content and the offset of the inner content within
57
+ * the original string (so positions map back correctly).
58
+ */
59
+ function stripDelimiters(input) {
60
+ const trimmed = input.trim();
61
+ if (trimmed.startsWith('{{') && trimmed.endsWith('}}')) {
62
+ const openIdx = input.indexOf('{{');
63
+ // +2 to skip past the opening {{
64
+ const innerStart = openIdx + 2;
65
+ const closeIdx = input.lastIndexOf('}}');
66
+ return {
67
+ inner: input.slice(innerStart, closeIdx),
68
+ offset: innerStart,
69
+ };
70
+ }
71
+ // No delimiters — treat as raw expression
72
+ return { inner: input, offset: 0 };
73
+ }
74
+ // ---------------------------------------------------------------------------
75
+ // Tokenize
76
+ // ---------------------------------------------------------------------------
77
+ /**
78
+ * Tokenize a condition string into a flat token array.
79
+ *
80
+ * @param input - The condition string, optionally wrapped in `{{ }}`.
81
+ * @returns An array of tokens ending with an `EOF` token.
82
+ * @throws {ParseError} on unexpected characters or unterminated strings.
83
+ */
84
+ export function tokenize(input) {
85
+ const { inner } = stripDelimiters(input);
86
+ const tokens = [];
87
+ let i = 0;
88
+ while (i < inner.length) {
89
+ const ch = inner[i];
90
+ // Skip whitespace
91
+ if (isWhitespace(ch)) {
92
+ i++;
93
+ continue;
94
+ }
95
+ // Single-quoted string literal
96
+ if (ch === "'") {
97
+ const start = i;
98
+ i++; // skip opening quote
99
+ let value = '';
100
+ while (i < inner.length && inner[i] !== "'") {
101
+ value += inner[i];
102
+ i++;
103
+ }
104
+ if (i >= inner.length) {
105
+ throw new ParseError('Unterminated string literal', pos(start));
106
+ }
107
+ i++; // skip closing quote
108
+ tokens.push({ type: 'StringLiteral', value, position: pos(start) });
109
+ continue;
110
+ }
111
+ // Number literal
112
+ if (isDigit(ch) || (ch === '-' && i + 1 < inner.length && isDigit(inner[i + 1]))) {
113
+ const start = i;
114
+ if (ch === '-')
115
+ i++; // consume negative sign
116
+ while (i < inner.length && isDigit(inner[i]))
117
+ i++;
118
+ // Decimal part
119
+ if (i < inner.length && inner[i] === '.' && i + 1 < inner.length && isDigit(inner[i + 1])) {
120
+ i++; // skip dot
121
+ while (i < inner.length && isDigit(inner[i]))
122
+ i++;
123
+ }
124
+ tokens.push({ type: 'NumberLiteral', value: inner.slice(start, i), position: pos(start) });
125
+ continue;
126
+ }
127
+ // Identifiers, keywords, operators, boolean literals
128
+ if (isIdentStart(ch)) {
129
+ const start = i;
130
+ while (i < inner.length && isIdentChar(inner[i]))
131
+ i++;
132
+ const word = inner.slice(start, i);
133
+ if (BOOLEAN_LITERALS.has(word)) {
134
+ tokens.push({ type: 'BooleanLiteral', value: word, position: pos(start) });
135
+ }
136
+ else if (OPERATORS.has(word)) {
137
+ tokens.push({ type: 'Operator', value: word, position: pos(start) });
138
+ }
139
+ else {
140
+ tokens.push({ type: 'Identifier', value: word, position: pos(start) });
141
+ }
142
+ continue;
143
+ }
144
+ // Parentheses
145
+ if (ch === '(') {
146
+ tokens.push({ type: 'LeftParen', value: '(', position: pos(i) });
147
+ i++;
148
+ continue;
149
+ }
150
+ if (ch === ')') {
151
+ tokens.push({ type: 'RightParen', value: ')', position: pos(i) });
152
+ i++;
153
+ continue;
154
+ }
155
+ // Comma
156
+ if (ch === ',') {
157
+ tokens.push({ type: 'Comma', value: ',', position: pos(i) });
158
+ i++;
159
+ continue;
160
+ }
161
+ // Unknown character
162
+ throw new ParseError(`Unexpected character '${ch}'`, pos(i));
163
+ }
164
+ tokens.push({ type: 'EOF', value: '', position: pos(i) });
165
+ return tokens;
166
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Expression Parser (Recursive Descent)
3
+ *
4
+ * Parses a token stream (produced by the lexer) into a typed AST.
5
+ *
6
+ * Operator precedence (lowest to highest):
7
+ * 1. `or`
8
+ * 2. `and`
9
+ * 3. comparisons: `eq`, `neq`, `gt`, `lt`, `gte`, `lte`
10
+ * 4. `not` (unary prefix)
11
+ * 5. primary: literals, variable refs, function calls, parenthesized exprs
12
+ */
13
+ import type { ASTNode } from './ast.js';
14
+ import type { Token } from './lexer.js';
15
+ /**
16
+ * Parse a token stream into an AST.
17
+ *
18
+ * @param tokens - Token array produced by `tokenize()`.
19
+ * @returns The root AST node.
20
+ * @throws {ParseError} on syntax errors.
21
+ */
22
+ export declare function parse(tokens: Token[]): ASTNode;
23
+ //# sourceMappingURL=parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../../../src/workflow/expression/parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAA;AACvC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAyLvC;;;;;;GAMG;AACH,wBAAgB,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAkB9C"}
@@ -0,0 +1,181 @@
1
+ /**
2
+ * Expression Parser (Recursive Descent)
3
+ *
4
+ * Parses a token stream (produced by the lexer) into a typed AST.
5
+ *
6
+ * Operator precedence (lowest to highest):
7
+ * 1. `or`
8
+ * 2. `and`
9
+ * 3. comparisons: `eq`, `neq`, `gt`, `lt`, `gte`, `lte`
10
+ * 4. `not` (unary prefix)
11
+ * 5. primary: literals, variable refs, function calls, parenthesized exprs
12
+ */
13
+ import { ParseError } from './lexer.js';
14
+ // ---------------------------------------------------------------------------
15
+ // Operator sets
16
+ // ---------------------------------------------------------------------------
17
+ const COMPARISON_OPERATORS = new Set(['eq', 'neq', 'gt', 'lt', 'gte', 'lte']);
18
+ function peek(state) {
19
+ return state.tokens[state.pos];
20
+ }
21
+ function advance(state) {
22
+ const token = state.tokens[state.pos];
23
+ state.pos++;
24
+ return token;
25
+ }
26
+ function expect(state, type, value) {
27
+ const token = peek(state);
28
+ if (token.type !== type || (value !== undefined && token.value !== value)) {
29
+ const expected = value ? `'${value}'` : type;
30
+ const got = token.type === 'EOF' ? 'end of expression' : `'${token.value}'`;
31
+ throw new ParseError(`Expected ${expected} but got ${got}`, token.position);
32
+ }
33
+ return advance(state);
34
+ }
35
+ // ---------------------------------------------------------------------------
36
+ // Grammar rules
37
+ // ---------------------------------------------------------------------------
38
+ /**
39
+ * expression := orExpr
40
+ */
41
+ function parseExpression(state) {
42
+ return parseOr(state);
43
+ }
44
+ /**
45
+ * orExpr := andExpr ( 'or' andExpr )*
46
+ */
47
+ function parseOr(state) {
48
+ let left = parseAnd(state);
49
+ while (peek(state).type === 'Operator' && peek(state).value === 'or') {
50
+ advance(state); // consume 'or'
51
+ const right = parseAnd(state);
52
+ left = { type: 'BinaryOp', operator: 'or', left, right };
53
+ }
54
+ return left;
55
+ }
56
+ /**
57
+ * andExpr := comparisonExpr ( 'and' comparisonExpr )*
58
+ */
59
+ function parseAnd(state) {
60
+ let left = parseComparison(state);
61
+ while (peek(state).type === 'Operator' && peek(state).value === 'and') {
62
+ advance(state); // consume 'and'
63
+ const right = parseComparison(state);
64
+ left = { type: 'BinaryOp', operator: 'and', left, right };
65
+ }
66
+ return left;
67
+ }
68
+ /**
69
+ * comparisonExpr := unaryExpr ( ('eq'|'neq'|'gt'|'lt'|'gte'|'lte') unaryExpr )?
70
+ *
71
+ * Comparisons are non-associative (no chaining).
72
+ */
73
+ function parseComparison(state) {
74
+ let left = parseUnary(state);
75
+ const token = peek(state);
76
+ if (token.type === 'Operator' && COMPARISON_OPERATORS.has(token.value)) {
77
+ const operator = advance(state).value;
78
+ const right = parseUnary(state);
79
+ left = { type: 'BinaryOp', operator, left, right };
80
+ }
81
+ return left;
82
+ }
83
+ /**
84
+ * unaryExpr := 'not' unaryExpr | primaryExpr
85
+ */
86
+ function parseUnary(state) {
87
+ const token = peek(state);
88
+ if (token.type === 'Operator' && token.value === 'not') {
89
+ advance(state); // consume 'not'
90
+ const operand = parseUnary(state);
91
+ return { type: 'UnaryOp', operator: 'not', operand };
92
+ }
93
+ return parsePrimary(state);
94
+ }
95
+ /**
96
+ * primaryExpr := BooleanLiteral
97
+ * | StringLiteral
98
+ * | NumberLiteral
99
+ * | Identifier '(' argList? ')' -- function call
100
+ * | Identifier -- variable ref
101
+ * | '(' expression ')' -- grouped sub-expression
102
+ */
103
+ function parsePrimary(state) {
104
+ const token = peek(state);
105
+ // Boolean literal
106
+ if (token.type === 'BooleanLiteral') {
107
+ advance(state);
108
+ return { type: 'BooleanLiteral', value: token.value === 'true' };
109
+ }
110
+ // String literal
111
+ if (token.type === 'StringLiteral') {
112
+ advance(state);
113
+ return { type: 'StringLiteral', value: token.value };
114
+ }
115
+ // Number literal
116
+ if (token.type === 'NumberLiteral') {
117
+ advance(state);
118
+ return { type: 'NumberLiteral', value: Number(token.value) };
119
+ }
120
+ // Identifier — could be variable ref or function call
121
+ if (token.type === 'Identifier') {
122
+ advance(state);
123
+ // Check for function call: Identifier '(' ...
124
+ if (peek(state).type === 'LeftParen') {
125
+ advance(state); // consume '('
126
+ const args = parseArgList(state);
127
+ expect(state, 'RightParen');
128
+ return { type: 'FunctionCall', name: token.value, args };
129
+ }
130
+ return { type: 'VariableRef', name: token.value };
131
+ }
132
+ // Parenthesized sub-expression
133
+ if (token.type === 'LeftParen') {
134
+ advance(state); // consume '('
135
+ const expr = parseExpression(state);
136
+ expect(state, 'RightParen');
137
+ return expr;
138
+ }
139
+ // Error: unexpected token
140
+ const got = token.type === 'EOF' ? 'end of expression' : `'${token.value}'`;
141
+ throw new ParseError(`Unexpected ${got}`, token.position);
142
+ }
143
+ /**
144
+ * argList := expression ( ',' expression )*
145
+ * | (empty)
146
+ */
147
+ function parseArgList(state) {
148
+ const args = [];
149
+ if (peek(state).type === 'RightParen') {
150
+ return args;
151
+ }
152
+ args.push(parseExpression(state));
153
+ while (peek(state).type === 'Comma') {
154
+ advance(state); // consume ','
155
+ args.push(parseExpression(state));
156
+ }
157
+ return args;
158
+ }
159
+ // ---------------------------------------------------------------------------
160
+ // Public API
161
+ // ---------------------------------------------------------------------------
162
+ /**
163
+ * Parse a token stream into an AST.
164
+ *
165
+ * @param tokens - Token array produced by `tokenize()`.
166
+ * @returns The root AST node.
167
+ * @throws {ParseError} on syntax errors.
168
+ */
169
+ export function parse(tokens) {
170
+ if (tokens.length === 0 || (tokens.length === 1 && tokens[0].type === 'EOF')) {
171
+ throw new ParseError('Empty expression', { offset: 0, column: 1 });
172
+ }
173
+ const state = { tokens, pos: 0 };
174
+ const ast = parseExpression(state);
175
+ // Ensure all tokens were consumed (except EOF)
176
+ const remaining = peek(state);
177
+ if (remaining.type !== 'EOF') {
178
+ throw new ParseError(`Unexpected '${remaining.value}' after expression`, remaining.position);
179
+ }
180
+ return ast;
181
+ }
@@ -4,11 +4,18 @@
4
4
  * Declarative workflow graph definitions using YAML (v1.1 schema extension).
5
5
  * Defines phases, transitions, escalation ladder, gates, and parallelism.
6
6
  */
7
- export type { EscalationStrategy, PhaseDefinition, TransitionDefinition, EscalationLadderRung, EscalationConfig, GateDefinition, ParallelismGroupDefinition, WorkflowDefinition, } from './workflow-types.js';
8
- export { PhaseDefinitionSchema, TransitionDefinitionSchema, EscalationLadderRungSchema, EscalationConfigSchema, GateDefinitionSchema, ParallelismGroupDefinitionSchema, WorkflowDefinitionSchema, validateWorkflowDefinition, } from './workflow-types.js';
7
+ export type { EscalationStrategy, PhaseDefinition, TransitionDefinition, EscalationLadderRung, EscalationConfig, GateDefinition, ParallelismGroupDefinition, WorkflowDefinition, BranchingDefinition, TemplateRetryConfig, TemplateTimeoutConfig, } from './workflow-types.js';
8
+ export { PhaseDefinitionSchema, TransitionDefinitionSchema, EscalationLadderRungSchema, EscalationConfigSchema, GateDefinitionSchema, ParallelismGroupDefinitionSchema, WorkflowDefinitionSchema, BranchingDefinitionSchema, TemplateRetryConfigSchema, TemplateTimeoutConfigSchema, validateWorkflowDefinition, } from './workflow-types.js';
9
9
  export { loadWorkflowDefinitionFile, getBuiltinWorkflowDir, getBuiltinWorkflowPath, } from './workflow-loader.js';
10
- export type { WorkflowRegistryConfig } from './workflow-registry.js';
10
+ export type { WorkflowRegistryConfig, WorkflowStoreSource } from './workflow-registry.js';
11
11
  export { WorkflowRegistry } from './workflow-registry.js';
12
12
  export type { TransitionContext, TransitionResult } from './transition-engine.js';
13
13
  export { evaluateTransitions } from './transition-engine.js';
14
+ export type { BranchingResult } from './branching-router.js';
15
+ export { evaluateBranching } from './branching-router.js';
16
+ export { parseDuration, DurationParseError } from './duration.js';
17
+ export type { ResolvedRetryConfig, ResolvedTimeoutConfig } from './retry-resolver.js';
18
+ export { resolveRetryConfig, resolveTimeoutConfig } from './retry-resolver.js';
19
+ export type { EvaluationContext } from './expression/index.js';
20
+ export { buildEvaluationContext, evaluateCondition } from './expression/index.js';
14
21
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/workflow/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,YAAY,EACV,kBAAkB,EAClB,eAAe,EACf,oBAAoB,EACpB,oBAAoB,EACpB,gBAAgB,EAChB,cAAc,EACd,0BAA0B,EAC1B,kBAAkB,GACnB,MAAM,qBAAqB,CAAA;AAE5B,OAAO,EACL,qBAAqB,EACrB,0BAA0B,EAC1B,0BAA0B,EAC1B,sBAAsB,EACtB,oBAAoB,EACpB,gCAAgC,EAChC,wBAAwB,EACxB,0BAA0B,GAC3B,MAAM,qBAAqB,CAAA;AAE5B,OAAO,EACL,0BAA0B,EAC1B,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,sBAAsB,CAAA;AAE7B,YAAY,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAA;AACpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAEzD,YAAY,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AACjF,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/workflow/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,YAAY,EACV,kBAAkB,EAClB,eAAe,EACf,oBAAoB,EACpB,oBAAoB,EACpB,gBAAgB,EAChB,cAAc,EACd,0BAA0B,EAC1B,kBAAkB,EAClB,mBAAmB,EACnB,mBAAmB,EACnB,qBAAqB,GACtB,MAAM,qBAAqB,CAAA;AAE5B,OAAO,EACL,qBAAqB,EACrB,0BAA0B,EAC1B,0BAA0B,EAC1B,sBAAsB,EACtB,oBAAoB,EACpB,gCAAgC,EAChC,wBAAwB,EACxB,yBAAyB,EACzB,yBAAyB,EACzB,2BAA2B,EAC3B,0BAA0B,GAC3B,MAAM,qBAAqB,CAAA;AAE5B,OAAO,EACL,0BAA0B,EAC1B,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,sBAAsB,CAAA;AAE7B,YAAY,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAA;AACzF,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAEzD,YAAY,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AACjF,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAA;AAE5D,YAAY,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AAGzD,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AAGjE,YAAY,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAA;AACrF,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AAG9E,YAAY,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AAC9D,OAAO,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA"}
@@ -4,7 +4,12 @@
4
4
  * Declarative workflow graph definitions using YAML (v1.1 schema extension).
5
5
  * Defines phases, transitions, escalation ladder, gates, and parallelism.
6
6
  */
7
- export { PhaseDefinitionSchema, TransitionDefinitionSchema, EscalationLadderRungSchema, EscalationConfigSchema, GateDefinitionSchema, ParallelismGroupDefinitionSchema, WorkflowDefinitionSchema, validateWorkflowDefinition, } from './workflow-types.js';
7
+ export { PhaseDefinitionSchema, TransitionDefinitionSchema, EscalationLadderRungSchema, EscalationConfigSchema, GateDefinitionSchema, ParallelismGroupDefinitionSchema, WorkflowDefinitionSchema, BranchingDefinitionSchema, TemplateRetryConfigSchema, TemplateTimeoutConfigSchema, validateWorkflowDefinition, } from './workflow-types.js';
8
8
  export { loadWorkflowDefinitionFile, getBuiltinWorkflowDir, getBuiltinWorkflowPath, } from './workflow-loader.js';
9
9
  export { WorkflowRegistry } from './workflow-registry.js';
10
10
  export { evaluateTransitions } from './transition-engine.js';
11
+ export { evaluateBranching } from './branching-router.js';
12
+ // Duration parser
13
+ export { parseDuration, DurationParseError } from './duration.js';
14
+ export { resolveRetryConfig, resolveTimeoutConfig } from './retry-resolver.js';
15
+ export { buildEvaluationContext, evaluateCondition } from './expression/index.js';
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Retry & Timeout Resolution Logic
3
+ *
4
+ * Resolves per-template retry and timeout configurations with a layered
5
+ * override precedence:
6
+ *
7
+ * template-level → phase-level → global escalation config → defaults
8
+ *
9
+ * All functions are pure — no side effects.
10
+ */
11
+ import type { TemplateRetryConfig, TemplateTimeoutConfig, EscalationConfig, EscalationLadderRung } from './workflow-types.js';
12
+ /**
13
+ * Resolved retry configuration with all values filled in.
14
+ */
15
+ export interface ResolvedRetryConfig {
16
+ maxAttempts: number;
17
+ ladder: EscalationLadderRung[];
18
+ }
19
+ /**
20
+ * Resolved timeout configuration with duration in milliseconds.
21
+ */
22
+ export interface ResolvedTimeoutConfig {
23
+ durationMs: number;
24
+ action: 'escalate' | 'skip' | 'fail';
25
+ }
26
+ /**
27
+ * Resolve retry configuration with override precedence:
28
+ * template-level retry → phase-level retry → global escalation config → defaults
29
+ *
30
+ * For each field (maxAttempts, ladder), the first defined value wins:
31
+ * 1. templateRetry (from branching block)
32
+ * 2. phaseRetry (from phase definition)
33
+ * 3. globalEscalation (from workflow definition)
34
+ * 4. Built-in defaults
35
+ *
36
+ * @param templateRetry - Per-template retry config (from branching block)
37
+ * @param phaseRetry - Per-phase retry config (from phase definition)
38
+ * @param globalEscalation - Global escalation config (from workflow definition)
39
+ * @returns Fully resolved retry config with fallback defaults
40
+ */
41
+ export declare function resolveRetryConfig(templateRetry?: TemplateRetryConfig, phaseRetry?: TemplateRetryConfig, globalEscalation?: EscalationConfig): ResolvedRetryConfig;
42
+ /**
43
+ * Resolve timeout configuration with override precedence:
44
+ * template-level timeout → phase-level timeout → null (no timeout)
45
+ *
46
+ * @param templateTimeout - Per-template timeout config (from branching block)
47
+ * @param phaseTimeout - Per-phase timeout config (from phase definition)
48
+ * @returns Resolved timeout config, or null if no timeout configured
49
+ */
50
+ export declare function resolveTimeoutConfig(templateTimeout?: TemplateTimeoutConfig, phaseTimeout?: TemplateTimeoutConfig): ResolvedTimeoutConfig | null;
51
+ //# sourceMappingURL=retry-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retry-resolver.d.ts","sourceRoot":"","sources":["../../../src/workflow/retry-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EACV,mBAAmB,EACnB,qBAAqB,EACrB,gBAAgB,EAChB,oBAAoB,EACrB,MAAM,qBAAqB,CAAA;AAO5B;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,EAAE,oBAAoB,EAAE,CAAA;CAC/B;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,MAAM,CAAA;IAClB,MAAM,EAAE,UAAU,GAAG,MAAM,GAAG,MAAM,CAAA;CACrC;AAkBD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAChC,aAAa,CAAC,EAAE,mBAAmB,EACnC,UAAU,CAAC,EAAE,mBAAmB,EAChC,gBAAgB,CAAC,EAAE,gBAAgB,GAClC,mBAAmB,CAcrB;AAMD;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,eAAe,CAAC,EAAE,qBAAqB,EACvC,YAAY,CAAC,EAAE,qBAAqB,GACnC,qBAAqB,GAAG,IAAI,CAW9B"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Retry & Timeout Resolution Logic
3
+ *
4
+ * Resolves per-template retry and timeout configurations with a layered
5
+ * override precedence:
6
+ *
7
+ * template-level → phase-level → global escalation config → defaults
8
+ *
9
+ * All functions are pure — no side effects.
10
+ */
11
+ import { parseDuration } from './duration.js';
12
+ // ---------------------------------------------------------------------------
13
+ // Defaults
14
+ // ---------------------------------------------------------------------------
15
+ const DEFAULT_MAX_ATTEMPTS = 3;
16
+ const DEFAULT_LADDER = [
17
+ { cycle: 1, strategy: 'normal' },
18
+ { cycle: 2, strategy: 'context-enriched' },
19
+ { cycle: 3, strategy: 'decompose' },
20
+ ];
21
+ // ---------------------------------------------------------------------------
22
+ // Retry Resolution
23
+ // ---------------------------------------------------------------------------
24
+ /**
25
+ * Resolve retry configuration with override precedence:
26
+ * template-level retry → phase-level retry → global escalation config → defaults
27
+ *
28
+ * For each field (maxAttempts, ladder), the first defined value wins:
29
+ * 1. templateRetry (from branching block)
30
+ * 2. phaseRetry (from phase definition)
31
+ * 3. globalEscalation (from workflow definition)
32
+ * 4. Built-in defaults
33
+ *
34
+ * @param templateRetry - Per-template retry config (from branching block)
35
+ * @param phaseRetry - Per-phase retry config (from phase definition)
36
+ * @param globalEscalation - Global escalation config (from workflow definition)
37
+ * @returns Fully resolved retry config with fallback defaults
38
+ */
39
+ export function resolveRetryConfig(templateRetry, phaseRetry, globalEscalation) {
40
+ const maxAttempts = templateRetry?.maxAttempts
41
+ ?? phaseRetry?.maxAttempts
42
+ ?? globalEscalation?.circuitBreaker.maxSessionsPerPhase
43
+ ?? DEFAULT_MAX_ATTEMPTS;
44
+ const ladder = templateRetry?.ladder
45
+ ?? phaseRetry?.ladder
46
+ ?? globalEscalation?.ladder
47
+ ?? DEFAULT_LADDER;
48
+ return { maxAttempts, ladder };
49
+ }
50
+ // ---------------------------------------------------------------------------
51
+ // Timeout Resolution
52
+ // ---------------------------------------------------------------------------
53
+ /**
54
+ * Resolve timeout configuration with override precedence:
55
+ * template-level timeout → phase-level timeout → null (no timeout)
56
+ *
57
+ * @param templateTimeout - Per-template timeout config (from branching block)
58
+ * @param phaseTimeout - Per-phase timeout config (from phase definition)
59
+ * @returns Resolved timeout config, or null if no timeout configured
60
+ */
61
+ export function resolveTimeoutConfig(templateTimeout, phaseTimeout) {
62
+ const timeout = templateTimeout ?? phaseTimeout;
63
+ if (!timeout) {
64
+ return null;
65
+ }
66
+ return {
67
+ durationMs: parseDuration(timeout.duration),
68
+ action: timeout.action,
69
+ };
70
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=retry-resolver.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retry-resolver.test.d.ts","sourceRoot":"","sources":["../../../src/workflow/retry-resolver.test.ts"],"names":[],"mappings":""}