@xn-intenton-z2a/agentic-lib 7.1.14 → 7.1.15

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xn-intenton-z2a/agentic-lib",
3
- "version": "7.1.14",
3
+ "version": "7.1.15",
4
4
  "description": "Agentic-lib Agentic Coding Systems SDK powering automated GitHub workflows.",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -3,7 +3,12 @@
3
3
  # .github/workflows/agent-supervisor.yml
4
4
  #
5
5
  # Reactive orchestration — triggers workflows based on telemetry.
6
- # When a build fails, dispatches fix-code. When issues pile up, dispatches review.
6
+ # Watches all agentic workflows and takes corrective or follow-up actions:
7
+ # - Failed builds on PR branches → dispatch fix-code (with loop protection)
8
+ # - Successful merges → dispatch transform to pick up next issue
9
+ # - Discussion bot actions → dispatch maintain or transform as follow-up
10
+ # - Stale issues → dispatch review
11
+ # - Stale conflicting PRs → remove automerge label
7
12
 
8
13
  name: agent-supervisor
9
14
  run-name: "agent-supervisor [${{ github.ref_name }}]"
@@ -14,6 +19,10 @@ on:
14
19
  - test
15
20
  - agent-flow-transform
16
21
  - agent-flow-maintain
22
+ - agent-flow-fix-code
23
+ - agent-flow-review
24
+ - agent-discussions-bot
25
+ - ci-automerge
17
26
  types:
18
27
  - completed
19
28
  workflow_dispatch:
@@ -22,8 +31,14 @@ permissions:
22
31
  actions: write
23
32
  contents: read
24
33
  issues: read
34
+ pull-requests: write
25
35
  checks: read
26
36
 
37
+ env:
38
+ maxFixAttempts: "3"
39
+ staleDays: "14"
40
+ stalePrDays: "3"
41
+
27
42
  jobs:
28
43
  evaluate:
29
44
  runs-on: ubuntu-latest
@@ -32,54 +47,173 @@ jobs:
32
47
  uses: actions/github-script@v7
33
48
  with:
34
49
  script: |
50
+ const owner = context.repo.owner;
51
+ const repo = context.repo.repo;
35
52
  const workflowRun = context.payload.workflow_run;
53
+ const workflowName = workflowRun?.name || '';
54
+ const conclusion = workflowRun?.conclusion || '';
55
+ const branch = workflowRun?.head_branch || '';
56
+ const isAgenticBranch = branch && !['main', 'master'].includes(branch);
57
+ const maxFixAttempts = parseInt(process.env.maxFixAttempts) || 3;
58
+ const staleDays = parseInt(process.env.staleDays) || 14;
59
+ const stalePrDays = parseInt(process.env.stalePrDays) || 3;
36
60
 
