@xn-intenton-z2a/agentic-lib 7.1.85 → 7.1.87

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.
@@ -190,7 +190,10 @@ jobs:
190
190
 
191
191
  dispatch-supervisor:
192
192
  needs: [params, respond]
193
- if: github.repository != 'xn-intenton-z2a/agentic-lib' && needs.respond.outputs.action == 'request-supervisor'
193
+ if: >-
194
+ github.repository != 'xn-intenton-z2a/agentic-lib'
195
+ && needs.respond.outputs.action == 'request-supervisor'
196
+ && inputs.message == ''
194
197
  runs-on: ubuntu-latest
195
198
  steps:
196
199
  - name: Dispatch supervisor workflow
@@ -23,6 +23,7 @@ on:
23
23
 
24
24
  permissions:
25
25
  contents: write
26
+ actions: write
26
27
 
27
28
  jobs:
28
29
  test:
@@ -95,3 +96,24 @@ jobs:
95
96
  echo "::warning::Failed to push screenshot after 3 attempts"
96
97
  fi
97
98
  done
99
+
100
+ # ─── Dispatch fix workflow when tests fail on main ─────────────────
101
+ dispatch-fix:
102
+ needs: [test, behaviour]
103
+ if: >-
104
+ always()
105
+ && github.ref == 'refs/heads/main'
106
+ && github.event_name != 'pull_request'
107
+ && github.repository != 'xn-intenton-z2a/agentic-lib'
108
+ && (needs.test.result == 'failure' || needs.behaviour.result == 'failure')
109
+ runs-on: ubuntu-latest
110
+ steps:
111
+ - name: Dispatch agentic-lib-workflow to fix broken build
112
+ env:
113
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
114
+ run: |
115
+ echo "Tests failed on main — dispatching agentic-lib-workflow to fix"
116
+ gh workflow run agentic-lib-workflow.yml \
117
+ --repo "${{ github.repository }}" \
118
+ -f mode=full \
119
+ -f message="Build broken on main: agentic-lib-test run ${{ github.run_id }} failed. Please fix."
@@ -438,6 +438,10 @@ jobs:
438
438
  - name: Install project dependencies
439
439
  run: npm ci
440
440
 
441
+ - name: Install Playwright (for behaviour tests)
442
+ if: hashFiles('playwright.config.js') != '' || hashFiles('playwright.config.ts') != ''
443
+ run: npx playwright install --with-deps chromium
444
+
441
445
  - name: Self-init (agentic-lib dev only)
442
446
  if: hashFiles('scripts/self-init.sh') != '' && hashFiles('.github/agentic-lib/actions/agentic-step/package.json') == ''
443
447
  run: bash scripts/self-init.sh
@@ -456,7 +460,7 @@ jobs:
456
460
  echo "mission-complete=false" >> $GITHUB_OUTPUT
457
461
  fi
458
462
 
459
- - name: Find and fix stuck PRs
463
+ - name: Find and fix stuck PRs or broken main build
460
464
  if: steps.fix-mission-check.outputs.mission-complete != 'true'
461
465
  uses: actions/github-script@v8
462
466
  env:
@@ -480,6 +484,7 @@ jobs:
480
484
  owner, repo, state: 'open', per_page: 10,
481
485
  });
482
486
 
487
+ let foundPR = false;
483
488
  for (const pr of openPRs) {
484
489
  const hasAutomerge = pr.labels.some(l => l.name === 'automerge');
485
490
  if (!hasAutomerge) continue;
@@ -514,9 +519,42 @@ jobs:
514
519
  core.info(`Will attempt to fix PR #${pr.number} (${reason})`);
515
520
  core.exportVariable('FIX_PR_NUMBER', String(pr.number));
516
521
  core.exportVariable('FIX_REASON', reason);
522
+ foundPR = true;
517
523
  break;
518
524
  }
519
525
 
