@fitlab-ai/agent-infra 0.9.15 → 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/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +6 -2
- package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +43 -3
- package/dist/lib/fs/durable-write.js +2 -0
- package/dist/lib/internal/cli-route-inventory.js +1 -1
- package/dist/lib/internal/manual-validation.js +17 -8
- package/dist/lib/internal/task-artifact.js +1 -1
- package/dist/lib/internal/task-lifecycle.js +2 -2
- package/dist/lib/internal/task-operation-registry.js +0 -1
- package/dist/lib/internal/task-review.js +4 -4
- package/dist/lib/platform/issue-comments.js +16 -10
- package/dist/lib/platform/issues.js +1 -1
- package/dist/lib/platform/pr-change-report.js +5 -4
- package/dist/lib/platform/pr-summary.js +64 -32
- package/dist/lib/platform/pull-requests.js +3 -3
- package/dist/lib/platform/task-identities.js +2 -9
- package/dist/lib/platform/verification-sync.js +6 -2
- package/dist/lib/pr-review/evidence-grading.js +4 -8
- package/dist/lib/sandbox/commands/rm.js +21 -2
- package/dist/lib/sandbox/control/server.js +29 -9
- package/dist/lib/sandbox/control/state.js +7 -1
- package/dist/lib/sandbox/control/workflow-executor.js +4 -14
- package/dist/lib/sandbox/index.js +1 -2
- package/dist/lib/sandbox/removal.js +116 -1011
- package/dist/lib/sandbox/workspace-identity.js +17 -1
- package/dist/lib/task/artifact-command.js +6 -28
- package/dist/lib/task/artifact-operations.js +16 -230
- package/dist/lib/task/artifact-recovery.js +430 -0
- package/dist/lib/task/artifact-repair-intent.js +73 -39
- package/dist/lib/task/commit-operation.js +31 -4
- package/dist/lib/task/control-authority.js +38 -7
- package/dist/lib/task/control-recovery.js +14 -2
- package/dist/lib/task/create-service.js +1 -8
- package/dist/lib/task/delegation-receipts.js +64 -3
- package/dist/lib/task/events.js +164 -22
- package/dist/lib/task/human-override.js +5 -8
- package/dist/lib/task/intermediate-cleanup.js +66 -8
- package/dist/lib/task/lifecycle-recovery.js +549 -0
- package/dist/lib/task/lifecycle.js +12 -3
- package/dist/lib/task/local-artifact-finalization.js +148 -123
- package/dist/lib/task/manual-validation-receipt.js +5 -1
- package/dist/lib/task/orchestration.js +52 -2
- package/dist/lib/task/pr-delivery-fact.js +12 -56
- package/dist/lib/task/recovery-warning.js +19 -0
- package/dist/lib/task/review-command.js +1 -0
- package/dist/lib/task/review-finalization.js +104 -77
- 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 +3 -14
- package/dist/lib/task/workflow-command.js +0 -1
- package/dist/lib/version.js +1 -11
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +7 -2
- package/lib/agent-clients/adapters/codex-lifecycle/store.ts +55 -3
- package/lib/fs/durable-write.ts +1 -0
- package/lib/internal/cli-route-inventory.ts +1 -1
- package/lib/internal/manual-validation.ts +17 -8
- package/lib/internal/task-artifact.ts +1 -1
- package/lib/internal/task-lifecycle.ts +4 -3
- package/lib/internal/task-operation-registry.ts +0 -1
- package/lib/internal/task-review.ts +4 -4
- package/lib/platform/issue-comments.ts +18 -10
- package/lib/platform/issues.ts +1 -1
- package/lib/platform/pr-change-report.ts +7 -5
- package/lib/platform/pr-summary.ts +64 -32
- package/lib/platform/pull-requests.ts +3 -3
- package/lib/platform/task-identities.ts +3 -14
- package/lib/platform/verification-sync.ts +6 -2
- package/lib/pr-review/evidence-grading.ts +4 -9
- package/lib/sandbox/commands/rm.ts +34 -2
- package/lib/sandbox/control/protocol.ts +5 -0
- package/lib/sandbox/control/server.ts +30 -10
- package/lib/sandbox/control/state.ts +9 -1
- package/lib/sandbox/control/workflow-executor.ts +4 -14
- package/lib/sandbox/index.ts +1 -2
- package/lib/sandbox/removal.ts +126 -1128
- package/lib/sandbox/workspace-identity.ts +19 -3
- package/lib/task/artifact-command.ts +8 -27
- package/lib/task/artifact-operations.ts +21 -251
- package/lib/task/artifact-recovery.ts +519 -0
- package/lib/task/artifact-repair-intent.ts +99 -51
- package/lib/task/commit-operation.ts +35 -4
- package/lib/task/control-authority.ts +47 -14
- package/lib/task/control-recovery.ts +13 -1
- package/lib/task/create-service.ts +1 -10
- package/lib/task/delegation-receipts.ts +73 -2
- package/lib/task/events.ts +187 -23
- package/lib/task/human-override.ts +5 -8
- package/lib/task/intermediate-cleanup.ts +50 -12
- package/lib/task/lifecycle-recovery.ts +691 -0
- package/lib/task/lifecycle.ts +13 -4
- package/lib/task/local-artifact-finalization.ts +176 -132
- package/lib/task/manual-validation-receipt.ts +5 -1
- package/lib/task/orchestration.ts +65 -0
- package/lib/task/pr-delivery-fact.ts +15 -62
- package/lib/task/recovery-warning.ts +21 -0
- package/lib/task/review-command.ts +1 -0
- package/lib/task/review-finalization.ts +106 -87
- package/lib/task/short-id.ts +27 -5
- package/lib/task/task-execution-lock.ts +1 -1
- package/lib/task/verification-engine.ts +3 -17
- package/lib/task/verification-types.ts +0 -2
- package/lib/task/workflow-command.ts +0 -1
- package/lib/version.ts +1 -15
- package/package.json +1 -1
- 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/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 +1 -1
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/complete-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +2 -2
- 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/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/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-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
- 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
|
@@ -2,12 +2,13 @@ import fs from 'node:fs';
|
|
|
2
2
|
import { inspectDecisionDetailDuplicates } from './decision-details.js';
|
|
3
3
|
import { scanVisibleMarkdown } from './markdown.js';
|
|
4
4
|
import { parseArtifactName } from './artifact-name.js';
|
|
5
|
-
import { hasOpenArtifactRound, validateArtifactPublication, validateCompletedArtifact } from './artifact-lifecycle.js';
|
|
5
|
+
import { hasOpenArtifactRound, parseCodePlanInputReference, validateArtifactPublication, validateCompletedArtifact } from './artifact-lifecycle.js';
|
|
6
6
|
import { resolveTaskRef } from './resolve-ref.js';
|
|
7
7
|
import { expectedQualificationRelations, validateQualificationAudit } from './qualification-audit.js';
|
|
8
8
|
import { canonicalSemanticDigest, inspectArtifactPatterns, inspectArtifactStructure, sha256Content } from './artifact-operations.js';
|
|
9
9
|
import { getArtifactSchema } from './artifact-schema.js';
|
|
10
|
-
import {
|
|
10
|
+
import { readArtifactRecoveryIntent } from './artifact-repair-intent.js';
|
|
11
|
+
import { beginArtifactRecovery, commitArtifactRecovery, consumeArtifactRecovery, prepareArtifactRecoveryCommit, recordArtifactRecoveryPassed, recoveryContextFromIntent, stageArtifactCandidate } from './artifact-recovery.js';
|
|
11
12
|
import { consumeLifecycleRecoveryAttestation, lifecycleRecoveryAttestationDigest, validateLifecycleRecoveryAttestation } from './control-authority.js';
|
|
12
13
|
const LOCAL_ARTIFACT_REQUIRED_SECTIONS = {
|
|
13
14
|
analysis: getArtifactSchema('analysis').sections.map((section) => section.headings.zh),
|
|
@@ -35,14 +36,21 @@ function validFinalizerAuthority(authority, request, taskId) {
|
|
|
35
36
|
&& authority.requestId.length > 0
|
|
36
37
|
&& authority.operationId.length > 0;
|
|
37
38
|
}
|
|
38
|
-
function
|
|
39
|
+
function recoveryInfo(context) {
|
|
40
|
+
return {
|
|
41
|
+
recoveryId: context.recoveryId,
|
|
42
|
+
candidatePath: context.stagingPath,
|
|
43
|
+
baselineSha256: context.baselineSha256,
|
|
44
|
+
baselineSemanticDigest: context.baselineSemanticDigest
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function consumeLocalArtifactFinalizationIntent(repoRoot, intent, options = {}) {
|
|
39
48
|
if (intent.state === 'consumed')
|
|
40
49
|
return intent;
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
return consumed;
|
|
50
|
+
const resolved = resolveTaskRef(intent.taskId, { repoRoot });
|
|
51
|
+
if (!resolved.ok)
|
|
52
|
+
throw new Error(resolved.message);
|
|
53
|
+
return consumeArtifactRecovery(recoveryContextFromIntent(repoRoot, resolved.taskDir, intent), options);
|
|
46
54
|
}
|
|
47
55
|
function localDiagnosticCode(code) {
|
|
48
56
|
const map = {
|
|
@@ -54,78 +62,56 @@ function localDiagnosticCode(code) {
|
|
|
54
62
|
return map[code] ?? 'LOCAL_STRUCTURAL_INVALID';
|
|
55
63
|
}
|
|
56
64
|
function localDiagnostic(item) {
|
|
57
|
-
|
|
58
|
-
if (!code)
|
|
59
|
-
return null;
|
|
60
|
-
return {
|
|
61
|
-
code,
|
|
62
|
-
message: item.message,
|
|
63
|
-
repairable: item.repairable,
|
|
64
|
-
line: item.line,
|
|
65
|
-
...(item.operation?.kind === 'replace-line'
|
|
66
|
-
? { from: item.operation.from, to: item.operation.to, operation: 'replace-line' }
|
|
67
|
-
: {})
|
|
68
|
-
};
|
|
65
|
+
return { code: localDiagnosticCode(item.code), message: item.message, line: item.line };
|
|
69
66
|
}
|
|
70
67
|
function isStatusPattern(pattern) {
|
|
71
68
|
return pattern === '^\\$ ';
|
|
72
69
|
}
|
|
73
70
|
function validateLocalArtifact(content, options) {
|
|
74
71
|
const schema = getArtifactSchema(options.family);
|
|
75
|
-
const patterns = schema.requiredPatterns;
|
|
76
72
|
const diagnostics = [];
|
|
77
73
|
const scanned = scanVisibleMarkdown(content);
|
|
78
74
|
const structure = inspectArtifactStructure(content, schema);
|
|
79
|
-
for (const item of structure.diagnostics)
|
|
80
|
-
|
|
81
|
-
if (mapped)
|
|
82
|
-
diagnostics.push(mapped);
|
|
83
|
-
}
|
|
75
|
+
for (const item of structure.diagnostics)
|
|
76
|
+
diagnostics.push(localDiagnostic(item));
|
|
84
77
|
const statusSection = schema.sections.find((section) => section.id === 'state-check');
|
|
85
78
|
if (statusSection) {
|
|
86
79
|
const statusHeading = scanned.headings.find((heading) => (heading.level === 2 && [statusSection.headings.zh, statusSection.headings.en].some((name) => (heading.text === name || heading.text === `${name}:` || heading.text === `${name}:`))));
|
|
87
80
|
if (statusHeading) {
|
|
88
81
|
const next = scanned.headings.find((candidate) => candidate.start > statusHeading.start && candidate.level <= 2);
|
|
89
82
|
const body = content.slice(statusHeading.end, next?.start ?? content.length);
|
|
90
|
-
for (const pattern of
|
|
83
|
+
for (const pattern of schema.requiredPatterns.filter(isStatusPattern)) {
|
|
91
84
|
if (!new RegExp(pattern, 'm').test(body)) {
|
|
92
|
-
diagnostics.push({ code: 'LOCAL_STATUS_COMMAND_MISSING', message: `status section '${statusHeading.text}' is missing required command output`,
|
|
85
|
+
diagnostics.push({ code: 'LOCAL_STATUS_COMMAND_MISSING', message: `status section '${statusHeading.text}' is missing required command output`, line: content.slice(0, statusHeading.start).split('\n').length });
|
|
93
86
|
}
|
|
94
87
|
}
|
|
95
88
|
}
|
|
96
89
|
}
|
|
97
90
|
const patternInspection = inspectArtifactPatterns(content, {
|
|
98
91
|
...schema,
|
|
99
|
-
requiredPatterns:
|
|
92
|
+
requiredPatterns: schema.requiredPatterns.filter((item) => !isStatusPattern(item))
|
|
100
93
|
});
|
|
101
|
-
for (const item of patternInspection.diagnostics)
|
|
102
|
-
diagnostics.push({ code: 'LOCAL_REQUIRED_PATTERN_MISSING', message: item.message,
|
|
103
|
-
}
|
|
94
|
+
for (const item of patternInspection.diagnostics)
|
|
95
|
+
diagnostics.push({ code: 'LOCAL_REQUIRED_PATTERN_MISSING', message: item.message, line: null });
|
|
104
96
|
const decisionDetails = inspectDecisionDetailDuplicates(content);
|
|
105
|
-
if (!decisionDetails.ok)
|
|
106
|
-
diagnostics.push({ code: 'LOCAL_DECISION_DETAIL_DUPLICATE', message: decisionDetails.message,
|
|
107
|
-
}
|
|
97
|
+
if (!decisionDetails.ok)
|
|
98
|
+
diagnostics.push({ code: 'LOCAL_DECISION_DETAIL_DUPLICATE', message: decisionDetails.message, line: null });
|
|
108
99
|
if (options.taskContent !== undefined) {
|
|
109
100
|
const expected = expectedQualificationRelations(options.taskContent, options.family);
|
|
110
|
-
if (!expected.ok)
|
|
111
|
-
diagnostics.push({ code: 'LOCAL_QUALIFICATION_AUDIT_INVALID', message: `${expected.code}: ${expected.message}`,
|
|
112
|
-
}
|
|
101
|
+
if (!expected.ok)
|
|
102
|
+
diagnostics.push({ code: 'LOCAL_QUALIFICATION_AUDIT_INVALID', message: `${expected.code}: ${expected.message}`, line: null });
|
|
113
103
|
const qualification = validateQualificationAudit(options.taskContent, content, {
|
|
114
|
-
family: options.family
|
|
104
|
+
family: options.family,
|
|
115
105
|
artifact: options.artifact,
|
|
116
106
|
expectedUpstreamRelations: expected.ok ? expected.relations : undefined
|
|
117
107
|
});
|
|
118
|
-
if (!qualification.ok)
|
|
119
|
-
diagnostics.push({ code: 'LOCAL_QUALIFICATION_AUDIT_INVALID', message: `${qualification.code}: ${qualification.message}`,
|
|
120
|
-
}
|
|
108
|
+
if (!qualification.ok)
|
|
109
|
+
diagnostics.push({ code: 'LOCAL_QUALIFICATION_AUDIT_INVALID', message: `${qualification.code}: ${qualification.message}`, line: null });
|
|
121
110
|
}
|
|
122
|
-
const repairable = structure.repair !== null && diagnostics.length === 1 && diagnostics[0]?.repairable === true;
|
|
123
|
-
const semanticDigestValue = canonicalSemanticDigest(content, structure.repair);
|
|
124
111
|
return {
|
|
125
112
|
ok: diagnostics.length === 0,
|
|
126
113
|
family: options.family,
|
|
127
|
-
semanticDigest:
|
|
128
|
-
repairable,
|
|
114
|
+
semanticDigest: canonicalSemanticDigest(content),
|
|
129
115
|
diagnostics
|
|
130
116
|
};
|
|
131
117
|
}
|
|
@@ -135,13 +121,24 @@ function failedFinalization(request, error, extra = {}) {
|
|
|
135
121
|
taskId: null, taskDir: null,
|
|
136
122
|
family: request.family, artifact: request.artifact,
|
|
137
123
|
artifactSha256: null, semanticDigest: null,
|
|
138
|
-
|
|
124
|
+
diagnostics: [], error, ...extra
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
function tupleFor(request, taskId, authority) {
|
|
128
|
+
const round = parseArtifactName(request.artifact)?.round ?? 0;
|
|
129
|
+
return {
|
|
130
|
+
taskId,
|
|
131
|
+
family: request.family,
|
|
132
|
+
artifact: request.artifact,
|
|
133
|
+
round,
|
|
134
|
+
requestId: authority?.lifecycleRequestId ?? `local-finalize:${taskId}:${request.family}:${round}`,
|
|
135
|
+
phase: authority?.phase ?? 'artifact.finalize-local',
|
|
136
|
+
authorityDigest: authority ? authorityDigest(authority) : null
|
|
139
137
|
};
|
|
140
138
|
}
|
|
141
|
-
/** Read and validate once; callers publish this content before committing provenance. */
|
|
142
139
|
function prepareLocalArtifact(request, candidate, authority) {
|
|
143
|
-
const failed = (code, message) => ({
|
|
144
|
-
result: failedFinalization(request, { code, message }), content: candidate ?? ''
|
|
140
|
+
const failed = (code, message, extra = {}) => ({
|
|
141
|
+
result: failedFinalization(request, { code, message }, extra), content: candidate ?? ''
|
|
145
142
|
});
|
|
146
143
|
const resolved = resolveTaskRef(request.taskRef, { repoRoot: request.repoRoot });
|
|
147
144
|
if (!resolved.ok)
|
|
@@ -149,114 +146,142 @@ function prepareLocalArtifact(request, candidate, authority) {
|
|
|
149
146
|
const parsed = parseArtifactName(request.artifact);
|
|
150
147
|
if (!parsed || parsed.family !== request.family)
|
|
151
148
|
return failed('ARTIFACT_IDENTITY_INVALID', `artifact '${request.artifact}' does not match ${request.family}`);
|
|
152
|
-
|
|
149
|
+
if (authority && !validFinalizerAuthority(authority, request, resolved.taskId)) {
|
|
150
|
+
return failed('LOCAL_EXECUTION_AUTHORITY_INVALID', 'finalizer authority does not match the artifact tuple');
|
|
151
|
+
}
|
|
153
152
|
let taskContent;
|
|
153
|
+
let content;
|
|
154
|
+
let recovery;
|
|
154
155
|
try {
|
|
155
156
|
taskContent = fs.readFileSync(resolved.taskMdPath, 'utf8');
|
|
156
|
-
if (
|
|
157
|
-
const
|
|
158
|
-
if (
|
|
159
|
-
return failed(
|
|
160
|
-
if (
|
|
161
|
-
return failed('
|
|
162
|
-
|
|
163
|
-
content =
|
|
157
|
+
if (request.recoveryId) {
|
|
158
|
+
const intent = readArtifactRecoveryIntent(resolved.repoRoot, resolved.taskId, request.family, request.artifact);
|
|
159
|
+
if (!intent || intent.recoveryOperationId !== request.recoveryId)
|
|
160
|
+
return failed('LOCAL_RECOVERY_PROVENANCE_CONFLICT', 'recovery id does not match the artifact journal');
|
|
161
|
+
if (!['awaiting-recovery', 'finalize-ready', 'commit-started'].includes(intent.state))
|
|
162
|
+
return failed('LOCAL_RECOVERY_PROVENANCE_CONFLICT', `recovery journal is in '${intent.state}' state`);
|
|
163
|
+
recovery = recoveryContextFromIntent(resolved.repoRoot, resolved.taskDir, intent);
|
|
164
|
+
content = fs.existsSync(recovery.stagingPath)
|
|
165
|
+
? fs.readFileSync(recovery.stagingPath, 'utf8')
|
|
166
|
+
: fs.readFileSync(recovery.formalPath, 'utf8');
|
|
164
167
|
}
|
|
165
168
|
else {
|
|
166
169
|
const validated = validateCompletedArtifact(resolved.taskDir, request.family, request.artifact, parsed.round);
|
|
167
170
|
if (!validated.ok)
|
|
168
171
|
return failed(validated.error.code, validated.error.message);
|
|
169
|
-
content = fs.readFileSync(validated.artifact.path, 'utf8');
|
|
172
|
+
content = candidate ?? fs.readFileSync(validated.artifact.path, 'utf8');
|
|
170
173
|
}
|
|
171
174
|
}
|
|
172
175
|
catch (error) {
|
|
173
176
|
return failed('ARTIFACT_NOT_READABLE', String(error));
|
|
174
177
|
}
|
|
178
|
+
if (resolved.state !== 'active' || !hasOpenArtifactRound(taskContent, request.family, parsed.round)) {
|
|
179
|
+
return failed('ARTIFACT_IDENTITY_INVALID', 'candidate must match one open started round in an active task');
|
|
180
|
+
}
|
|
175
181
|
const validation = validateLocalArtifact(content, {
|
|
176
182
|
family: request.family, requiredSections: request.requiredSections, taskContent, artifact: request.artifact
|
|
177
183
|
});
|
|
178
184
|
const artifactSha256 = sha256Content(content);
|
|
179
|
-
const { semanticDigest, repairable, diagnostics } = validation;
|
|
180
185
|
const result = failedFinalization(request, {
|
|
181
|
-
code: 'LOCAL_ARTIFACT_INVALID',
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
186
|
+
code: 'LOCAL_ARTIFACT_INVALID',
|
|
187
|
+
message: validation.diagnostics.map((item) => `${item.code}: ${item.message}`).join('; ')
|
|
188
|
+
}, {
|
|
189
|
+
taskId: resolved.taskId, taskDir: resolved.taskDir, artifactSha256,
|
|
190
|
+
semanticDigest: validation.semanticDigest, diagnostics: validation.diagnostics
|
|
191
|
+
});
|
|
192
|
+
if (request.recoveryId) {
|
|
193
|
+
if (!validation.ok)
|
|
194
|
+
return { result, content, repoRoot: resolved.repoRoot, recovery, authority, lockAlreadyHeld: request.lockAlreadyHeld };
|
|
185
195
|
return {
|
|
186
|
-
...
|
|
187
|
-
|
|
188
|
-
code: 'LOCAL_EXECUTION_AUTHORITY_INVALID',
|
|
189
|
-
message: 'finalizer authority does not match the artifact tuple'
|
|
190
|
-
}, diagnostics: [] }
|
|
196
|
+
result: { ...result, status: 'passed', error: null, recovery: recoveryInfo(recovery) },
|
|
197
|
+
content, repoRoot: resolved.repoRoot, recovery, authority, lockAlreadyHeld: request.lockAlreadyHeld
|
|
191
198
|
};
|
|
192
199
|
}
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
let intent;
|
|
198
|
-
try {
|
|
199
|
-
intent = readArtifactRepairIntent(resolved.repoRoot, resolved.taskId, request.family, request.artifact);
|
|
200
|
-
}
|
|
201
|
-
catch (error) {
|
|
202
|
-
return { ...preparedBase, result: { ...result, repairable: false, diagnostics: [],
|
|
203
|
-
error: { code: 'LOCAL_FINALIZATION_INTENT_INVALID', message: String(error) } } };
|
|
204
|
-
}
|
|
205
|
-
const prepared = { ...preparedBase, expectedIntent: intent };
|
|
206
|
-
if (intent && intent.recoveryOperationId !== null
|
|
207
|
-
&& (!authority || intent.recoveryOperationId !== authority.operationId)) {
|
|
208
|
-
return reject('LOCAL_REPAIR_PROVENANCE_CONFLICT', 'local repair provenance is owned by another recovery operation');
|
|
209
|
-
}
|
|
210
|
-
if (repairable) {
|
|
211
|
-
if (intent && (intent.state !== 'awaiting-repair' || intent.baselineSemanticDigest !== semanticDigest)) {
|
|
212
|
-
return reject('LOCAL_REPAIR_PROVENANCE_CONFLICT', 'a different local repair baseline is already recorded for this artifact');
|
|
200
|
+
const existing = readArtifactRecoveryIntent(resolved.repoRoot, resolved.taskId, request.family, request.artifact);
|
|
201
|
+
if (existing?.state === 'passed' || existing?.state === 'consumed') {
|
|
202
|
+
if (existing.finalArtifactSha256 !== artifactSha256 || existing.finalSemanticDigest !== validation.semanticDigest) {
|
|
203
|
+
return { ...failed('LOCAL_RECOVERY_PROVENANCE_CONFLICT', 'formal artifact does not match its completed recovery journal'), content, repoRoot: resolved.repoRoot, authority, lockAlreadyHeld: request.lockAlreadyHeld };
|
|
213
204
|
}
|
|
214
|
-
|
|
215
|
-
|
|
205
|
+
const context = recoveryContextFromIntent(resolved.repoRoot, resolved.taskDir, existing);
|
|
206
|
+
return {
|
|
207
|
+
result: { ...result, status: 'passed', error: null, recovery: recoveryInfo(context) },
|
|
208
|
+
content, repoRoot: resolved.repoRoot, recovery: context, authority, lockAlreadyHeld: request.lockAlreadyHeld
|
|
209
|
+
};
|
|
216
210
|
}
|
|
217
|
-
|
|
218
|
-
if (
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
211
|
+
try {
|
|
212
|
+
if (validation.ok) {
|
|
213
|
+
const context = recordArtifactRecoveryPassed(tupleFor(request, resolved.taskId, authority), {
|
|
214
|
+
repoRoot: resolved.repoRoot,
|
|
215
|
+
taskDir: resolved.taskDir,
|
|
216
|
+
expectedFinalSha256: artifactSha256,
|
|
217
|
+
expectedFinalSemanticDigest: validation.semanticDigest,
|
|
218
|
+
...(authority ? { recoveryId: authority.operationId } : {}),
|
|
219
|
+
lockAlreadyHeld: request.lockAlreadyHeld
|
|
220
|
+
});
|
|
221
|
+
recovery = context;
|
|
222
|
+
return { result: { ...result, status: 'passed', error: null, recovery: recoveryInfo(context) }, content, repoRoot: resolved.repoRoot, recovery, authority, lockAlreadyHeld: request.lockAlreadyHeld };
|
|
226
223
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
224
|
+
const context = beginArtifactRecovery(tupleFor(request, resolved.taskId, authority), Buffer.from(content, 'utf8'), {
|
|
225
|
+
repoRoot: resolved.repoRoot,
|
|
226
|
+
taskDir: resolved.taskDir,
|
|
227
|
+
...(authority ? { recoveryId: authority.operationId } : {}),
|
|
228
|
+
lockAlreadyHeld: request.lockAlreadyHeld
|
|
229
|
+
});
|
|
230
|
+
recovery = context;
|
|
231
|
+
return {
|
|
232
|
+
result: { ...result, recovery: recoveryInfo(context) },
|
|
233
|
+
content, repoRoot: resolved.repoRoot, recovery, authority, lockAlreadyHeld: request.lockAlreadyHeld
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
catch (error) {
|
|
237
|
+
return { ...failed('LOCAL_RECOVERY_PROVENANCE_CONFLICT', String(error)), content, repoRoot: resolved.repoRoot, authority, lockAlreadyHeld: request.lockAlreadyHeld };
|
|
230
238
|
}
|
|
231
|
-
const timestamp = Date.now();
|
|
232
|
-
return { ...prepared, provenance: {
|
|
233
|
-
version: 2, taskId: resolved.taskId, family: request.family, artifact: request.artifact,
|
|
234
|
-
state: repairable ? 'awaiting-repair' : 'passed',
|
|
235
|
-
baselineSemanticDigest: repairable ? semanticDigest : intent?.baselineSemanticDigest ?? null,
|
|
236
|
-
artifactSha256, semanticDigest,
|
|
237
|
-
recoveryOperationId: authority?.operationId ?? intent?.recoveryOperationId ?? null,
|
|
238
|
-
phase: authority?.phase ?? intent?.phase ?? null,
|
|
239
|
-
authorityDigest: authority ? authorityDigest(authority) : intent?.authorityDigest ?? null,
|
|
240
|
-
requestId: authority?.lifecycleRequestId ?? intent?.requestId ?? `local-finalize:${resolved.taskId}`,
|
|
241
|
-
createdAt: intent?.createdAt ?? timestamp,
|
|
242
|
-
updatedAt: timestamp
|
|
243
|
-
} };
|
|
244
239
|
}
|
|
245
240
|
function commitLocalArtifactProvenance(prepared) {
|
|
241
|
+
if (!prepared.recovery || !prepared.repoRoot || prepared.result.status === 'failed' && !prepared.result.recovery)
|
|
242
|
+
return prepared.result;
|
|
243
|
+
if (prepared.result.status === 'failed')
|
|
244
|
+
return prepared.result;
|
|
246
245
|
try {
|
|
247
|
-
|
|
248
|
-
|
|
246
|
+
const intent = readArtifactRecoveryIntent(prepared.repoRoot, prepared.recovery.taskId, prepared.recovery.family, prepared.recovery.artifact);
|
|
247
|
+
if (!intent)
|
|
248
|
+
throw new Error('ARTIFACT_RECOVERY_INTENT_MISSING');
|
|
249
|
+
let committed;
|
|
250
|
+
if (intent.state === 'passed' || intent.state === 'consumed') {
|
|
251
|
+
return {
|
|
252
|
+
...prepared.result,
|
|
253
|
+
artifactSha256: intent.finalArtifactSha256,
|
|
254
|
+
semanticDigest: intent.finalSemanticDigest,
|
|
255
|
+
error: null
|
|
256
|
+
};
|
|
249
257
|
}
|
|
250
|
-
|
|
258
|
+
else if (intent.state === 'finalize-ready' || intent.state === 'commit-started') {
|
|
259
|
+
committed = commitArtifactRecovery(prepared.recovery, { lockAlreadyHeld: prepared.lockAlreadyHeld });
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
const staged = stageArtifactCandidate(prepared.recovery, Buffer.from(prepared.content, 'utf8'), { lockAlreadyHeld: prepared.lockAlreadyHeld });
|
|
263
|
+
prepareArtifactRecoveryCommit(prepared.recovery, staged.candidateSha256, staged.semanticDigest, { lockAlreadyHeld: prepared.lockAlreadyHeld });
|
|
264
|
+
committed = commitArtifactRecovery(prepared.recovery, { lockAlreadyHeld: prepared.lockAlreadyHeld });
|
|
265
|
+
}
|
|
266
|
+
if (committed.state !== 'passed')
|
|
267
|
+
throw new Error(`ARTIFACT_RECOVERY_STATE_INVALID: commit ended in '${committed.state}'`);
|
|
268
|
+
return {
|
|
269
|
+
...prepared.result,
|
|
270
|
+
status: 'passed',
|
|
271
|
+
artifactSha256: committed.finalArtifactSha256,
|
|
272
|
+
semanticDigest: committed.finalSemanticDigest,
|
|
273
|
+
error: null
|
|
274
|
+
};
|
|
251
275
|
}
|
|
252
276
|
catch (error) {
|
|
253
277
|
return { ...prepared.result, status: 'failed', error: {
|
|
254
|
-
code: '
|
|
278
|
+
code: 'LOCAL_RECOVERY_COMMIT_FAILED', message: String(error)
|
|
255
279
|
} };
|
|
256
280
|
}
|
|
257
281
|
}
|
|
258
282
|
function finalizeLocalArtifact(request, authority, options = {}) {
|
|
259
|
-
const
|
|
283
|
+
const prepared = prepareLocalArtifact(request, undefined, authority);
|
|
284
|
+
const result = commitLocalArtifactProvenance(prepared);
|
|
260
285
|
if (authority && !options.deferLifecycleRecoveryConsumption && result.status === 'passed') {
|
|
261
286
|
try {
|
|
262
287
|
consumeLifecycleRecoveryAttestation(authority);
|
|
@@ -270,5 +295,5 @@ function finalizeLocalArtifact(request, authority, options = {}) {
|
|
|
270
295
|
}
|
|
271
296
|
return result;
|
|
272
297
|
}
|
|
273
|
-
export { LOCAL_ARTIFACT_REQUIRED_SECTIONS, consumeLocalArtifactFinalizationIntent, finalizeLocalArtifact, prepareLocalArtifact, commitLocalArtifactProvenance,
|
|
298
|
+
export { LOCAL_ARTIFACT_REQUIRED_SECTIONS, consumeLocalArtifactFinalizationIntent, finalizeLocalArtifact, prepareLocalArtifact, commitLocalArtifactProvenance, readArtifactRecoveryIntent as readLocalArtifactFinalizationIntent, canonicalSemanticDigest as semanticDigest, sha256Content, validateLocalArtifact };
|
|
274
299
|
//# sourceMappingURL=local-artifact-finalization.js.map
|
|
@@ -28,7 +28,11 @@ function manualValidationReceiptDigest(receipt) {
|
|
|
28
28
|
}
|
|
29
29
|
function manualValidationFinalSummaryDigest(body) {
|
|
30
30
|
const heading = /^###\s+✅\s+(?:Manual Validation Passed|人工验证已通过)\s*$/mu.exec(body);
|
|
31
|
-
const
|
|
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;
|
|
32
36
|
const preimage = projection.replace(/receipt=(?:[a-f0-9]{64}|<receipt>)/gu, 'receipt=<receipt>');
|
|
33
37
|
return createHash('sha256').update(preimage, 'utf8').digest('hex');
|
|
34
38
|
}
|
|
@@ -7,7 +7,7 @@ import { validateCurrentTaskContract } from './current-contract.js';
|
|
|
7
7
|
import { parseReviewSummary } from './review-artifacts.js';
|
|
8
8
|
import { LEDGER_SECTION_MISSING_CODE, LEDGER_SECTION_MISSING_MESSAGE, parseLedgerDocument, summarizeLedgerStage, validateLedgerRows } from './ledger.js';
|
|
9
9
|
import { resolveTaskRef } from './resolve-ref.js';
|
|
10
|
-
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';
|
|
11
11
|
import { normalizeAgentClients } from '../agent-clients/config.js';
|
|
12
12
|
import { resolveAgentRuntimeStoreRoot } from '../runtime/agent-runtime.js';
|
|
13
13
|
import { getAgentClientCapability, getAgentClientDelegationEvidence, getAgentClientModelSelection } from '../agent-clients/registry.js';
|
|
@@ -21,6 +21,7 @@ import { resolveDeliveryTarget, resolveDiffBase, resolveTargetHead } from './del
|
|
|
21
21
|
import { buildLifecycleFacts, canStart, recommendNext } from './capabilities.js';
|
|
22
22
|
import { hasOpenLifecycleExecution } from './activity-log.js';
|
|
23
23
|
import { invalidationBlocks } from './invalidation.js';
|
|
24
|
+
import { normalizeAgentToken } from '../agent-clients/tokens.js';
|
|
24
25
|
function supportsLifecycleDelegation(client) {
|
|
25
26
|
return getAgentClientCapability(client, 'subagents').level !== 'unsupported'
|
|
26
27
|
&& getAgentClientCapability(client, 'orchestration').level !== 'unsupported'
|
|
@@ -1090,6 +1091,55 @@ function recoverPreparedOrchestrationDelegation(taskRef, options = {}) {
|
|
|
1090
1091
|
return failed('ORCHESTRATION_PREPARED_RECOVERY_FAILED', error instanceof Error ? error.message : String(error), resolved.taskId);
|
|
1091
1092
|
}
|
|
1092
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
|
+
}
|
|
1093
1143
|
function completeOrchestrationStage(taskRef, event, options = {}) {
|
|
1094
1144
|
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
1095
1145
|
if (!resolved.ok)
|
|
@@ -1204,5 +1254,5 @@ function pauseOrchestration(taskRef, code, message, recoverable, options = {}) {
|
|
|
1204
1254
|
saveRun(resolved.taskDir, updated);
|
|
1205
1255
|
return { status: 'paused', changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
|
|
1206
1256
|
}
|
|
1207
|
-
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, };
|
|
1208
1258
|
//# sourceMappingURL=orchestration.js.map
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { isLegacyCompatibilityEnabled, legacyCompatibilityError, VERSION } from '../version.js';
|
|
2
1
|
import { isResourceIdentity, parseResourceIdentity, resourceIdentityNumber, serializeResourceIdentity } from '../platform/resource-identity.js';
|
|
3
2
|
const PR_DELIVERY_FACT_KEY = 'pr_delivery_fact';
|
|
4
3
|
function factError(message) {
|
|
@@ -28,11 +27,6 @@ function timestamp(value, label) {
|
|
|
28
27
|
throw factError(`${label} must be a valid timestamp`);
|
|
29
28
|
return new Date(result).toISOString();
|
|
30
29
|
}
|
|
31
|
-
function positiveNumber(value, label) {
|
|
32
|
-
if (!Number.isSafeInteger(value) || value <= 0)
|
|
33
|
-
throw factError(`${label} must be a positive integer`);
|
|
34
|
-
return value;
|
|
35
|
-
}
|
|
36
30
|
function parseRef(value, label) {
|
|
37
31
|
if (!isRecord(value))
|
|
38
32
|
throw factError(`${label} must be an object`);
|
|
@@ -51,36 +45,17 @@ function canonicalIdentity(value) {
|
|
|
51
45
|
base: parseRef(value.base, 'identity.base')
|
|
52
46
|
};
|
|
53
47
|
}
|
|
54
|
-
function decodeLegacyIdentity(value) {
|
|
55
|
-
if (!isRecord(value))
|
|
56
|
-
throw factError('legacy identity must be an object');
|
|
57
|
-
// TODO(compat): Remove the v1 identity decoder before the first stable v1.0.0 release.
|
|
58
|
-
exactKeys(value, ['repository', 'number', 'nodeId', 'url', 'head', 'base'], 'legacy identity');
|
|
59
|
-
const legacy = {
|
|
60
|
-
number: positiveNumber(value.number, 'legacy identity.number'),
|
|
61
|
-
repository: text(value.repository, 'identity.repository'),
|
|
62
|
-
nodeId: text(value.nodeId, 'legacy identity.nodeId'),
|
|
63
|
-
url: text(value.url, 'identity.url'),
|
|
64
|
-
head: parseRef(value.head, 'identity.head'),
|
|
65
|
-
base: parseRef(value.base, 'identity.base')
|
|
66
|
-
};
|
|
67
|
-
return {
|
|
68
|
-
resource: { kind: 'number', value: legacy.number },
|
|
69
|
-
repository: legacy.repository,
|
|
70
|
-
url: legacy.url,
|
|
71
|
-
head: legacy.head,
|
|
72
|
-
base: legacy.base
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
48
|
function provenanceForSource(source) { return source === 'created' ? 'create-post' : source === 'reused' ? 'reuse' : source; }
|
|
76
|
-
function
|
|
49
|
+
function legacyFactError() {
|
|
50
|
+
return Object.assign(new Error('PLATFORM_IDENTITY_LEGACY_UNSUPPORTED: v1 PR delivery fact is unsupported; use the previous tool version to view this task or migrate its fact to version 2 before using this version'), { code: 'PLATFORM_IDENTITY_LEGACY_UNSUPPORTED' });
|
|
51
|
+
}
|
|
52
|
+
function parseFact(value) {
|
|
77
53
|
if (!isRecord(value))
|
|
78
54
|
throw factError('fact must be an object');
|
|
79
|
-
if (
|
|
55
|
+
if (value.version === 1)
|
|
56
|
+
throw legacyFactError();
|
|
57
|
+
if (value.version !== 2 || typeof value.state !== 'string')
|
|
80
58
|
throw factError('version or state is invalid');
|
|
81
|
-
if (value.version === 1 && !isLegacyCompatibilityEnabled(runtimeVersion)) {
|
|
82
|
-
throw legacyCompatibilityError('v1 PR delivery fact', runtimeVersion);
|
|
83
|
-
}
|
|
84
59
|
if (value.state === 'unbound') {
|
|
85
60
|
exactKeys(value, ['version', 'state', 'reason'], 'unbound fact');
|
|
86
61
|
if (value.reason !== 'initial')
|
|
@@ -95,25 +70,6 @@ function parseFact(value, runtimeVersion = VERSION) {
|
|
|
95
70
|
}
|
|
96
71
|
if (value.state !== 'bound')
|
|
97
72
|
throw factError('state is invalid');
|
|
98
|
-
if (value.version === 1) {
|
|
99
|
-
// TODO(compat): Remove this v1-to-v2 shape conversion before the first stable v1.0.0 release.
|
|
100
|
-
exactKeys(value, ['version', 'state', 'identity', 'binding', 'provenance'], 'legacy bound fact');
|
|
101
|
-
if (!isRecord(value.binding) || !isRecord(value.provenance))
|
|
102
|
-
throw factError('legacy binding or provenance is invalid');
|
|
103
|
-
exactKeys(value.binding, ['status', 'source', 'verifiedAt', 'issueNumber', 'remoteState', 'mergedAt', 'mergeCommitSha'], 'legacy binding');
|
|
104
|
-
const issueNumber = value.binding.issueNumber === null ? null : positiveNumber(value.binding.issueNumber, 'legacy binding.issueNumber');
|
|
105
|
-
return parseFact({
|
|
106
|
-
version: 2,
|
|
107
|
-
state: 'bound',
|
|
108
|
-
identity: decodeLegacyIdentity(value.identity),
|
|
109
|
-
binding: {
|
|
110
|
-
status: 'verified', source: value.binding.source, verifiedAt: timestamp(value.binding.verifiedAt, 'binding.verifiedAt'),
|
|
111
|
-
issueIdentity: issueNumber === null ? null : { kind: 'number', value: issueNumber }, remoteState: value.binding.remoteState,
|
|
112
|
-
mergedAt: value.binding.mergedAt === null ? null : timestamp(value.binding.mergedAt, 'binding.mergedAt'), mergeCommitSha: nullableText(value.binding.mergeCommitSha, 'binding.mergeCommitSha')
|
|
113
|
-
},
|
|
114
|
-
provenance: value.provenance
|
|
115
|
-
}, runtimeVersion);
|
|
116
|
-
}
|
|
117
73
|
exactKeys(value, ['version', 'state', 'identity', 'binding', 'provenance'], 'bound fact');
|
|
118
74
|
if (!isRecord(value.binding) || !isRecord(value.provenance))
|
|
119
75
|
throw factError('binding or provenance is invalid');
|
|
@@ -142,7 +98,7 @@ function parseFact(value, runtimeVersion = VERSION) {
|
|
|
142
98
|
provenance: { establishedBy: value.provenance.establishedBy }
|
|
143
99
|
};
|
|
144
100
|
}
|
|
145
|
-
function decodePrDeliveryFact(value,
|
|
101
|
+
function decodePrDeliveryFact(value, _runtimeVersion) {
|
|
146
102
|
if (typeof value === 'string') {
|
|
147
103
|
let decoded;
|
|
148
104
|
try {
|
|
@@ -151,16 +107,16 @@ function decodePrDeliveryFact(value, runtimeVersion = VERSION) {
|
|
|
151
107
|
catch {
|
|
152
108
|
throw factError('value is not valid JSON');
|
|
153
109
|
}
|
|
154
|
-
return parseFact(decoded
|
|
110
|
+
return parseFact(decoded);
|
|
155
111
|
}
|
|
156
|
-
return parseFact(value
|
|
112
|
+
return parseFact(value);
|
|
157
113
|
}
|
|
158
114
|
function encodePrDeliveryFact(fact) { return JSON.stringify(parseFact(fact)); }
|
|
159
|
-
function readPrDeliveryFact(metadata,
|
|
115
|
+
function readPrDeliveryFact(metadata, _runtimeVersion) {
|
|
160
116
|
if (!Object.hasOwn(metadata, PR_DELIVERY_FACT_KEY) || metadata[PR_DELIVERY_FACT_KEY] === '')
|
|
161
117
|
return { status: 'missing', fact: null };
|
|
162
118
|
try {
|
|
163
|
-
return { status: 'valid', fact: decodePrDeliveryFact(metadata[PR_DELIVERY_FACT_KEY]
|
|
119
|
+
return { status: 'valid', fact: decodePrDeliveryFact(metadata[PR_DELIVERY_FACT_KEY]) };
|
|
164
120
|
}
|
|
165
121
|
catch (error) {
|
|
166
122
|
return { status: 'invalid', fact: null, error: asFactError(error) };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
function warningText(value) {
|
|
2
|
+
return typeof value === 'string' && value.length > 0 && value.trim() === value && !/[\r\n]/u.test(value);
|
|
3
|
+
}
|
|
4
|
+
export function isRecoveryWarning(value) {
|
|
5
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
6
|
+
return false;
|
|
7
|
+
const warning = value;
|
|
8
|
+
return Object.keys(warning).sort().join(',') === 'action,code,message'
|
|
9
|
+
&& warningText(warning.code)
|
|
10
|
+
&& warningText(warning.message)
|
|
11
|
+
&& warningText(warning.action);
|
|
12
|
+
}
|
|
13
|
+
export function sameRecoveryWarning(left, right) {
|
|
14
|
+
return isRecoveryWarning(left) && isRecoveryWarning(right)
|
|
15
|
+
&& left.code === right.code
|
|
16
|
+
&& left.message === right.message
|
|
17
|
+
&& left.action === right.action;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=recovery-warning.js.map
|
|
@@ -4,6 +4,7 @@ export function parseReviewCommand(args) {
|
|
|
4
4
|
const values = {};
|
|
5
5
|
const flags = {
|
|
6
6
|
'--stage': 'stage', '--artifact': 'artifact', '--orchestrated': 'orchestrated', '--dry-run': 'dryRun',
|
|
7
|
+
'--recovery-id': 'recoveryId',
|
|
7
8
|
'--override-ticket': 'overrideTicket', '--override-target': 'overrideTarget', '--override-scope': 'overrideScope'
|
|
8
9
|
};
|
|
9
10
|
for (let index = 2; index < args.length; index += 1) {
|