@agent-native/core 0.49.2 → 0.49.4
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/dist/agent/engine/builder-engine.d.ts +1 -1
- package/dist/agent/engine/builder-engine.d.ts.map +1 -1
- package/dist/agent/engine/index.d.ts +1 -1
- package/dist/agent/engine/index.d.ts.map +1 -1
- package/dist/agent/engine/index.js +1 -1
- package/dist/agent/engine/index.js.map +1 -1
- package/dist/agent/engine/registry.d.ts +1 -0
- package/dist/agent/engine/registry.d.ts.map +1 -1
- package/dist/agent/engine/registry.js +11 -0
- package/dist/agent/engine/registry.js.map +1 -1
- package/dist/agent/model-config.d.ts +2 -2
- package/dist/agent/model-config.d.ts.map +1 -1
- package/dist/agent/model-config.js +2 -6
- package/dist/agent/model-config.js.map +1 -1
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +3 -2
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/cli/code-agent-executor.d.ts.map +1 -1
- package/dist/cli/code-agent-executor.js +3 -2
- package/dist/cli/code-agent-executor.js.map +1 -1
- package/dist/cli/connect.d.ts.map +1 -1
- package/dist/cli/connect.js +117 -33
- package/dist/cli/connect.js.map +1 -1
- package/dist/cli/pr-visual-recap-workflow.d.ts +1 -1
- package/dist/cli/pr-visual-recap-workflow.d.ts.map +1 -1
- package/dist/cli/pr-visual-recap-workflow.js +1 -1
- package/dist/cli/pr-visual-recap-workflow.js.map +1 -1
- package/dist/cli/recap.d.ts +20 -0
- package/dist/cli/recap.d.ts.map +1 -1
- package/dist/cli/recap.js +256 -40
- package/dist/cli/recap.js.map +1 -1
- package/dist/cli/skills.d.ts +2 -2
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +5 -5
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/client/MultiTabAssistantChat.js +0 -7
- package/dist/client/MultiTabAssistantChat.js.map +1 -1
- package/dist/client/blocks/library/annotation-rail.d.ts +14 -0
- package/dist/client/blocks/library/annotation-rail.d.ts.map +1 -1
- package/dist/client/blocks/library/annotation-rail.js +64 -2
- package/dist/client/blocks/library/annotation-rail.js.map +1 -1
- package/dist/client/extensions/EmbeddedExtension.d.ts.map +1 -1
- package/dist/client/extensions/EmbeddedExtension.js +14 -6
- package/dist/client/extensions/EmbeddedExtension.js.map +1 -1
- package/dist/client/extensions/ExtensionViewer.d.ts.map +1 -1
- package/dist/client/extensions/ExtensionViewer.js +16 -7
- package/dist/client/extensions/ExtensionViewer.js.map +1 -1
- package/dist/client/extensions/extension-load-error.d.ts +7 -0
- package/dist/client/extensions/extension-load-error.d.ts.map +1 -0
- package/dist/client/extensions/extension-load-error.js +19 -0
- package/dist/client/extensions/extension-load-error.js.map +1 -0
- package/dist/client/use-chat-models.d.ts.map +1 -1
- package/dist/client/use-chat-models.js +0 -5
- package/dist/client/use-chat-models.js.map +1 -1
- package/dist/extensions/routes.d.ts.map +1 -1
- package/dist/extensions/routes.js +7 -3
- package/dist/extensions/routes.js.map +1 -1
- package/dist/file-upload/actions/upload-image.js +1 -1
- package/dist/file-upload/actions/upload-image.js.map +1 -1
- package/dist/file-upload/pre-upload-attachments.js +1 -1
- package/dist/file-upload/pre-upload-attachments.js.map +1 -1
- package/dist/file-upload/registry.js +1 -1
- package/dist/file-upload/registry.js.map +1 -1
- package/dist/integrations/webhook-handler.d.ts.map +1 -1
- package/dist/integrations/webhook-handler.js +3 -2
- package/dist/integrations/webhook-handler.js.map +1 -1
- package/dist/jobs/scheduler.d.ts.map +1 -1
- package/dist/jobs/scheduler.js +3 -2
- package/dist/jobs/scheduler.js.map +1 -1
- package/dist/mcp/connect-route.d.ts +2 -0
- package/dist/mcp/connect-route.d.ts.map +1 -1
- package/dist/mcp/connect-route.js +3 -0
- package/dist/mcp/connect-route.js.map +1 -1
- package/dist/mcp/server.js +1 -1
- package/dist/mcp/server.js.map +1 -1
- package/dist/observability/evals.d.ts.map +1 -1
- package/dist/observability/evals.js +5 -3
- package/dist/observability/evals.js.map +1 -1
- package/dist/scripts/agent-engines/list-agent-engines.d.ts.map +1 -1
- package/dist/scripts/agent-engines/list-agent-engines.js +6 -3
- package/dist/scripts/agent-engines/list-agent-engines.js.map +1 -1
- package/dist/scripts/agent-engines/manage-agent-engine.d.ts.map +1 -1
- package/dist/scripts/agent-engines/manage-agent-engine.js +5 -1
- package/dist/scripts/agent-engines/manage-agent-engine.js.map +1 -1
- package/dist/scripts/agent-engines/set-agent-engine.d.ts.map +1 -1
- package/dist/scripts/agent-engines/set-agent-engine.js +7 -2
- package/dist/scripts/agent-engines/set-agent-engine.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +14 -4
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/complete-text.d.ts.map +1 -1
- package/dist/server/complete-text.js +3 -2
- package/dist/server/complete-text.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts +2 -0
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +1 -0
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/templates/workspace-core/.agents/skills/external-agents/SKILL.md +7 -6
- package/dist/triggers/dispatcher.d.ts.map +1 -1
- package/dist/triggers/dispatcher.js +3 -2
- package/dist/triggers/dispatcher.js.map +1 -1
- package/docs/content/external-agents.md +1 -1
- package/docs/content/plan-plugin.md +2 -2
- package/docs/content/pr-visual-recap.md +12 -4
- package/package.json +1 -1
- package/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +7 -6
package/dist/cli/recap.js
CHANGED
|
@@ -125,10 +125,8 @@ export function writePrVisualRecapWorkflow(baseDir, options = {}) {
|
|
|
125
125
|
*/
|
|
126
126
|
export function buildReusableCallerWorkflow(options = {}) {
|
|
127
127
|
const ref = (options.ref ?? "main").replace(/^@/, "");
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
: "";
|
|
131
|
-
const modelLine = options.model ? `\n model: ${options.model}` : "";
|
|
128
|
+
const agentValue = options.agent ?? "${{ vars.VISUAL_RECAP_AGENT || 'claude' }}";
|
|
129
|
+
const modelValue = options.model ?? "${{ vars.VISUAL_RECAP_MODEL || '' }}";
|
|
132
130
|
return (`name: PR Visual Recap\n` +
|
|
133
131
|
`\n` +
|
|
134
132
|
`# Thin caller — the full workflow logic lives in BuilderIO/agent-native.\n` +
|
|
@@ -142,16 +140,25 @@ export function buildReusableCallerWorkflow(options = {}) {
|
|
|
142
140
|
`\n` +
|
|
143
141
|
`jobs:\n` +
|
|
144
142
|
` visual-recap:\n` +
|
|
143
|
+
` permissions:\n` +
|
|
144
|
+
` actions: write\n` +
|
|
145
|
+
` contents: read\n` +
|
|
146
|
+
` checks: write\n` +
|
|
147
|
+
` issues: write\n` +
|
|
148
|
+
` pull-requests: write\n` +
|
|
145
149
|
` uses: BuilderIO/agent-native/.github/workflows/pr-visual-recap-reusable.yml@${ref}\n` +
|
|
146
150
|
` secrets:\n` +
|
|
147
151
|
` PLAN_RECAP_TOKEN: \${{ secrets.PLAN_RECAP_TOKEN }}\n` +
|
|
148
152
|
` ANTHROPIC_API_KEY: \${{ secrets.ANTHROPIC_API_KEY }}\n` +
|
|
149
|
-
`
|
|
150
|
-
`
|
|
151
|
-
` with
|
|
153
|
+
` OPENAI_API_KEY: \${{ secrets.OPENAI_API_KEY }}\n` +
|
|
154
|
+
` PLAN_RECAP_APP_URL: \${{ secrets.PLAN_RECAP_APP_URL }}\n` +
|
|
155
|
+
` with:\n` +
|
|
156
|
+
` agent: ${agentValue}\n` +
|
|
157
|
+
` model: ${modelValue}\n` +
|
|
158
|
+
` reasoning: \${{ vars.VISUAL_RECAP_REASONING || '' }}\n` +
|
|
159
|
+
` skill-source: \${{ vars.VISUAL_RECAP_SKILL_SOURCE || 'auto' }}\n` +
|
|
152
160
|
` # cli-version: "latest" # pin to a specific @agent-native/core version\n` +
|
|
153
|
-
|
|
154
|
-
` # skill-source: "repo" # pin to committed visual-recap skill\n`);
|
|
161
|
+
``);
|
|
155
162
|
}
|
|
156
163
|
/** File name for the reusable caller workflow. */
|
|
157
164
|
const REUSABLE_CALLER_WORKFLOW_FILE = "pr-visual-recap.yml";
|
|
@@ -650,20 +657,23 @@ export function diffContainsSecret(diffText, allowlist = []) {
|
|
|
650
657
|
return false;
|
|
651
658
|
}
|
|
652
659
|
const AGENT_FAILURE_MAX_CHARS = 1200;
|
|
660
|
+
const STALE_WORKFLOW_FAILURE_SUMMARY = "No agent failure summary was captured. This repo may be using an older PR Visual Recap workflow; refresh `.github/workflows/pr-visual-recap.yml` with `npx -y @agent-native/core@latest recap setup --force`, then rerun the workflow. See the GitHub Actions log for the agent step.";
|
|
653
661
|
function compactWhitespace(text) {
|
|
654
662
|
return text.replace(/\s+/g, " ").trim();
|
|
655
663
|
}
|
|
656
664
|
export function sanitizeAgentFailureSummary(value, maxChars = AGENT_FAILURE_MAX_CHARS) {
|
|
665
|
+
const redactSecretValues = (line) => line
|
|
666
|
+
.replace(/Authorization:\s*Bearer\s+[A-Za-z0-9._-]{8,}/gi, "Authorization: Bearer [redacted]")
|
|
667
|
+
.replace(/Bearer\s+[A-Za-z0-9._-]{8,}/gi, "Bearer [redacted]")
|
|
668
|
+
.replace(/Authorization:\s*(?!Bearer\s+\[redacted\])[^\s]+/gi, "Authorization: [redacted]")
|
|
669
|
+
.replace(/PLAN_RECAP_TOKEN=([^\s]+)/g, "PLAN_RECAP_TOKEN=[redacted]")
|
|
670
|
+
.replace(/ANTHROPIC_API_KEY=([^\s]+)/g, "ANTHROPIC_API_KEY=[redacted]")
|
|
671
|
+
.replace(/OPENAI_API_KEY=([^\s]+)/g, "OPENAI_API_KEY=[redacted]");
|
|
657
672
|
const sanitizedLines = value
|
|
658
673
|
.replace(/\u001b\[[0-9;]*m/g, "")
|
|
659
674
|
.split("\n")
|
|
675
|
+
.map(redactSecretValues)
|
|
660
676
|
.map((line) => (lineLooksSecret(line) ? "[redacted sensitive line]" : line))
|
|
661
|
-
.map((line) => line
|
|
662
|
-
.replace(/Bearer\s+[A-Za-z0-9._-]{8,}/gi, "Bearer [redacted]")
|
|
663
|
-
.replace(/Authorization:\s*[^\s]+/gi, "Authorization: [redacted]")
|
|
664
|
-
.replace(/PLAN_RECAP_TOKEN=([^\s]+)/g, "PLAN_RECAP_TOKEN=[redacted]")
|
|
665
|
-
.replace(/ANTHROPIC_API_KEY=([^\s]+)/g, "ANTHROPIC_API_KEY=[redacted]")
|
|
666
|
-
.replace(/OPENAI_API_KEY=([^\s]+)/g, "OPENAI_API_KEY=[redacted]"))
|
|
667
677
|
.join("\n");
|
|
668
678
|
const compacted = compactWhitespace(sanitizedLines);
|
|
669
679
|
if (compacted.length <= maxChars)
|
|
@@ -689,6 +699,21 @@ function collectStringFields(value, fields, seen = new Set()) {
|
|
|
689
699
|
}
|
|
690
700
|
return out;
|
|
691
701
|
}
|
|
702
|
+
function isUsefulAgentSummaryCandidate(candidate) {
|
|
703
|
+
const value = candidate.trim();
|
|
704
|
+
if (!value)
|
|
705
|
+
return false;
|
|
706
|
+
if (/^(turn|session|item|response|task)\.[a-z0-9_.-]+$/i.test(value)) {
|
|
707
|
+
return false;
|
|
708
|
+
}
|
|
709
|
+
if (/^(success|completed|result|message|error)$/i.test(value)) {
|
|
710
|
+
return false;
|
|
711
|
+
}
|
|
712
|
+
return value.length > 12;
|
|
713
|
+
}
|
|
714
|
+
function isErrorLikeAgentSummary(candidate) {
|
|
715
|
+
return /error|failed|denied|not found|unavailable|unauthorized|forbidden|tool|exception|timeout|timed out|could not|cannot/i.test(candidate);
|
|
716
|
+
}
|
|
692
717
|
export function summarizeAgentResult(agent, resultText) {
|
|
693
718
|
const normalizedAgent = agent.toLowerCase();
|
|
694
719
|
const text = resultText.trim();
|
|
@@ -707,7 +732,10 @@ export function summarizeAgentResult(agent, resultText) {
|
|
|
707
732
|
"type",
|
|
708
733
|
]),
|
|
709
734
|
].filter(Boolean);
|
|
710
|
-
const
|
|
735
|
+
const usefulCandidates = candidates.filter(isUsefulAgentSummaryCandidate);
|
|
736
|
+
const preferred = usefulCandidates.find(isErrorLikeAgentSummary) ??
|
|
737
|
+
usefulCandidates[0] ??
|
|
738
|
+
candidates.find(isErrorLikeAgentSummary);
|
|
711
739
|
if (preferred)
|
|
712
740
|
return sanitizeAgentFailureSummary(preferred);
|
|
713
741
|
}
|
|
@@ -726,7 +754,13 @@ export function summarizeAgentResult(agent, resultText) {
|
|
|
726
754
|
"text",
|
|
727
755
|
"delta",
|
|
728
756
|
"reason",
|
|
729
|
-
"
|
|
757
|
+
"detail",
|
|
758
|
+
"details",
|
|
759
|
+
"stderr",
|
|
760
|
+
"stdout",
|
|
761
|
+
"summary",
|
|
762
|
+
"result",
|
|
763
|
+
"content",
|
|
730
764
|
]));
|
|
731
765
|
}
|
|
732
766
|
catch {
|
|
@@ -734,13 +768,88 @@ export function summarizeAgentResult(agent, resultText) {
|
|
|
734
768
|
}
|
|
735
769
|
}
|
|
736
770
|
const newestFirst = [...candidates].reverse();
|
|
737
|
-
const
|
|
738
|
-
|
|
771
|
+
const usefulCandidates = newestFirst.filter(isUsefulAgentSummaryCandidate);
|
|
772
|
+
const preferred = usefulCandidates.find(isErrorLikeAgentSummary) ?? usefulCandidates[0];
|
|
739
773
|
if (preferred)
|
|
740
774
|
return sanitizeAgentFailureSummary(preferred);
|
|
741
775
|
}
|
|
742
776
|
return sanitizeAgentFailureSummary(text);
|
|
743
777
|
}
|
|
778
|
+
function agentLabel(agent) {
|
|
779
|
+
const normalized = agent.toLowerCase();
|
|
780
|
+
if (normalized === "codex")
|
|
781
|
+
return "Codex";
|
|
782
|
+
if (normalized === "claude")
|
|
783
|
+
return "Claude";
|
|
784
|
+
return agent || "Agent";
|
|
785
|
+
}
|
|
786
|
+
export function summarizeAgentRun(input) {
|
|
787
|
+
const parts = [];
|
|
788
|
+
const exitCode = (input.exitCode ?? "").trim();
|
|
789
|
+
if (exitCode && exitCode !== "0") {
|
|
790
|
+
parts.push(`${agentLabel(input.agent)} exited with code ${exitCode}.`);
|
|
791
|
+
}
|
|
792
|
+
const resultSummary = summarizeAgentResult(input.agent, input.resultText ?? "");
|
|
793
|
+
if (resultSummary)
|
|
794
|
+
parts.push(resultSummary);
|
|
795
|
+
const stderrSummary = sanitizeAgentFailureSummary(input.stderrText ?? "", 500);
|
|
796
|
+
if (stderrSummary)
|
|
797
|
+
parts.push(`stderr: ${stderrSummary}`);
|
|
798
|
+
return sanitizeAgentFailureSummary(parts.join(" "));
|
|
799
|
+
}
|
|
800
|
+
function readTextIfExists(file) {
|
|
801
|
+
try {
|
|
802
|
+
if (!fs.existsSync(file))
|
|
803
|
+
return null;
|
|
804
|
+
return fs.readFileSync(file, "utf8");
|
|
805
|
+
}
|
|
806
|
+
catch {
|
|
807
|
+
return null;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
function localAgentResultCandidates(agent) {
|
|
811
|
+
const all = [
|
|
812
|
+
{
|
|
813
|
+
agent: "claude",
|
|
814
|
+
resultFile: "claude-result.json",
|
|
815
|
+
stderrFile: "claude-stderr.log",
|
|
816
|
+
exitCodeFile: "claude-exit-code.txt",
|
|
817
|
+
},
|
|
818
|
+
{
|
|
819
|
+
agent: "codex",
|
|
820
|
+
resultFile: "codex-events.jsonl",
|
|
821
|
+
stderrFile: "codex-stderr.log",
|
|
822
|
+
exitCodeFile: "codex-exit-code.txt",
|
|
823
|
+
},
|
|
824
|
+
];
|
|
825
|
+
const normalized = agent.toLowerCase();
|
|
826
|
+
if (normalized === "codex")
|
|
827
|
+
return [all[1], all[0]];
|
|
828
|
+
return all;
|
|
829
|
+
}
|
|
830
|
+
export function summarizeLocalAgentFailure(input = {}) {
|
|
831
|
+
const cwd = input.cwd ?? process.cwd();
|
|
832
|
+
for (const candidate of localAgentResultCandidates(input.agent ?? "")) {
|
|
833
|
+
const resultPath = path.join(cwd, candidate.resultFile);
|
|
834
|
+
const stderrPath = path.join(cwd, candidate.stderrFile);
|
|
835
|
+
const exitCodePath = path.join(cwd, candidate.exitCodeFile);
|
|
836
|
+
const resultText = readTextIfExists(resultPath);
|
|
837
|
+
const stderrText = readTextIfExists(stderrPath);
|
|
838
|
+
const exitCode = readTextIfExists(exitCodePath);
|
|
839
|
+
if (resultText === null && stderrText === null && exitCode === null) {
|
|
840
|
+
continue;
|
|
841
|
+
}
|
|
842
|
+
const summary = summarizeAgentRun({
|
|
843
|
+
agent: candidate.agent,
|
|
844
|
+
resultText: resultText ?? "",
|
|
845
|
+
stderrText: stderrText ?? "",
|
|
846
|
+
exitCode: exitCode ?? "",
|
|
847
|
+
});
|
|
848
|
+
if (summary)
|
|
849
|
+
return summary;
|
|
850
|
+
}
|
|
851
|
+
return "";
|
|
852
|
+
}
|
|
744
853
|
/* -------------------------------------------------------------------------- */
|
|
745
854
|
/* Bounded diff collection — was the workflow's "Collect bounded diff" step */
|
|
746
855
|
/* -------------------------------------------------------------------------- */
|
|
@@ -1385,17 +1494,22 @@ export function buildCommentBody(env = process.env) {
|
|
|
1385
1494
|
const markerPlanId = trustedPlanId ?? prevPlanId;
|
|
1386
1495
|
if (!safeUrl) {
|
|
1387
1496
|
const authFailed = env.RECAP_AUTH_FAILED === "true";
|
|
1388
|
-
const
|
|
1497
|
+
const diagnostic = buildRecapFailureDiagnostic({
|
|
1498
|
+
failureSummary: (env.RECAP_AGENT_SUMMARY || "").trim(),
|
|
1499
|
+
urlReason: (env.RECAP_URL_REASON || "").trim(),
|
|
1500
|
+
});
|
|
1389
1501
|
lines.push("### Visual recap — generation failed");
|
|
1390
1502
|
lines.push("");
|
|
1391
1503
|
if (authFailed) {
|
|
1392
|
-
lines.push("Recap authentication failed — the `PLAN_RECAP_TOKEN` secret may be expired or revoked. Re-mint it with `npx @agent-native/core@latest reconnect <app-url>` (or `npx @agent-native/core@latest connect <app-url>` for first-time setup) and update the repo secret.");
|
|
1504
|
+
lines.push("Recap authentication failed — the `PLAN_RECAP_TOKEN` secret may be expired or revoked. Re-mint it with `npx -y @agent-native/core@latest reconnect <app-url>` (or `npx @agent-native/core@latest connect <app-url>` for first-time setup) and update the repo secret.");
|
|
1393
1505
|
}
|
|
1394
1506
|
else {
|
|
1395
1507
|
lines.push("The visual recap could not be generated for this pull request. This is informational only and does **not** block the PR.");
|
|
1396
|
-
if (
|
|
1508
|
+
if (diagnostic) {
|
|
1397
1509
|
lines.push("");
|
|
1398
|
-
lines.push(
|
|
1510
|
+
lines.push("Diagnostic:");
|
|
1511
|
+
lines.push("");
|
|
1512
|
+
lines.push(diagnostic);
|
|
1399
1513
|
}
|
|
1400
1514
|
}
|
|
1401
1515
|
// Keep a link to the last-good recap so reviewers are not left in the dark.
|
|
@@ -1587,7 +1701,7 @@ const RECAP_SHOT_VIEWPORT = {
|
|
|
1587
1701
|
width: RECAP_SHOT_WIDTH,
|
|
1588
1702
|
height: RECAP_SHOT_MAX_HEIGHT,
|
|
1589
1703
|
};
|
|
1590
|
-
const RECAP_SHOT_DEVICE_SCALE_FACTOR =
|
|
1704
|
+
const RECAP_SHOT_DEVICE_SCALE_FACTOR = 2;
|
|
1591
1705
|
async function defaultImportPlaywright() {
|
|
1592
1706
|
try {
|
|
1593
1707
|
return (await import("playwright"));
|
|
@@ -1792,7 +1906,7 @@ async function runComment(args, sub) {
|
|
|
1792
1906
|
owner,
|
|
1793
1907
|
repo,
|
|
1794
1908
|
issue,
|
|
1795
|
-
body: buildCommentBody(),
|
|
1909
|
+
body: buildCommentBody(recoverRecapFailureEnv()),
|
|
1796
1910
|
updateOnly: args["update-only"] === true || args["update-only"] === "true",
|
|
1797
1911
|
});
|
|
1798
1912
|
process.stdout.write(`${JSON.stringify(result)}\n`);
|
|
@@ -1800,6 +1914,30 @@ async function runComment(args, sub) {
|
|
|
1800
1914
|
}
|
|
1801
1915
|
throw new Error("Usage: npx @agent-native/core@latest recap comment <find-plan-id|upsert> --repo owner/name --issue n --token token");
|
|
1802
1916
|
}
|
|
1917
|
+
function shouldRecoverRecapFailureDetails(env) {
|
|
1918
|
+
return (!(env.PLAN_URL || "").trim() &&
|
|
1919
|
+
env.DIFF_TINY !== "true" &&
|
|
1920
|
+
env.SUPPRESSED !== "true");
|
|
1921
|
+
}
|
|
1922
|
+
function recoverRecapFailureEnv(env = process.env) {
|
|
1923
|
+
if (!shouldRecoverRecapFailureDetails(env))
|
|
1924
|
+
return env;
|
|
1925
|
+
const recovered = { ...env };
|
|
1926
|
+
if (!recovered.RECAP_AGENT_SUMMARY) {
|
|
1927
|
+
recovered.RECAP_AGENT_SUMMARY = summarizeLocalAgentFailure({
|
|
1928
|
+
agent: recovered.RECAP_AGENT || recovered.VISUAL_RECAP_AGENT,
|
|
1929
|
+
});
|
|
1930
|
+
}
|
|
1931
|
+
if (!recovered.RECAP_URL_REASON) {
|
|
1932
|
+
recovered.RECAP_URL_REASON = inferLocalRecapUrlFailureReason({
|
|
1933
|
+
appUrl: recovered.PLAN_RECAP_APP_URL,
|
|
1934
|
+
});
|
|
1935
|
+
}
|
|
1936
|
+
if (!recovered.RECAP_AGENT_SUMMARY && !recovered.RECAP_URL_REASON) {
|
|
1937
|
+
recovered.RECAP_AGENT_SUMMARY = STALE_WORKFLOW_FAILURE_SUMMARY;
|
|
1938
|
+
}
|
|
1939
|
+
return recovered;
|
|
1940
|
+
}
|
|
1803
1941
|
/**
|
|
1804
1942
|
* Files that, if a PR touches them, would let that PR rewrite what the trusted
|
|
1805
1943
|
* recap job runs (the workflow itself, the skill, the local CLI, or any agent
|
|
@@ -2085,8 +2223,10 @@ export function appendGateSkipLine(existingBody, skipLine) {
|
|
|
2085
2223
|
*/
|
|
2086
2224
|
export function canonicalRecapUrl(rawUrl, appUrl) {
|
|
2087
2225
|
try {
|
|
2088
|
-
const parsed = new URL(rawUrl);
|
|
2089
2226
|
const trusted = new URL(appUrl || "https://plan.agent-native.com");
|
|
2227
|
+
const parsed = /^https?:\/\//i.test(rawUrl)
|
|
2228
|
+
? new URL(rawUrl)
|
|
2229
|
+
: new URL(rawUrl, trusted);
|
|
2090
2230
|
if (parsed.origin !== trusted.origin)
|
|
2091
2231
|
return "";
|
|
2092
2232
|
// Honor a path-prefixed mount (e.g. https://host/agent-native): strip the
|
|
@@ -2102,6 +2242,43 @@ export function canonicalRecapUrl(rawUrl, appUrl) {
|
|
|
2102
2242
|
return "";
|
|
2103
2243
|
}
|
|
2104
2244
|
}
|
|
2245
|
+
export function inferLocalRecapUrlFailureReason(input = {}) {
|
|
2246
|
+
const recapUrlPath = path.join(input.cwd ?? process.cwd(), "recap-url.txt");
|
|
2247
|
+
const raw = readTextIfExists(recapUrlPath);
|
|
2248
|
+
if (raw === null)
|
|
2249
|
+
return "recap-url.txt was not created by the agent.";
|
|
2250
|
+
const value = raw.replace(/[\r\n\s]/g, "");
|
|
2251
|
+
if (!value)
|
|
2252
|
+
return "recap-url.txt was empty.";
|
|
2253
|
+
const appUrl = input.appUrl ||
|
|
2254
|
+
process.env.PLAN_RECAP_APP_URL ||
|
|
2255
|
+
"https://plan.agent-native.com";
|
|
2256
|
+
if (canonicalRecapUrl(value, appUrl))
|
|
2257
|
+
return "";
|
|
2258
|
+
try {
|
|
2259
|
+
const trusted = new URL(appUrl || "https://plan.agent-native.com");
|
|
2260
|
+
const parsed = /^https?:\/\//i.test(value)
|
|
2261
|
+
? new URL(value)
|
|
2262
|
+
: new URL(value, trusted);
|
|
2263
|
+
if (parsed.origin !== trusted.origin) {
|
|
2264
|
+
return `recap-url.txt points at ${parsed.origin}, expected ${trusted.origin}.`;
|
|
2265
|
+
}
|
|
2266
|
+
return "recap-url.txt did not contain a valid /plans/<id> or /recaps/<id> URL for the configured plan app.";
|
|
2267
|
+
}
|
|
2268
|
+
catch {
|
|
2269
|
+
return "recap-url.txt was not a valid URL or recap path.";
|
|
2270
|
+
}
|
|
2271
|
+
}
|
|
2272
|
+
export function buildRecapFailureDiagnostic(input) {
|
|
2273
|
+
const parts = [];
|
|
2274
|
+
const urlReason = sanitizeAgentFailureSummary(input.urlReason ?? "", 400);
|
|
2275
|
+
const failureSummary = sanitizeAgentFailureSummary(input.failureSummary ?? "", 900);
|
|
2276
|
+
if (urlReason)
|
|
2277
|
+
parts.push(`No plan URL: ${urlReason}`);
|
|
2278
|
+
if (failureSummary)
|
|
2279
|
+
parts.push(`Agent output: ${failureSummary}`);
|
|
2280
|
+
return parts.join("\n\n");
|
|
2281
|
+
}
|
|
2105
2282
|
/**
|
|
2106
2283
|
* Map the workflow's terminal recap state to the completed check's
|
|
2107
2284
|
* conclusion/title/summary/text/details_url. Pure so it can be unit-tested —
|
|
@@ -2118,9 +2295,11 @@ export function recapCheckOutcome(input) {
|
|
|
2118
2295
|
let conclusion = "neutral";
|
|
2119
2296
|
let title = "Visual recap not generated";
|
|
2120
2297
|
let summary = "The visual recap did not produce a plan URL. This is informational only and does not block the PR.";
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
:
|
|
2298
|
+
const diagnostic = buildRecapFailureDiagnostic({
|
|
2299
|
+
failureSummary: input.failureSummary,
|
|
2300
|
+
urlReason: input.urlReason,
|
|
2301
|
+
});
|
|
2302
|
+
let text = diagnostic ? `### Diagnostic\n\n${diagnostic}` : "";
|
|
2124
2303
|
let detailsUrl = input.workflowUrl;
|
|
2125
2304
|
if (input.planOk) {
|
|
2126
2305
|
const recapUrl = canonicalRecapUrl(input.planUrl, input.appUrl);
|
|
@@ -2163,9 +2342,9 @@ export function recapCheckOutcome(input) {
|
|
|
2163
2342
|
summary = `No recap was published because ${reason}.`;
|
|
2164
2343
|
text = "";
|
|
2165
2344
|
}
|
|
2166
|
-
else if (
|
|
2345
|
+
else if (diagnostic) {
|
|
2167
2346
|
summary =
|
|
2168
|
-
"The visual recap agent ran but did not produce a plan URL. See
|
|
2347
|
+
"The visual recap agent ran but did not produce a plan URL. See diagnostics below.";
|
|
2169
2348
|
}
|
|
2170
2349
|
return { conclusion, title, summary, text, detailsUrl };
|
|
2171
2350
|
}
|
|
@@ -2229,15 +2408,39 @@ async function runCheckComplete(args) {
|
|
|
2229
2408
|
process.env.GITHUB_TOKEN ||
|
|
2230
2409
|
"";
|
|
2231
2410
|
const checkRunId = optionalArg(args, "check-run-id") ?? "";
|
|
2411
|
+
const planOk = boolFlag(args, "plan-ok");
|
|
2412
|
+
const huge = boolFlag(args, "huge");
|
|
2413
|
+
const tiny = boolFlag(args, "tiny");
|
|
2414
|
+
const suppressed = boolFlag(args, "suppressed");
|
|
2415
|
+
const appUrl = optionalArg(args, "app-url") ?? process.env.PLAN_RECAP_APP_URL ?? "";
|
|
2416
|
+
let failureSummary = optionalArg(args, "failure-summary") ?? "";
|
|
2417
|
+
let urlReason = optionalArg(args, "url-reason") ?? "";
|
|
2418
|
+
if (!planOk && !tiny && !suppressed) {
|
|
2419
|
+
if (!failureSummary) {
|
|
2420
|
+
failureSummary = summarizeLocalAgentFailure({
|
|
2421
|
+
agent: optionalArg(args, "agent") ??
|
|
2422
|
+
process.env.RECAP_AGENT ??
|
|
2423
|
+
process.env.VISUAL_RECAP_AGENT ??
|
|
2424
|
+
"",
|
|
2425
|
+
});
|
|
2426
|
+
}
|
|
2427
|
+
if (!urlReason) {
|
|
2428
|
+
urlReason = inferLocalRecapUrlFailureReason({ appUrl });
|
|
2429
|
+
}
|
|
2430
|
+
if (!failureSummary && !urlReason) {
|
|
2431
|
+
failureSummary = STALE_WORKFLOW_FAILURE_SUMMARY;
|
|
2432
|
+
}
|
|
2433
|
+
}
|
|
2232
2434
|
const outcome = recapCheckOutcome({
|
|
2233
|
-
planOk
|
|
2435
|
+
planOk,
|
|
2234
2436
|
planUrl: optionalArg(args, "plan-url") ?? "",
|
|
2235
|
-
appUrl
|
|
2236
|
-
huge
|
|
2237
|
-
tiny
|
|
2238
|
-
suppressed
|
|
2437
|
+
appUrl,
|
|
2438
|
+
huge,
|
|
2439
|
+
tiny,
|
|
2440
|
+
suppressed,
|
|
2239
2441
|
suppressedJson: optionalArg(args, "suppressed-json") ?? "",
|
|
2240
|
-
failureSummary
|
|
2442
|
+
failureSummary,
|
|
2443
|
+
urlReason,
|
|
2241
2444
|
workflowUrl: optionalArg(args, "workflow-url") ?? "",
|
|
2242
2445
|
});
|
|
2243
2446
|
try {
|
|
@@ -2456,6 +2659,8 @@ function writeGitHubOutput(name, value) {
|
|
|
2456
2659
|
function runAgentSummary(args) {
|
|
2457
2660
|
const agent = optionalArg(args, "agent") ?? "claude";
|
|
2458
2661
|
const resultFile = stringArg(args, "result-file");
|
|
2662
|
+
const stderrFile = optionalArg(args, "stderr-file");
|
|
2663
|
+
const exitCodeFile = optionalArg(args, "exit-code-file");
|
|
2459
2664
|
let raw = "";
|
|
2460
2665
|
try {
|
|
2461
2666
|
raw = fs.readFileSync(path.resolve(resultFile), "utf8");
|
|
@@ -2463,7 +2668,18 @@ function runAgentSummary(args) {
|
|
|
2463
2668
|
catch (err) {
|
|
2464
2669
|
raw = `could not read ${resultFile}: ${String(err)}`;
|
|
2465
2670
|
}
|
|
2466
|
-
const
|
|
2671
|
+
const stderrText = stderrFile
|
|
2672
|
+
? (readTextIfExists(path.resolve(stderrFile)) ?? "")
|
|
2673
|
+
: "";
|
|
2674
|
+
const exitCode = exitCodeFile
|
|
2675
|
+
? (readTextIfExists(path.resolve(exitCodeFile)) ?? "")
|
|
2676
|
+
: "";
|
|
2677
|
+
const summary = summarizeAgentRun({
|
|
2678
|
+
agent,
|
|
2679
|
+
resultText: raw,
|
|
2680
|
+
stderrText,
|
|
2681
|
+
exitCode,
|
|
2682
|
+
});
|
|
2467
2683
|
writeGitHubOutput("summary", summary);
|
|
2468
2684
|
process.stdout.write(`${JSON.stringify({ ok: Boolean(summary), summary })}\n`);
|
|
2469
2685
|
}
|
|
@@ -2478,14 +2694,14 @@ Usage:
|
|
|
2478
2694
|
npx @agent-native/core@latest recap build-prompt --pr <n> [--repo owner/name] [--head <sha>] [--app-url <url>] [--diff <path>] [--stat <path>] [--prev-plan-id <id>] [--huge] [--local-files] [--local-dir <folder>] [--skill-source auto|latest|repo] [--out <path>]
|
|
2479
2695
|
npx @agent-native/core@latest recap shot --url <planUrl> [--token <planToken>] [--app-url <url>] [--out recap.png]
|
|
2480
2696
|
npx @agent-native/core@latest recap usage --plan-url <planUrl> --result-file <path> --app-url <url> --token <planToken> [--agent claude|codex] [--model <id>]
|
|
2481
|
-
npx @agent-native/core@latest recap agent-summary --result-file <path> [--agent claude|codex]
|
|
2697
|
+
npx @agent-native/core@latest recap agent-summary --result-file <path> [--stderr-file <path>] [--exit-code-file <path>] [--agent claude|codex]
|
|
2482
2698
|
npx @agent-native/core@latest recap comment <find-plan-id|upsert> --repo owner/name --issue <n> --token <github-token>
|
|
2483
2699
|
npx @agent-native/core@latest recap check start [--repo owner/name] [--sha <headSha>] [--token <github-token>] [--workflow-url <url>]
|
|
2484
2700
|
Create the in-progress "Visual Recap" GitHub check run and write its id to
|
|
2485
2701
|
$GITHUB_OUTPUT (check_run_id). repo/sha/token default to GITHUB_REPOSITORY /
|
|
2486
2702
|
HEAD_SHA / GH_TOKEN (or GITHUB_TOKEN). Best-effort: warns and exits 0 on any
|
|
2487
2703
|
API error without emitting an id.
|
|
2488
|
-
npx @agent-native/core@latest recap check complete --check-run-id <id> [--repo owner/name] [--token <github-token>] [--plan-ok <bool>] [--plan-url <url>] [--app-url <url>] [--suppressed <bool>] [--suppressed-json <json>] [--huge <bool>] [--tiny <bool>] [--workflow-url <url>]
|
|
2704
|
+
npx @agent-native/core@latest recap check complete --check-run-id <id> [--repo owner/name] [--token <github-token>] [--plan-ok <bool>] [--plan-url <url>] [--app-url <url>] [--suppressed <bool>] [--suppressed-json <json>] [--huge <bool>] [--tiny <bool>] [--failure-summary <text>] [--url-reason <text>] [--workflow-url <url>]
|
|
2489
2705
|
Mark the "Visual Recap" check run completed with a computed
|
|
2490
2706
|
conclusion/title/summary/text/details_url (success when the agent published a
|
|
2491
2707
|
plan whose URL validates against --app-url; neutral/skipped otherwise).
|