@fitlab-ai/agent-infra 0.11.3 → 0.11.5
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 +17 -8
- package/README.zh-CN.md +17 -8
- package/dist/bin/internal-cli.js +1 -6
- package/dist/lib/decide.js +4 -41
- package/dist/lib/git/branch-worktree.js +45 -0
- package/dist/lib/internal/cli-route-inventory.js +1 -4
- package/dist/lib/internal/task-activity.js +7 -40
- package/dist/lib/internal/task-artifact.js +1 -1
- package/dist/lib/internal/task-event.js +4 -37
- package/dist/lib/internal/task-finalization.js +48 -1
- package/dist/lib/internal/task-ledger.js +14 -44
- package/dist/lib/internal/task-lifecycle.js +1 -1
- package/dist/lib/internal/task-operation-registry.js +2 -4
- package/dist/lib/internal/task-review.js +5 -37
- package/dist/lib/internal/task-verify.js +3 -47
- package/dist/lib/internal/task-warning.js +4 -40
- package/dist/lib/platform/completion-backfill.js +63 -17
- package/dist/lib/platform/pr-summary.js +4 -23
- package/dist/lib/platform/verification-sync.js +12 -49
- package/dist/lib/sandbox/control/client.js +4 -1
- package/dist/lib/sandbox/control/executor.js +12 -4
- package/dist/lib/sandbox/control/protocol.js +2 -1
- package/dist/lib/sandbox/control/server.js +26 -143
- package/dist/lib/sandbox/removal.js +15 -8
- package/dist/lib/sandbox/task-cleanup.js +13 -20
- package/dist/lib/task/activity-intent.js +1 -2
- package/dist/lib/task/artifact-command.js +11 -4
- package/dist/lib/task/artifact-lifecycle.js +217 -85
- package/dist/lib/task/artifact-operations.js +37 -28
- package/dist/lib/task/artifact-receipts.js +4 -1
- package/dist/lib/task/artifact-schema.js +8 -7
- package/dist/lib/task/artifact-selection.js +25 -0
- package/dist/lib/task/capabilities.js +158 -39
- package/dist/lib/task/commands/status.js +9 -2
- package/dist/lib/task/control-authority.js +12 -168
- package/dist/lib/task/decision-intents.js +14 -4
- package/dist/lib/task/delivery.js +1 -43
- package/dist/lib/task/events.js +68 -73
- package/dist/lib/task/finalization-handoff.js +92 -0
- package/dist/lib/task/finalization.js +423 -154
- package/dist/lib/task/intermediate-cleanup.js +9 -148
- package/dist/lib/task/ledger-intents.js +159 -20
- package/dist/lib/task/lifecycle-path.js +75 -0
- package/dist/lib/task/lifecycle.js +60 -11
- package/dist/lib/task/local-artifact-finalization.js +13 -47
- package/dist/lib/task/orchestration.js +17 -7
- package/dist/lib/task/review-command.js +1 -2
- package/dist/lib/task/review-finalization.js +4 -18
- package/dist/lib/task/rework-intent.js +70 -4
- package/dist/lib/task/snapshot.js +6 -6
- package/dist/lib/task/verification-engine.js +25 -30
- package/dist/lib/task/verification.js +2 -13
- package/dist/lib/task/workflow-warning-intents.js +23 -10
- package/dist/lib/task/workspace-snapshot.js +7 -2
- package/dist/lib/task/write.js +1 -2
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/decide.ts +5 -35
- package/lib/git/branch-worktree.ts +41 -0
- package/lib/internal/cli-route-inventory.ts +1 -3
- package/lib/internal/task-activity.ts +6 -34
- package/lib/internal/task-artifact.ts +1 -1
- package/lib/internal/task-event.ts +4 -37
- package/lib/internal/task-finalization.ts +47 -1
- package/lib/internal/task-ledger.ts +15 -41
- package/lib/internal/task-lifecycle.ts +2 -2
- package/lib/internal/task-operation-registry.ts +2 -4
- package/lib/internal/task-review.ts +5 -31
- package/lib/internal/task-verify.ts +3 -39
- package/lib/internal/task-warning.ts +3 -32
- package/lib/platform/completion-backfill.ts +78 -16
- package/lib/platform/pr-summary.ts +4 -25
- package/lib/platform/verification-sync.ts +13 -53
- package/lib/sandbox/control/client.ts +5 -1
- package/lib/sandbox/control/executor.ts +13 -4
- package/lib/sandbox/control/protocol.ts +3 -2
- package/lib/sandbox/control/server.ts +21 -146
- package/lib/sandbox/removal.ts +23 -7
- package/lib/sandbox/task-cleanup.ts +12 -20
- package/lib/task/activity-intent.ts +1 -2
- package/lib/task/artifact-command.ts +14 -4
- package/lib/task/artifact-lifecycle.ts +236 -85
- package/lib/task/artifact-operations.ts +59 -28
- package/lib/task/artifact-receipts.ts +4 -1
- package/lib/task/artifact-schema.ts +10 -7
- package/lib/task/artifact-selection.ts +53 -0
- package/lib/task/capabilities.ts +154 -40
- package/lib/task/commands/status.ts +11 -2
- package/lib/task/control-authority.ts +17 -189
- package/lib/task/decision-intents.ts +11 -4
- package/lib/task/delivery.ts +1 -40
- package/lib/task/events.ts +73 -74
- package/lib/task/finalization-handoff.ts +104 -0
- package/lib/task/finalization.ts +388 -122
- package/lib/task/intermediate-cleanup.ts +9 -125
- package/lib/task/ledger-intents.ts +157 -20
- package/lib/task/lifecycle-path.ts +98 -0
- package/lib/task/lifecycle.ts +58 -13
- package/lib/task/local-artifact-finalization.ts +14 -56
- package/lib/task/orchestration.ts +26 -8
- package/lib/task/review-command.ts +2 -5
- package/lib/task/review-finalization.ts +4 -20
- package/lib/task/rework-intent.ts +75 -5
- package/lib/task/snapshot.ts +6 -6
- package/lib/task/verification-engine.ts +29 -30
- package/lib/task/verification.ts +3 -16
- package/lib/task/workflow-warning-intents.ts +20 -10
- package/lib/task/workspace-snapshot.ts +3 -2
- package/lib/task/write.ts +1 -4
- package/package.json +1 -1
- package/templates/.agents/rules/review-handshake.en.md +4 -4
- package/templates/.agents/rules/review-handshake.zh-CN.md +4 -4
- package/templates/.agents/skills/analyze-task/SKILL.en.md +21 -5
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +21 -5
- package/templates/.agents/skills/code-task/SKILL.en.md +14 -8
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +18 -13
- package/templates/.agents/skills/code-task/reference/code-rules.en.md +2 -2
- package/templates/.agents/skills/code-task/reference/code-rules.zh-CN.md +2 -2
- package/templates/.agents/skills/code-task/reference/dual-mode.en.md +4 -4
- package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +4 -4
- package/templates/.agents/skills/code-task/reference/report-template.en.md +7 -7
- package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +7 -7
- package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +2 -2
- package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +2 -2
- package/templates/.agents/skills/complete-manual-validation/reference/report-template.en.md +1 -3
- package/templates/.agents/skills/complete-manual-validation/reference/report-template.zh-CN.md +1 -3
- 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/plan-task/SKILL.en.md +13 -10
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +13 -10
- package/templates/.agents/skills/review-analysis/SKILL.en.md +5 -1
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +5 -1
- package/templates/.agents/skills/review-analysis/reference/report-template.en.md +3 -3
- package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +3 -3
- package/templates/.agents/skills/review-code/SKILL.en.md +5 -1
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +5 -1
- package/templates/.agents/skills/review-code/reference/report-template.en.md +3 -3
- package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +3 -3
- package/templates/.agents/skills/review-plan/SKILL.en.md +5 -1
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +5 -1
- package/templates/.agents/skills/review-plan/reference/report-template.en.md +3 -3
- package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +3 -3
- package/templates/.agents/skills/review-pr/config/verify.en.json +3 -1
- package/templates/.agents/skills/review-pr/config/verify.zh-CN.json +3 -1
- package/templates/.agents/skills/review-pr/reference/report-template.en.md +3 -3
- package/templates/.agents/skills/review-pr/reference/report-template.zh-CN.md +3 -3
- package/templates/.agents/skills/run-manual-validation/config/verify.en.json +2 -2
- package/templates/.agents/skills/run-manual-validation/config/verify.zh-CN.json +2 -2
- package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +2 -2
- package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +2 -2
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +18 -2
- package/templates/.agents/templates/task.en.md +2 -2
- package/templates/.agents/templates/task.zh-CN.md +2 -2
- package/templates/.agents/workflows/bug-fix.en.yaml +11 -6
- package/templates/.agents/workflows/bug-fix.zh-CN.yaml +11 -6
- package/templates/.agents/workflows/feature-development.en.yaml +11 -6
- package/templates/.agents/workflows/feature-development.zh-CN.yaml +11 -6
- package/templates/.agents/workflows/refactoring.en.yaml +11 -6
- package/templates/.agents/workflows/refactoring.zh-CN.yaml +11 -6
- package/dist/lib/internal/task-override.js +0 -138
- package/dist/lib/sandbox/control/finalization-response.js +0 -33
- package/dist/lib/task/artifact-recovery.js +0 -476
- package/dist/lib/task/artifact-repair-intent.js +0 -181
- package/dist/lib/task/guard-override.js +0 -31
- package/dist/lib/task/human-override.js +0 -1259
- package/lib/internal/task-override.ts +0 -144
- package/lib/sandbox/control/finalization-response.ts +0 -35
- package/lib/task/artifact-recovery.ts +0 -572
- package/lib/task/artifact-repair-intent.ts +0 -227
- package/lib/task/guard-override.ts +0 -50
- package/lib/task/human-override.ts +0 -1522
|
@@ -3,8 +3,7 @@ import { spawnSync } from 'node:child_process';
|
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { parseTaskFrontmatter } from './frontmatter.js';
|
|
5
5
|
import { resolveTaskRef } from './resolve-ref.js';
|
|
6
|
-
import { resolveDeliveryTarget,
|
|
7
|
-
import { extractReviewBaseline, extractReviewDiffBase, extractReviewTargetHead, extractReviewedHead, findAuthoritativeReviewCodeArtifact } from './review-fingerprint.js';
|
|
6
|
+
import { resolveDeliveryTarget, validateBaseRef, validateRemote } from './delivery-target.js';
|
|
8
7
|
import { captureTaskWriteMetadata, writeTask } from './write.js';
|
|
9
8
|
import { withRepositoryMutationLock, withTaskExecutionLock } from './task-execution-lock.js';
|
|
10
9
|
function classifyDeliveryState(localHead, remoteHead, lastDelivered) {
|
|
@@ -27,41 +26,6 @@ function gitValue(cwd, args) {
|
|
|
27
26
|
function validateBranch(cwd, branch) {
|
|
28
27
|
return git(cwd, ['check-ref-format', `refs/heads/${branch}`]).status === 0;
|
|
29
28
|
}
|
|
30
|
-
function validateReviewTargetCompatibility(repoRoot, taskDir, target, reviewedHead) {
|
|
31
|
-
const artifact = findAuthoritativeReviewCodeArtifact(taskDir);
|
|
32
|
-
if (!artifact.ok || !artifact.path)
|
|
33
|
-
return { ok: false, code: 'DELIVERY_REVIEW_REQUIRED', message: 'A current review-code artifact is required before delivery' };
|
|
34
|
-
let content;
|
|
35
|
-
try {
|
|
36
|
-
content = fs.readFileSync(artifact.path, 'utf8');
|
|
37
|
-
}
|
|
38
|
-
catch (error) {
|
|
39
|
-
return { ok: false, code: 'DELIVERY_REVIEW_REQUIRED', message: error instanceof Error ? error.message : String(error) };
|
|
40
|
-
}
|
|
41
|
-
const savedReviewedHead = extractReviewedHead(content) || extractReviewBaseline(content);
|
|
42
|
-
const savedTargetHead = extractReviewTargetHead(content);
|
|
43
|
-
const savedDiffBase = extractReviewDiffBase(content);
|
|
44
|
-
if (!savedReviewedHead || !savedTargetHead || !savedDiffBase) {
|
|
45
|
-
return { ok: false, code: 'DELIVERY_REVIEW_TARGET_INVALID', message: 'review-code must save reviewed head, target head, and diff base before delivery' };
|
|
46
|
-
}
|
|
47
|
-
if (savedReviewedHead !== reviewedHead) {
|
|
48
|
-
return { ok: false, code: 'DELIVERY_REVIEW_REQUIRED', message: 'review-code reviewed head does not match last_reviewed_commit' };
|
|
49
|
-
}
|
|
50
|
-
const saved = resolveDiffBase(repoRoot, reviewedHead, savedTargetHead);
|
|
51
|
-
if (!saved.ok || saved.diffBase !== savedDiffBase) {
|
|
52
|
-
return { ok: false, code: 'DELIVERY_REVIEW_TARGET_INVALID', message: saved.ok ? 'saved review diff base does not match saved target head' : saved.message };
|
|
53
|
-
}
|
|
54
|
-
const currentTarget = resolveTargetHead(repoRoot, target);
|
|
55
|
-
if (!currentTarget.ok)
|
|
56
|
-
return { ok: false, code: currentTarget.code, message: currentTarget.message };
|
|
57
|
-
const current = resolveDiffBase(repoRoot, reviewedHead, currentTarget.head);
|
|
58
|
-
if (!current.ok)
|
|
59
|
-
return { ok: false, code: current.code, message: current.message };
|
|
60
|
-
if (current.diffBase !== savedDiffBase) {
|
|
61
|
-
return { ok: false, code: 'DELIVERY_REVIEW_TARGET_CHANGED', message: 'current delivery target changed the reviewed diff base; re-run review-code' };
|
|
62
|
-
}
|
|
63
|
-
return { ok: true };
|
|
64
|
-
}
|
|
65
29
|
function failure(taskId, code, message, retryable = false, status = retryable ? 'blocked' : 'failed') {
|
|
66
30
|
return { status, changed: false, taskId, remoteHead: null, localHead: null, state: null, error: { code, message, retryable } };
|
|
67
31
|
}
|
|
@@ -102,12 +66,6 @@ function deliverUnlocked(taskId, taskDir, repoRoot, options) {
|
|
|
102
66
|
return failure(taskId, 'DELIVERY_GIT_INSPECT_FAILED', status.stderr.trim() || 'Unable to inspect Git state');
|
|
103
67
|
if (status.stdout.trim())
|
|
104
68
|
return failure(taskId, 'DELIVERY_WORKTREE_DIRTY', 'Working tree must be clean before branch delivery');
|
|
105
|
-
const reviewed = frontmatter.last_reviewed_commit ?? '';
|
|
106
|
-
if (reviewed !== localHead)
|
|
107
|
-
return failure(taskId, 'DELIVERY_REVIEW_REQUIRED', 'Local HEAD must equal last_reviewed_commit before delivery');
|
|
108
|
-
const reviewTarget = validateReviewTargetCompatibility(repoRoot, taskDir, target.value, reviewed);
|
|
109
|
-
if (!reviewTarget.ok)
|
|
110
|
-
return failure(taskId, reviewTarget.code, reviewTarget.message, reviewTarget.code.includes('UNAVAILABLE'), reviewTarget.code.includes('UNAVAILABLE') ? 'blocked' : 'failed');
|
|
111
69
|
const ref = `refs/heads/${branch}`;
|
|
112
70
|
const remoteOutput = git(repoRoot, ['ls-remote', '--refs', target.value.remote, ref]);
|
|
113
71
|
if (remoteOutput.status !== 0)
|
package/dist/lib/task/events.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { execFileSync } from 'node:child_process';
|
|
4
|
+
import { resolveBranchWorktree } from '../git/branch-worktree.js';
|
|
4
5
|
import { appendActivityEntry, locateActivityLog, pairEntries, startedBackedRows } from './activity-log.js';
|
|
5
6
|
import { buildArtifactLinkSection, inspectArtifactDirectory, resolveArtifactContext, validateCompletedArtifact } from './artifact-lifecycle.js';
|
|
6
7
|
import { artifactName, parseArtifactName } from './artifact-name.js';
|
|
@@ -16,17 +17,17 @@ import { validateLifecycleExecution } from './lifecycle-execution.js';
|
|
|
16
17
|
import { commitOrchestrationStageCompletion } from './orchestration.js';
|
|
17
18
|
import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.js';
|
|
18
19
|
import { captureTaskWriteMetadata, writeTask } from './write.js';
|
|
19
|
-
import { allowsManualOverride } from './guard-override.js';
|
|
20
20
|
import { validateLocalArtifact } from './local-artifact-finalization.js';
|
|
21
|
-
import { buildLifecycleFacts, canStart } from './capabilities.js';
|
|
21
|
+
import { buildLifecycleFacts, canStart, effectiveReworkTarget } from './capabilities.js';
|
|
22
22
|
import { createInvalidationOperation, invalidationMutation, parseInvalidationDocument, targetIdFor, upsertInvalidation } from './invalidation.js';
|
|
23
23
|
import { reconcileTaskInvalidation } from './invalidation-command.js';
|
|
24
24
|
import { consumeReworkIntents, parseReworkIntentDocument, reworkIntentMutation, supersedeReworkIntents } from './rework-intent.js';
|
|
25
|
-
import { ARTIFACT_FAMILIES,
|
|
25
|
+
import { ARTIFACT_FAMILIES, parseQualificationAudit, parseTaskQualification, upstreamArtifactDigest } from './qualification-audit.js';
|
|
26
26
|
import { getArtifactSchema } from './artifact-schema.js';
|
|
27
27
|
import { canonicalSemanticDigest, inspectArtifactContract } from './artifact-operations.js';
|
|
28
28
|
import { consumeLifecycleRecoveryAttestation, lifecycleRecoveryAttestationDigest, validateLifecycleRecoveryAttestation } from './control-authority.js';
|
|
29
29
|
import { readManualValidationCompletion } from './manual-validation-completion.js';
|
|
30
|
+
import { parseLifecyclePathDecision } from './lifecycle-path.js';
|
|
30
31
|
const eventCatalog = [
|
|
31
32
|
'analyze.started', 'analyze.awaiting-input', 'analyze.completed',
|
|
32
33
|
'review-analysis.started', 'review-analysis.completed',
|
|
@@ -37,28 +38,36 @@ const eventCatalog = [
|
|
|
37
38
|
'manual-validation.started', 'manual-validation.completed',
|
|
38
39
|
'validation-run.started', 'validation-run.completed'
|
|
39
40
|
];
|
|
40
|
-
function
|
|
41
|
+
function branchWorktree(repoRoot, branch) {
|
|
42
|
+
if (!branch)
|
|
43
|
+
return repoRoot;
|
|
44
|
+
return resolveBranchWorktree(repoRoot, branch);
|
|
45
|
+
}
|
|
46
|
+
function approvedCleanReviewedCommit(reviewContent, verdict, repoRoot, branch) {
|
|
41
47
|
if (verdict !== 'approved')
|
|
42
48
|
return null;
|
|
43
49
|
const reviewedHead = extractReviewedHead(reviewContent) || extractReviewBaseline(reviewContent);
|
|
44
50
|
const reviewedTree = extractReviewedSnapshotTree(reviewContent);
|
|
45
51
|
if (!/^[a-f0-9]{40}$/.test(reviewedHead) || !/^[a-f0-9]{40}$/.test(reviewedTree))
|
|
46
52
|
return null;
|
|
53
|
+
const worktree = branchWorktree(repoRoot, branch);
|
|
54
|
+
if (!worktree)
|
|
55
|
+
return null;
|
|
47
56
|
try {
|
|
48
|
-
const currentHead = execFileSync('git', ['-C',
|
|
57
|
+
const currentHead = execFileSync('git', ['-C', worktree, 'rev-parse', 'HEAD'], { encoding: 'utf8' }).trim();
|
|
49
58
|
if (currentHead !== reviewedHead)
|
|
50
59
|
return null;
|
|
51
|
-
const currentTree = execFileSync('git', ['-C',
|
|
60
|
+
const currentTree = execFileSync('git', ['-C', worktree, 'rev-parse', 'HEAD^{tree}'], { encoding: 'utf8' }).trim();
|
|
52
61
|
if (currentTree !== reviewedTree)
|
|
53
62
|
return null;
|
|
54
|
-
execFileSync('git', ['-C',
|
|
63
|
+
execFileSync('git', ['-C', worktree, 'diff-index', '--quiet', 'HEAD', '--']);
|
|
55
64
|
return reviewedHead;
|
|
56
65
|
}
|
|
57
66
|
catch {
|
|
58
67
|
return null;
|
|
59
68
|
}
|
|
60
69
|
}
|
|
61
|
-
const BASE_FIELDS = new Set(['taskRef', 'event', 'agent', 'dryRun', '
|
|
70
|
+
const BASE_FIELDS = new Set(['taskRef', 'event', 'agent', 'dryRun', 'initiator', 'requestId', 'reasonCode', 'sourceFinding', 'sourceArtifact', 'sourceSha256']);
|
|
62
71
|
const SCHEMAS = {
|
|
63
72
|
'analyze.started': { optional: ['round'] },
|
|
64
73
|
'analyze.awaiting-input': { required: ['question'] },
|
|
@@ -283,7 +292,13 @@ function failed(request, error, extra = {}) {
|
|
|
283
292
|
}
|
|
284
293
|
function normalizeStarted(request, repoRoot) {
|
|
285
294
|
const family = eventParts(request.event).family;
|
|
286
|
-
const context = resolveArtifactContext(request.taskRef, FAMILY[family].artifact, {
|
|
295
|
+
const context = resolveArtifactContext(request.taskRef, FAMILY[family].artifact, {
|
|
296
|
+
repoRoot,
|
|
297
|
+
reasonCode: request.reasonCode,
|
|
298
|
+
sourceFinding: request.sourceFinding,
|
|
299
|
+
sourceArtifact: request.sourceArtifact,
|
|
300
|
+
sourceSha256: request.sourceSha256
|
|
301
|
+
});
|
|
287
302
|
if (context.status !== 'ready' || !context.next) {
|
|
288
303
|
return { error: { code: context.error?.code ?? 'EVENT_ARTIFACT_CONFLICT', message: context.error?.message ?? context.codeMode?.message ?? 'artifact context is not writable' }, context };
|
|
289
304
|
}
|
|
@@ -337,24 +352,6 @@ function openStartedIdentity(rows, family) {
|
|
|
337
352
|
function reviewInputFamily(family) {
|
|
338
353
|
return family === 'review-analysis' ? 'analysis' : family === 'review-plan' ? 'plan' : 'code';
|
|
339
354
|
}
|
|
340
|
-
function relationForStartedInput(family, input) {
|
|
341
|
-
if (family.startsWith('review-')) {
|
|
342
|
-
return input.family === reviewInputFamily(family) ? 'reviewed-input' : 'approval-context';
|
|
343
|
-
}
|
|
344
|
-
const consumer = family === 'analyze' ? 'analysis' : family;
|
|
345
|
-
if ((consumer === 'plan' && input.family === 'analysis') || (consumer === 'code' && input.family === 'plan'))
|
|
346
|
-
return 'required-input';
|
|
347
|
-
return 'review-context';
|
|
348
|
-
}
|
|
349
|
-
function qualificationRelationsForStarted(family, inputs) {
|
|
350
|
-
return inputs.map((input) => ({
|
|
351
|
-
upstreamFamily: input.family,
|
|
352
|
-
upstreamArtifact: input.name,
|
|
353
|
-
upstreamRound: input.round,
|
|
354
|
-
upstreamSha256: sha256File(input.path),
|
|
355
|
-
relation: relationForStartedInput(family, input)
|
|
356
|
-
}));
|
|
357
|
-
}
|
|
358
355
|
function lifecycleAction(family) {
|
|
359
356
|
return family === 'analyze' ? 'analysis' : family;
|
|
360
357
|
}
|
|
@@ -555,7 +552,21 @@ function reworkIntentMutationForCompletion(content, taskDir, family, artifact, t
|
|
|
555
552
|
return [];
|
|
556
553
|
}
|
|
557
554
|
}));
|
|
558
|
-
|
|
555
|
+
const action = family === 'analyze' ? 'analysis' : family;
|
|
556
|
+
let target = action;
|
|
557
|
+
if (family === 'analyze') {
|
|
558
|
+
const previousAnalysis = fs.readdirSync(taskDir)
|
|
559
|
+
.map((name) => parseArtifactName(name))
|
|
560
|
+
.filter((identity) => identity?.family === 'analysis' && identity.name !== artifact.name)
|
|
561
|
+
.sort((left, right) => right.round - left.round || left.name.localeCompare(right.name))[0];
|
|
562
|
+
const pathState = previousAnalysis
|
|
563
|
+
? parseLifecyclePathDecision(fs.readFileSync(path.join(taskDir, previousAnalysis.name), 'utf8'))
|
|
564
|
+
: undefined;
|
|
565
|
+
const pending = next.find((intent) => intent.status === 'pending');
|
|
566
|
+
if (pending && effectiveReworkTarget(pending.target, pathState) === action)
|
|
567
|
+
target = pending.target;
|
|
568
|
+
}
|
|
569
|
+
next = consumeReworkIntents(next, target, hashes, timestamp).intents;
|
|
559
570
|
}
|
|
560
571
|
if (family === 'review-analysis' || family === 'review-plan' || family === 'review-code') {
|
|
561
572
|
next = supersedeReworkIntents(next, artifact.name, hash, timestamp).intents;
|
|
@@ -577,13 +588,16 @@ function buildCompletionReceipt(content, taskDir, family, artifact, completedAt,
|
|
|
577
588
|
const startedSha256 = typeof frontmatter.code_input_sha256 === 'string'
|
|
578
589
|
? frontmatter.code_input_sha256 : existing?.inputSha256 ?? '';
|
|
579
590
|
if (!startedInput || !startedSha256)
|
|
580
|
-
return { ok: false, message: 'code.started
|
|
581
|
-
const
|
|
582
|
-
if (
|
|
583
|
-
return { ok: false, message: `code input '${startedInput}'
|
|
591
|
+
return { ok: false, message: 'code.started lifecycle input context is missing' };
|
|
592
|
+
const inputIdentity = parseArtifactName(startedInput);
|
|
593
|
+
if (!inputIdentity || !['analysis', 'plan'].includes(inputIdentity.family))
|
|
594
|
+
return { ok: false, message: `code input '${startedInput}' has an invalid family` };
|
|
595
|
+
const lifecycleInput = inspectArtifactDirectory(taskDir, inputIdentity.family);
|
|
596
|
+
if (lifecycleInput.status !== 'ready' || !lifecycleInput.latest || lifecycleInput.latest.name !== startedInput) {
|
|
597
|
+
return { ok: false, message: `code input '${startedInput}' is not the latest ${inputIdentity.family} artifact` };
|
|
584
598
|
}
|
|
585
599
|
try {
|
|
586
|
-
const inputSha256 = sha256File(
|
|
600
|
+
const inputSha256 = sha256File(lifecycleInput.latest.path);
|
|
587
601
|
if (inputSha256 !== startedSha256)
|
|
588
602
|
return { ok: false, message: `code input ${startedInput} changed after code.started` };
|
|
589
603
|
return {
|
|
@@ -595,7 +609,7 @@ function buildCompletionReceipt(content, taskDir, family, artifact, completedAt,
|
|
|
595
609
|
};
|
|
596
610
|
}
|
|
597
611
|
catch (error) {
|
|
598
|
-
return { ok: false, message: `cannot hash code input
|
|
612
|
+
return { ok: false, message: `cannot hash code lifecycle input: ${error instanceof Error ? error.message : String(error)}` };
|
|
599
613
|
}
|
|
600
614
|
}
|
|
601
615
|
if (!family.startsWith('review-'))
|
|
@@ -691,8 +705,7 @@ function applyTaskEventUnlocked(request, options = {}) {
|
|
|
691
705
|
const resolved = resolveTaskRef(request.taskRef, { repoRoot: options.repoRoot });
|
|
692
706
|
if (!resolved.ok)
|
|
693
707
|
return failed(request, { code: resolved.code, message: resolved.message }, { taskId: resolved.taskId });
|
|
694
|
-
|
|
695
|
-
if (resolved.state !== 'active' && !stateOverride)
|
|
708
|
+
if (resolved.state !== 'active')
|
|
696
709
|
return failed(request, { code: 'TASK_STATE_MISMATCH', message: `task ${resolved.taskId} is ${resolved.state}, expected active` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
|
|
697
710
|
let content;
|
|
698
711
|
try {
|
|
@@ -753,6 +766,12 @@ function applyTaskEventUnlocked(request, options = {}) {
|
|
|
753
766
|
normalized = { ...request, round: openIdentity.round, artifact: artifactName(FAMILY[initialParts.family].artifact, openIdentity.round), fixFor: openIdentity.fixFor, implementationInput: openIdentity.implementationInput };
|
|
754
767
|
return successNoOp(normalized, resolved.taskId, resolved.taskMdPath, typeof frontmatter.current_step === 'string' ? frontmatter.current_step : '', identity(normalized), openIdentity.row.started, frontmatter, null);
|
|
755
768
|
}
|
|
769
|
+
if (rows.some((item) => item.started && !item.done)) {
|
|
770
|
+
return failed(request, {
|
|
771
|
+
code: 'EVENT_TRANSITION_INVALID',
|
|
772
|
+
message: 'EXECUTION_BUSY: another lifecycle execution is open'
|
|
773
|
+
}, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
|
|
774
|
+
}
|
|
756
775
|
const result = normalizeStarted(request, resolved.repoRoot);
|
|
757
776
|
artifactContext = result.context;
|
|
758
777
|
if ('error' in result)
|
|
@@ -805,7 +824,7 @@ function applyTaskEventUnlocked(request, options = {}) {
|
|
|
805
824
|
}
|
|
806
825
|
return successNoOp(normalized, resolved.taskId, resolved.taskMdPath, currentStep, eventIdentity, row.started, frontmatter, artifactContext);
|
|
807
826
|
}
|
|
808
|
-
if (eventIdentity.phase === 'completed' && !row?.started && completedRows.length === 0
|
|
827
|
+
if (eventIdentity.phase === 'completed' && !row?.started && completedRows.length === 0)
|
|
809
828
|
return failed(normalized, { code: 'EVENT_START_MISSING', message: 'completion requires a started event or an earlier completed attempt' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
|
|
810
829
|
if (eventIdentity.phase === 'completed') {
|
|
811
830
|
const validated = validateCompletedArtifact(resolved.taskDir, FAMILY[eventIdentity.family].artifact, normalized.artifact, normalized.round);
|
|
@@ -822,25 +841,13 @@ function applyTaskEventUnlocked(request, options = {}) {
|
|
|
822
841
|
reviewContent = fs.readFileSync(completedArtifact.path, 'utf8');
|
|
823
842
|
}
|
|
824
843
|
catch (error) {
|
|
825
|
-
return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: `cannot read
|
|
844
|
+
return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: `cannot read ${completedArtifact.name}: ${error instanceof Error ? error.message : String(error)}` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
|
|
826
845
|
}
|
|
827
846
|
const schema = getArtifactSchema(eventIdentity.family);
|
|
828
847
|
const structure = schema ? inspectArtifactContract(reviewContent, schema) : null;
|
|
829
848
|
if (structure && !structure.ok) {
|
|
830
849
|
return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: `shared artifact structure invalid: ${structure.diagnostics.map((item) => `${item.code}: ${item.message}`).join('; ')}` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
|
|
831
850
|
}
|
|
832
|
-
const expected = expectedQualificationRelations(content, eventIdentity.family);
|
|
833
|
-
if (!expected.ok) {
|
|
834
|
-
return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: `${expected.code}: ${expected.message}` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
|
|
835
|
-
}
|
|
836
|
-
const qualification = validateQualificationAudit(content, reviewContent, {
|
|
837
|
-
family: eventIdentity.family,
|
|
838
|
-
artifact: completedArtifact.name,
|
|
839
|
-
expectedUpstreamRelations: expected.relations
|
|
840
|
-
});
|
|
841
|
-
if (!qualification.ok) {
|
|
842
|
-
return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: `${qualification.code}: ${qualification.message}` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
|
|
843
|
-
}
|
|
844
851
|
}
|
|
845
852
|
if (eventIdentity.family === 'analyze' || eventIdentity.family === 'plan' || eventIdentity.family === 'code') {
|
|
846
853
|
const localFamily = eventIdentity.family === 'analyze'
|
|
@@ -854,9 +861,7 @@ function applyTaskEventUnlocked(request, options = {}) {
|
|
|
854
861
|
return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: `cannot read ${completedArtifact.name}: ${String(error)}` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
|
|
855
862
|
}
|
|
856
863
|
const local = validateLocalArtifact(artifactContent, {
|
|
857
|
-
family: localFamily
|
|
858
|
-
taskContent: content,
|
|
859
|
-
artifact: completedArtifact.name
|
|
864
|
+
family: localFamily
|
|
860
865
|
});
|
|
861
866
|
if (!local.ok) {
|
|
862
867
|
return failed(normalized, {
|
|
@@ -889,10 +894,9 @@ function applyTaskEventUnlocked(request, options = {}) {
|
|
|
889
894
|
if (!facts.ok)
|
|
890
895
|
return failed(normalized, { code: 'EVENT_TRANSITION_INVALID', message: `${facts.code}: ${facts.message}` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
|
|
891
896
|
const capability = canStart(lifecycleAction(eventIdentity.family), facts.facts, trigger);
|
|
892
|
-
const
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
if (!capability.allowed && !capabilityOverride) {
|
|
897
|
+
const safetyFailure = capability.reasonCode === 'TASK_NOT_ACTIVE'
|
|
898
|
+
|| capability.reasonCode === 'LIFECYCLE_EXECUTION_OPEN';
|
|
899
|
+
if (!capability.allowed && (normalized.initiator === 'orchestrator' || safetyFailure)) {
|
|
896
900
|
const code = capability.reasonCode === 'INVALIDATION_INCOMPLETE' ? 'TASK_INVALIDATION_BLOCKED' : 'EVENT_TRANSITION_INVALID';
|
|
897
901
|
return failed(normalized, { code, message: `${capability.reasonCode}: ${capability.evidence.join(', ')}` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
|
|
898
902
|
}
|
|
@@ -901,10 +905,11 @@ function applyTaskEventUnlocked(request, options = {}) {
|
|
|
901
905
|
if (findingCountError)
|
|
902
906
|
return failed(normalized, findingCountError, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
|
|
903
907
|
let orchestrationCompletion = null;
|
|
904
|
-
if (eventIdentity.phase === 'completed' &&
|
|
908
|
+
if (eventIdentity.phase === 'completed' && normalized.orchestrated
|
|
909
|
+
&& eventIdentity.family !== 'manual-validation' && eventIdentity.family !== 'validation-run') {
|
|
905
910
|
const orchestrationStage = eventIdentity.family === 'analyze' ? 'analysis' : eventIdentity.family;
|
|
906
911
|
const execution = validateLifecycleExecution(normalized.taskRef, {
|
|
907
|
-
mode:
|
|
912
|
+
mode: 'orchestrated',
|
|
908
913
|
identity: {
|
|
909
914
|
stage: orchestrationStage,
|
|
910
915
|
round: normalized.round,
|
|
@@ -969,18 +974,10 @@ function applyTaskEventUnlocked(request, options = {}) {
|
|
|
969
974
|
if (currentFact)
|
|
970
975
|
frontmatterSet.completion_facts = JSON.stringify(replaceCompletionFact(completionFacts(frontmatter), currentFact));
|
|
971
976
|
let frontmatterRemove;
|
|
972
|
-
if (eventIdentity.phase === 'started' && artifactContext) {
|
|
973
|
-
try {
|
|
974
|
-
frontmatterSet.qualification_input_relations = JSON.stringify(qualificationRelationsForStarted(eventIdentity.family, artifactContext.inputs));
|
|
975
|
-
}
|
|
976
|
-
catch (error) {
|
|
977
|
-
return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: `cannot capture qualification input relations: ${error instanceof Error ? error.message : String(error)}` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: step, action: eventIdentity.action, phase: eventIdentity.phase, artifactContext });
|
|
978
|
-
}
|
|
979
|
-
}
|
|
980
977
|
if (eventIdentity.phase === 'started' && eventIdentity.family === 'code') {
|
|
981
|
-
const planInput = artifactContext?.inputs.find((input) => input.family === 'plan');
|
|
978
|
+
const planInput = artifactContext?.inputs.find((input) => input.family === 'plan' || input.family === 'analysis');
|
|
982
979
|
if (!planInput)
|
|
983
|
-
return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: 'code.started
|
|
980
|
+
return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: 'code.started lifecycle input context is unavailable' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: step, action: eventIdentity.action, phase: eventIdentity.phase, artifactContext });
|
|
984
981
|
try {
|
|
985
982
|
frontmatterSet.code_input_artifact = planInput.name;
|
|
986
983
|
frontmatterSet.code_input_sha256 = sha256File(planInput.path);
|
|
@@ -1009,14 +1006,12 @@ function applyTaskEventUnlocked(request, options = {}) {
|
|
|
1009
1006
|
frontmatterRemove = ['review_input_artifact', 'review_input_sha256'];
|
|
1010
1007
|
}
|
|
1011
1008
|
if (eventIdentity.phase === 'completed' && eventIdentity.family === 'review-code' && reviewContent !== null) {
|
|
1012
|
-
const reviewedCommit = approvedCleanReviewedCommit(reviewContent, normalized.verdict, resolved.repoRoot);
|
|
1009
|
+
const reviewedCommit = approvedCleanReviewedCommit(reviewContent, normalized.verdict, resolved.repoRoot, typeof frontmatter.branch === 'string' ? frontmatter.branch : '');
|
|
1013
1010
|
if (reviewedCommit)
|
|
1014
1011
|
frontmatterSet.last_reviewed_commit = reviewedCommit;
|
|
1015
1012
|
else if (normalized.verdict === 'approved')
|
|
1016
1013
|
frontmatterSet.last_reviewed_commit = '';
|
|
1017
1014
|
}
|
|
1018
|
-
if (eventIdentity.phase === 'completed')
|
|
1019
|
-
frontmatterRemove = [...(frontmatterRemove ?? []), 'qualification_input_relations'];
|
|
1020
1015
|
if (eventIdentity.phase === 'started' && normalized.implementationInput)
|
|
1021
1016
|
frontmatterSet.last_reviewed_commit = '';
|
|
1022
1017
|
const mutations = [
|
|
@@ -1057,7 +1052,7 @@ function applyTaskEventUnlocked(request, options = {}) {
|
|
|
1057
1052
|
mutations.push({ kind: 'section', aliases: ['活动日志', 'Activity Log'], heading: section.heading, body });
|
|
1058
1053
|
const sourceCompletion = eventIdentity.phase === 'completed'
|
|
1059
1054
|
&& ['analyze', 'plan', 'code'].includes(eventIdentity.family);
|
|
1060
|
-
const result = writeTask({ taskRef: normalized.taskRef, expectedState:
|
|
1055
|
+
const result = writeTask({ taskRef: normalized.taskRef, expectedState: 'active', dryRun: normalized.dryRun, mutations }, { ...options, invalidationContext: sourceCompletion ? 'source-completion' : 'standard', metadataProvider: () => metadata });
|
|
1061
1056
|
if (result.status === 'failed')
|
|
1062
1057
|
return failed(normalized, result.error, { taskId: result.taskId, taskMdPath: result.taskMdPath, fromStep: currentStep, toStep: step, action: eventIdentity.action, phase: eventIdentity.phase, timestamp: result.timestamp, agentInfraVersion: result.agentInfraVersion, operations: result.operations, artifactContext });
|
|
1063
1058
|
if (!normalized.dryRun && orchestrationCompletion) {
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
const HANDOFF_VERSION = 1;
|
|
5
|
+
const MAX_HANDOFF_BYTES = 1024 * 1024;
|
|
6
|
+
function digest(value) {
|
|
7
|
+
return createHash('sha256').update(JSON.stringify(value)).digest('hex');
|
|
8
|
+
}
|
|
9
|
+
export function finalizationHandoffPath(directory, taskId) {
|
|
10
|
+
return path.join(directory, '.agent-infra', 'task-finalization', `${taskId}.json`);
|
|
11
|
+
}
|
|
12
|
+
export function publishTaskFinalizationHandoff(directory, receipt, binding) {
|
|
13
|
+
if (receipt.taskId === '' || receipt.controlBinding?.generation !== binding.generation
|
|
14
|
+
|| receipt.controlBinding.requestId !== binding.requestId) {
|
|
15
|
+
throw new Error('TASK_FINALIZATION_HANDOFF_BINDING_INVALID');
|
|
16
|
+
}
|
|
17
|
+
const envelope = {
|
|
18
|
+
version: HANDOFF_VERSION, taskId: receipt.taskId, binding, receipt, receiptSha256: digest(receipt)
|
|
19
|
+
};
|
|
20
|
+
const target = finalizationHandoffPath(directory, receipt.taskId);
|
|
21
|
+
fs.mkdirSync(path.dirname(target), { recursive: true, mode: 0o700 });
|
|
22
|
+
const temporary = `${target}.${process.pid}.${randomUUID()}.tmp`;
|
|
23
|
+
fs.writeFileSync(temporary, `${JSON.stringify(envelope)}\n`, { mode: 0o600, flag: 'wx' });
|
|
24
|
+
try {
|
|
25
|
+
fs.renameSync(temporary, target);
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
try {
|
|
29
|
+
fs.unlinkSync(temporary);
|
|
30
|
+
}
|
|
31
|
+
catch { /* preserve the primary error */ }
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
34
|
+
return digest(envelope);
|
|
35
|
+
}
|
|
36
|
+
export function readTaskFinalizationHandoff(directory, taskId, binding, handoffSha256) {
|
|
37
|
+
const file = finalizationHandoffPath(directory, taskId);
|
|
38
|
+
const initial = fs.lstatSync(file);
|
|
39
|
+
if (!initial.isFile() || initial.isSymbolicLink() || initial.size > MAX_HANDOFF_BYTES) {
|
|
40
|
+
throw new Error('TASK_FINALIZATION_HANDOFF_INVALID');
|
|
41
|
+
}
|
|
42
|
+
const descriptor = fs.openSync(file, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW ?? 0));
|
|
43
|
+
let content;
|
|
44
|
+
try {
|
|
45
|
+
const opened = fs.fstatSync(descriptor);
|
|
46
|
+
if (!opened.isFile() || opened.dev !== initial.dev || opened.ino !== initial.ino
|
|
47
|
+
|| opened.size > MAX_HANDOFF_BYTES)
|
|
48
|
+
throw new Error('TASK_FINALIZATION_HANDOFF_INVALID');
|
|
49
|
+
const bytes = Buffer.alloc(MAX_HANDOFF_BYTES + 1);
|
|
50
|
+
const length = fs.readSync(descriptor, bytes, 0, bytes.length, 0);
|
|
51
|
+
if (length !== opened.size || length > MAX_HANDOFF_BYTES)
|
|
52
|
+
throw new Error('TASK_FINALIZATION_HANDOFF_INVALID');
|
|
53
|
+
content = bytes.toString('utf8', 0, length);
|
|
54
|
+
const after = fs.fstatSync(descriptor);
|
|
55
|
+
if (after.dev !== opened.dev || after.ino !== opened.ino || after.size !== opened.size) {
|
|
56
|
+
throw new Error('TASK_FINALIZATION_HANDOFF_INVALID');
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
finally {
|
|
60
|
+
fs.closeSync(descriptor);
|
|
61
|
+
}
|
|
62
|
+
const envelope = JSON.parse(content);
|
|
63
|
+
if (!envelope || typeof envelope !== 'object' || Array.isArray(envelope)
|
|
64
|
+
|| Object.keys(envelope).sort().join('\0') !== ['binding', 'receipt', 'receiptSha256', 'taskId', 'version'].join('\0')
|
|
65
|
+
|| envelope.version !== HANDOFF_VERSION || envelope.taskId !== taskId
|
|
66
|
+
|| !envelope.binding || typeof envelope.binding !== 'object' || Array.isArray(envelope.binding)
|
|
67
|
+
|| Object.keys(envelope.binding).sort().join('\0') !== ['generation', 'requestId'].join('\0')
|
|
68
|
+
|| envelope.binding.generation !== binding.generation
|
|
69
|
+
|| envelope.binding.requestId !== binding.requestId || !envelope.receipt
|
|
70
|
+
|| envelope.receipt.controlBinding?.generation !== binding.generation
|
|
71
|
+
|| envelope.receipt.controlBinding.requestId !== binding.requestId
|
|
72
|
+
|| typeof envelope.receiptSha256 !== 'string' || envelope.receiptSha256 !== digest(envelope.receipt)
|
|
73
|
+
|| digest(envelope) !== handoffSha256) {
|
|
74
|
+
throw new Error('TASK_FINALIZATION_HANDOFF_INVALID');
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
receipt: envelope.receipt,
|
|
78
|
+
cleanup: () => {
|
|
79
|
+
try {
|
|
80
|
+
const current = fs.lstatSync(file);
|
|
81
|
+
if (current.isFile() && !current.isSymbolicLink()
|
|
82
|
+
&& current.dev === initial.dev && current.ino === initial.ino)
|
|
83
|
+
fs.unlinkSync(file);
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
if (error.code !== 'ENOENT')
|
|
87
|
+
throw error;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=finalization-handoff.js.map
|