@generacy-ai/orchestrator 0.0.0-preview-20260304013206
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/LICENSE +191 -0
- package/dist/auth/api-key.d.ts +63 -0
- package/dist/auth/api-key.d.ts.map +1 -0
- package/dist/auth/api-key.js +102 -0
- package/dist/auth/api-key.js.map +1 -0
- package/dist/auth/github-oauth.d.ts +29 -0
- package/dist/auth/github-oauth.d.ts.map +1 -0
- package/dist/auth/github-oauth.js +136 -0
- package/dist/auth/github-oauth.js.map +1 -0
- package/dist/auth/index.d.ts +5 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +9 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/jwt.d.ts +34 -0
- package/dist/auth/jwt.d.ts.map +1 -0
- package/dist/auth/jwt.js +74 -0
- package/dist/auth/jwt.js.map +1 -0
- package/dist/auth/middleware.d.ts +43 -0
- package/dist/auth/middleware.d.ts.map +1 -0
- package/dist/auth/middleware.js +101 -0
- package/dist/auth/middleware.js.map +1 -0
- package/dist/config/index.d.ts +3 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +5 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/loader.d.ts +21 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +295 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/schema.d.ts +733 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +174 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +33 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/error-handler.d.ts +30 -0
- package/dist/middleware/error-handler.d.ts.map +1 -0
- package/dist/middleware/error-handler.js +173 -0
- package/dist/middleware/error-handler.js.map +1 -0
- package/dist/middleware/index.d.ts +4 -0
- package/dist/middleware/index.d.ts.map +1 -0
- package/dist/middleware/index.js +4 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/middleware/rate-limit.d.ts +32 -0
- package/dist/middleware/rate-limit.d.ts.map +1 -0
- package/dist/middleware/rate-limit.js +92 -0
- package/dist/middleware/rate-limit.js.map +1 -0
- package/dist/middleware/request-logger.d.ts +31 -0
- package/dist/middleware/request-logger.d.ts.map +1 -0
- package/dist/middleware/request-logger.js +89 -0
- package/dist/middleware/request-logger.js.map +1 -0
- package/dist/routes/agents.d.ts +7 -0
- package/dist/routes/agents.d.ts.map +1 -0
- package/dist/routes/agents.js +78 -0
- package/dist/routes/agents.js.map +1 -0
- package/dist/routes/dispatch.d.ts +8 -0
- package/dist/routes/dispatch.d.ts.map +1 -0
- package/dist/routes/dispatch.js +46 -0
- package/dist/routes/dispatch.js.map +1 -0
- package/dist/routes/events.d.ts +14 -0
- package/dist/routes/events.d.ts.map +1 -0
- package/dist/routes/events.js +240 -0
- package/dist/routes/events.js.map +1 -0
- package/dist/routes/health.d.ts +14 -0
- package/dist/routes/health.d.ts.map +1 -0
- package/dist/routes/health.js +132 -0
- package/dist/routes/health.js.map +1 -0
- package/dist/routes/index.d.ts +36 -0
- package/dist/routes/index.d.ts.map +1 -0
- package/dist/routes/index.js +34 -0
- package/dist/routes/index.js.map +1 -0
- package/dist/routes/integrations.d.ts +34 -0
- package/dist/routes/integrations.d.ts.map +1 -0
- package/dist/routes/integrations.js +81 -0
- package/dist/routes/integrations.js.map +1 -0
- package/dist/routes/metrics.d.ts +31 -0
- package/dist/routes/metrics.d.ts.map +1 -0
- package/dist/routes/metrics.js +157 -0
- package/dist/routes/metrics.js.map +1 -0
- package/dist/routes/pr-webhooks.d.ts +15 -0
- package/dist/routes/pr-webhooks.d.ts.map +1 -0
- package/dist/routes/pr-webhooks.js +114 -0
- package/dist/routes/pr-webhooks.js.map +1 -0
- package/dist/routes/queue.d.ts +7 -0
- package/dist/routes/queue.d.ts.map +1 -0
- package/dist/routes/queue.js +94 -0
- package/dist/routes/queue.js.map +1 -0
- package/dist/routes/webhooks.d.ts +15 -0
- package/dist/routes/webhooks.d.ts.map +1 -0
- package/dist/routes/webhooks.js +117 -0
- package/dist/routes/webhooks.js.map +1 -0
- package/dist/routes/workflows.d.ts +7 -0
- package/dist/routes/workflows.d.ts.map +1 -0
- package/dist/routes/workflows.js +138 -0
- package/dist/routes/workflows.js.map +1 -0
- package/dist/server.d.ts +35 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +264 -0
- package/dist/server.js.map +1 -0
- package/dist/services/agent-registry.d.ts +89 -0
- package/dist/services/agent-registry.d.ts.map +1 -0
- package/dist/services/agent-registry.js +173 -0
- package/dist/services/agent-registry.js.map +1 -0
- package/dist/services/epic-completion-monitor-service.d.ts +69 -0
- package/dist/services/epic-completion-monitor-service.d.ts.map +1 -0
- package/dist/services/epic-completion-monitor-service.js +200 -0
- package/dist/services/epic-completion-monitor-service.js.map +1 -0
- package/dist/services/identity.d.ts +41 -0
- package/dist/services/identity.d.ts.map +1 -0
- package/dist/services/identity.js +91 -0
- package/dist/services/identity.js.map +1 -0
- package/dist/services/index.d.ts +13 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +13 -0
- package/dist/services/index.js.map +1 -0
- package/dist/services/label-monitor-service.d.ts +95 -0
- package/dist/services/label-monitor-service.d.ts.map +1 -0
- package/dist/services/label-monitor-service.js +414 -0
- package/dist/services/label-monitor-service.js.map +1 -0
- package/dist/services/label-sync-service.d.ts +76 -0
- package/dist/services/label-sync-service.d.ts.map +1 -0
- package/dist/services/label-sync-service.js +108 -0
- package/dist/services/label-sync-service.js.map +1 -0
- package/dist/services/phase-tracker-service.d.ts +32 -0
- package/dist/services/phase-tracker-service.d.ts.map +1 -0
- package/dist/services/phase-tracker-service.js +82 -0
- package/dist/services/phase-tracker-service.js.map +1 -0
- package/dist/services/pr-feedback-monitor-service.d.ts +86 -0
- package/dist/services/pr-feedback-monitor-service.d.ts.map +1 -0
- package/dist/services/pr-feedback-monitor-service.js +423 -0
- package/dist/services/pr-feedback-monitor-service.js.map +1 -0
- package/dist/services/queue-service.d.ts +62 -0
- package/dist/services/queue-service.d.ts.map +1 -0
- package/dist/services/queue-service.js +147 -0
- package/dist/services/queue-service.js.map +1 -0
- package/dist/services/redis-queue-adapter.d.ts +35 -0
- package/dist/services/redis-queue-adapter.d.ts.map +1 -0
- package/dist/services/redis-queue-adapter.js +218 -0
- package/dist/services/redis-queue-adapter.js.map +1 -0
- package/dist/services/smee-receiver.d.ts +87 -0
- package/dist/services/smee-receiver.d.ts.map +1 -0
- package/dist/services/smee-receiver.js +225 -0
- package/dist/services/smee-receiver.js.map +1 -0
- package/dist/services/webhook-setup-service.d.ts +253 -0
- package/dist/services/webhook-setup-service.d.ts.map +1 -0
- package/dist/services/webhook-setup-service.js +369 -0
- package/dist/services/webhook-setup-service.js.map +1 -0
- package/dist/services/worker-dispatcher.d.ts +57 -0
- package/dist/services/worker-dispatcher.d.ts.map +1 -0
- package/dist/services/worker-dispatcher.js +211 -0
- package/dist/services/worker-dispatcher.js.map +1 -0
- package/dist/services/workflow-service.d.ts +65 -0
- package/dist/services/workflow-service.d.ts.map +1 -0
- package/dist/services/workflow-service.js +164 -0
- package/dist/services/workflow-service.js.map +1 -0
- package/dist/sse/events.d.ts +50 -0
- package/dist/sse/events.d.ts.map +1 -0
- package/dist/sse/events.js +152 -0
- package/dist/sse/events.js.map +1 -0
- package/dist/sse/index.d.ts +4 -0
- package/dist/sse/index.d.ts.map +1 -0
- package/dist/sse/index.js +7 -0
- package/dist/sse/index.js.map +1 -0
- package/dist/sse/stream.d.ts +109 -0
- package/dist/sse/stream.d.ts.map +1 -0
- package/dist/sse/stream.js +239 -0
- package/dist/sse/stream.js.map +1 -0
- package/dist/sse/subscriptions.d.ts +91 -0
- package/dist/sse/subscriptions.d.ts.map +1 -0
- package/dist/sse/subscriptions.js +299 -0
- package/dist/sse/subscriptions.js.map +1 -0
- package/dist/types/api.d.ts +651 -0
- package/dist/types/api.d.ts.map +1 -0
- package/dist/types/api.js +169 -0
- package/dist/types/api.js.map +1 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +27 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/monitor.d.ts +243 -0
- package/dist/types/monitor.d.ts.map +1 -0
- package/dist/types/monitor.js +2 -0
- package/dist/types/monitor.js.map +1 -0
- package/dist/types/problem-details.d.ts +63 -0
- package/dist/types/problem-details.d.ts.map +1 -0
- package/dist/types/problem-details.js +37 -0
- package/dist/types/problem-details.js.map +1 -0
- package/dist/types/sse.d.ts +224 -0
- package/dist/types/sse.d.ts.map +1 -0
- package/dist/types/sse.js +103 -0
- package/dist/types/sse.js.map +1 -0
- package/dist/types/webhook.d.ts +59 -0
- package/dist/types/webhook.d.ts.map +1 -0
- package/dist/types/webhook.js +2 -0
- package/dist/types/webhook.js.map +1 -0
- package/dist/utils/correlation.d.ts +30 -0
- package/dist/utils/correlation.d.ts.map +1 -0
- package/dist/utils/correlation.js +34 -0
- package/dist/utils/correlation.js.map +1 -0
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +3 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/shutdown.d.ts +28 -0
- package/dist/utils/shutdown.d.ts.map +1 -0
- package/dist/utils/shutdown.js +86 -0
- package/dist/utils/shutdown.js.map +1 -0
- package/dist/worker/claude-cli-worker.d.ts +61 -0
- package/dist/worker/claude-cli-worker.d.ts.map +1 -0
- package/dist/worker/claude-cli-worker.js +330 -0
- package/dist/worker/claude-cli-worker.js.map +1 -0
- package/dist/worker/cli-spawner.d.ts +42 -0
- package/dist/worker/cli-spawner.d.ts.map +1 -0
- package/dist/worker/cli-spawner.js +183 -0
- package/dist/worker/cli-spawner.js.map +1 -0
- package/dist/worker/config.d.ts +76 -0
- package/dist/worker/config.d.ts.map +1 -0
- package/dist/worker/config.js +39 -0
- package/dist/worker/config.js.map +1 -0
- package/dist/worker/epic-post-tasks.d.ts +73 -0
- package/dist/worker/epic-post-tasks.d.ts.map +1 -0
- package/dist/worker/epic-post-tasks.js +266 -0
- package/dist/worker/epic-post-tasks.js.map +1 -0
- package/dist/worker/gate-checker.d.ts +22 -0
- package/dist/worker/gate-checker.d.ts.map +1 -0
- package/dist/worker/gate-checker.js +41 -0
- package/dist/worker/gate-checker.js.map +1 -0
- package/dist/worker/index.d.ts +13 -0
- package/dist/worker/index.d.ts.map +1 -0
- package/dist/worker/index.js +19 -0
- package/dist/worker/index.js.map +1 -0
- package/dist/worker/label-manager.d.ts +89 -0
- package/dist/worker/label-manager.d.ts.map +1 -0
- package/dist/worker/label-manager.js +200 -0
- package/dist/worker/label-manager.js.map +1 -0
- package/dist/worker/output-capture.d.ts +58 -0
- package/dist/worker/output-capture.d.ts.map +1 -0
- package/dist/worker/output-capture.js +153 -0
- package/dist/worker/output-capture.js.map +1 -0
- package/dist/worker/phase-loop.d.ts +64 -0
- package/dist/worker/phase-loop.d.ts.map +1 -0
- package/dist/worker/phase-loop.js +185 -0
- package/dist/worker/phase-loop.js.map +1 -0
- package/dist/worker/phase-resolver.d.ts +62 -0
- package/dist/worker/phase-resolver.d.ts.map +1 -0
- package/dist/worker/phase-resolver.js +144 -0
- package/dist/worker/phase-resolver.js.map +1 -0
- package/dist/worker/pr-feedback-handler.d.ts +105 -0
- package/dist/worker/pr-feedback-handler.d.ts.map +1 -0
- package/dist/worker/pr-feedback-handler.js +391 -0
- package/dist/worker/pr-feedback-handler.js.map +1 -0
- package/dist/worker/pr-linker.d.ts +69 -0
- package/dist/worker/pr-linker.d.ts.map +1 -0
- package/dist/worker/pr-linker.js +108 -0
- package/dist/worker/pr-linker.js.map +1 -0
- package/dist/worker/pr-manager.d.ts +61 -0
- package/dist/worker/pr-manager.d.ts.map +1 -0
- package/dist/worker/pr-manager.js +140 -0
- package/dist/worker/pr-manager.js.map +1 -0
- package/dist/worker/repo-checkout.d.ts +63 -0
- package/dist/worker/repo-checkout.d.ts.map +1 -0
- package/dist/worker/repo-checkout.js +160 -0
- package/dist/worker/repo-checkout.js.map +1 -0
- package/dist/worker/stage-comment-manager.d.ts +34 -0
- package/dist/worker/stage-comment-manager.d.ts.map +1 -0
- package/dist/worker/stage-comment-manager.js +114 -0
- package/dist/worker/stage-comment-manager.js.map +1 -0
- package/dist/worker/types.d.ts +192 -0
- package/dist/worker/types.d.ts.map +1 -0
- package/dist/worker/types.js +53 -0
- package/dist/worker/types.js.map +1 -0
- package/package.json +64 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"phase-tracker-service.d.ts","sourceRoot":"","sources":["../../src/services/phase-tracker-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAItD,UAAU,MAAM;IACd,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACtD,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACtD,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACxD;AAED,MAAM,WAAW,mBAAmB;IAClC,2DAA2D;IAC3D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;GAIG;AACH,qBAAa,mBAAoB,YAAW,YAAY;IACtD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;IACrC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;gBAExB,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI,EAAE,OAAO,CAAC,EAAE,mBAAmB;IAM9E,OAAO,CAAC,QAAQ;IAIV,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAuBxF,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAkB/E,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAyB7F,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAkB9F"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
const DEFAULT_TTL_SECONDS = 86400; // 24 hours
|
|
2
|
+
/**
|
|
3
|
+
* Redis-backed phase tracker for deduplication.
|
|
4
|
+
* Uses SET with TTL to prevent duplicate processing of the same issue+phase.
|
|
5
|
+
* Gracefully degrades when Redis is unavailable (treats as "not duplicate").
|
|
6
|
+
*/
|
|
7
|
+
export class PhaseTrackerService {
|
|
8
|
+
redis;
|
|
9
|
+
logger;
|
|
10
|
+
ttlSeconds;
|
|
11
|
+
constructor(logger, redis, options) {
|
|
12
|
+
this.logger = logger;
|
|
13
|
+
this.redis = redis;
|
|
14
|
+
this.ttlSeconds = options?.ttlSeconds ?? DEFAULT_TTL_SECONDS;
|
|
15
|
+
}
|
|
16
|
+
buildKey(owner, repo, issue, phase) {
|
|
17
|
+
return `phase-tracker:${owner}:${repo}:${issue}:${phase}`;
|
|
18
|
+
}
|
|
19
|
+
async isDuplicate(owner, repo, issue, phase) {
|
|
20
|
+
if (!this.redis) {
|
|
21
|
+
this.logger.warn('Redis unavailable for phase tracker, treating as not duplicate');
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
const key = this.buildKey(owner, repo, issue, phase);
|
|
25
|
+
try {
|
|
26
|
+
const exists = await this.redis.exists(key);
|
|
27
|
+
if (exists) {
|
|
28
|
+
this.logger.info({ key, owner, repo, issue, phase }, 'Duplicate event detected');
|
|
29
|
+
}
|
|
30
|
+
return exists === 1;
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
this.logger.warn({ err: error, key }, 'Redis error in isDuplicate, treating as not duplicate');
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
async clear(owner, repo, issue, phase) {
|
|
38
|
+
if (!this.redis) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const key = this.buildKey(owner, repo, issue, phase);
|
|
42
|
+
try {
|
|
43
|
+
await this.redis.del(key);
|
|
44
|
+
this.logger.info({ key }, 'Cleared dedup key');
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
this.logger.warn({ err: error, key }, 'Redis error in clear, deduplication may block this event');
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
async tryMarkProcessed(owner, repo, issue, phase) {
|
|
51
|
+
if (!this.redis) {
|
|
52
|
+
this.logger.warn('Redis unavailable for phase tracker, treating as not duplicate');
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
const key = this.buildKey(owner, repo, issue, phase);
|
|
56
|
+
try {
|
|
57
|
+
const result = await this.redis.set(key, '1', 'EX', this.ttlSeconds, 'NX');
|
|
58
|
+
const won = result === 'OK';
|
|
59
|
+
this.logger.info({ key, owner, repo, issue, phase, won }, won ? 'Atomically marked phase as processed' : 'Duplicate detected (atomic check)');
|
|
60
|
+
return won;
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
this.logger.warn({ err: error, key }, 'Redis error in tryMarkProcessed, treating as not duplicate');
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
async markProcessed(owner, repo, issue, phase) {
|
|
68
|
+
if (!this.redis) {
|
|
69
|
+
this.logger.warn('Redis unavailable for phase tracker, skipping markProcessed');
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const key = this.buildKey(owner, repo, issue, phase);
|
|
73
|
+
try {
|
|
74
|
+
await this.redis.set(key, '1', 'EX', this.ttlSeconds);
|
|
75
|
+
this.logger.info({ key, ttl: this.ttlSeconds }, 'Marked phase as processed');
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
this.logger.warn({ err: error, key }, 'Redis error in markProcessed, deduplication may not work for this event');
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=phase-tracker-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"phase-tracker-service.js","sourceRoot":"","sources":["../../src/services/phase-tracker-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { GitHubClientFactory } from '@generacy-ai/workflow-engine';
|
|
2
|
+
import type { MonitorState, QueueAdapter, PhaseTracker, PrReviewEvent } from '../types/monitor.js';
|
|
3
|
+
import type { RepositoryConfig, PrMonitorConfig } from '../config/schema.js';
|
|
4
|
+
import type { Logger } from '../worker/types.js';
|
|
5
|
+
export interface PrFeedbackMonitorOptions {
|
|
6
|
+
repositories: RepositoryConfig[];
|
|
7
|
+
pollIntervalMs: number;
|
|
8
|
+
adaptivePolling: boolean;
|
|
9
|
+
maxConcurrentPolls: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* PR feedback monitor service that detects unresolved review comments
|
|
13
|
+
* on PRs linked to orchestrated issues and triggers the feedback-addressing flow.
|
|
14
|
+
*
|
|
15
|
+
* Uses a hybrid webhook + polling architecture mirroring LabelMonitorService.
|
|
16
|
+
*/
|
|
17
|
+
export declare class PrFeedbackMonitorService {
|
|
18
|
+
private readonly logger;
|
|
19
|
+
private readonly createClient;
|
|
20
|
+
private readonly phaseTracker;
|
|
21
|
+
private readonly queueAdapter;
|
|
22
|
+
private readonly options;
|
|
23
|
+
private readonly prLinker;
|
|
24
|
+
private readonly clusterGithubUsername;
|
|
25
|
+
private abortController;
|
|
26
|
+
private state;
|
|
27
|
+
constructor(logger: Logger, createClient: GitHubClientFactory, phaseTracker: PhaseTracker, queueAdapter: QueueAdapter, config: PrMonitorConfig, repositories: RepositoryConfig[], clusterGithubUsername?: string);
|
|
28
|
+
/**
|
|
29
|
+
* Process a PR review event: link PR to issue, check for unresolved threads,
|
|
30
|
+
* deduplicate, and enqueue feedback-addressing work.
|
|
31
|
+
*
|
|
32
|
+
* Shared by both webhook and polling paths.
|
|
33
|
+
*
|
|
34
|
+
* @returns true if feedback was enqueued, false if skipped or duplicate
|
|
35
|
+
*/
|
|
36
|
+
processPrReviewEvent(event: PrReviewEvent): Promise<boolean>;
|
|
37
|
+
/**
|
|
38
|
+
* Start the polling loop.
|
|
39
|
+
*/
|
|
40
|
+
startPolling(): Promise<void>;
|
|
41
|
+
/**
|
|
42
|
+
* Stop the polling loop.
|
|
43
|
+
*/
|
|
44
|
+
stopPolling(): void;
|
|
45
|
+
/**
|
|
46
|
+
* Run a single poll cycle across all watched repositories.
|
|
47
|
+
* Lists open PRs in each repo, checks for unresolved review threads.
|
|
48
|
+
*/
|
|
49
|
+
poll(): Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Poll a single repository for PRs with unresolved review threads.
|
|
52
|
+
* Lists open PRs and processes each through the standard event flow.
|
|
53
|
+
*/
|
|
54
|
+
private pollRepo;
|
|
55
|
+
/**
|
|
56
|
+
* FR-015: Deduplicate PRs that link to the same issue, keeping only the
|
|
57
|
+
* most recently updated PR per issue. Uses lightweight body/branch parsing
|
|
58
|
+
* (no API calls) to determine candidate issue numbers.
|
|
59
|
+
*
|
|
60
|
+
* PRs that don't resolve to any issue number are kept as-is (they'll be
|
|
61
|
+
* filtered out later by `processPrReviewEvent` when linking fails).
|
|
62
|
+
*/
|
|
63
|
+
private deduplicatePrsByIssue;
|
|
64
|
+
/**
|
|
65
|
+
* Check if an error is a GitHub API rate limit error.
|
|
66
|
+
*/
|
|
67
|
+
private isRateLimitError;
|
|
68
|
+
/**
|
|
69
|
+
* Record that a webhook event was received, updating health tracking.
|
|
70
|
+
*/
|
|
71
|
+
recordWebhookEvent(): void;
|
|
72
|
+
/**
|
|
73
|
+
* Update adaptive polling interval based on webhook health.
|
|
74
|
+
*/
|
|
75
|
+
private updateAdaptivePolling;
|
|
76
|
+
getState(): Readonly<MonitorState>;
|
|
77
|
+
/**
|
|
78
|
+
* Resolve workflow name from the issue's labels.
|
|
79
|
+
* Checks workflow:* labels first (authoritative, set by label monitor on process events),
|
|
80
|
+
* then falls back to process:* / completed:* / agent:* for pre-migration issues.
|
|
81
|
+
* Falls back to 'unknown' if no workflow label is found.
|
|
82
|
+
*/
|
|
83
|
+
private resolveWorkflowName;
|
|
84
|
+
private sleep;
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=pr-feedback-monitor-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pr-feedback-monitor-service.d.ts","sourceRoot":"","sources":["../../src/services/pr-feedback-monitor-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,KAAK,EACV,YAAY,EACZ,YAAY,EACZ,YAAY,EAEZ,aAAa,EAEd,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAE7E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAEjD,MAAM,WAAW,wBAAwB;IACvC,YAAY,EAAE,gBAAgB,EAAE,CAAC;IACjC,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,OAAO,CAAC;IACzB,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAYD;;;;;GAKG;AACH,qBAAa,wBAAwB;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAsB;IACnD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAe;IAC5C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAe;IAC5C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA2B;IACnD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAW;IACpC,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAqB;IAC3D,OAAO,CAAC,eAAe,CAAgC;IAEvD,OAAO,CAAC,KAAK,CAAe;gBAG1B,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,mBAAmB,EACjC,YAAY,EAAE,YAAY,EAC1B,YAAY,EAAE,YAAY,EAC1B,MAAM,EAAE,eAAe,EACvB,YAAY,EAAE,gBAAgB,EAAE,EAChC,qBAAqB,CAAC,EAAE,MAAM;IA4BhC;;;;;;;OAOG;IACG,oBAAoB,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC;IA0IlE;;OAEG;IACG,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAoCnC;;OAEG;IACH,WAAW,IAAI,IAAI;IAQnB;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAoB3B;;;OAGG;YACW,QAAQ;IAsDtB;;;;;;;OAOG;IACH,OAAO,CAAC,qBAAqB;IAwD7B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAYxB;;OAEG;IACH,kBAAkB,IAAI,IAAI;IAc1B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IA2B7B,QAAQ,IAAI,QAAQ,CAAC,YAAY,CAAC;IAQlC;;;;;OAKG;YACW,mBAAmB;IA0CjC,OAAO,CAAC,KAAK;CAkBd"}
|
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
import { PrLinker } from '../worker/pr-linker.js';
|
|
2
|
+
const WAITING_FOR_PR_FEEDBACK_LABEL = 'waiting-for:address-pr-feedback';
|
|
3
|
+
const DEDUP_PHASE = 'address-pr-feedback';
|
|
4
|
+
const MIN_POLL_INTERVAL_MS = 10000;
|
|
5
|
+
/**
|
|
6
|
+
* Adaptive polling divisor for PR feedback monitor.
|
|
7
|
+
* Per US4: "Polling interval decreases by 50%" → divide by 2.
|
|
8
|
+
* This differs from LabelMonitorService which uses ADAPTIVE_DIVISOR = 3.
|
|
9
|
+
*/
|
|
10
|
+
const ADAPTIVE_DIVISOR = 2;
|
|
11
|
+
/**
|
|
12
|
+
* PR feedback monitor service that detects unresolved review comments
|
|
13
|
+
* on PRs linked to orchestrated issues and triggers the feedback-addressing flow.
|
|
14
|
+
*
|
|
15
|
+
* Uses a hybrid webhook + polling architecture mirroring LabelMonitorService.
|
|
16
|
+
*/
|
|
17
|
+
export class PrFeedbackMonitorService {
|
|
18
|
+
logger;
|
|
19
|
+
createClient;
|
|
20
|
+
phaseTracker;
|
|
21
|
+
queueAdapter;
|
|
22
|
+
options;
|
|
23
|
+
prLinker;
|
|
24
|
+
clusterGithubUsername;
|
|
25
|
+
abortController = null;
|
|
26
|
+
state;
|
|
27
|
+
constructor(logger, createClient, phaseTracker, queueAdapter, config, repositories, clusterGithubUsername) {
|
|
28
|
+
this.logger = logger;
|
|
29
|
+
this.createClient = createClient;
|
|
30
|
+
this.phaseTracker = phaseTracker;
|
|
31
|
+
this.queueAdapter = queueAdapter;
|
|
32
|
+
this.clusterGithubUsername = clusterGithubUsername;
|
|
33
|
+
this.options = {
|
|
34
|
+
repositories,
|
|
35
|
+
pollIntervalMs: config.pollIntervalMs,
|
|
36
|
+
adaptivePolling: config.adaptivePolling,
|
|
37
|
+
maxConcurrentPolls: config.maxConcurrentPolls,
|
|
38
|
+
};
|
|
39
|
+
this.prLinker = new PrLinker(logger);
|
|
40
|
+
this.state = {
|
|
41
|
+
isPolling: false,
|
|
42
|
+
webhookHealthy: true,
|
|
43
|
+
lastWebhookEvent: null,
|
|
44
|
+
currentPollIntervalMs: config.pollIntervalMs,
|
|
45
|
+
basePollIntervalMs: config.pollIntervalMs,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
// ==========================================================================
|
|
49
|
+
// PR Review Event Processing
|
|
50
|
+
// ==========================================================================
|
|
51
|
+
/**
|
|
52
|
+
* Process a PR review event: link PR to issue, check for unresolved threads,
|
|
53
|
+
* deduplicate, and enqueue feedback-addressing work.
|
|
54
|
+
*
|
|
55
|
+
* Shared by both webhook and polling paths.
|
|
56
|
+
*
|
|
57
|
+
* @returns true if feedback was enqueued, false if skipped or duplicate
|
|
58
|
+
*/
|
|
59
|
+
async processPrReviewEvent(event) {
|
|
60
|
+
const { owner, repo, prNumber, prBody, branchName, source } = event;
|
|
61
|
+
this.logger.info({ owner, repo, prNumber, source }, `Processing PR review event from ${source}`);
|
|
62
|
+
const client = this.createClient();
|
|
63
|
+
// 1. Link PR to orchestrated issue
|
|
64
|
+
const prInput = {
|
|
65
|
+
number: prNumber,
|
|
66
|
+
body: prBody,
|
|
67
|
+
head: { ref: branchName },
|
|
68
|
+
};
|
|
69
|
+
const link = await this.prLinker.linkPrToIssue(client, owner, repo, prInput);
|
|
70
|
+
if (!link) {
|
|
71
|
+
this.logger.debug({ owner, repo, prNumber }, 'PR not linked to an orchestrated issue — skipping');
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
const { issueNumber, linkMethod, assignees } = link;
|
|
75
|
+
// 2. Assignee check — skip PR feedback for issues not assigned to this cluster
|
|
76
|
+
// Uses assignees returned by PrLinker to avoid a duplicate getIssue() call
|
|
77
|
+
if (this.clusterGithubUsername) {
|
|
78
|
+
if (assignees.length === 0) {
|
|
79
|
+
this.logger.warn({ owner, repo, issueNumber, prNumber }, 'Skipping PR feedback: linked issue has no assignees');
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
if (!assignees.includes(this.clusterGithubUsername)) {
|
|
83
|
+
this.logger.debug({ owner, repo, issueNumber, prNumber, assignees }, 'Skipping PR feedback: linked issue not assigned to this cluster');
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
if (assignees.length > 1) {
|
|
87
|
+
this.logger.warn({ owner, repo, issueNumber, assignees }, 'Issue has multiple assignees — may be processed by multiple clusters');
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// 3. Fetch review comments and filter for unresolved threads
|
|
91
|
+
let unresolvedThreadIds;
|
|
92
|
+
try {
|
|
93
|
+
const comments = await client.getPRComments(owner, repo, prNumber);
|
|
94
|
+
// Filter for root-level unresolved comments (not replies)
|
|
95
|
+
const unresolvedComments = comments.filter((c) => c.resolved === false && !c.in_reply_to_id);
|
|
96
|
+
unresolvedThreadIds = unresolvedComments.map((c) => c.id);
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
this.logger.error({ err: error, owner, repo, prNumber }, 'Failed to fetch PR comments');
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
if (unresolvedThreadIds.length === 0) {
|
|
103
|
+
this.logger.debug({ owner, repo, prNumber, issueNumber }, 'No unresolved review threads — skipping');
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
this.logger.info({ owner, repo, prNumber, issueNumber, linkMethod, unresolvedCount: unresolvedThreadIds.length }, `Found ${unresolvedThreadIds.length} unresolved review thread(s)`);
|
|
107
|
+
// 4. Atomic deduplication via tryMarkProcessed (SET NX)
|
|
108
|
+
const isNew = await this.phaseTracker.tryMarkProcessed(owner, repo, issueNumber, DEDUP_PHASE);
|
|
109
|
+
if (!isNew) {
|
|
110
|
+
this.logger.info({ owner, repo, issueNumber, prNumber }, 'Skipping duplicate — PR feedback already enqueued for this issue');
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
// 5. Resolve workflow name from issue labels
|
|
114
|
+
const workflowName = await this.resolveWorkflowName(owner, repo, issueNumber);
|
|
115
|
+
// 6. Build and enqueue queue item
|
|
116
|
+
const metadata = {
|
|
117
|
+
prNumber,
|
|
118
|
+
reviewThreadIds: unresolvedThreadIds,
|
|
119
|
+
};
|
|
120
|
+
const queueItem = {
|
|
121
|
+
owner,
|
|
122
|
+
repo,
|
|
123
|
+
issueNumber,
|
|
124
|
+
workflowName,
|
|
125
|
+
command: 'address-pr-feedback',
|
|
126
|
+
priority: Date.now(),
|
|
127
|
+
enqueuedAt: new Date().toISOString(),
|
|
128
|
+
metadata: metadata,
|
|
129
|
+
};
|
|
130
|
+
await this.queueAdapter.enqueue(queueItem);
|
|
131
|
+
this.logger.info({ owner, repo, issueNumber, prNumber, command: queueItem.command }, 'PR feedback work enqueued');
|
|
132
|
+
// 7. Add waiting-for label to issue
|
|
133
|
+
try {
|
|
134
|
+
await client.addLabels(owner, repo, issueNumber, [WAITING_FOR_PR_FEEDBACK_LABEL]);
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
this.logger.warn({ err: error, owner, repo, issueNumber }, 'Failed to add waiting-for:address-pr-feedback label');
|
|
138
|
+
}
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
// ==========================================================================
|
|
142
|
+
// Polling
|
|
143
|
+
// ==========================================================================
|
|
144
|
+
/**
|
|
145
|
+
* Start the polling loop.
|
|
146
|
+
*/
|
|
147
|
+
async startPolling() {
|
|
148
|
+
if (this.state.isPolling) {
|
|
149
|
+
this.logger.warn('PR feedback polling already running');
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
const ac = new AbortController();
|
|
153
|
+
this.abortController = ac;
|
|
154
|
+
this.state.isPolling = true;
|
|
155
|
+
this.logger.info({ intervalMs: this.state.currentPollIntervalMs, repos: this.options.repositories.length }, 'Starting PR feedback monitor polling');
|
|
156
|
+
while (!ac.signal.aborted) {
|
|
157
|
+
try {
|
|
158
|
+
await this.poll();
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
this.logger.error({ err: error }, 'Error during PR feedback poll cycle');
|
|
162
|
+
}
|
|
163
|
+
// Update adaptive polling before sleeping
|
|
164
|
+
if (this.options.adaptivePolling) {
|
|
165
|
+
this.updateAdaptivePolling();
|
|
166
|
+
}
|
|
167
|
+
await this.sleep(this.state.currentPollIntervalMs, ac.signal);
|
|
168
|
+
}
|
|
169
|
+
this.state.isPolling = false;
|
|
170
|
+
this.logger.info('PR feedback polling loop stopped');
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Stop the polling loop.
|
|
174
|
+
*/
|
|
175
|
+
stopPolling() {
|
|
176
|
+
if (this.abortController) {
|
|
177
|
+
this.abortController.abort();
|
|
178
|
+
this.abortController = null;
|
|
179
|
+
this.logger.info('PR feedback polling stop requested');
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Run a single poll cycle across all watched repositories.
|
|
184
|
+
* Lists open PRs in each repo, checks for unresolved review threads.
|
|
185
|
+
*/
|
|
186
|
+
async poll() {
|
|
187
|
+
const repos = this.options.repositories;
|
|
188
|
+
if (repos.length === 0)
|
|
189
|
+
return;
|
|
190
|
+
// Use semaphore pattern for concurrency limiting
|
|
191
|
+
const semaphore = new Semaphore(this.options.maxConcurrentPolls);
|
|
192
|
+
const pollTasks = repos.map(({ owner, repo }) => semaphore.acquire().then(async (release) => {
|
|
193
|
+
try {
|
|
194
|
+
await this.pollRepo(owner, repo);
|
|
195
|
+
}
|
|
196
|
+
finally {
|
|
197
|
+
release();
|
|
198
|
+
}
|
|
199
|
+
}));
|
|
200
|
+
await Promise.allSettled(pollTasks);
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Poll a single repository for PRs with unresolved review threads.
|
|
204
|
+
* Lists open PRs and processes each through the standard event flow.
|
|
205
|
+
*/
|
|
206
|
+
async pollRepo(owner, repo) {
|
|
207
|
+
const client = this.createClient();
|
|
208
|
+
let openPRs;
|
|
209
|
+
try {
|
|
210
|
+
openPRs = await client.listOpenPullRequests(owner, repo);
|
|
211
|
+
}
|
|
212
|
+
catch (error) {
|
|
213
|
+
if (this.isRateLimitError(error)) {
|
|
214
|
+
this.logger.warn({ owner, repo }, 'GitHub API rate limit hit while listing open PRs — skipping repo this cycle');
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
this.logger.error({ err: error, owner, repo }, 'Error polling repository for open PRs');
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
// FR-015: When multiple PRs exist for the same issue, process only the
|
|
221
|
+
// most recently updated PR. Use a lightweight pre-link pass (body/branch
|
|
222
|
+
// parsing only, no API calls) to group PRs by candidate issue number.
|
|
223
|
+
const prsToProcess = this.deduplicatePrsByIssue(owner, repo, openPRs);
|
|
224
|
+
for (const pr of prsToProcess) {
|
|
225
|
+
const event = {
|
|
226
|
+
owner,
|
|
227
|
+
repo,
|
|
228
|
+
prNumber: pr.number,
|
|
229
|
+
prBody: pr.body ?? '',
|
|
230
|
+
branchName: pr.head.ref,
|
|
231
|
+
source: 'poll',
|
|
232
|
+
};
|
|
233
|
+
try {
|
|
234
|
+
await this.processPrReviewEvent(event);
|
|
235
|
+
}
|
|
236
|
+
catch (error) {
|
|
237
|
+
if (this.isRateLimitError(error)) {
|
|
238
|
+
this.logger.warn({ owner, repo, prNumber: pr.number }, 'GitHub API rate limit hit while processing PR — stopping repo poll');
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
this.logger.error({ err: error, owner, repo, prNumber: pr.number }, 'Error processing PR during poll');
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* FR-015: Deduplicate PRs that link to the same issue, keeping only the
|
|
247
|
+
* most recently updated PR per issue. Uses lightweight body/branch parsing
|
|
248
|
+
* (no API calls) to determine candidate issue numbers.
|
|
249
|
+
*
|
|
250
|
+
* PRs that don't resolve to any issue number are kept as-is (they'll be
|
|
251
|
+
* filtered out later by `processPrReviewEvent` when linking fails).
|
|
252
|
+
*/
|
|
253
|
+
deduplicatePrsByIssue(owner, repo, prs) {
|
|
254
|
+
// Group PRs by candidate issue number
|
|
255
|
+
const issueGroups = new Map();
|
|
256
|
+
const unlinked = [];
|
|
257
|
+
for (const pr of prs) {
|
|
258
|
+
const candidateIssue = this.prLinker.parsePrBody(pr.body) ??
|
|
259
|
+
this.prLinker.parseBranchName(pr.head.ref);
|
|
260
|
+
if (candidateIssue === null) {
|
|
261
|
+
unlinked.push(pr);
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
const group = issueGroups.get(candidateIssue);
|
|
265
|
+
if (group) {
|
|
266
|
+
group.push(pr);
|
|
267
|
+
}
|
|
268
|
+
else {
|
|
269
|
+
issueGroups.set(candidateIssue, [pr]);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
const result = [...unlinked];
|
|
273
|
+
for (const [issueNumber, group] of issueGroups) {
|
|
274
|
+
if (group.length === 1) {
|
|
275
|
+
result.push(group[0]);
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
// Sort by updated_at descending — most recent first
|
|
279
|
+
group.sort((a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime());
|
|
280
|
+
const mostRecent = group[0];
|
|
281
|
+
result.push(mostRecent);
|
|
282
|
+
// Log warning for skipped older PRs
|
|
283
|
+
const skipped = group.slice(1);
|
|
284
|
+
for (const skippedPr of skipped) {
|
|
285
|
+
this.logger.warn({ owner, repo, issueNumber, skippedPrNumber: skippedPr.number, processedPrNumber: mostRecent.number }, `Skipping older PR #${skippedPr.number} for issue #${issueNumber} — processing most recent PR #${mostRecent.number}`);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return result;
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Check if an error is a GitHub API rate limit error.
|
|
292
|
+
*/
|
|
293
|
+
isRateLimitError(error) {
|
|
294
|
+
if (error instanceof Error) {
|
|
295
|
+
const msg = error.message.toLowerCase();
|
|
296
|
+
return msg.includes('rate limit') || msg.includes('api rate') || msg.includes('403');
|
|
297
|
+
}
|
|
298
|
+
return false;
|
|
299
|
+
}
|
|
300
|
+
// ==========================================================================
|
|
301
|
+
// Adaptive Polling
|
|
302
|
+
// ==========================================================================
|
|
303
|
+
/**
|
|
304
|
+
* Record that a webhook event was received, updating health tracking.
|
|
305
|
+
*/
|
|
306
|
+
recordWebhookEvent() {
|
|
307
|
+
this.state.lastWebhookEvent = Date.now();
|
|
308
|
+
const wasUnhealthy = !this.state.webhookHealthy;
|
|
309
|
+
this.state.webhookHealthy = true;
|
|
310
|
+
if (wasUnhealthy) {
|
|
311
|
+
this.state.currentPollIntervalMs = this.state.basePollIntervalMs;
|
|
312
|
+
this.logger.info({ intervalMs: this.state.currentPollIntervalMs }, 'Webhook reconnected, restoring normal PR feedback poll interval');
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Update adaptive polling interval based on webhook health.
|
|
317
|
+
*/
|
|
318
|
+
updateAdaptivePolling() {
|
|
319
|
+
if (this.state.lastWebhookEvent === null) {
|
|
320
|
+
// No webhook events yet — treat as healthy (no data, not unhealthy)
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
const timeSinceLastWebhook = Date.now() - this.state.lastWebhookEvent;
|
|
324
|
+
const unhealthyThreshold = this.state.basePollIntervalMs * 2;
|
|
325
|
+
if (timeSinceLastWebhook > unhealthyThreshold && this.state.webhookHealthy) {
|
|
326
|
+
// Webhooks went unhealthy — increase poll frequency
|
|
327
|
+
this.state.webhookHealthy = false;
|
|
328
|
+
this.state.currentPollIntervalMs = Math.max(MIN_POLL_INTERVAL_MS, Math.floor(this.state.basePollIntervalMs / ADAPTIVE_DIVISOR));
|
|
329
|
+
this.logger.info({ intervalMs: this.state.currentPollIntervalMs, timeSinceLastWebhook }, 'Webhooks appear unhealthy, increasing PR feedback poll frequency');
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
// ==========================================================================
|
|
333
|
+
// State Access
|
|
334
|
+
// ==========================================================================
|
|
335
|
+
getState() {
|
|
336
|
+
return { ...this.state };
|
|
337
|
+
}
|
|
338
|
+
// ==========================================================================
|
|
339
|
+
// Utilities
|
|
340
|
+
// ==========================================================================
|
|
341
|
+
/**
|
|
342
|
+
* Resolve workflow name from the issue's labels.
|
|
343
|
+
* Checks workflow:* labels first (authoritative, set by label monitor on process events),
|
|
344
|
+
* then falls back to process:* / completed:* / agent:* for pre-migration issues.
|
|
345
|
+
* Falls back to 'unknown' if no workflow label is found.
|
|
346
|
+
*/
|
|
347
|
+
async resolveWorkflowName(owner, repo, issueNumber) {
|
|
348
|
+
try {
|
|
349
|
+
const client = this.createClient();
|
|
350
|
+
const issue = await client.getIssue(owner, repo, issueNumber);
|
|
351
|
+
// Primary: persistent workflow:* label (authoritative)
|
|
352
|
+
for (const label of issue.labels) {
|
|
353
|
+
if (label.name.startsWith('workflow:')) {
|
|
354
|
+
return label.name.slice('workflow:'.length);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
// Fallback: existing logic for pre-migration issues
|
|
358
|
+
for (const label of issue.labels) {
|
|
359
|
+
if (label.name.startsWith('process:')) {
|
|
360
|
+
return label.name.slice('process:'.length);
|
|
361
|
+
}
|
|
362
|
+
if (label.name.startsWith('completed:')) {
|
|
363
|
+
return label.name.slice('completed:'.length);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
// Check agent:* labels for workflow name fallback
|
|
367
|
+
for (const label of issue.labels) {
|
|
368
|
+
if (label.name.startsWith('agent:') && label.name !== 'agent:in-progress' && label.name !== 'agent:error' && label.name !== 'agent:dispatched' && label.name !== 'agent:paused') {
|
|
369
|
+
return label.name.slice('agent:'.length);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
catch (error) {
|
|
374
|
+
this.logger.warn({ err: error, owner, repo, issueNumber }, 'Failed to resolve workflow name from issue labels');
|
|
375
|
+
}
|
|
376
|
+
return 'unknown';
|
|
377
|
+
}
|
|
378
|
+
sleep(ms, signal) {
|
|
379
|
+
return new Promise((resolve) => {
|
|
380
|
+
if (signal.aborted) {
|
|
381
|
+
resolve();
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
const timer = setTimeout(resolve, ms);
|
|
385
|
+
// Clean up timer if abort is signaled
|
|
386
|
+
const onAbort = () => {
|
|
387
|
+
clearTimeout(timer);
|
|
388
|
+
resolve();
|
|
389
|
+
};
|
|
390
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Simple semaphore for concurrency limiting.
|
|
396
|
+
*/
|
|
397
|
+
class Semaphore {
|
|
398
|
+
count;
|
|
399
|
+
waiting = [];
|
|
400
|
+
constructor(max) {
|
|
401
|
+
this.count = max;
|
|
402
|
+
}
|
|
403
|
+
async acquire() {
|
|
404
|
+
if (this.count > 0) {
|
|
405
|
+
this.count--;
|
|
406
|
+
return () => this.release();
|
|
407
|
+
}
|
|
408
|
+
return new Promise((resolve) => {
|
|
409
|
+
this.waiting.push(() => {
|
|
410
|
+
this.count--;
|
|
411
|
+
resolve(() => this.release());
|
|
412
|
+
});
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
release() {
|
|
416
|
+
this.count++;
|
|
417
|
+
const next = this.waiting.shift();
|
|
418
|
+
if (next) {
|
|
419
|
+
next();
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
//# sourceMappingURL=pr-feedback-monitor-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pr-feedback-monitor-service.js","sourceRoot":"","sources":["../../src/services/pr-feedback-monitor-service.ts"],"names":[],"mappings":"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"}
|