@fitlab-ai/agent-infra 0.9.15 → 0.11.0

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 (224) hide show
  1. package/README.md +2 -6
  2. package/README.zh-CN.md +2 -6
  3. package/bin/internal-cli.sh +0 -13
  4. package/dist/bin/internal-cli.js +11 -49
  5. package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +6 -2
  6. package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +43 -3
  7. package/dist/lib/fs/durable-write.js +2 -0
  8. package/dist/lib/internal/cli-route-inventory.js +3 -4
  9. package/dist/lib/internal/manual-validation.js +17 -8
  10. package/dist/lib/internal/platform-comment.js +60 -2
  11. package/dist/lib/internal/task-artifact.js +1 -1
  12. package/dist/lib/internal/task-finalization.js +2 -2
  13. package/dist/lib/internal/task-lifecycle.js +4 -4
  14. package/dist/lib/internal/task-operation-registry.js +22 -13
  15. package/dist/lib/internal/task-orchestration.js +2 -2
  16. package/dist/lib/internal/task-review.js +12 -7
  17. package/dist/lib/platform/activity-recovery.js +159 -0
  18. package/dist/lib/platform/issue-comments.js +89 -31
  19. package/dist/lib/platform/issues.js +1 -1
  20. package/dist/lib/platform/pr-change-report.js +5 -4
  21. package/dist/lib/platform/pr-summary.js +64 -32
  22. package/dist/lib/platform/pull-requests.js +3 -3
  23. package/dist/lib/platform/task-comment-projection.js +70 -0
  24. package/dist/lib/platform/task-identities.js +2 -9
  25. package/dist/lib/platform/task-recovery.js +55 -0
  26. package/dist/lib/platform/verification-sync.js +45 -7
  27. package/dist/lib/pr-review/evidence-grading.js +4 -8
  28. package/dist/lib/sandbox/commands/rm.js +21 -2
  29. package/dist/lib/sandbox/control/executor.js +4 -2
  30. package/dist/lib/sandbox/control/server.js +29 -9
  31. package/dist/lib/sandbox/control/state.js +7 -1
  32. package/dist/lib/sandbox/control/task-workflow.js +1 -1
  33. package/dist/lib/sandbox/control/workflow-executor.js +87 -35
  34. package/dist/lib/sandbox/index.js +1 -2
  35. package/dist/lib/sandbox/removal.js +116 -1011
  36. package/dist/lib/sandbox/workspace-identity.js +17 -1
  37. package/dist/lib/server/process-state.js +17 -5
  38. package/dist/lib/task/artifact-command.js +11 -30
  39. package/dist/lib/task/artifact-operations.js +17 -231
  40. package/dist/lib/task/artifact-recovery.js +476 -0
  41. package/dist/lib/task/artifact-repair-intent.js +90 -39
  42. package/dist/lib/task/commit-operation.js +31 -4
  43. package/dist/lib/task/control-authority.js +39 -8
  44. package/dist/lib/task/control-recovery.js +14 -2
  45. package/dist/lib/task/create-service.js +1 -8
  46. package/dist/lib/task/delegation-receipts.js +64 -3
  47. package/dist/lib/task/events.js +165 -23
  48. package/dist/lib/task/human-override.js +5 -8
  49. package/dist/lib/task/intermediate-cleanup.js +91 -8
  50. package/dist/lib/task/lifecycle-recovery.js +549 -0
  51. package/dist/lib/task/lifecycle.js +12 -3
  52. package/dist/lib/task/local-artifact-finalization.js +212 -123
  53. package/dist/lib/task/manual-validation-receipt.js +5 -1
  54. package/dist/lib/task/orchestration.js +52 -2
  55. package/dist/lib/task/pr-delivery-fact.js +12 -56
  56. package/dist/lib/task/recovery-warning.js +19 -0
  57. package/dist/lib/task/review-command.js +3 -2
  58. package/dist/lib/task/review-finalization.js +294 -74
  59. package/dist/lib/{host-control → task}/secure-fs.js +8 -19
  60. package/dist/lib/task/short-id.js +15 -7
  61. package/dist/lib/task/task-execution-lock.js +1 -1
  62. package/dist/lib/task/verification-engine.js +3 -14
  63. package/dist/lib/task/workflow-command.js +2 -1
  64. package/dist/lib/task/workflow-dispatch.js +161 -0
  65. package/dist/lib/version.js +1 -11
  66. package/dist/lifecycle-build-manifest.json +3 -3
  67. package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +7 -2
  68. package/lib/agent-clients/adapters/codex-lifecycle/store.ts +55 -3
  69. package/lib/fs/durable-write.ts +1 -0
  70. package/lib/internal/cli-route-inventory.ts +3 -4
  71. package/lib/internal/manual-validation.ts +17 -8
  72. package/lib/internal/platform-comment.ts +53 -2
  73. package/lib/internal/task-artifact.ts +1 -1
  74. package/lib/internal/task-finalization.ts +1 -1
  75. package/lib/internal/task-lifecycle.ts +5 -4
  76. package/lib/internal/task-operation-registry.ts +22 -13
  77. package/lib/internal/task-orchestration.ts +1 -1
  78. package/lib/internal/task-review.ts +11 -7
  79. package/lib/platform/activity-recovery.ts +179 -0
  80. package/lib/platform/issue-comments.ts +105 -30
  81. package/lib/platform/issues.ts +1 -1
  82. package/lib/platform/pr-change-report.ts +7 -5
  83. package/lib/platform/pr-summary.ts +64 -32
  84. package/lib/platform/pull-requests.ts +3 -3
  85. package/lib/platform/task-comment-projection.ts +79 -0
  86. package/lib/platform/task-identities.ts +3 -14
  87. package/lib/platform/task-recovery.ts +54 -0
  88. package/lib/platform/verification-sync.ts +48 -11
  89. package/lib/pr-review/evidence-grading.ts +4 -9
  90. package/lib/sandbox/commands/rm.ts +34 -2
  91. package/lib/sandbox/control/executor.ts +4 -2
  92. package/lib/sandbox/control/protocol.ts +6 -1
  93. package/lib/sandbox/control/server.ts +30 -10
  94. package/lib/sandbox/control/state.ts +9 -1
  95. package/lib/sandbox/control/task-workflow.ts +1 -1
  96. package/lib/sandbox/control/workflow-executor.ts +106 -35
  97. package/lib/sandbox/index.ts +1 -2
  98. package/lib/sandbox/removal.ts +126 -1128
  99. package/lib/sandbox/workspace-identity.ts +19 -3
  100. package/lib/server/process-state.ts +26 -5
  101. package/lib/task/artifact-command.ts +12 -29
  102. package/lib/task/artifact-operations.ts +22 -252
  103. package/lib/task/artifact-recovery.ts +572 -0
  104. package/lib/task/artifact-repair-intent.ts +126 -51
  105. package/lib/task/commit-operation.ts +35 -4
  106. package/lib/task/control-authority.ts +48 -15
  107. package/lib/task/control-recovery.ts +13 -1
  108. package/lib/task/create-service.ts +1 -10
  109. package/lib/task/delegation-receipts.ts +73 -2
  110. package/lib/task/events.ts +188 -24
  111. package/lib/task/human-override.ts +5 -8
  112. package/lib/task/intermediate-cleanup.ts +66 -12
  113. package/lib/task/lifecycle-recovery.ts +691 -0
  114. package/lib/task/lifecycle.ts +13 -4
  115. package/lib/task/local-artifact-finalization.ts +249 -131
  116. package/lib/task/manual-validation-receipt.ts +5 -1
  117. package/lib/task/orchestration.ts +65 -0
  118. package/lib/task/pr-delivery-fact.ts +15 -62
  119. package/lib/task/recovery-warning.ts +21 -0
  120. package/lib/task/review-command.ts +2 -1
  121. package/lib/task/review-finalization.ts +298 -84
  122. package/lib/{host-control → task}/secure-fs.ts +10 -32
  123. package/lib/task/short-id.ts +27 -5
  124. package/lib/task/task-execution-lock.ts +1 -1
  125. package/lib/task/verification-engine.ts +3 -17
  126. package/lib/task/verification-types.ts +0 -2
  127. package/lib/task/workflow-command.ts +2 -1
  128. package/lib/task/workflow-dispatch.ts +171 -0
  129. package/lib/version.ts +1 -15
  130. package/package.json +1 -1
  131. package/templates/.agents/rules/create-issue.en.md +1 -1
  132. package/templates/.agents/rules/create-issue.zh-CN.md +1 -1
  133. package/templates/.agents/rules/issue-sync.en.md +12 -2
  134. package/templates/.agents/rules/issue-sync.zh-CN.md +13 -5
  135. package/templates/.agents/rules/local-artifact-repair.en.md +47 -39
  136. package/templates/.agents/rules/local-artifact-repair.zh-CN.md +45 -36
  137. package/templates/.agents/skills/analyze-task/SKILL.en.md +6 -6
  138. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +6 -6
  139. package/templates/.agents/skills/analyze-task/config/verify.en.json +1 -1
  140. package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -1
  141. package/templates/.agents/skills/block-task/SKILL.en.md +2 -2
  142. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -2
  143. package/templates/.agents/skills/block-task/config/verify.json +1 -1
  144. package/templates/.agents/skills/cancel-task/SKILL.en.md +3 -3
  145. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +3 -3
  146. package/templates/.agents/skills/cancel-task/config/verify.json +1 -1
  147. package/templates/.agents/skills/code-task/SKILL.en.md +7 -7
  148. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +8 -8
  149. package/templates/.agents/skills/code-task/config/verify.en.json +1 -1
  150. package/templates/.agents/skills/code-task/config/verify.zh-CN.json +1 -1
  151. package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +1 -1
  152. package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +1 -1
  153. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +1 -1
  154. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +1 -1
  155. package/templates/.agents/skills/complete-task/SKILL.en.md +2 -2
  156. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +2 -2
  157. package/templates/.agents/skills/complete-task/config/verify.en.json +2 -2
  158. package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +2 -2
  159. package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +1 -1
  160. package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +1 -1
  161. package/templates/.agents/skills/create-pr/SKILL.en.md +2 -2
  162. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +2 -2
  163. package/templates/.agents/skills/create-pr/config/verify.json +1 -1
  164. package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +2 -2
  165. package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +2 -2
  166. package/templates/.agents/skills/create-task/SKILL.en.md +5 -5
  167. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +5 -5
  168. package/templates/.agents/skills/create-task/config/verify.json +1 -1
  169. package/templates/.agents/skills/import-issue/SKILL.en.md +8 -8
  170. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +8 -8
  171. package/templates/.agents/skills/import-issue/config/verify.json +2 -2
  172. package/templates/.agents/skills/plan-task/SKILL.en.md +4 -4
  173. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +4 -4
  174. package/templates/.agents/skills/plan-task/config/verify.en.json +1 -1
  175. package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +1 -1
  176. package/templates/.agents/skills/restore-task/SKILL.en.md +11 -6
  177. package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +11 -6
  178. package/templates/.agents/skills/restore-task/config/verify.json +1 -1
  179. package/templates/.agents/skills/review-analysis/SKILL.en.md +3 -3
  180. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +3 -3
  181. package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -1
  182. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -1
  183. package/templates/.agents/skills/review-analysis/reference/report-template.en.md +7 -7
  184. package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +6 -6
  185. package/templates/.agents/skills/review-code/SKILL.en.md +5 -4
  186. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +5 -4
  187. package/templates/.agents/skills/review-code/config/verify.en.json +1 -1
  188. package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -1
  189. package/templates/.agents/skills/review-code/reference/report-template.en.md +12 -12
  190. package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +12 -12
  191. package/templates/.agents/skills/review-plan/SKILL.en.md +3 -3
  192. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +3 -3
  193. package/templates/.agents/skills/review-plan/config/verify.en.json +1 -1
  194. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -1
  195. package/templates/.agents/skills/review-plan/reference/report-template.en.md +7 -7
  196. package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +7 -7
  197. package/templates/.agents/skills/review-pr/config/verify.en.json +1 -1
  198. package/templates/.agents/skills/review-pr/config/verify.zh-CN.json +1 -1
  199. package/templates/.agents/skills/review-pr/reference/evidence-grading.en.md +3 -3
  200. package/templates/.agents/skills/review-pr/reference/evidence-grading.zh-CN.md +3 -3
  201. package/templates/.agents/skills/run-manual-validation/config/verify.en.json +1 -1
  202. package/templates/.agents/skills/run-manual-validation/config/verify.zh-CN.json +1 -1
  203. package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
  204. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
  205. package/templates/.agents/templates/task.en.md +1 -1
  206. package/templates/.agents/templates/task.zh-CN.md +1 -1
  207. package/templates/.claude/commands/import-issue.en.md +1 -1
  208. package/templates/.claude/commands/import-issue.zh-CN.md +1 -1
  209. package/templates/.claude/commands/restore-task.en.md +1 -1
  210. package/templates/.claude/commands/restore-task.zh-CN.md +1 -1
  211. package/dist/lib/host-control/audit.js +0 -19
  212. package/dist/lib/host-control/client.js +0 -142
  213. package/dist/lib/host-control/command.js +0 -49
  214. package/dist/lib/host-control/path.js +0 -160
  215. package/dist/lib/host-control/server.js +0 -159
  216. package/dist/lib/host-control/service.js +0 -112
  217. package/dist/lib/internal/host-control.js +0 -28
  218. package/lib/host-control/audit.ts +0 -22
  219. package/lib/host-control/client.ts +0 -172
  220. package/lib/host-control/command.ts +0 -60
  221. package/lib/host-control/path.ts +0 -167
  222. package/lib/host-control/server.ts +0 -180
  223. package/lib/host-control/service.ts +0 -133
  224. package/lib/internal/host-control.ts +0 -28