526
+ // If no stuck PR found, check for broken main build
527
+ if (!foundPR) {
528
+ try {
529
+ const { data: testRuns } = await github.rest.actions.listWorkflowRuns({
530
+ owner, repo,
531
+ workflow_id: 'agentic-lib-test.yml',
532
+ branch: 'main',
533
+ per_page: 1,
534
+ });
535
+ if (testRuns.workflow_runs.length > 0) {
536
+ const latestTest = testRuns.workflow_runs[0];
537
+ if (latestTest.conclusion === 'failure') {
538
+ // Check we haven't already opened a fix branch for this
539
+ const { data: existingPRs } = await github.rest.pulls.list({
540
+ owner, repo, state: 'open', head: `${owner}:agentic-lib-fix-main-build`, per_page: 1,
541
+ });
542
+ if (existingPRs.length > 0) {
543
+ core.info(`Fix PR already open for main build (#${existingPRs[0].number}) — skipping`);
544
+ } else {
545
+ core.info(`Main build broken (run ${latestTest.id}) — will create fix branch`);
546
+ core.exportVariable('FIX_MAIN_BUILD', 'true');
547
+ core.exportVariable('FIX_RUN_ID', String(latestTest.id));
548
+ }
549
+ } else {
550
+ core.info(`Latest agentic-lib-test on main: ${latestTest.conclusion} — no fix needed`);
551
+ }
552
+ }
553
+ } catch (err) {
554
+ core.warning(`Could not check main build status: ${err.message}`);
555
+ }
556
+ }
557
+
520
558
  - name: Checkout PR branch
521
559
  if: env.FIX_PR_NUMBER != '' && steps.fix-mission-check.outputs.mission-complete != 'true'
522
560
  run: |
@@ -524,6 +562,11 @@ jobs:
524
562
  env:
525
563
  GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
526
564
 
565
+ - name: Create fix branch for broken main build
566
+ if: env.FIX_MAIN_BUILD == 'true' && steps.fix-mission-check.outputs.mission-complete != 'true'
567
+ run: |
568
+ git checkout -b agentic-lib-fix-main-build
569
+
527
570
  - name: "Tier 1: Auto-resolve trivial merge conflicts"
528
571
  if: env.FIX_PR_NUMBER != '' && env.FIX_REASON == 'merge conflicts' && steps.fix-mission-check.outputs.mission-complete != 'true'
529
572
  id: trivial-resolve
@@ -573,12 +616,52 @@ jobs:
573
616
  # test-command read from [execution].test in agentic-lib.toml
574
617
  model: ${{ needs.params.outputs.model }}
575
618
 
619
+ - name: "LLM fix for broken main build"
620
+ if: |
621
+ env.FIX_MAIN_BUILD == 'true' &&
622
+ steps.fix-mission-check.outputs.mission-complete != 'true'
623
+ uses: ./.github/agentic-lib/actions/agentic-step
624
+ env:
625
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
626
+ COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
627
+ FIX_RUN_ID: ${{ env.FIX_RUN_ID }}
628
+ with:
629
+ task: "fix-code"
630
+ config: ${{ needs.params.outputs.config-path }}
631
+ instructions: ".github/agentic-lib/agents/agent-apply-fix.md"
632
+ model: ${{ needs.params.outputs.model }}
633
+
634
+ - name: Build website and run behaviour tests (fix)
635
+ if: |
636
+ steps.fix-mission-check.outputs.mission-complete != 'true' &&
637
+ (env.FIX_PR_NUMBER != '' || env.FIX_MAIN_BUILD == 'true')
638
+ run: |
639
+ npm run build:web 2>/dev/null || echo "No build:web script"
640
+ npm run --if-present test:behaviour || echo "Behaviour tests failed (non-blocking for fix)"
641
+
576
642
  - name: Commit and push fixes
577
643
  if: github.repository != 'xn-intenton-z2a/agentic-lib' && env.FIX_PR_NUMBER != '' && steps.fix-mission-check.outputs.mission-complete != 'true'
578
644
  uses: ./.github/agentic-lib/actions/commit-if-changed
579
645
  with:
580
646
  commit-message: "agentic-step: fix failing tests / resolve conflicts"
581
647
 
