@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.
- package/{src → .github}/agents/agent-apply-fix.md +10 -0
- package/{src → .github}/agents/agent-director.md +10 -0
- package/{src → .github}/agents/agent-discovery.md +8 -0
- package/{src → .github}/agents/agent-discussion-bot.md +9 -0
- package/{src → .github}/agents/agent-issue-resolution.md +12 -0
- package/{src → .github}/agents/agent-iterate.md +8 -0
- package/{src → .github}/agents/agent-maintain-features.md +8 -0
- package/{src → .github}/agents/agent-maintain-library.md +7 -0
- package/{src → .github}/agents/agent-review-issue.md +8 -0
- package/{src → .github}/agents/agent-supervisor.md +9 -0
- package/.github/workflows/agentic-lib-test.yml +4 -2
- package/.github/workflows/agentic-lib-workflow.yml +70 -26
- package/README.md +5 -7
- package/agentic-lib.toml +16 -38
- package/bin/agentic-lib.js +49 -60
- package/package.json +3 -4
- package/src/actions/agentic-step/action.yml +1 -1
- package/src/actions/agentic-step/copilot.js +0 -5
- package/src/actions/agentic-step/index.js +8 -1
- package/src/actions/agentic-step/logging.js +14 -2
- package/src/actions/agentic-step/tasks/direct.js +86 -65
- package/src/actions/agentic-step/tasks/discussions.js +198 -264
- package/src/actions/agentic-step/tasks/enhance-issue.js +84 -33
- package/src/actions/agentic-step/tasks/fix-code.js +111 -57
- package/src/actions/agentic-step/tasks/maintain-features.js +69 -52
- package/src/actions/agentic-step/tasks/maintain-library.js +57 -19
- package/src/actions/agentic-step/tasks/resolve-issue.js +43 -18
- package/src/actions/agentic-step/tasks/review-issue.js +117 -117
- package/src/actions/agentic-step/tasks/supervise.js +140 -151
- package/src/actions/agentic-step/tasks/transform.js +106 -258
- package/src/copilot/agents.js +2 -2
- package/src/copilot/config.js +2 -18
- package/src/copilot/{hybrid-session.js → copilot-session.js} +39 -7
- package/src/copilot/github-tools.js +514 -0
- package/src/copilot/guards.js +1 -1
- package/src/copilot/session.js +0 -141
- package/src/copilot/tools.js +4 -0
- package/src/iterate.js +1 -1
- package/src/scripts/push-to-logs.sh +1 -1
- package/src/seeds/zero-SCREENSHOT_INDEX.png +0 -0
- package/src/seeds/zero-package.json +1 -1
- package/src/agents/agentic-lib.yml +0 -66
- package/src/copilot/context.js +0 -457
- package/src/mcp/server.js +0 -830
- /package/{src → .github}/agents/agent-ready-issue.md +0 -0
|
@@ -2,15 +2,20 @@
|
|
|
2
2
|
// Copyright (C) 2025-2026 Polycode Limited
|
|
3
3
|
// tasks/discussions.js — GitHub Discussions bot
|
|
4
4
|
//
|
|
5
|
-
//
|
|
6
|
-
// and
|
|
5
|
+
// Uses runCopilotSession with discussion tools: the model fetches, searches,
|
|
6
|
+
// and posts to discussions via tools instead of front-loaded prompts.
|
|
7
7
|
|
|
8
8
|
import * as core from "@actions/core";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
9
|
+
import { writeFileSync } from "fs";
|
|
10
|
+
import { readOptionalFile, extractNarrative, NARRATIVE_INSTRUCTION } from "../copilot.js";
|
|
11
|
+
import { runCopilotSession } from "../../../copilot/copilot-session.js";
|
|
12
|
+
import { createDiscussionTools, createGitHubTools } from "../../../copilot/github-tools.js";
|
|
11
13
|
|
|
12
14
|
const BOT_LOGINS = ["github-actions[bot]", "github-actions"];
|
|
13
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Pre-fetch discussion for the lean prompt (gives the model initial context).
|
|
18
|
+
*/
|
|
14
19
|
async function fetchDiscussion(octokit, discussionUrl, commentsLimit = 10) {
|
|
15
20
|
const urlMatch = discussionUrl.match(/github\.com\/([^/]+)\/([^/]+)\/discussions\/(\d+)/);
|
|
16
21
|
if (!urlMatch) {
|
|
@@ -54,38 +59,66 @@ async function fetchDiscussion(octokit, discussionUrl, commentsLimit = 10) {
|
|
|
54
59
|
}
|
|
55
60
|
}
|
|
56
61
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
62
|
+
/**
|
|
63
|
+
* Respond to a GitHub Discussion using the Copilot SDK with tool-driven exploration.
|
|
64
|
+
*
|
|
65
|
+
* @param {Object} context - Task context from index.js
|
|
66
|
+
* @returns {Promise<Object>} Result with outcome, action, tokensUsed, model
|
|
67
|
+
*/
|
|
68
|
+
export async function discussions(context) {
|
|
69
|
+
const { octokit, model, discussionUrl, repo, config, logFilePath, screenshotFilePath } = context;
|
|
70
|
+
const t = config?.tuning || {};
|
|
71
|
+
|
|
72
|
+
if (!discussionUrl) {
|
|
73
|
+
throw new Error("discussions task requires discussion-url input");
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Pre-fetch discussion for the lean prompt
|
|
77
|
+
const discussion = await fetchDiscussion(octokit, discussionUrl, t.discussionComments || 10);
|
|
60
78
|
|
|
61
|
-
|
|
62
|
-
const
|
|
79
|
+
// Filter discussion comments to only those after the most recent init
|
|
80
|
+
const initTs = config?.init?.timestamp || null;
|
|
81
|
+
if (initTs && discussion.comments.length > 0) {
|
|
82
|
+
const initDate = new Date(initTs);
|
|
83
|
+
discussion.comments = discussion.comments.filter((c) => new Date(c.createdAt) >= initDate);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const humanComments = discussion.comments.filter((c) => !BOT_LOGINS.includes(c.author?.login));
|
|
87
|
+
const botReplies = discussion.comments.filter((c) => BOT_LOGINS.includes(c.author?.login));
|
|
63
88
|
const latestHumanComment = humanComments.length > 0 ? humanComments[humanComments.length - 1] : null;
|
|
64
89
|
const lastBotReply = botReplies.length > 0 ? botReplies[botReplies.length - 1] : null;
|
|
65
90
|
|
|
91
|
+
// Extract trigger comment info
|
|
92
|
+
const triggerComment = {};
|
|
93
|
+
const eventComment = context.github?.payload?.comment;
|
|
94
|
+
if (eventComment) {
|
|
95
|
+
triggerComment.nodeId = eventComment.node_id || "";
|
|
96
|
+
triggerComment.createdAt = eventComment.created_at || "";
|
|
97
|
+
triggerComment.body = eventComment.body || "";
|
|
98
|
+
triggerComment.login = eventComment.user?.login || "";
|
|
99
|
+
core.info(`Trigger comment from event payload: ${triggerComment.login} at ${triggerComment.createdAt}`);
|
|
100
|
+
}
|
|
101
|
+
if (context.commentNodeId) triggerComment.nodeId = context.commentNodeId;
|
|
102
|
+
if (context.commentCreatedAt) triggerComment.createdAt = context.commentCreatedAt;
|
|
103
|
+
|
|
66
104
|
const mission = readOptionalFile(config.paths.mission.path);
|
|
67
|
-
const
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const recentActivity = readOptionalFile(config.intentionBot.intentionFilepath).split("\n").slice(-20).join("\n");
|
|
73
|
-
const agentInstructions = instructions || "Respond to the GitHub Discussion as the repository bot.";
|
|
74
|
-
|
|
75
|
-
const parts = [
|
|
105
|
+
const agentInstructions = context.instructions || "Respond to the GitHub Discussion as the repository bot.";
|
|
106
|
+
const botMessage = process.env.BOT_MESSAGE || "";
|
|
107
|
+
|
|
108
|
+
// ── Build lean prompt ──────────────────────────────────────────────
|
|
109
|
+
const promptParts = [
|
|
76
110
|
"## Instructions",
|
|
77
111
|
agentInstructions,
|
|
78
112
|
"",
|
|
79
113
|
"## Discussion Thread",
|
|
80
114
|
`URL: ${discussionUrl}`,
|
|
81
|
-
title ? `### ${title}` : "",
|
|
82
|
-
body || "(no body)",
|
|
115
|
+
discussion.title ? `### ${discussion.title}` : "",
|
|
116
|
+
discussion.body || "(no body)",
|
|
83
117
|
];
|
|
84
118
|
|
|
85
119
|
if (humanComments.length > 0) {
|
|
86
|
-
|
|
87
|
-
for (const c of humanComments) {
|
|
88
|
-
// Identify the triggering comment: match by node_id, then by createdAt, then fall back to latest
|
|
120
|
+
promptParts.push("", "### Recent Conversation");
|
|
121
|
+
for (const c of humanComments.slice(-5)) {
|
|
89
122
|
let isTrigger = false;
|
|
90
123
|
if (triggerComment?.nodeId && c.id) {
|
|
91
124
|
isTrigger = c.id === triggerComment.nodeId;
|
|
@@ -95,19 +128,16 @@ function buildPrompt(discussionUrl, discussion, context, t, repoContext, trigger
|
|
|
95
128
|
isTrigger = c === latestHumanComment;
|
|
96
129
|
}
|
|
97
130
|
const prefix = isTrigger ? ">>> **[TRIGGER — RESPOND TO THIS]** " : "";
|
|
98
|
-
|
|
99
|
-
parts.push(`${prefix}**${c.author?.login || "unknown"}** (${c.createdAt})${nodeIdTag}:\n${c.body}`);
|
|
131
|
+
promptParts.push(`${prefix}**${c.author?.login || "unknown"}** (${c.createdAt}):\n${c.body}`);
|
|
100
132
|
}
|
|
101
133
|
}
|
|
102
134
|
|
|
103
135
|
if (lastBotReply) {
|
|
104
|
-
|
|
136
|
+
promptParts.push("", "### Your Last Reply (DO NOT REPEAT THIS)", lastBotReply.body.substring(0, 500));
|
|
105
137
|
}
|
|
106
138
|
|
|
107
|
-
// Include supervisor message if dispatched with context
|
|
108
|
-
const botMessage = process.env.BOT_MESSAGE || "";
|
|
109
139
|
if (botMessage) {
|
|
110
|
-
|
|
140
|
+
promptParts.push(
|
|
111
141
|
"",
|
|
112
142
|
"## Triggering Request",
|
|
113
143
|
"The supervisor dispatched you with the following message. This is your primary request — respond to it in the discussion thread:",
|
|
@@ -116,266 +146,170 @@ function buildPrompt(discussionUrl, discussion, context, t, repoContext, trigger
|
|
|
116
146
|
);
|
|
117
147
|
}
|
|
118
148
|
|
|
119
|
-
|
|
149
|
+
promptParts.push(
|
|
120
150
|
"",
|
|
121
151
|
"## Repository Context",
|
|
122
152
|
`### Mission\n${mission}`,
|
|
123
|
-
|
|
124
|
-
|
|
153
|
+
"",
|
|
154
|
+
"## Your Task",
|
|
155
|
+
"Read the discussion above. Use list_issues to see open issues if relevant.",
|
|
156
|
+
"Use list_discussions or search_discussions to find related conversations.",
|
|
157
|
+
"Compose a concise, engaging reply and call report_action with your reply and chosen action.",
|
|
158
|
+
"",
|
|
159
|
+
"## Available Actions (pass to report_action)",
|
|
160
|
+
"- `nop` — No action needed, just respond conversationally",
|
|
161
|
+
"- `request-supervisor` — Ask the supervisor to evaluate and act on a user request",
|
|
162
|
+
"- `create-feature` — Create a new feature (pass name as argument)",
|
|
163
|
+
"- `create-issue` — Create a new issue (pass title as argument)",
|
|
164
|
+
"- `mission-complete` — Declare mission complete",
|
|
165
|
+
"- `stop` — Halt automation",
|
|
166
|
+
"",
|
|
167
|
+
"**You MUST call report_action exactly once** with your reply text and action choice.",
|
|
125
168
|
);
|
|
126
169
|
|
|
127
|
-
|
|
128
|
-
if (repoContext?.issuesSummary?.length > 0) {
|
|
129
|
-
parts.push(`### Open Issues (${repoContext.issuesSummary.length})`, repoContext.issuesSummary.join("\n"));
|
|
130
|
-
}
|
|
170
|
+
const prompt = promptParts.filter(Boolean).join("\n");
|
|
131
171
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
`### Actions Since Last Init${repoContext.initTimestamp ? ` (${repoContext.initTimestamp})` : ""}`,
|
|
136
|
-
...repoContext.actionsSinceInit.map((a) => {
|
|
137
|
-
let line = `- ${a.name}: ${a.conclusion} (${a.created}) [${a.commitSha}] ${a.commitMessage}`;
|
|
138
|
-
if (a.prNumber) {
|
|
139
|
-
line += ` — PR #${a.prNumber}: +${a.additions}/-${a.deletions} in ${a.changedFiles} file(s)`;
|
|
140
|
-
}
|
|
141
|
-
return line;
|
|
142
|
-
}),
|
|
143
|
-
);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
parts.push(
|
|
147
|
-
recentActivity ? `### Recent Activity\n${recentActivity}` : "",
|
|
148
|
-
config.configToml ? `### Configuration (agentic-lib.toml)\n\`\`\`toml\n${config.configToml}\n\`\`\`` : "",
|
|
149
|
-
config.packageJson ? `### Dependencies (package.json)\n\`\`\`json\n${config.packageJson}\n\`\`\`` : "",
|
|
150
|
-
"",
|
|
151
|
-
"## Actions",
|
|
152
|
-
"Include exactly one action tag in your response. Only mention actions to the user when relevant.",
|
|
153
|
-
"`[ACTION:request-supervisor] <free text>` — Ask the supervisor to evaluate and act on a user request",
|
|
154
|
-
"`[ACTION:create-feature] <name>` — Create a new feature",
|
|
155
|
-
"`[ACTION:update-feature] <name>` — Update an existing feature",
|
|
156
|
-
"`[ACTION:delete-feature] <name>` — Delete a feature",
|
|
157
|
-
"`[ACTION:create-issue] <title>` — Create a new issue",
|
|
158
|
-
"`[ACTION:seed-repository]` — Reset to initial state",
|
|
159
|
-
"`[ACTION:nop]` — No action needed, just respond conversationally",
|
|
160
|
-
"`[ACTION:mission-complete]` — Declare mission complete",
|
|
161
|
-
"`[ACTION:stop]` — Halt automation",
|
|
162
|
-
);
|
|
172
|
+
const systemPrompt =
|
|
173
|
+
"You are this repository. Respond in first person. Be concise and engaging — never repeat what you said in your last reply. Adapt to the user's language level. Encourage experimentation and suggest interesting projects. When a user requests an action, pass it to the supervisor via request-supervisor action. Protect the mission: push back on requests that contradict it." +
|
|
174
|
+
NARRATIVE_INSTRUCTION;
|
|
163
175
|
|
|
164
|
-
|
|
165
|
-
}
|
|
176
|
+
// ── Shared mutable state for action results ──────────────────────
|
|
177
|
+
const actionResults = { action: "nop", actionArg: "", replyBody: "" };
|
|
178
|
+
const isSdkRepo = process.env.GITHUB_REPOSITORY === "xn-intenton-z2a/agentic-lib";
|
|
166
179
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
180
|
+
// ── Create tools ─────────────────────────────────────────────────
|
|
181
|
+
const createTools = (defineTool, _wp, logger) => {
|
|
182
|
+
const discTools = createDiscussionTools(octokit, repo, defineTool, logger);
|
|
183
|
+
const ghTools = createGitHubTools(octokit, repo, defineTool, logger);
|
|
184
|
+
|
|
185
|
+
// Action tool — lets the model report its action and reply
|
|
186
|
+
const reportAction = defineTool("report_action", {
|
|
187
|
+
description: "Report the action taken in this discussion response and post your reply. Call this exactly once.",
|
|
188
|
+
parameters: {
|
|
189
|
+
type: "object",
|
|
190
|
+
properties: {
|
|
191
|
+
action: {
|
|
192
|
+
type: "string",
|
|
193
|
+
enum: ["nop", "request-supervisor", "create-feature", "update-feature", "delete-feature", "create-issue", "seed-repository", "mission-complete", "stop"],
|
|
194
|
+
description: "The action type",
|
|
195
|
+
},
|
|
196
|
+
argument: { type: "string", description: "Action argument (e.g. feature name, issue title, supervisor message)" },
|
|
197
|
+
reply: { type: "string", description: "The reply text to post to the discussion" },
|
|
198
|
+
},
|
|
199
|
+
required: ["action", "reply"],
|
|
200
|
+
},
|
|
201
|
+
handler: async ({ action, argument, reply }) => {
|
|
202
|
+
actionResults.action = action;
|
|
203
|
+
actionResults.actionArg = argument || "";
|
|
204
|
+
actionResults.replyBody = reply;
|
|
205
|
+
|
|
206
|
+
// Execute side effects
|
|
207
|
+
if (action === "mission-complete") {
|
|
208
|
+
const signal = [
|
|
209
|
+
"# Mission Complete",
|
|
210
|
+
"",
|
|
211
|
+
`- **Timestamp:** ${new Date().toISOString()}`,
|
|
212
|
+
`- **Detected by:** discussions`,
|
|
213
|
+
`- **Reason:** ${argument || "Declared via discussion bot"}`,
|
|
214
|
+
"",
|
|
215
|
+
"This file was created automatically. To restart transformations, delete this file or run `npx @xn-intenton-z2a/agentic-lib init --reseed`.",
|
|
216
|
+
].join("\n");
|
|
217
|
+
writeFileSync("MISSION_COMPLETE.md", signal);
|
|
218
|
+
logger.info("Mission complete signal written (MISSION_COMPLETE.md)");
|
|
219
|
+
}
|
|
191
220
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
221
|
+
if (action === "create-issue" && argument) {
|
|
222
|
+
try {
|
|
223
|
+
const { data: issue } = await octokit.rest.issues.create({
|
|
224
|
+
...repo,
|
|
225
|
+
title: argument,
|
|
226
|
+
labels: ["automated", "enhancement"],
|
|
227
|
+
});
|
|
228
|
+
logger.info(`Created issue #${issue.number}: ${argument}`);
|
|
229
|
+
} catch (err) {
|
|
230
|
+
logger.warning(`Failed to create issue: ${err.message}`);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
201
233
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
234
|
+
if (action === "request-supervisor") {
|
|
235
|
+
logger.info(`Supervisor requested: ${argument || "Discussion bot referral"} (dispatch handled by bot workflow)`);
|
|
236
|
+
}
|
|
205
237
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
}
|
|
238
|
+
if (action === "stop") {
|
|
239
|
+
if (isSdkRepo) {
|
|
240
|
+
logger.info("Skipping schedule dispatch — running in SDK repo");
|
|
241
|
+
} else {
|
|
242
|
+
try {
|
|
243
|
+
await octokit.rest.actions.createWorkflowDispatch({
|
|
244
|
+
...repo,
|
|
245
|
+
workflow_id: "agentic-lib-schedule.yml",
|
|
246
|
+
ref: "main",
|
|
247
|
+
inputs: { frequency: "off" },
|
|
248
|
+
});
|
|
249
|
+
logger.info("Automation stopped via discussions bot");
|
|
250
|
+
} catch (err) {
|
|
251
|
+
logger.warning(`Failed to stop automation: ${err.message}`);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
224
255
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
try {
|
|
228
|
-
const { data: runs } = await octokit.rest.actions.listWorkflowRunsForRepo({
|
|
229
|
-
...repo, per_page: 20,
|
|
230
|
-
});
|
|
231
|
-
const initDate = initTimestamp ? new Date(initTimestamp) : null;
|
|
232
|
-
const relevantRuns = initDate
|
|
233
|
-
? runs.workflow_runs.filter((r) => new Date(r.created_at) >= initDate)
|
|
234
|
-
: runs.workflow_runs.slice(0, 10);
|
|
235
|
-
|
|
236
|
-
for (const run of relevantRuns) {
|
|
237
|
-
const commit = run.head_commit;
|
|
238
|
-
const entry = {
|
|
239
|
-
name: run.name,
|
|
240
|
-
conclusion: run.conclusion || run.status,
|
|
241
|
-
created: run.created_at,
|
|
242
|
-
commitMessage: commit?.message?.split("\n")[0] || "",
|
|
243
|
-
commitSha: run.head_sha?.substring(0, 7) || "",
|
|
244
|
-
};
|
|
245
|
-
if (run.head_branch?.startsWith("agentic-lib-issue-")) {
|
|
256
|
+
// Post the reply to the discussion
|
|
257
|
+
if (discussion.nodeId && reply) {
|
|
246
258
|
try {
|
|
247
|
-
const
|
|
248
|
-
|
|
259
|
+
const mutation = `mutation($discussionId: ID!, $body: String!) {
|
|
260
|
+
addDiscussionComment(input: { discussionId: $discussionId, body: $body }) {
|
|
261
|
+
comment { url }
|
|
262
|
+
}
|
|
263
|
+
}`;
|
|
264
|
+
const { addDiscussionComment } = await octokit.graphql(mutation, {
|
|
265
|
+
discussionId: discussion.nodeId,
|
|
266
|
+
body: reply,
|
|
249
267
|
});
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
entry.changedFiles = prs[0].changed_files;
|
|
255
|
-
}
|
|
256
|
-
} catch { /* ignore */ }
|
|
268
|
+
logger.info(`Posted reply: ${addDiscussionComment.comment.url}`);
|
|
269
|
+
} catch (err) {
|
|
270
|
+
logger.warning(`Failed to post discussion reply: ${err.message}`);
|
|
271
|
+
}
|
|
257
272
|
}
|
|
258
|
-
repoContext.actionsSinceInit.push(entry);
|
|
259
|
-
}
|
|
260
|
-
} catch (err) {
|
|
261
|
-
core.warning(`Could not fetch workflow runs for discussion context: ${err.message}`);
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
273
|
|
|
265
|
-
|
|
274
|
+
return { textResultForLlm: `Action recorded: ${action}. Reply posted.` };
|
|
275
|
+
},
|
|
276
|
+
});
|
|
266
277
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
if (initTs && discussion.comments.length > 0) {
|
|
270
|
-
const initDate = new Date(initTs);
|
|
271
|
-
discussion.comments = discussion.comments.filter((c) => new Date(c.createdAt) >= initDate);
|
|
272
|
-
}
|
|
278
|
+
return [...discTools, ...ghTools, reportAction];
|
|
279
|
+
};
|
|
273
280
|
|
|
274
|
-
//
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
const triggerComment = {};
|
|
279
|
-
const eventComment = context.github?.payload?.comment;
|
|
280
|
-
if (eventComment) {
|
|
281
|
-
triggerComment.nodeId = eventComment.node_id || "";
|
|
282
|
-
triggerComment.createdAt = eventComment.created_at || "";
|
|
283
|
-
triggerComment.body = eventComment.body || "";
|
|
284
|
-
triggerComment.login = eventComment.user?.login || "";
|
|
285
|
-
core.info(`Trigger comment from event payload: ${triggerComment.login} at ${triggerComment.createdAt}`);
|
|
286
|
-
}
|
|
287
|
-
// Explicit inputs override event payload (workflow_dispatch or workflow_call may pass these)
|
|
288
|
-
if (context.commentNodeId) triggerComment.nodeId = context.commentNodeId;
|
|
289
|
-
if (context.commentCreatedAt) triggerComment.createdAt = context.commentCreatedAt;
|
|
281
|
+
// ── Run hybrid session ───────────────────────────────────────────
|
|
282
|
+
const attachments = [];
|
|
283
|
+
if (logFilePath) attachments.push({ type: "file", path: logFilePath });
|
|
284
|
+
if (screenshotFilePath) attachments.push({ type: "file", path: screenshotFilePath });
|
|
290
285
|
|
|
291
|
-
const
|
|
292
|
-
|
|
286
|
+
const result = await runCopilotSession({
|
|
287
|
+
workspacePath: process.cwd(),
|
|
293
288
|
model,
|
|
294
|
-
systemMessage:
|
|
295
|
-
"You are this repository. Respond in first person. Be concise and engaging — never repeat what you said in your last reply. Adapt to the user's language level. Encourage experimentation and suggest interesting projects. When a user requests an action, pass it to the supervisor via [ACTION:request-supervisor]. Protect the mission: push back on requests that contradict it.",
|
|
296
|
-
prompt,
|
|
297
|
-
writablePaths: [],
|
|
298
289
|
tuning: t,
|
|
290
|
+
agentPrompt: systemPrompt,
|
|
291
|
+
userPrompt: prompt,
|
|
292
|
+
writablePaths: [],
|
|
293
|
+
createTools,
|
|
294
|
+
attachments,
|
|
295
|
+
excludedTools: ["write_file", "run_command", "run_tests", "dispatch_workflow"],
|
|
296
|
+
logger: { info: core.info, warning: core.warning, error: core.error, debug: core.debug },
|
|
299
297
|
});
|
|
300
298
|
|
|
301
|
-
|
|
302
|
-
const action = actionMatch ? actionMatch[1] : "nop";
|
|
303
|
-
const actionArg = actionMatch && actionMatch[2] ? actionMatch[2].trim() : "";
|
|
304
|
-
const replyBody = content.replace(/\[ACTION:\S+?\].+/, "").trim();
|
|
305
|
-
|
|
306
|
-
core.info(`Discussion bot action: ${action}, arg: ${actionArg}`);
|
|
307
|
-
|
|
308
|
-
// Write MISSION_COMPLETE.md signal when bot declares mission complete
|
|
309
|
-
if (action === "mission-complete") {
|
|
310
|
-
const signal = [
|
|
311
|
-
"# Mission Complete",
|
|
312
|
-
"",
|
|
313
|
-
`- **Timestamp:** ${new Date().toISOString()}`,
|
|
314
|
-
`- **Detected by:** discussions`,
|
|
315
|
-
`- **Reason:** ${actionArg || "Declared via discussion bot"}`,
|
|
316
|
-
"",
|
|
317
|
-
"This file was created automatically. To restart transformations, delete this file or run `npx @xn-intenton-z2a/agentic-lib init --reseed`.",
|
|
318
|
-
].join("\n");
|
|
319
|
-
writeFileSync("MISSION_COMPLETE.md", signal);
|
|
320
|
-
core.info("Mission complete signal written (MISSION_COMPLETE.md)");
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
// Create issue when bot requests it
|
|
324
|
-
if (action === "create-issue" && actionArg) {
|
|
325
|
-
try {
|
|
326
|
-
const { data: issue } = await octokit.rest.issues.create({
|
|
327
|
-
...context.repo,
|
|
328
|
-
title: actionArg,
|
|
329
|
-
labels: ["automated", "enhancement"],
|
|
330
|
-
});
|
|
331
|
-
core.info(`Created issue #${issue.number}: ${actionArg}`);
|
|
332
|
-
} catch (err) {
|
|
333
|
-
core.warning(`Failed to create issue: ${err.message}`);
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
// Guard: never dispatch workflows from the SDK repo itself (agentic-lib)
|
|
338
|
-
const isSdkRepo = process.env.GITHUB_REPOSITORY === "xn-intenton-z2a/agentic-lib";
|
|
339
|
-
|
|
340
|
-
// Request supervisor evaluation — dispatch is handled by the bot workflow's
|
|
341
|
-
// dispatch-supervisor job, so we just log the action here to avoid double dispatch.
|
|
342
|
-
if (action === "request-supervisor") {
|
|
343
|
-
core.info(`Supervisor requested with message: ${actionArg || "Discussion bot referral"} (dispatch handled by bot workflow)`);
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
// Stop automation
|
|
347
|
-
if (action === "stop") {
|
|
348
|
-
if (isSdkRepo) {
|
|
349
|
-
core.info("Skipping schedule dispatch — running in SDK repo");
|
|
350
|
-
} else {
|
|
351
|
-
try {
|
|
352
|
-
await octokit.rest.actions.createWorkflowDispatch({
|
|
353
|
-
...context.repo,
|
|
354
|
-
workflow_id: "agentic-lib-schedule.yml",
|
|
355
|
-
ref: "main",
|
|
356
|
-
inputs: { frequency: "off" },
|
|
357
|
-
});
|
|
358
|
-
core.info("Automation stopped via discussions bot");
|
|
359
|
-
} catch (err) {
|
|
360
|
-
core.warning(`Failed to stop automation: ${err.message}`);
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
await postReply(octokit, discussion.nodeId, replyBody);
|
|
299
|
+
core.info(`Discussion bot action: ${actionResults.action}, arg: ${actionResults.actionArg}`);
|
|
366
300
|
|
|
367
|
-
const argSuffix = actionArg ? ` (${actionArg})` : "";
|
|
301
|
+
const argSuffix = actionResults.actionArg ? ` (${actionResults.actionArg})` : "";
|
|
368
302
|
return {
|
|
369
|
-
outcome: `discussion-${action}`,
|
|
370
|
-
tokensUsed,
|
|
371
|
-
inputTokens,
|
|
372
|
-
outputTokens,
|
|
373
|
-
cost,
|
|
303
|
+
outcome: `discussion-${actionResults.action}`,
|
|
304
|
+
tokensUsed: result.tokensIn + result.tokensOut,
|
|
305
|
+
inputTokens: result.tokensIn,
|
|
306
|
+
outputTokens: result.tokensOut,
|
|
307
|
+
cost: 0,
|
|
374
308
|
model,
|
|
375
|
-
details: `Action: ${action}${argSuffix}\nReply: ${replyBody.substring(0, 200)}`,
|
|
376
|
-
narrative: `Responded to discussion with action ${action}${argSuffix}
|
|
377
|
-
action,
|
|
378
|
-
actionArg,
|
|
379
|
-
replyBody,
|
|
309
|
+
details: `Action: ${actionResults.action}${argSuffix}\nReply: ${(actionResults.replyBody || "").substring(0, 200)}`,
|
|
310
|
+
narrative: result.narrative || extractNarrative(result.agentMessage, `Responded to discussion with action ${actionResults.action}${argSuffix}.`),
|
|
311
|
+
action: actionResults.action,
|
|
312
|
+
actionArg: actionResults.actionArg,
|
|
313
|
+
replyBody: actionResults.replyBody,
|
|
380
314
|
};
|
|
381
315
|
}
|