@@ -1,15 +1,15 @@
1
1
  import { parseReviewCommand } from '../task/review-command.js';
2
- import { finalizeReviewSummary } from '../task/review-finalization.js';
2
+ import { finalizeReviewSummary, preflightReviewSummary } from '../task/review-finalization.js';
3
3
  import { consumeHumanOverride, failureId, overrideDryRunConflict } from '../task/human-override.js';
4
4
  import { resolveTaskRef } from '../task/resolve-ref.js';
5
5
  import { TaskExecutionLockError, withTaskExecutionLock } from '../task/task-execution-lock.js';
6
6
  import { ensureInternalHandlerRoute } from './cli-route-inventory.js';
7
- const USAGE = 'Usage: agent-infra-internal task-review <task-ref> finalize-summary --stage <analysis|plan|code> --artifact <review-*.md> [--orchestrated] [--dry-run] [--override-ticket <ticket> --override-target <target> --override-scope <scope>]\n';
7
+ const USAGE = 'Usage: agent-infra-internal task-review <task-ref> <preflight|finalize-summary> --stage <analysis|plan|code> --artifact <review-*.md> [--recovery-id <id>] [--orchestrated] [--dry-run] [--override-ticket <ticket> --override-target <target> --override-scope <scope>]\n';
8
8
  function failUsage(message) {
9
9
  process.stdout.write(`${JSON.stringify({
10
10
  status: 'failed',
11
11
  changed: false,
12
- intent: 'finalize-summary',
12
+ intent: 'review-operation',
13
13
  error: { code: 'REVIEW_PAYLOAD_INVALID', message }
14
14
  })}\n`);
15
15
  process.stderr.write(USAGE);
@@ -30,7 +30,8 @@ async function taskReview(args = []) {
30
30
  failUsage(error instanceof Error ? error.message : String(error));
31
31
  return;
32
32
  }
33
- const { stage, artifact, dryRun, orchestrated, overrideTicket, overrideTarget, overrideScope } = request;
33
+ const { stage, artifact, recoveryId, dryRun, orchestrated, overrideTicket, overrideTarget, overrideScope } = request;
34
+ const operation = args[1];
34
35
  const dryRunConflict = overrideDryRunConflict({ dryRun, overrideTicket, overrideTarget, overrideScope });
35
36
  if (dryRunConflict) {
36
37
  failUsage(dryRunConflict.message);
@@ -44,8 +45,12 @@ async function taskReview(args = []) {
44
45
  let result;
45
46
  let humanOverride = null;
46
47
  try {
47
- result = await withTaskExecutionLock(resolved.repoRoot, resolved.taskId, 'task-review.finalize-summary', async () => {
48
- let current = finalizeReviewSummary({ taskRef: args[0], stage, artifact, orchestrated, dryRun }, { lockAlreadyHeld: true });
48
+ result = await withTaskExecutionLock(resolved.repoRoot, resolved.taskId, `task-review.${operation}`, async () => {
49
+ let current = operation === 'preflight'
50
+ ? preflightReviewSummary({ taskRef: args[0], stage, artifact, recoveryId, orchestrated, dryRun }, { lockAlreadyHeld: true })
51
+ : finalizeReviewSummary({ taskRef: args[0], stage, artifact, recoveryId, orchestrated, dryRun }, { lockAlreadyHeld: true });
52
+ if (operation === 'preflight')
53
+ return current;
49
54
  if (current.status !== 'failed' || !overrideTicket)
50
55
  return current;
51
56
  if (!overrideTarget || !overrideScope) {
@@ -58,7 +63,7 @@ async function taskReview(args = []) {
58
63
  target: overrideTarget, scope: overrideScope
59
64
  }, {
60
65
  effectExecutor: (capability) => {
61
- const retried = finalizeReviewSummary({ taskRef: args[0], stage, artifact, orchestrated, dryRun }, { lockAlreadyHeld: true, manualOverride: capability });
66
+ const retried = finalizeReviewSummary({ taskRef: args[0], stage, artifact, recoveryId, orchestrated, dryRun }, { lockAlreadyHeld: true, manualOverride: capability });
62
67
  current = retried;
63
68
  return retried.status === 'failed' || retried.status === 'planned'
64
69
  ? { code: 'OVERRIDE_EFFECT_FAILED', message: retried.status === 'planned' ? 'producer returned planned; no review effect was committed' : `${retried.error?.code ?? 'REVIEW_FAILED'}: ${retried.error?.message ?? 'manual review effect failed'}` }
@@ -0,0 +1,159 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { locateActivityLog } from '../task/activity-log.js';
3
+ const METADATA_PREFIX = '<!-- activity-recovery:v1:';
4
+ const METADATA_SUFFIX = ' -->';
5
+ function canonicalJson(value) {
6
+ if (value === null || typeof value !== 'object')
7
+ return JSON.stringify(value);
8
+ if (Array.isArray(value))
9
+ return `[${value.map(canonicalJson).join(',')}]`;
10
+ const record = value;
11
+ return `{${Object.keys(record).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(record[key])}`).join(',')}}`;
12
+ }
13
+ function sha256(value) {
14
+ return createHash('sha256').update(canonicalJson(value), 'utf8').digest('hex');
15
+ }
16
+ function baseStep(step) {
17
+ return step.replace(/\s*\[(?:started|aborted)\]\s*$/, '');
18
+ }
19
+ function entriesForArtifact(taskContent, artifact) {
20
+ const log = locateActivityLog(taskContent);
21
+ if (!log)
22
+ return [];
23
+ const selected = new Set();
24
+ for (let index = 0; index < log.entries.length; index += 1) {
25
+ const entry = log.entries[index];
26
+ if (!entry.note.includes(`→ ${artifact}`))
27
+ continue;
28
+ selected.add(index);
29
+ const step = baseStep(entry.step);
30
+ let startedAt = index;
31
+ for (let prior = index - 1; prior >= 0; prior -= 1) {
32
+ const candidate = log.entries[prior];
33
+ if (baseStep(candidate.step) !== step)
34
+ continue;
35
+ if (/\[started\]\s*$/.test(candidate.step)) {
36
+ selected.add(prior);
37
+ startedAt = prior;
38
+ }
39
+ break;
40
+ }
41
+ if (/^code(?:-r\d+)?\.md$/.test(artifact)) {
42
+ for (let between = startedAt + 1; between < index; between += 1) {
43
+ if (baseStep(log.entries[between].step) === 'Commit')
44
+ selected.add(between);
45
+ }
46
+ }
47
+ }
48
+ return log.entries.filter((_entry, index) => selected.has(index));
49
+ }
50
+ function recordForEntries(taskId, entries, artifact) {
51
+ if (entries.length === 0)
52
+ return null;
53
+ const base = { version: 1, taskId, ...(artifact ? { artifact } : {}), entries: [...entries] };
54
+ return { ...base, sha256: sha256(base) };
55
+ }
56
+ function recordForArtifact(taskId, taskContent, artifact) {
57
+ return recordForEntries(taskId, entriesForArtifact(taskContent, artifact), artifact);
58
+ }
59
+ function entriesForTask(taskContent) {
60
+ const log = locateActivityLog(taskContent);
61
+ if (!log)
62
+ return [];
63
+ const linked = new Set(entriesForArtifactNames(log.entries));
64
+ return log.entries.filter((entry, index) => !linked.has(index)
65
+ && !['Create PR', 'Complete Task', 'Cancel Task'].includes(baseStep(entry.step)));
66
+ }
67
+ function entriesForSummary(taskContent) {
68
+ const log = locateActivityLog(taskContent);
69
+ return log?.entries.filter((entry) => ['Create PR', 'Complete Task'].includes(baseStep(entry.step))) ?? [];
70
+ }
71
+ function entriesForCancel(taskContent) {
72
+ const log = locateActivityLog(taskContent);
73
+ return log?.entries.filter((entry) => baseStep(entry.step) === 'Cancel Task') ?? [];
74
+ }
75
+ function recordForTask(taskId, taskContent) {
76
+ return recordForEntries(taskId, entriesForTask(taskContent));
77
+ }
78
+ function recordForSummary(taskId, taskContent) {
79
+ return recordForEntries(taskId, entriesForSummary(taskContent));
80
+ }
81
+ function recordForCancel(taskId, taskContent) {
82
+ return recordForEntries(taskId, entriesForCancel(taskContent));
83
+ }
84
+ function entriesForArtifactNames(entries) {
85
+ const selected = new Set();
86
+ for (let index = 0; index < entries.length; index += 1) {
87
+ const entry = entries[index];
88
+ const artifact = /→ ([A-Za-z0-9][A-Za-z0-9._-]*\.md)(?:\s|$)/.exec(entry.note)?.[1];
89
+ if (!artifact)
90
+ continue;
91
+ selected.add(index);
92
+ const step = baseStep(entry.step);
93
+ let startedAt = index;
94
+ for (let prior = index - 1; prior >= 0; prior -= 1) {
95
+ const candidate = entries[prior];
96
+ if (baseStep(candidate.step) !== step)
97
+ continue;
98
+ if (/\[started\]\s*$/.test(candidate.step)) {
99
+ selected.add(prior);
100
+ startedAt = prior;
101
+ }
102
+ break;
103
+ }
104
+ if (/^code(?:-r\d+)?\.md$/.test(artifact)) {
105
+ for (let between = startedAt + 1; between < index; between += 1) {
106
+ if (baseStep(entries[between].step) === 'Commit')
107
+ selected.add(between);
108
+ }
109
+ }
110
+ }
111
+ return [...selected];
112
+ }
113
+ function renderActivityRecoveryMetadata(record) {
114
+ if (!record)
115
+ return '';
116
+ const encoded = Buffer.from(canonicalJson(record), 'utf8').toString('base64url');
117
+ const first = record.entries[0];
118
+ const last = record.entries.at(-1);
119
+ return [
120
+ '<details><summary>恢复元数据</summary>',
121
+ '',
122
+ `- 动作:${baseStep(last.step)}`,
123
+ `- 记录:${record.entries.length} 条(${first.time} 至 ${last.time})`,
124
+ ...(record.artifact ? [`- 产物:${record.artifact}`] : []),
125
+ `- 校验:\`${record.sha256}\``,
126
+ '',
127
+ `${METADATA_PREFIX}${encoded}${METADATA_SUFFIX}`,
128
+ '</details>',
129
+ ''
130
+ ].join('\n');
131
+ }
132
+ function parseActivityRecoveryMetadata(body) {
133
+ const match = body.match(/<!-- activity-recovery:v1:([A-Za-z0-9_-]+) -->/);
134
+ if (!match)
135
+ return null;
136
+ let value;
137
+ try {
138
+ value = JSON.parse(Buffer.from(match[1], 'base64url').toString('utf8'));
139
+ }
140
+ catch {
141
+ throw new Error('RECOVERY_EVIDENCE_MISSING: activity metadata is not valid');
142
+ }
143
+ if (!value || typeof value !== 'object')
144
+ throw new Error('RECOVERY_EVIDENCE_MISSING: activity metadata is invalid');
145
+ const record = value;
146
+ if (record.version !== 1 || typeof record.taskId !== 'string' || (record.artifact !== undefined && typeof record.artifact !== 'string')
147
+ || !Array.isArray(record.entries) || typeof record.sha256 !== 'string') {
148
+ throw new Error('RECOVERY_EVIDENCE_MISSING: activity metadata shape is invalid');
149
+ }
150
+ if (record.entries.some((entry) => !entry || typeof entry.time !== 'string' || typeof entry.step !== 'string' || typeof entry.agent !== 'string' || typeof entry.note !== 'string')) {
151
+ throw new Error('RECOVERY_EVIDENCE_MISSING: activity metadata entries are invalid');
152
+ }
153
+ const base = { version: 1, taskId: record.taskId, ...(record.artifact ? { artifact: record.artifact } : {}), entries: record.entries };
154
+ if (sha256(base) !== record.sha256)
155
+ throw new Error('RECOVERY_EVIDENCE_MISSING: activity metadata checksum does not match');
156
+ return { ...base, sha256: record.sha256 };
157
+ }
158
+ export { entriesForArtifact, entriesForCancel, entriesForSummary, entriesForTask, parseActivityRecoveryMetadata, recordForArtifact, recordForCancel, recordForEntries, recordForSummary, recordForTask, renderActivityRecoveryMetadata };
159
+ //# sourceMappingURL=activity-recovery.js.map
@@ -1,5 +1,6 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
+ import { createHash } from 'node:crypto';
3
4
  import { parseTaskFrontmatter } from '../task/frontmatter.js';
4
5
  import { resolveTaskRef } from '../task/resolve-ref.js';
5
6
  import { resolvePlatformProviderContext } from './context.js';
@@ -7,6 +8,8 @@ import { platformResult } from './types.js';
7
8
  import { providerError, providerOperationContext, providerStatus, providerResourceToken, unsupportedProviderOperation } from './provider-bridge.js';
8
9
  import { resourceIdentityNumber } from './resource-identity.js';
9
10
  import { taskIssueIdentity, taskIssueIdentityError } from './task-identities.js';
11
+ import { recordForArtifact, recordForCancel, recordForSummary, recordForTask, renderActivityRecoveryMetadata } from './activity-recovery.js';
12
+ import { projectTaskComment } from './task-comment-projection.js';
10
13
  import { canonicalizeCommentBody, escapeHtmlText, fenceRanges, renderSafeCodeFence } from './comment-safety.js';
11
14
  function providerCommentId(id, provider) {
12
15
  return provider.identity?.comment === 'number' && /^\d+$/.test(id) ? Number(id) : id;
@@ -16,8 +19,9 @@ const MARKERS = {
16
19
  artifact: (taskId, stem) => `<!-- sync-issue:${taskId}:${stem} -->`,
17
20
  artifactChunk: (taskId, stem, part, total) => `<!-- sync-issue:${taskId}:${stem}:${part}/${total} -->`,
18
21
  summary: (taskId) => `<!-- sync-issue:${taskId}:summary -->`,
19
- cancel: (taskId) => `<!-- sync-issue:${taskId}:cancel -->`
22
+ cancel: (taskId) => `<!-- sync-issue:${taskId}:cancel -->`,
20
23
  };
24
+ const COMMENT_BYTE_LIMIT = 60_000;
21
25
  const ARTIFACT_TITLES = {
22
26
  analysis: '需求分析',
23
27
  'review-analysis': '需求分析审查',
@@ -47,22 +51,52 @@ function sanitizeCommentBody(value, label) {
47
51
  function footer(agent, taskId) {
48
52
  return `---\n*由 ${agent} 自动生成 · 内部追踪:${taskId}*`;
49
53
  }
50
- function renderTaskComment(content, taskId, agent) {
54
+ function taskFrontmatterSummary(language = '') {
55
+ return language === 'en' || language === 'en-US'
56
+ ? 'Metadata (frontmatter)'
57
+ : '元数据 (frontmatter)';
58
+ }
59
+ function renderTaskCommentResult(content, taskId, language = '') {
51
60
  const split = splitFrontmatter(content);
52
- const safeBody = sanitizeCommentBody(split.body, 'task body');
53
- const taskBody = split.frontmatter === null
61
+ const projectedContent = split.frontmatter === null ? content : projectTaskComment(content).content;
62
+ const projected = splitFrontmatter(projectedContent);
63
+ const safeBody = sanitizeCommentBody(projected.body, 'task body');
64
+ const taskBody = projected.frontmatter === null
54
65
  ? safeBody
55
- : `<details><summary>元数据 (frontmatter)</summary>\n\n${renderSafeCodeFence(`---\n${split.frontmatter}\n---`, 'yaml')}\n\n</details>\n\n${safeBody}`;
56
- return normalizeCommentContent([
66
+ : `<details><summary>${taskFrontmatterSummary(language)}</summary>\n\n${renderSafeCodeFence(`---\n${projected.frontmatter}\n---`, 'yaml')}\n\n</details>\n\n${safeBody}`;
67
+ const recoveryMetadata = renderActivityRecoveryMetadata(recordForTask(taskId, content));
68
+ const body = normalizeCommentContent([
57
69
  MARKERS.task(taskId),
58
70
  '## 任务文件',
59
71
  '',
60
- `> **${agent}** · ${taskId}`,
72
+ `> 任务同步 · ${taskId}`,
61
73
  '',
74
+ ...(recoveryMetadata ? [recoveryMetadata.replace(/\n+$/, ''), ''] : []),
62
75
  taskBody.replace(/\n+$/, ''),
63
76
  '',
64
- footer(agent, taskId)
77
+ '---',
78
+ `*由 agent-infra 自动生成 · 内部追踪:${taskId}*`
65
79
  ].join('\n'));
80
+ return {
81
+ body,
82
+ byteLength: Buffer.byteLength(body, 'utf8'),
83
+ sha256: createHash('sha256').update(body, 'utf8').digest('hex')
84
+ };
85
+ }
86
+ function renderTaskComment(content, taskId, _agent, language = '') {
87
+ return renderTaskCommentResult(content, taskId, language).body;
88
+ }
89
+ function isTaskCommentTooLarge(content, taskId = 'TASK-UNKNOWN', _agent = 'codex') {
90
+ return renderTaskCommentResult(content, taskId).byteLength > COMMENT_BYTE_LIMIT;
91
+ }
92
+ function taskCommentLanguage(repoRoot) {
93
+ try {
94
+ const config = JSON.parse(fs.readFileSync(path.join(repoRoot, '.agents', '.airc.json'), 'utf8'));
95
+ return typeof config.language === 'string' ? config.language.trim() : '';
96
+ }
97
+ catch {
98
+ return '';
99
+ }
66
100
  }
67
101
  function artifactIdentity(artifact) {
68
102
  const stem = path.basename(artifact, '.md');
@@ -201,7 +235,7 @@ function independentChunkContent(piece, fences, maxBytes) {
201
235
  }
202
236
  return content;
203
237
  }
204
- function buildArtifactChunk(taskId, stem, title, agent, content, part, total, chunked, backfill, renderedContent = content) {
238
+ function buildArtifactChunk(taskId, stem, title, agent, content, part, total, chunked, backfill, renderedContent = content, recoveryMetadata = '') {
205
239
  const marker = chunked ? MARKERS.artifactChunk(taskId, stem, part, total) : MARKERS.artifact(taskId, stem);
206
240
  const heading = chunked ? `## ${title}(${part}/${total})` : `## ${title}`;
207
241
  const timelineHint = backfill ? '> 历史产物补发' : null;
@@ -212,6 +246,7 @@ function buildArtifactChunk(taskId, stem, title, agent, content, part, total, ch
212
246
  '',
213
247
  `> **${agent}** · ${taskId}`,
214
248
  '',
249
+ ...(recoveryMetadata ? [recoveryMetadata] : []),
215
250
  renderedContent,
216
251
  '',
217
252
  footer(agent, taskId)
@@ -219,10 +254,10 @@ function buildArtifactChunk(taskId, stem, title, agent, content, part, total, ch
219
254
  return { marker, body, content, part, total };
220
255
  }
221
256
  function chunkArtifactComment(input) {
222
- const byteLimit = input.byteLimit || 60_000;
257
+ const byteLimit = input.byteLimit || COMMENT_BYTE_LIMIT;
223
258
  const identity = artifactIdentity(input.artifact);
224
259
  const safeBody = sanitizeCommentBody(input.body, 'artifact body');
225
- const single = buildArtifactChunk(input.taskId, identity.stem, identity.title, input.agent, safeBody, 1, 1, false, Boolean(input.backfill));
260
+ const single = buildArtifactChunk(input.taskId, identity.stem, identity.title, input.agent, safeBody, 1, 1, false, Boolean(input.backfill), safeBody, input.recoveryMetadata);
226
261
  if (Buffer.byteLength(single.body, 'utf8') <= byteLimit)
227
262
  return [single];
228
263
  let total = 2;
@@ -231,7 +266,7 @@ function chunkArtifactComment(input) {
231
266
  if (!ranges.ok)
232
267
  throw new Error(`${ranges.error.code}: artifact body: ${ranges.error.message} at offset ${ranges.error.offset}`);
233
268
  for (;;) {
234
- const probe = buildArtifactChunk(input.taskId, identity.stem, identity.title, input.agent, '', total, total, true, Boolean(input.backfill));
269
+ const probe = buildArtifactChunk(input.taskId, identity.stem, identity.title, input.agent, '', 1, total, true, Boolean(input.backfill), '', input.recoveryMetadata);
235
270
  const available = byteLimit - Buffer.byteLength(probe.body, 'utf8');
236
271
  const sourceLimit = Math.min(available, payloadLimit);
237
272
  if (sourceLimit <= 0)
@@ -241,7 +276,7 @@ function chunkArtifactComment(input) {
241
276
  total = pieces.length;
242
277
  continue;
243
278
  }
244
- const chunks = pieces.map((piece, index) => buildArtifactChunk(input.taskId, identity.stem, identity.title, input.agent, piece.content, index + 1, total, true, Boolean(input.backfill), independentChunkContent(piece, ranges.value, sourceLimit)));
279
+ const chunks = pieces.map((piece, index) => buildArtifactChunk(input.taskId, identity.stem, identity.title, input.agent, piece.content, index + 1, total, true, Boolean(input.backfill), independentChunkContent(piece, ranges.value, sourceLimit), index === 0 ? input.recoveryMetadata : ''));
245
280
  const overflow = Math.max(...chunks.map((chunk) => Buffer.byteLength(chunk.body, 'utf8') - byteLimit));
246
281
  if (overflow <= 0)
247
282
  return chunks;
@@ -257,11 +292,6 @@ function flattenComments(value) {
257
292
  const flattened = value.flatMap((entry) => Array.isArray(entry) ? entry : [entry]);
258
293
  return flattened.filter((entry) => Boolean(entry && typeof entry === 'object' && (typeof entry.id === 'number' || typeof entry.id === 'string') && typeof entry.body === 'string'));
259
294
  }
260
- function issueNumberFromTask(content) {
261
- const value = parseTaskFrontmatter(content).issue_number;
262
- const number = Number(value);
263
- return Number.isInteger(number) && number > 0 ? number : null;
264
- }
265
295
  function listRemoteComments(client, repo, issue, cwd) {
266
296
  const result = client.json([
267
297
  'api', '--paginate', '--slurp', `repos/${repo}/issues/${issue}/comments?per_page=100`
@@ -277,9 +307,11 @@ function contextFields(context) {
277
307
  function hasResolvedPlatformContext(context) {
278
308
  return context.status === 'degraded' || (context.status === 'no-op' && context.error === null);
279
309
  }
280
- function bodyEnvelope(marker, title, taskId, agent, body) {
310
+ function bodyEnvelope(marker, title, taskId, agent, body, recoveryMetadata = '') {
281
311
  return normalizeCommentContent([
282
- marker, `## ${title}`, '', `> **${agent}** · ${taskId}`, '', sanitizeCommentBody(body, `${title} body`).replace(/\n+$/, ''), '', footer(agent, taskId)
312
+ marker, `## ${title}`, '', `> **${agent}** · ${taskId}`, '',
313
+ ...(recoveryMetadata ? [recoveryMetadata.replace(/\n+$/, ''), ''] : []),
314
+ sanitizeCommentBody(body, `${title} body`).replace(/\n+$/, ''), '', footer(agent, taskId)
283
315
  ].join('\n'));
284
316
  }
285
317
  function resolveArtifactBody(taskDir, artifact) {
@@ -296,9 +328,9 @@ function resolveArtifactBody(taskDir, artifact) {
296
328
  throw new Error('artifact must stay inside the task directory');
297
329
  return fs.readFileSync(realArtifact, 'utf8');
298
330
  }
299
- function expectedComments(taskId, taskContent, taskDir, options) {
331
+ function expectedComments(taskId, taskContent, taskDir, repoRoot, options) {
300
332
  if (options.kind === 'task') {
301
- const body = renderTaskComment(taskContent, taskId, options.agent);
333
+ const body = renderTaskComment(taskContent, taskId, options.agent, taskCommentLanguage(repoRoot));
302
334
  return [{ marker: MARKERS.task(taskId), body, content: taskContent, part: 1, total: 1 }];
303
335
  }
304
336
  if (options.kind === 'artifact') {
@@ -309,14 +341,18 @@ function expectedComments(taskId, taskContent, taskDir, options) {
309
341
  artifact: options.artifact,
310
342
  agent: options.agent,
311
343
  body: resolveArtifactBody(taskDir, options.artifact),
312
- backfill: options.backfill
344
+ backfill: options.backfill,
345
+ recoveryMetadata: renderActivityRecoveryMetadata(recordForArtifact(taskId, taskContent, options.artifact))
313
346
  });
314
347
  }
315
348
  if (options.body === undefined)
316
349
  throw new Error(`${options.kind} sync requires a body`);
317
350
  const marker = options.kind === 'summary' ? MARKERS.summary(taskId) : MARKERS.cancel(taskId);
318
351
  const title = options.kind === 'summary' ? '交付摘要' : '任务取消';
319
- const body = bodyEnvelope(marker, title, taskId, options.agent, options.body);
352
+ const recoveryMetadata = options.kind === 'summary'
353
+ ? renderActivityRecoveryMetadata(recordForSummary(taskId, taskContent))
354
+ : renderActivityRecoveryMetadata(recordForCancel(taskId, taskContent));
355
+ const body = bodyEnvelope(marker, title, taskId, options.agent, options.body, recoveryMetadata);
320
356
  return [{ marker, body, content: options.body, part: 1, total: 1 }];
321
357
  }
322
358
  function markerPrefix(taskId, options) {
@@ -374,19 +410,19 @@ async function syncPlatformComment(taskRef, options) {
374
410
  const taskContent = fs.readFileSync(resolved.taskMdPath, 'utf8');
375
411
  let issueIdentityFromTask;
376
412
  try {
377
- issueIdentityFromTask = taskIssueIdentity(parseTaskFrontmatter(taskContent), undefined, options.runtimeVersion);
413
+ issueIdentityFromTask = taskIssueIdentity(parseTaskFrontmatter(taskContent));
378
414
  }
379
415
  catch (error) {
380
416
  return platformResult('failed', { error: { ...taskIssueIdentityError(error), retryable: false } });
381
417
  }
382
418
  if (!issueIdentityFromTask) {
383
419
  return platformResult('no-op', {
384
- error: { code: 'ISSUE_NOT_LINKED', message: 'Task has no valid issue_number', retryable: false }
420
+ error: { code: 'ISSUE_NOT_LINKED', message: 'Task has no valid platform issue identity', retryable: false }
385
421
  });
386
422
  }
387
423
  let desired;
388
424
  try {
389
- desired = expectedComments(resolved.taskId, taskContent, resolved.taskDir, options);
425
+ desired = expectedComments(resolved.taskId, taskContent, resolved.taskDir, resolved.repoRoot, options);
390
426
  }
391
427
  catch (error) {
392
428
  return platformResult('failed', {
@@ -394,6 +430,13 @@ async function syncPlatformComment(taskRef, options) {
394
430
  error: { code: 'COMMENT_PAYLOAD_INVALID', message: error instanceof Error ? error.message : String(error), retryable: false }
395
431
  });
396
432
  }
433
+ if (options.kind === 'task' && Buffer.byteLength(desired[0].body, 'utf8') > COMMENT_BYTE_LIMIT) {
434
+ return platformResult('failed', {
435
+ resource: { kind: 'issue', number: resourceIdentityNumber(issueIdentityFromTask), identity: issueIdentityFromTask },
436
+ operations: [{ name: `comment:${MARKERS.task(resolved.taskId)}`, status: 'skipped', reasonCode: 'COMMENT_PAYLOAD_TOO_LARGE' }],
437
+ error: { code: 'COMMENT_PAYLOAD_TOO_LARGE', message: 'Projected task comment exceeds the platform byte limit', retryable: false }
438
+ });
439
+ }
397
440
  const loaded = await resolvePlatformProviderContext({ cwd: resolved.repoRoot, client: options.client });
398
441
  const context = loaded.ok ? loaded.value.context : loaded.context;
399
442
  if (!hasResolvedPlatformContext(context) || !loaded.ok)
@@ -424,6 +467,20 @@ async function syncPlatformComment(taskRef, options) {
424
467
  error: { code: 'COMMENT_MARKER_CONFLICT', message: 'Multiple comments use the same registered marker', retryable: false }
425
468
  });
426
469
  }
470
+ if (options.kind === 'task' && existing.length === 1) {
471
+ const owner = existing[0].user?.login;
472
+ if (!owner || !context.platform.currentUser || owner !== context.platform.currentUser) {
473
+ return platformResult('blocked', {
474
+ ...contextFields(context),
475
+ resource: { kind: 'issue', number: issue, identity: issueIdentityFromTask },
476
+ error: {
477
+ code: 'COMMENT_OWNER_CONFLICT',
478
+ message: owner ? `Task comment is owned by '${owner}'` : 'Task comment owner is unavailable',
479
+ retryable: false
480
+ }
481
+ });
482
+ }
483
+ }
427
484
  // Backfill only supplies missing artifact comments; valid existing marker sets stay untouched.
428
485
  if (options.kind === 'artifact' && options.backfill && existing.length > 0) {
429
486
  const operations = existing.map((comment) => ({
@@ -499,14 +556,15 @@ async function syncPlatformComment(taskRef, options) {
499
556
  operations.push({ name: `comment:${comment.id}`, status: 'applied', reasonCode: 'STALE_CHUNK_DELETED' });
500
557
  }
501
558
  const changed = operations.some((operation) => operation.status === 'applied');
502
- return platformResult(changed ? 'applied' : 'no-op', {
559
+ const result = platformResult(changed ? 'applied' : 'no-op', {
503
560
  ...contextFields(context),
504
561
  changed,
505
562
  resource: { kind: 'issue', number: issue },
506
563
  operations,
507
- comment: { kind: options.kind, marker: desired[0].marker, ids, parts: desired.length },
564
+ comment: { kind: options.kind, marker: desired[0]?.marker ?? markerPrefix(resolved.taskId, options), ids, parts: desired.length },
508
565
  error: null
509
566
  });
567
+ return result;
510
568
  }
511
569
  async function listPlatformComments(issue, cwd = process.cwd(), client) {
512
570
  const loaded = await resolvePlatformProviderContext({ cwd, client });
@@ -541,7 +599,7 @@ async function checkPlatformCommentOwner(taskRef, options = {}) {
541
599
  const content = fs.readFileSync(resolved.taskMdPath, 'utf8');
542
600
  let issueIdentity;
543
601
  try {
544
- issueIdentity = taskIssueIdentity(parseTaskFrontmatter(content), undefined, options.runtimeVersion);
602
+ issueIdentity = taskIssueIdentity(parseTaskFrontmatter(content));
545
603
  }
546
604
  catch (error) {
547
605
  return platformResult('failed', { error: { ...taskIssueIdentityError(error), retryable: false } });
@@ -572,5 +630,5 @@ async function checkPlatformCommentOwner(taskRef, options = {}) {
572
630
  }
573
631
  return platformResult('no-op', { ...contextFields(context), resource: { kind: 'issue', number: resourceIdentityNumber(issueIdentity), identity: issueIdentity }, error: null });
574
632
  }
575
- export { MARKERS, chunkArtifactComment, findMarkerComments, listRemoteComments, checkPlatformCommentOwner, listPlatformComments, normalizeCommentContent, renderTaskComment, syncPlatformComment, validateRelatedMarkerSet, writeComment };
633
+ export { COMMENT_BYTE_LIMIT, MARKERS, chunkArtifactComment, findMarkerComments, listRemoteComments, checkPlatformCommentOwner, listPlatformComments, normalizeCommentContent, renderTaskComment, renderTaskCommentResult, isTaskCommentTooLarge, syncPlatformComment, validateRelatedMarkerSet, writeComment };
576
634
  //# sourceMappingURL=issue-comments.js.map
@@ -32,7 +32,7 @@ async function resolvedContext(taskRef, options) {
32
32
  const frontmatter = parseTaskFrontmatter(content);
33
33
  let issueIdentity;
34
34
  try {
35
- issueIdentity = taskIssueIdentity(frontmatter, undefined, options.runtimeVersion);
35
+ issueIdentity = taskIssueIdentity(frontmatter);
36
36
  }
37
37
  catch (error) {
38
38
  return { ok: false, output: result('failed', resolved.taskId, null, { error: { ...taskIssueIdentityError(error), retryable: false } }) };
@@ -3,8 +3,9 @@ import path from 'node:path';
3
3
  import { createHash, randomUUID } from 'node:crypto';
4
4
  import { execFileSync } from 'node:child_process';
5
5
  import { escapeHtmlText } from './comment-safety.js';
6
+ import { isResourceIdentity } from './resource-identity.js';
6
7
  const REPORT_FILE_NAME = 'pr-change-report.json';
7
- const REPORT_VERSION = 1;
8
+ const REPORT_VERSION = 2;
8
9
  const CANONICAL_REPORT_PLACEHOLDER = '<!-- canonical-pr-change-report -->';
9
10
  const CANONICAL_REPORT_HEADING = '### PR 代码增减';
10
11
  const PRECHECK_IDS = [
@@ -211,8 +212,8 @@ function validatePrChangeReport(value) {
211
212
  }
212
213
  const identity = value.identity;
213
214
  const identityRepository = identity.repository;
214
- const identityNumber = identity.number;
215
- if (!isNonEmptyString(identityRepository) || typeof identityNumber !== 'number' || !Number.isSafeInteger(identityNumber) || identityNumber <= 0)
215
+ const identityResource = identity.resource;
216
+ if (!isNonEmptyString(identityRepository) || !isResourceIdentity(identityResource))
216
217
  return invalid('change report identity is invalid');
217
218
  const parseIdentityPart = (part, name) => {
218
219
  if (!isRecord(part) || !isNonEmptyString(part.repository) || !isNonEmptyString(part.ref) || !isSha(part.sha))
@@ -259,7 +260,7 @@ function validatePrChangeReport(value) {
259
260
  version: REPORT_VERSION,
260
261
  identity: {
261
262
  repository: identityRepository,
262
- number: identityNumber,
263
+ resource: identityResource,
263
264
  base: base.value,
264
265
  head: head.value
265
266
  },