@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.
Files changed (239) hide show
  1. package/README.md +11 -11
  2. package/README.zh-CN.md +11 -11
  3. package/dist/bin/internal-cli.js +24 -19
  4. package/dist/lib/agent-clients/next-steps.js +6 -1
  5. package/dist/lib/decide.js +3 -3
  6. package/dist/lib/internal/platform-checks.js +3 -3
  7. package/dist/lib/internal/platform-comment.js +9 -9
  8. package/dist/lib/internal/platform-context.js +2 -2
  9. package/dist/lib/internal/platform-issue.js +8 -9
  10. package/dist/lib/internal/platform-pr-review.js +10 -10
  11. package/dist/lib/internal/platform-pr.js +63 -24
  12. package/dist/lib/internal/platform-release-notes.js +3 -3
  13. package/dist/lib/internal/pr-review-grade.js +11 -9
  14. package/dist/lib/internal/release-workflow.js +4 -4
  15. package/dist/lib/internal/sandbox-control.js +30 -4
  16. package/dist/lib/internal/task-activity.js +3 -3
  17. package/dist/lib/internal/task-artifact.js +76 -24
  18. package/dist/lib/internal/task-create.js +2 -2
  19. package/dist/lib/internal/task-event.js +6 -3
  20. package/dist/lib/internal/task-finalization.js +2 -2
  21. package/dist/lib/internal/task-invalidation.js +60 -0
  22. package/dist/lib/internal/task-ledger.js +9 -6
  23. package/dist/lib/internal/task-lifecycle.js +2 -2
  24. package/dist/lib/internal/task-override.js +4 -4
  25. package/dist/lib/internal/task-review.js +3 -3
  26. package/dist/lib/internal/task-verify.js +6 -6
  27. package/dist/lib/internal/task-warning.js +3 -3
  28. package/dist/lib/platform/adapters.js +1 -3
  29. package/dist/lib/platform/change-request-git-evidence.js +27 -7
  30. package/dist/lib/platform/completion-backfill.js +6 -5
  31. package/dist/lib/platform/context.js +70 -130
  32. package/dist/lib/platform/github-client.js +1 -1
  33. package/dist/lib/platform/github-provider.js +903 -0
  34. package/dist/lib/platform/in-label-sync.js +143 -0
  35. package/dist/lib/platform/issue-comments.js +94 -41
  36. package/dist/lib/platform/issues.js +335 -189
  37. package/dist/lib/platform/metadata-labels.js +6 -2
  38. package/dist/lib/platform/none-provider.js +28 -0
  39. package/dist/lib/platform/pr-change-report.js +551 -0
  40. package/dist/lib/platform/pr-checks.js +119 -104
  41. package/dist/lib/platform/pr-review.js +88 -72
  42. package/dist/lib/platform/pr-summary.js +520 -50
  43. package/dist/lib/platform/provider-bridge.js +45 -0
  44. package/dist/lib/platform/provider-contract.d.ts +487 -0
  45. package/dist/lib/platform/provider-contract.js +95 -0
  46. package/dist/lib/platform/provider-loader.js +251 -0
  47. package/dist/lib/platform/provider-validation.js +504 -0
  48. package/dist/lib/platform/pull-request-metadata.js +8 -6
  49. package/dist/lib/platform/pull-requests.js +783 -537
  50. package/dist/lib/platform/release-notes.js +78 -55
  51. package/dist/lib/platform/releases.js +79 -50
  52. package/dist/lib/platform/resource-identity.d.ts +26 -0
  53. package/dist/lib/platform/resource-identity.js +92 -0
  54. package/dist/lib/platform/task-identities.js +31 -0
  55. package/dist/lib/platform/verification-required.js +11 -12
  56. package/dist/lib/platform/verification-sync.js +112 -238
  57. package/dist/lib/pr-review/evidence-grading.js +19 -6
  58. package/dist/lib/run/index.js +63 -23
  59. package/dist/lib/sandbox/control/client.js +96 -15
  60. package/dist/lib/sandbox/control/executor.js +3 -3
  61. package/dist/lib/sandbox/control/protocol.js +77 -0
  62. package/dist/lib/sandbox/control/server.js +398 -25
  63. package/dist/lib/sandbox/control/state.js +204 -1
  64. package/dist/lib/server/protocol.js +38 -1
  65. package/dist/lib/task/activity-log.js +8 -1
  66. package/dist/lib/task/artifact-lifecycle.js +33 -5
  67. package/dist/lib/task/capabilities.js +239 -0
  68. package/dist/lib/task/commands/status.js +32 -0
  69. package/dist/lib/task/commit-operation.js +12 -1
  70. package/dist/lib/task/control-authority.js +8 -5
  71. package/dist/lib/task/create-service.js +8 -8
  72. package/dist/lib/task/current-contract.js +5 -1
  73. package/dist/lib/task/decision-details.js +1 -1
  74. package/dist/lib/task/events.js +297 -17
  75. package/dist/lib/task/finalization.js +37 -13
  76. package/dist/lib/task/human-override.js +58 -36
  77. package/dist/lib/task/invalidation-command.js +86 -0
  78. package/dist/lib/task/invalidation.js +260 -0
  79. package/dist/lib/task/ledger-intents.js +37 -0
  80. package/dist/lib/task/local-artifact-finalization.js +374 -0
  81. package/dist/lib/task/orchestration.js +49 -93
  82. package/dist/lib/task/pr-delivery-fact.js +102 -101
  83. package/dist/lib/task/review-fingerprint.js +24 -2
  84. package/dist/lib/task/rework-intent.js +138 -0
  85. package/dist/lib/task/skill-scope.js +23 -0
  86. package/dist/lib/task/task-execution-lock.js +27 -17
  87. package/dist/lib/task/verification-engine.js +43 -16
  88. package/dist/lib/task/verification.js +3 -3
  89. package/dist/lib/task/workflow-warning-intents.js +1 -1
  90. package/dist/lib/task/write.js +11 -0
  91. package/dist/lib/version.js +11 -1
  92. package/dist/lifecycle-build-manifest.json +3 -3
  93. package/lib/agent-clients/next-steps.ts +6 -1
  94. package/lib/decide.ts +3 -3
  95. package/lib/init.ts +1 -1
  96. package/lib/internal/platform-checks.ts +3 -3
  97. package/lib/internal/platform-comment.ts +8 -8
  98. package/lib/internal/platform-context.ts +2 -2
  99. package/lib/internal/platform-issue.ts +7 -8
  100. package/lib/internal/platform-pr-review.ts +9 -9
  101. package/lib/internal/platform-pr.ts +47 -22
  102. package/lib/internal/platform-release-notes.ts +3 -3
  103. package/lib/internal/pr-review-grade.ts +10 -8
  104. package/lib/internal/release-workflow.ts +4 -4
  105. package/lib/internal/sandbox-control.ts +27 -3
  106. package/lib/internal/task-activity.ts +3 -3
  107. package/lib/internal/task-artifact.ts +65 -22
  108. package/lib/internal/task-create.ts +2 -2
  109. package/lib/internal/task-event.ts +6 -3
  110. package/lib/internal/task-finalization.ts +3 -3
  111. package/lib/internal/task-invalidation.ts +39 -0
  112. package/lib/internal/task-ledger.ts +11 -8
  113. package/lib/internal/task-lifecycle.ts +2 -2
  114. package/lib/internal/task-override.ts +6 -6
  115. package/lib/internal/task-review.ts +3 -3
  116. package/lib/internal/task-verify.ts +6 -6
  117. package/lib/internal/task-warning.ts +3 -3
  118. package/lib/platform/adapters.ts +3 -2
  119. package/lib/platform/change-request-git-evidence.ts +36 -9
  120. package/lib/platform/completion-backfill.ts +9 -8
  121. package/lib/platform/context.ts +97 -134
  122. package/lib/platform/github-client.ts +2 -2
  123. package/lib/platform/github-provider.ts +910 -0
  124. package/lib/platform/in-label-sync.ts +208 -0
  125. package/lib/platform/issue-comments.ts +97 -47
  126. package/lib/platform/issues.ts +344 -174
  127. package/lib/platform/metadata-labels.ts +5 -2
  128. package/lib/platform/none-provider.ts +35 -0
  129. package/lib/platform/pr-change-report.ts +645 -0
  130. package/lib/platform/pr-checks.ts +126 -106
  131. package/lib/platform/pr-review.ts +88 -85
  132. package/lib/platform/pr-summary.ts +560 -53
  133. package/lib/platform/provider-bridge.ts +82 -0
  134. package/lib/platform/provider-contract.ts +536 -0
  135. package/lib/platform/provider-loader.ts +307 -0
  136. package/lib/platform/provider-validation.ts +529 -0
  137. package/lib/platform/pull-request-metadata.ts +10 -7
  138. package/lib/platform/pull-requests.ts +826 -543
  139. package/lib/platform/release-notes.ts +76 -59
  140. package/lib/platform/releases.ts +77 -43
  141. package/lib/platform/resource-identity.ts +121 -0
  142. package/lib/platform/task-identities.ts +40 -0
  143. package/lib/platform/types.ts +4 -2
  144. package/lib/platform/verification-required.ts +11 -13
  145. package/lib/platform/verification-sync.ts +112 -292
  146. package/lib/pr-review/evidence-grading.ts +25 -8
  147. package/lib/run/index.ts +57 -22
  148. package/lib/sandbox/control/client.ts +105 -15
  149. package/lib/sandbox/control/executor.ts +4 -4
  150. package/lib/sandbox/control/protocol.ts +113 -0
  151. package/lib/sandbox/control/server.ts +431 -23
  152. package/lib/sandbox/control/state.ts +251 -1
  153. package/lib/server/protocol.ts +36 -1
  154. package/lib/task/activity-log.ts +8 -0
  155. package/lib/task/artifact-lifecycle.ts +32 -6
  156. package/lib/task/capabilities.ts +262 -0
  157. package/lib/task/commands/status.ts +38 -0
  158. package/lib/task/commit-operation.ts +12 -1
  159. package/lib/task/control-authority.ts +11 -8
  160. package/lib/task/create-service.ts +8 -8
  161. package/lib/task/current-contract.ts +6 -1
  162. package/lib/task/decision-details.ts +6 -2
  163. package/lib/task/events.ts +296 -16
  164. package/lib/task/finalization.ts +50 -14
  165. package/lib/task/human-override.ts +70 -46
  166. package/lib/task/invalidation-command.ts +86 -0
  167. package/lib/task/invalidation.ts +332 -0
  168. package/lib/task/ledger-intents.ts +35 -1
  169. package/lib/task/local-artifact-finalization.ts +563 -0
  170. package/lib/task/orchestration.ts +52 -85
  171. package/lib/task/pr-delivery-fact.ts +131 -148
  172. package/lib/task/review-fingerprint.ts +23 -2
  173. package/lib/task/rework-intent.ts +165 -0
  174. package/lib/task/skill-scope.ts +24 -0
  175. package/lib/task/task-execution-lock.ts +23 -14
  176. package/lib/task/verification-engine.ts +52 -17
  177. package/lib/task/verification.ts +4 -4
  178. package/lib/task/workflow-warning-intents.ts +1 -1
  179. package/lib/task/write.ts +19 -0
  180. package/lib/update.ts +1 -1
  181. package/lib/version.ts +15 -1
  182. package/package.json +7 -1
  183. package/templates/.agents/rules/README.en.md +1 -1
  184. package/templates/.agents/rules/README.zh-CN.md +1 -1
  185. package/templates/.agents/rules/issue-pr-commands.en.md +1 -1
  186. package/templates/.agents/rules/issue-pr-commands.zh-CN.md +1 -1
  187. package/templates/.agents/rules/issue-sync.en.md +2 -0
  188. package/templates/.agents/rules/issue-sync.zh-CN.md +2 -0
  189. package/templates/.agents/rules/local-artifact-repair.en.md +9 -2
  190. package/templates/.agents/rules/local-artifact-repair.zh-CN.md +9 -2
  191. package/templates/.agents/rules/pr-sync.en.md +10 -4
  192. package/templates/.agents/rules/pr-sync.zh-CN.md +17 -4
  193. package/templates/.agents/rules/task-short-id.en.md +1 -4
  194. package/templates/.agents/rules/task-short-id.zh-CN.md +1 -3
  195. package/templates/.agents/skills/analyze-task/SKILL.en.md +13 -3
  196. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +10 -3
  197. package/templates/.agents/skills/check-task/SKILL.en.md +16 -37
  198. package/templates/.agents/skills/check-task/SKILL.zh-CN.md +16 -37
  199. package/templates/.agents/skills/code-task/SKILL.en.md +27 -8
  200. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +29 -10
  201. package/templates/.agents/skills/code-task/config/verify.en.json +1 -0
  202. package/templates/.agents/skills/code-task/config/verify.zh-CN.json +1 -0
  203. package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +2 -0
  204. package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +2 -0
  205. package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +3 -3
  206. package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +3 -3
  207. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +5 -3
  208. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +5 -3
  209. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +4 -3
  210. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +4 -3
  211. package/templates/.agents/skills/complete-task/SKILL.en.md +21 -3
  212. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +20 -3
  213. package/templates/.agents/skills/create-pr/SKILL.en.md +7 -6
  214. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +7 -7
  215. package/templates/.agents/skills/create-pr/config/verify.json +1 -0
  216. package/templates/.agents/skills/create-pr/reference/change-report.en.md +16 -4
  217. package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +16 -4
  218. package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +5 -3
  219. package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +5 -3
  220. package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +1 -1
  221. package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +1 -1
  222. package/templates/.agents/skills/create-pr/scripts/change-report.mjs +3 -0
  223. package/templates/.agents/skills/plan-task/SKILL.en.md +13 -3
  224. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +10 -3
  225. package/templates/.agents/skills/review-analysis/SKILL.en.md +3 -3
  226. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +3 -3
  227. package/templates/.agents/skills/review-code/SKILL.en.md +4 -4
  228. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +4 -4
  229. package/templates/.agents/skills/review-plan/SKILL.en.md +3 -3
  230. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +3 -3
  231. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +4 -2
  232. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +4 -2
  233. package/templates/.agents/skills/watch-pr/SKILL.en.md +2 -2
  234. package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +2 -2
  235. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +6 -0
  236. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +6 -0
  237. package/templates/.agents/templates/task.en.md +18 -1
  238. package/templates/.agents/templates/task.zh-CN.md +18 -1
  239. package/templates/.github/workflows/pr-label.yml +7 -26
