@fitlab-ai/agent-infra 0.9.14 → 0.10.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.
- package/README.md +10 -0
- package/README.zh-CN.md +10 -0
- package/bin/internal-cli.sh +45 -0
- package/dist/bin/internal-cli.js +93 -30
- package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +165 -27
- package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +9 -4
- package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +43 -3
- package/dist/lib/defaults.json +103 -103
- package/dist/lib/fs/durable-write.js +39 -0
- package/dist/lib/fs/file-lock.js +111 -0
- package/dist/lib/host-control/audit.js +19 -0
- package/dist/lib/host-control/client.js +142 -0
- package/dist/lib/host-control/command.js +49 -0
- package/dist/lib/host-control/path.js +160 -0
- package/dist/lib/host-control/secure-fs.js +140 -0
- package/dist/lib/host-control/server.js +159 -0
- package/dist/lib/host-control/service.js +112 -0
- package/dist/lib/init.js +11 -2
- package/dist/lib/internal/cli-route-inventory.js +4 -2
- package/dist/lib/internal/codex-lifecycle.js +6 -6
- package/dist/lib/internal/demo-promotion.js +322 -0
- package/dist/lib/internal/demo-transcript.js +454 -0
- package/dist/lib/internal/host-control.js +28 -0
- package/dist/lib/internal/manual-validation.js +331 -0
- package/dist/lib/internal/platform-pr.js +18 -4
- package/dist/lib/internal/release-workflow.js +63 -84
- package/dist/lib/internal/sandbox-control.js +149 -2
- package/dist/lib/internal/task-artifact.js +16 -263
- package/dist/lib/internal/task-create.js +6 -5
- package/dist/lib/internal/task-event.js +37 -37
- package/dist/lib/internal/task-lifecycle.js +2 -2
- package/dist/lib/internal/task-operation-registry.js +101 -4
- package/dist/lib/internal/task-review.js +11 -56
- package/dist/lib/internal/task-validate.js +30 -10
- package/dist/lib/owned-path.js +33 -0
- package/dist/lib/platform/check-status.js +11 -0
- package/dist/lib/platform/comment-safety.js +4 -1
- package/dist/lib/platform/completion-backfill.js +1 -1
- package/dist/lib/platform/github-data.js +241 -0
- package/dist/lib/platform/github-provider.js +15 -20
- package/dist/lib/platform/issue-comments.js +18 -12
- package/dist/lib/platform/issues.js +2 -180
- package/dist/lib/platform/pr-change-report.js +5 -4
- package/dist/lib/platform/pr-checks.js +12 -65
- package/dist/lib/platform/pr-summary.js +117 -33
- package/dist/lib/platform/pull-requests.js +5 -108
- package/dist/lib/platform/snapshots.js +2 -0
- package/dist/lib/platform/task-identities.js +2 -9
- package/dist/lib/platform/verification-sync.js +132 -143
- package/dist/lib/pr-review/evidence-grading.js +4 -8
- package/dist/lib/release/workflow.js +1 -1
- package/dist/lib/sandbox/commands/create.js +7 -2
- package/dist/lib/sandbox/commands/rm.js +19 -1511
- package/dist/lib/sandbox/control/audit.js +203 -0
- package/dist/lib/sandbox/control/client.js +93 -54
- package/dist/lib/sandbox/control/executor.js +129 -65
- package/dist/lib/sandbox/control/finalization-response.js +27 -0
- package/dist/lib/sandbox/control/identity-sentinel.js +107 -0
- package/dist/lib/sandbox/control/lifecycle.js +36 -2
- package/dist/lib/sandbox/control/native-file-lock.js +6 -98
- package/dist/lib/sandbox/control/protocol.js +55 -4
- package/dist/lib/sandbox/control/server.js +518 -127
- package/dist/lib/sandbox/control/state.js +64 -0
- package/dist/lib/sandbox/control/task-workflow.js +74 -0
- package/dist/lib/sandbox/control/workflow-executor.js +89 -0
- package/dist/lib/sandbox/index.js +1 -2
- package/dist/lib/sandbox/recovery.js +28 -4
- package/dist/lib/sandbox/removal.js +822 -0
- package/dist/lib/sandbox/task-cleanup.js +217 -0
- package/dist/lib/sandbox/workspace-identity.js +17 -1
- package/dist/lib/sandbox/workspace-view.js +13 -14
- package/dist/lib/task/activity-intent.js +2 -1
- package/dist/lib/task/activity-log.js +30 -24
- package/dist/lib/task/artifact-command.js +79 -0
- package/dist/lib/task/artifact-lifecycle.js +30 -28
- package/dist/lib/task/artifact-name.js +36 -0
- package/dist/lib/task/artifact-operations.js +29 -289
- package/dist/lib/task/artifact-receipts.js +3 -15
- package/dist/lib/task/artifact-recovery.js +430 -0
- package/dist/lib/task/artifact-repair-intent.js +134 -29
- package/dist/lib/task/artifact-schema.js +6 -14
- package/dist/lib/task/capabilities.js +12 -19
- package/dist/lib/task/codex-orchestration.js +71 -25
- package/dist/lib/task/commit-identity.js +28 -0
- package/dist/lib/task/commit-operation.js +33 -20
- package/dist/lib/task/control-authority.js +403 -6
- package/dist/lib/task/control-recovery.js +140 -0
- package/dist/lib/task/create-service.js +1 -8
- package/dist/lib/task/create.js +62 -9
- package/dist/lib/task/decision-details.js +2 -46
- package/dist/lib/task/delegation-receipts.js +64 -3
- package/dist/lib/task/events.js +275 -22
- package/dist/lib/task/finalization-state.js +9 -0
- package/dist/lib/task/human-override.js +8 -14
- package/dist/lib/task/implementation-inputs.js +3 -3
- package/dist/lib/task/intermediate-cleanup.js +441 -0
- package/dist/lib/task/invalidation.js +18 -65
- package/dist/lib/task/ledger-intents.js +7 -22
- package/dist/lib/task/ledger.js +1 -3
- package/dist/lib/task/lifecycle-recovery.js +549 -0
- package/dist/lib/task/lifecycle.js +31 -4
- package/dist/lib/task/local-artifact-finalization.js +197 -197
- package/dist/lib/task/manual-validation-completion.js +26 -0
- package/dist/lib/task/manual-validation-evidence.js +130 -0
- package/dist/lib/task/manual-validation-receipt.js +105 -0
- package/dist/lib/task/manual-validation-shared.js +40 -0
- package/dist/lib/task/manual-validation-transaction.js +243 -0
- package/dist/lib/task/markdown.js +47 -0
- package/dist/lib/task/orchestration.js +66 -19
- package/dist/lib/task/pr-delivery-fact.js +12 -56
- package/dist/lib/task/qualification-audit.js +20 -34
- package/dist/lib/task/recovery-warning.js +19 -0
- package/dist/lib/task/review-artifacts.js +2 -18
- package/dist/lib/task/review-command.js +26 -0
- package/dist/lib/task/review-finalization.js +175 -216
- package/dist/lib/task/review-fingerprint.js +2 -2
- package/dist/lib/task/sections.js +34 -56
- package/dist/lib/task/short-id.js +15 -7
- package/dist/lib/task/task-execution-lock.js +1 -1
- package/dist/lib/task/verification-engine.js +119 -337
- package/dist/lib/task/verification.js +1 -1
- package/dist/lib/task/workflow-command.js +16 -0
- package/dist/lib/task/workflow-warning-intents.js +0 -16
- package/dist/lib/task/workflow-warnings.js +28 -2
- package/dist/lib/update.js +2 -0
- package/dist/lib/version.js +1 -11
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +223 -33
- package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +10 -4
- package/lib/agent-clients/adapters/codex-lifecycle/store.ts +55 -3
- package/lib/fs/durable-write.ts +30 -0
- package/lib/fs/file-lock.ts +113 -0
- package/lib/host-control/audit.ts +22 -0
- package/lib/host-control/client.ts +172 -0
- package/lib/host-control/command.ts +60 -0
- package/lib/host-control/path.ts +167 -0
- package/lib/host-control/secure-fs.ts +135 -0
- package/lib/host-control/server.ts +180 -0
- package/lib/host-control/service.ts +133 -0
- package/lib/init.ts +15 -2
- package/lib/internal/cli-route-inventory.ts +4 -2
- package/lib/internal/codex-lifecycle.ts +6 -6
- package/lib/internal/demo-promotion.ts +354 -0
- package/lib/internal/demo-transcript.ts +419 -0
- package/lib/internal/host-control.ts +28 -0
- package/lib/internal/manual-validation.ts +315 -0
- package/lib/internal/platform-pr.ts +15 -4
- package/lib/internal/release-workflow.ts +74 -82
- package/lib/internal/sandbox-control.ts +162 -1
- package/lib/internal/task-artifact.ts +14 -217
- package/lib/internal/task-create.ts +5 -5
- package/lib/internal/task-event.ts +20 -11
- package/lib/internal/task-lifecycle.ts +4 -3
- package/lib/internal/task-operation-registry.ts +111 -4
- package/lib/internal/task-review.ts +9 -54
- package/lib/internal/task-validate.ts +26 -10
- package/lib/owned-path.ts +26 -0
- package/lib/platform/check-status.ts +9 -0
- package/lib/platform/comment-safety.ts +5 -0
- package/lib/platform/completion-backfill.ts +1 -1
- package/lib/platform/github-data.ts +322 -0
- package/lib/platform/github-provider.ts +15 -20
- package/lib/platform/issue-comments.ts +21 -14
- package/lib/platform/issues.ts +5 -246
- package/lib/platform/pr-change-report.ts +7 -5
- package/lib/platform/pr-checks.ts +13 -77
- package/lib/platform/pr-summary.ts +123 -35
- package/lib/platform/pull-requests.ts +6 -173
- package/lib/platform/snapshots.ts +53 -0
- package/lib/platform/task-identities.ts +3 -14
- package/lib/platform/verification-sync.ts +150 -156
- package/lib/pr-review/evidence-grading.ts +4 -9
- package/lib/release/workflow.ts +3 -3
- package/lib/sandbox/commands/create.ts +7 -6
- package/lib/sandbox/commands/rm.ts +21 -1895
- package/lib/sandbox/control/audit.ts +253 -0
- package/lib/sandbox/control/client.ts +109 -66
- package/lib/sandbox/control/executor.ts +134 -74
- package/lib/sandbox/control/finalization-response.ts +29 -0
- package/lib/sandbox/control/identity-sentinel.ts +137 -0
- package/lib/sandbox/control/lifecycle.ts +45 -3
- package/lib/sandbox/control/native-file-lock.ts +6 -107
- package/lib/sandbox/control/protocol.ts +80 -6
- package/lib/sandbox/control/server.ts +567 -132
- package/lib/sandbox/control/state.ts +86 -0
- package/lib/sandbox/control/task-workflow.ts +95 -0
- package/lib/sandbox/control/workflow-executor.ts +92 -0
- package/lib/sandbox/index.ts +1 -2
- package/lib/sandbox/recovery.ts +28 -5
- package/lib/sandbox/removal.ts +1142 -0
- package/lib/sandbox/task-cleanup.ts +209 -0
- package/lib/sandbox/workspace-identity.ts +19 -3
- package/lib/sandbox/workspace-view.ts +17 -15
- package/lib/task/activity-intent.ts +2 -2
- package/lib/task/activity-log.ts +27 -19
- package/lib/task/artifact-command.ts +85 -0
- package/lib/task/artifact-lifecycle.ts +31 -26
- package/lib/task/artifact-name.ts +55 -0
- package/lib/task/artifact-operations.ts +34 -310
- package/lib/task/artifact-receipts.ts +3 -14
- package/lib/task/artifact-recovery.ts +519 -0
- package/lib/task/artifact-repair-intent.ts +162 -33
- package/lib/task/artifact-schema.ts +5 -34
- package/lib/task/capabilities.ts +13 -22
- package/lib/task/codex-orchestration.ts +85 -24
- package/lib/task/commit-identity.ts +31 -0
- package/lib/task/commit-operation.ts +37 -20
- package/lib/task/control-authority.ts +540 -10
- package/lib/task/control-recovery.ts +186 -0
- package/lib/task/create-service.ts +1 -10
- package/lib/task/create.ts +63 -8
- package/lib/task/decision-details.ts +3 -71
- package/lib/task/delegation-receipts.ts +73 -2
- package/lib/task/events.ts +309 -24
- package/lib/task/finalization-state.ts +11 -0
- package/lib/task/human-override.ts +8 -16
- package/lib/task/implementation-inputs.ts +3 -3
- package/lib/task/intermediate-cleanup.ts +493 -0
- package/lib/task/invalidation.ts +18 -62
- package/lib/task/ledger-intents.ts +7 -24
- package/lib/task/ledger.ts +1 -3
- package/lib/task/lifecycle-recovery.ts +691 -0
- package/lib/task/lifecycle.ts +30 -5
- package/lib/task/local-artifact-finalization.ts +244 -234
- package/lib/task/manual-validation-completion.ts +40 -0
- package/lib/task/manual-validation-evidence.ts +209 -0
- package/lib/task/manual-validation-receipt.ts +156 -0
- package/lib/task/manual-validation-shared.ts +50 -0
- package/lib/task/manual-validation-transaction.ts +304 -0
- package/lib/task/markdown.ts +70 -0
- package/lib/task/orchestration.ts +84 -19
- package/lib/task/pr-delivery-fact.ts +15 -62
- package/lib/task/qualification-audit.ts +22 -33
- package/lib/task/recovery-warning.ts +21 -0
- package/lib/task/review-artifacts.ts +1 -20
- package/lib/task/review-command.ts +24 -0
- package/lib/task/review-finalization.ts +183 -292
- package/lib/task/review-fingerprint.ts +2 -2
- package/lib/task/sections.ts +33 -54
- package/lib/task/short-id.ts +27 -5
- package/lib/task/task-execution-lock.ts +1 -1
- package/lib/task/verification-engine.ts +113 -383
- package/lib/task/verification-types.ts +15 -0
- package/lib/task/verification.ts +1 -1
- package/lib/task/workflow-command.ts +17 -0
- package/lib/task/workflow-warning-intents.ts +0 -13
- package/lib/task/workflow-warnings.ts +21 -2
- package/lib/update.ts +6 -0
- package/lib/version.ts +1 -15
- package/package.json +6 -6
- package/templates/.agents/hooks/lifecycle-delegation.js +6 -6
- package/templates/.agents/rules/create-issue.en.md +1 -1
- package/templates/.agents/rules/create-issue.zh-CN.md +1 -1
- package/templates/.agents/rules/issue-sync.en.md +4 -2
- package/templates/.agents/rules/issue-sync.zh-CN.md +4 -3
- package/templates/.agents/rules/local-artifact-repair.en.md +46 -39
- package/templates/.agents/rules/local-artifact-repair.zh-CN.md +43 -36
- package/templates/.agents/rules/testing-discipline.en.md +2 -0
- package/templates/.agents/rules/testing-discipline.zh-CN.md +2 -0
- package/templates/.agents/scripts/package.json +3 -0
- package/templates/.agents/skills/analyze-task/SKILL.en.md +6 -6
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +6 -6
- package/templates/.agents/skills/analyze-task/config/verify.en.json +1 -1
- package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/block-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/block-task/config/verify.json +1 -1
- package/templates/.agents/skills/cancel-task/SKILL.en.md +3 -3
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/cancel-task/config/verify.json +1 -1
- package/templates/.agents/skills/code-task/SKILL.en.md +7 -7
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +7 -7
- package/templates/.agents/skills/code-task/config/verify.en.json +1 -1
- package/templates/.agents/skills/code-task/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +1 -1
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +1 -1
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +33 -21
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +33 -21
- package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +1 -1
- package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +1 -1
- 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 +16 -7
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +16 -7
- package/templates/.agents/skills/complete-task/SKILL.en.md +3 -3
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/complete-task/config/verify.en.json +2 -2
- package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +2 -2
- package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +1 -1
- package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +1 -1
- package/templates/.agents/skills/create-pr/SKILL.en.md +2 -2
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/create-pr/config/verify.json +1 -1
- package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +2 -2
- package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +2 -2
- package/templates/.agents/skills/create-task/SKILL.en.md +5 -5
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/create-task/config/verify.json +1 -1
- package/templates/.agents/skills/import-issue/SKILL.en.md +8 -8
- package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +8 -8
- package/templates/.agents/skills/import-issue/config/verify.json +2 -2
- package/templates/.agents/skills/plan-task/SKILL.en.md +4 -4
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/plan-task/config/verify.en.json +1 -1
- package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/release/SKILL.en.md +7 -3
- package/templates/.agents/skills/release/SKILL.zh-CN.md +7 -3
- package/templates/.agents/skills/restore-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/restore-task/config/verify.json +1 -1
- package/templates/.agents/skills/review-analysis/SKILL.en.md +2 -2
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -1
- package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/review-analysis/reference/report-template.en.md +7 -7
- package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +6 -6
- package/templates/.agents/skills/review-code/SKILL.en.md +2 -2
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/review-code/config/verify.en.json +1 -1
- package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/review-code/reference/report-template.en.md +12 -12
- package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +12 -12
- package/templates/.agents/skills/review-plan/SKILL.en.md +2 -2
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/review-plan/config/verify.en.json +1 -1
- package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/review-plan/reference/report-template.en.md +7 -7
- package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +7 -7
- package/templates/.agents/skills/review-pr/config/verify.en.json +1 -1
- package/templates/.agents/skills/review-pr/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/review-pr/reference/evidence-grading.en.md +3 -3
- package/templates/.agents/skills/review-pr/reference/evidence-grading.zh-CN.md +3 -3
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +20 -3
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +20 -3
- package/templates/.agents/skills/run-manual-validation/config/verify.en.json +1 -1
- package/templates/.agents/skills/run-manual-validation/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.en.md +4 -1
- package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.zh-CN.md +4 -1
- package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +2 -0
- package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +2 -0
- package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/test/SKILL.en.md +17 -6
- package/templates/.agents/skills/test/SKILL.zh-CN.md +17 -6
- package/templates/.agents/templates/task.en.md +1 -1
- package/templates/.agents/templates/task.zh-CN.md +1 -1
- package/templates/.claude/commands/import-issue.en.md +1 -1
- package/templates/.claude/commands/import-issue.zh-CN.md +1 -1
- package/templates/.claude/commands/restore-task.en.md +1 -1
- package/templates/.claude/commands/restore-task.zh-CN.md +1 -1
- package/templates/.claude/commands/run-manual-validation.en.md +1 -1
- package/templates/.claude/commands/run-manual-validation.zh-CN.md +1 -1
- package/dist/lib/platform/adapters.js +0 -56
- package/lib/platform/adapters.ts +0 -197
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { createHash } from 'node:crypto';
|
|
4
|
+
import { MANUAL_VALIDATION_ARTIFACT as ARTIFACT, MANUAL_VALIDATION_SHA40 as SHA40, MANUAL_VALIDATION_SHA64 as SHA64, MANUAL_VALIDATION_TASK_ID as TASK_ID, exactKeys, isRecord, validTimestamp, writeJsonAtomic } from './manual-validation-shared.js';
|
|
5
|
+
const MANUAL_VALIDATION_RECEIPT_SCHEMA = 'agent-infra/manual-validation-receipt';
|
|
6
|
+
const MANUAL_VALIDATION_RECEIPT_VERSION = 1;
|
|
7
|
+
function invalid(message) {
|
|
8
|
+
return { ok: false, error: { code: 'MANUAL_VALIDATION_RECEIPT_INVALID', message } };
|
|
9
|
+
}
|
|
10
|
+
function canonicalPayload(receipt) {
|
|
11
|
+
return {
|
|
12
|
+
schema: MANUAL_VALIDATION_RECEIPT_SCHEMA,
|
|
13
|
+
version: MANUAL_VALIDATION_RECEIPT_VERSION,
|
|
14
|
+
transactionId: receipt.transactionId,
|
|
15
|
+
taskId: receipt.taskId,
|
|
16
|
+
prNumber: receipt.prNumber,
|
|
17
|
+
prHeadSha: receipt.prHeadSha,
|
|
18
|
+
evidenceDigest: receipt.evidenceDigest,
|
|
19
|
+
artifact: receipt.artifact,
|
|
20
|
+
artifactSha256: receipt.artifactSha256,
|
|
21
|
+
pendingSummaryDigest: receipt.pendingSummaryDigest,
|
|
22
|
+
finalSummaryDigest: receipt.finalSummaryDigest,
|
|
23
|
+
committedAt: receipt.committedAt
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function manualValidationReceiptDigest(receipt) {
|
|
27
|
+
return createHash('sha256').update(JSON.stringify(canonicalPayload(receipt)), 'utf8').digest('hex');
|
|
28
|
+
}
|
|
29
|
+
function manualValidationFinalSummaryDigest(body) {
|
|
30
|
+
const heading = /^###\s+✅\s+(?:Manual Validation Passed|人工验证已通过)\s*$/mu.exec(body);
|
|
31
|
+
const afterHeading = heading ? body.slice(heading.index + heading[0].length) : '';
|
|
32
|
+
const nextSection = heading ? /^#{1,3}\s/mu.exec(afterHeading) : null;
|
|
33
|
+
const projection = heading
|
|
34
|
+
? body.slice(heading.index, heading.index + heading[0].length + (nextSection?.index ?? afterHeading.length)).replace(/\s+$/u, '')
|
|
35
|
+
: body;
|
|
36
|
+
const preimage = projection.replace(/receipt=(?:[a-f0-9]{64}|<receipt>)/gu, 'receipt=<receipt>');
|
|
37
|
+
return createHash('sha256').update(preimage, 'utf8').digest('hex');
|
|
38
|
+
}
|
|
39
|
+
function manualValidationFinalSummaryProjectionMatches(body, receipt) {
|
|
40
|
+
const identity = `transaction=${receipt.transactionId}; receipt=${receipt.receiptDigest}; evidence=${receipt.evidenceDigest}; head=${receipt.prHeadSha}`;
|
|
41
|
+
return body.includes(identity) && manualValidationFinalSummaryDigest(body) === receipt.finalSummaryDigest;
|
|
42
|
+
}
|
|
43
|
+
function validateManualValidationReceipt(value, expected) {
|
|
44
|
+
if (!isRecord(value))
|
|
45
|
+
return invalid('receipt must be a JSON object');
|
|
46
|
+
const keys = ['schema', 'version', 'transactionId', 'taskId', 'prNumber', 'prHeadSha', 'evidenceDigest', 'artifact', 'artifactSha256', 'pendingSummaryDigest', 'finalSummaryDigest', 'committedAt', 'receiptDigest'];
|
|
47
|
+
const keyError = exactKeys(value, keys, 'receipt');
|
|
48
|
+
if (keyError)
|
|
49
|
+
return invalid(keyError);
|
|
50
|
+
if (value.schema !== MANUAL_VALIDATION_RECEIPT_SCHEMA || value.version !== MANUAL_VALIDATION_RECEIPT_VERSION)
|
|
51
|
+
return invalid('receipt schema or version is unsupported');
|
|
52
|
+
if (typeof value.transactionId !== 'string' || !/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u.test(value.transactionId))
|
|
53
|
+
return invalid('transactionId is invalid');
|
|
54
|
+
if (typeof value.taskId !== 'string' || !TASK_ID.test(value.taskId))
|
|
55
|
+
return invalid('taskId is invalid');
|
|
56
|
+
const prNumber = value.prNumber;
|
|
57
|
+
if (!Number.isSafeInteger(prNumber) || prNumber <= 0)
|
|
58
|
+
return invalid('prNumber is invalid');
|
|
59
|
+
for (const [name, pattern] of [['prHeadSha', SHA40], ['evidenceDigest', SHA64], ['artifactSha256', SHA64], ['pendingSummaryDigest', SHA64], ['finalSummaryDigest', SHA64], ['receiptDigest', SHA64]]) {
|
|
60
|
+
if (typeof value[name] !== 'string' || !pattern.test(value[name]))
|
|
61
|
+
return invalid(`${name} is invalid`);
|
|
62
|
+
}
|
|
63
|
+
if (typeof value.artifact !== 'string' || !ARTIFACT.test(value.artifact))
|
|
64
|
+
return invalid('artifact is not a canonical manual-validation artifact');
|
|
65
|
+
if (!validTimestamp(value.committedAt))
|
|
66
|
+
return invalid('committedAt is invalid');
|
|
67
|
+
const receipt = value;
|
|
68
|
+
if (manualValidationReceiptDigest(receipt) !== receipt.receiptDigest)
|
|
69
|
+
return invalid('receiptDigest does not match receipt contents');
|
|
70
|
+
if (expected && Object.entries(expected).some(([key, expectedValue]) => expectedValue !== undefined && receipt[key] !== expectedValue)) {
|
|
71
|
+
return { ok: false, error: { code: 'MANUAL_VALIDATION_RECEIPT_IDENTITY_MISMATCH', message: 'receipt identity does not match the expected transaction' } };
|
|
72
|
+
}
|
|
73
|
+
return { ok: true, value: receipt };
|
|
74
|
+
}
|
|
75
|
+
function createManualValidationReceipt(input) {
|
|
76
|
+
const payload = canonicalPayload(input);
|
|
77
|
+
const receipt = { ...payload, receiptDigest: manualValidationReceiptDigest(input) };
|
|
78
|
+
const checked = validateManualValidationReceipt(receipt);
|
|
79
|
+
if (!checked.ok)
|
|
80
|
+
throw new Error(`${checked.error.code}: ${checked.error.message}`);
|
|
81
|
+
return checked.value;
|
|
82
|
+
}
|
|
83
|
+
function manualValidationReceiptPath(taskDir) {
|
|
84
|
+
return path.join(taskDir, '.manual-validation', 'receipt.json');
|
|
85
|
+
}
|
|
86
|
+
function writeManualValidationReceiptAtomic(taskDir, receipt) {
|
|
87
|
+
const checked = validateManualValidationReceipt(receipt);
|
|
88
|
+
if (!checked.ok)
|
|
89
|
+
throw new Error(`${checked.error.code}: ${checked.error.message}`);
|
|
90
|
+
const target = manualValidationReceiptPath(taskDir);
|
|
91
|
+
return writeJsonAtomic(target, receipt);
|
|
92
|
+
}
|
|
93
|
+
function readManualValidationReceipt(taskDir, expected) {
|
|
94
|
+
const file = manualValidationReceiptPath(taskDir);
|
|
95
|
+
let value;
|
|
96
|
+
try {
|
|
97
|
+
value = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
return { ok: false, error: { code: error.code === 'ENOENT' ? 'MANUAL_VALIDATION_RECEIPT_MISSING' : 'MANUAL_VALIDATION_RECEIPT_INVALID', message: error instanceof Error ? error.message : String(error) } };
|
|
101
|
+
}
|
|
102
|
+
return validateManualValidationReceipt(value, expected);
|
|
103
|
+
}
|
|
104
|
+
export { MANUAL_VALIDATION_RECEIPT_SCHEMA, MANUAL_VALIDATION_RECEIPT_VERSION, createManualValidationReceipt, manualValidationFinalSummaryDigest, manualValidationFinalSummaryProjectionMatches, manualValidationReceiptDigest, manualValidationReceiptPath, readManualValidationReceipt, validateManualValidationReceipt, writeManualValidationReceiptAtomic };
|
|
105
|
+
//# sourceMappingURL=manual-validation-receipt.js.map
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { randomUUID } from 'node:crypto';
|
|
4
|
+
const MANUAL_VALIDATION_TASK_ID = /^TASK-\d{8}-\d{6}$/u;
|
|
5
|
+
const MANUAL_VALIDATION_SHA40 = /^[a-f0-9]{40}$/u;
|
|
6
|
+
const MANUAL_VALIDATION_SHA64 = /^[a-f0-9]{64}$/u;
|
|
7
|
+
const MANUAL_VALIDATION_ARTIFACT = /^manual-validation(?:-r[2-9]|-r[1-9]\d+)?.md$/u;
|
|
8
|
+
const MANUAL_VALIDATION_TIMESTAMP = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/u;
|
|
9
|
+
function isRecord(value) {
|
|
10
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
11
|
+
}
|
|
12
|
+
function exactKeys(value, keys, label) {
|
|
13
|
+
const allowed = new Set(keys);
|
|
14
|
+
const unknown = Object.keys(value).find((key) => !allowed.has(key));
|
|
15
|
+
if (unknown)
|
|
16
|
+
return `${label} contains unknown field '${unknown}'`;
|
|
17
|
+
const missing = keys.find((key) => !Object.hasOwn(value, key));
|
|
18
|
+
return missing ? `${label} is missing '${missing}'` : null;
|
|
19
|
+
}
|
|
20
|
+
function validTimestamp(value) {
|
|
21
|
+
return typeof value === 'string' && MANUAL_VALIDATION_TIMESTAMP.test(value) && Number.isFinite(Date.parse(value));
|
|
22
|
+
}
|
|
23
|
+
function writeJsonAtomic(file, value) {
|
|
24
|
+
const temporary = `${file}.${process.pid}.${randomUUID()}.tmp`;
|
|
25
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
26
|
+
try {
|
|
27
|
+
fs.writeFileSync(temporary, `${JSON.stringify(value)}\n`, { mode: 0o600 });
|
|
28
|
+
fs.renameSync(temporary, file);
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
try {
|
|
32
|
+
fs.unlinkSync(temporary);
|
|
33
|
+
}
|
|
34
|
+
catch { /* best effort cleanup of our own temp file */ }
|
|
35
|
+
throw error;
|
|
36
|
+
}
|
|
37
|
+
return file;
|
|
38
|
+
}
|
|
39
|
+
export { MANUAL_VALIDATION_ARTIFACT, MANUAL_VALIDATION_SHA40, MANUAL_VALIDATION_SHA64, MANUAL_VALIDATION_TASK_ID, MANUAL_VALIDATION_TIMESTAMP, exactKeys, isRecord, validTimestamp, writeJsonAtomic };
|
|
40
|
+
//# sourceMappingURL=manual-validation-shared.js.map
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
4
|
+
import { validateManualValidationReceipt } from './manual-validation-receipt.js';
|
|
5
|
+
import { MANUAL_VALIDATION_ARTIFACT as ARTIFACT, MANUAL_VALIDATION_SHA40 as SHA40, MANUAL_VALIDATION_SHA64 as SHA64, MANUAL_VALIDATION_TASK_ID as TASK_ID, exactKeys, isRecord, validTimestamp, writeJsonAtomic } from './manual-validation-shared.js';
|
|
6
|
+
const MANUAL_VALIDATION_TRANSACTION_SCHEMA = 'agent-infra/manual-validation-transaction';
|
|
7
|
+
const MANUAL_VALIDATION_TRANSACTION_VERSION = 1;
|
|
8
|
+
const TRANSACTION_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u;
|
|
9
|
+
function invalid(message) {
|
|
10
|
+
return { ok: false, error: { code: 'MANUAL_VALIDATION_TRANSACTION_INVALID', message } };
|
|
11
|
+
}
|
|
12
|
+
function summaryPreimageDigest(body) {
|
|
13
|
+
return createHash('sha256').update(body, 'utf8').digest('hex');
|
|
14
|
+
}
|
|
15
|
+
function validateManualValidationTransaction(value, expected) {
|
|
16
|
+
if (!isRecord(value))
|
|
17
|
+
return invalid('transaction must be a JSON object');
|
|
18
|
+
const keys = ['schema', 'version', 'transactionId', 'taskId', 'prNumber', 'prHeadSha', 'evidenceDigest', 'summaryPreimage', 'pendingSummaryDigest', 'finalSummaryDigest', 'artifact', 'phase', 'committedReceipt', 'eventAppended', 'attempt', 'postWriteVerified', 'createdAt', 'updatedAt', 'error'];
|
|
19
|
+
const keyError = exactKeys(value, keys, 'transaction');
|
|
20
|
+
if (keyError)
|
|
21
|
+
return invalid(keyError);
|
|
22
|
+
if (value.schema !== MANUAL_VALIDATION_TRANSACTION_SCHEMA || value.version !== MANUAL_VALIDATION_TRANSACTION_VERSION)
|
|
23
|
+
return invalid('transaction schema or version is unsupported');
|
|
24
|
+
if (typeof value.transactionId !== 'string' || !TRANSACTION_ID.test(value.transactionId))
|
|
25
|
+
return invalid('transactionId is invalid');
|
|
26
|
+
if (typeof value.taskId !== 'string' || !TASK_ID.test(value.taskId))
|
|
27
|
+
return invalid('taskId is invalid');
|
|
28
|
+
const prNumber = value.prNumber;
|
|
29
|
+
if (!Number.isSafeInteger(prNumber) || prNumber <= 0)
|
|
30
|
+
return invalid('prNumber is invalid');
|
|
31
|
+
if (typeof value.prHeadSha !== 'string' || !SHA40.test(value.prHeadSha))
|
|
32
|
+
return invalid('prHeadSha is invalid');
|
|
33
|
+
if (typeof value.evidenceDigest !== 'string' || !SHA64.test(value.evidenceDigest))
|
|
34
|
+
return invalid('evidenceDigest is invalid');
|
|
35
|
+
if (!isRecord(value.summaryPreimage))
|
|
36
|
+
return invalid('summaryPreimage is invalid');
|
|
37
|
+
const preimage = value.summaryPreimage;
|
|
38
|
+
const preimageKeyError = exactKeys(preimage, ['commentId', 'body', 'digest'], 'summaryPreimage');
|
|
39
|
+
if (preimageKeyError)
|
|
40
|
+
return invalid(preimageKeyError);
|
|
41
|
+
if (preimage.commentId !== null && typeof preimage.commentId !== 'number' && typeof preimage.commentId !== 'string')
|
|
42
|
+
return invalid('summaryPreimage.commentId is invalid');
|
|
43
|
+
if (typeof preimage.body !== 'string' || typeof preimage.digest !== 'string' || !SHA64.test(preimage.digest) || summaryPreimageDigest(preimage.body) !== preimage.digest)
|
|
44
|
+
return invalid('summaryPreimage digest is invalid');
|
|
45
|
+
for (const field of ['pendingSummaryDigest', 'finalSummaryDigest'])
|
|
46
|
+
if (typeof value[field] !== 'string' || !SHA64.test(value[field]))
|
|
47
|
+
return invalid(`${field} is invalid`);
|
|
48
|
+
if (typeof value.artifact !== 'string' || !ARTIFACT.test(value.artifact))
|
|
49
|
+
return invalid('artifact is invalid');
|
|
50
|
+
if (!['prepared', 'summary-staged', 'receipt-committed', 'final-promotion-in-progress', 'committed', 'aborted', 'recovery-required'].includes(value.phase))
|
|
51
|
+
return invalid('phase is invalid');
|
|
52
|
+
if (value.committedReceipt !== null && (typeof value.committedReceipt !== 'string' || !SHA64.test(value.committedReceipt)))
|
|
53
|
+
return invalid('committedReceipt is invalid');
|
|
54
|
+
if (typeof value.eventAppended !== 'boolean' || typeof value.postWriteVerified !== 'boolean')
|
|
55
|
+
return invalid('transaction completion flags are invalid');
|
|
56
|
+
const attempt = value.attempt;
|
|
57
|
+
if (!Number.isSafeInteger(attempt) || attempt < 1)
|
|
58
|
+
return invalid('attempt is invalid');
|
|
59
|
+
if (!validTimestamp(value.createdAt) || !validTimestamp(value.updatedAt))
|
|
60
|
+
return invalid('transaction timestamps are invalid');
|
|
61
|
+
if (value.error !== null && (typeof value.error !== 'string' || !value.error || /[\r\n]/u.test(value.error)))
|
|
62
|
+
return invalid('transaction error is invalid');
|
|
63
|
+
if (['receipt-committed', 'final-promotion-in-progress', 'committed'].includes(value.phase) && value.committedReceipt === null)
|
|
64
|
+
return invalid('receipt-backed phases require committedReceipt');
|
|
65
|
+
if (['final-promotion-in-progress', 'committed'].includes(value.phase) && !value.eventAppended)
|
|
66
|
+
return invalid('final promotion requires an appended completion event');
|
|
67
|
+
if (value.phase === 'committed' && !value.postWriteVerified)
|
|
68
|
+
return invalid('committed phase requires post-write verification');
|
|
69
|
+
const transaction = value;
|
|
70
|
+
if (expected && Object.entries(expected).some(([key, expectedValue]) => expectedValue !== undefined && transaction[key] !== expectedValue)) {
|
|
71
|
+
return { ok: false, error: { code: 'MANUAL_VALIDATION_TRANSACTION_IDENTITY_MISMATCH', message: 'transaction identity does not match the expected operation' } };
|
|
72
|
+
}
|
|
73
|
+
return { ok: true, value: transaction };
|
|
74
|
+
}
|
|
75
|
+
function createManualValidationTransaction(input) {
|
|
76
|
+
const transaction = {
|
|
77
|
+
schema: MANUAL_VALIDATION_TRANSACTION_SCHEMA,
|
|
78
|
+
version: MANUAL_VALIDATION_TRANSACTION_VERSION,
|
|
79
|
+
transactionId: input.transactionId ?? `mv-${randomUUID()}`,
|
|
80
|
+
taskId: input.taskId,
|
|
81
|
+
prNumber: input.prNumber,
|
|
82
|
+
prHeadSha: input.prHeadSha,
|
|
83
|
+
evidenceDigest: input.evidenceDigest,
|
|
84
|
+
summaryPreimage: input.summaryPreimage,
|
|
85
|
+
pendingSummaryDigest: input.pendingSummaryDigest,
|
|
86
|
+
finalSummaryDigest: input.finalSummaryDigest,
|
|
87
|
+
artifact: input.artifact,
|
|
88
|
+
phase: 'prepared',
|
|
89
|
+
committedReceipt: null,
|
|
90
|
+
eventAppended: false,
|
|
91
|
+
attempt: input.attempt,
|
|
92
|
+
postWriteVerified: false,
|
|
93
|
+
createdAt: input.createdAt,
|
|
94
|
+
updatedAt: input.updatedAt,
|
|
95
|
+
error: null
|
|
96
|
+
};
|
|
97
|
+
const checked = validateManualValidationTransaction(transaction);
|
|
98
|
+
if (!checked.ok)
|
|
99
|
+
throw new Error(`${checked.error.code}: ${checked.error.message}`);
|
|
100
|
+
return checked.value;
|
|
101
|
+
}
|
|
102
|
+
const allowedTransitions = {
|
|
103
|
+
prepared: ['prepared', 'summary-staged', 'aborted', 'recovery-required'],
|
|
104
|
+
'summary-staged': ['summary-staged', 'receipt-committed', 'aborted', 'recovery-required'],
|
|
105
|
+
'receipt-committed': ['receipt-committed', 'final-promotion-in-progress', 'aborted', 'recovery-required'],
|
|
106
|
+
'final-promotion-in-progress': ['final-promotion-in-progress', 'committed', 'aborted', 'recovery-required'],
|
|
107
|
+
committed: ['committed'],
|
|
108
|
+
aborted: ['aborted', 'prepared'],
|
|
109
|
+
'recovery-required': ['recovery-required', 'prepared']
|
|
110
|
+
};
|
|
111
|
+
function transitionManualValidationTransaction(current, phase, patch = {}) {
|
|
112
|
+
if (!allowedTransitions[current.phase].includes(phase))
|
|
113
|
+
return { ok: false, error: { code: 'MANUAL_VALIDATION_TRANSACTION_PHASE_INVALID', message: `cannot transition from ${current.phase} to ${phase}` } };
|
|
114
|
+
if (phase === 'final-promotion-in-progress' && (!current.eventAppended && patch.eventAppended !== true))
|
|
115
|
+
return { ok: false, error: { code: 'MANUAL_VALIDATION_TRANSACTION_PHASE_INVALID', message: 'final promotion requires an appended completion event' } };
|
|
116
|
+
const next = {
|
|
117
|
+
...current,
|
|
118
|
+
phase,
|
|
119
|
+
committedReceipt: patch.committedReceipt !== undefined ? patch.committedReceipt : current.committedReceipt,
|
|
120
|
+
eventAppended: patch.eventAppended ?? current.eventAppended,
|
|
121
|
+
postWriteVerified: patch.postWriteVerified ?? current.postWriteVerified,
|
|
122
|
+
attempt: patch.attempt ?? current.attempt,
|
|
123
|
+
error: patch.error !== undefined ? patch.error : current.error,
|
|
124
|
+
updatedAt: patch.updatedAt ?? new Date().toISOString()
|
|
125
|
+
};
|
|
126
|
+
const checked = validateManualValidationTransaction(next);
|
|
127
|
+
if (!checked.ok)
|
|
128
|
+
return checked;
|
|
129
|
+
return checked;
|
|
130
|
+
}
|
|
131
|
+
function retryManualValidationTransaction(current, now = new Date().toISOString()) {
|
|
132
|
+
if (!['aborted', 'recovery-required'].includes(current.phase))
|
|
133
|
+
return { ok: false, error: { code: 'MANUAL_VALIDATION_TRANSACTION_PHASE_INVALID', message: 'only aborted or recovery-required transactions can be retried' } };
|
|
134
|
+
return transitionManualValidationTransaction(current, 'prepared', {
|
|
135
|
+
updatedAt: now,
|
|
136
|
+
error: null,
|
|
137
|
+
committedReceipt: null,
|
|
138
|
+
eventAppended: false,
|
|
139
|
+
postWriteVerified: false,
|
|
140
|
+
attempt: current.attempt + 1
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
function manualValidationTransactionPath(taskDir) {
|
|
144
|
+
return path.join(taskDir, '.manual-validation', 'transaction.json');
|
|
145
|
+
}
|
|
146
|
+
function manualValidationGenerationPaths(taskDir, transaction) {
|
|
147
|
+
const stateDir = path.join(taskDir, '.manual-validation');
|
|
148
|
+
const historyDir = path.join(stateDir, 'history');
|
|
149
|
+
return {
|
|
150
|
+
receipt: {
|
|
151
|
+
current: path.join(stateDir, 'receipt.json'),
|
|
152
|
+
history: path.join(historyDir, `receipt-${transaction.transactionId}-attempt-${transaction.attempt}.json`)
|
|
153
|
+
},
|
|
154
|
+
transaction: {
|
|
155
|
+
current: manualValidationTransactionPath(taskDir),
|
|
156
|
+
history: path.join(historyDir, `transaction-${transaction.transactionId}-attempt-${transaction.attempt}.json`)
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
function readReceiptAt(file, transaction) {
|
|
161
|
+
let value;
|
|
162
|
+
try {
|
|
163
|
+
value = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
if (error.code === 'ENOENT')
|
|
167
|
+
return 'missing';
|
|
168
|
+
throw new Error(`manual-validation receipt is unreadable: ${error instanceof Error ? error.message : String(error)}`);
|
|
169
|
+
}
|
|
170
|
+
const checked = validateManualValidationReceipt(value, {
|
|
171
|
+
transactionId: transaction.transactionId,
|
|
172
|
+
taskId: transaction.taskId,
|
|
173
|
+
prNumber: transaction.prNumber,
|
|
174
|
+
prHeadSha: transaction.prHeadSha,
|
|
175
|
+
evidenceDigest: transaction.evidenceDigest,
|
|
176
|
+
artifact: transaction.artifact
|
|
177
|
+
});
|
|
178
|
+
if (!checked.ok)
|
|
179
|
+
throw new Error(`manual-validation receipt is invalid: ${checked.error.message}`);
|
|
180
|
+
if (transaction.committedReceipt !== null && checked.value.receiptDigest !== transaction.committedReceipt) {
|
|
181
|
+
throw new Error('manual-validation receipt does not match the committed transaction');
|
|
182
|
+
}
|
|
183
|
+
return 'valid';
|
|
184
|
+
}
|
|
185
|
+
function validateManualValidationGenerationArchive(taskDir, transaction, requireReceipt = false) {
|
|
186
|
+
const paths = manualValidationGenerationPaths(taskDir, transaction);
|
|
187
|
+
const currentTransaction = fs.existsSync(paths.transaction.current);
|
|
188
|
+
const archivedTransaction = fs.existsSync(paths.transaction.history);
|
|
189
|
+
if (currentTransaction && archivedTransaction)
|
|
190
|
+
throw new Error(`manual-validation archive has conflicting transaction files for ${transaction.transactionId}`);
|
|
191
|
+
if (!currentTransaction && !archivedTransaction)
|
|
192
|
+
throw new Error(`manual-validation archive source is missing: ${paths.transaction.current}`);
|
|
193
|
+
const currentReceipt = requireReceipt
|
|
194
|
+
? readReceiptAt(paths.receipt.current, transaction)
|
|
195
|
+
: (fs.existsSync(paths.receipt.current) ? 'valid' : 'missing');
|
|
196
|
+
const archivedReceipt = requireReceipt
|
|
197
|
+
? readReceiptAt(paths.receipt.history, transaction)
|
|
198
|
+
: (fs.existsSync(paths.receipt.history) ? 'valid' : 'missing');
|
|
199
|
+
if (requireReceipt && currentReceipt === 'missing' && archivedReceipt === 'missing') {
|
|
200
|
+
throw new Error(`manual-validation receipt is missing for transaction ${transaction.transactionId}`);
|
|
201
|
+
}
|
|
202
|
+
if (currentReceipt === 'valid' && archivedReceipt === 'valid') {
|
|
203
|
+
throw new Error(`manual-validation archive has conflicting receipt files for ${transaction.transactionId}`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
function archiveManualValidationGeneration(taskDir, transaction, requireReceipt = false, options = {}) {
|
|
207
|
+
const paths = manualValidationGenerationPaths(taskDir, transaction);
|
|
208
|
+
validateManualValidationGenerationArchive(taskDir, transaction, requireReceipt);
|
|
209
|
+
fs.mkdirSync(path.dirname(paths.receipt.history), { recursive: true });
|
|
210
|
+
const move = (source, target, required) => {
|
|
211
|
+
if (!fs.existsSync(source)) {
|
|
212
|
+
if (fs.existsSync(target) || !required)
|
|
213
|
+
return;
|
|
214
|
+
throw new Error(`manual-validation archive source is missing: ${source}`);
|
|
215
|
+
}
|
|
216
|
+
if (fs.existsSync(target))
|
|
217
|
+
throw new Error(`manual-validation archive target already exists: ${target}`);
|
|
218
|
+
fs.renameSync(source, target);
|
|
219
|
+
};
|
|
220
|
+
move(paths.receipt.current, paths.receipt.history, requireReceipt);
|
|
221
|
+
options.afterReceiptMove?.();
|
|
222
|
+
move(paths.transaction.current, paths.transaction.history, true);
|
|
223
|
+
}
|
|
224
|
+
function writeManualValidationTransactionAtomic(taskDir, transaction) {
|
|
225
|
+
const checked = validateManualValidationTransaction(transaction);
|
|
226
|
+
if (!checked.ok)
|
|
227
|
+
throw new Error(`${checked.error.code}: ${checked.error.message}`);
|
|
228
|
+
const target = manualValidationTransactionPath(taskDir);
|
|
229
|
+
return writeJsonAtomic(target, transaction);
|
|
230
|
+
}
|
|
231
|
+
function readManualValidationTransaction(taskDir, expected) {
|
|
232
|
+
const file = manualValidationTransactionPath(taskDir);
|
|
233
|
+
let value;
|
|
234
|
+
try {
|
|
235
|
+
value = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
236
|
+
}
|
|
237
|
+
catch (error) {
|
|
238
|
+
return { ok: false, error: { code: error.code === 'ENOENT' ? 'MANUAL_VALIDATION_TRANSACTION_MISSING' : 'MANUAL_VALIDATION_TRANSACTION_INVALID', message: error instanceof Error ? error.message : String(error) } };
|
|
239
|
+
}
|
|
240
|
+
return validateManualValidationTransaction(value, expected);
|
|
241
|
+
}
|
|
242
|
+
export { MANUAL_VALIDATION_TRANSACTION_SCHEMA, MANUAL_VALIDATION_TRANSACTION_VERSION, archiveManualValidationGeneration, createManualValidationTransaction, manualValidationTransactionPath, validateManualValidationGenerationArchive, readManualValidationTransaction, retryManualValidationTransaction, summaryPreimageDigest, transitionManualValidationTransaction, validateManualValidationTransaction, writeManualValidationTransactionAtomic };
|
|
243
|
+
//# sourceMappingURL=manual-validation-transaction.js.map
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
const HEADING = /^ {0,3}(#{1,6})[ \t]+(.+?)\s*$/;
|
|
2
|
+
const FENCE_OPEN = /^ {0,3}(`{3,}|~{3,})(.*)$/;
|
|
3
|
+
const FENCE_CLOSE = /^ {0,3}(`{3,}|~{3,})\s*$/;
|
|
4
|
+
const ANCHOR = /^\s*<a\s+id=(['"])([^'"]+)\1\s*><\/a>\s*$/;
|
|
5
|
+
function sourceLines(content) {
|
|
6
|
+
const lines = [];
|
|
7
|
+
let start = 0;
|
|
8
|
+
while (start < content.length) {
|
|
9
|
+
const newline = content.indexOf('\n', start);
|
|
10
|
+
const end = newline === -1 ? content.length : newline;
|
|
11
|
+
lines.push({ start, end, text: content.slice(start, end).replace(/\r$/, '') });
|
|
12
|
+
start = newline === -1 ? content.length : newline + 1;
|
|
13
|
+
}
|
|
14
|
+
return lines;
|
|
15
|
+
}
|
|
16
|
+
function scanVisibleMarkdown(content) {
|
|
17
|
+
const lines = sourceLines(content);
|
|
18
|
+
const visible = [];
|
|
19
|
+
const headings = [];
|
|
20
|
+
const anchors = [];
|
|
21
|
+
let fence = null;
|
|
22
|
+
for (const line of lines) {
|
|
23
|
+
if (fence) {
|
|
24
|
+
const close = line.text.match(FENCE_CLOSE);
|
|
25
|
+
if (close && close[1][0] === fence.character && close[1].length >= fence.length)
|
|
26
|
+
fence = null;
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
const open = line.text.match(FENCE_OPEN);
|
|
30
|
+
const marker = open?.[1];
|
|
31
|
+
const info = open?.[2] ?? '';
|
|
32
|
+
if (marker && !(marker[0] === '`' && info.includes('`'))) {
|
|
33
|
+
fence = { character: marker[0], length: marker.length };
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
visible.push(line);
|
|
37
|
+
const heading = line.text.match(HEADING);
|
|
38
|
+
if (heading)
|
|
39
|
+
headings.push({ ...line, level: heading[1].length, text: heading[2].trim() });
|
|
40
|
+
const anchor = line.text.match(ANCHOR);
|
|
41
|
+
if (anchor)
|
|
42
|
+
anchors.push({ ...line, id: anchor[2] });
|
|
43
|
+
}
|
|
44
|
+
return { lines: visible, headings, anchors, hasUnclosedFence: fence !== null };
|
|
45
|
+
}
|
|
46
|
+
export { scanVisibleMarkdown };
|
|
47
|
+
//# sourceMappingURL=markdown.js.map
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { createHash, randomUUID } from 'node:crypto';
|
|
4
|
+
import { artifactName, maxArtifactRound } from './artifact-name.js';
|
|
4
5
|
import { parseTypedTaskFrontmatter } from './frontmatter.js';
|
|
5
6
|
import { validateCurrentTaskContract } from './current-contract.js';
|
|
6
7
|
import { parseReviewSummary } from './review-artifacts.js';
|
|
7
8
|
import { LEDGER_SECTION_MISSING_CODE, LEDGER_SECTION_MISSING_MESSAGE, parseLedgerDocument, summarizeLedgerStage, validateLedgerRows } from './ledger.js';
|
|
8
9
|
import { resolveTaskRef } from './resolve-ref.js';
|
|
9
|
-
import { activateDelegation, abortPreparedDelegation, completeDelegationStage, consumeDelegation, dispatchDelegation, foldBlankToNull, isDelegationReceipt, managedDelegationRole, prepareDelegation, sealDelegation } from './delegation-receipts.js';
|
|
10
|
+
import { activateDelegation, abortActivatedDelegation, abortPreparedDelegation, completeDelegationStage, consumeDelegation, dispatchDelegation, foldBlankToNull, isDelegationReceipt, managedDelegationRole, prepareDelegation, sealDelegation } from './delegation-receipts.js';
|
|
10
11
|
import { normalizeAgentClients } from '../agent-clients/config.js';
|
|
11
12
|
import { resolveAgentRuntimeStoreRoot } from '../runtime/agent-runtime.js';
|
|
12
13
|
import { getAgentClientCapability, getAgentClientDelegationEvidence, getAgentClientModelSelection } from '../agent-clients/registry.js';
|
|
@@ -20,6 +21,7 @@ import { resolveDeliveryTarget, resolveDiffBase, resolveTargetHead } from './del
|
|
|
20
21
|
import { buildLifecycleFacts, canStart, recommendNext } from './capabilities.js';
|
|
21
22
|
import { hasOpenLifecycleExecution } from './activity-log.js';
|
|
22
23
|
import { invalidationBlocks } from './invalidation.js';
|
|
24
|
+
import { normalizeAgentToken } from '../agent-clients/tokens.js';
|
|
23
25
|
function supportsLifecycleDelegation(client) {
|
|
24
26
|
return getAgentClientCapability(client, 'subagents').level !== 'unsupported'
|
|
25
27
|
&& getAgentClientCapability(client, 'orchestration').level !== 'unsupported'
|
|
@@ -101,7 +103,7 @@ function isRecovery(value) {
|
|
|
101
103
|
&& guards.completionEvidenceAbsent === true
|
|
102
104
|
&& value.resultingStatus === 'running';
|
|
103
105
|
}
|
|
104
|
-
function isCompletionEvidence(value) {
|
|
106
|
+
export function isCompletionEvidence(value) {
|
|
105
107
|
return hasExactKeys(value, [
|
|
106
108
|
'kind', 'observedAt', 'head', 'headTree', 'worktreeTree', 'lastReviewedCommit', 'prNumber', 'prHead'
|
|
107
109
|
])
|
|
@@ -437,18 +439,8 @@ function beginOrResumeOrchestration(taskRef, options = {}) {
|
|
|
437
439
|
atomicWrite(orchestrationPath(resolved.taskDir), run);
|
|
438
440
|
return { status: 'running', changed: true, taskId: resolved.taskId, run, next: null, error: null };
|
|
439
441
|
}
|
|
440
|
-
function highestRound(taskDir, family) {
|
|
441
|
-
const pattern = new RegExp(`^${family}(?:-r(\\d+))?\\.md$`);
|
|
442
|
-
return fs.readdirSync(taskDir).reduce((max, name) => {
|
|
443
|
-
const match = pattern.exec(name);
|
|
444
|
-
return match ? Math.max(max, match[1] ? Number(match[1]) : 1) : max;
|
|
445
|
-
}, 0);
|
|
446
|
-
}
|
|
447
|
-
function artifactName(family, round) {
|
|
448
|
-
return round === 1 ? `${family}.md` : `${family}-r${round}.md`;
|
|
449
|
-
}
|
|
450
442
|
function validateSavedCurrentDiffBase(repoRoot, metadata, taskDir, reviewedHead) {
|
|
451
|
-
const reviewRound =
|
|
443
|
+
const reviewRound = maxArtifactRound(fs.readdirSync(taskDir), 'review-code');
|
|
452
444
|
const reviewContent = fs.readFileSync(path.join(taskDir, artifactName('review-code', reviewRound)), 'utf8');
|
|
453
445
|
const savedTargetHead = extractReviewTargetHead(reviewContent);
|
|
454
446
|
const savedDiffBase = extractReviewDiffBase(reviewContent);
|
|
@@ -492,10 +484,9 @@ function routeFromFacts(facts) {
|
|
|
492
484
|
if (action === 'manual-validation' || action === 'validation-run')
|
|
493
485
|
return null;
|
|
494
486
|
const family = action === 'analysis' ? 'analysis' : action;
|
|
495
|
-
const round =
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
})) + 1;
|
|
487
|
+
const round = maxArtifactRound([
|
|
488
|
+
...(facts.artifacts[family] ?? []), ...(facts.staleArtifacts?.[family] ?? [])
|
|
489
|
+
], family) + 1;
|
|
499
490
|
const command = action === 'analysis' ? 'analyze-task' : action === 'plan' ? 'plan-task' : action === 'code' ? 'code-task' : action;
|
|
500
491
|
const role = action.startsWith('review-') ? 'reviewer' : 'executor';
|
|
501
492
|
return { action: command, role, stage: action, round, artifact: artifactName(family, round) };
|
|
@@ -554,7 +545,7 @@ function routeOrchestration(taskRef, options = {}) {
|
|
|
554
545
|
if (!routed)
|
|
555
546
|
return failed('ORCHESTRATION_ROUTE_UNKNOWN', 'cannot determine a unique lifecycle action', resolved.taskId);
|
|
556
547
|
if ('completion' in routed) {
|
|
557
|
-
const reviewRound =
|
|
548
|
+
const reviewRound = maxArtifactRound(fs.readdirSync(resolved.taskDir), 'review-code');
|
|
558
549
|
const review = parseReviewSummary(fs.readFileSync(path.join(resolved.taskDir, artifactName('review-code', reviewRound)), 'utf8'));
|
|
559
550
|
if (!review.ok || review.summary.manualValidation === null) {
|
|
560
551
|
return failed('ORCHESTRATION_REVIEW_INVALID', 'latest code review has no numeric manual-validation count', resolved.taskId);
|
|
@@ -692,6 +683,13 @@ function prepareOrchestrationDelegationUnlocked(taskRef, input, options = {}) {
|
|
|
692
683
|
if (input.client === 'codex' && !input.lifecycleProvenance) {
|
|
693
684
|
return failed('ORCHESTRATION_CODEX_PROVENANCE_REQUIRED', 'Codex prepare requires lifecycle provenance', resolved.taskId);
|
|
694
685
|
}
|
|
686
|
+
if (input.lifecycleRecoveryAttestation
|
|
687
|
+
&& (input.lifecycleRecoveryAttestation.phase !== 'orchestration.prepare'
|
|
688
|
+
|| input.lifecycleRecoveryAttestation.taskId !== resolved.taskId
|
|
689
|
+
|| input.lifecycleRecoveryAttestation.artifact !== next.artifact
|
|
690
|
+
|| input.lifecycleRecoveryAttestation.round !== next.round)) {
|
|
691
|
+
return failed('LIFECYCLE_AUTHORITY_TUPLE_MISMATCH', 'orchestration authority does not match the routed stage', resolved.taskId);
|
|
692
|
+
}
|
|
695
693
|
const expectedPolicy = run.modelPolicy[next.role];
|
|
696
694
|
if (input.requestedModel !== expectedPolicy.model) {
|
|
697
695
|
return failed('ORCHESTRATION_REQUESTED_MODEL_MISMATCH', `requested model does not match the persisted ${next.role} model`, resolved.taskId);
|
|
@@ -1093,6 +1091,55 @@ function recoverPreparedOrchestrationDelegation(taskRef, options = {}) {
|
|
|
1093
1091
|
return failed('ORCHESTRATION_PREPARED_RECOVERY_FAILED', error instanceof Error ? error.message : String(error), resolved.taskId);
|
|
1094
1092
|
}
|
|
1095
1093
|
}
|
|
1094
|
+
function recoverActivatedOrchestrationDelegationUnderLock(taskRef, event, options = {}) {
|
|
1095
|
+
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
1096
|
+
if (!resolved.ok)
|
|
1097
|
+
return failed(resolved.code, resolved.message, resolved.taskId);
|
|
1098
|
+
const run = readRun(resolved.taskDir, options);
|
|
1099
|
+
if (!run)
|
|
1100
|
+
return failed('ORCHESTRATION_RUN_MISSING', 'no orchestration run exists', resolved.taskId);
|
|
1101
|
+
const matches = [
|
|
1102
|
+
...(run.pendingDelegation ? [run.pendingDelegation] : []),
|
|
1103
|
+
...run.receipts.filter((receipt) => receipt.id === event.receiptId)
|
|
1104
|
+
];
|
|
1105
|
+
const receipt = matches.find((candidate) => candidate.id === event.receiptId);
|
|
1106
|
+
if (!receipt)
|
|
1107
|
+
return failed('ORCHESTRATION_DELEGATION_MISSING', 'recovery receipt does not exist', resolved.taskId);
|
|
1108
|
+
if (receipt.taskId !== resolved.taskId
|
|
1109
|
+
|| receipt.stage !== event.stage
|
|
1110
|
+
|| receipt.round !== event.round
|
|
1111
|
+
|| receipt.artifact !== event.artifact
|
|
1112
|
+
|| receipt.childId !== event.childId
|
|
1113
|
+
|| normalizeAgentToken(event.startedAgent) !== normalizeAgentToken(receipt.client)) {
|
|
1114
|
+
return failed('ORCHESTRATION_PROVENANCE_MISMATCH', 'recovery selector does not match the delegation receipt', resolved.taskId);
|
|
1115
|
+
}
|
|
1116
|
+
if (receipt.status === 'aborted' && run.pendingDelegation === null) {
|
|
1117
|
+
return { status: run.status, changed: false, taskId: resolved.taskId, run, next: null, error: null };
|
|
1118
|
+
}
|
|
1119
|
+
if (run.pendingDelegation?.id !== receipt.id || receipt.status !== 'activated') {
|
|
1120
|
+
return failed('ORCHESTRATION_RECOVERY_UNSAFE', 'recovery requires one matching activated pending delegation', resolved.taskId);
|
|
1121
|
+
}
|
|
1122
|
+
if (run.receipts.some((candidate) => candidate.id === receipt.id)) {
|
|
1123
|
+
return failed('ORCHESTRATION_RECEIPT_DUPLICATE', 'recovery receipt already exists in the completed receipt history', resolved.taskId);
|
|
1124
|
+
}
|
|
1125
|
+
const aborted = abortActivatedDelegation(receipt, {
|
|
1126
|
+
childId: event.childId,
|
|
1127
|
+
stopRevision: event.stopRevision,
|
|
1128
|
+
consumer: event.consumer,
|
|
1129
|
+
consumedAt: event.consumedAt
|
|
1130
|
+
});
|
|
1131
|
+
if (!aborted.ok)
|
|
1132
|
+
return failed(aborted.code, aborted.message, resolved.taskId);
|
|
1133
|
+
const updated = withUpdatedRun(run, {
|
|
1134
|
+
status: 'running',
|
|
1135
|
+
pause: null,
|
|
1136
|
+
nextStage: null,
|
|
1137
|
+
pendingDelegation: null,
|
|
1138
|
+
receipts: Object.freeze([...run.receipts, aborted.receipt])
|
|
1139
|
+
}, options.now);
|
|
1140
|
+
saveRun(resolved.taskDir, updated);
|
|
1141
|
+
return { status: 'running', changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
|
|
1142
|
+
}
|
|
1096
1143
|
function completeOrchestrationStage(taskRef, event, options = {}) {
|
|
1097
1144
|
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
1098
1145
|
if (!resolved.ok)
|
|
@@ -1207,5 +1254,5 @@ function pauseOrchestration(taskRef, code, message, recoverable, options = {}) {
|
|
|
1207
1254
|
saveRun(resolved.taskDir, updated);
|
|
1208
1255
|
return { status: 'paused', changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
|
|
1209
1256
|
}
|
|
1210
|
-
export { activateMatchingOrchestrationDelegation, activateOrchestrationDelegation, awaitOrchestrationDelegationActivation, advanceOrchestration, beginOrResumeOrchestration, commitOrchestrationStageCompletion, completeOrchestrationStage, dispatchOrchestrationDelegation, inspectOrchestrationStage, hasActivatableOrchestrationDelegation, hasSealableOrchestrationDelegation, OrchestrationStateError, orchestrationPath, pauseMatchingOrchestrationDelegation, pauseOrchestration, planOrchestrationStageCompletion, prepareOrchestrationDelegation, readRun, reconcileMatchingOrchestrationDelegation, recoverPreparedOrchestrationDelegation, routeOrchestration, sealMatchingOrchestrationDelegation, sealMatchingOrchestrationDelegationWithHostEvidence, sealOrchestrationDelegation, statusOrchestration, };
|
|
1257
|
+
export { activateMatchingOrchestrationDelegation, activateOrchestrationDelegation, awaitOrchestrationDelegationActivation, advanceOrchestration, beginOrResumeOrchestration, commitOrchestrationStageCompletion, completeOrchestrationStage, dispatchOrchestrationDelegation, inspectOrchestrationStage, hasActivatableOrchestrationDelegation, hasSealableOrchestrationDelegation, OrchestrationStateError, orchestrationPath, pauseMatchingOrchestrationDelegation, pauseOrchestration, planOrchestrationStageCompletion, prepareOrchestrationDelegation, readRun, reconcileMatchingOrchestrationDelegation, recoverPreparedOrchestrationDelegation, recoverActivatedOrchestrationDelegationUnderLock, routeOrchestration, sealMatchingOrchestrationDelegation, sealMatchingOrchestrationDelegationWithHostEvidence, sealOrchestrationDelegation, statusOrchestration, };
|
|
1211
1258
|
//# sourceMappingURL=orchestration.js.map
|