@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,28 @@
1
+ /**
2
+ * Duration Parser
3
+ *
4
+ * Parses human-readable duration strings into milliseconds.
5
+ * Supported units: "m" (minutes), "h" (hours), "d" (days).
6
+ *
7
+ * Examples:
8
+ * "30m" → 1_800_000 (30 minutes)
9
+ * "2h" → 7_200_000 (2 hours)
10
+ * "1d" → 86_400_000 (1 day)
11
+ */
12
+ /**
13
+ * Error thrown for invalid duration strings.
14
+ */
15
+ export declare class DurationParseError extends Error {
16
+ constructor(input: string, reason: string);
17
+ }
18
+ /**
19
+ * Parse a duration string into milliseconds.
20
+ * Supports: "Nm" (minutes), "Nh" (hours), "Nd" (days)
21
+ * Examples: "30m" → 1800000, "2h" → 7200000, "1d" → 86400000
22
+ *
23
+ * @param duration - Duration string (e.g., "30m", "2h", "1d")
24
+ * @returns Duration in milliseconds
25
+ * @throws DurationParseError for invalid input
26
+ */
27
+ export declare function parseDuration(duration: string): number;
28
+ //# sourceMappingURL=duration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"duration.d.ts","sourceRoot":"","sources":["../../../src/workflow/duration.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAkBH;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAI1C;AAMD;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAmBtD"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Duration Parser
3
+ *
4
+ * Parses human-readable duration strings into milliseconds.
5
+ * Supported units: "m" (minutes), "h" (hours), "d" (days).
6
+ *
7
+ * Examples:
8
+ * "30m" → 1_800_000 (30 minutes)
9
+ * "2h" → 7_200_000 (2 hours)
10
+ * "1d" → 86_400_000 (1 day)
11
+ */
12
+ // ---------------------------------------------------------------------------
13
+ // Constants
14
+ // ---------------------------------------------------------------------------
15
+ const UNIT_TO_MS = {
16
+ m: 60 * 1000, // minutes
17
+ h: 60 * 60 * 1000, // hours
18
+ d: 24 * 60 * 60 * 1000, // days
19
+ };
20
+ const DURATION_REGEX = /^(\d+)(m|h|d)$/;
21
+ // ---------------------------------------------------------------------------
22
+ // Error
23
+ // ---------------------------------------------------------------------------
24
+ /**
25
+ * Error thrown for invalid duration strings.
26
+ */
27
+ export class DurationParseError extends Error {
28
+ constructor(input, reason) {
29
+ super(`Invalid duration "${input}": ${reason}`);
30
+ this.name = 'DurationParseError';
31
+ }
32
+ }
33
+ // ---------------------------------------------------------------------------
34
+ // Parser
35
+ // ---------------------------------------------------------------------------
36
+ /**
37
+ * Parse a duration string into milliseconds.
38
+ * Supports: "Nm" (minutes), "Nh" (hours), "Nd" (days)
39
+ * Examples: "30m" → 1800000, "2h" → 7200000, "1d" → 86400000
40
+ *
41
+ * @param duration - Duration string (e.g., "30m", "2h", "1d")
42
+ * @returns Duration in milliseconds
43
+ * @throws DurationParseError for invalid input
44
+ */
45
+ export function parseDuration(duration) {
46
+ if (!duration || typeof duration !== 'string') {
47
+ throw new DurationParseError(String(duration), 'duration must be a non-empty string');
48
+ }
49
+ const trimmed = duration.trim();
50
+ const match = DURATION_REGEX.exec(trimmed);
51
+ if (!match) {
52
+ throw new DurationParseError(trimmed, 'expected format "<number><unit>" where unit is m (minutes), h (hours), or d (days)');
53
+ }
54
+ const value = parseInt(match[1], 10);
55
+ const unit = match[2];
56
+ return value * UNIT_TO_MS[unit];
57
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=duration.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"duration.test.d.ts","sourceRoot":"","sources":["../../../src/workflow/duration.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,74 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { parseDuration, DurationParseError } from './duration.js';
3
+ describe('parseDuration', () => {
4
+ // -------------------------------------------------------------------------
5
+ // Valid inputs
6
+ // -------------------------------------------------------------------------
7
+ it('parses "30m" to 1800000 ms', () => {
8
+ expect(parseDuration('30m')).toBe(1_800_000);
9
+ });
10
+ it('parses "2h" to 7200000 ms', () => {
11
+ expect(parseDuration('2h')).toBe(7_200_000);
12
+ });
13
+ it('parses "1d" to 86400000 ms', () => {
14
+ expect(parseDuration('1d')).toBe(86_400_000);
15
+ });
16
+ it('parses "90m" to 5400000 ms', () => {
17
+ expect(parseDuration('90m')).toBe(5_400_000);
18
+ });
19
+ it('parses "0m" to 0', () => {
20
+ expect(parseDuration('0m')).toBe(0);
21
+ });
22
+ it('parses "0h" to 0', () => {
23
+ expect(parseDuration('0h')).toBe(0);
24
+ });
25
+ it('parses "0d" to 0', () => {
26
+ expect(parseDuration('0d')).toBe(0);
27
+ });
28
+ it('parses large values like "365d"', () => {
29
+ expect(parseDuration('365d')).toBe(365 * 24 * 60 * 60 * 1000);
30
+ });
31
+ // -------------------------------------------------------------------------
32
+ // Invalid inputs — missing unit
33
+ // -------------------------------------------------------------------------
34
+ it('throws DurationParseError for "30" (missing unit)', () => {
35
+ expect(() => parseDuration('30')).toThrow(DurationParseError);
36
+ expect(() => parseDuration('30')).toThrow(/Invalid duration/);
37
+ });
38
+ it('throws DurationParseError for "m" (missing number)', () => {
39
+ expect(() => parseDuration('m')).toThrow(DurationParseError);
40
+ });
41
+ it('throws DurationParseError for empty string', () => {
42
+ expect(() => parseDuration('')).toThrow(DurationParseError);
43
+ });
44
+ it('throws DurationParseError for "abc" (nonsense input)', () => {
45
+ expect(() => parseDuration('abc')).toThrow(DurationParseError);
46
+ });
47
+ it('throws DurationParseError for negative numbers like "-5m"', () => {
48
+ expect(() => parseDuration('-5m')).toThrow(DurationParseError);
49
+ });
50
+ // -------------------------------------------------------------------------
51
+ // Invalid inputs — unsupported units
52
+ // -------------------------------------------------------------------------
53
+ it('throws DurationParseError for "30s" (seconds not supported)', () => {
54
+ expect(() => parseDuration('30s')).toThrow(DurationParseError);
55
+ });
56
+ it('throws DurationParseError for "2w" (weeks not supported)', () => {
57
+ expect(() => parseDuration('2w')).toThrow(DurationParseError);
58
+ });
59
+ it('throws DurationParseError for "1y" (years not supported)', () => {
60
+ expect(() => parseDuration('1y')).toThrow(DurationParseError);
61
+ });
62
+ // -------------------------------------------------------------------------
63
+ // Edge cases
64
+ // -------------------------------------------------------------------------
65
+ it('throws DurationParseError for decimal values like "1.5h"', () => {
66
+ expect(() => parseDuration('1.5h')).toThrow(DurationParseError);
67
+ });
68
+ it('throws DurationParseError for multiple units like "1h30m"', () => {
69
+ expect(() => parseDuration('1h30m')).toThrow(DurationParseError);
70
+ });
71
+ it('handles whitespace around the value', () => {
72
+ expect(parseDuration(' 30m ')).toBe(1_800_000);
73
+ });
74
+ });
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Expression AST Node Definitions
3
+ *
4
+ * Typed AST nodes for parsed condition expressions from WorkflowDefinition
5
+ * `branching[].condition` fields. Uses discriminated unions with a literal
6
+ * `type` field for exhaustive pattern matching.
7
+ */
8
+ /** A reference to a named variable, e.g. `isParentIssue`, `priority` */
9
+ export interface VariableRef {
10
+ readonly type: 'VariableRef';
11
+ readonly name: string;
12
+ }
13
+ /** A boolean literal: `true` or `false` */
14
+ export interface BooleanLiteral {
15
+ readonly type: 'BooleanLiteral';
16
+ readonly value: boolean;
17
+ }
18
+ /** A single-quoted string literal, e.g. `'bug'` */
19
+ export interface StringLiteral {
20
+ readonly type: 'StringLiteral';
21
+ readonly value: string;
22
+ }
23
+ /** A numeric literal, e.g. `3`, `2.5` */
24
+ export interface NumberLiteral {
25
+ readonly type: 'NumberLiteral';
26
+ readonly value: number;
27
+ }
28
+ /** Unary operators */
29
+ export type UnaryOperator = 'not';
30
+ /** Binary logical/comparison operators */
31
+ export type BinaryOperator = 'and' | 'or' | 'eq' | 'neq' | 'gt' | 'lt' | 'gte' | 'lte';
32
+ /** A unary operation, e.g. `not <expr>` */
33
+ export interface UnaryOp {
34
+ readonly type: 'UnaryOp';
35
+ readonly operator: UnaryOperator;
36
+ readonly operand: ASTNode;
37
+ }
38
+ /** A binary operation, e.g. `<expr> and <expr>`, `<expr> gt <expr>` */
39
+ export interface BinaryOp {
40
+ readonly type: 'BinaryOp';
41
+ readonly operator: BinaryOperator;
42
+ readonly left: ASTNode;
43
+ readonly right: ASTNode;
44
+ }
45
+ /** A function call, e.g. `hasLabel('bug')`, `hasDirective('hotfix')` */
46
+ export interface FunctionCall {
47
+ readonly type: 'FunctionCall';
48
+ readonly name: string;
49
+ readonly args: ASTNode[];
50
+ }
51
+ /** Discriminated union of all AST node types */
52
+ export type ASTNode = VariableRef | BooleanLiteral | StringLiteral | NumberLiteral | UnaryOp | BinaryOp | FunctionCall;
53
+ //# sourceMappingURL=ast.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ast.d.ts","sourceRoot":"","sources":["../../../../src/workflow/expression/ast.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,wEAAwE;AACxE,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAA;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACtB;AAED,2CAA2C;AAC3C,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAA;IAC/B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;CACxB;AAED,mDAAmD;AACnD,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAA;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACvB;AAED,yCAAyC;AACzC,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAA;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACvB;AAMD,sBAAsB;AACtB,MAAM,MAAM,aAAa,GAAG,KAAK,CAAA;AAEjC,0CAA0C;AAC1C,MAAM,MAAM,cAAc,GACtB,KAAK,GACL,IAAI,GACJ,IAAI,GACJ,KAAK,GACL,IAAI,GACJ,IAAI,GACJ,KAAK,GACL,KAAK,CAAA;AAET,2CAA2C;AAC3C,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;IACxB,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAA;IAChC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;CAC1B;AAED,uEAAuE;AACvE,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAA;IACzB,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAA;IACjC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;IACtB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;CACxB;AAMD,wEAAwE;AACxE,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAA;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,IAAI,EAAE,OAAO,EAAE,CAAA;CACzB;AAMD,gDAAgD;AAChD,MAAM,MAAM,OAAO,GACf,WAAW,GACX,cAAc,GACd,aAAa,GACb,aAAa,GACb,OAAO,GACP,QAAQ,GACR,YAAY,CAAA"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Expression AST Node Definitions
3
+ *
4
+ * Typed AST nodes for parsed condition expressions from WorkflowDefinition
5
+ * `branching[].condition` fields. Uses discriminated unions with a literal
6
+ * `type` field for exhaustive pattern matching.
7
+ */
8
+ export {};
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Evaluation Context
3
+ *
4
+ * Defines the sandboxed context in which expression ASTs are evaluated.
5
+ * The context provides variable bindings and registered helper functions,
6
+ * with no access to globals, prototypes, or dynamic code execution.
7
+ */
8
+ import type { GovernorIssue } from '../../governor/governor-types.js';
9
+ /**
10
+ * The sandboxed evaluation context passed to the expression evaluator.
11
+ *
12
+ * - `variables` holds named values that `VariableRef` nodes resolve against.
13
+ * - `functions` holds callable helpers that `FunctionCall` nodes invoke.
14
+ */
15
+ export interface EvaluationContext {
16
+ /** Variable bindings — e.g., { isParentIssue: true, priority: 3 } */
17
+ readonly variables: Record<string, unknown>;
18
+ /** Registered helper functions */
19
+ readonly functions: Record<string, (...args: unknown[]) => unknown>;
20
+ }
21
+ export interface BuildContextOptions {
22
+ /** Whether the issue has sub-issues (i.e., it is a parent issue) */
23
+ hasSubIssues?: boolean;
24
+ }
25
+ /**
26
+ * Build an EvaluationContext from a GovernorIssue and optional phase state.
27
+ *
28
+ * This is the primary way to create contexts for condition evaluation
29
+ * during workflow transitions. It:
30
+ * 1. Extracts well-known variables from the issue (status, labels, etc.)
31
+ * 2. Merges in phase-state booleans (researchCompleted, etc.)
32
+ * 3. Registers all built-in helper functions
33
+ *
34
+ * @param issue - The issue being evaluated.
35
+ * @param phaseState - Optional map of phase completion flags.
36
+ * @param opts - Additional context not on the issue itself.
37
+ * @returns A fully populated EvaluationContext.
38
+ */
39
+ export declare function buildEvaluationContext(issue: GovernorIssue, phaseState?: Record<string, boolean>, opts?: BuildContextOptions): EvaluationContext;
40
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../../src/workflow/expression/context.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAA;AAOrE;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC,qEAAqE;IACrE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC3C,kCAAkC;IAClC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,CAAA;CACpE;AAMD,MAAM,WAAW,mBAAmB;IAClC,oEAAoE;IACpE,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,aAAa,EACpB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACpC,IAAI,CAAC,EAAE,mBAAmB,GACzB,iBAAiB,CAkBnB"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Evaluation Context
3
+ *
4
+ * Defines the sandboxed context in which expression ASTs are evaluated.
5
+ * The context provides variable bindings and registered helper functions,
6
+ * with no access to globals, prototypes, or dynamic code execution.
7
+ */
8
+ import { createBuiltinHelpers } from './helpers.js';
9
+ /**
10
+ * Build an EvaluationContext from a GovernorIssue and optional phase state.
11
+ *
12
+ * This is the primary way to create contexts for condition evaluation
13
+ * during workflow transitions. It:
14
+ * 1. Extracts well-known variables from the issue (status, labels, etc.)
15
+ * 2. Merges in phase-state booleans (researchCompleted, etc.)
16
+ * 3. Registers all built-in helper functions
17
+ *
18
+ * @param issue - The issue being evaluated.
19
+ * @param phaseState - Optional map of phase completion flags.
20
+ * @param opts - Additional context not on the issue itself.
21
+ * @returns A fully populated EvaluationContext.
22
+ */
23
+ export function buildEvaluationContext(issue, phaseState, opts) {
24
+ const hasSubIssues = opts?.hasSubIssues ?? false;
25
+ const variables = {
26
+ // Issue-derived variables
27
+ isParentIssue: hasSubIssues,
28
+ labels: issue.labels,
29
+ status: issue.status,
30
+ priority: 0, // GovernorIssue doesn't carry priority; default to 0
31
+ // Spread phase-state booleans so expressions like
32
+ // `researchCompleted and not backlogCreationCompleted` work directly.
33
+ ...phaseState,
34
+ };
35
+ const functions = createBuiltinHelpers(issue, { hasSubIssues });
36
+ return { variables, functions };
37
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Expression Evaluator (Tree-Walking)
3
+ *
4
+ * Walks a parsed AST and produces a result value within a sandboxed
5
+ * EvaluationContext. No `eval()`, `new Function()`, or dynamic code
6
+ * execution is used — evaluation is a pure recursive descent over the
7
+ * typed AST nodes.
8
+ */
9
+ import type { ASTNode } from './ast.js';
10
+ import type { EvaluationContext } from './context.js';
11
+ /**
12
+ * Error thrown when expression evaluation fails at runtime.
13
+ *
14
+ * Examples: unknown function name, type mismatch in numeric comparison.
15
+ */
16
+ export declare class EvaluationError extends Error {
17
+ constructor(message: string);
18
+ }
19
+ /**
20
+ * Evaluate an AST node within the given context.
21
+ *
22
+ * @param ast - The AST node to evaluate.
23
+ * @param context - The sandboxed evaluation context.
24
+ * @returns The result of evaluating the node.
25
+ * @throws {EvaluationError} on unknown functions or type mismatches.
26
+ */
27
+ export declare function evaluate(ast: ASTNode, context: EvaluationContext): unknown;
28
+ //# sourceMappingURL=evaluator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evaluator.d.ts","sourceRoot":"","sources":["../../../../src/workflow/expression/evaluator.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAA;AACvC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAMrD;;;;GAIG;AACH,qBAAa,eAAgB,SAAQ,KAAK;gBAC5B,OAAO,EAAE,MAAM;CAI5B;AAiBD;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,iBAAiB,GAAG,OAAO,CA6D1E"}
@@ -0,0 +1,165 @@
1
+ /**
2
+ * Expression Evaluator (Tree-Walking)
3
+ *
4
+ * Walks a parsed AST and produces a result value within a sandboxed
5
+ * EvaluationContext. No `eval()`, `new Function()`, or dynamic code
6
+ * execution is used — evaluation is a pure recursive descent over the
7
+ * typed AST nodes.
8
+ */
9
+ // ---------------------------------------------------------------------------
10
+ // EvaluationError
11
+ // ---------------------------------------------------------------------------
12
+ /**
13
+ * Error thrown when expression evaluation fails at runtime.
14
+ *
15
+ * Examples: unknown function name, type mismatch in numeric comparison.
16
+ */
17
+ export class EvaluationError extends Error {
18
+ constructor(message) {
19
+ super(message);
20
+ this.name = 'EvaluationError';
21
+ }
22
+ }
23
+ // ---------------------------------------------------------------------------
24
+ // Helpers
25
+ // ---------------------------------------------------------------------------
26
+ /**
27
+ * Coerce an arbitrary value to a boolean using JavaScript truthiness rules.
28
+ */
29
+ function toBool(value) {
30
+ return Boolean(value);
31
+ }
32
+ // ---------------------------------------------------------------------------
33
+ // Evaluator
34
+ // ---------------------------------------------------------------------------
35
+ /**
36
+ * Evaluate an AST node within the given context.
37
+ *
38
+ * @param ast - The AST node to evaluate.
39
+ * @param context - The sandboxed evaluation context.
40
+ * @returns The result of evaluating the node.
41
+ * @throws {EvaluationError} on unknown functions or type mismatches.
42
+ */
43
+ export function evaluate(ast, context) {
44
+ switch (ast.type) {
45
+ // -------------------------------------------------------------------
46
+ // Literals — return value directly
47
+ // -------------------------------------------------------------------
48
+ case 'BooleanLiteral':
49
+ return ast.value;
50
+ case 'StringLiteral':
51
+ return ast.value;
52
+ case 'NumberLiteral':
53
+ return ast.value;
54
+ // -------------------------------------------------------------------
55
+ // Variable reference — look up in context, default to false
56
+ // -------------------------------------------------------------------
57
+ case 'VariableRef': {
58
+ const value = context.variables[ast.name];
59
+ // Undefined variables resolve to `false` (no crash)
60
+ if (value === undefined) {
61
+ return false;
62
+ }
63
+ return value;
64
+ }
65
+ // -------------------------------------------------------------------
66
+ // Unary operator
67
+ // -------------------------------------------------------------------
68
+ case 'UnaryOp': {
69
+ // Currently only 'not' is supported
70
+ const operand = evaluate(ast.operand, context);
71
+ return !toBool(operand);
72
+ }
73
+ // -------------------------------------------------------------------
74
+ // Binary operator
75
+ // -------------------------------------------------------------------
76
+ case 'BinaryOp':
77
+ return evaluateBinaryOp(ast.operator, ast.left, ast.right, context);
78
+ // -------------------------------------------------------------------
79
+ // Function call
80
+ // -------------------------------------------------------------------
81
+ case 'FunctionCall': {
82
+ const fn = context.functions[ast.name];
83
+ if (!fn) {
84
+ throw new EvaluationError(`Unknown function '${ast.name}'. Available functions: ${Object.keys(context.functions).join(', ') || '(none)'}`);
85
+ }
86
+ const args = ast.args.map((arg) => evaluate(arg, context));
87
+ return fn(...args);
88
+ }
89
+ default: {
90
+ // Exhaustive check — should never reach here with a well-typed AST
91
+ const _exhaustive = ast;
92
+ throw new EvaluationError(`Unknown AST node type: ${_exhaustive.type}`);
93
+ }
94
+ }
95
+ }
96
+ // ---------------------------------------------------------------------------
97
+ // Binary operator evaluation
98
+ // ---------------------------------------------------------------------------
99
+ function evaluateBinaryOp(operator, left, right, context) {
100
+ switch (operator) {
101
+ // Short-circuit logical operators
102
+ case 'and': {
103
+ const leftVal = evaluate(left, context);
104
+ if (!toBool(leftVal))
105
+ return leftVal;
106
+ return evaluate(right, context);
107
+ }
108
+ case 'or': {
109
+ const leftVal = evaluate(left, context);
110
+ if (toBool(leftVal))
111
+ return leftVal;
112
+ return evaluate(right, context);
113
+ }
114
+ // Equality — works across all types
115
+ case 'eq': {
116
+ const leftVal = evaluate(left, context);
117
+ const rightVal = evaluate(right, context);
118
+ return leftVal === rightVal;
119
+ }
120
+ case 'neq': {
121
+ const leftVal = evaluate(left, context);
122
+ const rightVal = evaluate(right, context);
123
+ return leftVal !== rightVal;
124
+ }
125
+ // Ordering comparisons — require matching numeric types
126
+ case 'gt':
127
+ case 'lt':
128
+ case 'gte':
129
+ case 'lte': {
130
+ const leftVal = evaluate(left, context);
131
+ const rightVal = evaluate(right, context);
132
+ return evaluateOrdering(operator, leftVal, rightVal);
133
+ }
134
+ default:
135
+ throw new EvaluationError(`Unknown operator '${operator}'`);
136
+ }
137
+ }
138
+ function evaluateOrdering(operator, left, right) {
139
+ // Both must be numbers or both must be strings for ordering comparisons
140
+ if (typeof left === 'number' && typeof right === 'number') {
141
+ switch (operator) {
142
+ case 'gt': return left > right;
143
+ case 'lt': return left < right;
144
+ case 'gte': return left >= right;
145
+ case 'lte': return left <= right;
146
+ }
147
+ }
148
+ if (typeof left === 'string' && typeof right === 'string') {
149
+ switch (operator) {
150
+ case 'gt': return left > right;
151
+ case 'lt': return left < right;
152
+ case 'gte': return left >= right;
153
+ case 'lte': return left <= right;
154
+ }
155
+ }
156
+ throw new EvaluationError(`Cannot compare ${typeLabel(left)} and ${typeLabel(right)} with '${operator}'. ` +
157
+ `Both operands must be numbers or both must be strings.`);
158
+ }
159
+ function typeLabel(value) {
160
+ if (value === null)
161
+ return 'null';
162
+ if (Array.isArray(value))
163
+ return 'array';
164
+ return typeof value;
165
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=evaluator.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evaluator.test.d.ts","sourceRoot":"","sources":["../../../../src/workflow/expression/evaluator.test.ts"],"names":[],"mappings":""}