@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
@@ -0,0 +1,60 @@
1
+ import { reconcileTaskInvalidation } from '../task/invalidation-command.js';
2
+ import { resolveTaskRef } from '../task/resolve-ref.js';
3
+ import { TaskExecutionLockError, withTaskExecutionLock } from '../task/task-execution-lock.js';
4
+ const USAGE = 'Usage: agent-infra-internal task-invalidation <task-ref> reconcile [--max-targets <n>] [--dry-run]\n';
5
+ function taskInvalidation(args = []) {
6
+ if (args[0] === '--help' || args[0] === '-h') {
7
+ process.stdout.write(USAGE);
8
+ return;
9
+ }
10
+ const taskRef = args[0];
11
+ if (!taskRef || args[1] !== 'reconcile') {
12
+ process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: 'INVALIDATION_PAYLOAD_INVALID', message: 'task ref and reconcile are required' } })}\n`);
13
+ process.stderr.write(USAGE);
14
+ process.exitCode = 1;
15
+ return;
16
+ }
17
+ let maxTargets;
18
+ let dryRun = false;
19
+ for (let index = 2; index < args.length; index += 1) {
20
+ const flag = args[index];
21
+ if (flag === '--dry-run') {
22
+ if (dryRun) {
23
+ process.exitCode = 1;
24
+ return;
25
+ }
26
+ dryRun = true;
27
+ continue;
28
+ }
29
+ if (flag !== '--max-targets') {
30
+ process.exitCode = 1;
31
+ return;
32
+ }
33
+ const value = Number(args[++index]);
34
+ if (!Number.isInteger(value) || value < 1 || maxTargets !== undefined) {
35
+ process.exitCode = 1;
36
+ return;
37
+ }
38
+ maxTargets = value;
39
+ }
40
+ const resolved = resolveTaskRef(taskRef);
41
+ if (!resolved.ok) {
42
+ process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: resolved.code, message: resolved.message } })}\n`);
43
+ process.exitCode = 1;
44
+ return;
45
+ }
46
+ try {
47
+ const result = withTaskExecutionLock(resolved.repoRoot, resolved.taskId, 'task-invalidation.reconcile', () => reconcileTaskInvalidation(taskRef, { ...(maxTargets === undefined ? {} : { maxTargets }), dryRun }));
48
+ process.stdout.write(`${JSON.stringify(result)}\n`);
49
+ if (result.status === 'failed')
50
+ process.exitCode = 1;
51
+ }
52
+ catch (error) {
53
+ if (!(error instanceof TaskExecutionLockError))
54
+ throw error;
55
+ process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: error.code, message: error.message } })}\n`);
56
+ process.exitCode = 1;
57
+ }
58
+ }
59
+ export { taskInvalidation };
60
+ //# sourceMappingURL=task-invalidation.js.map
@@ -4,11 +4,13 @@ import { consumeHumanOverride, failureId, overrideDryRunConflict } from '../task
4
4
  import { LEDGER_SECTION_MISSING_CODE, LEDGER_SECTION_MISSING_MESSAGE, isReviewStage, parseLedgerDocument, summarizeLedgerStage, validateLedgerRows } from '../task/ledger.js';
5
5
  import { resolveTaskRef } from '../task/resolve-ref.js';
6
6
  import { TaskExecutionLockError, withTaskExecutionLock } from '../task/task-execution-lock.js';
7
- const USAGE = `Usage: agent-infra-internal task-ledger <task-ref> <intent> [intent flags] [--dry-run]\n\nIntents: finding-upsert, finding-respond, finding-review, decision-next-id, decision-upsert, stage-status\n`;
7
+ const USAGE = `Usage: agent-infra-internal task-ledger <task-ref> <intent> [intent flags] [--dry-run]\n\nIntents: finding-upsert, finding-respond, finding-review, decision-next-id, decision-upsert, rework-intent-upsert, stage-status\n`;
8
8
  const FLAGS = {
9
9
  '--stage': 'stage', '--review-artifact': 'reviewArtifact', '--ordinal': 'ordinal',
10
10
  '--severity': 'severity', '--evidence': 'evidence', '--id': 'id', '--round': 'round',
11
11
  '--status': 'status', '--artifact': 'artifact', '--needs-implementation': 'needsImplementation',
12
+ '--intent-id': 'intentId', '--finding-id': 'findingId', '--source-artifact': 'sourceArtifact',
13
+ '--source-sha256': 'sourceSha256', '--target': 'target',
12
14
  '--override-ticket': 'overrideTicket', '--override-target': 'overrideTarget', '--override-scope': 'overrideScope'
13
15
  };
14
16
  const NUMERIC = new Set(['ordinal', 'round']);
@@ -18,7 +20,7 @@ function usageFailure(message) {
18
20
  process.stderr.write(USAGE);
19
21
  process.exitCode = 1;
20
22
  }
21
- function taskLedger(args = []) {
23
+ async function taskLedger(args = []) {
22
24
  if (args[0] === '--help' || args[0] === '-h') {
23
25
  process.stdout.write(USAGE);
24
26
  return;
@@ -28,7 +30,7 @@ function taskLedger(args = []) {
28
30
  usageFailure('task ref and intent are required');
29
31
  return;
30
32
  }
31
- if (!['finding-upsert', 'finding-respond', 'finding-review', 'decision-next-id', 'decision-upsert', 'stage-status'].includes(kind)) {
33
+ if (!['finding-upsert', 'finding-respond', 'finding-review', 'decision-next-id', 'decision-upsert', 'rework-intent-upsert', 'stage-status'].includes(kind)) {
32
34
  usageFailure(`unknown ledger intent '${kind}'`);
33
35
  return;
34
36
  }
@@ -72,13 +74,14 @@ function taskLedger(args = []) {
72
74
  'finding-review': ['id', 'status', 'evidence'],
73
75
  'decision-next-id': [],
74
76
  'decision-upsert': ['id', 'stage', 'artifact'],
77
+ 'rework-intent-upsert': ['intentId', 'findingId', 'sourceArtifact', 'sourceSha256', 'target'],
75
78
  'stage-status': ['stage']
76
79
  };
77
80
  const optional = {
78
81
  'finding-review': ['needsImplementation'],
79
82
  'decision-upsert': ['needsImplementation']
80
83
  };
81
- const overrideFields = kind === 'stage-status' || kind === 'decision-next-id'
84
+ const overrideFields = kind === 'stage-status' || kind === 'decision-next-id' || kind === 'rework-intent-upsert'
82
85
  ? []
83
86
  : ['overrideTicket', 'overrideTarget', 'overrideScope'];
84
87
  const allowed = new Set(['kind', 'taskRef', 'dryRun', ...required[kind], ...(optional[kind] ?? []), ...overrideFields]);
@@ -139,7 +142,7 @@ function taskLedger(args = []) {
139
142
  let result;
140
143
  let humanOverride = null;
141
144
  try {
142
- result = withTaskExecutionLock(lockResolved.repoRoot, lockResolved.taskId, `task-ledger.${kind}`, () => {
145
+ result = await withTaskExecutionLock(lockResolved.repoRoot, lockResolved.taskId, `task-ledger.${kind}`, async () => {
143
146
  let current = applyLedgerIntent(values);
144
147
  if (current.status !== 'failed' || !values.overrideTicket)
145
148
  return current;
@@ -147,7 +150,7 @@ function taskLedger(args = []) {
147
150
  usageFailure('override ticket requires target and scope');
148
151
  return current;
149
152
  }
150
- const consumed = consumeHumanOverride({
153
+ const consumed = await consumeHumanOverride({
151
154
  taskRef,
152
155
  ticketId: String(values.overrideTicket),
153
156
  failureId: failureId('ledger-intent', current.error?.code ?? 'TASK_STATE_MISMATCH'),
@@ -11,7 +11,7 @@ function parseFailure(error) {
11
11
  const message = error instanceof Error ? error.message : String(error);
12
12
  return message.replace(/^TASK_CONTROL_OPERATION_INVALID: /u, '');
13
13
  }
14
- function taskLifecycle(args = []) {
14
+ async function taskLifecycle(args = []) {
15
15
  if (args[0] === '--help' || args[0] === '-h') {
16
16
  process.stdout.write(USAGE);
17
17
  return;
@@ -49,7 +49,7 @@ function taskLifecycle(args = []) {
49
49
  }
50
50
  repoRoot = resolved.repoRoot;
51
51
  }
52
- const result = dispatchTaskControlOperation(createDirectHostExecutionContext({ repoRoot }), operation);
52
+ const result = await dispatchTaskControlOperation(createDirectHostExecutionContext({ repoRoot }), operation);
53
53
  process.stdout.write(`${JSON.stringify(result)}\n`);
54
54
  if (result.status === 'failed')
55
55
  process.exitCode = 1;
@@ -28,7 +28,7 @@ function usageFailure(message) {
28
28
  process.stderr.write(USAGE);
29
29
  process.exitCode = 1;
30
30
  }
31
- function taskOverride(args = []) {
31
+ async function taskOverride(args = []) {
32
32
  if (args[0] === '--help' || args[0] === '-h') {
33
33
  process.stdout.write(USAGE);
34
34
  return;
@@ -79,10 +79,10 @@ function taskOverride(args = []) {
79
79
  let result;
80
80
  try {
81
81
  if (operation === 'diagnose') {
82
- result = diagnoseHumanOverrideForTask(taskRef, values.failureId ? String(values.failureId) : undefined, values.target ? String(values.target) : undefined);
82
+ result = await diagnoseHumanOverrideForTask(taskRef, values.failureId ? String(values.failureId) : undefined, values.target ? String(values.target) : undefined);
83
83
  }
84
84
  else if (operation === 'issue') {
85
- result = withTaskExecutionLock(resolved.repoRoot, resolved.taskId, 'task-override.issue', () => issueHumanOverride({
85
+ result = await withTaskExecutionLock(resolved.repoRoot, resolved.taskId, 'task-override.issue', async () => issueHumanOverride({
86
86
  taskRef,
87
87
  failureId: String(values.failureId),
88
88
  target: String(values.target),
@@ -99,7 +99,7 @@ function taskOverride(args = []) {
99
99
  }));
100
100
  }
101
101
  else {
102
- result = withTaskExecutionLock(resolved.repoRoot, resolved.taskId, 'task-override.consume', () => consumeHumanOverride({
102
+ result = await withTaskExecutionLock(resolved.repoRoot, resolved.taskId, 'task-override.consume', async () => consumeHumanOverride({
103
103
  taskRef,
104
104
  ticketId: String(values.ticketId),
105
105
  failureId: String(values.failureId),
@@ -13,7 +13,7 @@ function failUsage(message) {
13
13
  process.stderr.write(USAGE);
14
14
  process.exitCode = 2;
15
15
  }
16
- function taskReview(args = []) {
16
+ async function taskReview(args = []) {
17
17
  if (args[0] === '--help' || args[0] === '-h') {
18
18
  process.stdout.write(USAGE);
19
19
  return;
@@ -86,7 +86,7 @@ function taskReview(args = []) {
86
86
  let result;
87
87
  let humanOverride = null;
88
88
  try {
89
- result = withTaskExecutionLock(resolved.repoRoot, resolved.taskId, 'task-review.finalize-summary', () => {
89
+ result = await withTaskExecutionLock(resolved.repoRoot, resolved.taskId, 'task-review.finalize-summary', async () => {
90
90
  let current = finalizeReviewSummary({ taskRef: args[0], stage, artifact, orchestrated, dryRun }, { lockAlreadyHeld: true });
91
91
  if (current.status !== 'failed' || !overrideTicket)
92
92
  return current;
@@ -94,7 +94,7 @@ function taskReview(args = []) {
94
94
  failUsage('override ticket requires target and scope');
95
95
  return current;
96
96
  }
97
- const consumed = consumeHumanOverride({
97
+ const consumed = await consumeHumanOverride({
98
98
  taskRef: args[0], ticketId: overrideTicket,
99
99
  failureId: failureId('review-finalization', current.error?.code ?? 'TASK_STATE_MISMATCH'),
100
100
  target: overrideTarget, scope: overrideScope
@@ -8,7 +8,7 @@ function fail(message) {
8
8
  process.stderr.write(USAGE);
9
9
  process.exitCode = 1;
10
10
  }
11
- function taskVerify(args = []) {
11
+ async function taskVerify(args = []) {
12
12
  if (args[0] === '--help' || args[0] === '-h') {
13
13
  process.stdout.write(USAGE);
14
14
  return;
@@ -64,8 +64,8 @@ function taskVerify(args = []) {
64
64
  let result;
65
65
  let humanOverride = null;
66
66
  try {
67
- result = withTaskExecutionLock(resolved.repoRoot, resolved.taskId, `task-verify.${event}`, () => {
68
- let current = verifyTaskEvent({ taskRef, event, ...(artifact ? { artifact } : {}) });
67
+ result = await withTaskExecutionLock(resolved.repoRoot, resolved.taskId, `task-verify.${event}`, async () => {
68
+ let current = await verifyTaskEvent({ taskRef, event, ...(artifact ? { artifact } : {}) });
69
69
  if (current.status === 'pass' || !overrideTicket)
70
70
  return current;
71
71
  if (!overrideTarget || !overrideScope) {
@@ -80,13 +80,13 @@ function taskVerify(args = []) {
80
80
  : current.status === 'fail'
81
81
  ? 'CHECK_FAILED'
82
82
  : current.error?.code ?? 'VERIFY_TASK_STATE_MISMATCH';
83
- const consumed = consumeHumanOverride({
83
+ const consumed = await consumeHumanOverride({
84
84
  taskRef, ticketId: overrideTicket,
85
85
  failureId: failureId(failureProducer, failureCode),
86
86
  target: overrideTarget, scope: overrideScope
87
87
  }, {
88
- effectExecutor: (capability) => {
89
- const retried = verifyTaskEvent({ taskRef, event, ...(artifact ? { artifact } : {}) }, { manualOverride: capability });
88
+ effectExecutor: async (capability) => {
89
+ const retried = await verifyTaskEvent({ taskRef, event, ...(artifact ? { artifact } : {}) }, { manualOverride: capability });
90
90
  current = retried;
91
91
  return retried.status !== 'pass'
92
92
  ? { code: 'OVERRIDE_EFFECT_FAILED', message: `${retried.error?.code ?? 'VERIFY_FAILED'}: ${retried.error?.message ?? 'manual verification effect failed'}` }
@@ -13,7 +13,7 @@ function usageFailure(message) {
13
13
  process.stderr.write(USAGE);
14
14
  process.exitCode = 1;
15
15
  }
16
- function taskWarning(args = []) {
16
+ async function taskWarning(args = []) {
17
17
  if (args[0] === '--help' || args[0] === '-h') {
18
18
  process.stdout.write(USAGE);
19
19
  return;
@@ -85,7 +85,7 @@ function taskWarning(args = []) {
85
85
  let result;
86
86
  let humanOverride = null;
87
87
  try {
88
- result = withTaskExecutionLock(resolved.repoRoot, resolved.taskId, `task-warning.${kind}`, () => {
88
+ result = await withTaskExecutionLock(resolved.repoRoot, resolved.taskId, `task-warning.${kind}`, async () => {
89
89
  let current = applyWorkflowWarningIntent(values);
90
90
  if (current.status !== 'failed' || !values.overrideTicket)
91
91
  return current;
@@ -93,7 +93,7 @@ function taskWarning(args = []) {
93
93
  usageFailure('override ticket requires target and scope');
94
94
  return current;
95
95
  }
96
- const consumed = consumeHumanOverride({
96
+ const consumed = await consumeHumanOverride({
97
97
  taskRef, ticketId: String(values.overrideTicket),
98
98
  failureId: failureId('workflow-warning', current.error?.code ?? 'TASK_STATE_MISMATCH'),
99
99
  target: String(values.overrideTarget), scope: String(values.overrideScope)
@@ -8,9 +8,7 @@ function getPlatformAdapter(type) {
8
8
  return type ? adapters.get(type) ?? null : null;
9
9
  }
10
10
  function registerPlatformCapabilities(type, capabilities) {
11
- const adapter = adapters.get(type);
12
- if (!adapter)
13
- throw new Error(`Platform adapter '${type}' must be registered before its capabilities`);
11
+ const adapter = adapters.get(type) ?? { type };
14
12
  adapters.set(type, { ...adapter, ...capabilities });
15
13
  }
16
14
  function hasPlatformCapability(type, capability) {
@@ -2,7 +2,9 @@ import fs from "node:fs";
2
2
  import os from "node:os";
3
3
  import path from "node:path";
4
4
  import { execFileSync, spawnSync } from "node:child_process";
5
- import { resolvePlatformChangeRequestGitEvidence } from "./adapters.js";
5
+ import { resolvePlatformProviderContext } from './context.js';
6
+ import { providerError, providerOperationContext, unsupportedProviderOperation } from './provider-bridge.js';
7
+ import { isResourceIdentity } from './resource-identity.js';
6
8
  import { resolveReviewedHeadRelation } from "./merged-pr-equivalence.js";
7
9
  const SHA_PATTERN = /^[0-9a-f]{40}$/i;
8
10
  const REVIEWED_HEAD_REF = "refs/agent-infra/reviewed-head";
@@ -37,18 +39,36 @@ function validMergeIdentity(lastReviewedCommit, pullRequest) {
37
39
  }
38
40
  return null;
39
41
  }
40
- function resolveMaterializedReviewedHeadRelation(input) {
42
+ async function resolveMaterializedReviewedHeadRelation(input) {
41
43
  const identityFailure = validMergeIdentity(input.lastReviewedCommit, input.pullRequest);
42
44
  if (identityFailure)
43
45
  return identityFailure;
44
- const source = resolvePlatformChangeRequestGitEvidence(input.platformType, {
46
+ const loaded = await resolvePlatformProviderContext({
45
47
  cwd: input.cwd,
46
- repository: input.pullRequest.repository,
47
- number: input.pullRequest.number,
48
- pullRequest: input.pullRequest
48
+ platformType: input.platformType || undefined
49
49
  });
50
+ if (!loaded.ok) {
51
+ return blocked(loaded.context.error?.code || 'PR_MERGE_EVIDENCE_SOURCE_UNAVAILABLE', loaded.context.error?.message || 'Unable to resolve the configured platform provider');
52
+ }
53
+ const target = isResourceIdentity(input.pullRequest.identity)
54
+ ? input.pullRequest.identity
55
+ : { kind: 'number', value: input.pullRequest.number };
56
+ const operation = loaded.value.provider.changeRequests?.resolveGitEvidence
57
+ ? await loaded.value.provider.changeRequests.resolveGitEvidence({
58
+ context: providerOperationContext(loaded.value),
59
+ target,
60
+ expected: {
61
+ baseSha: input.pullRequest.base.sha,
62
+ headSha: input.pullRequest.head.sha,
63
+ targetBranch: input.pullRequest.base.ref
64
+ }
65
+ })
66
+ : unsupportedProviderOperation(loaded.value.provider, 'changeRequests.resolveGitEvidence');
67
+ const source = operation.ok
68
+ ? operation
69
+ : { ok: false, error: providerError(operation.error, 'PR_MERGE_EVIDENCE_SOURCE_UNAVAILABLE') };
50
70
  if (!source.ok || !source.value) {
51
- return blocked(source.error?.code || "PR_MERGE_EVIDENCE_SOURCE_UNAVAILABLE", source.error?.message || "Unable to resolve authoritative PR Git evidence");
71
+ return blocked(('error' in source && source.error?.code) || "PR_MERGE_EVIDENCE_SOURCE_UNAVAILABLE", ('error' in source && source.error?.message) || "Unable to resolve authoritative PR Git evidence");
52
72
  }
53
73
  if (!source.value.remoteUrl.trim() ||
54
74
  source.value.remoteUrl.trimStart().startsWith("-") ||
@@ -9,6 +9,7 @@ import { applyWorkflowWarningIntent } from '../task/workflow-warning-intents.js'
9
9
  import { captureTaskWriteMetadata } from '../task/write.js';
10
10
  import { syncPlatformComment } from './issue-comments.js';
11
11
  import { platformResult } from './types.js';
12
+ import { taskIssueIdentity } from './task-identities.js';
12
13
  function result(base, artifacts, warnings) {
13
14
  return { ...base, artifacts, warnings };
14
15
  }
@@ -29,7 +30,7 @@ function excludedPrReview(taskDir, message) {
29
30
  return null;
30
31
  }
31
32
  }
32
- function backfillCompletionComments(taskRef, options) {
33
+ async function backfillCompletionComments(taskRef, options) {
33
34
  const resolved = resolveTaskRef(taskRef, options.cwd ? { repoRoot: options.cwd } : {});
34
35
  if (!resolved.ok)
35
36
  return result(platformResult('failed', {
@@ -45,16 +46,16 @@ function backfillCompletionComments(taskRef, options) {
45
46
  }
46
47
  }), [], []);
47
48
  const initialContent = fs.readFileSync(resolved.taskMdPath, 'utf8');
48
- const issueNumber = Number(parseTaskFrontmatter(initialContent).issue_number);
49
- if (!Number.isInteger(issueNumber) || issueNumber <= 0)
49
+ const issueIdentity = taskIssueIdentity(parseTaskFrontmatter(initialContent));
50
+ if (!issueIdentity)
50
51
  return result(platformResult('no-op', {
51
- error: { code: 'ISSUE_NOT_LINKED', message: 'Task has no valid issue_number', retryable: false }
52
+ error: { code: 'ISSUE_NOT_LINKED', message: 'Task has no valid platform issue identity', retryable: false }
52
53
  }), [], []);
53
54
  const artifacts = [];
54
55
  const operations = [];
55
56
  let latest = platformResult('no-op', { error: null });
56
57
  for (const artifact of inventory.artifacts) {
57
- latest = syncPlatformComment(resolved.taskId, {
58
+ latest = await syncPlatformComment(resolved.taskId, {
58
59
  kind: 'artifact',
59
60
  artifact: artifact.name,
60
61
  agent: options.agent,
@@ -1,148 +1,88 @@
1
- import fs from 'node:fs';
2
1
  import path from 'node:path';
3
- import { execFileSync } from 'node:child_process';
4
- import semver from 'semver';
5
- import { getPlatformAdapter, registerPlatformAdapter } from './adapters.js';
6
- import { createGitHubClient, MINIMUM_GITHUB_CLI_VERSION } from './github-client.js';
2
+ import { loadPlatformProvider } from './provider-loader.js';
3
+ import { defaultGitRemote, parseGitHubRemote } from './github-provider.js';
7
4
  import { platformResult } from './types.js';
8
- const CURRENT_USER_QUERY = 'query { viewer { login } }';
9
- function findRepoRoot(cwd) {
10
- try {
11
- return execFileSync('git', ['rev-parse', '--show-toplevel'], {
12
- cwd, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe']
13
- }).trim();
14
- }
15
- catch {
16
- return cwd;
17
- }
18
- }
19
- function parseGitHubRemote(remote) {
20
- const trimmed = remote.trim().replace(/\.git$/, '');
21
- const match = trimmed.match(/^(?:https?:\/\/github\.com\/|ssh:\/\/(?:git@)?github\.com\/|git@github\.com:)([^/\s]+\/[^/\s]+)$/i);
22
- return match?.[1] || null;
23
- }
24
- function defaultGitRemote(cwd) {
25
- try {
26
- return execFileSync('git', ['remote', 'get-url', 'origin'], {
27
- cwd, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe']
28
- }).trim();
29
- }
30
- catch {
31
- return null;
32
- }
5
+ function errorStatus(error) {
6
+ if (error.retryable || error.code === 'AUTH_REQUIRED' || error.code === 'PLATFORM_DEPENDENCY_MISSING')
7
+ return 'blocked';
8
+ if (error.code === 'REMOTE_MISSING' || error.code === 'PLATFORM_UNSUPPORTED')
9
+ return 'no-op';
10
+ return 'failed';
33
11
  }
34
- function readPlatformType(repoRoot) {
35
- try {
36
- const config = JSON.parse(fs.readFileSync(path.join(repoRoot, '.agents', '.airc.json'), 'utf8'));
37
- return typeof config?.platform?.type === 'string' ? config.platform.type : null;
38
- }
39
- catch {
40
- return null;
41
- }
42
- }
43
- function failure(error, repo) {
44
- const blocked = error.retryable || error.code === 'AUTH_REQUIRED' || error.code === 'PLATFORM_DEPENDENCY_MISSING';
45
- return platformResult(blocked ? 'blocked' : 'failed', {
46
- platform: { type: 'github', repository: repo, currentUser: null }, error
12
+ function contextError(providerType, error, repository = null) {
13
+ return platformResult(errorStatus(error), {
14
+ platform: { type: providerType || null, repository, currentUser: null },
15
+ error: { code: error.code, message: error.message, retryable: error.retryable }
47
16
  });
48
17
  }
49
- function resolveGitHubContext(options) {
50
- const { cwd } = options;
51
- const platform = 'github';
52
- const client = options.client || createGitHubClient();
53
- const version = client.version({ cwd });
54
- if (!version.ok)
55
- return failure(version.error, null);
56
- if (!semver.gte(version.value, MINIMUM_GITHUB_CLI_VERSION)) {
57
- return failure({
58
- code: 'GH_CLI_VERSION_UNSUPPORTED',
59
- message: `GitHub CLI ${version.value} is unsupported; install gh >= ${MINIMUM_GITHUB_CLI_VERSION}`,
60
- retryable: false
61
- }, null);
62
- }
63
- const remote = (options.gitRemote || defaultGitRemote)(cwd);
64
- if (!remote) {
18
+ function contextFromSnapshot(providerType, snapshot) {
19
+ if (providerType === 'none') {
65
20
  return platformResult('no-op', {
66
- platform: { type: platform, repository: null, currentUser: null },
67
- error: { code: 'REMOTE_MISSING', message: 'Git origin remote is not configured', retryable: false }
68
- });
69
- }
70
- const ownerRepo = parseGitHubRemote(remote);
71
- if (!ownerRepo) {
72
- const nonGitHub = !/github\.com/i.test(remote);
73
- return platformResult(nonGitHub ? 'no-op' : 'failed', {
74
- platform: { type: platform, repository: null, currentUser: null },
75
- error: {
76
- code: nonGitHub ? 'PLATFORM_UNSUPPORTED' : 'REMOTE_INVALID',
77
- message: `Unable to parse GitHub owner/repo from '${remote}'`,
78
- retryable: false
79
- }
80
- });
81
- }
82
- const repository = client.json(['api', `repos/${ownerRepo}`], { cwd });
83
- if (!repository.ok)
84
- return failure(repository.error, null);
85
- const repositoryValue = repository.value;
86
- const upstream = repositoryValue?.fork ? repositoryValue.parent?.full_name : repositoryValue?.full_name;
87
- if (!upstream) {
88
- return platformResult('blocked', {
89
- platform: { type: platform, repository: null, currentUser: null },
90
- error: { code: 'UPSTREAM_UNRESOLVED', message: 'Unable to resolve the upstream repository', retryable: false }
21
+ platform: { type: 'none', repository: null, currentUser: null },
22
+ operations: [{ name: 'resolve', status: 'no-op', reasonCode: 'PLATFORM_DISABLED' }]
91
23
  });
92
24
  }
93
- const user = client.json(['api', 'graphql', '-f', `query=${CURRENT_USER_QUERY}`], { cwd });
94
- if (!user.ok)
95
- return failure(user.error, upstream);
96
- const userValue = user.value;
97
- const currentUser = userValue?.data?.viewer?.login || null;
98
- const permissions = client.json(['api', `repos/${upstream}`], { cwd });
99
- if (!permissions.ok)
100
- return failure(permissions.error, upstream);
101
- const permissionValue = permissions.value;
102
- const values = permissionValue?.permissions || {};
103
- const capabilities = {
104
- authenticated: Boolean(currentUser),
105
- comment: Boolean(currentUser),
106
- triage: Boolean(values.triage || values.push || values.admin),
107
- push: Boolean(values.push || values.admin),
108
- admin: Boolean(values.admin)
109
- };
25
+ const capabilities = snapshot.capabilities;
110
26
  const status = Object.values(capabilities).every(Boolean) ? 'no-op' : 'degraded';
27
+ const repository = snapshot.scope.label || snapshot.scope.id || null;
111
28
  return platformResult(status, {
112
- changed: false,
113
- platform: { type: platform, repository: upstream, currentUser },
29
+ platform: {
30
+ type: snapshot.type || providerType,
31
+ repository,
32
+ currentUser: snapshot.currentUser?.name || snapshot.currentUser?.id || null
33
+ },
114
34
  resource: { kind: 'repository', number: null },
115
35
  capabilities,
116
36
  operations: [{ name: 'resolve', status: 'no-op', reasonCode: null }]
117
37
  });
118
38
  }
119
- registerPlatformAdapter({
120
- type: 'github',
121
- resolveContext(options) {
122
- return resolveGitHubContext(options);
123
- }
124
- });
125
- registerPlatformAdapter({
126
- type: 'none',
127
- resolveContext() {
128
- return platformResult('no-op', {
129
- platform: { type: 'none', repository: null, currentUser: null },
130
- operations: [{ name: 'resolve', status: 'no-op', reasonCode: 'PLATFORM_DISABLED' }]
131
- });
132
- }
133
- });
134
- function resolvePlatformContext(options = {}) {
135
- const cwd = path.resolve(options.cwd || process.cwd());
136
- const repoRoot = findRepoRoot(cwd);
137
- const platform = options.platformType ?? readPlatformType(repoRoot);
138
- const adapter = getPlatformAdapter(platform);
139
- if (!adapter) {
140
- return platformResult('no-op', {
141
- platform: { type: platform, repository: null, currentUser: null },
142
- error: { code: 'PLATFORM_UNSUPPORTED', message: `Platform '${platform || 'none'}' has no registered adapter`, retryable: false }
143
- });
39
+ async function resolvePlatformProviderContext(options = {}) {
40
+ const workingDirectory = path.resolve(options.cwd || process.cwd());
41
+ const loaded = await loadPlatformProvider({
42
+ cwd: workingDirectory,
43
+ platformType: options.platformType,
44
+ client: options.client
45
+ });
46
+ if (!loaded.ok) {
47
+ if (!loaded.error.providerType && options.platformType === undefined) {
48
+ return {
49
+ ok: false,
50
+ context: platformResult('no-op', {
51
+ platform: { type: null, repository: null, currentUser: null },
52
+ error: {
53
+ code: 'PLATFORM_UNSUPPORTED',
54
+ message: 'No platform provider is configured',
55
+ retryable: false
56
+ }
57
+ })
58
+ };
59
+ }
60
+ return { ok: false, context: contextError(loaded.error.providerType || options.platformType || '', loaded.error) };
144
61
  }
145
- return adapter.resolveContext({ cwd, gitRemote: options.gitRemote, client: options.client });
62
+ const gitRemote = options.gitRemote ? options.gitRemote(workingDirectory) : null;
63
+ const resolved = await loaded.value.provider.context.resolve({
64
+ repositoryRoot: loaded.value.repositoryRoot,
65
+ workingDirectory,
66
+ scopeId: loaded.value.repositoryRoot,
67
+ gitRemote
68
+ });
69
+ if (!resolved.ok)
70
+ return {
71
+ ok: false,
72
+ context: contextError(loaded.value.providerType, resolved.error)
73
+ };
74
+ return {
75
+ ok: true,
76
+ value: {
77
+ ...loaded.value,
78
+ snapshot: resolved.value,
79
+ context: contextFromSnapshot(loaded.value.providerType, resolved.value)
80
+ }
81
+ };
82
+ }
83
+ async function resolvePlatformContext(options = {}) {
84
+ const resolved = await resolvePlatformProviderContext(options);
85
+ return resolved.ok ? resolved.value.context : resolved.context;
146
86
  }
147
- export { parseGitHubRemote, resolvePlatformContext };
87
+ export { defaultGitRemote, parseGitHubRemote, resolvePlatformContext, resolvePlatformProviderContext };
148
88
  //# sourceMappingURL=context.js.map
@@ -125,7 +125,7 @@ function createGitHubClient(options = {}) {
125
125
  const sleep = options.sleep || defaultSleep;
126
126
  function run(args, request = {}) {
127
127
  const method = request.method || 'GET';
128
- const retryableMethod = method === 'GET' || method === 'PATCH';
128
+ const retryableMethod = method === 'GET' || method === 'PATCH' || method === 'PUT';
129
129
  let attempt = 0;
130
130
  while (true) {
131
131
  const result = runner(args, request);