@@ -21,6 +21,7 @@ import { snapshotReview } from "../git/review-snapshot.js";
21
21
  import { OrchestrationStateError, readRun } from "./orchestration.js";
22
22
  import { resolveDeliveryTarget } from "./delivery-target.js";
23
23
  import { readPrDeliveryFact } from "./pr-delivery-fact.js";
24
+ import { validateLocalArtifact } from "./local-artifact-finalization.js";
24
25
  const TASK_ENUMS = {
25
26
  type: ["feature", "bugfix", "refactor", "docs", "chore"],
26
27
  workflow: ["feature-development", "bug-fix", "refactoring"],
@@ -69,7 +70,7 @@ const scriptPath = fileURLToPath(import.meta.url);
69
70
  let repoRoot = path.resolve(path.dirname(scriptPath), "..", "..");
70
71
  const PLATFORM_ADAPTERS = {
71
72
  "platform-sync": checkPlatformSync,
72
- "platform-sync-preflight": (context, shared) => ({ ...checkPlatformSync(context, shared), type: "platform-sync-preflight" }),
73
+ "platform-sync-preflight": async (context, shared) => ({ ...await checkPlatformSync(context, shared), type: "platform-sync-preflight" }),
73
74
  "required-checks": checkRequiredChecks
74
75
  };
75
76
  const OPTIONAL_PLATFORM_CHECKS = new Set(["platform-sync"]);
@@ -86,7 +87,7 @@ const sharedUtils = {
86
87
  parseIssueNumber,
87
88
  parsePrNumber
88
89
  };
89
- function runCheck(type, context, shared) {
90
+ async function runCheck(type, context, shared) {
90
91
  switch (type) {
91
92
  case "task-meta":
92
93
  return checkTaskMeta(context);
@@ -124,7 +125,7 @@ function runCheck(type, context, shared) {
124
125
  }
125
126
  return failResult(type, `Unsupported check type '${type}'.`);
126
127
  }
127
- return adapter(context, shared);
128
+ return await adapter(context, shared);
128
129
  }
129
130
  }
130
131
  }
