@fitlab-ai/agent-infra 0.9.12 → 0.9.13
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/README.md +11 -11
- package/README.zh-CN.md +11 -11
- package/dist/bin/internal-cli.js +24 -19
- package/dist/lib/agent-clients/next-steps.js +6 -1
- package/dist/lib/decide.js +3 -3
- package/dist/lib/internal/platform-checks.js +3 -3
- package/dist/lib/internal/platform-comment.js +9 -9
- package/dist/lib/internal/platform-context.js +2 -2
- package/dist/lib/internal/platform-issue.js +8 -9
- package/dist/lib/internal/platform-pr-review.js +10 -10
- package/dist/lib/internal/platform-pr.js +63 -24
- package/dist/lib/internal/platform-release-notes.js +3 -3
- package/dist/lib/internal/pr-review-grade.js +11 -9
- package/dist/lib/internal/release-workflow.js +4 -4
- package/dist/lib/internal/sandbox-control.js +30 -4
- package/dist/lib/internal/task-activity.js +3 -3
- package/dist/lib/internal/task-artifact.js +76 -24
- package/dist/lib/internal/task-create.js +2 -2
- package/dist/lib/internal/task-event.js +6 -3
- package/dist/lib/internal/task-finalization.js +2 -2
- package/dist/lib/internal/task-invalidation.js +60 -0
- package/dist/lib/internal/task-ledger.js +9 -6
- package/dist/lib/internal/task-lifecycle.js +2 -2
- package/dist/lib/internal/task-override.js +4 -4
- package/dist/lib/internal/task-review.js +3 -3
- package/dist/lib/internal/task-verify.js +6 -6
- package/dist/lib/internal/task-warning.js +3 -3
- package/dist/lib/platform/adapters.js +1 -3
- package/dist/lib/platform/change-request-git-evidence.js +27 -7
- package/dist/lib/platform/completion-backfill.js +6 -5
- package/dist/lib/platform/context.js +70 -130
- package/dist/lib/platform/github-client.js +1 -1
- package/dist/lib/platform/github-provider.js +903 -0
- package/dist/lib/platform/in-label-sync.js +143 -0
- package/dist/lib/platform/issue-comments.js +94 -41
- package/dist/lib/platform/issues.js +335 -189
- package/dist/lib/platform/metadata-labels.js +6 -2
- package/dist/lib/platform/none-provider.js +28 -0
- package/dist/lib/platform/pr-change-report.js +551 -0
- package/dist/lib/platform/pr-checks.js +119 -104
- package/dist/lib/platform/pr-review.js +88 -72
- package/dist/lib/platform/pr-summary.js +520 -50
- package/dist/lib/platform/provider-bridge.js +45 -0
- package/dist/lib/platform/provider-contract.d.ts +487 -0
- package/dist/lib/platform/provider-contract.js +95 -0
- package/dist/lib/platform/provider-loader.js +251 -0
- package/dist/lib/platform/provider-validation.js +504 -0
- package/dist/lib/platform/pull-request-metadata.js +8 -6
- package/dist/lib/platform/pull-requests.js +783 -537
- package/dist/lib/platform/release-notes.js +78 -55
- package/dist/lib/platform/releases.js +79 -50
- package/dist/lib/platform/resource-identity.d.ts +26 -0
- package/dist/lib/platform/resource-identity.js +92 -0
- package/dist/lib/platform/task-identities.js +31 -0
- package/dist/lib/platform/verification-required.js +11 -12
- package/dist/lib/platform/verification-sync.js +112 -238
- package/dist/lib/pr-review/evidence-grading.js +19 -6
- package/dist/lib/run/index.js +63 -23
- package/dist/lib/sandbox/control/client.js +96 -15
- package/dist/lib/sandbox/control/executor.js +3 -3
- package/dist/lib/sandbox/control/protocol.js +77 -0
- package/dist/lib/sandbox/control/server.js +398 -25
- package/dist/lib/sandbox/control/state.js +204 -1
- package/dist/lib/server/protocol.js +38 -1
- package/dist/lib/task/activity-log.js +8 -1
- package/dist/lib/task/artifact-lifecycle.js +33 -5
- package/dist/lib/task/capabilities.js +239 -0
- package/dist/lib/task/commands/status.js +32 -0
- package/dist/lib/task/commit-operation.js +12 -1
- package/dist/lib/task/control-authority.js +8 -5
- package/dist/lib/task/create-service.js +8 -8
- package/dist/lib/task/current-contract.js +5 -1
- package/dist/lib/task/decision-details.js +1 -1
- package/dist/lib/task/events.js +297 -17
- package/dist/lib/task/finalization.js +37 -13
- package/dist/lib/task/human-override.js +58 -36
- package/dist/lib/task/invalidation-command.js +86 -0
- package/dist/lib/task/invalidation.js +260 -0
- package/dist/lib/task/ledger-intents.js +37 -0
- package/dist/lib/task/local-artifact-finalization.js +374 -0
- package/dist/lib/task/orchestration.js +49 -93
- package/dist/lib/task/pr-delivery-fact.js +102 -101
- package/dist/lib/task/review-fingerprint.js +24 -2
- package/dist/lib/task/rework-intent.js +138 -0
- package/dist/lib/task/skill-scope.js +23 -0
- package/dist/lib/task/task-execution-lock.js +27 -17
- package/dist/lib/task/verification-engine.js +43 -16
- package/dist/lib/task/verification.js +3 -3
- package/dist/lib/task/workflow-warning-intents.js +1 -1
- package/dist/lib/task/write.js +11 -0
- package/dist/lib/version.js +11 -1
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-clients/next-steps.ts +6 -1
- package/lib/decide.ts +3 -3
- package/lib/init.ts +1 -1
- package/lib/internal/platform-checks.ts +3 -3
- package/lib/internal/platform-comment.ts +8 -8
- package/lib/internal/platform-context.ts +2 -2
- package/lib/internal/platform-issue.ts +7 -8
- package/lib/internal/platform-pr-review.ts +9 -9
- package/lib/internal/platform-pr.ts +47 -22
- package/lib/internal/platform-release-notes.ts +3 -3
- package/lib/internal/pr-review-grade.ts +10 -8
- package/lib/internal/release-workflow.ts +4 -4
- package/lib/internal/sandbox-control.ts +27 -3
- package/lib/internal/task-activity.ts +3 -3
- package/lib/internal/task-artifact.ts +65 -22
- package/lib/internal/task-create.ts +2 -2
- package/lib/internal/task-event.ts +6 -3
- package/lib/internal/task-finalization.ts +3 -3
- package/lib/internal/task-invalidation.ts +39 -0
- package/lib/internal/task-ledger.ts +11 -8
- package/lib/internal/task-lifecycle.ts +2 -2
- package/lib/internal/task-override.ts +6 -6
- package/lib/internal/task-review.ts +3 -3
- package/lib/internal/task-verify.ts +6 -6
- package/lib/internal/task-warning.ts +3 -3
- package/lib/platform/adapters.ts +3 -2
- package/lib/platform/change-request-git-evidence.ts +36 -9
- package/lib/platform/completion-backfill.ts +9 -8
- package/lib/platform/context.ts +97 -134
- package/lib/platform/github-client.ts +2 -2
- package/lib/platform/github-provider.ts +910 -0
- package/lib/platform/in-label-sync.ts +208 -0
- package/lib/platform/issue-comments.ts +97 -47
- package/lib/platform/issues.ts +344 -174
- package/lib/platform/metadata-labels.ts +5 -2
- package/lib/platform/none-provider.ts +35 -0
- package/lib/platform/pr-change-report.ts +645 -0
- package/lib/platform/pr-checks.ts +126 -106
- package/lib/platform/pr-review.ts +88 -85
- package/lib/platform/pr-summary.ts +560 -53
- package/lib/platform/provider-bridge.ts +82 -0
- package/lib/platform/provider-contract.ts +536 -0
- package/lib/platform/provider-loader.ts +307 -0
- package/lib/platform/provider-validation.ts +529 -0
- package/lib/platform/pull-request-metadata.ts +10 -7
- package/lib/platform/pull-requests.ts +826 -543
- package/lib/platform/release-notes.ts +76 -59
- package/lib/platform/releases.ts +77 -43
- package/lib/platform/resource-identity.ts +121 -0
- package/lib/platform/task-identities.ts +40 -0
- package/lib/platform/types.ts +4 -2
- package/lib/platform/verification-required.ts +11 -13
- package/lib/platform/verification-sync.ts +112 -292
- package/lib/pr-review/evidence-grading.ts +25 -8
- package/lib/run/index.ts +57 -22
- package/lib/sandbox/control/client.ts +105 -15
- package/lib/sandbox/control/executor.ts +4 -4
- package/lib/sandbox/control/protocol.ts +113 -0
- package/lib/sandbox/control/server.ts +431 -23
- package/lib/sandbox/control/state.ts +251 -1
- package/lib/server/protocol.ts +36 -1
- package/lib/task/activity-log.ts +8 -0
- package/lib/task/artifact-lifecycle.ts +32 -6
- package/lib/task/capabilities.ts +262 -0
- package/lib/task/commands/status.ts +38 -0
- package/lib/task/commit-operation.ts +12 -1
- package/lib/task/control-authority.ts +11 -8
- package/lib/task/create-service.ts +8 -8
- package/lib/task/current-contract.ts +6 -1
- package/lib/task/decision-details.ts +6 -2
- package/lib/task/events.ts +296 -16
- package/lib/task/finalization.ts +50 -14
- package/lib/task/human-override.ts +70 -46
- package/lib/task/invalidation-command.ts +86 -0
- package/lib/task/invalidation.ts +332 -0
- package/lib/task/ledger-intents.ts +35 -1
- package/lib/task/local-artifact-finalization.ts +563 -0
- package/lib/task/orchestration.ts +52 -85
- package/lib/task/pr-delivery-fact.ts +131 -148
- package/lib/task/review-fingerprint.ts +23 -2
- package/lib/task/rework-intent.ts +165 -0
- package/lib/task/skill-scope.ts +24 -0
- package/lib/task/task-execution-lock.ts +23 -14
- package/lib/task/verification-engine.ts +52 -17
- package/lib/task/verification.ts +4 -4
- package/lib/task/workflow-warning-intents.ts +1 -1
- package/lib/task/write.ts +19 -0
- package/lib/update.ts +1 -1
- package/lib/version.ts +15 -1
- package/package.json +7 -1
- package/templates/.agents/rules/README.en.md +1 -1
- package/templates/.agents/rules/README.zh-CN.md +1 -1
- package/templates/.agents/rules/issue-pr-commands.en.md +1 -1
- package/templates/.agents/rules/issue-pr-commands.zh-CN.md +1 -1
- package/templates/.agents/rules/issue-sync.en.md +2 -0
- package/templates/.agents/rules/issue-sync.zh-CN.md +2 -0
- package/templates/.agents/rules/local-artifact-repair.en.md +9 -2
- package/templates/.agents/rules/local-artifact-repair.zh-CN.md +9 -2
- package/templates/.agents/rules/pr-sync.en.md +10 -4
- package/templates/.agents/rules/pr-sync.zh-CN.md +17 -4
- package/templates/.agents/rules/task-short-id.en.md +1 -4
- package/templates/.agents/rules/task-short-id.zh-CN.md +1 -3
- package/templates/.agents/skills/analyze-task/SKILL.en.md +13 -3
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +10 -3
- package/templates/.agents/skills/check-task/SKILL.en.md +16 -37
- package/templates/.agents/skills/check-task/SKILL.zh-CN.md +16 -37
- package/templates/.agents/skills/code-task/SKILL.en.md +27 -8
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +29 -10
- package/templates/.agents/skills/code-task/config/verify.en.json +1 -0
- package/templates/.agents/skills/code-task/config/verify.zh-CN.json +1 -0
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +2 -0
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +2 -0
- package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +3 -3
- package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +3 -3
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +5 -3
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +5 -3
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +4 -3
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +4 -3
- package/templates/.agents/skills/complete-task/SKILL.en.md +21 -3
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +20 -3
- package/templates/.agents/skills/create-pr/SKILL.en.md +7 -6
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +7 -7
- package/templates/.agents/skills/create-pr/config/verify.json +1 -0
- package/templates/.agents/skills/create-pr/reference/change-report.en.md +16 -4
- package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +16 -4
- package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +5 -3
- package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +5 -3
- package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +1 -1
- package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +1 -1
- package/templates/.agents/skills/create-pr/scripts/change-report.mjs +3 -0
- package/templates/.agents/skills/plan-task/SKILL.en.md +13 -3
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +10 -3
- package/templates/.agents/skills/review-analysis/SKILL.en.md +3 -3
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/review-code/SKILL.en.md +4 -4
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/review-plan/SKILL.en.md +3 -3
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +4 -2
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +4 -2
- package/templates/.agents/skills/watch-pr/SKILL.en.md +2 -2
- package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +6 -0
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +6 -0
- package/templates/.agents/templates/task.en.md +18 -1
- package/templates/.agents/templates/task.zh-CN.md +18 -1
- package/templates/.github/workflows/pr-label.yml +7 -26
|
@@ -2,14 +2,16 @@ import fs from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import process from "node:process";
|
|
4
4
|
import { execFileSync } from "node:child_process";
|
|
5
|
-
import {
|
|
6
|
-
import { resolvePlatformContext } from "./context.js";
|
|
5
|
+
import { resolvePlatformProviderContext } from "./context.js";
|
|
7
6
|
import { hasCheckedRequirement, resolveRequirementSection } from "./issue-metadata.js";
|
|
8
|
-
import {
|
|
9
|
-
import { listRemoteComments } from "./issue-comments.js";
|
|
7
|
+
import { requirementSectionAnchors } from "./issues.js";
|
|
10
8
|
import { taskTypeLabel } from "./metadata-labels.js";
|
|
9
|
+
import { planInLabelUpdate, validateInLabelMapping, validateRepositoryLabelPayload } from "./in-label-sync.js";
|
|
11
10
|
import { inspectGitHubPullRequest } from "./pull-requests.js";
|
|
11
|
+
import { createGitHubClient } from "./github-client.js";
|
|
12
12
|
import { readPrDeliveryFact } from "../task/pr-delivery-fact.js";
|
|
13
|
+
import { providerError, providerOperationContext, resourceIdentityNumber, unsupportedProviderOperation } from "./provider-bridge.js";
|
|
14
|
+
import { taskIssueIdentity } from "./task-identities.js";
|
|
13
15
|
const CHECK_TYPE = "platform-sync";
|
|
14
16
|
const VERSION_LINE_REGEX = /^[0-9]+\.[0-9]+\.x$/;
|
|
15
17
|
const FRONTMATTER_FIELD_MAP = {
|
|
@@ -26,7 +28,6 @@ const OPTION_LOCALIZATION = {
|
|
|
26
28
|
};
|
|
27
29
|
let activeShared = null;
|
|
28
30
|
let repoRoot = "";
|
|
29
|
-
const githubClient = createGitHubClient();
|
|
30
31
|
export function getDefaults() {
|
|
31
32
|
return {
|
|
32
33
|
statusLabels: {
|
|
@@ -79,20 +80,14 @@ function blockedResult(...args) {
|
|
|
79
80
|
function safeStat(...args) {
|
|
80
81
|
return getShared().safeStat(...args);
|
|
81
82
|
}
|
|
82
|
-
function
|
|
83
|
-
return getShared().parseIssueNumber(...args);
|
|
84
|
-
}
|
|
85
|
-
function parsePrNumber(...args) {
|
|
86
|
-
return getShared().parsePrNumber(...args);
|
|
87
|
-
}
|
|
88
|
-
export function check({ taskDir, config, artifactFile }, shared) {
|
|
83
|
+
export async function check({ taskDir, config, artifactFile }, shared) {
|
|
89
84
|
activeShared = shared;
|
|
90
85
|
repoRoot = shared.repoRoot;
|
|
91
|
-
const context = buildSyncContext({ taskDir, config, artifactFile });
|
|
86
|
+
const context = await buildSyncContext({ taskDir, config, artifactFile });
|
|
92
87
|
if (context.earlyReturn) {
|
|
93
88
|
return context.earlyReturn;
|
|
94
89
|
}
|
|
95
|
-
const remoteData = fetchRemoteData(context);
|
|
90
|
+
const remoteData = await fetchRemoteData(context);
|
|
96
91
|
if (remoteData.earlyReturn) {
|
|
97
92
|
return remoteData.earlyReturn;
|
|
98
93
|
}
|
|
@@ -120,29 +115,32 @@ export function check({ taskDir, config, artifactFile }, shared) {
|
|
|
120
115
|
return result;
|
|
121
116
|
}
|
|
122
117
|
}
|
|
123
|
-
return passResult(CHECK_TYPE, `
|
|
118
|
+
return passResult(CHECK_TYPE, `Platform sync checks passed for Issue ${context.issueNumber || "identity"}`);
|
|
124
119
|
}
|
|
125
|
-
function buildSyncContext({ taskDir, config, artifactFile }) {
|
|
120
|
+
async function buildSyncContext({ taskDir, config, artifactFile }) {
|
|
126
121
|
const task = loadTask(taskDir);
|
|
127
122
|
if (!task.ok) {
|
|
128
123
|
return { earlyReturn: failResult(CHECK_TYPE, task.message) };
|
|
129
124
|
}
|
|
130
|
-
const
|
|
125
|
+
const issueIdentity = taskIssueIdentity(task.metadata);
|
|
126
|
+
const issueNumber = resourceIdentityNumber(issueIdentity);
|
|
131
127
|
const fact = readPrDeliveryFact(task.metadata);
|
|
132
128
|
if (fact.status === "invalid") {
|
|
133
129
|
return { earlyReturn: failResult(CHECK_TYPE, fact.error.message, "check_failed") };
|
|
134
130
|
}
|
|
135
|
-
const
|
|
136
|
-
|
|
131
|
+
const prIdentity = fact.status === "valid" && fact.fact.state === "bound" ? fact.fact.identity.resource : null;
|
|
132
|
+
const prNumber = resourceIdentityNumber(prIdentity);
|
|
133
|
+
if (config.when === "issue_number_exists" && !issueIdentity) {
|
|
137
134
|
return { earlyReturn: passResult(CHECK_TYPE, "Skipped: task has no issue_number") };
|
|
138
135
|
}
|
|
139
|
-
if (config.when === "pr_fact_bound" && !
|
|
136
|
+
if (config.when === "pr_fact_bound" && !prIdentity) {
|
|
140
137
|
return { earlyReturn: passResult(CHECK_TYPE, "Skipped: task has no verified bound pull request") };
|
|
141
138
|
}
|
|
142
|
-
if (!
|
|
139
|
+
if (!issueIdentity) {
|
|
143
140
|
return { earlyReturn: passResult(CHECK_TYPE, "Skipped: platform-sync not required for this task") };
|
|
144
141
|
}
|
|
145
|
-
const
|
|
142
|
+
const loaded = await resolvePlatformProviderContext({ cwd: repoRoot });
|
|
143
|
+
const platformContext = loaded.ok ? loaded.value.context : loaded.context;
|
|
146
144
|
if (platformContext.status === "failed") {
|
|
147
145
|
return { earlyReturn: failResult(CHECK_TYPE, platformContext.error?.message || "Platform context failed", "check_failed") };
|
|
148
146
|
}
|
|
@@ -155,7 +153,6 @@ function buildSyncContext({ taskDir, config, artifactFile }) {
|
|
|
155
153
|
}
|
|
156
154
|
return { earlyReturn: passResult(CHECK_TYPE, `Skipped: ${platformContext.error?.message || "platform unavailable"}`) };
|
|
157
155
|
}
|
|
158
|
-
const repoOwnerType = detectRepoOwnerType(platformContext.platform.repository, taskDir);
|
|
159
156
|
const expectedValues = resolveExpectedValues(config);
|
|
160
157
|
if (!expectedValues.ok) {
|
|
161
158
|
return { earlyReturn: failResult(CHECK_TYPE, expectedValues.message, "check_failed") };
|
|
@@ -173,15 +170,20 @@ function buildSyncContext({ taskDir, config, artifactFile }) {
|
|
|
173
170
|
config,
|
|
174
171
|
artifactFile,
|
|
175
172
|
artifactPath,
|
|
173
|
+
issueIdentity,
|
|
174
|
+
prIdentity,
|
|
176
175
|
issueNumber,
|
|
177
176
|
prNumber,
|
|
178
177
|
upstreamRepo: platformContext.platform.repository,
|
|
179
|
-
repoOwnerType,
|
|
178
|
+
repoOwnerType: String(loaded.ok ? loaded.value.snapshot.metadata?.ownerType || "unknown" : "unknown"),
|
|
180
179
|
hasTriage: platformContext.capabilities.triage,
|
|
181
180
|
hasPush: platformContext.capabilities.push,
|
|
182
181
|
expectedStatusLabel: expectedValues.statusLabel,
|
|
183
182
|
marker,
|
|
184
|
-
prMarker
|
|
183
|
+
prMarker,
|
|
184
|
+
provider: loaded.ok ? loaded.value.provider : null,
|
|
185
|
+
providerType: loaded.ok ? loaded.value.providerType : null,
|
|
186
|
+
loadedContext: loaded.ok ? loaded.value : null
|
|
185
187
|
};
|
|
186
188
|
}
|
|
187
189
|
function resolveExpectedValues(config) {
|
|
@@ -230,139 +232,69 @@ function resolveDefaultValue({ collection, key, value, configKey }) {
|
|
|
230
232
|
}
|
|
231
233
|
return { ok: true, value: resolvedValue };
|
|
232
234
|
}
|
|
233
|
-
function fetchRemoteData(context) {
|
|
234
|
-
const
|
|
235
|
-
const
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
}
|
|
245
|
-
:
|
|
246
|
-
|
|
247
|
-
type: sharedIssue.error.retryable ? "network_error" : "check_failed",
|
|
248
|
-
message: sharedIssue.error.message
|
|
249
|
-
};
|
|
250
|
-
if (!issueResult.ok) {
|
|
251
|
-
return {
|
|
252
|
-
earlyReturn: issueResult.type === "check_failed"
|
|
253
|
-
? failResult(CHECK_TYPE, issueResult.message, issueResult.type)
|
|
254
|
-
: blockedResult(CHECK_TYPE, issueResult.message, issueResult.type)
|
|
255
|
-
};
|
|
256
|
-
}
|
|
257
|
-
const issue = issueResult.value;
|
|
258
|
-
if (context.config.issue_must_exist !== false && !issue) {
|
|
235
|
+
async function fetchRemoteData(context) {
|
|
236
|
+
const provider = context.provider;
|
|
237
|
+
const operationContext = providerOperationContext(context.loadedContext, context.taskDir);
|
|
238
|
+
const facts = provider?.verification?.fetchRemoteFacts
|
|
239
|
+
? await provider.verification.fetchRemoteFacts({
|
|
240
|
+
context: operationContext,
|
|
241
|
+
taskId: context.task?.metadata?.id || context.task?.id || "",
|
|
242
|
+
...(context.issueIdentity ? { issue: context.issueIdentity } : {}),
|
|
243
|
+
...(context.prIdentity ? { changeRequest: context.prIdentity } : {}),
|
|
244
|
+
includeComments: shouldFetchComments(context.config),
|
|
245
|
+
includeFields: Boolean(context.config.verify_issue_fields)
|
|
246
|
+
})
|
|
247
|
+
: unsupportedProviderOperation(provider, "verification.fetchRemoteFacts");
|
|
248
|
+
if (!facts.ok) {
|
|
259
249
|
return {
|
|
260
|
-
earlyReturn:
|
|
250
|
+
earlyReturn: facts.error.retryable
|
|
251
|
+
? blockedResult(CHECK_TYPE, providerError(facts.error, "PLATFORM_PROVIDER_OPERATION_FAILED").message, "network_error")
|
|
252
|
+
: failResult(CHECK_TYPE, providerError(facts.error, "PLATFORM_PROVIDER_OPERATION_FAILED").message, "check_failed")
|
|
261
253
|
};
|
|
262
254
|
}
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
], context.taskDir));
|
|
271
|
-
if (!commentsResult.ok) {
|
|
272
|
-
return {
|
|
273
|
-
earlyReturn: commentsResult.type === "check_failed"
|
|
274
|
-
? failResult(CHECK_TYPE, commentsResult.message, commentsResult.type)
|
|
275
|
-
: blockedResult(CHECK_TYPE, commentsResult.message, commentsResult.type)
|
|
276
|
-
};
|
|
277
|
-
}
|
|
278
|
-
comments = flattenComments(commentsResult.value);
|
|
279
|
-
}
|
|
280
|
-
let prComments = null;
|
|
281
|
-
if (context.prMarker) {
|
|
282
|
-
if (!context.prNumber) {
|
|
283
|
-
return {
|
|
284
|
-
earlyReturn: failResult(CHECK_TYPE, "Expected a verified bound pull request for PR comment verification", "check_failed")
|
|
285
|
-
};
|
|
286
|
-
}
|
|
287
|
-
const prCommentsResult = listRemoteComments(githubClient, context.upstreamRepo, context.prNumber, context.taskDir);
|
|
288
|
-
if (!prCommentsResult.ok) {
|
|
289
|
-
return {
|
|
290
|
-
earlyReturn: prCommentsResult.error.retryable
|
|
291
|
-
? blockedResult(CHECK_TYPE, prCommentsResult.error.message, "network_error")
|
|
292
|
-
: failResult(CHECK_TYPE, prCommentsResult.error.message, "check_failed")
|
|
293
|
-
};
|
|
294
|
-
}
|
|
295
|
-
prComments = prCommentsResult.value;
|
|
296
|
-
}
|
|
297
|
-
let issueType;
|
|
298
|
-
if (context.config.verify_issue_type && context.hasPush) {
|
|
299
|
-
const issueTypeResult = withRetry(() => ghText([
|
|
300
|
-
"api",
|
|
301
|
-
`repos/${context.upstreamRepo}/issues/${context.issueNumber}`,
|
|
302
|
-
"--jq",
|
|
303
|
-
".type.name // empty"
|
|
304
|
-
], context.taskDir));
|
|
305
|
-
if (issueTypeResult.ok) {
|
|
306
|
-
issueType = issueTypeResult.value || null;
|
|
255
|
+
const issueSnapshot = facts.value.issue;
|
|
256
|
+
const issue = issueSnapshot
|
|
257
|
+
? {
|
|
258
|
+
state: issueSnapshot.state.toUpperCase(),
|
|
259
|
+
labels: issueSnapshot.labels.map((name) => ({ name })),
|
|
260
|
+
body: issueSnapshot.body,
|
|
261
|
+
milestone: issueSnapshot.milestone ? { title: issueSnapshot.milestone } : null
|
|
307
262
|
}
|
|
308
|
-
|
|
263
|
+
: null;
|
|
309
264
|
let issueFields;
|
|
310
|
-
if (context.config.verify_issue_fields &&
|
|
311
|
-
const
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
"
|
|
315
|
-
|
|
316
|
-
`query=${ISSUE_FIELDS_QUERY}`,
|
|
317
|
-
"-F",
|
|
318
|
-
`owner=${owner}`,
|
|
319
|
-
"-F",
|
|
320
|
-
`name=${name}`,
|
|
321
|
-
"-F",
|
|
322
|
-
`number=${context.issueNumber}`
|
|
323
|
-
], context.taskDir));
|
|
324
|
-
if (issueFieldsResult.ok) {
|
|
325
|
-
issueFields = normalizeIssueFields(issueFieldsResult.value);
|
|
326
|
-
}
|
|
265
|
+
if (context.config.verify_issue_fields && issueSnapshot?.issueType) {
|
|
266
|
+
const fieldKinds = new Map(issueSnapshot.issueType.fields.map((field) => [field.name, field.kind]));
|
|
267
|
+
issueFields = {
|
|
268
|
+
pinnedNames: new Set(issueSnapshot.issueType.fields.map((field) => field.name)),
|
|
269
|
+
values: new Map(Object.entries(issueSnapshot.fields).map(([name, value]) => [name, { kind: fieldKinds.get(name) || (typeof value === "number" ? "number" : "single-select"), value }]))
|
|
270
|
+
};
|
|
327
271
|
}
|
|
328
|
-
let
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
if (((context.config.verify_in_labels_match_pr && context.hasTriage)
|
|
333
|
-
|| (context.config.verify_pr_type_label && context.hasTriage)
|
|
334
|
-
|| (context.config.verify_milestone && context.hasTriage)
|
|
335
|
-
|| (context.config.verify_pr_assignee && context.hasPush)
|
|
336
|
-
|| context.config.verify_pr_comment_last_commit_matches_head) && context.prNumber) {
|
|
337
|
-
const prResult = inspectGitHubPullRequest(githubClient, context.upstreamRepo, context.prNumber, context.taskDir);
|
|
338
|
-
if (!prResult.ok) {
|
|
272
|
+
let prComments = null;
|
|
273
|
+
if (context.prMarker && context.prIdentity && shouldFetchComments(context.config) && provider?.comments?.list) {
|
|
274
|
+
const listed = await provider.comments.list({ context: operationContext, parent: context.prIdentity });
|
|
275
|
+
if (!listed.ok)
|
|
339
276
|
return {
|
|
340
|
-
earlyReturn:
|
|
341
|
-
? blockedResult(CHECK_TYPE,
|
|
342
|
-
: failResult(CHECK_TYPE,
|
|
277
|
+
earlyReturn: listed.error.retryable
|
|
278
|
+
? blockedResult(CHECK_TYPE, listed.error.message, "network_error")
|
|
279
|
+
: failResult(CHECK_TYPE, listed.error.message, "check_failed")
|
|
343
280
|
};
|
|
344
|
-
}
|
|
345
|
-
prLabels = (context.config.verify_in_labels_match_pr || context.config.verify_pr_type_label)
|
|
346
|
-
? prResult.value.labels
|
|
347
|
-
: null;
|
|
348
|
-
prMilestone = context.config.verify_milestone
|
|
349
|
-
? prResult.value.milestone ? { title: prResult.value.milestone } : null
|
|
350
|
-
: undefined;
|
|
351
|
-
prAssignees = context.config.verify_pr_assignee
|
|
352
|
-
? prResult.value.assignees
|
|
353
|
-
: undefined;
|
|
354
|
-
prHeadSha = prResult.value.head.sha;
|
|
281
|
+
prComments = listed.value.map((comment) => ({ id: comment.id, body: comment.body }));
|
|
355
282
|
}
|
|
283
|
+
const changeRequest = facts.value.changeRequest;
|
|
356
284
|
return {
|
|
357
285
|
issue,
|
|
358
|
-
comments,
|
|
286
|
+
comments: facts.value.comments.map((comment) => ({ id: comment.id, body: comment.body })),
|
|
359
287
|
prComments,
|
|
360
|
-
prLabels,
|
|
361
|
-
issueType
|
|
288
|
+
prLabels: changeRequest?.labels || null,
|
|
289
|
+
issueType: issueSnapshot
|
|
290
|
+
? (issueSnapshot.issueType ? issueSnapshot.issueType.name : null)
|
|
291
|
+
: undefined,
|
|
362
292
|
issueFields,
|
|
363
|
-
prMilestone
|
|
364
|
-
|
|
365
|
-
|
|
293
|
+
prMilestone: changeRequest
|
|
294
|
+
? (changeRequest.milestone ? { title: changeRequest.milestone } : null)
|
|
295
|
+
: undefined,
|
|
296
|
+
prAssignees: changeRequest?.assignees,
|
|
297
|
+
prHeadSha: changeRequest?.headSha
|
|
366
298
|
};
|
|
367
299
|
}
|
|
368
300
|
function mapTaskTypeToLabel(taskType) {
|
|
@@ -377,12 +309,6 @@ function shouldFetchComments(config) {
|
|
|
377
309
|
|| config.verify_comment_content
|
|
378
310
|
|| config.verify_task_comment_content);
|
|
379
311
|
}
|
|
380
|
-
function flattenComments(value) {
|
|
381
|
-
if (!Array.isArray(value)) {
|
|
382
|
-
return [];
|
|
383
|
-
}
|
|
384
|
-
return value.flatMap((page) => Array.isArray(page) ? page : []);
|
|
385
|
-
}
|
|
386
312
|
function checkStatusLabel(context, remoteData) {
|
|
387
313
|
if (!context.expectedStatusLabel || !context.hasTriage) {
|
|
388
314
|
return null;
|
|
@@ -541,7 +467,7 @@ function checkInLabelsComputed(context, remoteData) {
|
|
|
541
467
|
if (!context.config.verify_in_labels_computed || !context.hasTriage) {
|
|
542
468
|
return null;
|
|
543
469
|
}
|
|
544
|
-
const expectedInLabels = computeExpectedInLabels(context.taskDir);
|
|
470
|
+
const expectedInLabels = computeExpectedInLabels(context.taskDir, context.upstreamRepo);
|
|
545
471
|
if (!expectedInLabels.ok) {
|
|
546
472
|
return expectedInLabels.type === "check_failed"
|
|
547
473
|
? failResult(CHECK_TYPE, expectedInLabels.message, expectedInLabels.type)
|
|
@@ -781,7 +707,6 @@ function mapTaskTypeToIssueType(taskType) {
|
|
|
781
707
|
};
|
|
782
708
|
return mapping[taskType] || "Task";
|
|
783
709
|
}
|
|
784
|
-
const ISSUE_FIELDS_QUERY = `query($owner:String!,$name:String!,$number:Int!){repository(owner:$owner,name:$name){issue(number:$number){issueType{name pinnedFields{__typename ... on IssueFieldSingleSelect{id name} ... on IssueFieldDate{id name} ... on IssueFieldText{id name} ... on IssueFieldNumber{id name}}} issueFieldValues(first:50){nodes{__typename ... on IssueFieldSingleSelectValue{name optionId field{... on IssueFieldSingleSelect{name}}} ... on IssueFieldDateValue{value field{... on IssueFieldDate{name}}} ... on IssueFieldTextValue{value field{... on IssueFieldText{name}}} ... on IssueFieldNumberValue{value field{... on IssueFieldNumber{name}}}}}}}}`;
|
|
785
710
|
function normalizeIssueFields(payload) {
|
|
786
711
|
const issue = payload?.data?.repository?.issue;
|
|
787
712
|
const pinnedFields = Array.isArray(issue?.issueType?.pinnedFields)
|
|
@@ -842,10 +767,18 @@ function arraysEqual(left, right) {
|
|
|
842
767
|
function formatLabelList(labels) {
|
|
843
768
|
return labels.length > 0 ? labels.join(", ") : "none";
|
|
844
769
|
}
|
|
845
|
-
function computeExpectedInLabels(taskDir) {
|
|
846
|
-
const
|
|
770
|
+
function computeExpectedInLabels(taskDir, repository) {
|
|
771
|
+
const task = loadTask(taskDir);
|
|
772
|
+
if (!task.ok) {
|
|
773
|
+
return task;
|
|
774
|
+
}
|
|
775
|
+
const baseRef = String(task.metadata?.delivery_base_ref || "").trim();
|
|
776
|
+
if (!baseRef) {
|
|
777
|
+
return { ok: false, type: "check_failed", message: "Task has no delivery_base_ref for in-label evidence" };
|
|
778
|
+
}
|
|
779
|
+
const changedFilesResult = gitText(["diff", `${baseRef}...HEAD`, "--name-only"], taskDir);
|
|
847
780
|
if (!changedFilesResult.ok) {
|
|
848
|
-
return changedFilesResult;
|
|
781
|
+
return { ...changedFilesResult, type: "network_error" };
|
|
849
782
|
}
|
|
850
783
|
const changedFiles = String(changedFilesResult.value || "")
|
|
851
784
|
.split(/\r?\n/)
|
|
@@ -855,45 +788,30 @@ function computeExpectedInLabels(taskDir) {
|
|
|
855
788
|
if (!mapping.ok) {
|
|
856
789
|
return mapping;
|
|
857
790
|
}
|
|
858
|
-
if (Object.keys(mapping.value ?? {}).length
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
}
|
|
866
|
-
}
|
|
867
|
-
return { ok: true, labels: Array.from(labels).sort(), mode: "mapped" };
|
|
868
|
-
}
|
|
869
|
-
const repoLabelsResult = withRetry(() => ghJson([
|
|
870
|
-
"label",
|
|
871
|
-
"list",
|
|
872
|
-
"--limit",
|
|
873
|
-
"200",
|
|
874
|
-
"--json",
|
|
875
|
-
"name"
|
|
876
|
-
], taskDir));
|
|
791
|
+
if (Object.keys(mapping.value ?? {}).length === 0) {
|
|
792
|
+
return { ok: true, labels: [], mode: "mapped" };
|
|
793
|
+
}
|
|
794
|
+
const client = createGitHubClient();
|
|
795
|
+
const repoLabelsResult = withRetry(() => client.json([
|
|
796
|
+
"api", "--paginate", "--slurp", `repos/${repository}/labels?per_page=100`
|
|
797
|
+
], { cwd: taskDir }));
|
|
877
798
|
if (!repoLabelsResult.ok) {
|
|
878
799
|
return repoLabelsResult;
|
|
879
800
|
}
|
|
880
|
-
const
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
return { ok: true, labels: [], mode: "fallback" };
|
|
801
|
+
const repositoryLabels = validateRepositoryLabelPayload(repoLabelsResult.value);
|
|
802
|
+
if (!repositoryLabels.ok) {
|
|
803
|
+
return { ok: false, type: "check_failed", message: repositoryLabels.error.message };
|
|
884
804
|
}
|
|
885
|
-
const
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
labels.add(candidate);
|
|
894
|
-
}
|
|
805
|
+
const planned = planInLabelUpdate({
|
|
806
|
+
changedFiles,
|
|
807
|
+
currentLabels: [],
|
|
808
|
+
mapping: mapping.value ?? {},
|
|
809
|
+
repositoryLabels: new Set(repositoryLabels.value)
|
|
810
|
+
});
|
|
811
|
+
if (planned.error) {
|
|
812
|
+
return { ok: false, type: "check_failed", message: planned.error.message };
|
|
895
813
|
}
|
|
896
|
-
return { ok: true, labels:
|
|
814
|
+
return { ok: true, labels: planned.target, mode: "mapped" };
|
|
897
815
|
}
|
|
898
816
|
function loadInLabelMapping() {
|
|
899
817
|
const configPath = path.join(repoRoot, ".agents", ".airc.json");
|
|
@@ -902,60 +820,16 @@ function loadInLabelMapping() {
|
|
|
902
820
|
}
|
|
903
821
|
try {
|
|
904
822
|
const config = JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
905
|
-
const mapping = config?.labels?.in;
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
const normalized = {};
|
|
910
|
-
for (const [label, prefixes] of Object.entries(mapping)) {
|
|
911
|
-
if (!Array.isArray(prefixes)) {
|
|
912
|
-
continue;
|
|
913
|
-
}
|
|
914
|
-
const cleaned = prefixes
|
|
915
|
-
.map((value) => String(value || "").trim())
|
|
916
|
-
.filter(Boolean);
|
|
917
|
-
if (cleaned.length > 0) {
|
|
918
|
-
normalized[label] = cleaned;
|
|
919
|
-
}
|
|
920
|
-
}
|
|
921
|
-
return { ok: true, value: normalized };
|
|
823
|
+
const mapping = validateInLabelMapping(config?.labels?.in);
|
|
824
|
+
return mapping.ok
|
|
825
|
+
? { ok: true, value: mapping.value }
|
|
826
|
+
: { ok: false, type: "check_failed", message: mapping.error.message };
|
|
922
827
|
}
|
|
923
828
|
catch (error) {
|
|
924
829
|
return { ok: false, type: "check_failed", message: `Unable to parse .agents/.airc.json: ${error.message}` };
|
|
925
830
|
}
|
|
926
831
|
}
|
|
927
832
|
// === GitHub API ===
|
|
928
|
-
function detectRepoOwnerType(upstreamRepo, taskDir) {
|
|
929
|
-
const ownerTypeResult = withRetry(() => ghText([
|
|
930
|
-
"api",
|
|
931
|
-
`repos/${upstreamRepo}`,
|
|
932
|
-
"--jq",
|
|
933
|
-
".owner.type // empty"
|
|
934
|
-
], taskDir));
|
|
935
|
-
if (!ownerTypeResult.ok) {
|
|
936
|
-
return "unknown";
|
|
937
|
-
}
|
|
938
|
-
return ownerTypeResult.value || "unknown";
|
|
939
|
-
}
|
|
940
|
-
function ghJson(args, cwd) {
|
|
941
|
-
return mapClientResult(githubClient.json(args, { cwd }));
|
|
942
|
-
}
|
|
943
|
-
function ghText(args, cwd) {
|
|
944
|
-
return mapClientResult(githubClient.text(args, { cwd }));
|
|
945
|
-
}
|
|
946
|
-
function ghPaginatedJson(args, cwd) {
|
|
947
|
-
return ghJson(args, cwd);
|
|
948
|
-
}
|
|
949
|
-
function mapClientResult(result) {
|
|
950
|
-
if (result.ok)
|
|
951
|
-
return result;
|
|
952
|
-
const checkFailed = ["RESOURCE_NOT_FOUND", "PERMISSION_DENIED", "PLATFORM_REQUEST_INVALID"].includes(result.error.code);
|
|
953
|
-
return {
|
|
954
|
-
ok: false,
|
|
955
|
-
type: checkFailed ? "check_failed" : "network_error",
|
|
956
|
-
message: result.error.message
|
|
957
|
-
};
|
|
958
|
-
}
|
|
959
833
|
function gitText(args, cwd) {
|
|
960
834
|
try {
|
|
961
835
|
const value = execFileSync("git", args, { cwd, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] });
|
|
@@ -2,6 +2,8 @@ import fs from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { parseTypedTaskFrontmatter } from '../task/frontmatter.js';
|
|
4
4
|
import { readPrDeliveryFact } from '../task/pr-delivery-fact.js';
|
|
5
|
+
import { resourceIdentityEquals, resourceIdentityNumber } from '../platform/resource-identity.js';
|
|
6
|
+
import { taskIssueIdentity } from '../platform/task-identities.js';
|
|
5
7
|
export class HostResolutionError extends Error {
|
|
6
8
|
code;
|
|
7
9
|
constructor(code, message) {
|
|
@@ -70,14 +72,23 @@ export function collectHostCandidates(input) {
|
|
|
70
72
|
catch {
|
|
71
73
|
continue;
|
|
72
74
|
}
|
|
73
|
-
const
|
|
75
|
+
const issueIdentity = taskIssueIdentity(frontmatter);
|
|
76
|
+
const issueNumber = resourceIdentityNumber(issueIdentity) ?? toPositiveNumber(frontmatter.issue_number);
|
|
74
77
|
const fact = readPrDeliveryFact(frontmatter);
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
+
const prIdentity = fact.status === 'valid' && fact.fact.state === 'bound' ? fact.fact.identity.resource : null;
|
|
79
|
+
const prNumber = resourceIdentityNumber(prIdentity);
|
|
80
|
+
const matchesPr = input.prIdentity
|
|
81
|
+
? Boolean(prIdentity && resourceIdentityEquals(prIdentity, input.prIdentity))
|
|
82
|
+
: input.prNumber !== undefined && prNumber === input.prNumber;
|
|
83
|
+
const identityDetails = {
|
|
84
|
+
...(issueIdentity && issueIdentity.kind !== 'number' ? { issueIdentity } : {}),
|
|
85
|
+
...(prIdentity && prIdentity.kind !== 'number' ? { prIdentity } : {})
|
|
86
|
+
};
|
|
87
|
+
if (matchesPr) {
|
|
88
|
+
candidates.push({ taskId: entry.name, taskDir, issueNumber, prNumber, ...identityDetails });
|
|
78
89
|
}
|
|
79
90
|
else if (issueNumber !== null && input.closingIssues.includes(issueNumber)) {
|
|
80
|
-
candidates.push({ taskId: entry.name, taskDir, issueNumber, prNumber });
|
|
91
|
+
candidates.push({ taskId: entry.name, taskDir, issueNumber, prNumber, ...identityDetails });
|
|
81
92
|
}
|
|
82
93
|
}
|
|
83
94
|
return candidates;
|
|
@@ -97,7 +108,9 @@ export function resolveHostFromCandidates(candidates) {
|
|
|
97
108
|
taskId: candidate.taskId,
|
|
98
109
|
taskDir: candidate.taskDir,
|
|
99
110
|
issueNumber: candidate.issueNumber ?? 0,
|
|
100
|
-
prNumber: candidate.prNumber ?? 0
|
|
111
|
+
prNumber: candidate.prNumber ?? 0,
|
|
112
|
+
...(candidate.issueIdentity ? { issueIdentity: candidate.issueIdentity } : {}),
|
|
113
|
+
...(candidate.prIdentity ? { prIdentity: candidate.prIdentity } : {})
|
|
101
114
|
};
|
|
102
115
|
}
|
|
103
116
|
return {
|