@xn-intenton-z2a/agentic-lib 7.1.76 → 7.1.77
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
|
@@ -9,6 +9,67 @@ import * as core from "@actions/core";
|
|
|
9
9
|
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
10
10
|
import { runCopilotTask, readOptionalFile, scanDirectory, filterIssues } from "../copilot.js";
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Look up the "Talk to the repository" discussion URL via GraphQL.
|
|
14
|
+
* Returns { url, nodeId } or { url: "", nodeId: "" } on failure.
|
|
15
|
+
*/
|
|
16
|
+
async function findTalkDiscussion(octokit, repo) {
|
|
17
|
+
try {
|
|
18
|
+
const query = `query($owner: String!, $name: String!) {
|
|
19
|
+
repository(owner: $owner, name: $name) {
|
|
20
|
+
discussions(first: 10, orderBy: { field: CREATED_AT, direction: DESC }) {
|
|
21
|
+
nodes {
|
|
22
|
+
id
|
|
23
|
+
number
|
|
24
|
+
title
|
|
25
|
+
url
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}`;
|
|
30
|
+
const result = await octokit.graphql(query, { owner: repo.owner, name: repo.repo });
|
|
31
|
+
const disc = result.repository.discussions.nodes.find(
|
|
32
|
+
(d) => d.title.toLowerCase().includes("talk to the repository"),
|
|
33
|
+
);
|
|
34
|
+
if (disc) {
|
|
35
|
+
return { url: disc.url, nodeId: disc.id };
|
|
36
|
+
}
|
|
37
|
+
} catch (err) {
|
|
38
|
+
core.warning(`Could not look up Talk discussion: ${err.message}`);
|
|
39
|
+
}
|
|
40
|
+
return { url: "", nodeId: "" };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Construct the GitHub Pages website URL for a repository.
|
|
45
|
+
*/
|
|
46
|
+
function getWebsiteUrl(repo) {
|
|
47
|
+
return `https://${repo.owner}.github.io/${repo.repo}/`;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Dispatch the discussions bot with a message and discussion URL.
|
|
52
|
+
*/
|
|
53
|
+
async function dispatchBot(octokit, repo, message, discussionUrl) {
|
|
54
|
+
if (process.env.GITHUB_REPOSITORY === "xn-intenton-z2a/agentic-lib") {
|
|
55
|
+
core.info("Skipping bot dispatch — running in SDK repo");
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const inputs = { message };
|
|
59
|
+
if (discussionUrl) inputs["discussion-url"] = discussionUrl;
|
|
60
|
+
try {
|
|
61
|
+
await octokit.rest.actions.createWorkflowDispatch({
|
|
62
|
+
...repo,
|
|
63
|
+
workflow_id: "agentic-lib-bot.yml",
|
|
64
|
+
ref: "main",
|
|
65
|
+
inputs,
|
|
66
|
+
});
|
|
67
|
+
core.info(`Dispatched bot: ${message.substring(0, 100)}`);
|
|
68
|
+
} catch (err) {
|
|
69
|
+
core.warning(`Could not dispatch bot: ${err.message}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
12
73
|
async function gatherContext(octokit, repo, config, t) {
|
|
13
74
|
const mission = readOptionalFile(config.paths.mission.path);
|
|
14
75
|
const intentionLogFull = readOptionalFile(config.intentionBot.intentionFilepath);
|
|
@@ -37,7 +98,13 @@ async function gatherContext(octokit, repo, config, t) {
|
|
|
37
98
|
|
|
38
99
|
// Extract discussion URL from recent activity for supervisor reporting
|
|
39
100
|
const discussionUrlMatch = recentActivity.match(/https:\/\/github\.com\/[^/]+\/[^/]+\/discussions\/\d+/);
|
|
40
|
-
|
|
101
|
+
let activeDiscussionUrl = discussionUrlMatch ? discussionUrlMatch[0] : "";
|
|
102
|
+
|
|
103
|
+
// Fallback: look up the "Talk to the repository" discussion if not found in activity log
|
|
104
|
+
if (!activeDiscussionUrl) {
|
|
105
|
+
const talk = await findTalkDiscussion(octokit, repo);
|
|
106
|
+
activeDiscussionUrl = talk.url;
|
|
107
|
+
}
|
|
41
108
|
|
|
42
109
|
const featuresPath = config.paths.features.path;
|
|
43
110
|
const featureNames = existsSync(featuresPath)
|
|
@@ -345,12 +412,17 @@ function parseReasoning(content) {
|
|
|
345
412
|
return match ? match[1].trim() : "";
|
|
346
413
|
}
|
|
347
414
|
|
|
348
|
-
async function executeDispatch(octokit, repo, actionName, params) {
|
|
415
|
+
async function executeDispatch(octokit, repo, actionName, params, ctx) {
|
|
349
416
|
const workflowFile = actionName.replace("dispatch:", "") + ".yml";
|
|
350
417
|
const inputs = {};
|
|
351
418
|
if (params["pr-number"]) inputs["pr-number"] = params["pr-number"];
|
|
352
419
|
if (params["issue-number"]) inputs["issue-number"] = params["issue-number"];
|
|
353
420
|
|
|
421
|
+
// Pass discussion-url when dispatching the bot
|
|
422
|
+
if (workflowFile === "agentic-lib-bot.yml" && ctx?.activeDiscussionUrl) {
|
|
423
|
+
if (!inputs["discussion-url"]) inputs["discussion-url"] = ctx.activeDiscussionUrl;
|
|
424
|
+
}
|
|
425
|
+
|
|
354
426
|
// Guard: never dispatch workflows from the SDK repo itself (agentic-lib)
|
|
355
427
|
if (process.env.GITHUB_REPOSITORY === "xn-intenton-z2a/agentic-lib") {
|
|
356
428
|
core.info(`Skipping dispatch of ${workflowFile} — running in SDK repo`);
|
|
@@ -434,9 +506,9 @@ async function executeCloseIssue(octokit, repo, params) {
|
|
|
434
506
|
return "skipped:close-issue-missing-number";
|
|
435
507
|
}
|
|
436
508
|
|
|
437
|
-
async function executeRespondDiscussions(octokit, repo, params) {
|
|
509
|
+
async function executeRespondDiscussions(octokit, repo, params, ctx) {
|
|
438
510
|
const message = params.message || "";
|
|
439
|
-
const url = params["discussion-url"] || "";
|
|
511
|
+
const url = params["discussion-url"] || ctx?.activeDiscussionUrl || "";
|
|
440
512
|
if (message) {
|
|
441
513
|
if (process.env.GITHUB_REPOSITORY === "xn-intenton-z2a/agentic-lib") {
|
|
442
514
|
core.info("Skipping bot dispatch — running in SDK repo");
|
|
@@ -456,7 +528,7 @@ async function executeRespondDiscussions(octokit, repo, params) {
|
|
|
456
528
|
return "skipped:respond-no-message";
|
|
457
529
|
}
|
|
458
530
|
|
|
459
|
-
async function executeMissionComplete(octokit, repo, params) {
|
|
531
|
+
async function executeMissionComplete(octokit, repo, params, ctx) {
|
|
460
532
|
const reason = params.reason || "All acceptance criteria satisfied";
|
|
461
533
|
const signal = [
|
|
462
534
|
"# Mission Complete",
|
|
@@ -480,11 +552,16 @@ async function executeMissionComplete(octokit, repo, params) {
|
|
|
480
552
|
} catch (err) {
|
|
481
553
|
core.warning(`Could not set schedule to off: ${err.message}`);
|
|
482
554
|
}
|
|
555
|
+
|
|
556
|
+
// Announce mission complete via bot
|
|
557
|
+
const websiteUrl = getWebsiteUrl(repo);
|
|
558
|
+
const discussionUrl = ctx?.activeDiscussionUrl || "";
|
|
559
|
+
await dispatchBot(octokit, repo, `Mission complete! ${reason}\n\nWebsite: ${websiteUrl}`, discussionUrl);
|
|
483
560
|
}
|
|
484
561
|
return `mission-complete:${reason.substring(0, 100)}`;
|
|
485
562
|
}
|
|
486
563
|
|
|
487
|
-
async function executeMissionFailed(octokit, repo, params) {
|
|
564
|
+
async function executeMissionFailed(octokit, repo, params, ctx) {
|
|
488
565
|
const reason = params.reason || "Mission could not be completed";
|
|
489
566
|
const signal = [
|
|
490
567
|
"# Mission Failed",
|
|
@@ -508,6 +585,11 @@ async function executeMissionFailed(octokit, repo, params) {
|
|
|
508
585
|
} catch (err) {
|
|
509
586
|
core.warning(`Could not set schedule to off: ${err.message}`);
|
|
510
587
|
}
|
|
588
|
+
|
|
589
|
+
// Announce mission failed via bot
|
|
590
|
+
const websiteUrl = getWebsiteUrl(repo);
|
|
591
|
+
const discussionUrl = ctx?.activeDiscussionUrl || "";
|
|
592
|
+
await dispatchBot(octokit, repo, `Mission failed. ${reason}\n\nWebsite: ${websiteUrl}`, discussionUrl);
|
|
511
593
|
}
|
|
512
594
|
return `mission-failed:${reason.substring(0, 100)}`;
|
|
513
595
|
}
|
|
@@ -540,12 +622,12 @@ async function executeSetSchedule(octokit, repo, frequency) {
|
|
|
540
622
|
return `set-schedule:${frequency}`;
|
|
541
623
|
}
|
|
542
624
|
|
|
543
|
-
async function executeAction(octokit, repo, action, params) {
|
|
544
|
-
if (action.startsWith("dispatch:")) return executeDispatch(octokit, repo, action, params);
|
|
625
|
+
async function executeAction(octokit, repo, action, params, ctx) {
|
|
626
|
+
if (action.startsWith("dispatch:")) return executeDispatch(octokit, repo, action, params, ctx);
|
|
545
627
|
if (action.startsWith("set-schedule:")) return executeSetSchedule(octokit, repo, action.replace("set-schedule:", ""));
|
|
546
628
|
if (action === "nop") return "nop";
|
|
547
629
|
const handler = ACTION_HANDLERS[action];
|
|
548
|
-
if (handler) return handler(octokit, repo, params);
|
|
630
|
+
if (handler) return handler(octokit, repo, params, ctx);
|
|
549
631
|
core.warning(`Unknown action: ${action}`);
|
|
550
632
|
return `unknown:${action}`;
|
|
551
633
|
}
|
|
@@ -561,6 +643,22 @@ export async function supervise(context) {
|
|
|
561
643
|
const t = config.tuning || {};
|
|
562
644
|
|
|
563
645
|
const ctx = await gatherContext(octokit, repo, config, t);
|
|
646
|
+
const websiteUrl = getWebsiteUrl(repo);
|
|
647
|
+
|
|
648
|
+
// --- Deterministic lifecycle posts (before LLM) ---
|
|
649
|
+
|
|
650
|
+
// Step 2: Auto-announce on first run after init
|
|
651
|
+
// Detect first supervisor run: initTimestamp exists but no supervisor entries in activity
|
|
652
|
+
if (ctx.initTimestamp && !ctx.missionComplete && !ctx.missionFailed) {
|
|
653
|
+
const hasPriorSupervisor = ctx.recentActivity.includes("supervisor");
|
|
654
|
+
if (!hasPriorSupervisor && ctx.mission && ctx.activeDiscussionUrl) {
|
|
655
|
+
core.info("First supervisor run after init — announcing mission");
|
|
656
|
+
const announcement = `New mission started!\n\n**Mission:** ${ctx.mission.substring(0, 300)}\n\n**Website:** ${websiteUrl}`;
|
|
657
|
+
await dispatchBot(octokit, repo, announcement, ctx.activeDiscussionUrl);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// --- LLM decision ---
|
|
564
662
|
const agentInstructions = instructions || "You are the supervisor. Decide what actions to take.";
|
|
565
663
|
const prompt = buildPrompt(ctx, agentInstructions);
|
|
566
664
|
|
|
@@ -582,7 +680,7 @@ export async function supervise(context) {
|
|
|
582
680
|
const results = [];
|
|
583
681
|
for (const { action, params } of actions) {
|
|
584
682
|
try {
|
|
585
|
-
const result = await executeAction(octokit, repo, action, params);
|
|
683
|
+
const result = await executeAction(octokit, repo, action, params, ctx);
|
|
586
684
|
results.push(result);
|
|
587
685
|
core.info(`Action result: ${result}`);
|
|
588
686
|
} catch (err) {
|
|
@@ -591,6 +689,24 @@ export async function supervise(context) {
|
|
|
591
689
|
}
|
|
592
690
|
}
|
|
593
691
|
|
|
692
|
+
// --- Deterministic lifecycle posts (after LLM) ---
|
|
693
|
+
|
|
694
|
+
// Step 3: Auto-respond when a message referral is present
|
|
695
|
+
// If the workflow was triggered with a message (from bot's request-supervisor),
|
|
696
|
+
// and the LLM didn't include a respond:discussions action, post back automatically
|
|
697
|
+
const workflowMessage = context.discussionUrl ? "" : (process.env.INPUT_MESSAGE || "");
|
|
698
|
+
if (workflowMessage && ctx.activeDiscussionUrl) {
|
|
699
|
+
const hasDiscussionResponse = results.some((r) => r.startsWith("respond-discussions:"));
|
|
700
|
+
if (!hasDiscussionResponse) {
|
|
701
|
+
core.info("Message referral detected — auto-responding to discussion");
|
|
702
|
+
const response = reasoning
|
|
703
|
+
? `Supervisor update: ${reasoning.substring(0, 400)}`
|
|
704
|
+
: `Supervisor processed your request. Actions taken: ${results.join(", ")}`;
|
|
705
|
+
await dispatchBot(octokit, repo, response, ctx.activeDiscussionUrl);
|
|
706
|
+
results.push(`auto-respond:${ctx.activeDiscussionUrl}`);
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
|
|
594
710
|
// Build changes list from executed actions
|
|
595
711
|
const changes = results
|
|
596
712
|
.filter((r) => r.startsWith("created-issue:") || r.startsWith("mission-complete:") || r.startsWith("mission-failed:"))
|
|
@@ -170,20 +170,12 @@ export async function transform(context) {
|
|
|
170
170
|
|
|
171
171
|
core.info(`Transformation step completed (${tokensUsed} tokens)`);
|
|
172
172
|
|
|
173
|
-
// Detect mission-complete: if the LLM
|
|
173
|
+
// Detect mission-complete hint: if the LLM indicates mission satisfaction, log it
|
|
174
|
+
// but do NOT write MISSION_COMPLETE.md — the supervisor is the single authority
|
|
175
|
+
// for mission lifecycle declarations (it also handles bot announcements)
|
|
174
176
|
const lowerResult = resultContent.toLowerCase();
|
|
175
177
|
if (lowerResult.includes("mission is satisfied") || lowerResult.includes("mission is complete") || lowerResult.includes("no changes needed")) {
|
|
176
|
-
core.info("
|
|
177
|
-
const signal = [
|
|
178
|
-
"# Mission Complete",
|
|
179
|
-
"",
|
|
180
|
-
`- **Timestamp:** ${new Date().toISOString()}`,
|
|
181
|
-
`- **Detected by:** transform`,
|
|
182
|
-
`- **Reason:** ${resultContent.substring(0, 200)}`,
|
|
183
|
-
"",
|
|
184
|
-
"This file was created automatically. To restart transformations, delete this file or run `npx @xn-intenton-z2a/agentic-lib init --reseed`.",
|
|
185
|
-
].join("\n");
|
|
186
|
-
writeFileSync("MISSION_COMPLETE.md", signal);
|
|
178
|
+
core.info("Transform indicates mission may be complete — supervisor will verify on next cycle");
|
|
187
179
|
}
|
|
188
180
|
|
|
189
181
|
const promptBudget = [
|