@xn-intenton-z2a/agentic-lib 7.4.8 → 7.4.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/{src → .github}/agents/agent-apply-fix.md +10 -0
  2. package/{src → .github}/agents/agent-director.md +10 -0
  3. package/{src → .github}/agents/agent-discovery.md +8 -0
  4. package/{src → .github}/agents/agent-discussion-bot.md +9 -0
  5. package/{src → .github}/agents/agent-issue-resolution.md +12 -0
  6. package/{src → .github}/agents/agent-iterate.md +8 -0
  7. package/{src → .github}/agents/agent-maintain-features.md +8 -0
  8. package/{src → .github}/agents/agent-maintain-library.md +7 -0
  9. package/{src → .github}/agents/agent-review-issue.md +8 -0
  10. package/{src → .github}/agents/agent-supervisor.md +9 -0
  11. package/.github/workflows/agentic-lib-test.yml +4 -2
  12. package/.github/workflows/agentic-lib-workflow.yml +70 -26
  13. package/README.md +5 -7
  14. package/agentic-lib.toml +16 -38
  15. package/bin/agentic-lib.js +49 -60
  16. package/package.json +3 -4
  17. package/src/actions/agentic-step/action.yml +1 -1
  18. package/src/actions/agentic-step/copilot.js +0 -5
  19. package/src/actions/agentic-step/index.js +8 -1
  20. package/src/actions/agentic-step/logging.js +14 -2
  21. package/src/actions/agentic-step/tasks/direct.js +86 -65
  22. package/src/actions/agentic-step/tasks/discussions.js +198 -264
  23. package/src/actions/agentic-step/tasks/enhance-issue.js +84 -33
  24. package/src/actions/agentic-step/tasks/fix-code.js +111 -57
  25. package/src/actions/agentic-step/tasks/maintain-features.js +69 -52
  26. package/src/actions/agentic-step/tasks/maintain-library.js +57 -19
  27. package/src/actions/agentic-step/tasks/resolve-issue.js +43 -18
  28. package/src/actions/agentic-step/tasks/review-issue.js +117 -117
  29. package/src/actions/agentic-step/tasks/supervise.js +140 -151
  30. package/src/actions/agentic-step/tasks/transform.js +106 -258
  31. package/src/copilot/agents.js +2 -2
  32. package/src/copilot/config.js +2 -18
  33. package/src/copilot/{hybrid-session.js → copilot-session.js} +39 -7
  34. package/src/copilot/github-tools.js +514 -0
  35. package/src/copilot/guards.js +1 -1
  36. package/src/copilot/session.js +0 -141
  37. package/src/copilot/tools.js +4 -0
  38. package/src/iterate.js +1 -1
  39. package/src/scripts/push-to-logs.sh +1 -1
  40. package/src/seeds/zero-SCREENSHOT_INDEX.png +0 -0
  41. package/src/seeds/zero-package.json +1 -1
  42. package/src/agents/agentic-lib.yml +0 -66
  43. package/src/copilot/context.js +0 -457
  44. package/src/mcp/server.js +0 -830
  45. /package/{src → .github}/agents/agent-ready-issue.md +0 -0
@@ -2,18 +2,35 @@
2
2
  // Copyright (C) 2025-2026 Polycode Limited
3
3
  // tasks/enhance-issue.js — Add testable acceptance criteria to issues
4
4
  //
5
- // Takes an issue and enhances it with clear, testable acceptance criteria
6
- // so that the resolve-issue task can implement it effectively.
7
- // Supports batch mode: when no issueNumber is provided, enhances up to 3 issues.
5
+ // Uses runCopilotSession with lean prompts: the model reads features and docs
6
+ // via tools to enhance issues with acceptance criteria.
8
7
 
9
8
  import * as core from "@actions/core";
9
+ import { existsSync, readdirSync } from "fs";
10
10
  import { isIssueResolved } from "../safety.js";
11
- import { runCopilotTask, readOptionalFile, scanDirectory } from "../copilot.js";
11
+ import { readOptionalFile, extractNarrative, NARRATIVE_INSTRUCTION } from "../copilot.js";
12
+ import { runCopilotSession } from "../../../copilot/copilot-session.js";
13
+ import { createGitHubTools } from "../../../copilot/github-tools.js";
14
+
15
+ /**
16
+ * Build a file listing summary (names only).
17
+ */
18
+ function listFiles(dirPath, extension) {
19
+ if (!dirPath || !existsSync(dirPath)) return [];
20
+ try {
21
+ return readdirSync(dirPath, { recursive: true })
22
+ .filter((f) => String(f).endsWith(extension))
23
+ .map((f) => String(f))
24
+ .slice(0, 20);
25
+ } catch {
26
+ return [];
27
+ }
28
+ }
12
29
 
