@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.
- package/dist/src/config/repository-config.d.ts +14 -0
- package/dist/src/config/repository-config.d.ts.map +1 -1
- package/dist/src/config/repository-config.js +20 -0
- package/dist/src/governor/event-types.d.ts +18 -1
- package/dist/src/governor/event-types.d.ts.map +1 -1
- package/dist/src/governor/event-types.js +4 -0
- package/dist/src/merge-queue/adapters/github-native.d.ts +22 -0
- package/dist/src/merge-queue/adapters/github-native.d.ts.map +1 -0
- package/dist/src/merge-queue/adapters/github-native.js +243 -0
- package/dist/src/merge-queue/adapters/github-native.test.d.ts +2 -0
- package/dist/src/merge-queue/adapters/github-native.test.d.ts.map +1 -0
- package/dist/src/merge-queue/adapters/github-native.test.js +384 -0
- package/dist/src/merge-queue/index.d.ts +18 -0
- package/dist/src/merge-queue/index.d.ts.map +1 -0
- package/dist/src/merge-queue/index.js +28 -0
- package/dist/src/merge-queue/merge-queue.integration.test.d.ts +2 -0
- package/dist/src/merge-queue/merge-queue.integration.test.d.ts.map +1 -0
- package/dist/src/merge-queue/merge-queue.integration.test.js +128 -0
- package/dist/src/merge-queue/types.d.ts +48 -0
- package/dist/src/merge-queue/types.d.ts.map +1 -0
- package/dist/src/merge-queue/types.js +8 -0
- package/dist/src/orchestrator/artifact-tracker.d.ts +93 -0
- package/dist/src/orchestrator/artifact-tracker.d.ts.map +1 -0
- package/dist/src/orchestrator/artifact-tracker.js +235 -0
- package/dist/src/orchestrator/artifact-tracker.test.d.ts +2 -0
- package/dist/src/orchestrator/artifact-tracker.test.d.ts.map +1 -0
- package/dist/src/orchestrator/artifact-tracker.test.js +189 -0
- package/dist/src/orchestrator/context-manager.d.ts +72 -0
- package/dist/src/orchestrator/context-manager.d.ts.map +1 -0
- package/dist/src/orchestrator/context-manager.js +120 -0
- package/dist/src/orchestrator/context-manager.test.d.ts +2 -0
- package/dist/src/orchestrator/context-manager.test.d.ts.map +1 -0
- package/dist/src/orchestrator/context-manager.test.js +137 -0
- package/dist/src/orchestrator/index.d.ts +8 -2
- package/dist/src/orchestrator/index.d.ts.map +1 -1
- package/dist/src/orchestrator/index.js +8 -1
- package/dist/src/orchestrator/orchestrator.d.ts +12 -0
- package/dist/src/orchestrator/orchestrator.d.ts.map +1 -1
- package/dist/src/orchestrator/orchestrator.js +258 -2
- package/dist/src/orchestrator/state-recovery.d.ts +21 -2
- package/dist/src/orchestrator/state-recovery.d.ts.map +1 -1
- package/dist/src/orchestrator/state-recovery.js +54 -2
- package/dist/src/orchestrator/state-recovery.test.js +106 -2
- package/dist/src/orchestrator/state-types.d.ts +62 -0
- package/dist/src/orchestrator/state-types.d.ts.map +1 -1
- package/dist/src/orchestrator/state-types.js +5 -1
- package/dist/src/orchestrator/summary-builder.d.ts +47 -0
- package/dist/src/orchestrator/summary-builder.d.ts.map +1 -0
- package/dist/src/orchestrator/summary-builder.js +240 -0
- package/dist/src/orchestrator/summary-builder.test.d.ts +2 -0
- package/dist/src/orchestrator/summary-builder.test.d.ts.map +1 -0
- package/dist/src/orchestrator/summary-builder.test.js +236 -0
- package/dist/src/orchestrator/types.d.ts +2 -0
- package/dist/src/orchestrator/types.d.ts.map +1 -1
- package/dist/src/orchestrator/work-types.d.ts +1 -1
- package/dist/src/orchestrator/work-types.d.ts.map +1 -1
- package/dist/src/templates/registry.test.js +2 -2
- package/dist/src/templates/types.d.ts +2 -0
- package/dist/src/templates/types.d.ts.map +1 -1
- package/dist/src/templates/types.js +1 -0
- package/dist/src/workflow/branching-router.d.ts +38 -0
- package/dist/src/workflow/branching-router.d.ts.map +1 -0
- package/dist/src/workflow/branching-router.js +52 -0
- package/dist/src/workflow/branching-router.test.d.ts +2 -0
- package/dist/src/workflow/branching-router.test.d.ts.map +1 -0
- package/dist/src/workflow/branching-router.test.js +209 -0
- package/dist/src/workflow/duration.d.ts +28 -0
- package/dist/src/workflow/duration.d.ts.map +1 -0
- package/dist/src/workflow/duration.js +57 -0
- package/dist/src/workflow/duration.test.d.ts +2 -0
- package/dist/src/workflow/duration.test.d.ts.map +1 -0
- package/dist/src/workflow/duration.test.js +74 -0
- package/dist/src/workflow/expression/ast.d.ts +53 -0
- package/dist/src/workflow/expression/ast.d.ts.map +1 -0
- package/dist/src/workflow/expression/ast.js +8 -0
- package/dist/src/workflow/expression/context.d.ts +40 -0
- package/dist/src/workflow/expression/context.d.ts.map +1 -0
- package/dist/src/workflow/expression/context.js +37 -0
- package/dist/src/workflow/expression/evaluator.d.ts +28 -0
- package/dist/src/workflow/expression/evaluator.d.ts.map +1 -0
- package/dist/src/workflow/expression/evaluator.js +165 -0
- package/dist/src/workflow/expression/evaluator.test.d.ts +2 -0
- package/dist/src/workflow/expression/evaluator.test.d.ts.map +1 -0
- package/dist/src/workflow/expression/evaluator.test.js +792 -0
- package/dist/src/workflow/expression/expression.test.d.ts +2 -0
- package/dist/src/workflow/expression/expression.test.d.ts.map +1 -0
- package/dist/src/workflow/expression/expression.test.js +516 -0
- package/dist/src/workflow/expression/helpers.d.ts +21 -0
- package/dist/src/workflow/expression/helpers.d.ts.map +1 -0
- package/dist/src/workflow/expression/helpers.js +56 -0
- package/dist/src/workflow/expression/index.d.ts +55 -0
- package/dist/src/workflow/expression/index.d.ts.map +1 -0
- package/dist/src/workflow/expression/index.js +71 -0
- package/dist/src/workflow/expression/lexer.d.ts +37 -0
- package/dist/src/workflow/expression/lexer.d.ts.map +1 -0
- package/dist/src/workflow/expression/lexer.js +166 -0
- package/dist/src/workflow/expression/parser.d.ts +23 -0
- package/dist/src/workflow/expression/parser.d.ts.map +1 -0
- package/dist/src/workflow/expression/parser.js +181 -0
- package/dist/src/workflow/index.d.ts +10 -3
- package/dist/src/workflow/index.d.ts.map +1 -1
- package/dist/src/workflow/index.js +6 -1
- package/dist/src/workflow/retry-resolver.d.ts +51 -0
- package/dist/src/workflow/retry-resolver.d.ts.map +1 -0
- package/dist/src/workflow/retry-resolver.js +70 -0
- package/dist/src/workflow/retry-resolver.test.d.ts +2 -0
- package/dist/src/workflow/retry-resolver.test.d.ts.map +1 -0
- package/dist/src/workflow/retry-resolver.test.js +149 -0
- package/dist/src/workflow/transition-engine.d.ts +3 -1
- package/dist/src/workflow/transition-engine.d.ts.map +1 -1
- package/dist/src/workflow/transition-engine.js +14 -7
- package/dist/src/workflow/transition-engine.test.js +123 -11
- package/dist/src/workflow/workflow-registry.d.ts +41 -0
- package/dist/src/workflow/workflow-registry.d.ts.map +1 -1
- package/dist/src/workflow/workflow-registry.js +66 -0
- package/dist/src/workflow/workflow-types.d.ts +181 -8
- package/dist/src/workflow/workflow-types.d.ts.map +1 -1
- package/dist/src/workflow/workflow-types.js +31 -6
- 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 @@
|
|
|
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,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 @@
|
|
|
1
|
+
{"version":3,"file":"evaluator.test.d.ts","sourceRoot":"","sources":["../../../../src/workflow/expression/evaluator.test.ts"],"names":[],"mappings":""}
|