@fitlab-ai/agent-infra 0.9.12 → 0.9.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -11
- package/README.zh-CN.md +11 -11
- package/bin/cli.ts +26 -9
- package/dist/bin/cli.js +173 -157
- package/dist/bin/internal-cli.js +66 -40
- package/dist/lib/agent-client.js +5 -0
- package/dist/lib/agent-clients/next-steps.js +6 -1
- package/dist/lib/decide.js +3 -3
- package/dist/lib/internal/agent-client.js +5 -2
- package/dist/lib/internal/cli-route-inventory.js +152 -0
- package/dist/lib/internal/codex-lifecycle.js +9 -6
- package/dist/lib/internal/codex-sandbox-controller.js +8 -2
- package/dist/lib/internal/git-workflow.js +10 -6
- package/dist/lib/internal/platform-checks.js +19 -7
- package/dist/lib/internal/platform-comment.js +25 -13
- package/dist/lib/internal/platform-context.js +6 -3
- package/dist/lib/internal/platform-issue.js +20 -13
- package/dist/lib/internal/platform-metadata.js +88 -0
- package/dist/lib/internal/platform-pr-review.js +26 -13
- package/dist/lib/internal/platform-pr.js +84 -31
- package/dist/lib/internal/platform-release-notes.js +9 -6
- package/dist/lib/internal/platform-security.js +93 -0
- package/dist/lib/internal/pr-review-grade.js +25 -12
- package/dist/lib/internal/release-workflow.js +23 -9
- package/dist/lib/internal/sandbox-control.js +50 -7
- package/dist/lib/internal/task-activity.js +16 -7
- package/dist/lib/internal/task-artifact.js +235 -24
- package/dist/lib/internal/task-context.js +4 -1
- package/dist/lib/internal/task-create.js +50 -17
- package/dist/lib/internal/task-delivery.js +4 -1
- package/dist/lib/internal/task-event.js +10 -4
- package/dist/lib/internal/task-finalization.js +6 -3
- package/dist/lib/internal/task-invalidation.js +63 -0
- package/dist/lib/internal/task-ledger.js +20 -6
- package/dist/lib/internal/task-lifecycle.js +6 -3
- package/dist/lib/internal/task-operation-registry.js +421 -0
- package/dist/lib/internal/task-orchestration.js +12 -0
- package/dist/lib/internal/task-override.js +14 -7
- package/dist/lib/internal/task-qualification.js +90 -0
- package/dist/lib/internal/task-review.js +7 -4
- package/dist/lib/internal/task-short-id.js +11 -1
- package/dist/lib/internal/task-snapshot.js +4 -1
- package/dist/lib/internal/task-validate.js +7 -2
- package/dist/lib/internal/task-verify.js +11 -7
- package/dist/lib/internal/task-warning.js +11 -4
- package/dist/lib/platform/adapters.js +1 -3
- package/dist/lib/platform/change-request-git-evidence.js +27 -7
- package/dist/lib/platform/comment-safety.js +226 -0
- package/dist/lib/platform/completion-backfill.js +6 -5
- package/dist/lib/platform/context.js +70 -130
- package/dist/lib/platform/github-client.js +1 -1
- package/dist/lib/platform/github-provider.js +1130 -0
- package/dist/lib/platform/in-label-sync.js +143 -0
- package/dist/lib/platform/issue-comments.js +237 -72
- package/dist/lib/platform/issues.js +335 -189
- package/dist/lib/platform/metadata-labels.js +6 -2
- package/dist/lib/platform/none-provider.js +28 -0
- package/dist/lib/platform/pr-change-report.js +545 -0
- package/dist/lib/platform/pr-checks.js +119 -104
- package/dist/lib/platform/pr-review.js +88 -72
- package/dist/lib/platform/pr-summary.js +541 -50
- package/dist/lib/platform/provider-bridge.js +45 -0
- package/dist/lib/platform/provider-contract.d.ts +553 -0
- package/dist/lib/platform/provider-contract.js +99 -0
- package/dist/lib/platform/provider-loader.js +251 -0
- package/dist/lib/platform/provider-validation.js +577 -0
- package/dist/lib/platform/pull-request-metadata.js +8 -6
- package/dist/lib/platform/pull-requests.js +734 -543
- package/dist/lib/platform/release-notes.js +78 -55
- package/dist/lib/platform/releases.js +79 -50
- package/dist/lib/platform/repository-metadata.js +377 -0
- package/dist/lib/platform/resource-identity.d.ts +26 -0
- package/dist/lib/platform/resource-identity.js +92 -0
- package/dist/lib/platform/security-alerts.js +155 -0
- package/dist/lib/platform/task-identities.js +31 -0
- package/dist/lib/platform/verification-required.js +11 -12
- package/dist/lib/platform/verification-sync.js +129 -248
- package/dist/lib/pr-review/evidence-grading.js +19 -6
- package/dist/lib/process-data/index.js +7 -0
- package/dist/lib/run/index.js +68 -23
- package/dist/lib/sandbox/commands/control-status.js +22 -0
- package/dist/lib/sandbox/commands/create.js +32 -6
- package/dist/lib/sandbox/commands/ls.js +5 -0
- package/dist/lib/sandbox/commands/rm.js +827 -120
- package/dist/lib/sandbox/commands/show.js +16 -0
- package/dist/lib/sandbox/control/client.js +174 -19
- package/dist/lib/sandbox/control/completed-reentry.js +85 -0
- package/dist/lib/sandbox/control/container-identity.js +28 -31
- package/dist/lib/sandbox/control/executor.js +151 -16
- package/dist/lib/sandbox/control/lifecycle.js +621 -127
- package/dist/lib/sandbox/control/native-file-lock.js +177 -0
- package/dist/lib/sandbox/control/protocol.js +77 -0
- package/dist/lib/sandbox/control/server.js +677 -34
- package/dist/lib/sandbox/control/state.js +209 -4
- package/dist/lib/sandbox/control/task-view.js +183 -0
- package/dist/lib/sandbox/engines/authority.js +289 -0
- package/dist/lib/sandbox/engines/docker-exact-discovery.js +97 -0
- package/dist/lib/sandbox/index.js +5 -0
- package/dist/lib/sandbox/recovery.js +21 -1
- package/dist/lib/sandbox/workspace-view.js +2 -1
- package/dist/lib/sandbox/worktree-safety.js +4 -1
- package/dist/lib/server/index.js +8 -0
- package/dist/lib/server/protocol.js +38 -1
- package/dist/lib/task/activity-log.js +8 -1
- package/dist/lib/task/artifact-lifecycle.js +75 -18
- package/dist/lib/task/artifact-operations.js +520 -0
- package/dist/lib/task/artifact-repair-intent.js +59 -0
- package/dist/lib/task/artifact-schema.js +135 -0
- package/dist/lib/task/capabilities.js +310 -0
- package/dist/lib/task/commands/status.js +32 -0
- package/dist/lib/task/commit-operation.js +12 -1
- package/dist/lib/task/control-authority.js +12 -5
- package/dist/lib/task/create-service.js +111 -12
- package/dist/lib/task/create.js +25 -0
- package/dist/lib/task/current-contract.js +5 -1
- package/dist/lib/task/decision-details.js +1 -1
- package/dist/lib/task/events.js +416 -17
- package/dist/lib/task/finalization.js +37 -13
- package/dist/lib/task/human-override.js +58 -36
- package/dist/lib/task/index.js +11 -3
- package/dist/lib/task/invalidation-command.js +86 -0
- package/dist/lib/task/invalidation.js +260 -0
- package/dist/lib/task/ledger-intents.js +37 -0
- package/dist/lib/task/local-artifact-finalization.js +299 -0
- package/dist/lib/task/orchestration.js +240 -144
- package/dist/lib/task/pr-delivery-fact.js +102 -101
- package/dist/lib/task/qualification-audit.js +490 -0
- package/dist/lib/task/qualification-confirmation.js +109 -0
- package/dist/lib/task/qualification-intents.js +148 -0
- package/dist/lib/task/review-artifacts.js +22 -23
- package/dist/lib/task/review-finalization.js +121 -4
- package/dist/lib/task/review-fingerprint.js +24 -2
- package/dist/lib/task/rework-intent.js +138 -0
- package/dist/lib/task/sections.js +6 -4
- package/dist/lib/task/skill-scope.js +23 -0
- package/dist/lib/task/task-execution-lock.js +27 -17
- package/dist/lib/task/verification-config.js +11 -0
- package/dist/lib/task/verification-engine.js +93 -16
- package/dist/lib/task/verification.js +3 -3
- package/dist/lib/task/workflow-warning-intents.js +1 -1
- package/dist/lib/task/write.js +11 -0
- package/dist/lib/version.js +11 -1
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-client.ts +9 -0
- package/lib/agent-clients/next-steps.ts +6 -1
- package/lib/decide.ts +3 -3
- package/lib/init.ts +1 -1
- package/lib/internal/agent-client.ts +4 -2
- package/lib/internal/cli-route-inventory.ts +140 -0
- package/lib/internal/codex-lifecycle.ts +8 -6
- package/lib/internal/codex-sandbox-controller.ts +7 -2
- package/lib/internal/git-workflow.ts +9 -6
- package/lib/internal/platform-checks.ts +14 -6
- package/lib/internal/platform-comment.ts +20 -12
- package/lib/internal/platform-context.ts +5 -3
- package/lib/internal/platform-issue.ts +17 -11
- package/lib/internal/platform-metadata.ts +70 -0
- package/lib/internal/platform-pr-review.ts +23 -12
- package/lib/internal/platform-pr.ts +65 -27
- package/lib/internal/platform-release-notes.ts +8 -6
- package/lib/internal/platform-security.ts +83 -0
- package/lib/internal/pr-review-grade.ts +20 -11
- package/lib/internal/release-workflow.ts +21 -9
- package/lib/internal/sandbox-control.ts +44 -6
- package/lib/internal/task-activity.ts +15 -7
- package/lib/internal/task-artifact.ts +204 -22
- package/lib/internal/task-context.ts +3 -1
- package/lib/internal/task-create.ts +78 -17
- package/lib/internal/task-delivery.ts +3 -1
- package/lib/internal/task-event.ts +9 -4
- package/lib/internal/task-finalization.ts +6 -4
- package/lib/internal/task-invalidation.ts +41 -0
- package/lib/internal/task-ledger.ts +21 -8
- package/lib/internal/task-lifecycle.ts +5 -3
- package/lib/internal/task-operation-registry.ts +482 -0
- package/lib/internal/task-orchestration.ts +12 -0
- package/lib/internal/task-override.ts +15 -9
- package/lib/internal/task-qualification.ts +61 -0
- package/lib/internal/task-review.ts +6 -4
- package/lib/internal/task-short-id.ts +10 -1
- package/lib/internal/task-snapshot.ts +3 -1
- package/lib/internal/task-validate.ts +6 -2
- package/lib/internal/task-verify.ts +10 -7
- package/lib/internal/task-warning.ts +10 -4
- package/lib/platform/adapters.ts +3 -2
- package/lib/platform/change-request-git-evidence.ts +36 -9
- package/lib/platform/comment-safety.ts +250 -0
- package/lib/platform/completion-backfill.ts +9 -8
- package/lib/platform/context.ts +97 -134
- package/lib/platform/github-client.ts +2 -2
- package/lib/platform/github-provider.ts +1132 -0
- package/lib/platform/in-label-sync.ts +208 -0
- package/lib/platform/issue-comments.ts +255 -80
- package/lib/platform/issues.ts +344 -174
- package/lib/platform/metadata-labels.ts +5 -2
- package/lib/platform/none-provider.ts +35 -0
- package/lib/platform/pr-change-report.ts +639 -0
- package/lib/platform/pr-checks.ts +126 -106
- package/lib/platform/pr-review.ts +88 -85
- package/lib/platform/pr-summary.ts +581 -53
- package/lib/platform/provider-bridge.ts +82 -0
- package/lib/platform/provider-contract.ts +611 -0
- package/lib/platform/provider-loader.ts +307 -0
- package/lib/platform/provider-validation.ts +611 -0
- package/lib/platform/pull-request-metadata.ts +10 -7
- package/lib/platform/pull-requests.ts +781 -550
- package/lib/platform/release-notes.ts +76 -59
- package/lib/platform/releases.ts +77 -43
- package/lib/platform/repository-metadata.ts +453 -0
- package/lib/platform/resource-identity.ts +121 -0
- package/lib/platform/security-alerts.ts +191 -0
- package/lib/platform/task-identities.ts +40 -0
- package/lib/platform/types.ts +4 -2
- package/lib/platform/verification-required.ts +11 -13
- package/lib/platform/verification-sync.ts +128 -304
- package/lib/pr-review/evidence-grading.ts +25 -8
- package/lib/process-data/index.ts +7 -0
- package/lib/run/index.ts +62 -22
- package/lib/sandbox/commands/control-status.ts +26 -0
- package/lib/sandbox/commands/create.ts +46 -6
- package/lib/sandbox/commands/ls.ts +7 -0
- package/lib/sandbox/commands/rm.ts +1000 -87
- package/lib/sandbox/commands/show.ts +16 -0
- package/lib/sandbox/control/client.ts +203 -19
- package/lib/sandbox/control/completed-reentry.ts +98 -0
- package/lib/sandbox/control/container-identity.ts +27 -43
- package/lib/sandbox/control/executor.ts +180 -15
- package/lib/sandbox/control/lifecycle.ts +629 -17
- package/lib/sandbox/control/native-file-lock.ts +209 -0
- package/lib/sandbox/control/protocol.ts +118 -2
- package/lib/sandbox/control/server.ts +724 -34
- package/lib/sandbox/control/state.ts +257 -4
- package/lib/sandbox/control/task-view.ts +240 -0
- package/lib/sandbox/engines/authority.ts +365 -0
- package/lib/sandbox/engines/docker-exact-discovery.ts +132 -0
- package/lib/sandbox/index.ts +7 -0
- package/lib/sandbox/recovery.ts +21 -1
- package/lib/sandbox/workspace-view.ts +9 -3
- package/lib/sandbox/worktree-safety.ts +4 -1
- package/lib/server/index.ts +10 -0
- package/lib/server/protocol.ts +36 -1
- package/lib/task/activity-log.ts +8 -0
- package/lib/task/artifact-lifecycle.ts +70 -22
- package/lib/task/artifact-operations.ts +626 -0
- package/lib/task/artifact-repair-intent.ts +71 -0
- package/lib/task/artifact-schema.ts +192 -0
- package/lib/task/capabilities.ts +331 -0
- package/lib/task/commands/status.ts +38 -0
- package/lib/task/commit-operation.ts +12 -1
- package/lib/task/control-authority.ts +22 -9
- package/lib/task/create-service.ts +139 -14
- package/lib/task/create.ts +27 -0
- package/lib/task/current-contract.ts +6 -1
- package/lib/task/decision-details.ts +6 -2
- package/lib/task/events.ts +392 -16
- package/lib/task/finalization.ts +50 -14
- package/lib/task/human-override.ts +70 -46
- package/lib/task/index.ts +16 -3
- package/lib/task/invalidation-command.ts +86 -0
- package/lib/task/invalidation.ts +332 -0
- package/lib/task/ledger-intents.ts +35 -1
- package/lib/task/local-artifact-finalization.ts +432 -0
- package/lib/task/orchestration.ts +223 -133
- package/lib/task/pr-delivery-fact.ts +131 -148
- package/lib/task/qualification-audit.ts +599 -0
- package/lib/task/qualification-confirmation.ts +117 -0
- package/lib/task/qualification-intents.ts +150 -0
- package/lib/task/review-artifacts.ts +21 -22
- package/lib/task/review-finalization.ts +133 -4
- package/lib/task/review-fingerprint.ts +23 -2
- package/lib/task/rework-intent.ts +165 -0
- package/lib/task/sections.ts +6 -4
- package/lib/task/skill-scope.ts +24 -0
- package/lib/task/task-execution-lock.ts +23 -14
- package/lib/task/verification-config.ts +12 -0
- package/lib/task/verification-engine.ts +103 -17
- package/lib/task/verification.ts +4 -4
- package/lib/task/workflow-warning-intents.ts +1 -1
- package/lib/task/write.ts +19 -0
- package/lib/update.ts +1 -1
- package/lib/version.ts +15 -1
- package/package.json +12 -1
- package/templates/.agents/rules/README.en.md +3 -1
- package/templates/.agents/rules/README.zh-CN.md +3 -1
- package/templates/.agents/rules/decision-qualification.en.md +22 -0
- package/templates/.agents/rules/decision-qualification.zh-CN.md +22 -0
- package/templates/.agents/rules/evidence-reporting.en.md +28 -0
- package/templates/.agents/rules/evidence-reporting.zh-CN.md +28 -0
- package/templates/.agents/rules/issue-pr-commands.en.md +1 -1
- package/templates/.agents/rules/issue-pr-commands.zh-CN.md +1 -1
- package/templates/.agents/rules/issue-sync.en.md +2 -0
- package/templates/.agents/rules/issue-sync.zh-CN.md +2 -0
- package/templates/.agents/rules/label-milestone-setup.en.md +7 -7
- package/templates/.agents/rules/label-milestone-setup.zh-CN.md +7 -7
- package/templates/.agents/rules/local-artifact-repair.en.md +13 -2
- package/templates/.agents/rules/local-artifact-repair.zh-CN.md +13 -2
- package/templates/.agents/rules/pr-sync.en.md +10 -4
- package/templates/.agents/rules/pr-sync.zh-CN.md +17 -4
- package/templates/.agents/rules/security-alerts.en.md +8 -8
- package/templates/.agents/rules/security-alerts.zh-CN.md +8 -8
- package/templates/.agents/rules/task-short-id.en.md +1 -4
- package/templates/.agents/rules/task-short-id.zh-CN.md +1 -3
- package/templates/.agents/skills/analyze-task/SKILL.en.md +28 -5
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +29 -5
- package/templates/.agents/skills/analyze-task/config/verify.en.json +1 -12
- package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -12
- package/templates/.agents/skills/check-task/SKILL.en.md +16 -37
- package/templates/.agents/skills/check-task/SKILL.zh-CN.md +16 -37
- package/templates/.agents/skills/close-codescan/SKILL.en.md +2 -2
- package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/close-dependabot/SKILL.en.md +2 -2
- package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/code-task/SKILL.en.md +41 -9
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +43 -11
- package/templates/.agents/skills/code-task/config/verify.en.json +6 -14
- package/templates/.agents/skills/code-task/config/verify.zh-CN.json +6 -14
- package/templates/.agents/skills/code-task/reference/code-rules.en.md +5 -4
- package/templates/.agents/skills/code-task/reference/code-rules.zh-CN.md +5 -4
- package/templates/.agents/skills/code-task/reference/dual-mode.en.md +1 -1
- package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +1 -1
- package/templates/.agents/skills/code-task/reference/fix-mode.en.md +3 -1
- package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +3 -1
- package/templates/.agents/skills/code-task/reference/report-template.en.md +42 -15
- package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +39 -12
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +2 -0
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +2 -0
- package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +3 -3
- package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +3 -3
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +10 -4
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +10 -4
- package/templates/.agents/skills/complete-manual-validation/reference/report-template.en.md +2 -0
- package/templates/.agents/skills/complete-manual-validation/reference/report-template.zh-CN.md +2 -0
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +4 -3
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +4 -3
- package/templates/.agents/skills/complete-task/SKILL.en.md +26 -21
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +25 -21
- package/templates/.agents/skills/create-pr/SKILL.en.md +7 -6
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +7 -7
- package/templates/.agents/skills/create-pr/config/verify.json +1 -0
- package/templates/.agents/skills/create-pr/reference/change-report.en.md +16 -4
- package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +16 -4
- package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +5 -3
- package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +5 -3
- package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +1 -1
- package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +1 -1
- package/templates/.agents/skills/create-pr/scripts/change-report.mjs +3 -0
- package/templates/.agents/skills/create-task/SKILL.en.md +8 -0
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +7 -0
- package/templates/.agents/skills/import-codescan/SKILL.en.md +1 -1
- package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/import-dependabot/SKILL.en.md +1 -1
- package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/init-labels/SKILL.en.md +8 -8
- package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +8 -8
- package/templates/.agents/skills/init-milestones/SKILL.en.md +9 -11
- package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +9 -11
- package/templates/.agents/skills/plan-task/SKILL.en.md +27 -4
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +26 -4
- package/templates/.agents/skills/plan-task/config/verify.en.json +4 -14
- package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +4 -14
- package/templates/.agents/skills/review-analysis/SKILL.en.md +17 -4
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +17 -4
- package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -18
- package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -18
- package/templates/.agents/skills/review-analysis/reference/report-template.en.md +50 -23
- package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +45 -18
- package/templates/.agents/skills/review-code/SKILL.en.md +18 -5
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +18 -5
- package/templates/.agents/skills/review-code/config/verify.en.json +1 -23
- package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -23
- package/templates/.agents/skills/review-code/reference/report-template.en.md +49 -22
- package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +45 -18
- package/templates/.agents/skills/review-plan/SKILL.en.md +17 -4
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +17 -4
- package/templates/.agents/skills/review-plan/config/verify.en.json +1 -18
- package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -18
- package/templates/.agents/skills/review-plan/reference/report-template.en.md +49 -22
- package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +45 -18
- package/templates/.agents/skills/review-pr/SKILL.en.md +5 -1
- package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +5 -1
- package/templates/.agents/skills/review-pr/reference/report-template.en.md +4 -3
- package/templates/.agents/skills/review-pr/reference/report-template.zh-CN.md +4 -3
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +9 -3
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +9 -3
- package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +3 -1
- package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +3 -1
- package/templates/.agents/skills/test/SKILL.en.md +13 -2
- package/templates/.agents/skills/test/SKILL.zh-CN.md +13 -2
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +14 -0
- package/templates/.agents/skills/watch-pr/SKILL.en.md +2 -2
- package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +6 -0
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +6 -0
- package/templates/.agents/templates/task.en.md +24 -1
- package/templates/.agents/templates/task.zh-CN.md +24 -1
- package/templates/.github/workflows/pr-label.yml +7 -26
- package/templates/.agents/scripts/security-alerts.github.sh +0 -151
- package/templates/.agents/scripts/security-alerts.sh +0 -31
- package/templates/.agents/skills/init-labels/scripts/init-labels.github.sh +0 -146
- package/templates/.agents/skills/init-labels/scripts/init-labels.sh +0 -32
- package/templates/.agents/skills/init-milestones/scripts/init-milestones.github.sh +0 -294
- package/templates/.agents/skills/init-milestones/scripts/init-milestones.sh +0 -6
|
@@ -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,15 @@ 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 {
|
|
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
|
+
import { CONTROL_MARKER_PATTERN, escapeHtmlText, fenceRanges, renderSafeCodeFence, sanitizeMarkdownDocument } from './comment-safety.js';
|
|
11
|
+
function providerCommentId(id, provider) {
|
|
12
|
+
return provider.identity?.comment === 'number' && /^\d+$/.test(id) ? Number(id) : id;
|
|
13
|
+
}
|
|
8
14
|
const MARKERS = {
|
|
9
15
|
task: (taskId) => `<!-- sync-issue:${taskId}:task -->`,
|
|
10
16
|
artifact: (taskId, stem) => `<!-- sync-issue:${taskId}:${stem} -->`,
|
|
@@ -32,14 +38,21 @@ function splitFrontmatter(content) {
|
|
|
32
38
|
return { frontmatter: null, body: content };
|
|
33
39
|
return { frontmatter: match[1], body: content.slice(match[0].length).replace(/^\r?\n/, '') };
|
|
34
40
|
}
|
|
41
|
+
function sanitizeCommentBody(value, label) {
|
|
42
|
+
const result = sanitizeMarkdownDocument(value, { reservedMarkers: [CONTROL_MARKER_PATTERN] });
|
|
43
|
+
if (!result.ok)
|
|
44
|
+
throw new Error(`${result.error.code}: ${label}: ${result.error.message} at offset ${result.error.offset}`);
|
|
45
|
+
return result.value;
|
|
46
|
+
}
|
|
35
47
|
function footer(agent, taskId) {
|
|
36
48
|
return `---\n*由 ${agent} 自动生成 · 内部追踪:${taskId}*`;
|
|
37
49
|
}
|
|
38
50
|
function renderTaskComment(content, taskId, agent) {
|
|
39
51
|
const split = splitFrontmatter(content);
|
|
52
|
+
const safeBody = sanitizeCommentBody(split.body, 'task body');
|
|
40
53
|
const taskBody = split.frontmatter === null
|
|
41
|
-
?
|
|
42
|
-
: `<details><summary>元数据 (frontmatter)</summary>\n\n
|
|
54
|
+
? safeBody
|
|
55
|
+
: `<details><summary>元数据 (frontmatter)</summary>\n\n${renderSafeCodeFence(`---\n${split.frontmatter}\n---`, 'yaml')}\n\n</details>\n\n${safeBody}`;
|
|
43
56
|
return normalizeCommentContent([
|
|
44
57
|
MARKERS.task(taskId),
|
|
45
58
|
'## 任务文件',
|
|
@@ -64,10 +77,11 @@ function chunkByUtf8(content, maxBytes) {
|
|
|
64
77
|
if (maxBytes <= 0)
|
|
65
78
|
throw new Error('comment byte limit is too small');
|
|
66
79
|
const chunks = [];
|
|
67
|
-
let
|
|
68
|
-
while (
|
|
80
|
+
let start = 0;
|
|
81
|
+
while (start < content.length) {
|
|
82
|
+
const remaining = content.slice(start);
|
|
69
83
|
if (Buffer.byteLength(remaining, 'utf8') <= maxBytes) {
|
|
70
|
-
chunks.push(remaining);
|
|
84
|
+
chunks.push({ content: remaining, start, end: content.length });
|
|
71
85
|
break;
|
|
72
86
|
}
|
|
73
87
|
let bytes = 0;
|
|
@@ -85,12 +99,109 @@ function chunkByUtf8(content, maxBytes) {
|
|
|
85
99
|
const cut = newlineIndex > 0 ? newlineIndex : index;
|
|
86
100
|
if (cut === 0)
|
|
87
101
|
throw new Error('comment byte limit cannot fit one Unicode code point');
|
|
88
|
-
chunks.push(remaining.slice(0, cut));
|
|
89
|
-
|
|
102
|
+
chunks.push({ content: remaining.slice(0, cut), start, end: start + cut });
|
|
103
|
+
start += cut;
|
|
104
|
+
}
|
|
105
|
+
return chunks.length > 0 ? chunks : [{ content: '', start: 0, end: 0 }];
|
|
106
|
+
}
|
|
107
|
+
function longestFenceLineRun(content, character) {
|
|
108
|
+
const pattern = new RegExp(`^ {0,3}(${character}+)[ \\t]*(?:\\n|$)`, 'gm');
|
|
109
|
+
let longest = 0;
|
|
110
|
+
for (const match of content.matchAll(pattern))
|
|
111
|
+
longest = Math.max(longest, match[1].length);
|
|
112
|
+
return longest;
|
|
113
|
+
}
|
|
114
|
+
function boundedFence(content, sourceCharacter, maxBytes) {
|
|
115
|
+
const candidates = sourceCharacter === '`' ? ['~', '`'] : ['`', '~'];
|
|
116
|
+
for (const character of candidates) {
|
|
117
|
+
const length = Math.max(3, longestFenceLineRun(content, character) + 1);
|
|
118
|
+
const delimiter = character.repeat(length);
|
|
119
|
+
if (Buffer.byteLength(delimiter, 'utf8') * 2 + 2 <= maxBytes)
|
|
120
|
+
return { opening: `${delimiter}\n`, closing: `${delimiter}\n` };
|
|
121
|
+
}
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
function sourceSlice(piece, start, end) {
|
|
125
|
+
return piece.content.slice(Math.max(0, start - piece.start), Math.max(0, end - piece.start));
|
|
126
|
+
}
|
|
127
|
+
function renderOversizedFenceChunk(piece, fences, maxBytes) {
|
|
128
|
+
const insertions = new Map();
|
|
129
|
+
const omitted = [];
|
|
130
|
+
const insert = (position, value, ensureLineStart = false) => {
|
|
131
|
+
const values = insertions.get(position) || [];
|
|
132
|
+
values.push({ value, ensureLineStart });
|
|
133
|
+
insertions.set(position, values);
|
|
134
|
+
};
|
|
135
|
+
for (const fence of fences) {
|
|
136
|
+
if (piece.end <= fence.start || piece.start >= fence.end)
|
|
137
|
+
continue;
|
|
138
|
+
const oversized = Buffer.byteLength(fence.opening, 'utf8') > maxBytes
|
|
139
|
+
|| Buffer.byteLength(fence.closing, 'utf8') > maxBytes;
|
|
140
|
+
if (!oversized) {
|
|
141
|
+
if (piece.start > fence.start && piece.start < fence.end)
|
|
142
|
+
insert(piece.start, fence.opening);
|
|
143
|
+
if (piece.end > fence.start && piece.end < fence.end)
|
|
144
|
+
insert(piece.end, fence.closing, true);
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
const code = sourceSlice(piece, Math.max(piece.start, fence.openingEnd), Math.min(piece.end, fence.closingStart));
|
|
148
|
+
const delimiter = boundedFence(code, fence.character, maxBytes);
|
|
149
|
+
if (!delimiter)
|
|
150
|
+
return escapeHtmlText(piece.content);
|
|
151
|
+
insert(Math.max(piece.start, fence.start), delimiter.opening);
|
|
152
|
+
insert(Math.min(piece.end, fence.end), delimiter.closing, true);
|
|
153
|
+
const openingStart = Math.max(piece.start, fence.start);
|
|
154
|
+
const openingEnd = Math.min(piece.end, fence.openingEnd);
|
|
155
|
+
if (openingStart < openingEnd)
|
|
156
|
+
omitted.push({ start: openingStart, end: openingEnd });
|
|
157
|
+
const closingStart = Math.max(piece.start, fence.closingStart);
|
|
158
|
+
const closingEnd = Math.min(piece.end, fence.end);
|
|
159
|
+
if (closingStart < closingEnd)
|
|
160
|
+
omitted.push({ start: closingStart, end: closingEnd });
|
|
161
|
+
}
|
|
162
|
+
const boundaries = new Set([piece.start, piece.end]);
|
|
163
|
+
for (const position of insertions.keys())
|
|
164
|
+
boundaries.add(position);
|
|
165
|
+
for (const range of omitted) {
|
|
166
|
+
boundaries.add(range.start);
|
|
167
|
+
boundaries.add(range.end);
|
|
90
168
|
}
|
|
91
|
-
|
|
169
|
+
const points = [...boundaries].sort((left, right) => left - right);
|
|
170
|
+
let content = '';
|
|
171
|
+
for (let index = 0; index < points.length; index += 1) {
|
|
172
|
+
const point = points[index];
|
|
173
|
+
for (const insertion of insertions.get(point) || []) {
|
|
174
|
+
if (insertion.ensureLineStart && content.length > 0 && !content.endsWith('\n'))
|
|
175
|
+
content += '\n';
|
|
176
|
+
content += insertion.value;
|
|
177
|
+
}
|
|
178
|
+
const next = points[index + 1];
|
|
179
|
+
if (next === undefined)
|
|
180
|
+
continue;
|
|
181
|
+
const skipped = omitted.some((range) => range.start <= point && next <= range.end);
|
|
182
|
+
if (!skipped)
|
|
183
|
+
content += sourceSlice(piece, point, next);
|
|
184
|
+
}
|
|
185
|
+
return content;
|
|
92
186
|
}
|
|
93
|
-
function
|
|
187
|
+
function independentChunkContent(piece, fences, maxBytes) {
|
|
188
|
+
const oversizedFence = fences.some((fence) => piece.end > fence.start && piece.start < fence.end
|
|
189
|
+
&& (Buffer.byteLength(fence.opening, 'utf8') > maxBytes || Buffer.byteLength(fence.closing, 'utf8') > maxBytes));
|
|
190
|
+
if (oversizedFence)
|
|
191
|
+
return renderOversizedFenceChunk(piece, fences, maxBytes);
|
|
192
|
+
const openingFence = fences.find((fence) => piece.start > fence.start && piece.start < fence.end);
|
|
193
|
+
const closingFence = fences.find((fence) => piece.end > fence.start && piece.end < fence.end);
|
|
194
|
+
let content = piece.content;
|
|
195
|
+
if (openingFence)
|
|
196
|
+
content = openingFence.opening + content;
|
|
197
|
+
if (closingFence) {
|
|
198
|
+
if (!content.endsWith('\n'))
|
|
199
|
+
content += '\n';
|
|
200
|
+
content += closingFence.closing;
|
|
201
|
+
}
|
|
202
|
+
return content;
|
|
203
|
+
}
|
|
204
|
+
function buildArtifactChunk(taskId, stem, title, agent, content, part, total, chunked, backfill, renderedContent = content) {
|
|
94
205
|
const marker = chunked ? MARKERS.artifactChunk(taskId, stem, part, total) : MARKERS.artifact(taskId, stem);
|
|
95
206
|
const heading = chunked ? `## ${title}(${part}/${total})` : `## ${title}`;
|
|
96
207
|
const timelineHint = backfill ? '> 历史产物补发' : null;
|
|
@@ -101,7 +212,7 @@ function buildArtifactChunk(taskId, stem, title, agent, content, part, total, ch
|
|
|
101
212
|
'',
|
|
102
213
|
`> **${agent}** · ${taskId}`,
|
|
103
214
|
'',
|
|
104
|
-
|
|
215
|
+
renderedContent,
|
|
105
216
|
'',
|
|
106
217
|
footer(agent, taskId)
|
|
107
218
|
].join('\n'));
|
|
@@ -110,25 +221,32 @@ function buildArtifactChunk(taskId, stem, title, agent, content, part, total, ch
|
|
|
110
221
|
function chunkArtifactComment(input) {
|
|
111
222
|
const byteLimit = input.byteLimit || 60_000;
|
|
112
223
|
const identity = artifactIdentity(input.artifact);
|
|
113
|
-
const
|
|
224
|
+
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));
|
|
114
226
|
if (Buffer.byteLength(single.body, 'utf8') <= byteLimit)
|
|
115
227
|
return [single];
|
|
116
228
|
let total = 2;
|
|
117
|
-
let
|
|
229
|
+
let payloadLimit = Number.POSITIVE_INFINITY;
|
|
230
|
+
const ranges = fenceRanges(safeBody);
|
|
231
|
+
if (!ranges.ok)
|
|
232
|
+
throw new Error(`${ranges.error.code}: artifact body: ${ranges.error.message} at offset ${ranges.error.offset}`);
|
|
118
233
|
for (;;) {
|
|
119
234
|
const probe = buildArtifactChunk(input.taskId, identity.stem, identity.title, input.agent, '', total, total, true, Boolean(input.backfill));
|
|
120
235
|
const available = byteLimit - Buffer.byteLength(probe.body, 'utf8');
|
|
121
|
-
|
|
122
|
-
if (
|
|
123
|
-
|
|
124
|
-
|
|
236
|
+
const sourceLimit = Math.min(available, payloadLimit);
|
|
237
|
+
if (sourceLimit <= 0)
|
|
238
|
+
throw new Error('comment byte limit is too small for an artifact chunk');
|
|
239
|
+
const pieces = chunkByUtf8(safeBody, sourceLimit);
|
|
240
|
+
if (pieces.length !== total) {
|
|
241
|
+
total = pieces.length;
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
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)));
|
|
245
|
+
const overflow = Math.max(...chunks.map((chunk) => Buffer.byteLength(chunk.body, 'utf8') - byteLimit));
|
|
246
|
+
if (overflow <= 0)
|
|
247
|
+
return chunks;
|
|
248
|
+
payloadLimit = sourceLimit - overflow;
|
|
125
249
|
}
|
|
126
|
-
return pieces.map((content, index) => {
|
|
127
|
-
const chunk = buildArtifactChunk(input.taskId, identity.stem, identity.title, input.agent, content, index + 1, total, true, Boolean(input.backfill));
|
|
128
|
-
if (Buffer.byteLength(chunk.body, 'utf8') > byteLimit)
|
|
129
|
-
throw new Error('rendered comment exceeds byte limit');
|
|
130
|
-
return chunk;
|
|
131
|
-
});
|
|
132
250
|
}
|
|
133
251
|
function findMarkerComments(comments, marker) {
|
|
134
252
|
return comments.filter((comment) => normalizeCommentContent(String(comment.body || '')).split('\n', 1)[0] === marker);
|
|
@@ -137,7 +255,7 @@ function flattenComments(value) {
|
|
|
137
255
|
if (!Array.isArray(value))
|
|
138
256
|
return [];
|
|
139
257
|
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'));
|
|
258
|
+
return flattened.filter((entry) => Boolean(entry && typeof entry === 'object' && (typeof entry.id === 'number' || typeof entry.id === 'string') && typeof entry.body === 'string'));
|
|
141
259
|
}
|
|
142
260
|
function issueNumberFromTask(content) {
|
|
143
261
|
const value = parseTaskFrontmatter(content).issue_number;
|
|
@@ -161,7 +279,7 @@ function hasResolvedPlatformContext(context) {
|
|
|
161
279
|
}
|
|
162
280
|
function bodyEnvelope(marker, title, taskId, agent, body) {
|
|
163
281
|
return normalizeCommentContent([
|
|
164
|
-
marker, `## ${title}`, '', `> **${agent}** · ${taskId}`, '', body.replace(/\n+$/, ''), '', footer(agent, taskId)
|
|
282
|
+
marker, `## ${title}`, '', `> **${agent}** · ${taskId}`, '', sanitizeCommentBody(body, `${title} body`).replace(/\n+$/, ''), '', footer(agent, taskId)
|
|
165
283
|
].join('\n'));
|
|
166
284
|
}
|
|
167
285
|
function resolveArtifactBody(taskDir, artifact) {
|
|
@@ -246,7 +364,7 @@ function writeComment(client, repo, issue, cwd, body, id) {
|
|
|
246
364
|
const endpoint = id ? `repos/${repo}/issues/comments/${id}` : `repos/${repo}/issues/${issue}/comments`;
|
|
247
365
|
return client.json(['api', endpoint, '-X', id ? 'PATCH' : 'POST', '--input', '-'], { cwd, method: id ? 'PATCH' : 'POST', input: JSON.stringify({ body }) });
|
|
248
366
|
}
|
|
249
|
-
function syncPlatformComment(taskRef, options) {
|
|
367
|
+
async function syncPlatformComment(taskRef, options) {
|
|
250
368
|
const resolved = resolveTaskRef(taskRef, options.cwd ? { repoRoot: options.cwd } : {});
|
|
251
369
|
if (!resolved.ok) {
|
|
252
370
|
return platformResult('failed', {
|
|
@@ -254,33 +372,50 @@ function syncPlatformComment(taskRef, options) {
|
|
|
254
372
|
});
|
|
255
373
|
}
|
|
256
374
|
const taskContent = fs.readFileSync(resolved.taskMdPath, 'utf8');
|
|
257
|
-
|
|
258
|
-
|
|
375
|
+
let issueIdentityFromTask;
|
|
376
|
+
try {
|
|
377
|
+
issueIdentityFromTask = taskIssueIdentity(parseTaskFrontmatter(taskContent), undefined, options.runtimeVersion);
|
|
378
|
+
}
|
|
379
|
+
catch (error) {
|
|
380
|
+
return platformResult('failed', { error: { ...taskIssueIdentityError(error), retryable: false } });
|
|
381
|
+
}
|
|
382
|
+
if (!issueIdentityFromTask) {
|
|
259
383
|
return platformResult('no-op', {
|
|
260
384
|
error: { code: 'ISSUE_NOT_LINKED', message: 'Task has no valid issue_number', retryable: false }
|
|
261
385
|
});
|
|
262
386
|
}
|
|
263
|
-
const context = resolvePlatformContext({ cwd: resolved.repoRoot, client: options.client });
|
|
264
|
-
if (!hasResolvedPlatformContext(context))
|
|
265
|
-
return context;
|
|
266
|
-
const client = options.client || createGitHubClient();
|
|
267
|
-
const listed = listRemoteComments(client, context.platform.repository, issue, resolved.repoRoot);
|
|
268
|
-
if (!listed.ok) {
|
|
269
|
-
return platformResult(listed.error.retryable ? 'blocked' : 'failed', {
|
|
270
|
-
...contextFields(context), resource: { kind: 'issue', number: issue }, error: listed.error
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
387
|
let desired;
|
|
274
388
|
try {
|
|
275
389
|
desired = expectedComments(resolved.taskId, taskContent, resolved.taskDir, options);
|
|
276
390
|
}
|
|
277
391
|
catch (error) {
|
|
278
392
|
return platformResult('failed', {
|
|
279
|
-
|
|
280
|
-
resource: { kind: 'issue', number: issue },
|
|
393
|
+
resource: { kind: 'issue', number: resourceIdentityNumber(issueIdentityFromTask) },
|
|
281
394
|
error: { code: 'COMMENT_PAYLOAD_INVALID', message: error instanceof Error ? error.message : String(error), retryable: false }
|
|
282
395
|
});
|
|
283
396
|
}
|
|
397
|
+
const loaded = await resolvePlatformProviderContext({ cwd: resolved.repoRoot, client: options.client });
|
|
398
|
+
const context = loaded.ok ? loaded.value.context : loaded.context;
|
|
399
|
+
if (!hasResolvedPlatformContext(context) || !loaded.ok)
|
|
400
|
+
return context;
|
|
401
|
+
const issue = resourceIdentityNumber(issueIdentityFromTask);
|
|
402
|
+
const listed = loaded.value.provider.comments?.list
|
|
403
|
+
? await loaded.value.provider.comments.list({
|
|
404
|
+
context: providerOperationContext(loaded.value),
|
|
405
|
+
parent: issueIdentityFromTask
|
|
406
|
+
}).then((response) => response.ok
|
|
407
|
+
? { ok: true, value: response.value.map((comment) => ({
|
|
408
|
+
id: providerCommentId(comment.id, loaded.value.provider),
|
|
409
|
+
body: comment.body,
|
|
410
|
+
user: comment.author?.name ? { login: comment.author.name } : undefined
|
|
411
|
+
})) }
|
|
412
|
+
: response)
|
|
413
|
+
: unsupportedProviderOperation(loaded.value.provider, 'comments.list');
|
|
414
|
+
if (!listed.ok) {
|
|
415
|
+
return platformResult(listed.error.retryable ? 'blocked' : 'failed', {
|
|
416
|
+
...contextFields(context), resource: { kind: 'issue', number: issue }, error: listed.error
|
|
417
|
+
});
|
|
418
|
+
}
|
|
284
419
|
const existing = relatedComments(listed.value, markerPrefix(resolved.taskId, options));
|
|
285
420
|
if (!validateRelatedMarkerSet(existing, markerPrefix(resolved.taskId, options)).ok) {
|
|
286
421
|
return platformResult('failed', {
|
|
@@ -310,7 +445,6 @@ function syncPlatformComment(taskRef, options) {
|
|
|
310
445
|
error: null
|
|
311
446
|
});
|
|
312
447
|
}
|
|
313
|
-
const repo = context.platform.repository;
|
|
314
448
|
const operations = [];
|
|
315
449
|
const ids = [];
|
|
316
450
|
for (const item of desired) {
|
|
@@ -320,16 +454,18 @@ function syncPlatformComment(taskRef, options) {
|
|
|
320
454
|
operations.push({ name: `comment:${item.marker}`, status: 'no-op', reasonCode: null });
|
|
321
455
|
continue;
|
|
322
456
|
}
|
|
323
|
-
const written =
|
|
457
|
+
const written = loaded.value.provider.comments?.write
|
|
458
|
+
? await loaded.value.provider.comments.write({
|
|
459
|
+
context: providerOperationContext(loaded.value),
|
|
460
|
+
parent: issueIdentityFromTask,
|
|
461
|
+
body: item.body,
|
|
462
|
+
...(current ? { existingComment: { kind: 'id', value: String(current.id) } } : {}),
|
|
463
|
+
mutation: { idempotencyKey: `comment:${resolved.taskId}:${item.marker}` }
|
|
464
|
+
})
|
|
465
|
+
: unsupportedProviderOperation(loaded.value.provider, 'comments.write');
|
|
324
466
|
if (!written.ok) {
|
|
325
467
|
if (!current && written.error.retryable) {
|
|
326
|
-
|
|
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
|
-
}
|
|
468
|
+
// A provider owns reconciliation because only it knows how to address the resource.
|
|
333
469
|
}
|
|
334
470
|
return platformResult(written.error.retryable ? 'blocked' : 'failed', {
|
|
335
471
|
...contextFields(context),
|
|
@@ -338,15 +474,20 @@ function syncPlatformComment(taskRef, options) {
|
|
|
338
474
|
error: written.error
|
|
339
475
|
});
|
|
340
476
|
}
|
|
341
|
-
ids.push(
|
|
477
|
+
ids.push(providerCommentId(written.value.remoteId, loaded.value.provider));
|
|
342
478
|
operations.push({ name: `comment:${item.marker}`, status: 'applied', reasonCode: null });
|
|
343
479
|
}
|
|
344
480
|
const desiredMarkers = new Set(desired.map((item) => item.marker));
|
|
345
481
|
const stale = existing.filter((comment) => !desiredMarkers.has(normalizeCommentContent(comment.body).split('\n', 1)[0]));
|
|
346
482
|
for (const comment of stale) {
|
|
347
|
-
const deleted =
|
|
348
|
-
|
|
349
|
-
|
|
483
|
+
const deleted = loaded.value.provider.comments?.delete
|
|
484
|
+
? await loaded.value.provider.comments.delete({
|
|
485
|
+
context: providerOperationContext(loaded.value),
|
|
486
|
+
parent: issueIdentityFromTask,
|
|
487
|
+
comment: { kind: 'id', value: String(comment.id) },
|
|
488
|
+
mutation: { idempotencyKey: `comment-delete:${resolved.taskId}:${comment.id}` }
|
|
489
|
+
})
|
|
490
|
+
: unsupportedProviderOperation(loaded.value.provider, 'comments.delete');
|
|
350
491
|
if (!deleted.ok) {
|
|
351
492
|
return platformResult(deleted.error.retryable ? 'blocked' : 'failed', {
|
|
352
493
|
...contextFields(context),
|
|
@@ -367,31 +508,55 @@ function syncPlatformComment(taskRef, options) {
|
|
|
367
508
|
error: null
|
|
368
509
|
});
|
|
369
510
|
}
|
|
370
|
-
function listPlatformComments(issue, cwd = process.cwd(), client
|
|
371
|
-
const
|
|
372
|
-
|
|
511
|
+
async function listPlatformComments(issue, cwd = process.cwd(), client) {
|
|
512
|
+
const loaded = await resolvePlatformProviderContext({ cwd, client });
|
|
513
|
+
const context = loaded.ok ? loaded.value.context : loaded.context;
|
|
514
|
+
if (!hasResolvedPlatformContext(context) || !loaded.ok)
|
|
515
|
+
return context;
|
|
516
|
+
let identity;
|
|
517
|
+
try {
|
|
518
|
+
identity = loaded.ok ? providerResourceToken(loaded.value.provider, 'issue', String(issue)) : null;
|
|
519
|
+
}
|
|
520
|
+
catch (error) {
|
|
521
|
+
return platformResult('failed', { ...contextFields(context), error: { code: 'PLATFORM_IDENTITY_TOKEN_INVALID', message: error instanceof Error ? error.message : String(error), retryable: false } });
|
|
522
|
+
}
|
|
523
|
+
if (!loaded.ok || !identity)
|
|
373
524
|
return context;
|
|
374
|
-
const
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
525
|
+
const listed = loaded.value.provider.comments?.list
|
|
526
|
+
? await loaded.value.provider.comments.list({ context: providerOperationContext(loaded.value), parent: identity }).then((response) => response.ok
|
|
527
|
+
? { ok: true, value: response.value.map((comment) => ({ id: comment.id, body: comment.body, user: comment.author?.name ? { login: comment.author.name } : undefined })) }
|
|
528
|
+
: response)
|
|
529
|
+
: unsupportedProviderOperation(loaded.value.provider, 'comments.list');
|
|
530
|
+
if (!listed.ok) {
|
|
531
|
+
return platformResult(providerStatus(listed.error), {
|
|
532
|
+
...contextFields(context), resource: { kind: 'issue', number: resourceIdentityNumber(identity), identity }, error: providerError(listed.error, 'PLATFORM_PROVIDER_OPERATION_FAILED')
|
|
378
533
|
});
|
|
379
534
|
}
|
|
380
|
-
return { ...platformResult('no-op', { ...contextFields(context), resource: { kind: 'issue', number:
|
|
535
|
+
return { ...platformResult('no-op', { ...contextFields(context), resource: { kind: 'issue', number: resourceIdentityNumber(identity), identity }, error: null }), comments: listed.value };
|
|
381
536
|
}
|
|
382
|
-
function checkPlatformCommentOwner(taskRef, options = {}) {
|
|
537
|
+
async function checkPlatformCommentOwner(taskRef, options = {}) {
|
|
383
538
|
const resolved = resolveTaskRef(taskRef, options.cwd ? { repoRoot: options.cwd } : {});
|
|
384
539
|
if (!resolved.ok)
|
|
385
540
|
return platformResult('failed', { error: { code: resolved.code, message: resolved.message, retryable: false } });
|
|
386
541
|
const content = fs.readFileSync(resolved.taskMdPath, 'utf8');
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
542
|
+
let issueIdentity;
|
|
543
|
+
try {
|
|
544
|
+
issueIdentity = taskIssueIdentity(parseTaskFrontmatter(content), undefined, options.runtimeVersion);
|
|
545
|
+
}
|
|
546
|
+
catch (error) {
|
|
547
|
+
return platformResult('failed', { error: { ...taskIssueIdentityError(error), retryable: false } });
|
|
548
|
+
}
|
|
549
|
+
if (!issueIdentity)
|
|
550
|
+
return platformResult('no-op', { error: { code: 'ISSUE_NOT_LINKED', message: 'Task has no valid platform issue identity', retryable: false } });
|
|
551
|
+
const loaded = await resolvePlatformProviderContext({ cwd: resolved.repoRoot, client: options.client });
|
|
552
|
+
const context = loaded.ok ? loaded.value.context : loaded.context;
|
|
553
|
+
if (!hasResolvedPlatformContext(context) || !loaded.ok)
|
|
393
554
|
return context;
|
|
394
|
-
const listed =
|
|
555
|
+
const listed = loaded.value.provider.comments?.list
|
|
556
|
+
? await loaded.value.provider.comments.list({ context: providerOperationContext(loaded.value), parent: issueIdentity }).then((response) => response.ok
|
|
557
|
+
? { ok: true, value: response.value.map((comment) => ({ id: comment.id, body: comment.body, user: comment.author?.name ? { login: comment.author.name } : undefined })) }
|
|
558
|
+
: response)
|
|
559
|
+
: unsupportedProviderOperation(loaded.value.provider, 'comments.list');
|
|
395
560
|
if (!listed.ok)
|
|
396
561
|
return platformResult(listed.error.retryable ? 'blocked' : 'failed', { ...contextFields(context), error: listed.error });
|
|
397
562
|
const matches = findMarkerComments(listed.value, MARKERS.task(resolved.taskId));
|
|
@@ -401,11 +566,11 @@ function checkPlatformCommentOwner(taskRef, options = {}) {
|
|
|
401
566
|
if (owner && owner !== context.platform.currentUser && !context.capabilities.triage) {
|
|
402
567
|
return platformResult('blocked', {
|
|
403
568
|
...contextFields(context),
|
|
404
|
-
resource: { kind: 'issue', number:
|
|
569
|
+
resource: { kind: 'issue', number: resourceIdentityNumber(issueIdentity), identity: issueIdentity },
|
|
405
570
|
error: { code: 'COMMENT_OWNER_CONFLICT', message: `Task comment is owned by '${owner}'`, retryable: false }
|
|
406
571
|
});
|
|
407
572
|
}
|
|
408
|
-
return platformResult('no-op', { ...contextFields(context), resource: { kind: 'issue', number:
|
|
573
|
+
return platformResult('no-op', { ...contextFields(context), resource: { kind: 'issue', number: resourceIdentityNumber(issueIdentity), identity: issueIdentity }, error: null });
|
|
409
574
|
}
|
|
410
575
|
export { MARKERS, chunkArtifactComment, findMarkerComments, listRemoteComments, checkPlatformCommentOwner, listPlatformComments, normalizeCommentContent, renderTaskComment, syncPlatformComment, validateRelatedMarkerSet, writeComment };
|
|
411
576
|
//# sourceMappingURL=issue-comments.js.map
|