648
+ - name: Commit, push, and open PR for main build fix
649
+ if: github.repository != 'xn-intenton-z2a/agentic-lib' && env.FIX_MAIN_BUILD == 'true' && steps.fix-mission-check.outputs.mission-complete != 'true'
650
+ env:
651
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
652
+ run: |
653
+ if git diff --quiet && git diff --cached --quiet; then
654
+ echo "No changes to commit"
655
+ exit 0
656
+ fi
657
+ git add -A
658
+ git commit -m "agentic-step: fix broken main build (run ${{ env.FIX_RUN_ID }})"
659
+ git push -u origin agentic-lib-fix-main-build
660
+ gh pr create \
661
+ --title "fix: auto-fix broken main build" \
662
+ --body "Automated fix for failing agentic-lib-test run [${{ env.FIX_RUN_ID }}](https://github.com/${{ github.repository }}/actions/runs/${{ env.FIX_RUN_ID }})." \
663
+ --label automerge
664
+
582
665
  # ─── Review: close resolved issues, enhance with criteria ──────────
583
666
  review-features:
584
667
  needs: [params, supervisor]
@@ -645,6 +728,10 @@ jobs:
645
728
  - name: Install project dependencies
646
729
  run: npm ci
647
730
 
731
+ - name: Install Playwright (for behaviour tests)
732
+ if: hashFiles('playwright.config.js') != '' || hashFiles('playwright.config.ts') != ''
733
+ run: npx playwright install --with-deps chromium
734
+
648
735
  - name: Self-init (agentic-lib dev only)
649
736
  if: hashFiles('scripts/self-init.sh') != '' && hashFiles('.github/agentic-lib/actions/agentic-step/package.json') == ''
650
737
  run: bash scripts/self-init.sh
@@ -739,18 +826,26 @@ jobs:
739
826
  CONFIG="${{ needs.params.outputs.config-path }}"
740
827
  TEST_CMD=$(grep -E '^\s*test\s*=' "$CONFIG" 2>/dev/null | sed 's/.*=\s*"\(.*\)"/\1/' | head -1)
741
828
  TEST_CMD="${TEST_CMD:-npm ci && npm test}"
742
- echo "Running: $TEST_CMD"
829
+ echo "Running unit tests: $TEST_CMD"
743
830
  set +e
744
831
  eval "$TEST_CMD" 2>&1 | tail -30
745
832
  EXIT_CODE=$?
746
833
  set -e
747
834
  if [ $EXIT_CODE -ne 0 ]; then
748
835
  echo "tests-passed=false" >> $GITHUB_OUTPUT
749
- echo "WARNING: Tests failed (exit $EXIT_CODE) — skipping commit and PR"
836
+ echo "WARNING: Unit tests failed (exit $EXIT_CODE) — skipping commit and PR"
837
+ exit 0
838
+ fi
839
+ # Run behaviour tests if available
840
+ if npm run --if-present test:behaviour; then
841
+ echo "Behaviour tests passed"
750
842
  else
751
- echo "tests-passed=true" >> $GITHUB_OUTPUT
752
- echo "Tests passed"
843
+ echo "tests-passed=false" >> $GITHUB_OUTPUT
844
+ echo "WARNING: Behaviour tests failed — skipping commit and PR"
845
+ exit 0
753
846
  fi
847
+ echo "tests-passed=true" >> $GITHUB_OUTPUT
848
+ echo "All tests passed"
754
849
 
755
850
  - name: Commit and push
756
851
  if: github.repository != 'xn-intenton-z2a/agentic-lib' && steps.issue.outputs.issue-number != '' && needs.params.outputs.dry-run != 'true' && steps.pre-commit-test.outputs.tests-passed == 'true'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xn-intenton-z2a/agentic-lib",
