@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,143 @@
1
+ import { computeInLabels } from './metadata-labels.js';
2
+ function inLabels(labels) {
3
+ return [...new Set(labels.filter((label) => label.startsWith('in:')))].sort();
4
+ }
5
+ function mergeInLabels(labels, target) {
6
+ return [...new Set([
7
+ ...labels.filter((label) => !label.startsWith('in:')),
8
+ ...target
9
+ ])].sort();
10
+ }
11
+ function invalidInLabelInput(code, message) {
12
+ return { ok: false, error: { code, message, retryable: false } };
13
+ }
14
+ function validateInLabelMapping(value) {
15
+ if (value === undefined)
16
+ return { ok: true, value: {} };
17
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
18
+ return invalidInLabelInput('IN_LABEL_SYNC_MAPPING_INVALID', 'labels.in must be an object of non-empty string arrays');
19
+ }
20
+ const normalized = {};
21
+ for (const [name, rawPrefixes] of Object.entries(value)) {
22
+ if (!name.trim() || !Array.isArray(rawPrefixes) || rawPrefixes.length === 0 || rawPrefixes.some((prefix) => typeof prefix !== 'string' || !prefix.trim())) {
23
+ return invalidInLabelInput('IN_LABEL_SYNC_MAPPING_INVALID', `labels.in entry '${name}' must be a non-empty string array`);
24
+ }
25
+ normalized[name] = rawPrefixes.map((prefix) => prefix.trim());
26
+ }
27
+ return { ok: true, value: normalized };
28
+ }
29
+ function repositoryLabelPages(value) {
30
+ if (!Array.isArray(value))
31
+ return null;
32
+ if (value.length === 0)
33
+ return [];
34
+ if (value.every((page) => Array.isArray(page)))
35
+ return value;
36
+ if (value.every((entry) => entry && typeof entry === 'object' && !Array.isArray(entry)))
37
+ return [value];
38
+ return null;
39
+ }
40
+ function validateRepositoryLabelPayload(value) {
41
+ const pages = repositoryLabelPages(value);
42
+ if (!pages)
43
+ return invalidInLabelInput('IN_LABEL_SYNC_LABELS_INVALID', 'Repository labels response must be an array of pages');
44
+ const names = [];
45
+ for (const page of pages) {
46
+ for (const entry of page) {
47
+ if (!entry || typeof entry !== 'object' || Array.isArray(entry) || typeof entry.name !== 'string' || !entry.name.trim()) {
48
+ return invalidInLabelInput('IN_LABEL_SYNC_LABELS_INVALID', 'Repository labels response contains an entry without a valid name');
49
+ }
50
+ names.push(entry.name);
51
+ }
52
+ }
53
+ return { ok: true, value: [...new Set(names)].sort() };
54
+ }
55
+ function validateRepositoryLabelSet(value) {
56
+ if (!(value instanceof Set) || [...value].some((label) => typeof label !== 'string' || !label.trim())) {
57
+ return invalidInLabelInput('IN_LABEL_SYNC_LABELS_INVALID', 'Repository labels must be a set of non-empty names');
58
+ }
59
+ return { ok: true, value: new Set([...value]) };
60
+ }
61
+ function planInLabelUpdate(input) {
62
+ const current = inLabels(input.currentLabels);
63
+ const mapping = validateInLabelMapping(input.mapping);
64
+ const repositoryLabels = validateRepositoryLabelSet(input.repositoryLabels);
65
+ if (!mapping.ok)
66
+ return { current, target: [...current], labels: [...input.currentLabels].sort(), changed: false, error: mapping.error };
67
+ if (!repositoryLabels.ok)
68
+ return { current, target: [...current], labels: [...input.currentLabels].sort(), changed: false, error: repositoryLabels.error };
69
+ const target = computeInLabels([...input.changedFiles], mapping.value, repositoryLabels.value);
70
+ const labels = mergeInLabels(input.currentLabels, target);
71
+ return { current, target, labels, changed: labels.join('\0') !== [...input.currentLabels].sort().join('\0'), error: null };
72
+ }
73
+ function startsWithPrefix(label, prefix) {
74
+ return (Array.isArray(prefix) ? prefix : [prefix]).some((value) => label.startsWith(value));
75
+ }
76
+ function labelDelta(current, target, prefix = 'in:') {
77
+ const currentLabels = [...new Set(current)].sort();
78
+ const targetLabels = [...new Set(target.filter((label) => startsWithPrefix(label, prefix)))].sort();
79
+ return {
80
+ add: targetLabels.filter((label) => !currentLabels.includes(label)),
81
+ remove: currentLabels.filter((label) => startsWithPrefix(label, prefix) && !targetLabels.includes(label))
82
+ };
83
+ }
84
+ function syncLabelDelta(client, repository, number, cwd, current, target, prefix = 'in:') {
85
+ const delta = labelDelta(current, target, prefix);
86
+ let changed = false;
87
+ const failure = (error) => {
88
+ if (changed || error.retryable) {
89
+ return {
90
+ status: 'blocked',
91
+ changed,
92
+ error: { ...error, code: 'IN_LABEL_SYNC_PARTIAL', message: `In-label synchronization is partial or unknown: ${error.message}` }
93
+ };
94
+ }
95
+ return { status: 'failed', changed: false, error };
96
+ };
97
+ for (const label of delta.remove) {
98
+ const removed = client.json([
99
+ 'api', `repos/${repository}/issues/${number}/labels/${encodeURIComponent(label)}`, '-X', 'DELETE'
100
+ ], { cwd, method: 'DELETE' });
101
+ if (!removed.ok)
102
+ return failure(removed.error);
103
+ changed = true;
104
+ }
105
+ for (const label of delta.add) {
106
+ const added = client.json([
107
+ 'api', `repos/${repository}/issues/${number}/labels`, '-X', 'POST', '--input', '-'
108
+ ], { cwd, method: 'POST', input: JSON.stringify({ labels: [label] }) });
109
+ if (!added.ok)
110
+ return failure(added.error);
111
+ changed = true;
112
+ }
113
+ return { status: changed ? 'applied' : 'no-op', changed, error: null };
114
+ }
115
+ function flattenPages(value) {
116
+ if (!Array.isArray(value))
117
+ return [];
118
+ return value.flatMap((page) => Array.isArray(page) ? page : [page]);
119
+ }
120
+ function extractRepositoryLabelNames(value) {
121
+ return flattenPages(value).flatMap((entry) => {
122
+ if (typeof entry === 'string')
123
+ return [entry];
124
+ if (entry && typeof entry === 'object' && typeof entry.name === 'string') {
125
+ return [entry.name];
126
+ }
127
+ return [];
128
+ }).filter(Boolean).sort();
129
+ }
130
+ function extractPullRequestFileNames(value) {
131
+ const entries = flattenPages(value);
132
+ if (!Array.isArray(value) || entries.some((entry) => !entry || typeof entry !== 'object'))
133
+ return null;
134
+ const names = entries.map((entry) => entry.filename);
135
+ if (names.some((name) => typeof name !== 'string' || !name.trim()))
136
+ return null;
137
+ return [...new Set(names)].sort();
138
+ }
139
+ function resourceSnapshot(kind, number, labels) {
140
+ return { kind, number, labels: [...labels].sort() };
141
+ }
142
+ export { extractPullRequestFileNames, extractRepositoryLabelNames, flattenPages, inLabels, labelDelta, mergeInLabels, planInLabelUpdate, resourceSnapshot, syncLabelDelta, validateInLabelMapping, validateRepositoryLabelPayload };
143
+ //# sourceMappingURL=in-label-sync.js.map
@@ -2,9 +2,14 @@ import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { parseTaskFrontmatter } from '../task/frontmatter.js';
4
4
  import { resolveTaskRef } from '../task/resolve-ref.js';
