@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,12 +2,15 @@
2
2
  // Copyright (C) 2025-2026 Polycode Limited
3
3
  // tasks/supervise.js — Supervisor orchestration via LLM
4
4
  //
5
- // Gathers repository context (issues, PRs, workflows, features, library, activity),
6
- // asks the Copilot SDK to choose multiple concurrent actions, then dispatches them.
5
+ // Uses runCopilotSession with lean prompts: the model explores issues, PRs,
6
+ // and repository state via tools, then reports its chosen actions via a
7
+ // report_supervisor_plan tool whose handler executes them.
7
8
 
8
9
  import * as core from "@actions/core";
9
10
  import { existsSync, readFileSync, readdirSync, statSync } from "fs";
10
- import { runCopilotTask, readOptionalFile, scanDirectory, filterIssues } from "../copilot.js";
11
+ import { readOptionalFile, scanDirectory, filterIssues, extractNarrative, NARRATIVE_INSTRUCTION } from "../copilot.js";
12
+ import { runCopilotSession } from "../../../copilot/copilot-session.js";
13
+ import { createGitHubTools, createDiscussionTools, createGitTools } from "../../../copilot/github-tools.js";
11
14
 
12
15
  /**
13
16
  * Look up the "Talk to the repository" discussion URL via GraphQL.
@@ -402,6 +405,13 @@ async function gatherContext(octokit, repo, config, t) {
402
405
  }
403
406
 
404
407
  function buildPrompt(ctx, agentInstructions, config) {
408
+ // Build mission-complete metrics inline for the LLM
409
+ const thresholds = config?.missionCompleteThresholds || {};
410
+ const minResolved = thresholds.minResolvedIssues ?? 3;
411
+ const minTests = thresholds.minDedicatedTests ?? 1;
412
+ const maxTodos = thresholds.maxSourceTodos ?? 0;
413
+ const resolvedCount = ctx.recentlyClosedSummary.filter((s) => s.includes("RESOLVED")).length;
414
+
405
415
  return [
406
416
  "## Instructions",
407
417
  agentInstructions,
@@ -409,91 +419,32 @@ function buildPrompt(ctx, agentInstructions, config) {
409
419
  "## Mission",
410
420
  ctx.mission || "(no mission defined)",
411
421
  "",
412
- "## Repository State",
413
- `### Open Issues (${ctx.issuesSummary.length})`,
414
- ctx.issuesSummary.join("\n") || "none",
415
- "",
416
- `### Recently Closed Issues (${ctx.recentlyClosedSummary.length})`,
417
- ctx.recentlyClosedSummary.join("\n") || "none",
418
- "",
419
- `### Open PRs (${ctx.prsSummary.length})`,
420
- ctx.prsSummary.join("\n") || "none",
422
+ "## Repository Summary",
423
+ `Open issues: ${ctx.issuesSummary.length}`,
424
+ ctx.issuesSummary.join("\n") || "(none)",
421
425
  "",
422
- `### Features (${ctx.featureNames.length}/${ctx.featuresLimit})`,
423
- ctx.featureNames.join(", ") || "none",
426
+ `Recently closed issues: ${ctx.recentlyClosedSummary.length}`,
427
+ ctx.recentlyClosedSummary.join("\n") || "(none)",
424
428
  "",
425
- `### Library Docs (${ctx.libraryNames.length}/${ctx.libraryLimit})`,
426
- ctx.libraryNames.join(", ") || "none",
427
- "",
428
- ...(ctx.sourceExports?.length > 0
429
- ? [
430
- `### Source Exports`,
431
- "Functions and constants exported from source files:",
432
- ...ctx.sourceExports.map((e) => `- ${e}`),
433
- "",
434
- ]
435
- : []),
436
- `### Test Coverage`,
437
- ctx.dedicatedTestCount > 0
438
- ? `Dedicated test files (${ctx.dedicatedTestCount}): ${ctx.dedicatedTestFiles.join(", ")}`
439
- : "**No dedicated test files found.** Only seed tests (main.test.js, web.test.js) exist. Mission-complete requires dedicated tests that import from src/lib/.",
429
+ `Open PRs: ${ctx.prsSummary.length}`,
430
+ ctx.prsSummary.join("\n") || "(none)",
440
431
  "",
441
- `### Source TODO Count: ${ctx.sourceTodoCount}`,
442
- ctx.sourceTodoCount > 0
443
- ? `**${ctx.sourceTodoCount} TODO(s) found in source.** All TODOs must be resolved before mission-complete can be declared.`
444
- : "No TODOs found in source — this criterion is met.",
432
+ `Features: ${ctx.featureNames.length}/${ctx.featuresLimit}`,
433
+ `Library docs: ${ctx.libraryNames.length}/${ctx.libraryLimit}`,
434
+ `Dedicated test files: ${ctx.dedicatedTestCount}`,
435
+ `Source TODOs: ${ctx.sourceTodoCount}`,
445
436
  "",
446
- ...(() => {
447
- // W10: Build mission-complete metrics inline for the LLM
448
- const thresholds = config?.missionCompleteThresholds || {};
449
- const minResolved = thresholds.minResolvedIssues ?? 3;
450
- const minTests = thresholds.minDedicatedTests ?? 1;
451
- const maxTodos = thresholds.maxSourceTodos ?? 0;
452
- const resolvedCount = ctx.recentlyClosedSummary.filter((s) => s.includes("RESOLVED")).length;
453
- const rows = [
454
- `### Mission-Complete Metrics`,
455
- "| Metric | Value | Target | Status |",
456
- "|--------|-------|--------|--------|",
457
- `| Open issues | ${ctx.issuesSummary.length} | 0 | ${ctx.issuesSummary.length === 0 ? "MET" : "NOT MET"} |`,
458
- `| Open PRs | ${ctx.prsSummary.length} | 0 | ${ctx.prsSummary.length === 0 ? "MET" : "NOT MET"} |`,
459
- `| Issues resolved (RESOLVED) | ${resolvedCount} | >= ${minResolved} | ${resolvedCount >= minResolved ? "MET" : "NOT MET"} |`,
460
- `| Dedicated test files | ${ctx.dedicatedTestCount} | >= ${minTests} | ${ctx.dedicatedTestCount >= minTests ? "MET" : "NOT MET"} |`,
461
- `| Source TODO count | ${ctx.sourceTodoCount} | <= ${maxTodos} | ${ctx.sourceTodoCount <= maxTodos ? "MET" : "NOT MET"} |`,
462
- `| Budget used | ${ctx.cumulativeTransformationCost}/${ctx.transformationBudget} | < ${ctx.transformationBudget || "unlimited"} | ${ctx.transformationBudget > 0 && ctx.cumulativeTransformationCost >= ctx.transformationBudget ? "EXHAUSTED" : "OK"} |`,
463
- "",
464
- "**All metrics must show MET/OK for mission-complete to be declared.**",
465
- "",
466
- ];
467
- return rows;
468
- })(),
469
- `### Recent Workflow Runs`,
470
- ctx.workflowsSummary.join("\n") || "none",
471
- "",
472
- ...(ctx.actionsSinceInit.length > 0
473
- ? [
474
- `### Actions Since Last Init${ctx.initTimestamp ? ` (${ctx.initTimestamp})` : ""}`,
475
- "Each entry: workflow | outcome | commit | branch | changes",
476
- ...ctx.actionsSinceInit.map((a) => {
477
- let line = `- ${a.name}: ${a.conclusion} (${a.created}) [${a.commitSha}] ${a.commitMessage}`;
478
- if (a.prNumber) {
479
- line += ` — PR #${a.prNumber}: +${a.additions}/-${a.deletions} in ${a.changedFiles} file(s)`;
480
- }
481
- return line;
482
- }),
483
- "",
484
- ]
485
- : []),
486
- `### Recent Activity`,
487
- ctx.recentActivity || "none",
437
+ `### Mission-Complete Metrics`,
438
+ "| Metric | Value | Target | Status |",
439
+ "|--------|-------|--------|--------|",
440
+ `| Open issues | ${ctx.issuesSummary.length} | 0 | ${ctx.issuesSummary.length === 0 ? "MET" : "NOT MET"} |`,
441
+ `| Open PRs | ${ctx.prsSummary.length} | 0 | ${ctx.prsSummary.length === 0 ? "MET" : "NOT MET"} |`,
442
+ `| Issues resolved (RESOLVED) | ${resolvedCount} | >= ${minResolved} | ${resolvedCount >= minResolved ? "MET" : "NOT MET"} |`,
443
+ `| Dedicated test files | ${ctx.dedicatedTestCount} | >= ${minTests} | ${ctx.dedicatedTestCount >= minTests ? "MET" : "NOT MET"} |`,
444
+ `| Source TODO count | ${ctx.sourceTodoCount} | <= ${maxTodos} | ${ctx.sourceTodoCount <= maxTodos ? "MET" : "NOT MET"} |`,
445
+ `| Budget used | ${ctx.cumulativeTransformationCost}/${ctx.transformationBudget} | < ${ctx.transformationBudget || "unlimited"} | ${ctx.transformationBudget > 0 && ctx.cumulativeTransformationCost >= ctx.transformationBudget ? "EXHAUSTED" : "OK"} |`,
488
446
  "",
489
447
  `### Supervisor: ${ctx.supervisor}`,
490
- "",
491
- "### Configuration (agentic-lib.toml)",
492
- "```toml",
493
- ctx.configToml || "",
494
- "```",
495
- "",
496
- ...(ctx.packageJson ? ["### Dependencies (package.json)", "```json", ctx.packageJson, "```", ""] : []),
497
448
  ...(ctx.activeDiscussionUrl ? [`### Active Discussion`, `${ctx.activeDiscussionUrl}`, ""] : []),
498
449
  ...(ctx.oldestReadyIssue
499
450
  ? [`### Oldest Ready Issue`, `#${ctx.oldestReadyIssue.number}: ${ctx.oldestReadyIssue.title}`, ""]
@@ -501,65 +452,37 @@ function buildPrompt(ctx, agentInstructions, config) {
501
452
  ...(ctx.missionComplete
502
453
  ? [
503
454
  `### Mission Status: COMPLETE`,
504
- ctx.missionCompleteInfo,
505
455
  "Transformation budget is frozen — no transform, maintain, or fix-code dispatches allowed.",
506
- "You may still: review/close issues, respond to discussions, adjust schedule.",
507
456
  "",
508
457
  ]
509
458
  : []),
510
459
  ...(ctx.missionFailed
511
460
  ? [
512
461
  `### Mission Status: FAILED`,
513
- ctx.missionFailedInfo,
514
462
  "The mission has been declared failed. The schedule should be set to off.",
515
- "You may still: review/close issues, respond to discussions.",
516
463
  "",
517
464
  ]
518
465
  : []),
519
466
  ...(ctx.transformationBudget > 0
520
- ? [`### Transformation Budget: ${ctx.cumulativeTransformationCost}/${ctx.transformationBudget} used (${Math.max(0, ctx.transformationBudget - ctx.cumulativeTransformationCost)} remaining)`, "Note: instability transforms (infrastructure fixes) do not count against this budget.", ""]
467
+ ? [`### Transformation Budget: ${ctx.cumulativeTransformationCost}/${ctx.transformationBudget} used (${Math.max(0, ctx.transformationBudget - ctx.cumulativeTransformationCost)} remaining)`, ""]
521
468
  : []),
522
469
  `### Issue Limits`,
523
470
  `Feature development WIP limit: ${ctx.featureIssuesWipLimit}`,
524
471
  `Maintenance WIP limit: ${ctx.maintenanceIssuesWipLimit}`,
525
472
  `Open issues: ${ctx.issuesSummary.length} (capacity for ${Math.max(0, ctx.featureIssuesWipLimit - ctx.issuesSummary.length)} more)`,
526
473
  "",
527
- "## Available Actions",
528
- "Pick one or more actions. Output them in the format below.",
529
- "",
530
- "### Workflow Dispatches",
531
- "- `dispatch:agentic-lib-workflow | mode: dev-only | issue-number: <N>` — Pick up issue #N, generate code, open PR. Always specify the issue-number of the oldest ready issue.",
532
- "- `dispatch:agentic-lib-workflow | mode: maintain-only` — Refresh feature definitions and library docs",
533
- "- `dispatch:agentic-lib-workflow | mode: review-only` — Close resolved issues, enhance issue criteria",
534
- "- `dispatch:agentic-lib-workflow | mode: pr-cleanup-only` — Merge open PRs with the automerge label if checks pass",
535
- "- `dispatch:agentic-lib-bot` — Proactively post in discussions",
536
- "",
537
- "### GitHub API Actions",
538
- "- `github:create-issue | title: <text> | labels: <comma-separated>` — Create a new issue",
539
- "- `github:label-issue | issue-number: <N> | labels: <comma-separated>` — Add labels to an issue",
540
- "- `github:close-issue | issue-number: <N>` — Close an issue",
541
- "",
542
- "### Communication",
543
- "- `respond:discussions | message: <text> | discussion-url: <url>` — Reply via discussions bot",
544
- "",
545
- "### Schedule Control",
546
- "- `set-schedule:<frequency>` — Change supervisor schedule (off, weekly, daily, hourly, continuous). Use `set-schedule:weekly` when mission is substantially complete, `set-schedule:continuous` to ramp up.",
474
+ `### Recent Activity`,
475
+ ctx.recentActivity || "none",
547
476
  "",
548
- "- `nop` — No action needed this cycle",
477
+ "## Your Task",
478
+ "Use list_issues, list_prs, read_file, and search_discussions to explore the repository state.",
479
+ "Then call report_supervisor_plan with your chosen actions and reasoning.",
549
480
  "",
550
- "## Output Format",
551
- "Respond with EXACTLY this structure:",
552
- "```",
553
- "[ACTIONS]",
554
- "action-name | param: value | param: value",
555
- "[/ACTIONS]",
556
- "[REASONING]",
557
- "Why you chose these actions...",
558
- "[/REASONING]",
559
- "```",
481
+ "**You MUST call report_supervisor_plan exactly once.**",
560
482
  ].join("\n");
561
483
  }
562
484
 
485
+ // Legacy text parsers — kept as fallback if the model doesn't call report_supervisor_plan
563
486
  function parseActions(content) {
564
487
  const actionsMatch = content.match(/\[ACTIONS\]([\s\S]*?)\[\/ACTIONS\]/);
565
488
  if (!actionsMatch) return [];
@@ -742,7 +665,7 @@ async function executeAction(octokit, repo, action, params, ctx) {
742
665
  * @returns {Promise<Object>} Result with outcome, tokensUsed, model
743
666
  */