37
- // If a workflow failed, trigger fix-code
38
- if (workflowRun && workflowRun.conclusion === 'failure') {
39
- const branch = workflowRun.head_branch;
40
- const isAgenticBranch = !['main', 'master'].includes(branch);
61
+ core.info(`Supervisor: workflow="${workflowName}" conclusion="${conclusion}" branch="${branch}"`);
41
62
 
42
- if (isAgenticBranch) {
43
- core.info(`Workflow "${workflowRun.name}" failed on branch ${branch}. Dispatching fix-code.`);
63
+ // ─── 1. Failure on PR branch → dispatch fix-code (with loop protection) ───
44
64
 
45
- // Find the PR for this branch
65
+ try {
66
+ if (workflowRun && conclusion === 'failure' && isAgenticBranch) {
46
67
  const { data: prs } = await github.rest.pulls.list({
47
- ...context.repo,
48
- state: 'open',
49
- head: `${context.repo.owner}:${branch}`,
68
+ owner, repo, state: 'open',
69
+ head: `${owner}:${branch}`,
50
70
  per_page: 1,
51
71
  });
52
72
 
53
73
  if (prs.length > 0) {
54
- await github.rest.actions.createWorkflowDispatch({
55
- ...context.repo,
74
+ const pr = prs[0];
75
+ const prNumber = pr.number;
76
+
77
+ // Count prior fix-code runs for this branch to prevent infinite loops
78
+ const { data: fixRuns } = await github.rest.actions.listWorkflowRuns({
79
+ owner, repo,
56
80
  workflow_id: 'agent-flow-fix-code.yml',
57
- ref: context.ref,
58
- inputs: { 'pr-number': String(prs[0].number) },
81
+ branch,
82
+ per_page: maxFixAttempts + 1,
59
83
  });
60
- core.info(`Dispatched fix-code for PR #${prs[0].number}`);
84
+ const fixCount = fixRuns.total_count;
85
+
86
+ if (fixCount >= maxFixAttempts) {
87
+ core.info(`PR #${prNumber} has had ${fixCount} fix attempts (limit: ${maxFixAttempts}). Removing automerge label.`);
88
+ try {
89
+ await github.rest.issues.removeLabel({
90
+ owner, repo, issue_number: prNumber, name: 'automerge',
91
+ });
92
+ } catch (e) { /* label may not exist */ }
93
+ await github.rest.issues.createComment({
94
+ owner, repo, issue_number: prNumber,
95
+ body: `Supervisor: ${fixCount} fix-code attempts have failed. Removing automerge label — manual intervention needed.`,
96
+ });
97
+ } else {
98
+ core.info(`Dispatching fix-code for PR #${prNumber} (attempt ${fixCount + 1}/${maxFixAttempts}).`);
99
+ await github.rest.actions.createWorkflowDispatch({
100
+ owner, repo,
101
+ workflow_id: 'agent-flow-fix-code.yml',
102
+ ref: 'main',
103
+ inputs: { 'pr-number': String(prNumber) },
104
+ });
105
+ }
106
+ }
107
+ }
108
+ } catch (err) {
109
+ core.warning(`Fix-code dispatch failed: ${err.message}`);
110
+ }
111
+
112
+ // ─── 2. Successful merge → dispatch transform for next issue ──────────
113
+
114
+ try {
115
+ if (workflowName === 'ci-automerge' && conclusion === 'success') {
116
+ core.info('Automerge succeeded. Dispatching transform to pick up next issue.');
117
+ await github.rest.actions.createWorkflowDispatch({
118
+ owner, repo,
119
+ workflow_id: 'agent-flow-transform.yml',
120
+ ref: 'main',
121
+ });
122
+ }
123
+ } catch (err) {
124
+ core.warning(`Post-merge transform dispatch failed: ${err.message}`);
125
+ }
126
+
127
+ // ─── 3. Discussion bot follow-through ─────────────────────────────────
128
+
129
+ try {
130
+ if (workflowName === 'agent-discussions-bot' && conclusion === 'success') {
131
+ // Check the most recent run's jobs for the outcome
132
+ const runId = workflowRun.id;
133
+ const { data: jobs } = await github.rest.actions.listJobsForWorkflowRun({
134
+ owner, repo, run_id: runId,
135
+ });
136
+ const respondJob = jobs.jobs.find(j => j.name === 'respond');
137
+ if (respondJob) {
138
+ // Look through step names/outputs for action indicators
139
+ const steps = respondJob.steps || [];
140
+ const agenticStep = steps.find(s => s.name === 'Respond to discussion');
141
+ if (agenticStep && agenticStep.conclusion === 'success') {
142
+ core.info('Discussion bot completed. Dispatching maintain to pick up any new features.');
143
+ await github.rest.actions.createWorkflowDispatch({
144
+ owner, repo,
145
+ workflow_id: 'agent-flow-maintain.yml',
146
+ ref: 'main',
147
+ });
148
+ }
61
149
  }
62
150
  }
151
+ } catch (err) {
152
+ core.warning(`Discussion bot follow-through failed: ${err.message}`);
63
153
  }
64
154
 
65
- // Check for stale issues (open > 14 days with no activity)
66
- const { data: issues } = await github.rest.issues.listForRepo({
67
- ...context.repo,
68
- state: 'open',
69
- labels: 'automated',
70
- sort: 'updated',
71
- direction: 'asc',
72
- per_page: 5,
73
- });
74
-
75
- const fourteenDaysAgo = new Date(Date.now() - 14 * 24 * 60 * 60 * 1000);
76
- const staleIssues = issues.filter(i => new Date(i.updated_at) < fourteenDaysAgo);
77
-
78
- if (staleIssues.length > 0) {
79
- core.info(`Found ${staleIssues.length} stale issue(s). Dispatching review.`);
80
- await github.rest.actions.createWorkflowDispatch({
81
- ...context.repo,
82
- workflow_id: 'agent-flow-review.yml',
83
- ref: context.ref,
155
+ // ─── 4. Stale issues dispatch review ────────────────────────────────
156
+
157
+ try {
158
+ const { data: issues } = await github.rest.issues.listForRepo({
159
+ owner, repo, state: 'open',
160
+ labels: 'automated',
161
+ sort: 'updated',
162
+ direction: 'asc',
163
+ per_page: 5,
84
164
  });
165
+
166
+ const cutoff = new Date(Date.now() - staleDays * 24 * 60 * 60 * 1000);
167
+ const staleIssues = issues.filter(i => new Date(i.updated_at) < cutoff);
168
+
169
+ if (staleIssues.length > 0) {
170
+ core.info(`Found ${staleIssues.length} stale issue(s). Dispatching review.`);
171
+ await github.rest.actions.createWorkflowDispatch({
172
+ owner, repo,
173
+ workflow_id: 'agent-flow-review.yml',
174
+ ref: 'main',
175
+ });
176
+ }
177
+ } catch (err) {
178
+ core.warning(`Stale issue check failed: ${err.message}`);
179
+ }
180
+
181
+ // ─── 5. Stale conflicting PRs → remove automerge label ────────────────
182
+
183
+ try {
184
+ const { data: openPRs } = await github.rest.pulls.list({
185
+ owner, repo, state: 'open',
186
+ sort: 'updated',
187
+ direction: 'asc',
188
+ per_page: 10,
189
+ });
190
+
191
+ const prCutoff = new Date(Date.now() - stalePrDays * 24 * 60 * 60 * 1000);
192
+ for (const pr of openPRs) {
193
+ const hasAutomerge = pr.labels.some(l => l.name === 'automerge');
194
+ if (!hasAutomerge) continue;
195
+
196
+ const isStale = new Date(pr.updated_at) < prCutoff;
197
+ if (!isStale) continue;
198
+
199
+ // Fetch full PR to get mergeable_state
200
+ const { data: fullPr } = await github.rest.pulls.get({
201
+ owner, repo, pull_number: pr.number,
202
+ });
203
+
204
+ if (fullPr.mergeable_state === 'dirty' || fullPr.mergeable === false) {
205
+ core.info(`PR #${pr.number} has conflicts and is stale (${stalePrDays}+ days). Removing automerge label.`);
206
+ try {
207
+ await github.rest.issues.removeLabel({
208
+ owner, repo, issue_number: pr.number, name: 'automerge',
209
+ });
210
+ } catch (e) { /* label may not exist */ }
211
+ await github.rest.issues.createComment({
212
+ owner, repo, issue_number: pr.number,
213
+ body: `Supervisor: this PR has had conflicts for ${stalePrDays}+ days. Removing automerge label — resolve conflicts and re-add to retry.`,
214
+ });
215
+ }
216
+ }
217
+ } catch (err) {
218
+ core.warning(`Stale PR check failed: ${err.message}`);
85
219
  }
@@ -356,18 +356,18 @@ jobs:
356
356
  message = `Branch '${pullRequest.head.ref}' deleted.`;
357
357
  prMerged = 'true';
358
358
  } else if (pullRequest.mergeable_state === 'dirty' || pullRequest.mergeable === false) {
359
- message = `PR #${pullNumber} has conflicts. Closing.`;
359
+ message = `PR #${pullNumber} has conflicts. Removing automerge label.`;
360
+ try {
361
+ await github.rest.issues.removeLabel({
362
+ owner, repo, issue_number: pullNumber, name: 'automerge',
363
+ });
364
+ } catch (e) {
365
+ core.info(`Could not remove automerge label: ${e.message}`);
366
+ }
360
367
  await github.rest.issues.createComment({
361
368
  owner, repo, issue_number: pullNumber,
362
- body: `This pull request is being closed due to conflicts (mergeable_state: ${pullRequest.mergeable_state}, mergeable: ${pullRequest.mergeable}).`,
369
+ body: `Automerge label removed this PR has conflicts (mergeable_state: ${pullRequest.mergeable_state}). Resolve conflicts and re-add the label to retry.`,
363
370
  });
364
- await github.rest.pulls.update({ owner, repo, pull_number: pullNumber, state: "closed" });
365
- try {
366
- await github.rest.git.deleteRef({ owner, repo, ref: `heads/${pullRequest.head.ref}` });
367
- message += ` Branch '${pullRequest.head.ref}' deleted.`;
368
- } catch (error) {
369
- message += ` Failed to delete branch: ${error.message}`;
370
- }
371
371
  prMerged = 'false';
372
372
  } else if (pullRequest.mergeable_state === 'unstable') {
373
373
  message = `PR #${pullNumber} checks still running (mergeable_state: unstable). Will retry when test workflow completes.`;