@@ -419,7 +420,7 @@ function checkTaskMeta({ taskDir, config }) {
419
420
  const warningSuffix = warnings.length > 0 ? `; warnings: ${warnings.join("; ")}` : "";
420
421
  return passResult("task-meta", `Task metadata valid (${requiredFields.length} required fields checked${warningSuffix})`, warnings);
421
422
  }
422
- function checkRequiredPrDelivery({ taskDir, repositoryRoot, mode }) {
423
+ async function checkRequiredPrDelivery({ taskDir, repositoryRoot, mode }) {
423
424
  const task = loadTask(taskDir);
424
425
  if (!task.ok)
425
426
  return failResult('required-pr-delivery', task.message);
@@ -438,10 +439,11 @@ function checkRequiredPrDelivery({ taskDir, repositoryRoot, mode }) {
438
439
  return failResult('required-pr-delivery', factRead.error.message);
439
440
  const fact = factRead.status === 'valid' ? factRead.fact : null;
440
441
  const status = fact?.state || 'unbound';
441
- const prNumber = fact?.state === 'bound' ? fact.identity.number : null;
442
+ const prIdentity = fact?.state === 'bound' ? fact.identity.resource : null;
443
+ const prNumber = prIdentity?.kind === 'number' ? prIdentity.value : null;
442
444
  if (flow === 'disabled')
443
445
  return passResult('required-pr-delivery', 'Pull request delivery is disabled by project policy');
444
- if (flow === 'required' && (status !== 'bound' || prNumber === null)) {
446
+ if (flow === 'required' && (status !== 'bound' || !prIdentity)) {
445
447
  return failResult('required-pr-delivery', 'Project policy requires a bound pull request before completion');
446
448
  }
447
449
  if (flow !== 'required' && status === 'unbound') {
@@ -451,7 +453,7 @@ function checkRequiredPrDelivery({ taskDir, repositoryRoot, mode }) {
451
453
  if (mode === 'gate') {
452
454
  return passResult('required-pr-delivery', 'Bound pull request shape is valid; merged-state inspection is reserved for hard preflight');
453
455
  }
454
- const inspected = inspectPlatformPullRequest(task.metadata.id, { cwd: repositoryRoot });
456
+ const inspected = await inspectPlatformPullRequest(task.metadata.id, { cwd: repositoryRoot });
455
457
  if (inspected.status === 'blocked') {
456
458
  return blockedResult('required-pr-delivery', inspected.error?.message || 'Unable to inspect the required pull request', inspected.error?.code || 'PR_INSPECTION_BLOCKED');
457
459
  }
@@ -459,7 +461,7 @@ function checkRequiredPrDelivery({ taskDir, repositoryRoot, mode }) {
459
461
  const repository = inspected.platform.repository?.toLowerCase() || '';
460
462
  const taskBranch = String(task.metadata.branch || '').trim();
461
463
  const deliveryBaseRef = String(task.metadata.delivery_base_ref || projectConfig.delivery?.baseRef || '').trim();
462
- if (inspected.status !== 'no-op' || !pullRequest || pullRequest.number !== prNumber) {
464
+ if (inspected.status !== 'no-op' || !pullRequest) {
463
465
  return failResult('required-pr-delivery', 'Bound pull request identity could not be verified');
464
466
  }
465
467
  if (!repository
@@ -472,7 +474,7 @@ function checkRequiredPrDelivery({ taskDir, repositoryRoot, mode }) {
472
474
  if (pullRequest.state !== 'closed' || !pullRequest.mergedAt || !pullRequest.mergeCommitSha) {
473
475
  return failResult('required-pr-delivery', 'Project policy requires the bound pull request to be merged');
474
476
  }
475
- return passResult('required-pr-delivery', `Pull request delivery policy satisfied (merged PR #${prNumber})`);
477
+ return passResult('required-pr-delivery', `Pull request delivery policy satisfied (merged PR ${prNumber === null ? 'identity' : `#${prNumber}`})`);
476
478
  }
477
479
  return passResult('required-pr-delivery', `Pull request delivery policy satisfied (${status})`);
478
480
  }
@@ -504,7 +506,7 @@ function loadProjectName() {
504
506
  return "";
505
507
  }
506
508
  }
507
- function checkArtifact({ taskDir, config, artifactFile }) {
509
+ function checkArtifact({ taskDir, config, artifactFile, skillName }) {
508
510
  const resolvedArtifact = resolveArtifactPath(taskDir, config.file_pattern, artifactFile);
509
511
  if (!resolvedArtifact.ok) {
510
512
  return failResult("artifact", resolvedArtifact.message);
@@ -519,6 +521,20 @@ function checkArtifact({ taskDir, config, artifactFile }) {
519
521
  }
520
522
  const content = fs.readFileSync(artifactPath, "utf8");
521
523
  const requiredSections = config.required_sections || [];
524
+ const localFamily = skillName === "analyze-task"
525
+ ? "analysis"
526
+ : skillName === "plan-task" ? "plan" : skillName === "code-task" ? "code" : null;
527
+ if (localFamily) {
528
+ const local = validateLocalArtifact(content, {
529
+ family: localFamily,
530
+ requiredSections: requiredSections.filter((section) => typeof section === "string"),
531
+ requiredPatterns: (config.required_patterns || []).filter((pattern) => typeof pattern === "string")
532
+ });
533
+ if (!local.ok) {
534
+ return failResult("artifact", `${path.basename(artifactPath)} has local structural errors: ${local.diagnostics.map((item) => `${item.code}: ${item.message}`).join('; ')}`);
535
+ }
536
+ return passResult("artifact", `${path.basename(artifactPath)} passed (${requiredSections.length} sections)`);
537
+ }
522
538
  const missingSections = requiredSections.filter((section) => !new RegExp(`^##\\s+${escapeRegExp(section)}\\s*$`, "m").test(content));
523
539
  if (missingSections.length > 0) {
524
540
  return failResult("artifact", `${path.basename(artifactPath)} is missing sections: ${missingSections.join(", ")}`);
@@ -902,6 +918,8 @@ function checkManualValidation({ taskDir }) {
902
918
  // 1. Latest review-code artifact; none -> pass (no review, check not applicable).
903
919
  const review = findAuthoritativeReviewCodeArtifact(taskDir);
904
920
  if (!review.ok) {
921
+ if (review.error)
922
+ return failResult("manual-validation", `Review-code artifact is unavailable: ${review.error}`);
905
923
  return passResult("manual-validation", "No review-code artifact; manual validation not applicable");
906
924
  }
907
925
  // 2. Parse the numeric manual-validation count; unparseable/null -> fail closed
@@ -953,9 +971,11 @@ function checkManualValidation({ taskDir }) {
953
971
  }
954
972
  return passResult("manual-validation", `Manual validation completed → ${artifactName}`);
955
973
  }
956
- function checkPostReviewCommit({ taskDir, config }) {
974
+ async function checkPostReviewCommit({ taskDir, config }) {
957
975
  const reviewArtifact = findAuthoritativeReviewCodeArtifact(taskDir);
958
976
  if (!reviewArtifact.ok) {
977
+ if (reviewArtifact.error)
978
+ return failResult("post-review-commit", `Review-code artifact is unavailable: ${reviewArtifact.error}`);
959
979
  return passResult("post-review-commit", "No review-code artifact; check inactive");
960
980
  }
961
981
  let gitRoot;
@@ -977,7 +997,7 @@ function checkPostReviewCommit({ taskDir, config }) {
977
997
  const hasPullRequest = fact?.state === 'bound';
978
998
  let inspected = null;
979
999
  if (hasPullRequest) {
980
- inspected = inspectPlatformPullRequest(task.metadata.id, { cwd: gitRoot });
1000
+ inspected = await inspectPlatformPullRequest(task.metadata.id, { cwd: gitRoot });
981
1001
  if (!inspected.pullRequest) {
982
1002
  const message = inspected.error?.message || "PR merge snapshot is unavailable";
983
1003
  const code = inspected.error?.code || "PR_INSPECTION_INVALID";
@@ -988,7 +1008,7 @@ function checkPostReviewCommit({ taskDir, config }) {
988
1008
  if (inspected.pullRequest.state === "closed" &&
989
1009
  inspected.pullRequest.mergedAt &&
990
1010
  inspected.pullRequest.mergeCommitSha) {
991
- const relation = resolveMaterializedReviewedHeadRelation({
1011
+ const relation = await resolveMaterializedReviewedHeadRelation({
992
1012
  cwd: gitRoot,
993
1013
  platformType: inspected.platform.type,
994
1014
  lastReviewedCommit,
@@ -1124,6 +1144,13 @@ function checkReviewFact({ taskDir, artifactFile }) {
1124
1144
  if (!resolvedArtifact.ok) {
1125
1145
  return failResult("review-fact", resolvedArtifact.message);
1126
1146
  }
1147
+ const authoritative = findAuthoritativeReviewCodeArtifact(taskDir);
1148
+ if (!authoritative.ok) {
1149
+ return failResult("review-fact", authoritative.error ?? "No active review-code artifact is available");
1150
+ }
1151
+ if (path.basename(resolvedArtifact.path) !== authoritative.fileName) {
1152
+ return failResult("review-fact", `review-code artifact ${path.basename(resolvedArtifact.path)} is not the current active artifact ${authoritative.fileName}; re-run review-code`);
1153
+ }
1127
1154
  const task = loadTask(taskDir);
1128
1155
  if (!task.ok) {
1129
1156
  return failResult("review-fact", task.message);
@@ -1401,7 +1428,7 @@ function escapeRegExp(value) {
1401
1428
  function isBlank(value) {
1402
1429
  return value === undefined || value === null || String(value).trim() === "";
1403
1430
  }
1404
- function verifyInProcess({ mode, skillName, taskDir, artifactFile, checks: requestedChecks, repositoryRoot }) {
1431
+ async function verifyInProcess({ mode, skillName, taskDir, artifactFile, checks: requestedChecks, repositoryRoot }) {
1405
1432
  if (repositoryRoot)
1406
1433
  repoRoot = path.resolve(repositoryRoot);
1407
1434
  else {
@@ -1418,7 +1445,7 @@ function verifyInProcess({ mode, skillName, taskDir, artifactFile, checks: reque
1418
1445
  for (const [type, checkConfig] of Object.entries(verifyConfig.checks || {})) {
1419
1446
  if (checkConfig === null)
1420
1447
  continue;
1421
- const result = runCheck(type, {
1448
+ const result = await runCheck(type, {
1422
1449
  mode,
1423
1450
  skillName,
1424
1451
  taskDir: path.resolve(taskDir),
@@ -1441,7 +1468,7 @@ function verifyInProcess({ mode, skillName, taskDir, artifactFile, checks: reque
1441
1468
  return { skill: skillName, ...passResult(type, `Check '${type}' is disabled for skill '${skillName}'.`) };
1442
1469
  return {
1443
1470
  skill: skillName,
1444
- ...runCheck(type, {
1471
+ ...await runCheck(type, {
1445
1472
  skillName,
1446
1473
  taskDir: path.resolve(taskDir),
1447
1474
  artifactFile,
@@ -41,7 +41,7 @@ function failure(request, code, message, extra = {}) {
41
41
  artifact: request.artifact ?? null, invocations: [], error: { code, message }, ...extra
42
42
  };
43
43
  }
44
- function verifyTaskEvent(request, options = {}) {
44
+ async function verifyTaskEvent(request, options = {}) {
45
45
  const spec = VERIFICATION_CATALOG[request.event];
46
46
  if (!spec)
47
47
  return failure(request, 'VERIFY_EVENT_UNKNOWN', `unknown verification event '${request.event}'`);
@@ -66,7 +66,7 @@ function verifyTaskEvent(request, options = {}) {
66
66
  const invocations = [];
67
67
  const engine = options.engine ?? verifyInProcess;
68
68
  if (spec.mode === 'gate') {
69
- const payload = engine({ mode: 'gate', skillName: spec.skill, taskDir: resolved.taskDir, artifactFile: request.artifact, checks: [], repositoryRoot: resolved.repoRoot });
69
+ const payload = await engine({ mode: 'gate', skillName: spec.skill, taskDir: resolved.taskDir, artifactFile: request.artifact, checks: [], repositoryRoot: resolved.repoRoot });
70
70
  const status = payload.gate;
71
71
  if (status !== 'pass' && allowsManualOverride(options.manualOverride, 'verification-engine', status === 'blocked' ? 'CHECK_BLOCKED' : 'CHECK_FAILED')) {
72
72
  invocations.push({ status: 'pass', exitCode: 0, payload: { ...payload, gate: 'pass', humanOverride: 'human-approved', originalGate: status } });
@@ -79,7 +79,7 @@ function verifyTaskEvent(request, options = {}) {
79
79
  }
80
80
  else {
81
81
  for (const check of spec.checks ?? []) {
82
- const payload = engine({ mode: 'checks', skillName: spec.skill, taskDir: resolved.taskDir, artifactFile: request.artifact, checks: [check], repositoryRoot: resolved.repoRoot });
82
+ const payload = await engine({ mode: 'checks', skillName: spec.skill, taskDir: resolved.taskDir, artifactFile: request.artifact, checks: [check], repositoryRoot: resolved.repoRoot });
83
83
  const status = payload.status;
84
84
  if (status !== 'pass' && allowsManualOverride(options.manualOverride, 'verification-engine', status === 'blocked' ? 'CHECK_BLOCKED' : 'CHECK_FAILED')) {
85
85
  invocations.push({ status: 'pass', exitCode: 0, payload: { ...payload, status: 'pass', humanOverride: 'human-approved', originalStatus: status } });
@@ -38,7 +38,7 @@ function sectionMutation(content) {
38
38
  const english = /^##\s+Activity Log\s*$/m.test(content);
39
39
  return [{
40
40
  kind: 'section', aliases: WORKFLOW_WARNING_HEADINGS, heading: english ? 'Workflow Warnings' : '工作流告警',
41
- body: `| ${WORKFLOW_WARNING_COLUMNS.join(' | ')} |\n|----|------|------|----------|------|--------|--------|---------|--------|-------------|------------|`
41
+ body: `| ${WORKFLOW_WARNING_COLUMNS.join(' | ')} |\n|----|------|------|----------|------|--------|---------|--------|-------------|------------|------------|`
42
42
  }];
43
43
  }
44
44
  function rowMutation(row) {
@@ -6,6 +6,7 @@ import { resolveTaskRef } from './resolve-ref.js';
6
6
  import { allowsManualOverride } from './guard-override.js';
7
7
  import { mutateTableRow, upsertSection } from './sections.js';
8
8
  import { validateCurrentTaskContract } from './current-contract.js';
9
+ import { invalidationBlocks, parseInvalidationDocument } from './invalidation.js';
9
10
  const DEFAULT_FILE_SYSTEM = {
10
11
  readFileSync: (file) => fs.readFileSync(file, 'utf8'),
11
12
  statModeSync: (file) => fs.statSync(file).mode,
@@ -90,6 +91,16 @@ function writeTask(request, options = {}) {
90
91
  if (!contract.ok) {
91
92
  return failure(request, identity, contract.code, contract.message);
92
93
  }
94
+ if (invalidationBlocks(contract.invalidation)
95
+ && options.invalidationContext !== 'source-completion'
96
+ && options.invalidationContext !== 'reconcile') {
97
+ return failure(request, identity, 'TASK_INVALIDATION_BLOCKED', 'task has an incomplete artifact invalidation operation; reconcile it before writing downstream state');
98
+ }
99
+ }
100
+ else {
101
+ const invalidation = parseInvalidationDocument(original);
102
+ if (!invalidation.ok)
103
+ return failure(request, identity, 'TASK_INVALIDATION_INVALID', invalidation.message);
93
104
  }
94
105
  let candidate = original;
95
106
  const operations = [];
@@ -1,4 +1,5 @@
1
1
  import { existsSync, readFileSync } from 'node:fs';
2
+ import semver from 'semver';
2
3
  const packageJsonUrl = [
3
4
  new URL('../package.json', import.meta.url),
4
5
  new URL('../../package.json', import.meta.url),
@@ -9,8 +10,17 @@ if (!packageJsonUrl) {
9
10
  const { version } = JSON.parse(readFileSync(packageJsonUrl, 'utf8'));
10
11
  const VERSION = `v${version}`;
11
12
  const AGENT_INFRA_VERSION_PATTERN = /^v\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
13
+ const LEGACY_COMPATIBILITY_CUTOFF_VERSION = '1.0.0';
12
14
  function isValidAgentInfraVersion(value) {
13
15
  return typeof value === 'string' && AGENT_INFRA_VERSION_PATTERN.test(value);
14
16
  }
15
- export { VERSION, isValidAgentInfraVersion };
17
+ function isLegacyCompatibilityEnabled(runtimeVersion = VERSION) {
18
+ const normalized = runtimeVersion.startsWith('v') ? runtimeVersion.slice(1) : runtimeVersion;
19
+ const parsed = semver.valid(normalized);
20
+ return parsed !== null && semver.lt(parsed, LEGACY_COMPATIBILITY_CUTOFF_VERSION);
21
+ }
22
+ function legacyCompatibilityError(resource, runtimeVersion = VERSION) {
23
+ return Object.assign(new Error(`PLATFORM_IDENTITY_LEGACY_UNSUPPORTED: ${resource} requires migration to the current schema before v${LEGACY_COMPATIBILITY_CUTOFF_VERSION} runtime ${runtimeVersion}`), { code: 'PLATFORM_IDENTITY_LEGACY_UNSUPPORTED' });
24
+ }
25
+ export { LEGACY_COMPATIBILITY_CUTOFF_VERSION, VERSION, isLegacyCompatibilityEnabled, isValidAgentInfraVersion, legacyCompatibilityError };
16
26
  //# sourceMappingURL=version.js.map
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "protocolVersion": 3,
3
- "packageVersion": "0.9.12",
4
- "sourceInputHash": "5e1b341aad5fe825884f4dbdbd01d0752ca66107d1ccb4b0fe4d8d3aae49120d",
5
- "internalExecutableBuildHash": "39bf2c60e3a9b834d4a24ab3088f108583d15ee2bdc77e6376e517921be47c80"
3
+ "packageVersion": "0.9.13",
4
+ "sourceInputHash": "5e4d3ad1c1e7e3c909489ef59ff318ea9cffe5f538ae37d83923773946357afe",
5
+ "internalExecutableBuildHash": "c8cabf5c6bc776cdcb9621f712172ebda95ff9b12fb6f67213e18bc0159516fd"
6
6
  }
@@ -1,6 +1,7 @@
1
1
  import { listEnabledAgentClientAdapters } from './registry.ts';
2
2
  import { renderAgentClientInvocation } from './invocation.ts';
3
3
  import semver from 'semver';
4
+ import { isTaskScopeSkill } from '../task/skill-scope.ts';
4
5
  import type { CustomTUI } from './custom-tuis.ts';
5
6
  import type { AgentClientId, AgentClientState } from './types.ts';
6
7
 
@@ -45,7 +46,11 @@ function renderNextStepCommands(
45
46
  }
46
47
 
47
48
  const args = [
48
- ...(input.taskRef === undefined ? [] : [input.taskRef]),
49
+ ...(input.taskRef === undefined
50
+ ? []
51
+ : isTaskScopeSkill(input.skillName)
52
+ ? ['--task', input.taskRef]
53
+ : [input.taskRef]),
49
54
  ...(input.version === undefined ? [] : [input.version])
50
55
  ];
51
56
 
package/lib/decide.ts CHANGED
@@ -79,11 +79,11 @@ export async function decide(args: string[], options: DecideOptions = {}): Promi
79
79
  metadataProvider: () => ({ timestamp: now, agentInfraVersion: options.version ?? VERSION })
80
80
  };
81
81
  let result;
82
- const execute = () => {
82
+ const execute = async () => {
83
83
  let current = applyHumanDecision(request, writeOptions);
84
84
  if (current.status !== 'failed' || !parsedDecision.overrideTicket) return current;
85
85
  if (!parsedDecision.overrideTarget || !parsedDecision.overrideScope) throw new Error('override ticket requires --override-target and --override-scope');
86
- const consumed = consumeHumanOverride({
86
+ const consumed = await consumeHumanOverride({
87
87
  taskRef: resolved.taskId,
88
88
  ticketId: parsedDecision.overrideTicket,
89
89
  failureId: failureId('decision-intent', current.error?.code ?? 'TASK_STATE_MISMATCH'),
@@ -102,7 +102,7 @@ export async function decide(args: string[], options: DecideOptions = {}): Promi
102
102
  if (consumed.status === 'failed') throw new Error(consumed.error.message);
103
103
  return current;
104
104
  };
105
- result = withTaskExecutionLock(resolved.repoRoot, resolved.taskId, 'task-decision', execute);
105
+ result = await withTaskExecutionLock(resolved.repoRoot, resolved.taskId, 'task-decision', execute);
106
106
  if (result.error) throw new Error(result.error.message);
107
107
  return 0;
108
108
  } catch (error) {
package/lib/init.ts CHANGED
@@ -40,7 +40,7 @@ type AgentConfig = {
40
40
  project: string;
41
41
  org: string;
42
42
  language: string;
43
- platform: { type: string };
43
+ platform: { type: string; providers?: Record<string, unknown> };
44
44
  templateVersion: string;
45
45
  sandbox: Record<string, unknown>;
46
46
  task: { shortIdLength: number };
@@ -70,17 +70,17 @@ async function platformChecks(args: string[] = []): Promise<void> {
70
70
  };
71
71
  const unexpected = Object.keys(values).find((name) => !allowed[operation]!.includes(name));
72
72
  if (unexpected) { fail(`${operation} does not accept --${unexpected}`); return; }
73
- if (operation === 'inspect') { finish(inspectPullRequestReadiness(taskRef, { cwd })); return; }
73
+ if (operation === 'inspect') { finish(await inspectPullRequestReadiness(taskRef, { cwd })); return; }
74
74
  if (operation === 'resolve-run') {
75
75
  if (!values.checkName) { fail('resolve-run requires --check-name'); return; }
76
- finish(resolvePlatformCheckRun(taskRef, { cwd, checkName: values.checkName, detailsUrl: values.detailsUrl }));
76
+ finish(await resolvePlatformCheckRun(taskRef, { cwd, checkName: values.checkName, detailsUrl: values.detailsUrl }));
77
77
  return;
78
78
  }
79
79
  if (operation === 'logs') {
80
80
  const run = positive(values.run);
81
81
  const job = values.job === undefined ? undefined : positive(values.job);
82
82
  if (!run || values.job !== undefined && !job) { fail('logs requires a positive --run and optional positive --job'); return; }
83
- finish(fetchPlatformCheckLogs(taskRef, { cwd, run, job: job || undefined }));
83
+ finish(await fetchPlatformCheckLogs(taskRef, { cwd, run, job: job || undefined }));
84
84
  return;
85
85
  }
86
86
  const intervalSeconds = positive(values.intervalSeconds);
@@ -11,7 +11,7 @@ import type { CommentKind } from '../platform/issue-comments.ts';
11
11
  import type { PlatformResult } from '../platform/types.ts';
12
12
  import { backfillCompletionComments } from '../platform/completion-backfill.ts';
13
13
 
14
- const USAGE = `Usage: agent-infra-internal platform-comment list --issue <N> [--cwd <path>]
14
+ const USAGE = `Usage: agent-infra-internal platform-comment list --issue <token> [--cwd <path>]
15
15
  agent-infra-internal platform-comment owner <task-ref> [--cwd <path>]
16
16
  agent-infra-internal platform-comment backfill <task-ref> --agent <agent> [--cwd <path>]
17
17
  agent-infra-internal platform-comment sync <task-ref> --kind <kind> --agent <agent> [--artifact <file>] [--body-file <path|->] [--status-label <label>] [--backfill] [--cwd <path>]
@@ -52,7 +52,7 @@ function readBodyFile(value: string, cwd: string): string {
52
52
  return fs.readFileSync(path.resolve(cwd, value), 'utf8');
53
53
  }
54
54
 
55
- function platformComment(args: string[] = []): void {
55
+ async function platformComment(args: string[] = []): Promise<void> {
56
56
  if (args[0] === '--help' || args[0] === '-h') { process.stdout.write(USAGE); return; }
57
57
  const operation = args[0];
58
58
  if (!operation || !['list', 'owner', 'backfill', 'sync'].includes(operation)) { fail('a valid operation is required'); return; }
@@ -63,17 +63,17 @@ function platformComment(args: string[] = []): void {
63
63
  if (parsed.error) { fail(parsed.error); return; }
64
64
  const cwd = path.resolve(typeof parsed.values.cwd === 'string' ? parsed.values.cwd : process.cwd());
65
65
  if (operation === 'list') {
66
- const issue = Number(parsed.values.issue);
67
- if (!Number.isInteger(issue) || issue <= 0) { fail('list requires a positive --issue'); return; }
66
+ const issue = parsed.values.issue;
67
+ if (typeof issue !== 'string' || !issue) { fail('list requires --issue <token>'); return; }
68
68
  const unexpected = Object.keys(parsed.values).find((key) => !['issue', 'cwd'].includes(key));
69
69
  if (unexpected) { fail(`list does not accept '--${unexpected}'`); return; }
70
- finish(listPlatformComments(issue, cwd));
70
+ finish(await listPlatformComments(issue, cwd));
71
71
  return;
72
72
  }
73
73
  if (operation === 'owner') {
74
74
  const unexpected = Object.keys(parsed.values).find((key) => key !== 'cwd');
75
75
  if (unexpected) { fail(`owner does not accept '--${unexpected}'`); return; }
76
- finish(checkPlatformCommentOwner(taskRef!, { cwd }));
76
+ finish(await checkPlatformCommentOwner(taskRef!, { cwd }));
77
77
  return;
78
78
  }
79
79
  if (operation === 'backfill') {
@@ -83,7 +83,7 @@ function platformComment(args: string[] = []): void {
83
83
  if (typeof agent !== 'string' || !agent) { fail('backfill requires --agent'); return; }
84
84
  const normalizedAgent = normalizeAgentToken(agent);
85
85
  if (!normalizedAgent) { fail(`invalid --agent '${agent}': ${AGENT_USAGE_HINT}`); return; }
86
- finish(backfillCompletionComments(taskRef!, { cwd, agent: normalizedAgent }));
86
+ finish(await backfillCompletionComments(taskRef!, { cwd, agent: normalizedAgent }));
87
87
  return;
88
88
  }
89
89
  const kind = parsed.values.kind;
@@ -101,7 +101,7 @@ function platformComment(args: string[] = []): void {
101
101
  fail(`unable to read body file: ${error instanceof Error ? error.message : String(error)}`);
102
102
  return;
103
103
  }
104
- finish(syncPlatformComment(taskRef!, {
104
+ finish(await syncPlatformComment(taskRef!, {
105
105
  kind: kind as CommentKind,
106
106
  agent: normalizedAgent,
107
107
  artifact: typeof parsed.values.artifact === 'string' ? parsed.values.artifact : undefined,
@@ -10,7 +10,7 @@ function fail(message: string): void {
10
10
  process.exitCode = 1;
11
11
  }
12
12
 
13
- function platformContext(args: string[] = []): void {
13
+ async function platformContext(args: string[] = []): Promise<void> {
14
14
  if (args[0] === '--help' || args[0] === '-h') { process.stdout.write(USAGE); return; }
15
15
  if (args[0] !== 'resolve') { fail("operation must be 'resolve'"); return; }
16
16
  let cwd = process.cwd();
@@ -24,7 +24,7 @@ function platformContext(args: string[] = []): void {
24
24
  seen.add(flag);
25
25
  cwd = path.resolve(value);
26
26
  }
27
- const result = resolvePlatformContext({ cwd });
27
+ const result = await resolvePlatformContext({ cwd });
28
28
  process.stdout.write(`${JSON.stringify(result)}\n`);
29
29
  if (result.status === 'failed') process.exitCode = 1;
30
30
  if (result.status === 'blocked') process.exitCode = 2;
@@ -11,7 +11,7 @@ import type { IssueResult } from '../platform/issues.ts';
11
11
 
12
12
  const USAGE = `Usage: agent-infra-internal platform-issue inspect <task-ref> [--cwd <path>]
13
13
  agent-infra-internal platform-issue create <task-ref> --agent <agent> [--dry-run] [--cwd <path>]
14
- agent-infra-internal platform-issue bind <task-ref> --issue <N> --agent <agent> [--dry-run] [--cwd <path>]
14
+ agent-infra-internal platform-issue bind <task-ref> --issue <token> --agent <agent> [--dry-run] [--cwd <path>]
15
15
  agent-infra-internal platform-issue sync <task-ref> --agent <agent> [--status <suffix|none>] [--assignees current|none] [--milestone initial|specific|none] [--issue-type] [--fields] [--requirements] [--in-labels from-diff|none] [--base <branch>] [--state open|closed] [--close-reason completed|not_planned] [--dry-run] [--cwd <path>]
16
16
  `;
17
17
 
@@ -59,7 +59,7 @@ function oneOf(values: Record<string, string | boolean>, name: string, allowed:
59
59
  return value;
60
60
  }
61
61
 
62
- function platformIssue(args: string[] = []): void {
62
+ async function platformIssue(args: string[] = []): Promise<void> {
63
63
  if (args[0] === '--help' || args[0] === '-h') { process.stdout.write(USAGE); return; }
64
64
  const operation = args[0];
65
65
  if (!operation || !['inspect', 'create', 'bind', 'sync'].includes(operation)) { fail('a valid operation is required'); return; }
@@ -75,18 +75,17 @@ function platformIssue(args: string[] = []): void {
75
75
  };
76
76
  const unexpected = Object.keys(values).find((name) => !allowed[operation]!.includes(name));
77
77
  if (unexpected) { fail(`${operation} does not accept --${unexpected}`); return; }
78
- if (operation === 'inspect') { finish(inspectPlatformIssue(taskRef, { cwd })); return; }
78
+ if (operation === 'inspect') { finish(await inspectPlatformIssue(taskRef, { cwd })); return; }
79
79
  if (typeof values.agent !== 'string' || !values.agent) { fail(`${operation} requires --agent`); return; }
80
80
  const agent = normalizeAgentToken(values.agent);
81
81
  if (!agent) { fail(`invalid --agent '${values.agent}': ${AGENT_USAGE_HINT}`); return; }
82
82
  values.agent = agent;
83
83
  if (operation === 'create') {
84
- finish(createPlatformIssue(taskRef, { cwd, agent: values.agent, dryRun: values.dryRun === true })); return;
84
+ finish(await createPlatformIssue(taskRef, { cwd, agent: values.agent, dryRun: values.dryRun === true })); return;
85
85
  }
86
86
  if (operation === 'bind') {
87
- const issue = Number(values.issue);
88
- if (!Number.isInteger(issue) || issue <= 0) { fail('bind requires a positive --issue'); return; }
89
- finish(bindPlatformIssue(taskRef, { cwd, agent: values.agent, issue, dryRun: values.dryRun === true })); return;
87
+ if (typeof values.issue !== 'string' || !values.issue) { fail('bind requires --issue <token>'); return; }
88
+ finish(await bindPlatformIssue(taskRef, { cwd, agent: values.agent, issue: values.issue, dryRun: values.dryRun === true })); return;
90
89
  }
91
90
  try {
92
91
  const assignees = oneOf(values, 'assignees', ['current', 'none']);
@@ -99,7 +98,7 @@ function platformIssue(args: string[] = []): void {
99
98
  if (closeReason && state !== 'closed') throw new Error('--close-reason requires --state closed');
100
99
  const desiredKeys = ['status', 'assignees', 'milestone', 'issueType', 'fields', 'requirements', 'inLabels', 'state'];
101
100
  if (!desiredKeys.some((name) => values[name] !== undefined)) throw new Error('sync requires at least one desired-state option');
102
- finish(syncPlatformIssue(taskRef, {
101
+ finish(await syncPlatformIssue(taskRef, {
103
102
  cwd, agent: values.agent, dryRun: values.dryRun === true,
104
103
  status: typeof values.status === 'string' ? values.status : undefined,
105
104
  assignees: assignees as 'current' | 'none' | undefined,
@@ -6,9 +6,9 @@ import { listPrReviews, publishPrReview } from '../platform/pr-review.ts';
6
6
  import type { PrReviewEvent } from '../platform/pr-review.ts';
7
7
  import type { PlatformResult } from '../platform/types.ts';
8
8
 
9
- const USAGE = `Usage: agent-infra-internal platform-pr-review inspect --pr <N> [--cwd <path>]
10
- agent-infra-internal platform-pr-review list --pr <N> [--cwd <path>]
11
- agent-infra-internal platform-pr-review publish --pr <N> --scope <taskId|pr{N}> --round <N> --commit <sha> --event <COMMENT|APPROVE|REQUEST_CHANGES> --body-file <path|-> [--dry-run] [--cwd <path>]
9
+ const USAGE = `Usage: agent-infra-internal platform-pr-review inspect --pr <token> [--cwd <path>]
10
+ agent-infra-internal platform-pr-review list --pr <token> [--cwd <path>]
11
+ agent-infra-internal platform-pr-review publish --pr <token> --scope <taskId|pr{N}> --round <N> --commit <sha> --event <COMMENT|APPROVE|REQUEST_CHANGES> --body-file <path|-> [--dry-run] [--cwd <path>]
12
12
  `;
13
13
 
14
14
  const BOOLEAN_FLAGS = new Set(['--dry-run']);
@@ -50,7 +50,7 @@ function readBodyFile(value: string, cwd: string): string {
50
50
  return value === '-' ? fs.readFileSync(0, 'utf8') : fs.readFileSync(path.resolve(cwd, value), 'utf8');
51
51
  }
52
52
 
53
- function platformPrReview(args: string[] = []): void {
53
+ async function platformPrReview(args: string[] = []): Promise<void> {
54
54
  if (args[0] === '--help' || args[0] === '-h') { process.stdout.write(USAGE); return; }
55
55
  const operation = args[0];
56
56
  if (!operation || !['inspect', 'list', 'publish'].includes(operation)) { fail('a valid operation is required'); return; }
@@ -65,14 +65,14 @@ function platformPrReview(args: string[] = []): void {
65
65
  };
66
66
  const unexpected = Object.keys(values).find((name) => !allowed[operation]!.includes(name));
67
67
  if (unexpected) { fail(`${operation} does not accept --${unexpected}`); return; }
68
- const pr = Number(values.pr);
69
- if (!Number.isInteger(pr) || pr <= 0) { fail(`${operation} requires a positive --pr`); return; }
68
+ const pr = values.pr;
69
+ if (typeof pr !== 'string' || !pr) { fail(`${operation} requires --pr <token>`); return; }
70
70
  if (operation === 'inspect') {
71
- finish(inspectPlatformPullRequestByNumber(pr, { cwd }));
71
+ finish(await inspectPlatformPullRequestByNumber(pr, { cwd }));
72
72
  return;
73
73
  }
74
74
  if (operation === 'list') {
75
- finish(listPrReviews(pr, { cwd }));
75
+ finish(await listPrReviews(pr, { cwd }));
76
76
  return;
77
77
  }
78
78
  const scope = typeof values.scope === 'string' ? values.scope : '';
@@ -91,7 +91,7 @@ function platformPrReview(args: string[] = []): void {
91
91
  fail(`unable to read body file: ${error instanceof Error ? error.message : String(error)}`);
92
92
  return;
93
93
  }
94
- finish(publishPrReview({
94
+ finish(await publishPrReview({
95
95
  cwd,
96
96
  prNumber: pr,
97
97
  identity: { scope, round, commitSha: commit },