744
667
  export async function supervise(context) {
745
- const { octokit, repo, config, instructions, model } = context;
668
+ const { octokit, repo, config, instructions, model, logFilePath, screenshotFilePath } = context;
746
669
  const t = config.tuning || {};
747
670
 
748
671
  const ctx = await gatherContext(octokit, repo, config, t);
@@ -751,9 +674,7 @@ export async function supervise(context) {
751
674
  // --- Deterministic lifecycle posts (before LLM) ---
752
675
 
753
676
  // Step 2: Auto-announce on first run after init
754
- // Detect first supervisor run: initTimestamp exists but no prior supervisor workflow runs since init
755
677
  if (ctx.initTimestamp && !ctx.missionComplete && !ctx.missionFailed) {
756
- // Check for any prior agentic-lib-workflow runs since init (count > 1 because current run is included)
757
678
  const supervisorRunCount = ctx.actionsSinceInit.filter(
758
679
  (a) => a.name.startsWith("agentic-lib-workflow"),
759
680
  ).length;
@@ -765,47 +686,115 @@ export async function supervise(context) {
765
686
  }
766
687
  }
767
688
 
768
- // --- LLM decision ---
689
+ // --- LLM decision via hybrid session ---
769
690
  const agentInstructions = instructions || "You are the supervisor. Decide what actions to take.";
770
691
  const prompt = buildPrompt(ctx, agentInstructions, config);
771
692
 
772
- const { content, tokensUsed, inputTokens, outputTokens, cost } = await runCopilotTask({
693
+ const systemPrompt =
694
+ "You are the supervisor of an autonomous coding repository. Your job is to advance the mission by choosing which workflows to dispatch and which GitHub actions to take. Pick multiple actions when appropriate. Be strategic — consider what's already in progress, what's blocked, and what will make the most impact." +
695
+ NARRATIVE_INSTRUCTION;
696
+
697
+ // Shared mutable state to capture the plan
698
+ const planResult = { actions: [], reasoning: "" };
699
+
700
+ const createTools = (defineTool, _wp, logger) => {
701
+ const ghTools = createGitHubTools(octokit, repo, defineTool, logger);
702
+ const discTools = createDiscussionTools(octokit, repo, defineTool, logger);
703
+ const gitTools = createGitTools(defineTool, logger);
704
+
705
+ const reportPlan = defineTool("report_supervisor_plan", {
706
+ description: "Report the supervisor's chosen actions and reasoning. Call this exactly once. Actions will be executed automatically.",
707
+ parameters: {
708
+ type: "object",
709
+ properties: {
710
+ actions: {
711
+ type: "array",
712
+ items: {
713
+ type: "object",
714
+ properties: {
715
+ action: { type: "string", description: "Action name (e.g. dispatch:agentic-lib-workflow, github:create-issue, set-schedule:weekly, nop)" },
716
+ params: { type: "object", description: "Action parameters (e.g. mode, issue-number, title, labels, message, discussion-url, frequency)" },
717
+ },
718
+ required: ["action"],
719
+ },
720
+ description: "List of actions to execute",
721
+ },
722
+ reasoning: { type: "string", description: "Why you chose these actions" },
723
+ },
724
+ required: ["actions", "reasoning"],
725
+ },
726
+ handler: async ({ actions, reasoning }) => {
727
+ planResult.reasoning = reasoning || "";
728
+
729
+ // Execute each action using existing handlers
730
+ const results = [];
731
+ for (const { action, params } of (actions || [])) {
732
+ try {
733
+ const result = await executeAction(octokit, repo, action, params || {}, ctx);
734
+ results.push(result);
735
+ logger.info(`Action result: ${result}`);
736
+ } catch (err) {
737
+ logger.warning(`Action ${action} failed: ${err.message}`);
738
+ results.push(`error:${action}:${err.message}`);
739
+ }
740
+ }
741
+
742
+ planResult.actions = actions || [];
743
+ planResult.results = results;
744
+ return { textResultForLlm: `Executed ${results.length} action(s): ${results.join(", ")}` };
745
+ },
746
+ });
747
+
748
+ return [...ghTools, ...discTools, ...gitTools, reportPlan];
749
+ };
750
+
751
+ const attachments = [];
752
+ if (logFilePath) attachments.push({ type: "file", path: logFilePath });
753
+ if (screenshotFilePath) attachments.push({ type: "file", path: screenshotFilePath });
754
+
755
+ const result = await runCopilotSession({
756
+ workspacePath: process.cwd(),
773
757
  model,
774
- systemMessage:
775
- "You are the supervisor of an autonomous coding repository. Your job is to advance the mission by choosing which workflows to dispatch and which GitHub actions to take. Pick multiple actions when appropriate. Be strategic — consider what's already in progress, what's blocked, and what will make the most impact.",
776
- prompt,
777
- writablePaths: [],
778
758
  tuning: t,
759
+ agentPrompt: systemPrompt,
760
+ userPrompt: prompt,
761
+ writablePaths: [],
762
+ createTools,
763
+ attachments,
764
+ excludedTools: ["write_file", "run_command", "run_tests"],
765
+ logger: { info: core.info, warning: core.warning, error: core.error, debug: core.debug },
779
766
  });
780
767
 
781
- const actions = parseActions(content);
782
- const reasoning = parseReasoning(content);
768
+ const tokensUsed = result.tokensIn + result.tokensOut;
783
769
 
784
- core.info(`Supervisor reasoning: ${reasoning.substring(0, 200)}`);
785
- core.info(`Supervisor chose ${actions.length} action(s)`);
770
+ // Extract actions — prefer tool result, fall back to text parsing
771
+ let actions = planResult.actions;
772
+ let reasoning = planResult.reasoning;
773
+ let results = planResult.results || [];
786
774
 
787
- const results = [];
788
- for (const { action, params } of actions) {
789
- try {
790
- const result = await executeAction(octokit, repo, action, params, ctx);
791
- results.push(result);
792
- core.info(`Action result: ${result}`);
793
- } catch (err) {
794
- core.warning(`Action ${action} failed: ${err.message}`);
795
- results.push(`error:${action}:${err.message}`);
775
+ if (actions.length === 0 && result.agentMessage) {
776
+ actions = parseActions(result.agentMessage);
777
+ reasoning = parseReasoning(result.agentMessage);
778
+
779
+ // Execute fallback-parsed actions
780
+ for (const { action, params } of actions) {
781
+ try {
782
+ const r = await executeAction(octokit, repo, action, params, ctx);
783
+ results.push(r);
784
+ core.info(`Action result: ${r}`);
785
+ } catch (err) {
786
+ core.warning(`Action ${action} failed: ${err.message}`);
787
+ results.push(`error:${action}:${err.message}`);
788
+ }
796
789
  }
797
790
  }
798
791
 
799
- // --- Deterministic lifecycle posts (after LLM) ---
792
+ core.info(`Supervisor reasoning: ${reasoning.substring(0, 200)}`);
793
+ core.info(`Supervisor chose ${actions.length} action(s)`);
800
794
 
801
- // W12: Mission-complete/failed evaluation moved to the director task.
802
- // The supervisor no longer declares mission-complete or mission-failed.
795
+ // --- Deterministic lifecycle posts (after LLM) ---
803
796
 
804
797
  // Step 3: Auto-respond when a message referral is present
805
- // If the workflow was triggered with a message (from bot's request-supervisor),
806
- // and the LLM didn't include a respond:discussions action, post back directly.
807
- // Posts directly via GraphQL to avoid triggering the bot workflow (which would
808
- // request-supervisor again, creating an infinite loop).
809
798
  const workflowMessage = context.discussionUrl ? "" : (process.env.INPUT_MESSAGE || "");
810
799
  if (workflowMessage && ctx.activeDiscussionUrl) {
811
800
  const hasDiscussionResponse = results.some((r) => r.startsWith("respond-discussions:"));
@@ -827,12 +816,12 @@ export async function supervise(context) {
827
816
  return {
828
817
  outcome: actions.length === 0 ? "nop" : `supervised:${actions.length}-actions`,
829
818
  tokensUsed,
830
- inputTokens,
831
- outputTokens,
832
- cost,
819
+ inputTokens: result.tokensIn,
820
+ outputTokens: result.tokensOut,
821
+ cost: 0,
833
822
  model,
834
823
  details: `Actions: ${results.join(", ")}\nReasoning: ${reasoning.substring(0, 300)}`,
835
- narrative: reasoning.substring(0, 500),
824
+ narrative: result.narrative || reasoning.substring(0, 500),
836
825
  changes,
837
826
  };
838
827
  }