@renseiai/agentfactory 0.8.6 → 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/README.md +2 -2
- 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/decision-engine.d.ts +7 -0
- package/dist/src/governor/decision-engine.d.ts.map +1 -1
- package/dist/src/governor/decision-engine.js +59 -1
- 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/governor/governor.d.ts +5 -1
- package/dist/src/governor/governor.d.ts.map +1 -1
- package/dist/src/governor/governor.js +6 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -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/activity-emitter.d.ts +3 -3
- package/dist/src/orchestrator/activity-emitter.d.ts.map +1 -1
- package/dist/src/orchestrator/activity-emitter.js +1 -1
- 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/detect-work-type.test.js +25 -16
- package/dist/src/orchestrator/index.d.ts +12 -2
- package/dist/src/orchestrator/index.d.ts.map +1 -1
- package/dist/src/orchestrator/index.js +9 -1
- package/dist/src/orchestrator/issue-tracker-client.d.ts +103 -0
- package/dist/src/orchestrator/issue-tracker-client.d.ts.map +1 -0
- package/dist/src/orchestrator/issue-tracker-client.js +8 -0
- package/dist/src/orchestrator/log-analyzer.d.ts +19 -4
- package/dist/src/orchestrator/log-analyzer.d.ts.map +1 -1
- package/dist/src/orchestrator/log-analyzer.js +26 -50
- package/dist/src/orchestrator/orchestrator-utils.test.js +3 -0
- package/dist/src/orchestrator/orchestrator.d.ts +16 -2
- package/dist/src/orchestrator/orchestrator.d.ts.map +1 -1
- package/dist/src/orchestrator/orchestrator.js +449 -115
- package/dist/src/orchestrator/parse-work-result.d.ts +1 -1
- package/dist/src/orchestrator/parse-work-result.d.ts.map +1 -1
- package/dist/src/orchestrator/parse-work-result.js +1 -1
- package/dist/src/orchestrator/session-logger.d.ts +1 -1
- package/dist/src/orchestrator/session-logger.d.ts.map +1 -1
- package/dist/src/orchestrator/state-recovery.d.ts +22 -3
- package/dist/src/orchestrator/state-recovery.d.ts.map +1 -1
- package/dist/src/orchestrator/state-recovery.js +55 -2
- package/dist/src/orchestrator/state-recovery.test.js +106 -2
- package/dist/src/orchestrator/state-types.d.ts +63 -1
- 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 +24 -2
- package/dist/src/orchestrator/types.d.ts.map +1 -1
- package/dist/src/orchestrator/work-types.d.ts +50 -0
- package/dist/src/orchestrator/work-types.d.ts.map +1 -0
- package/dist/src/orchestrator/work-types.js +20 -0
- package/dist/src/templates/registry.d.ts +1 -1
- package/dist/src/templates/registry.test.js +2 -2
- package/dist/src/templates/renderer.d.ts +1 -1
- package/dist/src/templates/types.d.ts +6 -2
- package/dist/src/templates/types.d.ts.map +1 -1
- package/dist/src/templates/types.js +2 -0
- package/dist/src/templates/types.test.js +4 -3
- package/dist/src/tools/index.d.ts +0 -3
- package/dist/src/tools/index.d.ts.map +1 -1
- package/dist/src/tools/index.js +0 -2
- 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 +21 -0
- package/dist/src/workflow/index.d.ts.map +1 -0
- package/dist/src/workflow/index.js +15 -0
- 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 +46 -0
- package/dist/src/workflow/transition-engine.d.ts.map +1 -0
- package/dist/src/workflow/transition-engine.js +113 -0
- package/dist/src/workflow/transition-engine.test.d.ts +2 -0
- package/dist/src/workflow/transition-engine.test.d.ts.map +1 -0
- package/dist/src/workflow/transition-engine.test.js +425 -0
- package/dist/src/workflow/workflow-loader.d.ts +21 -0
- package/dist/src/workflow/workflow-loader.d.ts.map +1 -0
- package/dist/src/workflow/workflow-loader.js +40 -0
- package/dist/src/workflow/workflow-loader.test.d.ts +2 -0
- package/dist/src/workflow/workflow-loader.test.d.ts.map +1 -0
- package/dist/src/workflow/workflow-loader.test.js +134 -0
- package/dist/src/workflow/workflow-registry.d.ts +97 -0
- package/dist/src/workflow/workflow-registry.d.ts.map +1 -0
- package/dist/src/workflow/workflow-registry.js +173 -0
- package/dist/src/workflow/workflow-registry.test.d.ts +2 -0
- package/dist/src/workflow/workflow-registry.test.d.ts.map +1 -0
- package/dist/src/workflow/workflow-registry.test.js +201 -0
- package/dist/src/workflow/workflow-types.d.ts +442 -0
- package/dist/src/workflow/workflow-types.d.ts.map +1 -0
- package/dist/src/workflow/workflow-types.js +113 -0
- package/dist/src/workflow/workflow-types.test.d.ts +2 -0
- package/dist/src/workflow/workflow-types.test.d.ts.map +1 -0
- package/dist/src/workflow/workflow-types.test.js +440 -0
- package/package.json +3 -4
- package/dist/src/linear-cli.d.ts +0 -38
- package/dist/src/linear-cli.d.ts.map +0 -1
- package/dist/src/linear-cli.js +0 -674
- package/dist/src/tools/linear-runner.d.ts +0 -34
- package/dist/src/tools/linear-runner.d.ts.map +0 -1
- package/dist/src/tools/linear-runner.js +0 -700
- package/dist/src/tools/plugins/linear.d.ts +0 -9
- package/dist/src/tools/plugins/linear.d.ts.map +0 -1
- package/dist/src/tools/plugins/linear.js +0 -138
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ Part of the [AgentFactory](https://github.com/renseiai/agentfactory) monorepo.
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
npm install @renseiai/agentfactory @renseiai/
|
|
10
|
+
npm install @renseiai/agentfactory @renseiai/plugin-linear
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
## Quick Start
|
|
@@ -115,7 +115,7 @@ The governor evaluates each issue against status, active sessions, cooldowns, hu
|
|
|
115
115
|
|
|
116
116
|
| Package | Description |
|
|
117
117
|
|---------|-------------|
|
|
118
|
-
| [@renseiai/
|
|
118
|
+
| [@renseiai/plugin-linear](https://www.npmjs.com/package/@renseiai/plugin-linear) | Linear issue tracker integration |
|
|
119
119
|
| [@renseiai/agentfactory-server](https://www.npmjs.com/package/@renseiai/agentfactory-server) | Redis work queue, distributed workers |
|
|
120
120
|
| [@renseiai/agentfactory-cli](https://www.npmjs.com/package/@renseiai/agentfactory-cli) | CLI tools |
|
|
121
121
|
| [@renseiai/agentfactory-nextjs](https://www.npmjs.com/package/@renseiai/agentfactory-nextjs) | Next.js webhook server |
|
|
@@ -100,6 +100,20 @@ export declare const RepositoryConfigSchema: z.ZodObject<{
|
|
|
100
100
|
a2a: "a2a";
|
|
101
101
|
}>>>;
|
|
102
102
|
}, z.core.$strip>>;
|
|
103
|
+
mergeQueue: z.ZodOptional<z.ZodObject<{
|
|
104
|
+
provider: z.ZodDefault<z.ZodEnum<{
|
|
105
|
+
"github-native": "github-native";
|
|
106
|
+
mergify: "mergify";
|
|
107
|
+
trunk: "trunk";
|
|
108
|
+
}>>;
|
|
109
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
110
|
+
autoMerge: z.ZodDefault<z.ZodBoolean>;
|
|
111
|
+
requiredChecks: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
112
|
+
}, z.core.$strip>>;
|
|
113
|
+
mergeDriver: z.ZodOptional<z.ZodEnum<{
|
|
114
|
+
default: "default";
|
|
115
|
+
mergiraf: "mergiraf";
|
|
116
|
+
}>>;
|
|
103
117
|
}, z.core.$strip>;
|
|
104
118
|
export type RepositoryConfig = z.infer<typeof RepositoryConfigSchema>;
|
|
105
119
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"repository-config.d.ts","sourceRoot":"","sources":["../../../src/config/repository-config.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAIvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAM5D,qEAAqE;AACrE,eAAO,MAAM,mBAAmB;;;;;;;;;;;;iBAW9B,CAAA;AAEF,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAA;AAW/D,uCAAuC;AACvC,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;iBAOhC,CAAA;AAEF,eAAO,MAAM,sBAAsB
|
|
1
|
+
{"version":3,"file":"repository-config.d.ts","sourceRoot":"","sources":["../../../src/config/repository-config.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAIvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAM5D,qEAAqE;AACrE,eAAO,MAAM,mBAAmB;;;;;;;;;;;;iBAW9B,CAAA;AAEF,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAA;AAW/D,uCAAuC;AACvC,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;iBAOhC,CAAA;AAEF,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAuElC,CAAA;AAMD,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAA;AAMrE;;;;GAIG;AACH,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,EAAE,GAAG,SAAS,CAK1F;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAepG;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAKhG;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,gBAAgB,GAAG,eAAe,GAAG,SAAS,CAExF;AAMD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAQ7E"}
|
|
@@ -90,6 +90,26 @@ export const RepositoryConfigSchema = z.object({
|
|
|
90
90
|
* Allows routing agents to different providers by work type or project.
|
|
91
91
|
*/
|
|
92
92
|
providers: ProvidersConfigSchema.optional(),
|
|
93
|
+
/**
|
|
94
|
+
* Merge queue configuration.
|
|
95
|
+
* Controls which merge queue provider agents use for automated merging.
|
|
96
|
+
*/
|
|
97
|
+
mergeQueue: z.object({
|
|
98
|
+
/** Merge queue provider to use */
|
|
99
|
+
provider: z.enum(['github-native', 'mergify', 'trunk']).default('github-native'),
|
|
100
|
+
/** Whether merge queue integration is enabled */
|
|
101
|
+
enabled: z.boolean().default(false),
|
|
102
|
+
/** Automatically add approved PRs to merge queue */
|
|
103
|
+
autoMerge: z.boolean().default(true),
|
|
104
|
+
/** Required CI checks that must pass before merge (provider-specific) */
|
|
105
|
+
requiredChecks: z.array(z.string()).optional(),
|
|
106
|
+
}).optional(),
|
|
107
|
+
/**
|
|
108
|
+
* Git merge driver to use in agent worktrees.
|
|
109
|
+
* 'mergiraf' enables syntax-aware merging for supported file types.
|
|
110
|
+
* Defaults to 'default' (standard git line-based merge).
|
|
111
|
+
*/
|
|
112
|
+
mergeDriver: z.enum(['mergiraf', 'default']).optional(),
|
|
93
113
|
}).refine((data) => !(data.allowedProjects && data.projectPaths), { message: 'allowedProjects and projectPaths are mutually exclusive — use one or the other' });
|
|
94
114
|
// ---------------------------------------------------------------------------
|
|
95
115
|
// Helpers
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* state, active sessions, cooldowns, and configuration flags.
|
|
9
9
|
*/
|
|
10
10
|
import type { GovernorAction, GovernorConfig, GovernorIssue } from './governor-types.js';
|
|
11
|
+
import type { WorkflowRegistry } from '../workflow/workflow-registry.js';
|
|
11
12
|
/**
|
|
12
13
|
* All external state the Governor gathers before asking the decision engine
|
|
13
14
|
* what to do. Callers are responsible for populating this context; the
|
|
@@ -25,6 +26,12 @@ export interface DecisionContext {
|
|
|
25
26
|
backlogCreationCompleted: boolean;
|
|
26
27
|
/** Number of completed agent sessions for this issue (for circuit breaker) */
|
|
27
28
|
completedSessionCount: number;
|
|
29
|
+
/**
|
|
30
|
+
* Optional workflow registry for declarative transition routing.
|
|
31
|
+
* When present, the transition engine is used instead of the hard-coded
|
|
32
|
+
* switch statement. Falls back to the switch statement when absent.
|
|
33
|
+
*/
|
|
34
|
+
workflowRegistry?: WorkflowRegistry;
|
|
28
35
|
}
|
|
29
36
|
/** Max agent sessions before the circuit breaker trips and the issue is held */
|
|
30
37
|
export declare const MAX_SESSION_ATTEMPTS = 3;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decision-engine.d.ts","sourceRoot":"","sources":["../../../src/governor/decision-engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"decision-engine.d.ts","sourceRoot":"","sources":["../../../src/governor/decision-engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAMxF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAA;AAOxE;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,aAAa,CAAA;IACpB,MAAM,EAAE,cAAc,CAAA;IACtB,gBAAgB,EAAE,OAAO,CAAA;IACzB,MAAM,EAAE,OAAO,CAAA;IACf,gBAAgB,EAAE,OAAO,CAAA;IACzB,aAAa,EAAE,OAAO,CAAA;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,iBAAiB,EAAE,OAAO,CAAA;IAC1B,wBAAwB,EAAE,OAAO,CAAA;IACjC,8EAA8E;IAC9E,qBAAqB,EAAE,MAAM,CAAA;IAC7B;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,gBAAgB,CAAA;CACpC;AAED,gFAAgF;AAChF,eAAO,MAAM,oBAAoB,IAAI,CAAA;AAMrC,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,cAAc,CAAA;IACtB,MAAM,EAAE,MAAM,CAAA;CACf;AAaD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,eAAe,GAAG,cAAc,CAsGjE"}
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* state, active sessions, cooldowns, and configuration flags.
|
|
9
9
|
*/
|
|
10
10
|
import { determineTopOfFunnelAction, DEFAULT_TOP_OF_FUNNEL_CONFIG, } from './top-of-funnel.js';
|
|
11
|
+
import { evaluateTransitions } from '../workflow/transition-engine.js';
|
|
11
12
|
/** Max agent sessions before the circuit breaker trips and the issue is held */
|
|
12
13
|
export const MAX_SESSION_ATTEMPTS = 3;
|
|
13
14
|
// ---------------------------------------------------------------------------
|
|
@@ -71,7 +72,35 @@ export function decideAction(ctx) {
|
|
|
71
72
|
reason: `Sub-issue ${issue.identifier} skipped — coordinator manages sub-issues via parent`,
|
|
72
73
|
};
|
|
73
74
|
}
|
|
74
|
-
// ---
|
|
75
|
+
// --- Declarative transition routing (v1.1) ---
|
|
76
|
+
// When a WorkflowRegistry is present, delegate to the transition engine
|
|
77
|
+
// for status→phase routing. This reads from the WorkflowDefinition YAML
|
|
78
|
+
// instead of the hard-coded switch statement below.
|
|
79
|
+
//
|
|
80
|
+
// Icebox is excluded: top-of-funnel heuristics (description quality, delay
|
|
81
|
+
// thresholds, label checks) are too nuanced for simple status→phase mapping
|
|
82
|
+
// and remain in the dedicated decideIcebox() path until Phase 3 conditions
|
|
83
|
+
// can express them declaratively.
|
|
84
|
+
if (ctx.workflowRegistry && issue.status !== 'Icebox') {
|
|
85
|
+
// "Started" is a no-op in the current workflow — agent is already working.
|
|
86
|
+
if (issue.status === 'Started') {
|
|
87
|
+
return { action: 'none', reason: `Issue ${issue.identifier} is in Started status (agent already working)` };
|
|
88
|
+
}
|
|
89
|
+
// Check governor enable flags before delegating to the transition engine.
|
|
90
|
+
// These are configuration guards, not workflow graph concerns.
|
|
91
|
+
const enableCheck = checkEnableFlag(issue.status, config, issue.identifier);
|
|
92
|
+
if (enableCheck)
|
|
93
|
+
return enableCheck;
|
|
94
|
+
const result = evaluateTransitions({
|
|
95
|
+
issue,
|
|
96
|
+
registry: ctx.workflowRegistry,
|
|
97
|
+
workflowStrategy: ctx.workflowStrategy,
|
|
98
|
+
isParentIssue: ctx.isParentIssue,
|
|
99
|
+
});
|
|
100
|
+
return result;
|
|
101
|
+
}
|
|
102
|
+
// --- Fallback: hard-coded status-specific decisions ---
|
|
103
|
+
// Used when no WorkflowRegistry is available (backward compatibility).
|
|
75
104
|
switch (issue.status) {
|
|
76
105
|
case 'Icebox':
|
|
77
106
|
return decideIcebox(ctx);
|
|
@@ -90,6 +119,35 @@ export function decideAction(ctx) {
|
|
|
90
119
|
}
|
|
91
120
|
}
|
|
92
121
|
// ---------------------------------------------------------------------------
|
|
122
|
+
// Enable-flag guard (shared between declarative and legacy paths)
|
|
123
|
+
// ---------------------------------------------------------------------------
|
|
124
|
+
/**
|
|
125
|
+
* Check whether the governor enable flag for a given status allows dispatch.
|
|
126
|
+
* Returns a DecisionResult to skip if disabled, or null to proceed.
|
|
127
|
+
*/
|
|
128
|
+
function checkEnableFlag(status, config, issueIdentifier) {
|
|
129
|
+
switch (status) {
|
|
130
|
+
case 'Backlog':
|
|
131
|
+
if (!config.enableAutoDevelopment) {
|
|
132
|
+
return { action: 'none', reason: `Auto-development is disabled for ${issueIdentifier}` };
|
|
133
|
+
}
|
|
134
|
+
break;
|
|
135
|
+
case 'Finished':
|
|
136
|
+
if (!config.enableAutoQA) {
|
|
137
|
+
return { action: 'none', reason: `Auto-QA is disabled for ${issueIdentifier}` };
|
|
138
|
+
}
|
|
139
|
+
break;
|
|
140
|
+
case 'Delivered':
|
|
141
|
+
if (!config.enableAutoAcceptance) {
|
|
142
|
+
return { action: 'none', reason: `Auto-acceptance is disabled for ${issueIdentifier}` };
|
|
143
|
+
}
|
|
144
|
+
break;
|
|
145
|
+
// Rejected has no enable flag — refinement always triggers.
|
|
146
|
+
// Icebox is handled separately via top-of-funnel.
|
|
147
|
+
}
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
// ---------------------------------------------------------------------------
|
|
93
151
|
// Per-status decision helpers
|
|
94
152
|
// ---------------------------------------------------------------------------
|
|
95
153
|
/**
|
|
@@ -64,8 +64,25 @@ export interface PollSnapshotEvent {
|
|
|
64
64
|
timestamp: string;
|
|
65
65
|
source: EventSource;
|
|
66
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* Fired when a nudge action is taken on a stuck agent session.
|
|
69
|
+
* Tracks nudge lifecycle: sent, succeeded (activity resumed), or failed (escalated).
|
|
70
|
+
*/
|
|
71
|
+
export interface NudgeEvent {
|
|
72
|
+
type: 'nudge-sent' | 'nudge-succeeded' | 'nudge-failed';
|
|
73
|
+
sessionId: string;
|
|
74
|
+
issueId: string;
|
|
75
|
+
issueIdentifier: string;
|
|
76
|
+
workerId: string;
|
|
77
|
+
attemptNumber: number;
|
|
78
|
+
nudgeMessage: string;
|
|
79
|
+
reason: string;
|
|
80
|
+
/** ISO-8601 timestamp */
|
|
81
|
+
timestamp: string;
|
|
82
|
+
source: EventSource;
|
|
83
|
+
}
|
|
67
84
|
export type EventSource = 'webhook' | 'poll' | 'manual';
|
|
68
|
-
export type GovernorEvent = IssueStatusChangedEvent | CommentAddedEvent | SessionCompletedEvent | PollSnapshotEvent;
|
|
85
|
+
export type GovernorEvent = IssueStatusChangedEvent | CommentAddedEvent | SessionCompletedEvent | PollSnapshotEvent | NudgeEvent;
|
|
69
86
|
/**
|
|
70
87
|
* Generate a deduplication key for an event.
|
|
71
88
|
* Same issue at the same status = duplicate within the dedup window.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"event-types.d.ts","sourceRoot":"","sources":["../../../src/governor/event-types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAMxD;;;GAGG;AACH,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,sBAAsB,CAAA;IAC5B,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,aAAa,CAAA;IACpB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,SAAS,EAAE,MAAM,CAAA;IACjB,yBAAyB;IACzB,SAAS,EAAE,MAAM,CAAA;IACjB,kCAAkC;IAClC,MAAM,EAAE,WAAW,CAAA;CACpB;AAED;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,eAAe,CAAA;IACrB,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,aAAa,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,yBAAyB;IACzB,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,WAAW,CAAA;CACpB;AAED;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,mBAAmB,CAAA;IACzB,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,aAAa,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,SAAS,GAAG,SAAS,CAAA;IAC9B,yBAAyB;IACzB,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,WAAW,CAAA;CACpB;AAED;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,eAAe,CAAA;IACrB,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,aAAa,CAAA;IACpB,OAAO,EAAE,MAAM,CAAA;IACf,yBAAyB;IACzB,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,WAAW,CAAA;CACpB;AAMD,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,CAAA;AAMvD,MAAM,MAAM,aAAa,GACrB,uBAAuB,GACvB,iBAAiB,GACjB,qBAAqB,GACrB,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"event-types.d.ts","sourceRoot":"","sources":["../../../src/governor/event-types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAMxD;;;GAGG;AACH,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,sBAAsB,CAAA;IAC5B,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,aAAa,CAAA;IACpB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,SAAS,EAAE,MAAM,CAAA;IACjB,yBAAyB;IACzB,SAAS,EAAE,MAAM,CAAA;IACjB,kCAAkC;IAClC,MAAM,EAAE,WAAW,CAAA;CACpB;AAED;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,eAAe,CAAA;IACrB,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,aAAa,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,yBAAyB;IACzB,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,WAAW,CAAA;CACpB;AAED;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,mBAAmB,CAAA;IACzB,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,aAAa,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,SAAS,GAAG,SAAS,CAAA;IAC9B,yBAAyB;IACzB,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,WAAW,CAAA;CACpB;AAED;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,eAAe,CAAA;IACrB,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,aAAa,CAAA;IACpB,OAAO,EAAE,MAAM,CAAA;IACf,yBAAyB;IACzB,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,WAAW,CAAA;CACpB;AAED;;;GAGG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,YAAY,GAAG,iBAAiB,GAAG,cAAc,CAAA;IACvD,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;IACf,eAAe,EAAE,MAAM,CAAA;IACvB,QAAQ,EAAE,MAAM,CAAA;IAChB,aAAa,EAAE,MAAM,CAAA;IACrB,YAAY,EAAE,MAAM,CAAA;IACpB,MAAM,EAAE,MAAM,CAAA;IACd,yBAAyB;IACzB,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,WAAW,CAAA;CACpB;AAMD,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,CAAA;AAMvD,MAAM,MAAM,aAAa,GACrB,uBAAuB,GACvB,iBAAiB,GACjB,qBAAqB,GACrB,iBAAiB,GACjB,UAAU,CAAA;AAMd;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,CAe1D;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAEvC"}
|
|
@@ -22,6 +22,10 @@ export function eventDedupKey(event) {
|
|
|
22
22
|
return `${event.issueId}:session:${event.sessionId}`;
|
|
23
23
|
case 'poll-snapshot':
|
|
24
24
|
return `${event.issueId}:${event.issue.status}`;
|
|
25
|
+
case 'nudge-sent':
|
|
26
|
+
case 'nudge-succeeded':
|
|
27
|
+
case 'nudge-failed':
|
|
28
|
+
return `${event.sessionId}:nudge:${event.type}:${event.attemptNumber}`;
|
|
25
29
|
}
|
|
26
30
|
}
|
|
27
31
|
/**
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import type { GovernorAction, GovernorConfig, GovernorIssue, ScanResult } from './governor-types.js';
|
|
12
12
|
import type { OverridePriority } from './override-parser.js';
|
|
13
|
+
import { type WorkflowRegistryConfig } from '../workflow/workflow-registry.js';
|
|
13
14
|
/**
|
|
14
15
|
* Abstract dependencies that the Governor needs to interact with
|
|
15
16
|
* external systems. Callers inject these at construction time.
|
|
@@ -53,10 +54,13 @@ export declare class WorkflowGovernor {
|
|
|
53
54
|
private readonly config;
|
|
54
55
|
private readonly deps;
|
|
55
56
|
private readonly callbacks;
|
|
57
|
+
private readonly workflowRegistry;
|
|
56
58
|
private intervalHandle;
|
|
57
59
|
private running;
|
|
58
60
|
private scanning;
|
|
59
|
-
constructor(config: Partial<GovernorConfig
|
|
61
|
+
constructor(config: Partial<GovernorConfig> & {
|
|
62
|
+
workflow?: WorkflowRegistryConfig;
|
|
63
|
+
}, deps: GovernorDependencies, callbacks?: WorkflowGovernorCallbacks);
|
|
60
64
|
/**
|
|
61
65
|
* Start the scan loop. Runs `scanOnce()` immediately, then repeats
|
|
62
66
|
* on the configured interval.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"governor.d.ts","sourceRoot":"","sources":["../../../src/governor/governor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EACV,cAAc,EACd,cAAc,EACd,aAAa,EACb,UAAU,EACX,MAAM,qBAAqB,CAAA;AAG5B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;
|
|
1
|
+
{"version":3,"file":"governor.d.ts","sourceRoot":"","sources":["../../../src/governor/governor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EACV,cAAc,EACd,cAAc,EACd,aAAa,EACb,UAAU,EACX,MAAM,qBAAqB,CAAA;AAG5B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAC5D,OAAO,EAAoB,KAAK,sBAAsB,EAAE,MAAM,kCAAkC,CAAA;AAsChG;;;;;;GAMG;AACH,MAAM,WAAW,oBAAoB;IACnC,6CAA6C;IAC7C,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,aAAa,EAAE,CAAC,CAAA;IACzD,oDAAoD;IACpD,gBAAgB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IACvD,kEAAkE;IAClE,gBAAgB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IACvD,qDAAqD;IACrD,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IACpD,mDAAmD;IACnD,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IAC7C,mEAAmE;IACnE,mBAAmB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAA;IAC1E,wDAAwD;IACxD,mBAAmB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAA;IACrE,kEAAkE;IAClE,mBAAmB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IAC1D,0EAA0E;IAC1E,0BAA0B,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IACjE,wEAAwE;IACxE,wBAAwB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;IAC9D,wDAAwD;IACxD,YAAY,EAAE,CAAC,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAC9E;AAMD;;;;GAIG;AACH,MAAM,WAAW,yBAAyB;IACxC,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,UAAU,EAAE,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACjE;AAED,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;IACvC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAsB;IAC3C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA2B;IACrD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAkB;IACnD,OAAO,CAAC,cAAc,CAA8C;IACpE,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,QAAQ,CAAQ;gBAGtB,MAAM,EAAE,OAAO,CAAC,cAAc,CAAC,GAAG;QAAE,QAAQ,CAAC,EAAE,sBAAsB,CAAA;KAAE,EACvE,IAAI,EAAE,oBAAoB,EAC1B,SAAS,CAAC,EAAE,yBAAyB;IAavC;;;OAGG;IACH,KAAK,IAAI,IAAI;IAuBb;;;OAGG;IACH,IAAI,IAAI,IAAI;IAgBZ;;OAEG;IACH,SAAS,IAAI,OAAO;IAQpB;;;;;;;;;;OAUG;IACG,QAAQ,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;IA4BvC;;;;;;;OAOG;YACW,WAAW;IA2GzB;;OAEG;YACW,aAAa;CAsC5B"}
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { DEFAULT_GOVERNOR_CONFIG } from './governor-types.js';
|
|
12
12
|
import { decideAction } from './decision-engine.js';
|
|
13
|
+
import { WorkflowRegistry } from '../workflow/workflow-registry.js';
|
|
13
14
|
// ---------------------------------------------------------------------------
|
|
14
15
|
// Logging
|
|
15
16
|
// ---------------------------------------------------------------------------
|
|
@@ -39,13 +40,16 @@ export class WorkflowGovernor {
|
|
|
39
40
|
config;
|
|
40
41
|
deps;
|
|
41
42
|
callbacks;
|
|
43
|
+
workflowRegistry;
|
|
42
44
|
intervalHandle = null;
|
|
43
45
|
running = false;
|
|
44
46
|
scanning = false;
|
|
45
47
|
constructor(config, deps, callbacks) {
|
|
46
|
-
|
|
48
|
+
const { workflow: workflowConfig, ...governorConfig } = config;
|
|
49
|
+
this.config = { ...DEFAULT_GOVERNOR_CONFIG, ...governorConfig };
|
|
47
50
|
this.deps = deps;
|
|
48
51
|
this.callbacks = callbacks ?? {};
|
|
52
|
+
this.workflowRegistry = WorkflowRegistry.create(workflowConfig);
|
|
49
53
|
}
|
|
50
54
|
// -------------------------------------------------------------------------
|
|
51
55
|
// Lifecycle
|
|
@@ -255,6 +259,7 @@ export class WorkflowGovernor {
|
|
|
255
259
|
workflowStrategy,
|
|
256
260
|
researchCompleted,
|
|
257
261
|
backlogCreationCompleted,
|
|
262
|
+
workflowRegistry: this.workflowRegistry,
|
|
258
263
|
completedSessionCount,
|
|
259
264
|
};
|
|
260
265
|
return decideAction(ctx);
|
package/dist/src/index.d.ts
CHANGED
package/dist/src/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,yBAAyB,CAAA;AACvC,cAAc,sBAAsB,CAAA;AACpC,cAAc,aAAa,CAAA;AAC3B,cAAc,uBAAuB,CAAA;AACrC,cAAc,sBAAsB,CAAA;AACpC,cAAc,qBAAqB,CAAA;AACnC,cAAc,qBAAqB,CAAA;AACnC,cAAc,mBAAmB,CAAA;AACjC,cAAc,qBAAqB,CAAA;AACnC,cAAc,kBAAkB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,yBAAyB,CAAA;AACvC,cAAc,sBAAsB,CAAA;AACpC,cAAc,aAAa,CAAA;AAC3B,cAAc,uBAAuB,CAAA;AACrC,cAAc,sBAAsB,CAAA;AACpC,cAAc,qBAAqB,CAAA;AACnC,cAAc,qBAAqB,CAAA;AACnC,cAAc,mBAAmB,CAAA;AACjC,cAAc,qBAAqB,CAAA;AACnC,cAAc,kBAAkB,CAAA;AAChC,cAAc,qBAAqB,CAAA"}
|
package/dist/src/index.js
CHANGED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub Native Merge Queue Adapter
|
|
3
|
+
*
|
|
4
|
+
* Implements MergeQueueAdapter for GitHub's built-in merge queue feature.
|
|
5
|
+
* Uses `gh api graphql` CLI for all GitHub API interactions.
|
|
6
|
+
*/
|
|
7
|
+
import type { MergeQueueAdapter, MergeQueueStatus } from '../types.js';
|
|
8
|
+
export declare class GitHubNativeMergeQueueAdapter implements MergeQueueAdapter {
|
|
9
|
+
readonly name: "github-native";
|
|
10
|
+
canEnqueue(owner: string, repo: string, prNumber: number): Promise<boolean>;
|
|
11
|
+
enqueue(owner: string, repo: string, prNumber: number): Promise<MergeQueueStatus>;
|
|
12
|
+
getStatus(owner: string, repo: string, prNumber: number): Promise<MergeQueueStatus>;
|
|
13
|
+
dequeue(owner: string, repo: string, prNumber: number): Promise<void>;
|
|
14
|
+
isEnabled(owner: string, repo: string): Promise<boolean>;
|
|
15
|
+
/** Get the GraphQL node ID for a PR */
|
|
16
|
+
private getPRNodeId;
|
|
17
|
+
/** Execute a GraphQL query/mutation via gh CLI with retry */
|
|
18
|
+
private graphql;
|
|
19
|
+
/** Map GitHub merge queue entry to our MergeQueueStatus */
|
|
20
|
+
private mapEntryToStatus;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=github-native.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-native.d.ts","sourceRoot":"","sources":["../../../../src/merge-queue/adapters/github-native.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAatE,qBAAa,6BAA8B,YAAW,iBAAiB;IACrE,QAAQ,CAAC,IAAI,EAAG,eAAe,CAAS;IAElC,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IA6C3E,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAwCjF,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA+FnF,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBrE,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAmC9D,uCAAuC;YACzB,WAAW;IAiBzB,6DAA6D;YAC/C,OAAO;IAgCrB,2DAA2D;IAC3D,OAAO,CAAC,gBAAgB;CAgBzB"}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub Native Merge Queue Adapter
|
|
3
|
+
*
|
|
4
|
+
* Implements MergeQueueAdapter for GitHub's built-in merge queue feature.
|
|
5
|
+
* Uses `gh api graphql` CLI for all GitHub API interactions.
|
|
6
|
+
*/
|
|
7
|
+
import { exec } from 'child_process';
|
|
8
|
+
import { promisify } from 'util';
|
|
9
|
+
const execAsync = promisify(exec);
|
|
10
|
+
/** Timeout for GitHub API calls (30s) */
|
|
11
|
+
const GH_API_TIMEOUT = 30000;
|
|
12
|
+
/** Maximum retries for transient failures */
|
|
13
|
+
const MAX_RETRIES = 2;
|
|
14
|
+
/** Backoff delay between retries (ms) */
|
|
15
|
+
const RETRY_DELAY = 1000;
|
|
16
|
+
export class GitHubNativeMergeQueueAdapter {
|
|
17
|
+
name = 'github-native';
|
|
18
|
+
async canEnqueue(owner, repo, prNumber) {
|
|
19
|
+
try {
|
|
20
|
+
const query = `
|
|
21
|
+
query($owner: String!, $repo: String!, $prNumber: Int!) {
|
|
22
|
+
repository(owner: $owner, name: $repo) {
|
|
23
|
+
pullRequest(number: $prNumber) {
|
|
24
|
+
mergeable
|
|
25
|
+
reviewDecision
|
|
26
|
+
mergeQueueEntry { state }
|
|
27
|
+
baseRef {
|
|
28
|
+
branchProtectionRule {
|
|
29
|
+
requiresStatusChecks
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
`;
|
|
36
|
+
const result = await this.graphql(query, { owner, repo, prNumber });
|
|
37
|
+
const pr = result.repository.pullRequest;
|
|
38
|
+
// Already in queue
|
|
39
|
+
if (pr.mergeQueueEntry)
|
|
40
|
+
return false;
|
|
41
|
+
// Must be mergeable
|
|
42
|
+
if (pr.mergeable !== 'MERGEABLE')
|
|
43
|
+
return false;
|
|
44
|
+
// Must be approved (if reviews are required)
|
|
45
|
+
if (pr.reviewDecision && pr.reviewDecision !== 'APPROVED')
|
|
46
|
+
return false;
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
async enqueue(owner, repo, prNumber) {
|
|
54
|
+
// First get the PR's node ID
|
|
55
|
+
const prId = await this.getPRNodeId(owner, repo, prNumber);
|
|
56
|
+
const mutation = `
|
|
57
|
+
mutation($prId: ID!) {
|
|
58
|
+
enqueuePullRequest(input: { pullRequestId: $prId }) {
|
|
59
|
+
mergeQueueEntry {
|
|
60
|
+
state
|
|
61
|
+
position
|
|
62
|
+
headCommit { oid }
|
|
63
|
+
enqueuedAt
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
`;
|
|
68
|
+
try {
|
|
69
|
+
const result = await this.graphql(mutation, { prId });
|
|
70
|
+
const entry = result.enqueuePullRequest.mergeQueueEntry;
|
|
71
|
+
return this.mapEntryToStatus(entry);
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
return {
|
|
75
|
+
state: 'failed',
|
|
76
|
+
failureReason: error instanceof Error ? error.message : String(error),
|
|
77
|
+
checksStatus: [],
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
async getStatus(owner, repo, prNumber) {
|
|
82
|
+
const query = `
|
|
83
|
+
query($owner: String!, $repo: String!, $prNumber: Int!) {
|
|
84
|
+
repository(owner: $owner, name: $repo) {
|
|
85
|
+
pullRequest(number: $prNumber) {
|
|
86
|
+
mergeQueueEntry {
|
|
87
|
+
state
|
|
88
|
+
position
|
|
89
|
+
headCommit { oid }
|
|
90
|
+
enqueuedAt
|
|
91
|
+
}
|
|
92
|
+
commits(last: 1) {
|
|
93
|
+
nodes {
|
|
94
|
+
commit {
|
|
95
|
+
statusCheckRollup {
|
|
96
|
+
contexts(first: 50) {
|
|
97
|
+
nodes {
|
|
98
|
+
... on CheckRun {
|
|
99
|
+
name
|
|
100
|
+
conclusion
|
|
101
|
+
status
|
|
102
|
+
}
|
|
103
|
+
... on StatusContext {
|
|
104
|
+
context
|
|
105
|
+
state
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
`;
|
|
117
|
+
const result = await this.graphql(query, { owner, repo, prNumber });
|
|
118
|
+
const pr = result.repository.pullRequest;
|
|
119
|
+
if (!pr.mergeQueueEntry) {
|
|
120
|
+
return { state: 'not-queued', checksStatus: [] };
|
|
121
|
+
}
|
|
122
|
+
const status = this.mapEntryToStatus(pr.mergeQueueEntry);
|
|
123
|
+
// Map check statuses
|
|
124
|
+
const commitNode = pr.commits.nodes[0];
|
|
125
|
+
if (commitNode?.commit.statusCheckRollup) {
|
|
126
|
+
status.checksStatus = commitNode.commit.statusCheckRollup.contexts.nodes.map((node) => {
|
|
127
|
+
if ('name' in node) {
|
|
128
|
+
return {
|
|
129
|
+
name: node.name,
|
|
130
|
+
status: node.conclusion === 'SUCCESS' ? 'pass'
|
|
131
|
+
: node.status === 'COMPLETED' ? 'fail'
|
|
132
|
+
: 'pending',
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
return {
|
|
136
|
+
name: node.context,
|
|
137
|
+
status: node.state === 'SUCCESS' ? 'pass'
|
|
138
|
+
: node.state === 'PENDING' ? 'pending'
|
|
139
|
+
: 'fail',
|
|
140
|
+
};
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
return status;
|
|
144
|
+
}
|
|
145
|
+
async dequeue(owner, repo, prNumber) {
|
|
146
|
+
const prId = await this.getPRNodeId(owner, repo, prNumber);
|
|
147
|
+
const mutation = `
|
|
148
|
+
mutation($prId: ID!) {
|
|
149
|
+
dequeuePullRequest(input: { pullRequestId: $prId }) {
|
|
150
|
+
mergeQueueEntry {
|
|
151
|
+
state
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
`;
|
|
156
|
+
await this.graphql(mutation, { prId });
|
|
157
|
+
}
|
|
158
|
+
async isEnabled(owner, repo) {
|
|
159
|
+
try {
|
|
160
|
+
const query = `
|
|
161
|
+
query($owner: String!, $repo: String!) {
|
|
162
|
+
repository(owner: $owner, name: $repo) {
|
|
163
|
+
defaultBranchRef {
|
|
164
|
+
branchProtectionRule {
|
|
165
|
+
requiresMergeQueue
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
`;
|
|
171
|
+
// Note: requiresMergeQueue might not be available in all GitHub API versions.
|
|
172
|
+
// Fallback: check if merge queue entries exist
|
|
173
|
+
const result = await this.graphql(query, { owner, repo });
|
|
174
|
+
return result.repository.defaultBranchRef?.branchProtectionRule?.requiresMergeQueue ?? false;
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
// ---------------------------------------------------------------------------
|
|
181
|
+
// Private helpers
|
|
182
|
+
// ---------------------------------------------------------------------------
|
|
183
|
+
/** Get the GraphQL node ID for a PR */
|
|
184
|
+
async getPRNodeId(owner, repo, prNumber) {
|
|
185
|
+
const query = `
|
|
186
|
+
query($owner: String!, $repo: String!, $prNumber: Int!) {
|
|
187
|
+
repository(owner: $owner, name: $repo) {
|
|
188
|
+
pullRequest(number: $prNumber) {
|
|
189
|
+
id
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
`;
|
|
194
|
+
const result = await this.graphql(query, { owner, repo, prNumber });
|
|
195
|
+
return result.repository.pullRequest.id;
|
|
196
|
+
}
|
|
197
|
+
/** Execute a GraphQL query/mutation via gh CLI with retry */
|
|
198
|
+
async graphql(query, variables) {
|
|
199
|
+
const varsArgs = Object.entries(variables)
|
|
200
|
+
.map(([key, value]) => {
|
|
201
|
+
if (typeof value === 'number') {
|
|
202
|
+
return `-F ${key}=${value}`;
|
|
203
|
+
}
|
|
204
|
+
return `-f ${key}=${String(value)}`;
|
|
205
|
+
})
|
|
206
|
+
.join(' ');
|
|
207
|
+
const command = `gh api graphql -f query='${query.replace(/'/g, "'\\''")}' ${varsArgs}`;
|
|
208
|
+
let lastError = null;
|
|
209
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
210
|
+
try {
|
|
211
|
+
const { stdout } = await execAsync(command, { timeout: GH_API_TIMEOUT });
|
|
212
|
+
const response = JSON.parse(stdout);
|
|
213
|
+
if (response.errors?.length) {
|
|
214
|
+
throw new Error(response.errors.map((e) => e.message).join('; '));
|
|
215
|
+
}
|
|
216
|
+
return response.data;
|
|
217
|
+
}
|
|
218
|
+
catch (error) {
|
|
219
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
220
|
+
if (attempt < MAX_RETRIES) {
|
|
221
|
+
await new Promise((resolve) => setTimeout(resolve, RETRY_DELAY * (attempt + 1)));
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
throw lastError;
|
|
226
|
+
}
|
|
227
|
+
/** Map GitHub merge queue entry to our MergeQueueStatus */
|
|
228
|
+
mapEntryToStatus(entry) {
|
|
229
|
+
const stateMap = {
|
|
230
|
+
QUEUED: 'queued',
|
|
231
|
+
AWAITING_CHECKS: 'queued',
|
|
232
|
+
MERGEABLE: 'merging',
|
|
233
|
+
MERGED: 'merged',
|
|
234
|
+
UNMERGEABLE: 'failed',
|
|
235
|
+
LOCKED: 'blocked',
|
|
236
|
+
};
|
|
237
|
+
return {
|
|
238
|
+
state: stateMap[entry.state] ?? 'not-queued',
|
|
239
|
+
position: entry.position,
|
|
240
|
+
checksStatus: [],
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-native.test.d.ts","sourceRoot":"","sources":["../../../../src/merge-queue/adapters/github-native.test.ts"],"names":[],"mappings":""}
|