5
- import { createGitHubClient } from './github-client.js';
6
- import { resolvePlatformContext } from './context.js';
5
+ import { resolvePlatformProviderContext } from './context.js';
7
6
  import { platformResult } from './types.js';
7
+ import { providerError, providerOperationContext, providerStatus, providerResourceToken, unsupportedProviderOperation } from './provider-bridge.js';
8
+ import { resourceIdentityNumber } from './resource-identity.js';
9
+ import { taskIssueIdentity, taskIssueIdentityError } from './task-identities.js';
10
+ function providerCommentId(id, provider) {
11
+ return provider.identity?.comment === 'number' && /^\d+$/.test(id) ? Number(id) : id;
12
+ }
8
13
  const MARKERS = {
9
14
  task: (taskId) => `<!-- sync-issue:${taskId}:task -->`,
10
15
  artifact: (taskId, stem) => `<!-- sync-issue:${taskId}:${stem} -->`,
@@ -137,7 +142,7 @@ function flattenComments(value) {
137
142
  if (!Array.isArray(value))
138
143
  return [];
139
144
  const flattened = value.flatMap((entry) => Array.isArray(entry) ? entry : [entry]);
140
- return flattened.filter((entry) => Boolean(entry && typeof entry === 'object' && typeof entry.id === 'number' && typeof entry.body === 'string'));
145
+ return flattened.filter((entry) => Boolean(entry && typeof entry === 'object' && (typeof entry.id === 'number' || typeof entry.id === 'string') && typeof entry.body === 'string'));
141
146
  }
142
147
  function issueNumberFromTask(content) {
143
148
  const value = parseTaskFrontmatter(content).issue_number;
@@ -246,7 +251,7 @@ function writeComment(client, repo, issue, cwd, body, id) {
246
251
  const endpoint = id ? `repos/${repo}/issues/comments/${id}` : `repos/${repo}/issues/${issue}/comments`;
247
252
  return client.json(['api', endpoint, '-X', id ? 'PATCH' : 'POST', '--input', '-'], { cwd, method: id ? 'PATCH' : 'POST', input: JSON.stringify({ body }) });
248
253
  }
249
- function syncPlatformComment(taskRef, options) {
254
+ async function syncPlatformComment(taskRef, options) {
250
255
  const resolved = resolveTaskRef(taskRef, options.cwd ? { repoRoot: options.cwd } : {});
251
256
  if (!resolved.ok) {
252
257
  return platformResult('failed', {
@@ -254,17 +259,35 @@ function syncPlatformComment(taskRef, options) {
254
259
  });
255
260
  }
256
261
  const taskContent = fs.readFileSync(resolved.taskMdPath, 'utf8');
257
- const issue = issueNumberFromTask(taskContent);
258
- if (!issue) {
262
+ let issueIdentityFromTask;
263
+ try {
264
+ issueIdentityFromTask = taskIssueIdentity(parseTaskFrontmatter(taskContent), undefined, options.runtimeVersion);
265
+ }
266
+ catch (error) {
267
+ return platformResult('failed', { error: { ...taskIssueIdentityError(error), retryable: false } });
268
+ }
269
+ if (!issueIdentityFromTask) {
259
270
  return platformResult('no-op', {
260
271
  error: { code: 'ISSUE_NOT_LINKED', message: 'Task has no valid issue_number', retryable: false }
261
272
  });
262
273
  }
263
- const context = resolvePlatformContext({ cwd: resolved.repoRoot, client: options.client });
264
- if (!hasResolvedPlatformContext(context))
274
+ const loaded = await resolvePlatformProviderContext({ cwd: resolved.repoRoot, client: options.client });
275
+ const context = loaded.ok ? loaded.value.context : loaded.context;
276
+ if (!hasResolvedPlatformContext(context) || !loaded.ok)
265
277
  return context;
266
- const client = options.client || createGitHubClient();
267
- const listed = listRemoteComments(client, context.platform.repository, issue, resolved.repoRoot);
278
+ const issue = resourceIdentityNumber(issueIdentityFromTask);
279
+ const listed = loaded.value.provider.comments?.list
280
+ ? await loaded.value.provider.comments.list({
281
+ context: providerOperationContext(loaded.value),
282
+ parent: issueIdentityFromTask
283
+ }).then((response) => response.ok
284
+ ? { ok: true, value: response.value.map((comment) => ({
285
+ id: providerCommentId(comment.id, loaded.value.provider),
286
+ body: comment.body,
287
+ user: comment.author?.name ? { login: comment.author.name } : undefined
288
+ })) }
289
+ : response)
290
+ : unsupportedProviderOperation(loaded.value.provider, 'comments.list');
268
291
  if (!listed.ok) {
269
292
  return platformResult(listed.error.retryable ? 'blocked' : 'failed', {
270
293
  ...contextFields(context), resource: { kind: 'issue', number: issue }, error: listed.error
@@ -310,7 +333,6 @@ function syncPlatformComment(taskRef, options) {
310
333
  error: null
311
334
  });
312
335
  }
313
- const repo = context.platform.repository;
314
336
  const operations = [];
315
337
  const ids = [];
316
338
  for (const item of desired) {
@@ -320,16 +342,18 @@ function syncPlatformComment(taskRef, options) {
320
342
  operations.push({ name: `comment:${item.marker}`, status: 'no-op', reasonCode: null });
321
343
  continue;
322
344
  }
323
- const written = writeComment(client, repo, issue, resolved.repoRoot, item.body, current?.id);
345
+ const written = loaded.value.provider.comments?.write
346
+ ? await loaded.value.provider.comments.write({
347
+ context: providerOperationContext(loaded.value),
348
+ parent: issueIdentityFromTask,
349
+ body: item.body,
350
+ ...(current ? { existingComment: { kind: 'id', value: String(current.id) } } : {}),
351
+ mutation: { idempotencyKey: `comment:${resolved.taskId}:${item.marker}` }
352
+ })
353
+ : unsupportedProviderOperation(loaded.value.provider, 'comments.write');
324
354
  if (!written.ok) {
325
355
  if (!current && written.error.retryable) {
326
- const reconciled = listRemoteComments(client, repo, issue, resolved.repoRoot);
327
- const found = reconciled.ok ? findMarkerComments(reconciled.value, item.marker) : [];
328
- if (found.length === 1) {
329
- ids.push(found[0].id);
330
- operations.push({ name: `comment:${item.marker}`, status: 'applied', reasonCode: 'CREATE_RECONCILED' });
331
- continue;
332
- }
356
+ // A provider owns reconciliation because only it knows how to address the resource.
333
357
  }
334
358
  return platformResult(written.error.retryable ? 'blocked' : 'failed', {
335
359
  ...contextFields(context),
@@ -338,15 +362,20 @@ function syncPlatformComment(taskRef, options) {
338
362
  error: written.error
339
363
  });
340
364
  }
341
- ids.push(current?.id || written.value.id || 0);
365
+ ids.push(providerCommentId(written.value.remoteId, loaded.value.provider));
342
366
  operations.push({ name: `comment:${item.marker}`, status: 'applied', reasonCode: null });
343
367
  }
344
368
  const desiredMarkers = new Set(desired.map((item) => item.marker));
345
369
  const stale = existing.filter((comment) => !desiredMarkers.has(normalizeCommentContent(comment.body).split('\n', 1)[0]));
346
370
  for (const comment of stale) {
347
- const deleted = client.text(['api', `repos/${repo}/issues/comments/${comment.id}`, '-X', 'DELETE'], {
348
- cwd: resolved.repoRoot, method: 'DELETE'
349
- });
371
+ const deleted = loaded.value.provider.comments?.delete
372
+ ? await loaded.value.provider.comments.delete({
373
+ context: providerOperationContext(loaded.value),
374
+ parent: issueIdentityFromTask,
375
+ comment: { kind: 'id', value: String(comment.id) },
376
+ mutation: { idempotencyKey: `comment-delete:${resolved.taskId}:${comment.id}` }
377
+ })
378
+ : unsupportedProviderOperation(loaded.value.provider, 'comments.delete');
350
379
  if (!deleted.ok) {
351
380
  return platformResult(deleted.error.retryable ? 'blocked' : 'failed', {
352
381
  ...contextFields(context),
@@ -367,31 +396,55 @@ function syncPlatformComment(taskRef, options) {
367
396
  error: null
368
397
  });
369
398
  }
370
- function listPlatformComments(issue, cwd = process.cwd(), client = createGitHubClient()) {
371
- const context = resolvePlatformContext({ cwd, client });
372
- if (!hasResolvedPlatformContext(context))
399
+ async function listPlatformComments(issue, cwd = process.cwd(), client) {
400
+ const loaded = await resolvePlatformProviderContext({ cwd, client });
401
+ const context = loaded.ok ? loaded.value.context : loaded.context;
402
+ if (!hasResolvedPlatformContext(context) || !loaded.ok)
373
403
  return context;
374
- const result = listRemoteComments(client, context.platform.repository, issue, cwd);
375
- if (!result.ok) {
376
- return platformResult(result.error.retryable ? 'blocked' : 'failed', {
377
- ...contextFields(context), resource: { kind: 'issue', number: issue }, error: result.error
404
+ let identity;
405
+ try {
406
+ identity = loaded.ok ? providerResourceToken(loaded.value.provider, 'issue', String(issue)) : null;
407
+ }
408
+ catch (error) {
409
+ return platformResult('failed', { ...contextFields(context), error: { code: 'PLATFORM_IDENTITY_TOKEN_INVALID', message: error instanceof Error ? error.message : String(error), retryable: false } });
410
+ }
411
+ if (!loaded.ok || !identity)
412
+ return context;
413
+ const listed = loaded.value.provider.comments?.list
414
+ ? await loaded.value.provider.comments.list({ context: providerOperationContext(loaded.value), parent: identity }).then((response) => response.ok
415
+ ? { ok: true, value: response.value.map((comment) => ({ id: comment.id, body: comment.body, user: comment.author?.name ? { login: comment.author.name } : undefined })) }
416
+ : response)
417
+ : unsupportedProviderOperation(loaded.value.provider, 'comments.list');
418
+ if (!listed.ok) {
419
+ return platformResult(providerStatus(listed.error), {
420
+ ...contextFields(context), resource: { kind: 'issue', number: resourceIdentityNumber(identity), identity }, error: providerError(listed.error, 'PLATFORM_PROVIDER_OPERATION_FAILED')
378
421
  });
379
422
  }
380
- return { ...platformResult('no-op', { ...contextFields(context), resource: { kind: 'issue', number: issue }, error: null }), comments: result.value };
423
+ return { ...platformResult('no-op', { ...contextFields(context), resource: { kind: 'issue', number: resourceIdentityNumber(identity), identity }, error: null }), comments: listed.value };
381
424
  }
382
- function checkPlatformCommentOwner(taskRef, options = {}) {
425
+ async function checkPlatformCommentOwner(taskRef, options = {}) {
383
426
  const resolved = resolveTaskRef(taskRef, options.cwd ? { repoRoot: options.cwd } : {});
384
427
  if (!resolved.ok)
385
428
  return platformResult('failed', { error: { code: resolved.code, message: resolved.message, retryable: false } });
386
429
  const content = fs.readFileSync(resolved.taskMdPath, 'utf8');
387
- const issue = issueNumberFromTask(content);
388
- if (!issue)
389
- return platformResult('no-op', { error: { code: 'ISSUE_NOT_LINKED', message: 'Task has no valid issue_number', retryable: false } });
390
- const client = options.client || createGitHubClient();
391
- const context = resolvePlatformContext({ cwd: resolved.repoRoot, client });
392
- if (!hasResolvedPlatformContext(context))
430
+ let issueIdentity;
431
+ try {
432
+ issueIdentity = taskIssueIdentity(parseTaskFrontmatter(content), undefined, options.runtimeVersion);
433
+ }
434
+ catch (error) {
435
+ return platformResult('failed', { error: { ...taskIssueIdentityError(error), retryable: false } });
436
+ }
437
+ if (!issueIdentity)
438
+ return platformResult('no-op', { error: { code: 'ISSUE_NOT_LINKED', message: 'Task has no valid platform issue identity', retryable: false } });
439
+ const loaded = await resolvePlatformProviderContext({ cwd: resolved.repoRoot, client: options.client });
440
+ const context = loaded.ok ? loaded.value.context : loaded.context;
441
+ if (!hasResolvedPlatformContext(context) || !loaded.ok)
393
442
  return context;
394
- const listed = listRemoteComments(client, context.platform.repository, issue, resolved.repoRoot);
443
+ const listed = loaded.value.provider.comments?.list
444
+ ? await loaded.value.provider.comments.list({ context: providerOperationContext(loaded.value), parent: issueIdentity }).then((response) => response.ok
445
+ ? { ok: true, value: response.value.map((comment) => ({ id: comment.id, body: comment.body, user: comment.author?.name ? { login: comment.author.name } : undefined })) }
446
+ : response)
447
+ : unsupportedProviderOperation(loaded.value.provider, 'comments.list');
395
448
  if (!listed.ok)
396
449
  return platformResult(listed.error.retryable ? 'blocked' : 'failed', { ...contextFields(context), error: listed.error });
397
450
  const matches = findMarkerComments(listed.value, MARKERS.task(resolved.taskId));
@@ -401,11 +454,11 @@ function checkPlatformCommentOwner(taskRef, options = {}) {
401
454
  if (owner && owner !== context.platform.currentUser && !context.capabilities.triage) {
402
455
  return platformResult('blocked', {
403
456
  ...contextFields(context),
404
- resource: { kind: 'issue', number: issue },
457
+ resource: { kind: 'issue', number: resourceIdentityNumber(issueIdentity), identity: issueIdentity },
405
458
  error: { code: 'COMMENT_OWNER_CONFLICT', message: `Task comment is owned by '${owner}'`, retryable: false }
406
459
  });
407
460
  }
408
- return platformResult('no-op', { ...contextFields(context), resource: { kind: 'issue', number: issue }, error: null });
461
+ return platformResult('no-op', { ...contextFields(context), resource: { kind: 'issue', number: resourceIdentityNumber(issueIdentity), identity: issueIdentity }, error: null });
409
462
  }
410
463
  export { MARKERS, chunkArtifactComment, findMarkerComments, listRemoteComments, checkPlatformCommentOwner, listPlatformComments, normalizeCommentContent, renderTaskComment, syncPlatformComment, validateRelatedMarkerSet, writeComment };
411
464
  //# sourceMappingURL=issue-comments.js.map