@generacy-ai/orchestrator 0.7.0 → 0.9.0
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/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +47 -1
- package/dist/config/loader.js.map +1 -1
- package/dist/config/schema.d.ts +459 -0
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +13 -1
- package/dist/config/schema.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/launcher/agent-launcher.d.ts +15 -16
- package/dist/launcher/agent-launcher.d.ts.map +1 -1
- package/dist/launcher/agent-launcher.js +54 -23
- package/dist/launcher/agent-launcher.js.map +1 -1
- package/dist/launcher/constants.d.ts +5 -0
- package/dist/launcher/constants.d.ts.map +1 -0
- package/dist/launcher/constants.js +5 -0
- package/dist/launcher/constants.js.map +1 -0
- package/dist/launcher/errors.d.ts +15 -0
- package/dist/launcher/errors.d.ts.map +1 -0
- package/dist/launcher/errors.js +25 -0
- package/dist/launcher/errors.js.map +1 -0
- package/dist/launcher/generic-subprocess-plugin.d.ts +1 -0
- package/dist/launcher/generic-subprocess-plugin.d.ts.map +1 -1
- package/dist/launcher/generic-subprocess-plugin.js +2 -0
- package/dist/launcher/generic-subprocess-plugin.js.map +1 -1
- package/dist/launcher/index.d.ts +2 -1
- package/dist/launcher/index.d.ts.map +1 -1
- package/dist/launcher/index.js +1 -0
- package/dist/launcher/index.js.map +1 -1
- package/dist/launcher/types.d.ts +81 -4
- package/dist/launcher/types.d.ts.map +1 -1
- package/dist/routes/health.d.ts +7 -0
- package/dist/routes/health.d.ts.map +1 -1
- package/dist/routes/health.js +5 -0
- package/dist/routes/health.js.map +1 -1
- package/dist/routes/internal-relay-events.d.ts +2 -2
- package/dist/routes/internal-relay-events.d.ts.map +1 -1
- package/dist/routes/internal-relay-events.js +12 -0
- package/dist/routes/internal-relay-events.js.map +1 -1
- package/dist/routes/retained-tunnel-event.d.ts +20 -0
- package/dist/routes/retained-tunnel-event.d.ts.map +1 -0
- package/dist/routes/retained-tunnel-event.js +67 -0
- package/dist/routes/retained-tunnel-event.js.map +1 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +168 -27
- package/dist/server.js.map +1 -1
- package/dist/services/adaptive-poll-controller.d.ts +75 -0
- package/dist/services/adaptive-poll-controller.d.ts.map +1 -0
- package/dist/services/adaptive-poll-controller.js +96 -0
- package/dist/services/adaptive-poll-controller.js.map +1 -0
- package/dist/services/clarification-answer-monitor-service.d.ts +71 -0
- package/dist/services/clarification-answer-monitor-service.d.ts.map +1 -0
- package/dist/services/clarification-answer-monitor-service.js +366 -0
- package/dist/services/clarification-answer-monitor-service.js.map +1 -0
- package/dist/services/failure-fingerprint-tracker.d.ts +28 -0
- package/dist/services/failure-fingerprint-tracker.d.ts.map +1 -0
- package/dist/services/failure-fingerprint-tracker.js +38 -0
- package/dist/services/failure-fingerprint-tracker.js.map +1 -0
- package/dist/services/index.d.ts +1 -0
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +3 -0
- package/dist/services/index.js.map +1 -1
- package/dist/services/label-monitor-service.d.ts +2 -2
- package/dist/services/label-monitor-service.d.ts.map +1 -1
- package/dist/services/label-monitor-service.js +33 -16
- package/dist/services/label-monitor-service.js.map +1 -1
- package/dist/services/merge-conflict-monitor-service.d.ts +1 -1
- package/dist/services/merge-conflict-monitor-service.d.ts.map +1 -1
- package/dist/services/merge-conflict-monitor-service.js +33 -13
- package/dist/services/merge-conflict-monitor-service.js.map +1 -1
- package/dist/services/post-activation-retry.d.ts +3 -0
- package/dist/services/post-activation-retry.d.ts.map +1 -1
- package/dist/services/post-activation-retry.js +34 -2
- package/dist/services/post-activation-retry.js.map +1 -1
- package/dist/services/pr-feedback-monitor-service.d.ts +1 -1
- package/dist/services/pr-feedback-monitor-service.d.ts.map +1 -1
- package/dist/services/pr-feedback-monitor-service.js +33 -16
- package/dist/services/pr-feedback-monitor-service.js.map +1 -1
- package/dist/services/relay-bridge.d.ts +1 -0
- package/dist/services/relay-bridge.d.ts.map +1 -1
- package/dist/services/relay-bridge.js +21 -0
- package/dist/services/relay-bridge.js.map +1 -1
- package/dist/services/smee-channel-resolver.d.ts +60 -0
- package/dist/services/smee-channel-resolver.d.ts.map +1 -0
- package/dist/services/smee-channel-resolver.js +177 -0
- package/dist/services/smee-channel-resolver.js.map +1 -0
- package/dist/services/webhook-setup-service.d.ts +115 -111
- package/dist/services/webhook-setup-service.d.ts.map +1 -1
- package/dist/services/webhook-setup-service.js +377 -209
- package/dist/services/webhook-setup-service.js.map +1 -1
- package/dist/types/api.d.ts +3 -0
- package/dist/types/api.d.ts.map +1 -1
- package/dist/types/api.js +1 -0
- package/dist/types/api.js.map +1 -1
- package/dist/types/monitor.d.ts +15 -2
- package/dist/types/monitor.d.ts.map +1 -1
- package/dist/worker/clarification-markers.d.ts +62 -0
- package/dist/worker/clarification-markers.d.ts.map +1 -1
- package/dist/worker/clarification-markers.js +101 -0
- package/dist/worker/clarification-markers.js.map +1 -1
- package/dist/worker/clarification-poster.d.ts +102 -6
- package/dist/worker/clarification-poster.d.ts.map +1 -1
- package/dist/worker/clarification-poster.js +299 -89
- package/dist/worker/clarification-poster.js.map +1 -1
- package/dist/worker/claude-cli-worker.d.ts +7 -0
- package/dist/worker/claude-cli-worker.d.ts.map +1 -1
- package/dist/worker/claude-cli-worker.js +7 -3
- package/dist/worker/claude-cli-worker.js.map +1 -1
- package/dist/worker/cli-spawner.d.ts.map +1 -1
- package/dist/worker/cli-spawner.js +2 -0
- package/dist/worker/cli-spawner.js.map +1 -1
- package/dist/worker/config.d.ts +396 -0
- package/dist/worker/config.d.ts.map +1 -1
- package/dist/worker/config.js +142 -0
- package/dist/worker/config.js.map +1 -1
- package/dist/worker/failure-fingerprint.d.ts +41 -0
- package/dist/worker/failure-fingerprint.d.ts.map +1 -0
- package/dist/worker/failure-fingerprint.js +75 -0
- package/dist/worker/failure-fingerprint.js.map +1 -0
- package/dist/worker/label-manager.d.ts +42 -0
- package/dist/worker/label-manager.d.ts.map +1 -1
- package/dist/worker/label-manager.js +108 -4
- package/dist/worker/label-manager.js.map +1 -1
- package/dist/worker/phase-loop.d.ts +30 -0
- package/dist/worker/phase-loop.d.ts.map +1 -1
- package/dist/worker/phase-loop.js +158 -27
- package/dist/worker/phase-loop.js.map +1 -1
- package/dist/worker/pr-feedback-handler.d.ts +13 -0
- package/dist/worker/pr-feedback-handler.d.ts.map +1 -1
- package/dist/worker/pr-feedback-handler.js +58 -3
- package/dist/worker/pr-feedback-handler.js.map +1 -1
- package/dist/worker/stage-comment-manager.d.ts +5 -0
- package/dist/worker/stage-comment-manager.d.ts.map +1 -1
- package/dist/worker/stage-comment-manager.js +8 -1
- package/dist/worker/stage-comment-manager.js.map +1 -1
- package/dist/worker/terminal-label-op-error.d.ts +1 -1
- package/dist/worker/terminal-label-op-error.d.ts.map +1 -1
- package/dist/worker/terminal-label-op-error.js.map +1 -1
- package/dist/worker/types.d.ts +51 -0
- package/dist/worker/types.d.ts.map +1 -1
- package/dist/worker/types.js +11 -0
- package/dist/worker/types.js.map +1 -1
- package/package.json +11 -7
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #953 — Shared adaptive-polling decision function used by
|
|
3
|
+
* `LabelMonitorService`, `PrFeedbackMonitorService`, and
|
|
4
|
+
* `MergeConflictMonitorService`. Extracted to eliminate the three-way
|
|
5
|
+
* copy-paste that produced the "smee-less cluster stuck at base cadence" bug.
|
|
6
|
+
*
|
|
7
|
+
* Pure: no I/O, no `this`, no `Date.now()`. Time flows in via `params.nowMs`.
|
|
8
|
+
*/
|
|
9
|
+
export interface AdaptivePollParams {
|
|
10
|
+
/** From MonitorState — construction-time constant. */
|
|
11
|
+
webhooksConfigured: boolean;
|
|
12
|
+
/** From service options — reflects the config knob. */
|
|
13
|
+
adaptivePolling: boolean;
|
|
14
|
+
/** Configured base cadence. */
|
|
15
|
+
basePollIntervalMs: number;
|
|
16
|
+
/** Current cadence being run at (for transition detection). */
|
|
17
|
+
currentPollIntervalMs: number;
|
|
18
|
+
/** From MonitorState — null when never received. */
|
|
19
|
+
lastWebhookEvent: number | null;
|
|
20
|
+
/** From MonitorState — current health belief. */
|
|
21
|
+
webhookHealthy: boolean;
|
|
22
|
+
/** Per-service tuning: LabelMonitor=3, PrFeedback=2, MergeConflict=2. */
|
|
23
|
+
adaptiveDivisor: number;
|
|
24
|
+
/** Per-service tuning: 10_000 across all three today. */
|
|
25
|
+
minPollIntervalMs: number;
|
|
26
|
+
/**
|
|
27
|
+
* Injected for testability. Callers pass `Date.now()`.
|
|
28
|
+
* Only consulted on the `webhooksConfigured === true` branch.
|
|
29
|
+
*/
|
|
30
|
+
nowMs: number;
|
|
31
|
+
}
|
|
32
|
+
export type AdaptivePollReason = 'webhooks-not-configured' | 'operator-opt-out' | 'webhook-stale' | 'webhook-recovered' | 'quiet';
|
|
33
|
+
export interface AdaptivePollDecision {
|
|
34
|
+
/** New effective cadence to run at. */
|
|
35
|
+
currentPollIntervalMs: number;
|
|
36
|
+
/** New health belief. Meaningful only when `webhooksConfigured === true`. */
|
|
37
|
+
webhookHealthy: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Whether an edge fired this call. Callers gate log emission on this.
|
|
40
|
+
* - 'to-fast': cadence dropped to the adaptive fast interval
|
|
41
|
+
* - 'to-base': cadence returned to base (webhook recovered)
|
|
42
|
+
* - 'none': no cadence change
|
|
43
|
+
*/
|
|
44
|
+
transition: 'to-fast' | 'to-base' | 'none';
|
|
45
|
+
/** Why the decision was reached — stable per parameter shape. */
|
|
46
|
+
reason: AdaptivePollReason;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Fast interval calculation shared between `decideAdaptivePoll` and callers
|
|
50
|
+
* that need the value at construction time.
|
|
51
|
+
*/
|
|
52
|
+
export declare function computeFastInterval(basePollIntervalMs: number, adaptiveDivisor: number, minPollIntervalMs: number): number;
|
|
53
|
+
/**
|
|
54
|
+
* Decide the next poll cadence given current state and tuning.
|
|
55
|
+
*
|
|
56
|
+
* Branch summary:
|
|
57
|
+
* - webhooksConfigured=false, adaptivePolling=true:
|
|
58
|
+
* cycle 1 → { fast, healthy=false, to-fast, webhooks-not-configured }
|
|
59
|
+
* cycle 2+ → { fast, healthy=false, none, webhooks-not-configured }
|
|
60
|
+
* - webhooksConfigured=false, adaptivePolling=false:
|
|
61
|
+
* always → { base, healthy=true, none, operator-opt-out }
|
|
62
|
+
* - webhooksConfigured=true, lastWebhookEvent===null:
|
|
63
|
+
* always → { unchanged, unchanged, none, quiet }
|
|
64
|
+
* - webhooksConfigured=true, lastWebhookEvent!==null:
|
|
65
|
+
* staleness = nowMs - lastWebhookEvent; threshold = basePoll * 2
|
|
66
|
+
* if (staleness > threshold && webhookHealthy) → to-fast, webhook-stale
|
|
67
|
+
* else → { unchanged, unchanged, none, quiet }
|
|
68
|
+
*
|
|
69
|
+
* The `recordWebhookEvent` handler in each service also delegates here for
|
|
70
|
+
* the recovery path — it flips `webhookHealthy=true` and `lastWebhookEvent`
|
|
71
|
+
* before calling; the helper returns `to-base, webhook-recovered` when the
|
|
72
|
+
* caller was previously at the fast interval.
|
|
73
|
+
*/
|
|
74
|
+
export declare function decideAdaptivePoll(params: AdaptivePollParams): AdaptivePollDecision;
|
|
75
|
+
//# sourceMappingURL=adaptive-poll-controller.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adaptive-poll-controller.d.ts","sourceRoot":"","sources":["../../src/services/adaptive-poll-controller.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,WAAW,kBAAkB;IACjC,sDAAsD;IACtD,kBAAkB,EAAE,OAAO,CAAC;IAC5B,uDAAuD;IACvD,eAAe,EAAE,OAAO,CAAC;IACzB,+BAA+B;IAC/B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,+DAA+D;IAC/D,qBAAqB,EAAE,MAAM,CAAC;IAC9B,oDAAoD;IACpD,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,iDAAiD;IACjD,cAAc,EAAE,OAAO,CAAC;IACxB,yEAAyE;IACzE,eAAe,EAAE,MAAM,CAAC;IACxB,yDAAyD;IACzD,iBAAiB,EAAE,MAAM,CAAC;IAC1B;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,kBAAkB,GAC1B,yBAAyB,GACzB,kBAAkB,GAClB,eAAe,GACf,mBAAmB,GACnB,OAAO,CAAC;AAEZ,MAAM,WAAW,oBAAoB;IACnC,uCAAuC;IACvC,qBAAqB,EAAE,MAAM,CAAC;IAC9B,6EAA6E;IAC7E,cAAc,EAAE,OAAO,CAAC;IACxB;;;;;OAKG;IACH,UAAU,EAAE,SAAS,GAAG,SAAS,GAAG,MAAM,CAAC;IAC3C,iEAAiE;IACjE,MAAM,EAAE,kBAAkB,CAAC;CAC5B;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,kBAAkB,EAAE,MAAM,EAC1B,eAAe,EAAE,MAAM,EACvB,iBAAiB,EAAE,MAAM,GACxB,MAAM,CAER;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,kBAAkB,GAAG,oBAAoB,CA4EnF"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #953 — Shared adaptive-polling decision function used by
|
|
3
|
+
* `LabelMonitorService`, `PrFeedbackMonitorService`, and
|
|
4
|
+
* `MergeConflictMonitorService`. Extracted to eliminate the three-way
|
|
5
|
+
* copy-paste that produced the "smee-less cluster stuck at base cadence" bug.
|
|
6
|
+
*
|
|
7
|
+
* Pure: no I/O, no `this`, no `Date.now()`. Time flows in via `params.nowMs`.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Fast interval calculation shared between `decideAdaptivePoll` and callers
|
|
11
|
+
* that need the value at construction time.
|
|
12
|
+
*/
|
|
13
|
+
export function computeFastInterval(basePollIntervalMs, adaptiveDivisor, minPollIntervalMs) {
|
|
14
|
+
return Math.max(minPollIntervalMs, Math.floor(basePollIntervalMs / adaptiveDivisor));
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Decide the next poll cadence given current state and tuning.
|
|
18
|
+
*
|
|
19
|
+
* Branch summary:
|
|
20
|
+
* - webhooksConfigured=false, adaptivePolling=true:
|
|
21
|
+
* cycle 1 → { fast, healthy=false, to-fast, webhooks-not-configured }
|
|
22
|
+
* cycle 2+ → { fast, healthy=false, none, webhooks-not-configured }
|
|
23
|
+
* - webhooksConfigured=false, adaptivePolling=false:
|
|
24
|
+
* always → { base, healthy=true, none, operator-opt-out }
|
|
25
|
+
* - webhooksConfigured=true, lastWebhookEvent===null:
|
|
26
|
+
* always → { unchanged, unchanged, none, quiet }
|
|
27
|
+
* - webhooksConfigured=true, lastWebhookEvent!==null:
|
|
28
|
+
* staleness = nowMs - lastWebhookEvent; threshold = basePoll * 2
|
|
29
|
+
* if (staleness > threshold && webhookHealthy) → to-fast, webhook-stale
|
|
30
|
+
* else → { unchanged, unchanged, none, quiet }
|
|
31
|
+
*
|
|
32
|
+
* The `recordWebhookEvent` handler in each service also delegates here for
|
|
33
|
+
* the recovery path — it flips `webhookHealthy=true` and `lastWebhookEvent`
|
|
34
|
+
* before calling; the helper returns `to-base, webhook-recovered` when the
|
|
35
|
+
* caller was previously at the fast interval.
|
|
36
|
+
*/
|
|
37
|
+
export function decideAdaptivePoll(params) {
|
|
38
|
+
const { webhooksConfigured, adaptivePolling, basePollIntervalMs, currentPollIntervalMs, lastWebhookEvent, webhookHealthy, adaptiveDivisor, minPollIntervalMs, nowMs, } = params;
|
|
39
|
+
const fastIntervalMs = computeFastInterval(basePollIntervalMs, adaptiveDivisor, minPollIntervalMs);
|
|
40
|
+
if (!webhooksConfigured) {
|
|
41
|
+
if (!adaptivePolling) {
|
|
42
|
+
return {
|
|
43
|
+
currentPollIntervalMs: basePollIntervalMs,
|
|
44
|
+
webhookHealthy: true,
|
|
45
|
+
transition: 'none',
|
|
46
|
+
reason: 'operator-opt-out',
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
// adaptivePolling === true, webhooksConfigured === false:
|
|
50
|
+
// fast interval, unhealthy. Emit `to-fast` on the transition edge only.
|
|
51
|
+
const transition = webhookHealthy ? 'to-fast' : 'none';
|
|
52
|
+
return {
|
|
53
|
+
currentPollIntervalMs: fastIntervalMs,
|
|
54
|
+
webhookHealthy: false,
|
|
55
|
+
transition,
|
|
56
|
+
reason: 'webhooks-not-configured',
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
// webhooksConfigured === true from here on.
|
|
60
|
+
if (lastWebhookEvent === null) {
|
|
61
|
+
// Grace: never received an event, don't ratchet down cadence.
|
|
62
|
+
return {
|
|
63
|
+
currentPollIntervalMs,
|
|
64
|
+
webhookHealthy,
|
|
65
|
+
transition: 'none',
|
|
66
|
+
reason: 'quiet',
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
const staleness = nowMs - lastWebhookEvent;
|
|
70
|
+
const staleThreshold = basePollIntervalMs * 2;
|
|
71
|
+
// Recovery path: caller set webhookHealthy=true before delegating.
|
|
72
|
+
// If we're on the fast interval, transition back to base.
|
|
73
|
+
if (webhookHealthy && currentPollIntervalMs !== basePollIntervalMs) {
|
|
74
|
+
return {
|
|
75
|
+
currentPollIntervalMs: basePollIntervalMs,
|
|
76
|
+
webhookHealthy: true,
|
|
77
|
+
transition: 'to-base',
|
|
78
|
+
reason: 'webhook-recovered',
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
if (staleness > staleThreshold && webhookHealthy) {
|
|
82
|
+
return {
|
|
83
|
+
currentPollIntervalMs: fastIntervalMs,
|
|
84
|
+
webhookHealthy: false,
|
|
85
|
+
transition: 'to-fast',
|
|
86
|
+
reason: 'webhook-stale',
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
currentPollIntervalMs,
|
|
91
|
+
webhookHealthy,
|
|
92
|
+
transition: 'none',
|
|
93
|
+
reason: 'quiet',
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=adaptive-poll-controller.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adaptive-poll-controller.js","sourceRoot":"","sources":["../../src/services/adaptive-poll-controller.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAiDH;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CACjC,kBAA0B,EAC1B,eAAuB,EACvB,iBAAyB;IAEzB,OAAO,IAAI,CAAC,GAAG,CAAC,iBAAiB,EAAE,IAAI,CAAC,KAAK,CAAC,kBAAkB,GAAG,eAAe,CAAC,CAAC,CAAC;AACvF,CAAC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,kBAAkB,CAAC,MAA0B;IAC3D,MAAM,EACJ,kBAAkB,EAClB,eAAe,EACf,kBAAkB,EAClB,qBAAqB,EACrB,gBAAgB,EAChB,cAAc,EACd,eAAe,EACf,iBAAiB,EACjB,KAAK,GACN,GAAG,MAAM,CAAC;IAEX,MAAM,cAAc,GAAG,mBAAmB,CAAC,kBAAkB,EAAE,eAAe,EAAE,iBAAiB,CAAC,CAAC;IAEnG,IAAI,CAAC,kBAAkB,EAAE,CAAC;QACxB,IAAI,CAAC,eAAe,EAAE,CAAC;YACrB,OAAO;gBACL,qBAAqB,EAAE,kBAAkB;gBACzC,cAAc,EAAE,IAAI;gBACpB,UAAU,EAAE,MAAM;gBAClB,MAAM,EAAE,kBAAkB;aAC3B,CAAC;QACJ,CAAC;QACD,0DAA0D;QAC1D,wEAAwE;QACxE,MAAM,UAAU,GAAG,cAAc,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC;QACvD,OAAO;YACL,qBAAqB,EAAE,cAAc;YACrC,cAAc,EAAE,KAAK;YACrB,UAAU;YACV,MAAM,EAAE,yBAAyB;SAClC,CAAC;IACJ,CAAC;IAED,4CAA4C;IAE5C,IAAI,gBAAgB,KAAK,IAAI,EAAE,CAAC;QAC9B,8DAA8D;QAC9D,OAAO;YACL,qBAAqB;YACrB,cAAc;YACd,UAAU,EAAE,MAAM;YAClB,MAAM,EAAE,OAAO;SAChB,CAAC;IACJ,CAAC;IAED,MAAM,SAAS,GAAG,KAAK,GAAG,gBAAgB,CAAC;IAC3C,MAAM,cAAc,GAAG,kBAAkB,GAAG,CAAC,CAAC;IAE9C,mEAAmE;IACnE,0DAA0D;IAC1D,IAAI,cAAc,IAAI,qBAAqB,KAAK,kBAAkB,EAAE,CAAC;QACnE,OAAO;YACL,qBAAqB,EAAE,kBAAkB;YACzC,cAAc,EAAE,IAAI;YACpB,UAAU,EAAE,SAAS;YACrB,MAAM,EAAE,mBAAmB;SAC5B,CAAC;IACJ,CAAC;IAED,IAAI,SAAS,GAAG,cAAc,IAAI,cAAc,EAAE,CAAC;QACjD,OAAO;YACL,qBAAqB,EAAE,cAAc;YACrC,cAAc,EAAE,KAAK;YACrB,UAAU,EAAE,SAAS;YACrB,MAAM,EAAE,eAAe;SACxB,CAAC;IACJ,CAAC;IAED,OAAO;QACL,qBAAqB;QACrB,cAAc;QACd,UAAU,EAAE,MAAM;QAClB,MAAM,EAAE,OAAO;KAChB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #958 T013 — Poll-based monitor that enqueues `continue` resume queue items
|
|
3
|
+
* for issues sitting at `waiting-for:clarification` + `agent:paused` when a
|
|
4
|
+
* new human-authored comment appears.
|
|
5
|
+
*
|
|
6
|
+
* Shape template: `merge-conflict-monitor-service.ts` (spec §Assumptions).
|
|
7
|
+
* Contract: `specs/958-found-during-local-snappoll/contracts/
|
|
8
|
+
* clarification-answer-monitor.md`.
|
|
9
|
+
*
|
|
10
|
+
* Key divergences from the merge-conflict monitor (per data-model.md):
|
|
11
|
+
* - Precondition label: `waiting-for:clarification` (was `waiting-for:merge-conflicts`).
|
|
12
|
+
* - Additional predicate: at least one comment with `viewerDidAuthor === false`
|
|
13
|
+
* that also passes `isTrustedCommentAuthor(_, 'answer-scanner', trustCtx)`.
|
|
14
|
+
* Comments carrying the engine-written answer marker are treated as
|
|
15
|
+
* cluster-self (not counted).
|
|
16
|
+
* - Queue command: `command: 'continue'` (was `'resolve-merge-conflicts'`).
|
|
17
|
+
*
|
|
18
|
+
* Non-behavior (contract):
|
|
19
|
+
* - MUST NOT apply `completed:clarification`. That label is reserved for
|
|
20
|
+
* the human's explicit force-advance override (FR-011).
|
|
21
|
+
* - MUST NOT modify `clarifications.md` — the monitor has no checkout; the
|
|
22
|
+
* phase loop performs integration on the resume path.
|
|
23
|
+
* - MUST NOT clear `waiting-for:clarification` or `agent:paused`.
|
|
24
|
+
*/
|
|
25
|
+
import { type GitHubClientFactory } from '@generacy-ai/workflow-engine';
|
|
26
|
+
import type { MonitorState, QueueManager } from '../types/monitor.js';
|
|
27
|
+
import type { RepositoryConfig, PrMonitorConfig } from '../config/schema.js';
|
|
28
|
+
import type { Logger } from '../worker/types.js';
|
|
29
|
+
import type { AuthHealthSink } from './label-monitor-service.js';
|
|
30
|
+
export interface ClarificationAnswerEvent {
|
|
31
|
+
owner: string;
|
|
32
|
+
repo: string;
|
|
33
|
+
issueNumber: number;
|
|
34
|
+
issueLabels: string[];
|
|
35
|
+
source: 'poll';
|
|
36
|
+
}
|
|
37
|
+
export interface ClarificationAnswerMonitorOptions {
|
|
38
|
+
repositories: RepositoryConfig[];
|
|
39
|
+
pollIntervalMs: number;
|
|
40
|
+
adaptivePolling: boolean;
|
|
41
|
+
maxConcurrentPolls: number;
|
|
42
|
+
}
|
|
43
|
+
export declare class ClarificationAnswerMonitorService {
|
|
44
|
+
private readonly logger;
|
|
45
|
+
private readonly createClient;
|
|
46
|
+
private readonly tokenProvider?;
|
|
47
|
+
private readonly queueManager;
|
|
48
|
+
private readonly options;
|
|
49
|
+
private readonly clusterGithubUsername;
|
|
50
|
+
private readonly authHealth;
|
|
51
|
+
private readonly githubAppCredentialId;
|
|
52
|
+
private abortController;
|
|
53
|
+
private state;
|
|
54
|
+
constructor(logger: Logger, createClient: GitHubClientFactory, queueManager: QueueManager, config: PrMonitorConfig, repositories: RepositoryConfig[], clusterGithubUsername?: string, tokenProvider?: () => Promise<string | undefined>, authHealth?: AuthHealthSink, githubAppCredentialId?: string);
|
|
55
|
+
/**
|
|
56
|
+
* Process a clarification-answer event: verify preconditions, check for a
|
|
57
|
+
* new human-authored comment, enqueue a resume via `enqueueIfAbsent`.
|
|
58
|
+
*
|
|
59
|
+
* Returns true if enqueued, false if skipped or duplicate.
|
|
60
|
+
*/
|
|
61
|
+
processClarificationAnswerEvent(event: ClarificationAnswerEvent): Promise<boolean>;
|
|
62
|
+
startPolling(): Promise<void>;
|
|
63
|
+
stopPolling(): void;
|
|
64
|
+
poll(): Promise<void>;
|
|
65
|
+
private pollRepo;
|
|
66
|
+
recordWebhookEvent(): void;
|
|
67
|
+
private updateAdaptivePolling;
|
|
68
|
+
getState(): Readonly<MonitorState>;
|
|
69
|
+
private sleep;
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=clarification-answer-monitor-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clarification-answer-monitor-service.d.ts","sourceRoot":"","sources":["../../src/services/clarification-answer-monitor-service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,EAKL,KAAK,mBAAmB,EACzB,MAAM,8BAA8B,CAAC;AAEtC,OAAO,KAAK,EACV,YAAY,EACZ,YAAY,EAEb,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC7E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAEjD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAQjE,MAAM,WAAW,wBAAwB;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,iCAAiC;IAChD,YAAY,EAAE,gBAAgB,EAAE,CAAC;IACjC,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,OAAO,CAAC;IACzB,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AA4BD,qBAAa,iCAAiC;IAC5C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAsB;IACnD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAoC;IACnE,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAe;IAC5C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoC;IAC5D,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAqB;IAC3D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAiB;IAC5C,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAqB;IAC3D,OAAO,CAAC,eAAe,CAAgC;IACvD,OAAO,CAAC,KAAK,CAAe;gBAG1B,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,mBAAmB,EACjC,YAAY,EAAE,YAAY,EAC1B,MAAM,EAAE,eAAe,EACvB,YAAY,EAAE,gBAAgB,EAAE,EAChC,qBAAqB,CAAC,EAAE,MAAM,EAC9B,aAAa,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,EACjD,UAAU,CAAC,EAAE,cAAc,EAC3B,qBAAqB,CAAC,EAAE,MAAM;IAgChC;;;;;OAKG;IACG,+BAA+B,CACnC,KAAK,EAAE,wBAAwB,GAC9B,OAAO,CAAC,OAAO,CAAC;IA4Gb,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAmCnC,WAAW,IAAI,IAAI;IAQb,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;YAkBb,QAAQ;IAoFtB,kBAAkB,IAAI,IAAI;IAa1B,OAAO,CAAC,qBAAqB;IAqB7B,QAAQ,IAAI,QAAQ,CAAC,YAAY,CAAC;IAQlC,OAAO,CAAC,KAAK;CAcd"}
|
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #958 T013 — Poll-based monitor that enqueues `continue` resume queue items
|
|
3
|
+
* for issues sitting at `waiting-for:clarification` + `agent:paused` when a
|
|
4
|
+
* new human-authored comment appears.
|
|
5
|
+
*
|
|
6
|
+
* Shape template: `merge-conflict-monitor-service.ts` (spec §Assumptions).
|
|
7
|
+
* Contract: `specs/958-found-during-local-snappoll/contracts/
|
|
8
|
+
* clarification-answer-monitor.md`.
|
|
9
|
+
*
|
|
10
|
+
* Key divergences from the merge-conflict monitor (per data-model.md):
|
|
11
|
+
* - Precondition label: `waiting-for:clarification` (was `waiting-for:merge-conflicts`).
|
|
12
|
+
* - Additional predicate: at least one comment with `viewerDidAuthor === false`
|
|
13
|
+
* that also passes `isTrustedCommentAuthor(_, 'answer-scanner', trustCtx)`.
|
|
14
|
+
* Comments carrying the engine-written answer marker are treated as
|
|
15
|
+
* cluster-self (not counted).
|
|
16
|
+
* - Queue command: `command: 'continue'` (was `'resolve-merge-conflicts'`).
|
|
17
|
+
*
|
|
18
|
+
* Non-behavior (contract):
|
|
19
|
+
* - MUST NOT apply `completed:clarification`. That label is reserved for
|
|
20
|
+
* the human's explicit force-advance override (FR-011).
|
|
21
|
+
* - MUST NOT modify `clarifications.md` — the monitor has no checkout; the
|
|
22
|
+
* phase loop performs integration on the resume path.
|
|
23
|
+
* - MUST NOT clear `waiting-for:clarification` or `agent:paused`.
|
|
24
|
+
*/
|
|
25
|
+
import { GhAuthError, isTrustedCommentAuthor, tryLoadCommentTrustConfig, } from '@generacy-ai/workflow-engine';
|
|
26
|
+
import { JitTokenError } from '@generacy-ai/control-plane';
|
|
27
|
+
import { filterByAssignee } from './identity.js';
|
|
28
|
+
import { commentCarriesMachineMarker } from '../worker/clarification-markers.js';
|
|
29
|
+
const WAITING_FOR_CLARIFICATION_LABEL = 'waiting-for:clarification';
|
|
30
|
+
const AGENT_PAUSED_LABEL = 'agent:paused';
|
|
31
|
+
const MIN_POLL_INTERVAL_MS = 10_000;
|
|
32
|
+
const ADAPTIVE_DIVISOR = 2;
|
|
33
|
+
/**
|
|
34
|
+
* Adapter: pino-style logger (WorkerContext.Logger) → workflow-engine Logger.
|
|
35
|
+
* Same bridge used by `clarification-poster.ts` — kept private to this file
|
|
36
|
+
* so the merge-conflict monitor's DI surface stays untouched.
|
|
37
|
+
*/
|
|
38
|
+
function toEngineLogger(logger) {
|
|
39
|
+
return {
|
|
40
|
+
info: (msg, meta) => {
|
|
41
|
+
if (meta && typeof meta === 'object')
|
|
42
|
+
logger.info(meta, msg);
|
|
43
|
+
else
|
|
44
|
+
logger.info(msg);
|
|
45
|
+
},
|
|
46
|
+
warn: (msg, meta) => {
|
|
47
|
+
if (meta && typeof meta === 'object')
|
|
48
|
+
logger.warn(meta, msg);
|
|
49
|
+
else
|
|
50
|
+
logger.warn(msg);
|
|
51
|
+
},
|
|
52
|
+
error: (msg, meta) => {
|
|
53
|
+
if (meta && typeof meta === 'object')
|
|
54
|
+
logger.error(meta, msg);
|
|
55
|
+
else
|
|
56
|
+
logger.error(msg);
|
|
57
|
+
},
|
|
58
|
+
debug: (msg, meta) => {
|
|
59
|
+
if (meta && typeof meta === 'object')
|
|
60
|
+
logger.debug(meta, msg);
|
|
61
|
+
else
|
|
62
|
+
logger.debug(msg);
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
export class ClarificationAnswerMonitorService {
|
|
67
|
+
logger;
|
|
68
|
+
createClient;
|
|
69
|
+
tokenProvider;
|
|
70
|
+
queueManager;
|
|
71
|
+
options;
|
|
72
|
+
clusterGithubUsername;
|
|
73
|
+
authHealth;
|
|
74
|
+
githubAppCredentialId;
|
|
75
|
+
abortController = null;
|
|
76
|
+
state;
|
|
77
|
+
constructor(logger, createClient, queueManager, config, repositories, clusterGithubUsername, tokenProvider, authHealth, githubAppCredentialId) {
|
|
78
|
+
this.logger = logger;
|
|
79
|
+
this.createClient = createClient;
|
|
80
|
+
this.tokenProvider = tokenProvider;
|
|
81
|
+
this.queueManager = queueManager;
|
|
82
|
+
this.clusterGithubUsername = clusterGithubUsername;
|
|
83
|
+
this.authHealth = authHealth ?? { recordResult: () => undefined };
|
|
84
|
+
this.githubAppCredentialId = githubAppCredentialId;
|
|
85
|
+
this.options = {
|
|
86
|
+
repositories,
|
|
87
|
+
pollIntervalMs: config.pollIntervalMs,
|
|
88
|
+
adaptivePolling: config.adaptivePolling,
|
|
89
|
+
maxConcurrentPolls: config.maxConcurrentPolls,
|
|
90
|
+
};
|
|
91
|
+
this.state = {
|
|
92
|
+
isPolling: false,
|
|
93
|
+
webhookHealthy: true,
|
|
94
|
+
lastWebhookEvent: null,
|
|
95
|
+
currentPollIntervalMs: config.pollIntervalMs,
|
|
96
|
+
basePollIntervalMs: config.pollIntervalMs,
|
|
97
|
+
// #953: this monitor is poll-driven and has no webhook feeder signal,
|
|
98
|
+
// so it runs at base cadence (no adaptive backoff). Matches the
|
|
99
|
+
// `false` default the other poll-fed monitors pass.
|
|
100
|
+
webhooksConfigured: false,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
// ==========================================================================
|
|
104
|
+
// Event Processing
|
|
105
|
+
// ==========================================================================
|
|
106
|
+
/**
|
|
107
|
+
* Process a clarification-answer event: verify preconditions, check for a
|
|
108
|
+
* new human-authored comment, enqueue a resume via `enqueueIfAbsent`.
|
|
109
|
+
*
|
|
110
|
+
* Returns true if enqueued, false if skipped or duplicate.
|
|
111
|
+
*/
|
|
112
|
+
async processClarificationAnswerEvent(event) {
|
|
113
|
+
const { owner, repo, issueNumber, issueLabels, source } = event;
|
|
114
|
+
const hasWaitingFor = issueLabels.includes(WAITING_FOR_CLARIFICATION_LABEL);
|
|
115
|
+
const hasPaused = issueLabels.includes(AGENT_PAUSED_LABEL);
|
|
116
|
+
if (!hasWaitingFor || !hasPaused) {
|
|
117
|
+
this.logger.debug({ owner, repo, issueNumber, source, hasWaitingFor, hasPaused }, 'Precondition failed for clarification-answer enqueue (need waiting-for:clarification + agent:paused)');
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
const blockedLabel = issueLabels.find((l) => l.startsWith('blocked:'));
|
|
121
|
+
if (blockedLabel) {
|
|
122
|
+
this.logger.info({ owner, repo, issueNumber, blockedLabel, reason: 'blocked-label-present' }, 'Skipping clarification-answer enqueue while blocked:* label is present');
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
// Fetch comments and require ≥1 human-authored (viewerDidAuthor === false)
|
|
126
|
+
// trusted comment. `viewerDidAuthor === true` comments (cluster-self) do
|
|
127
|
+
// not count as "new human answer" — even if they carry the answer marker,
|
|
128
|
+
// the phase loop is the source of truth for integrating cluster-relayed
|
|
129
|
+
// answers, not the monitor.
|
|
130
|
+
const client = this.createClient(undefined, this.tokenProvider);
|
|
131
|
+
let comments;
|
|
132
|
+
try {
|
|
133
|
+
comments = await client.getIssueCommentsWithViewerAuth(owner, repo, issueNumber);
|
|
134
|
+
}
|
|
135
|
+
catch (err) {
|
|
136
|
+
this.logger.warn({ err: String(err), owner, repo, issueNumber }, 'Failed to fetch comments during clarification-answer event — skipping');
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
const trustConfig = tryLoadCommentTrustConfig(process.cwd(), toEngineLogger(this.logger));
|
|
140
|
+
const trustCtx = {
|
|
141
|
+
logger: toEngineLogger(this.logger),
|
|
142
|
+
...(this.clusterGithubUsername ? { botLogin: this.clusterGithubUsername } : {}),
|
|
143
|
+
...(trustConfig ? { config: trustConfig } : {}),
|
|
144
|
+
};
|
|
145
|
+
let hasHumanTrustedComment = false;
|
|
146
|
+
for (const c of comments) {
|
|
147
|
+
if (commentCarriesMachineMarker(c.body))
|
|
148
|
+
continue;
|
|
149
|
+
const decision = isTrustedCommentAuthor(c, 'answer-scanner', trustCtx);
|
|
150
|
+
if (decision.trusted) {
|
|
151
|
+
hasHumanTrustedComment = true;
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (!hasHumanTrustedComment) {
|
|
156
|
+
this.logger.debug({ owner, repo, issueNumber }, 'No trusted human-authored comment found — nothing to resume on');
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
const workflowLabel = issueLabels.find((l) => l.startsWith('workflow:'));
|
|
160
|
+
const workflowName = workflowLabel
|
|
161
|
+
? workflowLabel.slice('workflow:'.length)
|
|
162
|
+
: 'speckit-feature';
|
|
163
|
+
const queueItem = {
|
|
164
|
+
owner,
|
|
165
|
+
repo,
|
|
166
|
+
issueNumber,
|
|
167
|
+
workflowName,
|
|
168
|
+
command: 'continue',
|
|
169
|
+
priority: Date.now(),
|
|
170
|
+
enqueuedAt: new Date().toISOString(),
|
|
171
|
+
metadata: {},
|
|
172
|
+
queueReason: 'resume',
|
|
173
|
+
};
|
|
174
|
+
const itemKey = `${owner}/${repo}#${issueNumber}`;
|
|
175
|
+
const enqueued = await this.queueManager.enqueueIfAbsent(queueItem);
|
|
176
|
+
if (!enqueued) {
|
|
177
|
+
this.logger.info({ itemKey, reason: 'in-flight', owner, repo, issueNumber }, 'Dropping clarification-answer enqueue (item already in flight)');
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
this.logger.info({
|
|
181
|
+
event: 'clarification-answer-resume-enqueued',
|
|
182
|
+
owner,
|
|
183
|
+
repo,
|
|
184
|
+
issueNumber,
|
|
185
|
+
source,
|
|
186
|
+
}, 'Clarification-answer resume enqueued');
|
|
187
|
+
return true;
|
|
188
|
+
}
|
|
189
|
+
// ==========================================================================
|
|
190
|
+
// Polling
|
|
191
|
+
// ==========================================================================
|
|
192
|
+
async startPolling() {
|
|
193
|
+
if (this.state.isPolling) {
|
|
194
|
+
this.logger.warn('Clarification-answer monitor polling already running');
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
const ac = new AbortController();
|
|
198
|
+
this.abortController = ac;
|
|
199
|
+
this.state.isPolling = true;
|
|
200
|
+
this.logger.info({ intervalMs: this.state.currentPollIntervalMs, repos: this.options.repositories.length }, 'Starting clarification-answer monitor polling');
|
|
201
|
+
while (!ac.signal.aborted) {
|
|
202
|
+
try {
|
|
203
|
+
await this.poll();
|
|
204
|
+
}
|
|
205
|
+
catch (error) {
|
|
206
|
+
this.logger.error({ err: error }, 'Error during clarification-answer poll cycle');
|
|
207
|
+
}
|
|
208
|
+
if (this.options.adaptivePolling) {
|
|
209
|
+
this.updateAdaptivePolling();
|
|
210
|
+
}
|
|
211
|
+
await this.sleep(this.state.currentPollIntervalMs, ac.signal);
|
|
212
|
+
}
|
|
213
|
+
this.state.isPolling = false;
|
|
214
|
+
this.logger.info('Clarification-answer monitor polling loop stopped');
|
|
215
|
+
}
|
|
216
|
+
stopPolling() {
|
|
217
|
+
if (this.abortController) {
|
|
218
|
+
this.abortController.abort();
|
|
219
|
+
this.abortController = null;
|
|
220
|
+
this.logger.info('Clarification-answer monitor polling stop requested');
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
async poll() {
|
|
224
|
+
const repos = this.options.repositories;
|
|
225
|
+
if (repos.length === 0)
|
|
226
|
+
return;
|
|
227
|
+
const semaphore = new Semaphore(this.options.maxConcurrentPolls);
|
|
228
|
+
const pollTasks = repos.map(({ owner, repo }) => semaphore.acquire().then(async (release) => {
|
|
229
|
+
try {
|
|
230
|
+
await this.pollRepo(owner, repo);
|
|
231
|
+
}
|
|
232
|
+
finally {
|
|
233
|
+
release();
|
|
234
|
+
}
|
|
235
|
+
}));
|
|
236
|
+
await Promise.allSettled(pollTasks);
|
|
237
|
+
}
|
|
238
|
+
async pollRepo(owner, repo) {
|
|
239
|
+
const client = this.createClient(undefined, this.tokenProvider);
|
|
240
|
+
let allIssues;
|
|
241
|
+
try {
|
|
242
|
+
allIssues = await client.listIssuesWithLabel(owner, repo, WAITING_FOR_CLARIFICATION_LABEL);
|
|
243
|
+
if (this.githubAppCredentialId) {
|
|
244
|
+
this.authHealth.recordResult(this.githubAppCredentialId, { ok: true });
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
catch (error) {
|
|
248
|
+
if (error instanceof JitTokenError) {
|
|
249
|
+
this.logger.warn({ code: error.code, message: error.message, owner, repo }, 'JIT GitHub token refresh failed — skipping clarification-answer monitor cycle');
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
if (error instanceof GhAuthError) {
|
|
253
|
+
if (this.githubAppCredentialId) {
|
|
254
|
+
this.authHealth.recordResult(this.githubAppCredentialId, { ok: false, statusCode: error.statusCode });
|
|
255
|
+
}
|
|
256
|
+
this.logger.warn({
|
|
257
|
+
credentialId: this.githubAppCredentialId,
|
|
258
|
+
statusCode: error.statusCode,
|
|
259
|
+
owner,
|
|
260
|
+
repo,
|
|
261
|
+
}, 'GitHub authentication failing on clarification-answer monitor — investigate credential refresh chain');
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
this.logger.warn({ err: String(error), owner, repo }, 'Error polling repository for clarification-answer pauses');
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
const issues = filterByAssignee(allIssues, this.clusterGithubUsername, this.logger);
|
|
268
|
+
if (issues.length === 0) {
|
|
269
|
+
this.logger.debug({ owner, repo }, 'No clarification-answer pauses found this cycle');
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
this.logger.info({ owner, repo, count: issues.length }, 'Clarification-answer pauses found');
|
|
273
|
+
for (const issue of issues) {
|
|
274
|
+
const event = {
|
|
275
|
+
owner,
|
|
276
|
+
repo,
|
|
277
|
+
issueNumber: issue.number,
|
|
278
|
+
issueLabels: issue.labels.map((l) => l.name),
|
|
279
|
+
source: 'poll',
|
|
280
|
+
};
|
|
281
|
+
try {
|
|
282
|
+
await this.processClarificationAnswerEvent(event);
|
|
283
|
+
}
|
|
284
|
+
catch (err) {
|
|
285
|
+
this.logger.warn({ err: String(err), owner, repo, issueNumber: issue.number }, 'Error processing clarification-answer event — continuing to next issue');
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
// ==========================================================================
|
|
290
|
+
// Adaptive Polling
|
|
291
|
+
// ==========================================================================
|
|
292
|
+
recordWebhookEvent() {
|
|
293
|
+
this.state.lastWebhookEvent = Date.now();
|
|
294
|
+
const wasUnhealthy = !this.state.webhookHealthy;
|
|
295
|
+
this.state.webhookHealthy = true;
|
|
296
|
+
if (wasUnhealthy) {
|
|
297
|
+
this.state.currentPollIntervalMs = this.state.basePollIntervalMs;
|
|
298
|
+
this.logger.info({ intervalMs: this.state.currentPollIntervalMs }, 'Webhook reconnected, restoring clarification-answer monitor poll interval');
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
updateAdaptivePolling() {
|
|
302
|
+
if (this.state.lastWebhookEvent === null)
|
|
303
|
+
return;
|
|
304
|
+
const timeSinceLastWebhook = Date.now() - this.state.lastWebhookEvent;
|
|
305
|
+
const unhealthyThreshold = this.state.basePollIntervalMs * 2;
|
|
306
|
+
if (timeSinceLastWebhook > unhealthyThreshold && this.state.webhookHealthy) {
|
|
307
|
+
this.state.webhookHealthy = false;
|
|
308
|
+
this.state.currentPollIntervalMs = Math.max(MIN_POLL_INTERVAL_MS, Math.floor(this.state.basePollIntervalMs / ADAPTIVE_DIVISOR));
|
|
309
|
+
this.logger.info({ intervalMs: this.state.currentPollIntervalMs, timeSinceLastWebhook }, 'Webhooks appear unhealthy, increasing clarification-answer poll frequency');
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
// ==========================================================================
|
|
313
|
+
// State access
|
|
314
|
+
// ==========================================================================
|
|
315
|
+
getState() {
|
|
316
|
+
return { ...this.state };
|
|
317
|
+
}
|
|
318
|
+
// ==========================================================================
|
|
319
|
+
// Utilities
|
|
320
|
+
// ==========================================================================
|
|
321
|
+
sleep(ms, signal) {
|
|
322
|
+
return new Promise((resolve) => {
|
|
323
|
+
if (signal.aborted) {
|
|
324
|
+
resolve();
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
const timer = setTimeout(resolve, ms);
|
|
328
|
+
const onAbort = () => {
|
|
329
|
+
clearTimeout(timer);
|
|
330
|
+
resolve();
|
|
331
|
+
};
|
|
332
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Simple semaphore for bounded concurrency. Copy of the merge-conflict
|
|
338
|
+
* monitor's helper — deliberately duplicated to keep the two monitors
|
|
339
|
+
* shape-parallel and diffable.
|
|
340
|
+
*/
|
|
341
|
+
class Semaphore {
|
|
342
|
+
count;
|
|
343
|
+
waiting = [];
|
|
344
|
+
constructor(max) {
|
|
345
|
+
this.count = max;
|
|
346
|
+
}
|
|
347
|
+
async acquire() {
|
|
348
|
+
if (this.count > 0) {
|
|
349
|
+
this.count--;
|
|
350
|
+
return () => this.release();
|
|
351
|
+
}
|
|
352
|
+
return new Promise((resolve) => {
|
|
353
|
+
this.waiting.push(() => {
|
|
354
|
+
this.count--;
|
|
355
|
+
resolve(() => this.release());
|
|
356
|
+
});
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
release() {
|
|
360
|
+
this.count++;
|
|
361
|
+
const next = this.waiting.shift();
|
|
362
|
+
if (next)
|
|
363
|
+
next();
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
//# sourceMappingURL=clarification-answer-monitor-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clarification-answer-monitor-service.js","sourceRoot":"","sources":["../../src/services/clarification-answer-monitor-service.ts"],"names":[],"mappings":"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"}
|