@renseiai/agentfactory 0.8.8 → 0.8.9
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/index.d.ts +1 -1
- package/dist/src/config/index.d.ts.map +1 -1
- package/dist/src/config/index.js +1 -1
- package/dist/src/config/repository-config.d.ts +23 -0
- package/dist/src/config/repository-config.d.ts.map +1 -1
- package/dist/src/config/repository-config.js +27 -0
- package/dist/src/config/repository-config.test.js +140 -1
- package/dist/src/governor/decision-engine.d.ts +3 -0
- package/dist/src/governor/decision-engine.d.ts.map +1 -1
- package/dist/src/governor/decision-engine.js +11 -0
- package/dist/src/governor/decision-engine.test.js +33 -0
- package/dist/src/governor/governor-types.d.ts +1 -1
- package/dist/src/governor/governor-types.d.ts.map +1 -1
- package/dist/src/governor/governor.d.ts +17 -1
- package/dist/src/governor/governor.d.ts.map +1 -1
- package/dist/src/governor/governor.js +112 -1
- package/dist/src/governor/governor.test.js +155 -0
- 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/orchestrator/issue-tracker-client.d.ts +4 -0
- package/dist/src/orchestrator/issue-tracker-client.d.ts.map +1 -1
- package/dist/src/orchestrator/orchestrator.d.ts.map +1 -1
- package/dist/src/orchestrator/orchestrator.js +24 -0
- package/dist/src/orchestrator/parse-work-result.d.ts.map +1 -1
- package/dist/src/orchestrator/parse-work-result.js +6 -0
- package/dist/src/orchestrator/parse-work-result.test.js +19 -0
- package/dist/src/providers/index.d.ts +64 -1
- package/dist/src/providers/index.d.ts.map +1 -1
- package/dist/src/providers/index.js +132 -1
- package/dist/src/providers/index.test.js +340 -2
- package/dist/src/routing/index.d.ts +7 -0
- package/dist/src/routing/index.d.ts.map +1 -0
- package/dist/src/routing/index.js +6 -0
- package/dist/src/routing/observation-recorder.d.ts +19 -0
- package/dist/src/routing/observation-recorder.d.ts.map +1 -0
- package/dist/src/routing/observation-recorder.js +73 -0
- package/dist/src/routing/observation-recorder.test.d.ts +2 -0
- package/dist/src/routing/observation-recorder.test.d.ts.map +1 -0
- package/dist/src/routing/observation-recorder.test.js +322 -0
- package/dist/src/routing/observation-store.d.ts +40 -0
- package/dist/src/routing/observation-store.d.ts.map +1 -0
- package/dist/src/routing/observation-store.js +1 -0
- package/dist/src/routing/observation-store.test.d.ts +2 -0
- package/dist/src/routing/observation-store.test.d.ts.map +1 -0
- package/dist/src/routing/observation-store.test.js +138 -0
- package/dist/src/routing/posterior-store.d.ts +12 -0
- package/dist/src/routing/posterior-store.d.ts.map +1 -0
- package/dist/src/routing/posterior-store.js +13 -0
- package/dist/src/routing/posterior-store.test.d.ts +2 -0
- package/dist/src/routing/posterior-store.test.d.ts.map +1 -0
- package/dist/src/routing/posterior-store.test.js +37 -0
- package/dist/src/routing/reward.d.ts +16 -0
- package/dist/src/routing/reward.d.ts.map +1 -0
- package/dist/src/routing/reward.js +29 -0
- package/dist/src/routing/reward.test.d.ts +2 -0
- package/dist/src/routing/reward.test.d.ts.map +1 -0
- package/dist/src/routing/reward.test.js +210 -0
- package/dist/src/routing/routing-engine.d.ts +20 -0
- package/dist/src/routing/routing-engine.d.ts.map +1 -0
- package/dist/src/routing/routing-engine.js +113 -0
- package/dist/src/routing/routing-engine.test.d.ts +2 -0
- package/dist/src/routing/routing-engine.test.d.ts.map +1 -0
- package/dist/src/routing/routing-engine.test.js +310 -0
- package/dist/src/routing/types.d.ts +157 -0
- package/dist/src/routing/types.d.ts.map +1 -0
- package/dist/src/routing/types.js +68 -0
- package/dist/src/routing/types.test.d.ts +2 -0
- package/dist/src/routing/types.test.d.ts.map +1 -0
- package/dist/src/routing/types.test.js +184 -0
- package/dist/src/templates/types.d.ts +3 -0
- package/dist/src/templates/types.d.ts.map +1 -1
- package/dist/src/templates/types.js +2 -0
- package/dist/src/workflow/agent-cancellation.d.ts +37 -0
- package/dist/src/workflow/agent-cancellation.d.ts.map +1 -0
- package/dist/src/workflow/agent-cancellation.js +41 -0
- package/dist/src/workflow/agent-cancellation.test.d.ts +2 -0
- package/dist/src/workflow/agent-cancellation.test.d.ts.map +1 -0
- package/dist/src/workflow/agent-cancellation.test.js +86 -0
- package/dist/src/workflow/concurrency-semaphore.d.ts +21 -0
- package/dist/src/workflow/concurrency-semaphore.d.ts.map +1 -0
- package/dist/src/workflow/concurrency-semaphore.js +46 -0
- package/dist/src/workflow/concurrency-semaphore.test.d.ts +2 -0
- package/dist/src/workflow/concurrency-semaphore.test.d.ts.map +1 -0
- package/dist/src/workflow/concurrency-semaphore.test.js +183 -0
- package/dist/src/workflow/gate-state.d.ts +115 -0
- package/dist/src/workflow/gate-state.d.ts.map +1 -0
- package/dist/src/workflow/gate-state.js +185 -0
- package/dist/src/workflow/gate-state.test.d.ts +2 -0
- package/dist/src/workflow/gate-state.test.d.ts.map +1 -0
- package/dist/src/workflow/gate-state.test.js +251 -0
- package/dist/src/workflow/gates/gate-evaluator.d.ts +119 -0
- package/dist/src/workflow/gates/gate-evaluator.d.ts.map +1 -0
- package/dist/src/workflow/gates/gate-evaluator.js +243 -0
- package/dist/src/workflow/gates/gate-evaluator.test.d.ts +2 -0
- package/dist/src/workflow/gates/gate-evaluator.test.d.ts.map +1 -0
- package/dist/src/workflow/gates/gate-evaluator.test.js +240 -0
- package/dist/src/workflow/gates/signal-gate.d.ts +114 -0
- package/dist/src/workflow/gates/signal-gate.d.ts.map +1 -0
- package/dist/src/workflow/gates/signal-gate.js +216 -0
- package/dist/src/workflow/gates/signal-gate.test.d.ts +2 -0
- package/dist/src/workflow/gates/signal-gate.test.d.ts.map +1 -0
- package/dist/src/workflow/gates/signal-gate.test.js +199 -0
- package/dist/src/workflow/gates/timeout-engine.d.ts +96 -0
- package/dist/src/workflow/gates/timeout-engine.d.ts.map +1 -0
- package/dist/src/workflow/gates/timeout-engine.js +162 -0
- package/dist/src/workflow/gates/timeout-engine.test.d.ts +2 -0
- package/dist/src/workflow/gates/timeout-engine.test.d.ts.map +1 -0
- package/dist/src/workflow/gates/timeout-engine.test.js +186 -0
- package/dist/src/workflow/gates/timer-gate.d.ts +125 -0
- package/dist/src/workflow/gates/timer-gate.d.ts.map +1 -0
- package/dist/src/workflow/gates/timer-gate.js +381 -0
- package/dist/src/workflow/gates/timer-gate.test.d.ts +2 -0
- package/dist/src/workflow/gates/timer-gate.test.d.ts.map +1 -0
- package/dist/src/workflow/gates/timer-gate.test.js +211 -0
- package/dist/src/workflow/gates/webhook-gate.d.ts +132 -0
- package/dist/src/workflow/gates/webhook-gate.d.ts.map +1 -0
- package/dist/src/workflow/gates/webhook-gate.js +216 -0
- package/dist/src/workflow/gates/webhook-gate.test.d.ts +2 -0
- package/dist/src/workflow/gates/webhook-gate.test.d.ts.map +1 -0
- package/dist/src/workflow/gates/webhook-gate.test.js +182 -0
- package/dist/src/workflow/index.d.ts +23 -2
- package/dist/src/workflow/index.d.ts.map +1 -1
- package/dist/src/workflow/index.js +15 -1
- package/dist/src/workflow/parallelism-executor.d.ts +25 -0
- package/dist/src/workflow/parallelism-executor.d.ts.map +1 -0
- package/dist/src/workflow/parallelism-executor.js +53 -0
- package/dist/src/workflow/parallelism-executor.test.d.ts +2 -0
- package/dist/src/workflow/parallelism-executor.test.d.ts.map +1 -0
- package/dist/src/workflow/parallelism-executor.test.js +191 -0
- package/dist/src/workflow/parallelism-types.d.ts +80 -0
- package/dist/src/workflow/parallelism-types.d.ts.map +1 -0
- package/dist/src/workflow/parallelism-types.js +8 -0
- package/dist/src/workflow/phase-context-injector.d.ts +29 -0
- package/dist/src/workflow/phase-context-injector.d.ts.map +1 -0
- package/dist/src/workflow/phase-context-injector.js +43 -0
- package/dist/src/workflow/phase-context-injector.test.d.ts +2 -0
- package/dist/src/workflow/phase-context-injector.test.d.ts.map +1 -0
- package/dist/src/workflow/phase-context-injector.test.js +123 -0
- package/dist/src/workflow/phase-output-collector.d.ts +39 -0
- package/dist/src/workflow/phase-output-collector.d.ts.map +1 -0
- package/dist/src/workflow/phase-output-collector.js +141 -0
- package/dist/src/workflow/phase-output-collector.test.d.ts +2 -0
- package/dist/src/workflow/phase-output-collector.test.d.ts.map +1 -0
- package/dist/src/workflow/phase-output-collector.test.js +179 -0
- package/dist/src/workflow/strategies/fan-in-strategy.d.ts +21 -0
- package/dist/src/workflow/strategies/fan-in-strategy.d.ts.map +1 -0
- package/dist/src/workflow/strategies/fan-in-strategy.js +92 -0
- package/dist/src/workflow/strategies/fan-in-strategy.test.d.ts +2 -0
- package/dist/src/workflow/strategies/fan-in-strategy.test.d.ts.map +1 -0
- package/dist/src/workflow/strategies/fan-in-strategy.test.js +182 -0
- package/dist/src/workflow/strategies/fan-out-strategy.d.ts +16 -0
- package/dist/src/workflow/strategies/fan-out-strategy.d.ts.map +1 -0
- package/dist/src/workflow/strategies/fan-out-strategy.js +47 -0
- package/dist/src/workflow/strategies/fan-out-strategy.test.d.ts +2 -0
- package/dist/src/workflow/strategies/fan-out-strategy.test.d.ts.map +1 -0
- package/dist/src/workflow/strategies/fan-out-strategy.test.js +97 -0
- package/dist/src/workflow/strategies/index.d.ts +4 -0
- package/dist/src/workflow/strategies/index.d.ts.map +1 -0
- package/dist/src/workflow/strategies/index.js +3 -0
- package/dist/src/workflow/strategies/race-strategy.d.ts +19 -0
- package/dist/src/workflow/strategies/race-strategy.d.ts.map +1 -0
- package/dist/src/workflow/strategies/race-strategy.js +92 -0
- package/dist/src/workflow/strategies/race-strategy.test.d.ts +2 -0
- package/dist/src/workflow/strategies/race-strategy.test.d.ts.map +1 -0
- package/dist/src/workflow/strategies/race-strategy.test.js +318 -0
- package/dist/src/workflow/transition-engine.d.ts.map +1 -1
- package/dist/src/workflow/transition-engine.js +12 -0
- package/dist/src/workflow/transition-engine.test.js +92 -0
- package/dist/src/workflow/workflow-registry.d.ts +5 -1
- package/dist/src/workflow/workflow-registry.d.ts.map +1 -1
- package/dist/src/workflow/workflow-registry.js +8 -0
- package/dist/src/workflow/workflow-registry.test.js +54 -0
- package/dist/src/workflow/workflow-types.d.ts +151 -6
- package/dist/src/workflow/workflow-types.d.ts.map +1 -1
- package/dist/src/workflow/workflow-types.js +71 -1
- package/dist/src/workflow/workflow-types.test.js +293 -2
- package/package.json +2 -2
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { loadRepositoryConfig, RepositoryConfigSchema, ProjectConfigSchema, ProvidersConfigSchema, getEffectiveAllowedProjects, getProjectConfig, getProjectPath, getProvidersConfig } from './repository-config.js';
|
|
1
|
+
export { loadRepositoryConfig, RepositoryConfigSchema, ProjectConfigSchema, ProvidersConfigSchema, RoutingConfigSectionSchema, getEffectiveAllowedProjects, getProjectConfig, getProjectPath, getProvidersConfig, getRoutingConfig } from './repository-config.js';
|
|
2
2
|
export type { RepositoryConfig, ProjectConfig } from './repository-config.js';
|
|
3
3
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/config/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,2BAA2B,EAAE,gBAAgB,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/config/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,0BAA0B,EAAE,2BAA2B,EAAE,gBAAgB,EAAE,cAAc,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAClQ,YAAY,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAA"}
|
package/dist/src/config/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { loadRepositoryConfig, RepositoryConfigSchema, ProjectConfigSchema, ProvidersConfigSchema, getEffectiveAllowedProjects, getProjectConfig, getProjectPath, getProvidersConfig } from './repository-config.js';
|
|
1
|
+
export { loadRepositoryConfig, RepositoryConfigSchema, ProjectConfigSchema, ProvidersConfigSchema, RoutingConfigSectionSchema, getEffectiveAllowedProjects, getProjectConfig, getProjectPath, getProvidersConfig, getRoutingConfig } from './repository-config.js';
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { z } from 'zod';
|
|
10
10
|
import type { ProvidersConfig } from '../providers/index.js';
|
|
11
|
+
import type { RoutingConfig } from '../routing/types.js';
|
|
11
12
|
/** Per-project configuration (object form of projectPaths values) */
|
|
12
13
|
export declare const ProjectConfigSchema: z.ZodObject<{
|
|
13
14
|
path: z.ZodString;
|
|
@@ -47,6 +48,15 @@ export declare const ProvidersConfigSchema: z.ZodObject<{
|
|
|
47
48
|
a2a: "a2a";
|
|
48
49
|
}>>>;
|
|
49
50
|
}, z.core.$strip>;
|
|
51
|
+
/** Routing configuration for MAB-based provider selection */
|
|
52
|
+
export declare const RoutingConfigSectionSchema: z.ZodObject<{
|
|
53
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
54
|
+
explorationRate: z.ZodDefault<z.ZodNumber>;
|
|
55
|
+
windowSize: z.ZodDefault<z.ZodNumber>;
|
|
56
|
+
discountFactor: z.ZodDefault<z.ZodNumber>;
|
|
57
|
+
minObservationsForExploit: z.ZodDefault<z.ZodNumber>;
|
|
58
|
+
changeDetectionThreshold: z.ZodDefault<z.ZodNumber>;
|
|
59
|
+
}, z.core.$strip>;
|
|
50
60
|
export declare const RepositoryConfigSchema: z.ZodObject<{
|
|
51
61
|
apiVersion: z.ZodString;
|
|
52
62
|
kind: z.ZodLiteral<"RepositoryConfig">;
|
|
@@ -100,6 +110,14 @@ export declare const RepositoryConfigSchema: z.ZodObject<{
|
|
|
100
110
|
a2a: "a2a";
|
|
101
111
|
}>>>;
|
|
102
112
|
}, z.core.$strip>>;
|
|
113
|
+
routing: z.ZodOptional<z.ZodObject<{
|
|
114
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
115
|
+
explorationRate: z.ZodDefault<z.ZodNumber>;
|
|
116
|
+
windowSize: z.ZodDefault<z.ZodNumber>;
|
|
117
|
+
discountFactor: z.ZodDefault<z.ZodNumber>;
|
|
118
|
+
minObservationsForExploit: z.ZodDefault<z.ZodNumber>;
|
|
119
|
+
changeDetectionThreshold: z.ZodDefault<z.ZodNumber>;
|
|
120
|
+
}, z.core.$strip>>;
|
|
103
121
|
mergeQueue: z.ZodOptional<z.ZodObject<{
|
|
104
122
|
provider: z.ZodDefault<z.ZodEnum<{
|
|
105
123
|
"github-native": "github-native";
|
|
@@ -138,6 +156,11 @@ export declare function getProjectPath(config: RepositoryConfig, projectName: st
|
|
|
138
156
|
* Convenience helper for passing to ProviderResolutionContext.configProviders.
|
|
139
157
|
*/
|
|
140
158
|
export declare function getProvidersConfig(config: RepositoryConfig): ProvidersConfig | undefined;
|
|
159
|
+
/**
|
|
160
|
+
* Returns the routing config from a RepositoryConfig, if present.
|
|
161
|
+
* Convenience helper for passing to async provider resolution.
|
|
162
|
+
*/
|
|
163
|
+
export declare function getRoutingConfig(config: RepositoryConfig): RoutingConfig | undefined;
|
|
141
164
|
/**
|
|
142
165
|
* Load and validate .agentfactory/config.yaml from the given git root.
|
|
143
166
|
*
|
|
@@ -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;
|
|
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;AAC5D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAMxD,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,6DAA6D;AAC7D,eAAO,MAAM,0BAA0B;;;;;;;iBAarC,CAAA;AAEF,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA4ElC,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;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,gBAAgB,GAAG,aAAa,GAAG,SAAS,CAEpF;AAMD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAQ7E"}
|
|
@@ -42,6 +42,21 @@ export const ProvidersConfigSchema = z.object({
|
|
|
42
42
|
/** Provider overrides by project name (e.g., { Social: 'codex' }) */
|
|
43
43
|
byProject: z.record(z.string(), AgentProviderNameSchema).optional(),
|
|
44
44
|
});
|
|
45
|
+
/** Routing configuration for MAB-based provider selection */
|
|
46
|
+
export const RoutingConfigSectionSchema = z.object({
|
|
47
|
+
/** Enable MAB-based intelligent routing (default: false) */
|
|
48
|
+
enabled: z.boolean().default(false),
|
|
49
|
+
/** Exploration rate for Thompson Sampling (0-1, default: 0.1) */
|
|
50
|
+
explorationRate: z.number().min(0).max(1).default(0.1),
|
|
51
|
+
/** Observation window size (default: 100) */
|
|
52
|
+
windowSize: z.number().int().positive().default(100),
|
|
53
|
+
/** Discount factor for older observations (default: 0.99) */
|
|
54
|
+
discountFactor: z.number().min(0).max(1).default(0.99),
|
|
55
|
+
/** Minimum observations before exploiting (default: 5) */
|
|
56
|
+
minObservationsForExploit: z.number().int().min(0).default(5),
|
|
57
|
+
/** Change detection threshold (default: 0.2) */
|
|
58
|
+
changeDetectionThreshold: z.number().min(0).default(0.2),
|
|
59
|
+
});
|
|
45
60
|
export const RepositoryConfigSchema = z.object({
|
|
46
61
|
apiVersion: z.string(),
|
|
47
62
|
kind: z.literal('RepositoryConfig'),
|
|
@@ -90,6 +105,11 @@ export const RepositoryConfigSchema = z.object({
|
|
|
90
105
|
* Allows routing agents to different providers by work type or project.
|
|
91
106
|
*/
|
|
92
107
|
providers: ProvidersConfigSchema.optional(),
|
|
108
|
+
/**
|
|
109
|
+
* Routing configuration for MAB-based intelligent provider selection.
|
|
110
|
+
* When enabled, Thompson Sampling is used to learn optimal provider routing.
|
|
111
|
+
*/
|
|
112
|
+
routing: RoutingConfigSectionSchema.optional(),
|
|
93
113
|
/**
|
|
94
114
|
* Merge queue configuration.
|
|
95
115
|
* Controls which merge queue provider agents use for automated merging.
|
|
@@ -165,6 +185,13 @@ export function getProjectPath(config, projectName) {
|
|
|
165
185
|
export function getProvidersConfig(config) {
|
|
166
186
|
return config.providers;
|
|
167
187
|
}
|
|
188
|
+
/**
|
|
189
|
+
* Returns the routing config from a RepositoryConfig, if present.
|
|
190
|
+
* Convenience helper for passing to async provider resolution.
|
|
191
|
+
*/
|
|
192
|
+
export function getRoutingConfig(config) {
|
|
193
|
+
return config.routing;
|
|
194
|
+
}
|
|
168
195
|
// ---------------------------------------------------------------------------
|
|
169
196
|
// Loader
|
|
170
197
|
// ---------------------------------------------------------------------------
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
2
|
import { existsSync, readFileSync } from 'fs';
|
|
3
|
-
import { loadRepositoryConfig, RepositoryConfigSchema, getEffectiveAllowedProjects, getProjectConfig, getProjectPath, getProvidersConfig, ProvidersConfigSchema } from './repository-config.js';
|
|
3
|
+
import { loadRepositoryConfig, RepositoryConfigSchema, getEffectiveAllowedProjects, getProjectConfig, getProjectPath, getProvidersConfig, getRoutingConfig, ProvidersConfigSchema, RoutingConfigSectionSchema } from './repository-config.js';
|
|
4
4
|
vi.mock('fs', () => ({
|
|
5
5
|
existsSync: vi.fn(),
|
|
6
6
|
readFileSync: vi.fn(),
|
|
@@ -548,3 +548,142 @@ describe('getProvidersConfig', () => {
|
|
|
548
548
|
expect(getProvidersConfig(config)).toBeUndefined();
|
|
549
549
|
});
|
|
550
550
|
});
|
|
551
|
+
describe('RoutingConfigSectionSchema', () => {
|
|
552
|
+
it('validates a complete routing config', () => {
|
|
553
|
+
const result = RoutingConfigSectionSchema.parse({
|
|
554
|
+
enabled: true,
|
|
555
|
+
explorationRate: 0.2,
|
|
556
|
+
windowSize: 200,
|
|
557
|
+
discountFactor: 0.95,
|
|
558
|
+
minObservationsForExploit: 10,
|
|
559
|
+
changeDetectionThreshold: 0.3,
|
|
560
|
+
});
|
|
561
|
+
expect(result.enabled).toBe(true);
|
|
562
|
+
expect(result.explorationRate).toBe(0.2);
|
|
563
|
+
expect(result.windowSize).toBe(200);
|
|
564
|
+
expect(result.discountFactor).toBe(0.95);
|
|
565
|
+
expect(result.minObservationsForExploit).toBe(10);
|
|
566
|
+
expect(result.changeDetectionThreshold).toBe(0.3);
|
|
567
|
+
});
|
|
568
|
+
it('applies defaults when only enabled is provided', () => {
|
|
569
|
+
const result = RoutingConfigSectionSchema.parse({ enabled: true });
|
|
570
|
+
expect(result.enabled).toBe(true);
|
|
571
|
+
expect(result.explorationRate).toBe(0.1);
|
|
572
|
+
expect(result.windowSize).toBe(100);
|
|
573
|
+
expect(result.discountFactor).toBe(0.99);
|
|
574
|
+
expect(result.minObservationsForExploit).toBe(5);
|
|
575
|
+
expect(result.changeDetectionThreshold).toBe(0.2);
|
|
576
|
+
});
|
|
577
|
+
it('applies defaults for an empty object (enabled defaults to false)', () => {
|
|
578
|
+
const result = RoutingConfigSectionSchema.parse({});
|
|
579
|
+
expect(result.enabled).toBe(false);
|
|
580
|
+
expect(result.explorationRate).toBe(0.1);
|
|
581
|
+
});
|
|
582
|
+
it('rejects explorationRate outside 0-1 range', () => {
|
|
583
|
+
expect(() => RoutingConfigSectionSchema.parse({ enabled: true, explorationRate: 1.5 })).toThrow();
|
|
584
|
+
expect(() => RoutingConfigSectionSchema.parse({ enabled: true, explorationRate: -0.1 })).toThrow();
|
|
585
|
+
});
|
|
586
|
+
it('rejects non-positive windowSize', () => {
|
|
587
|
+
expect(() => RoutingConfigSectionSchema.parse({ enabled: true, windowSize: 0 })).toThrow();
|
|
588
|
+
expect(() => RoutingConfigSectionSchema.parse({ enabled: true, windowSize: -1 })).toThrow();
|
|
589
|
+
});
|
|
590
|
+
it('rejects discountFactor outside 0-1 range', () => {
|
|
591
|
+
expect(() => RoutingConfigSectionSchema.parse({ enabled: true, discountFactor: 1.1 })).toThrow();
|
|
592
|
+
});
|
|
593
|
+
it('rejects negative minObservationsForExploit', () => {
|
|
594
|
+
expect(() => RoutingConfigSectionSchema.parse({ enabled: true, minObservationsForExploit: -1 })).toThrow();
|
|
595
|
+
});
|
|
596
|
+
it('rejects negative changeDetectionThreshold', () => {
|
|
597
|
+
expect(() => RoutingConfigSectionSchema.parse({ enabled: true, changeDetectionThreshold: -0.1 })).toThrow();
|
|
598
|
+
});
|
|
599
|
+
});
|
|
600
|
+
describe('RepositoryConfigSchema with routing', () => {
|
|
601
|
+
it('validates config with routing field', () => {
|
|
602
|
+
const result = RepositoryConfigSchema.parse({
|
|
603
|
+
apiVersion: 'v1',
|
|
604
|
+
kind: 'RepositoryConfig',
|
|
605
|
+
routing: {
|
|
606
|
+
enabled: true,
|
|
607
|
+
explorationRate: 0.05,
|
|
608
|
+
},
|
|
609
|
+
});
|
|
610
|
+
expect(result.routing).toBeDefined();
|
|
611
|
+
expect(result.routing.enabled).toBe(true);
|
|
612
|
+
expect(result.routing.explorationRate).toBe(0.05);
|
|
613
|
+
// Defaults should be applied
|
|
614
|
+
expect(result.routing.windowSize).toBe(100);
|
|
615
|
+
});
|
|
616
|
+
it('validates config without routing field', () => {
|
|
617
|
+
const result = RepositoryConfigSchema.parse({
|
|
618
|
+
apiVersion: 'v1',
|
|
619
|
+
kind: 'RepositoryConfig',
|
|
620
|
+
});
|
|
621
|
+
expect(result.routing).toBeUndefined();
|
|
622
|
+
});
|
|
623
|
+
it('validates config with both providers and routing', () => {
|
|
624
|
+
const result = RepositoryConfigSchema.parse({
|
|
625
|
+
apiVersion: 'v1',
|
|
626
|
+
kind: 'RepositoryConfig',
|
|
627
|
+
providers: { default: 'codex' },
|
|
628
|
+
routing: { enabled: true },
|
|
629
|
+
});
|
|
630
|
+
expect(result.providers).toEqual({ default: 'codex' });
|
|
631
|
+
expect(result.routing.enabled).toBe(true);
|
|
632
|
+
});
|
|
633
|
+
});
|
|
634
|
+
describe('loadRepositoryConfig with routing', () => {
|
|
635
|
+
beforeEach(() => {
|
|
636
|
+
vi.clearAllMocks();
|
|
637
|
+
});
|
|
638
|
+
afterEach(() => {
|
|
639
|
+
vi.restoreAllMocks();
|
|
640
|
+
});
|
|
641
|
+
it('parses config with routing section', () => {
|
|
642
|
+
mockExistsSync.mockReturnValue(true);
|
|
643
|
+
mockReadFileSync.mockReturnValue(`apiVersion: v1
|
|
644
|
+
kind: RepositoryConfig
|
|
645
|
+
routing:
|
|
646
|
+
enabled: true
|
|
647
|
+
explorationRate: 0.05
|
|
648
|
+
windowSize: 50
|
|
649
|
+
`);
|
|
650
|
+
const result = loadRepositoryConfig('/some/repo');
|
|
651
|
+
expect(result?.routing).toBeDefined();
|
|
652
|
+
expect(result?.routing?.enabled).toBe(true);
|
|
653
|
+
expect(result?.routing?.explorationRate).toBe(0.05);
|
|
654
|
+
expect(result?.routing?.windowSize).toBe(50);
|
|
655
|
+
// Defaults applied for unspecified fields
|
|
656
|
+
expect(result?.routing?.discountFactor).toBe(0.99);
|
|
657
|
+
expect(result?.routing?.minObservationsForExploit).toBe(5);
|
|
658
|
+
expect(result?.routing?.changeDetectionThreshold).toBe(0.2);
|
|
659
|
+
});
|
|
660
|
+
it('parses config with routing disabled (default)', () => {
|
|
661
|
+
mockExistsSync.mockReturnValue(true);
|
|
662
|
+
mockReadFileSync.mockReturnValue(`apiVersion: v1
|
|
663
|
+
kind: RepositoryConfig
|
|
664
|
+
routing:
|
|
665
|
+
enabled: false
|
|
666
|
+
`);
|
|
667
|
+
const result = loadRepositoryConfig('/some/repo');
|
|
668
|
+
expect(result?.routing?.enabled).toBe(false);
|
|
669
|
+
});
|
|
670
|
+
});
|
|
671
|
+
describe('getRoutingConfig', () => {
|
|
672
|
+
it('returns routing config when present', () => {
|
|
673
|
+
const config = RepositoryConfigSchema.parse({
|
|
674
|
+
apiVersion: 'v1',
|
|
675
|
+
kind: 'RepositoryConfig',
|
|
676
|
+
routing: { enabled: true },
|
|
677
|
+
});
|
|
678
|
+
const routing = getRoutingConfig(config);
|
|
679
|
+
expect(routing).toBeDefined();
|
|
680
|
+
expect(routing.enabled).toBe(true);
|
|
681
|
+
});
|
|
682
|
+
it('returns undefined when routing not set', () => {
|
|
683
|
+
const config = RepositoryConfigSchema.parse({
|
|
684
|
+
apiVersion: 'v1',
|
|
685
|
+
kind: 'RepositoryConfig',
|
|
686
|
+
});
|
|
687
|
+
expect(getRoutingConfig(config)).toBeUndefined();
|
|
688
|
+
});
|
|
689
|
+
});
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import type { GovernorAction, GovernorConfig, GovernorIssue } from './governor-types.js';
|
|
11
11
|
import type { WorkflowRegistry } from '../workflow/workflow-registry.js';
|
|
12
|
+
import type { GateEvaluationResult } from '../workflow/gates/gate-evaluator.js';
|
|
12
13
|
/**
|
|
13
14
|
* All external state the Governor gathers before asking the decision engine
|
|
14
15
|
* what to do. Callers are responsible for populating this context; the
|
|
@@ -32,6 +33,8 @@ export interface DecisionContext {
|
|
|
32
33
|
* switch statement. Falls back to the switch statement when absent.
|
|
33
34
|
*/
|
|
34
35
|
workflowRegistry?: WorkflowRegistry;
|
|
36
|
+
/** Gate evaluation result from the gate system (Phase 4) */
|
|
37
|
+
gateEvaluation?: GateEvaluationResult;
|
|
35
38
|
}
|
|
36
39
|
/** Max agent sessions before the circuit breaker trips and the issue is held */
|
|
37
40
|
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;AAMxF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,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;AAExE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qCAAqC,CAAA;AAM/E;;;;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;IACnC,4DAA4D;IAC5D,cAAc,CAAC,EAAE,oBAAoB,CAAA;CACtC;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,CAkHjE"}
|
|
@@ -47,6 +47,17 @@ export function decideAction(ctx) {
|
|
|
47
47
|
if (ctx.isHeld) {
|
|
48
48
|
return { action: 'none', reason: `Issue ${issue.identifier} is held (HOLD override active)` };
|
|
49
49
|
}
|
|
50
|
+
// --- Gate check (Phase 4) ---
|
|
51
|
+
// When the governor has evaluated gates for the current phase and any are
|
|
52
|
+
// unsatisfied, block the transition. The governor populates gateEvaluation
|
|
53
|
+
// via the gate evaluator before calling decideAction().
|
|
54
|
+
if (ctx.gateEvaluation && !ctx.gateEvaluation.allSatisfied) {
|
|
55
|
+
const activeNames = ctx.gateEvaluation.activeGates.map(g => g.gateName).join(', ');
|
|
56
|
+
return {
|
|
57
|
+
action: 'none',
|
|
58
|
+
reason: `Issue ${issue.identifier} has unsatisfied gates: ${activeNames}`,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
50
61
|
// --- Circuit breaker ---
|
|
51
62
|
// Prevent issues from cycling through agents indefinitely.
|
|
52
63
|
// If an issue has had too many sessions without reaching a terminal status,
|
|
@@ -627,3 +627,36 @@ describe('decideAction — escalation strategy effects', () => {
|
|
|
627
627
|
expect(decideAction(ctx).action).toBe('trigger-acceptance');
|
|
628
628
|
});
|
|
629
629
|
});
|
|
630
|
+
// ---------------------------------------------------------------------------
|
|
631
|
+
// Gate check
|
|
632
|
+
// ---------------------------------------------------------------------------
|
|
633
|
+
describe('decideAction — gate check', () => {
|
|
634
|
+
it('returns none when gates are unsatisfied', () => {
|
|
635
|
+
const ctx = makeContext({
|
|
636
|
+
gateEvaluation: {
|
|
637
|
+
allSatisfied: false,
|
|
638
|
+
activeGates: [{ issueId: 'issue-1', gateName: 'human-review', gateType: 'signal', status: 'active', activatedAt: Date.now() }],
|
|
639
|
+
newlySatisfied: [],
|
|
640
|
+
timeoutResolutions: [],
|
|
641
|
+
reason: 'Unsatisfied gates: human-review',
|
|
642
|
+
},
|
|
643
|
+
});
|
|
644
|
+
const result = decideAction(ctx);
|
|
645
|
+
expect(result.action).toBe('none');
|
|
646
|
+
expect(result.reason).toContain('unsatisfied gates');
|
|
647
|
+
});
|
|
648
|
+
it('proceeds when all gates are satisfied', () => {
|
|
649
|
+
const ctx = makeContext({
|
|
650
|
+
issue: makeIssue({ status: 'Backlog' }),
|
|
651
|
+
gateEvaluation: {
|
|
652
|
+
allSatisfied: true,
|
|
653
|
+
activeGates: [],
|
|
654
|
+
newlySatisfied: [],
|
|
655
|
+
timeoutResolutions: [],
|
|
656
|
+
reason: 'All gates satisfied',
|
|
657
|
+
},
|
|
658
|
+
});
|
|
659
|
+
const result = decideAction(ctx);
|
|
660
|
+
expect(result.action).toBe('trigger-development');
|
|
661
|
+
});
|
|
662
|
+
});
|
|
@@ -20,7 +20,7 @@ import type { TopOfFunnelConfig } from './top-of-funnel.js';
|
|
|
20
20
|
* - escalate-human: Create a human escalation touchpoint
|
|
21
21
|
* - none: No action needed
|
|
22
22
|
*/
|
|
23
|
-
export type GovernorAction = 'trigger-research' | 'trigger-backlog-creation' | 'trigger-development' | 'trigger-qa' | 'trigger-acceptance' | 'trigger-refinement' | 'decompose' | 'escalate-human' | 'none';
|
|
23
|
+
export type GovernorAction = 'trigger-research' | 'trigger-backlog-creation' | 'trigger-development' | 'trigger-qa' | 'trigger-acceptance' | 'trigger-refinement' | 'trigger-parallel-group' | 'decompose' | 'escalate-human' | 'none';
|
|
24
24
|
/**
|
|
25
25
|
* Configuration for the Workflow Governor scan loop.
|
|
26
26
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"governor-types.d.ts","sourceRoot":"","sources":["../../../src/governor/governor-types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAM3D;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,cAAc,GACtB,kBAAkB,GAClB,0BAA0B,GAC1B,qBAAqB,GACrB,YAAY,GACZ,oBAAoB,GACpB,oBAAoB,GACpB,WAAW,GACX,gBAAgB,GAChB,MAAM,CAAA;AAMV;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,uBAAuB;IACvB,QAAQ,EAAE,MAAM,EAAE,CAAA;IAClB,qDAAqD;IACrD,cAAc,EAAE,MAAM,CAAA;IACtB,0DAA0D;IAC1D,uBAAuB,EAAE,MAAM,CAAA;IAC/B,uDAAuD;IACvD,kBAAkB,EAAE,OAAO,CAAA;IAC3B,+DAA+D;IAC/D,yBAAyB,EAAE,OAAO,CAAA;IAClC,2DAA2D;IAC3D,qBAAqB,EAAE,OAAO,CAAA;IAC9B,mDAAmD;IACnD,YAAY,EAAE,OAAO,CAAA;IACrB,4DAA4D;IAC5D,oBAAoB,EAAE,OAAO,CAAA;IAC7B,gEAAgE;IAChE,sBAAsB,EAAE,MAAM,CAAA;IAC9B,4CAA4C;IAC5C,WAAW,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAA;CACzC;AAED;;GAEG;AACH,eAAO,MAAM,uBAAuB,EAAE,cAUrC,CAAA;AAMD;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAA;IACf,aAAa,EAAE,MAAM,CAAA;IACrB,iBAAiB,EAAE,MAAM,CAAA;IACzB,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACnC,MAAM,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAClD;AAMD;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAA;IACV,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB"}
|
|
1
|
+
{"version":3,"file":"governor-types.d.ts","sourceRoot":"","sources":["../../../src/governor/governor-types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAM3D;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,cAAc,GACtB,kBAAkB,GAClB,0BAA0B,GAC1B,qBAAqB,GACrB,YAAY,GACZ,oBAAoB,GACpB,oBAAoB,GACpB,wBAAwB,GACxB,WAAW,GACX,gBAAgB,GAChB,MAAM,CAAA;AAMV;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,uBAAuB;IACvB,QAAQ,EAAE,MAAM,EAAE,CAAA;IAClB,qDAAqD;IACrD,cAAc,EAAE,MAAM,CAAA;IACtB,0DAA0D;IAC1D,uBAAuB,EAAE,MAAM,CAAA;IAC/B,uDAAuD;IACvD,kBAAkB,EAAE,OAAO,CAAA;IAC3B,+DAA+D;IAC/D,yBAAyB,EAAE,OAAO,CAAA;IAClC,2DAA2D;IAC3D,qBAAqB,EAAE,OAAO,CAAA;IAC9B,mDAAmD;IACnD,YAAY,EAAE,OAAO,CAAA;IACrB,4DAA4D;IAC5D,oBAAoB,EAAE,OAAO,CAAA;IAC7B,gEAAgE;IAChE,sBAAsB,EAAE,MAAM,CAAA;IAC9B,4CAA4C;IAC5C,WAAW,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAA;CACzC;AAED;;GAEG;AACH,eAAO,MAAM,uBAAuB,EAAE,cAUrC,CAAA;AAMD;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAA;IACf,aAAa,EAAE,MAAM,CAAA;IACrB,iBAAiB,EAAE,MAAM,CAAA;IACzB,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACnC,MAAM,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAClD;AAMD;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAA;IACV,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB"}
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
import type { GovernorAction, GovernorConfig, GovernorIssue, ScanResult } from './governor-types.js';
|
|
12
12
|
import type { OverridePriority } from './override-parser.js';
|
|
13
13
|
import { type WorkflowRegistryConfig } from '../workflow/workflow-registry.js';
|
|
14
|
+
import { ParallelismExecutor } from '../workflow/parallelism-executor.js';
|
|
14
15
|
/**
|
|
15
16
|
* Abstract dependencies that the Governor needs to interact with
|
|
16
17
|
* external systems. Callers inject these at construction time.
|
|
@@ -41,6 +42,8 @@ export interface GovernorDependencies {
|
|
|
41
42
|
getCompletedSessionCount: (issueId: string) => Promise<number>;
|
|
42
43
|
/** Dispatch work for an issue with a specific action */
|
|
43
44
|
dispatchWork: (issue: GovernorIssue, action: GovernorAction) => Promise<void>;
|
|
45
|
+
/** Get sub-issues for a parent issue (for parallel dispatch) */
|
|
46
|
+
getSubIssues?: (parentId: string) => Promise<GovernorIssue[]>;
|
|
44
47
|
}
|
|
45
48
|
/**
|
|
46
49
|
* The Workflow Governor scans projects on a configurable interval,
|
|
@@ -55,12 +58,13 @@ export declare class WorkflowGovernor {
|
|
|
55
58
|
private readonly deps;
|
|
56
59
|
private readonly callbacks;
|
|
57
60
|
private readonly workflowRegistry;
|
|
61
|
+
private readonly parallelismExecutor;
|
|
58
62
|
private intervalHandle;
|
|
59
63
|
private running;
|
|
60
64
|
private scanning;
|
|
61
65
|
constructor(config: Partial<GovernorConfig> & {
|
|
62
66
|
workflow?: WorkflowRegistryConfig;
|
|
63
|
-
}, deps: GovernorDependencies, callbacks?: WorkflowGovernorCallbacks);
|
|
67
|
+
}, deps: GovernorDependencies, callbacks?: WorkflowGovernorCallbacks, parallelismExecutor?: ParallelismExecutor);
|
|
64
68
|
/**
|
|
65
69
|
* Start the scan loop. Runs `scanOnce()` immediately, then repeats
|
|
66
70
|
* on the configured interval.
|
|
@@ -100,5 +104,17 @@ export declare class WorkflowGovernor {
|
|
|
100
104
|
* Gather context for a single issue and run it through the decision engine.
|
|
101
105
|
*/
|
|
102
106
|
private evaluateIssue;
|
|
107
|
+
/**
|
|
108
|
+
* Handle dispatching work for a parallel group.
|
|
109
|
+
*
|
|
110
|
+
* Fetches sub-issues for the parent, finds the applicable parallelism group,
|
|
111
|
+
* and executes the group using the ParallelismExecutor.
|
|
112
|
+
*/
|
|
113
|
+
private handleParallelGroupDispatch;
|
|
114
|
+
/**
|
|
115
|
+
* Handle post-parallel-group completion: if all tasks succeeded,
|
|
116
|
+
* evaluate transitions to advance the parent issue to the next phase.
|
|
117
|
+
*/
|
|
118
|
+
private handleParallelGroupCompletion;
|
|
103
119
|
}
|
|
104
120
|
//# sourceMappingURL=governor.d.ts.map
|
|
@@ -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;AAC5D,OAAO,EAAoB,KAAK,sBAAsB,EAAE,MAAM,kCAAkC,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;AAChG,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAA;AAyCzE;;;;;;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;IAC7E,gEAAgE;IAChE,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,aAAa,EAAE,CAAC,CAAA;CAC9D;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,QAAQ,CAAC,mBAAmB,CAAqB;IACzD,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,EACrC,mBAAmB,CAAC,EAAE,mBAAmB;IAuB3C;;;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;IAyHzB;;OAEG;YACW,aAAa;IAuC3B;;;;;OAKG;YACW,2BAA2B;IAmDzC;;;OAGG;YACW,6BAA6B;CA4C5C"}
|
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
import { DEFAULT_GOVERNOR_CONFIG } from './governor-types.js';
|
|
12
12
|
import { decideAction } from './decision-engine.js';
|
|
13
13
|
import { WorkflowRegistry } from '../workflow/workflow-registry.js';
|
|
14
|
+
import { ParallelismExecutor } from '../workflow/parallelism-executor.js';
|
|
15
|
+
import { FanOutStrategy, FanInStrategy, RaceStrategy } from '../workflow/strategies/index.js';
|
|
16
|
+
import { evaluateTransitions } from '../workflow/transition-engine.js';
|
|
14
17
|
// ---------------------------------------------------------------------------
|
|
15
18
|
// Logging
|
|
16
19
|
// ---------------------------------------------------------------------------
|
|
@@ -41,15 +44,26 @@ export class WorkflowGovernor {
|
|
|
41
44
|
deps;
|
|
42
45
|
callbacks;
|
|
43
46
|
workflowRegistry;
|
|
47
|
+
parallelismExecutor;
|
|
44
48
|
intervalHandle = null;
|
|
45
49
|
running = false;
|
|
46
50
|
scanning = false;
|
|
47
|
-
constructor(config, deps, callbacks) {
|
|
51
|
+
constructor(config, deps, callbacks, parallelismExecutor) {
|
|
48
52
|
const { workflow: workflowConfig, ...governorConfig } = config;
|
|
49
53
|
this.config = { ...DEFAULT_GOVERNOR_CONFIG, ...governorConfig };
|
|
50
54
|
this.deps = deps;
|
|
51
55
|
this.callbacks = callbacks ?? {};
|
|
52
56
|
this.workflowRegistry = WorkflowRegistry.create(workflowConfig);
|
|
57
|
+
// Use the provided executor or create a default one with all strategies registered
|
|
58
|
+
if (parallelismExecutor) {
|
|
59
|
+
this.parallelismExecutor = parallelismExecutor;
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
this.parallelismExecutor = new ParallelismExecutor();
|
|
63
|
+
this.parallelismExecutor.registerStrategy('fan-out', new FanOutStrategy());
|
|
64
|
+
this.parallelismExecutor.registerStrategy('fan-in', new FanInStrategy());
|
|
65
|
+
this.parallelismExecutor.registerStrategy('race', new RaceStrategy());
|
|
66
|
+
}
|
|
53
67
|
}
|
|
54
68
|
// -------------------------------------------------------------------------
|
|
55
69
|
// Lifecycle
|
|
@@ -207,6 +221,18 @@ export class WorkflowGovernor {
|
|
|
207
221
|
break;
|
|
208
222
|
}
|
|
209
223
|
try {
|
|
224
|
+
// Handle parallel group dispatch separately
|
|
225
|
+
if (item.action === 'trigger-parallel-group') {
|
|
226
|
+
await this.handleParallelGroupDispatch(item.issue);
|
|
227
|
+
result.actionsDispatched++;
|
|
228
|
+
log.info('Dispatched parallel group action', {
|
|
229
|
+
issueIdentifier: item.issue.identifier,
|
|
230
|
+
action: item.action,
|
|
231
|
+
reason: item.reason,
|
|
232
|
+
priority: item.priority ?? 'none',
|
|
233
|
+
});
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
210
236
|
await this.deps.dispatchWork(item.issue, item.action);
|
|
211
237
|
result.actionsDispatched++;
|
|
212
238
|
log.info('Dispatched action', {
|
|
@@ -264,4 +290,89 @@ export class WorkflowGovernor {
|
|
|
264
290
|
};
|
|
265
291
|
return decideAction(ctx);
|
|
266
292
|
}
|
|
293
|
+
/**
|
|
294
|
+
* Handle dispatching work for a parallel group.
|
|
295
|
+
*
|
|
296
|
+
* Fetches sub-issues for the parent, finds the applicable parallelism group,
|
|
297
|
+
* and executes the group using the ParallelismExecutor.
|
|
298
|
+
*/
|
|
299
|
+
async handleParallelGroupDispatch(issue) {
|
|
300
|
+
if (!this.deps.getSubIssues) {
|
|
301
|
+
log.warn('getSubIssues dependency not provided, falling back to normal dispatch');
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
const workflow = this.workflowRegistry.getWorkflow();
|
|
305
|
+
if (!workflow?.parallelism)
|
|
306
|
+
return;
|
|
307
|
+
const subIssues = await this.deps.getSubIssues(issue.id);
|
|
308
|
+
// Find the applicable parallelism group.
|
|
309
|
+
// Use the first group whose phases include a phase that can be
|
|
310
|
+
// resolved from the current workflow transitions.
|
|
311
|
+
const group = workflow.parallelism.find(g => g.phases.length > 0);
|
|
312
|
+
if (!group)
|
|
313
|
+
return;
|
|
314
|
+
const tasks = subIssues.map(sub => ({
|
|
315
|
+
id: sub.id,
|
|
316
|
+
issueId: sub.identifier,
|
|
317
|
+
phaseName: group.phases[0],
|
|
318
|
+
}));
|
|
319
|
+
const result = await this.parallelismExecutor.execute(group, tasks, async (task) => {
|
|
320
|
+
await this.deps.dispatchWork(subIssues.find(s => s.id === task.id), `trigger-${task.phaseName}`);
|
|
321
|
+
return {
|
|
322
|
+
id: task.id,
|
|
323
|
+
issueId: task.issueId,
|
|
324
|
+
success: true,
|
|
325
|
+
};
|
|
326
|
+
});
|
|
327
|
+
log.info('Parallel group dispatched', {
|
|
328
|
+
group: group.name,
|
|
329
|
+
strategy: group.strategy,
|
|
330
|
+
completed: result.completed.length,
|
|
331
|
+
failed: result.failed.length,
|
|
332
|
+
cancelled: result.cancelled.length,
|
|
333
|
+
});
|
|
334
|
+
// Propagate result to advance parent issue if group completed successfully
|
|
335
|
+
await this.handleParallelGroupCompletion(issue, result, group);
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Handle post-parallel-group completion: if all tasks succeeded,
|
|
339
|
+
* evaluate transitions to advance the parent issue to the next phase.
|
|
340
|
+
*/
|
|
341
|
+
async handleParallelGroupCompletion(parentIssue, result, group) {
|
|
342
|
+
// Only advance if the group completed with no failures
|
|
343
|
+
const allSucceeded = result.failed.length === 0 && result.completed.length > 0
|
|
344
|
+
&& result.completed.every(t => t.success);
|
|
345
|
+
if (!allSucceeded) {
|
|
346
|
+
log.info('Parallel group has failures, not advancing parent', {
|
|
347
|
+
group: group.name,
|
|
348
|
+
failedCount: result.failed.length,
|
|
349
|
+
});
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
// Evaluate the next transition for the parent issue.
|
|
353
|
+
// After a parallel development group completes, the parent
|
|
354
|
+
// should transition based on its next status mapping.
|
|
355
|
+
const nextPhaseResult = evaluateTransitions({
|
|
356
|
+
issue: { ...parentIssue, status: 'Finished' },
|
|
357
|
+
registry: this.workflowRegistry,
|
|
358
|
+
isParentIssue: true,
|
|
359
|
+
});
|
|
360
|
+
if (nextPhaseResult.action !== 'none') {
|
|
361
|
+
log.info('Advancing parent after parallel group completion', {
|
|
362
|
+
issueIdentifier: parentIssue.identifier,
|
|
363
|
+
nextAction: nextPhaseResult.action,
|
|
364
|
+
reason: nextPhaseResult.reason,
|
|
365
|
+
});
|
|
366
|
+
try {
|
|
367
|
+
await this.deps.dispatchWork(parentIssue, nextPhaseResult.action);
|
|
368
|
+
}
|
|
369
|
+
catch (err) {
|
|
370
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
371
|
+
log.error('Failed to advance parent after parallel group', {
|
|
372
|
+
issueIdentifier: parentIssue.identifier,
|
|
373
|
+
error: errorMsg,
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
267
378
|
}
|