3
- "version": "7.1.85",
3
+ "version": "7.1.87",
4
4
  "description": "Agentic-lib Agentic Coding Systems SDK powering automated GitHub workflows.",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -121,9 +121,67 @@ async function resolveConflicts({ config, pr, prNumber, instructions, model, wri
121
121
  }
122
122
 
123
123
  /**
124
- * Fix failing code or resolve merge conflicts on a pull request.
124
+ * Fix a broken main branch build.
125
+ * Called when no PR is involved — just a failing workflow run on main.
126
+ */
127
+ async function fixMainBuild({ config, runId, instructions, model, writablePaths, testCommand }) {
128
+ const logContent = fetchRunLog(runId);
129
+ if (!logContent) {
130
+ core.info(`Could not fetch log for run ${runId}. Returning nop.`);
131
+ return { outcome: "nop", details: `Could not fetch log for run ${runId}` };
132
+ }
133
+
134
+ const agentInstructions = instructions || "Fix the failing tests by modifying the source code.";
135
+ const readOnlyPaths = config.readOnlyPaths;
136
+
137
+ const prompt = [
138
+ "## Instructions",
139
+ agentInstructions,
140
+ "",
141
+ "## Broken Build on Main",
142
+ `Workflow run ${runId} failed on the main branch.`,
143
+ "Fix the code so that the build passes.",
144
+ "",
145
+ "## Failed Run Log",
146
+ logContent,
147
+ "",
148
+ formatPathsSection(writablePaths, readOnlyPaths, config),
149
+ "",
150
+ "## Constraints",
151
+ `- Run \`${testCommand}\` to validate your fixes`,
152
+ "- Make minimal changes to fix the failing tests",
153
+ "- Do not introduce new features — focus on making the build green",
154
+ ].join("\n");
155
+
156
+ const t = config.tuning || {};
157
+ const { tokensUsed, inputTokens, outputTokens, cost, content: resultContent } = await runCopilotTask({
158
+ model,
159
+ systemMessage: `You are an autonomous coding agent fixing a broken build on the main branch. The test/build workflow has failed. Analyze the error log and make minimal, targeted changes to fix it.` + NARRATIVE_INSTRUCTION,
160
+ prompt,
161
+ writablePaths,
162
+ tuning: t,
163
+ });
164
+
165
+ core.info(`Main build fix completed (${tokensUsed} tokens)`);
166
+
167
+ return {
168
+ outcome: "fix-applied",
169
+ tokensUsed,
170
+ inputTokens,
171
+ outputTokens,
172
+ cost,
173
+ model,
174
+ details: `Applied fix for broken main build (run ${runId})`,
175
+ narrative: extractNarrative(resultContent, `Fixed broken main build (run ${runId}).`),
176
+ };
177
+ }
178
+
179
+ /**
180
+ * Fix failing code or resolve merge conflicts on a pull request,
181
+ * or fix a broken build on main.
125
182
  *
126
- * Priority: conflicts first (if NON_TRIVIAL_FILES env is set), then failing checks.
183
+ * Priority: main build fix (if FIX_RUN_ID env is set),
184
+ * then conflicts (if NON_TRIVIAL_FILES env is set), then failing checks.
127
185
  *
128
186
  * @param {Object} context - Task context from index.js
129
187
  * @returns {Promise<Object>} Result with outcome, tokensUsed, model
@@ -131,8 +189,14 @@ async function resolveConflicts({ config, pr, prNumber, instructions, model, wri
131
189
  export async function fixCode(context) {
132
190
  const { octokit, repo, config, prNumber, instructions, writablePaths, testCommand, model } = context;
133
191
 
192
+ // Fix main build (no PR involved)
193
+ const fixRunId = process.env.FIX_RUN_ID || "";
194
+ if (fixRunId && !prNumber) {
195
+ return fixMainBuild({ config, runId: fixRunId, instructions, model, writablePaths, testCommand });
196
+ }
197
+
134
198
  if (!prNumber) {
135
- throw new Error("fix-code task requires pr-number input");
199
+ throw new Error("fix-code task requires pr-number input or FIX_RUN_ID env var");
136
200
  }
137
201
 
138
202
  // Fetch the PR
@@ -71,6 +71,36 @@ async function dispatchBot(octokit, repo, message, discussionUrl) {
71
71
  }
72
72
  }
73
73
 
74
+ /**
75
+ * Post a comment directly to a discussion via GraphQL.
76
+ * Used for supervisor-originated messages to avoid triggering the bot workflow
77
+ * (which could loop back via request-supervisor).
78
+ */
79
+ async function postDirectReply(octokit, repo, nodeId, body) {
80
+ if (process.env.GITHUB_REPOSITORY === "xn-intenton-z2a/agentic-lib") {
81
+ core.info("Skipping direct reply — running in SDK repo");
82
+ return;
83
+ }
84
+ if (!nodeId || !body) {
85
+ core.warning(`Cannot post direct reply: ${!nodeId ? "no nodeId" : "no body"}`);
86
+ return;
87
+ }
88
+ try {
89
+ const mutation = `mutation($discussionId: ID!, $body: String!) {
90
+ addDiscussionComment(input: { discussionId: $discussionId, body: $body }) {
91
+ comment { url }
92
+ }
93
+ }`;
94
+ const { addDiscussionComment } = await octokit.graphql(mutation, {
95
+ discussionId: nodeId,
96
+ body,
97
+ });
98
+ core.info(`Posted direct reply: ${addDiscussionComment.comment.url}`);
99
+ } catch (err) {
100
+ core.warning(`Could not post direct reply: ${err.message}`);
101
+ }
102
+ }
103
+
74
104
  async function gatherContext(octokit, repo, config, t) {
75
105
  const mission = readOptionalFile(config.paths.mission.path);
76
106
  const intentionLogFull = readOptionalFile(config.intentionBot.intentionFilepath);
@@ -100,11 +130,21 @@ async function gatherContext(octokit, repo, config, t) {
100
130
  // Extract discussion URL from recent activity for supervisor reporting
101
131
  const discussionUrlMatch = recentActivity.match(/https:\/\/github\.com\/[^/]+\/[^/]+\/discussions\/\d+/);
102
132
  let activeDiscussionUrl = discussionUrlMatch ? discussionUrlMatch[0] : "";
133
+ let activeDiscussionNodeId = "";
103
134
 
104
135
  // Fallback: look up the "Talk to the repository" discussion if not found in activity log
105
136
  if (!activeDiscussionUrl) {
106
137
  const talk = await findTalkDiscussion(octokit, repo);
107
138
  activeDiscussionUrl = talk.url;
139
+ activeDiscussionNodeId = talk.nodeId;
140
+ }
141
+
142
+ // Resolve node ID from URL if we got the URL from activity log
143
+ if (activeDiscussionUrl && !activeDiscussionNodeId) {
144
+ const talk = await findTalkDiscussion(octokit, repo);
145
+ if (talk.url === activeDiscussionUrl) {
146
+ activeDiscussionNodeId = talk.nodeId;
147
+ }
108
148
  }
109
149
 
110
150
  const featuresPath = config.paths.features.path;
@@ -271,6 +311,7 @@ async function gatherContext(octokit, repo, config, t) {
271
311
  featureIssuesWipLimit: config.featureDevelopmentIssuesWipLimit,
272
312
  maintenanceIssuesWipLimit: config.maintenanceIssuesWipLimit,
273
313
  activeDiscussionUrl,
314
+ activeDiscussionNodeId,
274
315
  missionComplete,
275
316
  missionCompleteInfo,
276
317
  missionFailed,
@@ -670,14 +711,15 @@ export async function supervise(context) {
670
711
  // Step 2: Auto-announce on first run after init
671
712
  // Detect first supervisor run: initTimestamp exists but no prior supervisor workflow runs since init
672
713
  if (ctx.initTimestamp && !ctx.missionComplete && !ctx.missionFailed) {
673
- const hasPriorSupervisor = ctx.actionsSinceInit.some(
674
- (a) => a.name === "agentic-lib-workflow" && a.conclusion === "success" &&
675
- a.commitMessage?.toLowerCase().includes("supervisor"),
676
- ) || ctx.recentActivity.includes("supervised:");
714
+ // Check for any prior agentic-lib-workflow runs since init (count > 1 because current run is included)
715
+ const supervisorRunCount = ctx.actionsSinceInit.filter(
716
+ (a) => a.name === "agentic-lib-workflow",
717
+ ).length;
718
+ const hasPriorSupervisor = supervisorRunCount > 1 || ctx.recentActivity.includes("supervised:");
677
719
  if (!hasPriorSupervisor && ctx.mission && ctx.activeDiscussionUrl) {
678
- core.info("First supervisor run after init — announcing mission");
720
+ core.info("First supervisor run after init — announcing mission directly");
679
721
  const announcement = `New mission started!\n\n**Mission:** ${ctx.mission.substring(0, 300)}\n\n**Website:** ${websiteUrl}`;
680
- await dispatchBot(octokit, repo, announcement, ctx.activeDiscussionUrl);
722
+ await postDirectReply(octokit, repo, ctx.activeDiscussionNodeId, announcement);
681
723
  }
682
724
  }
683
725
 
@@ -738,16 +780,18 @@ export async function supervise(context) {
738
780
 
739
781
  // Step 3: Auto-respond when a message referral is present
740
782
  // If the workflow was triggered with a message (from bot's request-supervisor),
741
- // and the LLM didn't include a respond:discussions action, post back automatically
783
+ // and the LLM didn't include a respond:discussions action, post back directly.
784
+ // Posts directly via GraphQL to avoid triggering the bot workflow (which would
785
+ // request-supervisor again, creating an infinite loop).
742
786
  const workflowMessage = context.discussionUrl ? "" : (process.env.INPUT_MESSAGE || "");
743
787
  if (workflowMessage && ctx.activeDiscussionUrl) {
744
788
  const hasDiscussionResponse = results.some((r) => r.startsWith("respond-discussions:"));
745
789
  if (!hasDiscussionResponse) {
746
- core.info("Message referral detected — auto-responding to discussion");
790
+ core.info("Message referral detected — posting auto-response directly");
747
791
  const response = reasoning
748
792
  ? `Supervisor update: ${reasoning.substring(0, 400)}`
749
793
  : `Supervisor processed your request. Actions taken: ${results.join(", ")}`;
750
- await dispatchBot(octokit, repo, response, ctx.activeDiscussionUrl);
794
+ await postDirectReply(octokit, repo, ctx.activeDiscussionNodeId, response);
751
795
  results.push(`auto-respond:${ctx.activeDiscussionUrl}`);
752
796
  }
753
797
  }
@@ -15,6 +15,10 @@ do it. Don't limit yourself to the minimal fix when you can deliver the whole mi
15
15
  The repository has a website in `src/web/` that uses the JS library. If a fix affects library
16
16
  exports or behaviour, also update the website files to stay in sync.
17
17
 
18
+ **Both unit tests AND behaviour tests must pass.** If the project has a `test:behaviour` npm script
19
+ (typically Playwright-based, testing the website), run it after your changes. If the behaviour test
20
+ expects elements like `#demo-output`, `#lib-name`, or `#lib-version`, ensure the website provides them.
21
+
18
22
  ## Merge Conflict Resolution
19
23
 
20
24
  When resolving merge conflicts (files containing <<<<<<< / ======= / >>>>>>> markers):
@@ -21,6 +21,11 @@ When writing both tests and implementation:
21
21
  - Ensure the implementation passes those tests before delivering
22
22
  - Do not write tests with different expectations than the implementation produces
23
23
 
24
+ **Both unit tests AND behaviour tests must pass.** If the project has a `test:behaviour` npm script
25
+ (typically Playwright-based, testing the website), run it after your changes. If your changes affect
26
+ the website (`src/web/`) or library exports, ensure the behaviour test still finds the expected
27
+ elements (e.g. `#demo-output`, `#lib-name`, `#lib-version`).
28
+
24
29
  Follow the linting guidelines and the formatting guidelines from the included config.
25
30
 
26
31
  ## Evidence Gathering
@@ -17,7 +17,7 @@
17
17
  "author": "",
18
18
  "license": "MIT",
19
19
  "dependencies": {
20
- "@xn-intenton-z2a/agentic-lib": "^7.1.85"
20
+ "@xn-intenton-z2a/agentic-lib": "^7.1.87"
21
21
  },
22
22
  "devDependencies": {
23
23
  "@playwright/test": "^1.58.0",