13
30
  /**
14
31
  * Enhance a single GitHub issue with testable acceptance criteria.
15
32
  */
16
- async function enhanceSingleIssue({ octokit, repo, config, issueNumber, instructions, model, tuning: t }) {
33
+ async function enhanceSingleIssue({ octokit, repo, config, issueNumber, instructions, model, tuning: t, logFilePath, screenshotFilePath }) {
17
34
  if (await isIssueResolved(octokit, repo, issueNumber)) {
18
35
  return { outcome: "nop", details: `Issue #${issueNumber} already resolved` };
19
36
  }
@@ -28,10 +45,13 @@ async function enhanceSingleIssue({ octokit, repo, config, issueNumber, instruct
28
45
  }
29
46
 
30
47
  const contributing = readOptionalFile(config.paths.contributing.path);
31
- const features = scanDirectory(config.paths.features.path, ".md", { contentLimit: t.documentSummary || 2000 });
48
+ const featureFiles = listFiles(config.paths.features.path, ".md");
32
49
 
33
50
  const agentInstructions = instructions || "Enhance this issue with clear, testable acceptance criteria.";
34
51
 
52
+ // Shared mutable state to capture the enhanced body
53
+ const enhanceResult = { body: "" };
54
+
35
55
  const prompt = [
36
56
  "## Instructions",
37
57
  agentInstructions,
@@ -39,39 +59,71 @@ async function enhanceSingleIssue({ octokit, repo, config, issueNumber, instruct
39
59
  `## Issue #${issueNumber}: ${issue.title}`,
40
60
  issue.body || "(no description)",
41
61
  "",
42
- contributing ? `## Contributing Guidelines\n${contributing.substring(0, t.documentSummary || 2000)}` : "",
43
- features.length > 0 ? `## Related Features\n${features.map((f) => f.content).join("\n---\n")}` : "",
44
- config.configToml ? `## Configuration (agentic-lib.toml)\n\`\`\`toml\n${config.configToml}\n\`\`\`` : "",
45
- config.packageJson ? `## Dependencies (package.json)\n\`\`\`json\n${config.packageJson}\n\`\`\`` : "",
62
+ contributing ? `## Contributing Guidelines\n${contributing.substring(0, 2000)}` : "",
63
+ featureFiles.length > 0 ? `## Feature Files (read with read_file for details)\n${featureFiles.join(", ")}` : "",
46
64
  "",
47
65
  "## Your Task",
66
+ "Read relevant feature files if needed using read_file.",
48
67
  "Write an enhanced version of this issue body that includes:",
49
68
  "1. Clear problem statement or feature description",
50
69
  "2. Testable acceptance criteria (Given/When/Then or checkbox format)",
51
70
  "3. Implementation hints if applicable",
52
71
  "",
53
- "Output ONLY the new issue body text, no markdown code fences.",
72
+ "**Call report_enhanced_body exactly once** with the improved issue body.",
54
73
  ].join("\n");
55
74
 
56
- const {
57
- content: enhancedBody,
58
- tokensUsed,
59
- inputTokens,
60
- outputTokens,
61
- cost,
62
- } = await runCopilotTask({
75
+ const systemPrompt =
76
+ "You are a requirements analyst. Enhance GitHub issues with clear, testable acceptance criteria." +
77
+ NARRATIVE_INSTRUCTION;
78
+
79
+ const createTools = (defineTool, _wp, logger) => {
80
+ const ghTools = createGitHubTools(octokit, repo, defineTool, logger);
81
+
82
+ const reportEnhancedBody = defineTool("report_enhanced_body", {
83
+ description: "Report the enhanced issue body with testable acceptance criteria. Call this exactly once.",
84
+ parameters: {
85
+ type: "object",
86
+ properties: {
87
+ enhanced_body: { type: "string", description: "The complete enhanced issue body text" },
88
+ },
89
+ required: ["enhanced_body"],
90
+ },
91
+ handler: async ({ enhanced_body }) => {
92
+ enhanceResult.body = enhanced_body;
93
+ return { textResultForLlm: "Enhanced body recorded." };
94
+ },
95
+ });
96
+
97
+ return [...ghTools, reportEnhancedBody];
98
+ };
99
+
100
+ const attachments = [];
101
+ if (logFilePath) attachments.push({ type: "file", path: logFilePath });
102
+ if (screenshotFilePath) attachments.push({ type: "file", path: screenshotFilePath });
103
+
104
+ const result = await runCopilotSession({
105
+ workspacePath: process.cwd(),
63
106
  model,
64
- systemMessage: "You are a requirements analyst. Enhance GitHub issues with clear, testable acceptance criteria.",
65
- prompt,
66
- writablePaths: [],
67
107
  tuning: t,
108
+ agentPrompt: systemPrompt,
109
+ userPrompt: prompt,
110
+ writablePaths: [],
111
+ createTools,
112
+ attachments,
113
+ excludedTools: ["write_file", "run_command", "run_tests", "dispatch_workflow", "close_issue", "label_issue", "post_discussion_comment"],
114
+ logger: { info: core.info, warning: core.warning, error: core.error, debug: core.debug },
68
115
  });
69
116
 
70
- if (enhancedBody.trim()) {
117
+ const tokensUsed = result.tokensIn + result.tokensOut;
118
+
119
+ // Fall back to agent message if report_enhanced_body wasn't called
120
+ const enhancedBody = enhanceResult.body || (result.agentMessage || "").trim();
121
+
122
+ if (enhancedBody) {
71
123
  await octokit.rest.issues.update({
72
124
  ...repo,
73
125
  issue_number: Number(issueNumber),
74
- body: enhancedBody.trim(),
126
+ body: enhancedBody,
75
127
  });
76
128
  await octokit.rest.issues.addLabels({
77
129
  ...repo,
@@ -86,7 +138,6 @@ async function enhanceSingleIssue({ octokit, repo, config, issueNumber, instruct
86
138
  "",
87
139
  `**Task:** enhance-issue`,
88
140
  `**Model:** ${model}`,
89
- `**Features referenced:** ${features.length}`,
90
141
  "",
91
142
  "The issue body has been updated. The `ready` label has been added to indicate it is ready for implementation.",
92
143
  ].join("\n"),
@@ -97,12 +148,12 @@ async function enhanceSingleIssue({ octokit, repo, config, issueNumber, instruct
97
148
  return {
98
149
  outcome: "issue-enhanced",
99
150
  tokensUsed,
100
- inputTokens,
101
- outputTokens,
102
- cost,
151
+ inputTokens: result.tokensIn,
152
+ outputTokens: result.tokensOut,
153
+ cost: 0,
103
154
  model,
104
155
  details: `Enhanced issue #${issueNumber} with acceptance criteria`,
105
- narrative: `Enhanced issue #${issueNumber} with testable acceptance criteria.`,
156
+ narrative: result.narrative || `Enhanced issue #${issueNumber} with testable acceptance criteria.`,
106
157
  };
107
158
  }
108
159
 
@@ -114,12 +165,12 @@ async function enhanceSingleIssue({ octokit, repo, config, issueNumber, instruct
114
165
  * @returns {Promise<Object>} Result with outcome, tokensUsed, model
115
166
  */
116
167
  export async function enhanceIssue(context) {
117
- const { octokit, repo, config, issueNumber, instructions, model } = context;
168
+ const { octokit, repo, config, issueNumber, instructions, model, logFilePath, screenshotFilePath } = context;
118
169
  const t = config.tuning || {};
119
170
 
120
171
  // Single issue mode
121
172
  if (issueNumber) {
122
- return enhanceSingleIssue({ octokit, repo, config, issueNumber, instructions, model, tuning: t });
173
+ return enhanceSingleIssue({ octokit, repo, config, issueNumber, instructions, model, tuning: t, logFilePath, screenshotFilePath });
123
174
  }
124
175
 
125
176
  // Batch mode: find up to 3 unready automated issues
@@ -141,7 +192,6 @@ export async function enhanceIssue(context) {
141
192
  let totalTokens = 0;
142
193
  let totalInputTokens = 0;
143
194
  let totalOutputTokens = 0;
144
- let totalCost = 0;
145
195
 
146
196
  for (const issue of unready) {
147
197
  core.info(`Batch enhancing issue #${issue.number} (${results.length + 1}/${unready.length})`);
@@ -153,12 +203,13 @@ export async function enhanceIssue(context) {
153
203
  instructions,
154
204
  model,
155
205
  tuning: t,
206
+ logFilePath,
207
+ screenshotFilePath,
156
208
  });
157
209
  results.push(result);
158
210
  totalTokens += result.tokensUsed || 0;
159
211
  totalInputTokens += result.inputTokens || 0;
160
212
  totalOutputTokens += result.outputTokens || 0;
161
- totalCost += result.cost || 0;
162
213
  }
163
214
 
164
215
  const enhanced = results.filter((r) => r.outcome === "issue-enhanced").length;
@@ -168,7 +219,7 @@ export async function enhanceIssue(context) {
168
219
  tokensUsed: totalTokens,
169
220
  inputTokens: totalInputTokens,
170
221
  outputTokens: totalOutputTokens,
171
- cost: totalCost,
222
+ cost: 0,
172
223
  model,
173
224
  details: `Batch enhanced ${enhanced}/${results.length} issues. ${results
174
225
  .map((r) => r.details)
@@ -2,14 +2,15 @@
2
2
  // Copyright (C) 2025-2026 Polycode Limited
3
3
  // tasks/fix-code.js — Fix failing tests or resolve merge conflicts on a PR
4
4
  //
5
- // Given a PR number, detects merge conflicts or failing checks and resolves them.
6
- // Conflict resolution: reads files with conflict markers, asks the LLM to resolve.
7
- // Failing checks: analyzes test output, generates code fixes.
5
+ // Uses runCopilotSession with lean prompts: the model reads files, analyzes
6
+ // failures, writes fixes, and runs tests via tools.
8
7
 
9
8
  import * as core from "@actions/core";
10
9
  import { readFileSync } from "fs";
11
10
  import { execSync } from "child_process";
12
- import { runCopilotTask, formatPathsSection, extractNarrative, NARRATIVE_INSTRUCTION } from "../copilot.js";
11
+ import { formatPathsSection, extractNarrative, NARRATIVE_INSTRUCTION } from "../copilot.js";
12
+ import { runCopilotSession } from "../../../copilot/copilot-session.js";
13
+ import { createGitHubTools, createGitTools } from "../../../copilot/github-tools.js";
13
14
 
14
15
  /**
15
16
  * Extract run_id from a check run's details_url.
@@ -40,13 +41,8 @@ function fetchRunLog(runId) {
40
41
 
41
42
  /**
42
43
  * Resolve merge conflicts on a PR using the Copilot SDK.
43
- * Called when the workflow has started a `git merge origin/main` that left
44
- * conflict markers in non-trivial files (listed in NON_TRIVIAL_FILES env var).
45
- *
46
- * @param {Object} params
47
- * @returns {Promise<Object>} Result with outcome, tokensUsed, model
48
44
  */
49
- async function resolveConflicts({ config, pr, prNumber, instructions, model, writablePaths, testCommand }) {
45
+ async function resolveConflicts({ config, pr, prNumber, instructions, model, writablePaths, testCommand, octokit, repo, logFilePath, screenshotFilePath }) {
50
46
  const nonTrivialEnv = process.env.NON_TRIVIAL_FILES || "";
51
47
  const conflictedPaths = nonTrivialEnv
52
48
  .split("\n")
@@ -70,7 +66,6 @@ async function resolveConflicts({ config, pr, prNumber, instructions, model, wri
70
66
  });
71
67
 
72
68
  const agentInstructions = instructions || "Resolve the merge conflicts while preserving the PR's intended changes.";
73
- const readOnlyPaths = config.readOnlyPaths;
74
69
 
75
70
  const prompt = [
76
71
  "## Instructions",
@@ -89,42 +84,60 @@ async function resolveConflicts({ config, pr, prNumber, instructions, model, wri
89
84
  `## Conflicted Files (${conflicts.length})`,
90
85
  ...conflicts.map((f) => `### ${f.name}\n\`\`\`\n${f.content}\n\`\`\``),
91
86
  "",
92
- formatPathsSection(writablePaths, readOnlyPaths, config),
87
+ formatPathsSection(writablePaths, config.readOnlyPaths, config),
93
88
  "",
94
89
  "## Constraints",
95
90
  "- Remove ALL conflict markers (<<<<<<, =======, >>>>>>>)",
96
91
  "- Preserve the PR's feature/fix intent",
97
- `- Run \`${testCommand}\` to validate your resolution`,
92
+ `- Run \`${testCommand}\` via run_tests to validate your resolution`,
98
93
  ].join("\n");
99
94
 
100
95
  const t = config.tuning || {};
101
- const { tokensUsed, inputTokens, outputTokens, cost, content: resultContent } = await runCopilotTask({
96
+ const systemPrompt =
97
+ `You are resolving git merge conflicts on PR #${prNumber}. Write resolved versions of each conflicted file, removing all conflict markers. Preserve the PR's feature intent while incorporating main's updates.` +
98
+ NARRATIVE_INSTRUCTION;
99
+
100
+ const createTools = (defineTool, _wp, logger) => {
101
+ const ghTools = createGitHubTools(octokit, repo, defineTool, logger);
102
+ const gitTools = createGitTools(defineTool, logger);
103
+ return [...ghTools, ...gitTools];
104
+ };
105
+
106
+ const attachments = [];
107
+ if (logFilePath) attachments.push({ type: "file", path: logFilePath });
108
+ if (screenshotFilePath) attachments.push({ type: "file", path: screenshotFilePath });
109
+
110
+ const result = await runCopilotSession({
111
+ workspacePath: process.cwd(),
102
112
  model,
103
- systemMessage: `You are resolving git merge conflicts on PR #${prNumber}. Write resolved versions of each conflicted file, removing all conflict markers. Preserve the PR's feature intent while incorporating main's updates.` + NARRATIVE_INSTRUCTION,
104
- prompt,
105
- writablePaths,
106
113
  tuning: t,
114
+ agentPrompt: systemPrompt,
115
+ userPrompt: prompt,
116
+ writablePaths,
117
+ createTools,
118
+ attachments,
119
+ excludedTools: ["dispatch_workflow", "close_issue", "label_issue", "post_discussion_comment"],
120
+ logger: { info: core.info, warning: core.warning, error: core.error, debug: core.debug },
107
121
  });
108
122
 
109
- core.info(`Conflict resolution completed (${tokensUsed} tokens)`);
123
+ core.info(`Conflict resolution completed (${result.tokensIn + result.tokensOut} tokens)`);
110
124
 
111
125
  return {
112
126
  outcome: "conflicts-resolved",
113
- tokensUsed,
114
- inputTokens,
115
- outputTokens,
116
- cost,
127
+ tokensUsed: result.tokensIn + result.tokensOut,
128
+ inputTokens: result.tokensIn,
129
+ outputTokens: result.tokensOut,
130
+ cost: 0,
117
131
  model,
118
132
  details: `Resolved ${conflicts.length} conflicted file(s) on PR #${prNumber}`,
119
- narrative: extractNarrative(resultContent, `Resolved ${conflicts.length} merge conflict(s) on PR #${prNumber}.`),
133
+ narrative: result.narrative || extractNarrative(result.agentMessage, `Resolved ${conflicts.length} merge conflict(s) on PR #${prNumber}.`),
120
134
  };
121
135
  }
122
136
 
123
137
  /**
124
138
  * Fix a broken main branch build.
125
- * Called when no PR is involved — just a failing workflow run on main.
126
139
  */
127
- async function fixMainBuild({ config, runId, instructions, model, writablePaths, testCommand }) {
140
+ async function fixMainBuild({ config, runId, instructions, model, writablePaths, testCommand, octokit, repo, logFilePath, screenshotFilePath }) {
128
141
  const logContent = fetchRunLog(runId);
129
142
  if (!logContent) {
130
143
  core.info(`Could not fetch log for run ${runId}. Returning nop.`);
@@ -132,7 +145,6 @@ async function fixMainBuild({ config, runId, instructions, model, writablePaths,
132
145
  }
133
146
 
134
147
  const agentInstructions = instructions || "Fix the failing tests by modifying the source code.";
135
- const readOnlyPaths = config.readOnlyPaths;
136
148
 
137
149
  const prompt = [
138
150
  "## Instructions",
@@ -145,34 +157,57 @@ async function fixMainBuild({ config, runId, instructions, model, writablePaths,
145
157
  "## Failed Run Log",
146
158
  logContent,
147
159
  "",
148
- formatPathsSection(writablePaths, readOnlyPaths, config),
160
+ "## Your Task",
161
+ "Use read_file to read the relevant source and test files.",
162
+ "Make minimal changes to fix the failing tests, then run run_tests to verify.",
163
+ "",
164
+ formatPathsSection(writablePaths, config.readOnlyPaths, config),
149
165
  "",
150
166
  "## Constraints",
151
- `- Run \`${testCommand}\` to validate your fixes`,
167
+ `- Run \`${testCommand}\` via run_tests to validate your fixes`,
152
168
  "- Make minimal changes to fix the failing tests",
153
169
  "- Do not introduce new features — focus on making the build green",
154
170
  ].join("\n");
155
171
 
156
172
  const t = config.tuning || {};
157
- const { tokensUsed, inputTokens, outputTokens, cost, content: resultContent } = await runCopilotTask({
173
+ const systemPrompt =
174
+ `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.` +
175
+ NARRATIVE_INSTRUCTION;
176
+
177
+ const createTools = (defineTool, _wp, logger) => {
178
+ const ghTools = createGitHubTools(octokit, repo, defineTool, logger);
179
+ const gitTools = createGitTools(defineTool, logger);
180
+ return [...ghTools, ...gitTools];
181
+ };
182
+
183
+ const attachments = [];
184
+ if (logFilePath) attachments.push({ type: "file", path: logFilePath });
185
+ if (screenshotFilePath) attachments.push({ type: "file", path: screenshotFilePath });
186
+
187
+ const result = await runCopilotSession({
188
+ workspacePath: process.cwd(),
158
189
  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
190
  tuning: t,
191
+ agentPrompt: systemPrompt,
192
+ userPrompt: prompt,
193
+ writablePaths,
194
+ createTools,
195
+ attachments,
196
+ excludedTools: ["dispatch_workflow", "close_issue", "label_issue", "post_discussion_comment"],
197
+ logger: { info: core.info, warning: core.warning, error: core.error, debug: core.debug },
163
198
  });
164
199
 
165
- core.info(`Main build fix completed (${tokensUsed} tokens)`);
200
+ core.info(`Main build fix completed (${result.tokensIn + result.tokensOut} tokens)`);
166
201
 
167
202
  return {
168
203
  outcome: "fix-applied",
169
- tokensUsed,
170
- inputTokens,
171
- outputTokens,
172
- cost,
204
+ tokensUsed: result.tokensIn + result.tokensOut,
205
+ inputTokens: result.tokensIn,
206
+ outputTokens: result.tokensOut,
207
+ cost: 0,
173
208
  model,
174
209
  details: `Applied fix for broken main build (run ${runId})`,
175
- narrative: extractNarrative(resultContent, `Fixed broken main build (run ${runId}).`),
210
+ narrative: result.narrative || extractNarrative(result.agentMessage, `Fixed broken main build (run ${runId}).`),
176
211
  };
177
212
  }
178
213
 
@@ -180,19 +215,16 @@ async function fixMainBuild({ config, runId, instructions, model, writablePaths,
180
215
  * Fix failing code or resolve merge conflicts on a pull request,
181
216
  * or fix a broken build on main.
182
217
  *
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.
185
- *
186
218
  * @param {Object} context - Task context from index.js
187
219
  * @returns {Promise<Object>} Result with outcome, tokensUsed, model
188
220
  */
189
221
  export async function fixCode(context) {
190
- const { octokit, repo, config, prNumber, instructions, writablePaths, testCommand, model } = context;
222
+ const { octokit, repo, config, prNumber, instructions, writablePaths, testCommand, model, logFilePath, screenshotFilePath } = context;
191
223
 
192
224
  // Fix main build (no PR involved)
193
225
  const fixRunId = process.env.FIX_RUN_ID || "";
194
226
  if (fixRunId && !prNumber) {
195
- return fixMainBuild({ config, runId: fixRunId, instructions, model, writablePaths, testCommand });
227
+ return fixMainBuild({ config, runId: fixRunId, instructions, model, writablePaths, testCommand, octokit, repo, logFilePath, screenshotFilePath });
196
228
  }
197
229
 
198
230
  if (!prNumber) {
@@ -204,7 +236,7 @@ export async function fixCode(context) {
204
236
 
205
237
  // If we have non-trivial conflict files from the workflow's Tier 1 step, resolve them
206
238
  if (process.env.NON_TRIVIAL_FILES) {
207
- return resolveConflicts({ config, pr, prNumber, instructions, model, writablePaths, testCommand });
239
+ return resolveConflicts({ config, pr, prNumber, instructions, model, writablePaths, testCommand, octokit, repo, logFilePath, screenshotFilePath });
208
240
  }
209
241
 
210
242
  // Otherwise, check for failing checks
@@ -230,7 +262,6 @@ export async function fixCode(context) {
230
262
  .join("\n\n");
231
263
 
232
264
  const agentInstructions = instructions || "Fix the failing tests by modifying the source code.";
233
- const readOnlyPaths = config.readOnlyPaths;
234
265
 
235
266
  const prompt = [
236
267
  "## Instructions",
@@ -243,32 +274,55 @@ export async function fixCode(context) {
243
274
  "## Failing Checks",
244
275
  failureDetails,
245
276
  "",
246
- formatPathsSection(writablePaths, readOnlyPaths, config),
277
+ "## Your Task",
278
+ "Use read_file to read the relevant source and test files.",
279
+ "Make minimal changes to fix the failures, then run run_tests to verify.",
280
+ "",
281
+ formatPathsSection(writablePaths, config.readOnlyPaths, config),
247
282
  "",
248
283
  "## Constraints",
249
- `- Run \`${testCommand}\` to validate your fixes`,
284
+ `- Run \`${testCommand}\` via run_tests to validate your fixes`,
250
285
  "- Make minimal changes to fix the failing tests",
251
286
  ].join("\n");
252
287
 
253
288
  const t = config.tuning || {};
254
- const { tokensUsed, inputTokens, outputTokens, cost, content: resultContent } = await runCopilotTask({
289
+ const systemPrompt =
290
+ `You are an autonomous coding agent fixing failing tests on PR #${prNumber}. Make minimal, targeted changes to fix the test failures.` +
291
+ NARRATIVE_INSTRUCTION;
292
+
293
+ const createTools = (defineTool, _wp, logger) => {
294
+ const ghTools = createGitHubTools(octokit, repo, defineTool, logger);
295
+ const gitTools = createGitTools(defineTool, logger);
296
+ return [...ghTools, ...gitTools];
297
+ };
298
+
299
+ const attachments = [];
300
+ if (logFilePath) attachments.push({ type: "file", path: logFilePath });
301
+ if (screenshotFilePath) attachments.push({ type: "file", path: screenshotFilePath });
302
+
303
+ const result = await runCopilotSession({
304
+ workspacePath: process.cwd(),
255
305
  model,
256
- systemMessage: `You are an autonomous coding agent fixing failing tests on PR #${prNumber}. Make minimal, targeted changes to fix the test failures.` + NARRATIVE_INSTRUCTION,
257
- prompt,
258
- writablePaths,
259
306
  tuning: t,
307
+ agentPrompt: systemPrompt,
308
+ userPrompt: prompt,
309
+ writablePaths,
310
+ createTools,
311
+ attachments,
312
+ excludedTools: ["dispatch_workflow", "close_issue", "label_issue", "post_discussion_comment"],
313
+ logger: { info: core.info, warning: core.warning, error: core.error, debug: core.debug },
260
314
  });
261
315
 
262
- core.info(`Copilot SDK fix response received (${tokensUsed} tokens)`);
316
+ core.info(`Fix response received (${result.tokensIn + result.tokensOut} tokens)`);
263
317
 
264
318
  return {
265
319
  outcome: "fix-applied",
266
- tokensUsed,
267
- inputTokens,
268
- outputTokens,
269
- cost,
320
+ tokensUsed: result.tokensIn + result.tokensOut,
321
+ inputTokens: result.tokensIn,
322
+ outputTokens: result.tokensOut,
323
+ cost: 0,
270
324
  model,
271
325
  details: `Applied fix for ${failedChecks.length} failing check(s) on PR #${prNumber}`,
272
- narrative: extractNarrative(resultContent, `Fixed ${failedChecks.length} failing check(s) on PR #${prNumber}.`),
326
+ narrative: result.narrative || extractNarrative(result.agentMessage, `Fixed ${failedChecks.length} failing check(s) on PR #${prNumber}.`),
273
327
  };
274
328
  }