@fitlab-ai/agent-infra 0.11.6 → 0.11.8
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/bin/internal-cli.js +8 -2
- package/dist/lib/agent-clients/adapters/codex.js +1 -1
- package/dist/lib/agent-clients/config.js +11 -3
- package/dist/lib/defaults.json +6 -3
- package/dist/lib/git/primary-worktree.js +58 -0
- package/dist/lib/internal/cli-route-inventory.js +3 -0
- package/dist/lib/internal/platform-pr-review.js +20 -3
- package/dist/lib/internal/task-operation-registry.js +3 -1
- package/dist/lib/internal/task-platform-recovery.js +56 -0
- package/dist/lib/merge.js +25 -5
- package/dist/lib/platform/completion-backfill.js +2 -1
- package/dist/lib/platform/in-label-sync.js +12 -1
- package/dist/lib/platform/issue-comments.js +58 -4
- package/dist/lib/platform/issues.js +105 -5
- package/dist/lib/platform/operation-coordinator.js +74 -0
- package/dist/lib/platform/pr-review.js +107 -1
- package/dist/lib/platform/pr-summary.js +79 -57
- package/dist/lib/platform/pull-requests.js +125 -7
- package/dist/lib/sandbox/commands/create.js +6 -3
- package/dist/lib/sandbox/commands/ls.js +4 -3
- package/dist/lib/sandbox/commands/rm.js +1 -1
- package/dist/lib/sandbox/config.js +22 -4
- package/dist/lib/sandbox/control/server.js +6 -2
- package/dist/lib/sandbox/git-metadata.js +50 -0
- package/dist/lib/sandbox/index.js +1 -1
- package/dist/lib/sandbox/mounts.js +12 -0
- package/dist/lib/sandbox/recovery.js +11 -0
- package/dist/lib/sandbox/removal.js +58 -20
- package/dist/lib/sandbox/tools.js +29 -7
- package/dist/lib/server/config.js +7 -27
- package/dist/lib/server/daemon.js +4 -3
- package/dist/lib/server/process-control.js +4 -2
- package/dist/lib/server/runner.js +4 -3
- package/dist/lib/task/activity-intent.js +4 -3
- package/dist/lib/task/artifact-lifecycle.js +42 -10
- package/dist/lib/task/artifact-receipts.js +62 -22
- package/dist/lib/task/capabilities.js +22 -4
- package/dist/lib/task/completion-facts.js +56 -0
- package/dist/lib/task/create-service.js +11 -5
- package/dist/lib/task/create.js +60 -79
- package/dist/lib/task/events.js +268 -195
- package/dist/lib/task/finalization-artifacts.js +4 -1
- package/dist/lib/task/finalization-handoff.js +2 -1
- package/dist/lib/task/finalization.js +157 -24
- package/dist/lib/task/invalidation-command.js +4 -2
- package/dist/lib/task/invalidation.js +38 -9
- package/dist/lib/task/lifecycle.js +38 -2
- package/dist/lib/task/local-artifact-finalization.js +17 -3
- package/dist/lib/task/platform-operation-journal.js +152 -0
- package/dist/lib/task/platform-operation-recovery.js +459 -0
- package/dist/lib/task/qualification-audit.js +23 -105
- package/dist/lib/task/review-finalization.js +7 -0
- package/dist/lib/task/review-identity.js +69 -0
- package/dist/lib/task/sections.js +6 -5
- package/dist/lib/task/task-execution-lock.js +6 -3
- package/dist/lib/task/verification-engine.js +14 -63
- package/dist/lib/task/verification.js +35 -38
- package/dist/lib/task/workflow-warning-intents.js +7 -1
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-clients/adapters/codex.ts +1 -1
- package/lib/agent-clients/config.ts +9 -3
- package/lib/defaults.json +6 -3
- package/lib/git/primary-worktree.ts +62 -0
- package/lib/internal/cli-route-inventory.ts +2 -0
- package/lib/internal/platform-pr-review.ts +14 -3
- package/lib/internal/task-operation-registry.ts +3 -1
- package/lib/internal/task-platform-recovery.ts +53 -0
- package/lib/merge.ts +26 -6
- package/lib/platform/completion-backfill.ts +2 -1
- package/lib/platform/in-label-sync.ts +12 -0
- package/lib/platform/issue-comments.ts +59 -3
- package/lib/platform/issues.ts +103 -5
- package/lib/platform/operation-coordinator.ts +96 -0
- package/lib/platform/pr-review.ts +113 -1
- package/lib/platform/pr-summary.ts +22 -1
- package/lib/platform/pull-requests.ts +132 -8
- package/lib/sandbox/commands/create.ts +7 -8
- package/lib/sandbox/commands/ls.ts +4 -3
- package/lib/sandbox/commands/rm.ts +1 -1
- package/lib/sandbox/config.ts +23 -5
- package/lib/sandbox/control/server.ts +5 -2
- package/lib/sandbox/git-metadata.ts +66 -0
- package/lib/sandbox/index.ts +1 -1
- package/lib/sandbox/mounts.ts +14 -0
- package/lib/sandbox/recovery.ts +13 -0
- package/lib/sandbox/removal.ts +57 -19
- package/lib/sandbox/tool-types.ts +1 -1
- package/lib/sandbox/tools.ts +36 -7
- package/lib/server/config.ts +7 -29
- package/lib/server/daemon.ts +5 -3
- package/lib/server/process-control.ts +4 -2
- package/lib/server/runner.ts +7 -4
- package/lib/server/server.schema.json +0 -4
- package/lib/task/activity-intent.ts +4 -3
- package/lib/task/artifact-lifecycle.ts +35 -11
- package/lib/task/artifact-receipts.ts +55 -15
- package/lib/task/capabilities.ts +17 -3
- package/lib/task/completion-facts.ts +66 -0
- package/lib/task/create-service.ts +12 -6
- package/lib/task/create.ts +54 -69
- package/lib/task/events.ts +248 -182
- package/lib/task/finalization-artifacts.ts +4 -1
- package/lib/task/finalization-handoff.ts +2 -1
- package/lib/task/finalization.ts +154 -21
- package/lib/task/invalidation-command.ts +4 -2
- package/lib/task/invalidation.ts +35 -10
- package/lib/task/lifecycle.ts +35 -3
- package/lib/task/local-artifact-finalization.ts +13 -3
- package/lib/task/platform-operation-journal.ts +230 -0
- package/lib/task/platform-operation-recovery.ts +435 -0
- package/lib/task/qualification-audit.ts +21 -114
- package/lib/task/review-finalization.ts +7 -0
- package/lib/task/review-identity.ts +85 -0
- package/lib/task/sections.ts +6 -6
- package/lib/task/task-execution-lock.ts +7 -3
- package/lib/task/verification-engine.ts +13 -86
- package/lib/task/verification.ts +34 -38
- package/lib/task/workflow-warning-intents.ts +9 -1
- package/package.json +1 -1
- package/templates/.agents/README.en.md +19 -17
- package/templates/.agents/README.zh-CN.md +18 -16
- package/templates/.agents/rules/README.en.md +2 -1
- package/templates/.agents/rules/README.zh-CN.md +2 -1
- package/templates/.agents/rules/decision-qualification.en.md +4 -3
- package/templates/.agents/rules/decision-qualification.zh-CN.md +4 -3
- package/templates/.agents/rules/task-management.en.md +12 -0
- package/templates/.agents/rules/task-management.zh-CN.md +12 -0
- package/templates/.agents/rules/validation-output.en.md +19 -0
- package/templates/.agents/rules/validation-output.zh-CN.md +19 -0
- package/templates/.agents/skills/analyze-task/SKILL.en.md +3 -1
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/block-task/SKILL.en.md +2 -0
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/cancel-task/SKILL.en.md +6 -2
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +5 -3
- package/templates/.agents/skills/code-task/SKILL.en.md +5 -3
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/code-task/reference/report-template.en.md +4 -8
- package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +4 -8
- package/templates/.agents/skills/commit/SKILL.en.md +1 -1
- package/templates/.agents/skills/commit/SKILL.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 +4 -0
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +4 -0
- package/templates/.agents/skills/create-pr/SKILL.en.md +2 -0
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/create-task/SKILL.en.md +2 -0
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/import-codescan/SKILL.en.md +2 -0
- package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/import-dependabot/SKILL.en.md +2 -0
- package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/import-issue/SKILL.en.md +2 -0
- package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/plan-task/SKILL.en.md +3 -1
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +3 -3
- 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/review-analysis/SKILL.en.md +3 -1
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +3 -1
- package/templates/.agents/skills/review-analysis/reference/report-template.en.md +4 -8
- package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +4 -8
- package/templates/.agents/skills/review-code/SKILL.en.md +13 -12
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +11 -12
- package/templates/.agents/skills/review-code/config/verify.en.json +1 -3
- package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -3
- package/templates/.agents/skills/review-code/reference/output-templates.en.md +23 -46
- package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +27 -48
- package/templates/.agents/skills/review-code/reference/report-template.en.md +8 -11
- package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +8 -11
- package/templates/.agents/skills/review-code/reference/review-criteria.en.md +2 -2
- package/templates/.agents/skills/review-code/reference/review-criteria.zh-CN.md +2 -2
- package/templates/.agents/skills/review-plan/SKILL.en.md +3 -1
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +3 -1
- package/templates/.agents/skills/review-plan/reference/report-template.en.md +5 -9
- package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +5 -9
- package/templates/.agents/skills/review-pr/SKILL.en.md +13 -11
- package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +13 -11
- package/templates/.agents/skills/review-pr/reference/output-guidance.en.md +3 -3
- package/templates/.agents/skills/review-pr/reference/output-guidance.zh-CN.md +3 -3
- package/templates/.agents/skills/review-pr/reference/report-template.en.md +4 -0
- package/templates/.agents/skills/review-pr/reference/report-template.zh-CN.md +4 -0
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +1 -1
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +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/skills/update-agent-infra/scripts/sync-templates.js +19 -6
- package/templates/.agents/skills/watch-pr/SKILL.en.md +2 -0
- package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +1 -1
- package/templates/.agents/templates/task.en.md +2 -2
- package/templates/.agents/templates/task.zh-CN.md +2 -2
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
4
|
+
|
|
5
|
+
import { isResourceIdentity } from '../platform/resource-identity.ts';
|
|
6
|
+
import type { ResourceIdentity } from '../platform/resource-identity.ts';
|
|
7
|
+
import { resolveTaskRef } from './resolve-ref.ts';
|
|
8
|
+
|
|
9
|
+
const JOURNAL_FILE = '.platform-operations.json';
|
|
10
|
+
const MAX_ATTEMPTS = 3;
|
|
11
|
+
|
|
12
|
+
type PlatformOperationKind = 'task-comment' | 'artifact-comment' | 'summary-comment' | 'cancel-comment' | 'issue-create' | 'issue-metadata' | 'pull-request' | 'pull-request-summary' | 'pull-request-review';
|
|
13
|
+
type PlatformOperationState = 'queued' | 'pending' | 'unknown' | 'succeeded' | 'failed';
|
|
14
|
+
type PlatformIssueMetadataIntent = Readonly<{
|
|
15
|
+
requirements: boolean;
|
|
16
|
+
issueType: boolean;
|
|
17
|
+
fields: boolean;
|
|
18
|
+
status?: string;
|
|
19
|
+
assignees?: 'current' | 'none';
|
|
20
|
+
milestone?: 'initial' | 'specific' | 'none';
|
|
21
|
+
inLabels?: 'from-diff' | 'none';
|
|
22
|
+
base?: string;
|
|
23
|
+
fromDiffFiles?: readonly string[];
|
|
24
|
+
inLabelMappingDigest?: string;
|
|
25
|
+
state?: 'open' | 'closed';
|
|
26
|
+
closeReason?: 'completed' | 'not_planned';
|
|
27
|
+
}>;
|
|
28
|
+
type PlatformPullRequestIntent = Readonly<{
|
|
29
|
+
action: 'create' | 'bind' | 'sync';
|
|
30
|
+
baseRef?: string;
|
|
31
|
+
headRef?: string;
|
|
32
|
+
prToken?: string;
|
|
33
|
+
metadata?: boolean;
|
|
34
|
+
closingIssue?: boolean;
|
|
35
|
+
}>;
|
|
36
|
+
type PlatformIssueCreateIntent = Readonly<{ title: string; bodyDigest: string }>;
|
|
37
|
+
type PlatformPullRequestSummaryIntent = Readonly<{ body: string; changeReportFile: string }>;
|
|
38
|
+
type PlatformPullRequestReviewIntent = Readonly<{
|
|
39
|
+
prNumber: string;
|
|
40
|
+
resource: ResourceIdentity;
|
|
41
|
+
providerScopeId: string;
|
|
42
|
+
scope: string;
|
|
43
|
+
round: number;
|
|
44
|
+
commitSha: string;
|
|
45
|
+
event: 'COMMENT' | 'APPROVE' | 'REQUEST_CHANGES';
|
|
46
|
+
artifactFile: string;
|
|
47
|
+
bodyDigest: string;
|
|
48
|
+
}>;
|
|
49
|
+
type PlatformOperation = Readonly<{
|
|
50
|
+
id: string;
|
|
51
|
+
kind: PlatformOperationKind;
|
|
52
|
+
target: string;
|
|
53
|
+
expectedDigest: string;
|
|
54
|
+
dependency: 'deferred' | 'required';
|
|
55
|
+
state: PlatformOperationState;
|
|
56
|
+
attempts: number;
|
|
57
|
+
maxAttempts: typeof MAX_ATTEMPTS;
|
|
58
|
+
lastCode: string | null;
|
|
59
|
+
issueMetadata?: PlatformIssueMetadataIntent;
|
|
60
|
+
issueCreate?: PlatformIssueCreateIntent;
|
|
61
|
+
pullRequest?: PlatformPullRequestIntent;
|
|
62
|
+
pullRequestSummary?: PlatformPullRequestSummaryIntent;
|
|
63
|
+
pullRequestReview?: PlatformPullRequestReviewIntent;
|
|
64
|
+
updatedAt: string;
|
|
65
|
+
}>;
|
|
66
|
+
type PlatformOperationJournal = Readonly<{
|
|
67
|
+
version: 1;
|
|
68
|
+
taskId: string;
|
|
69
|
+
operations: readonly PlatformOperation[];
|
|
70
|
+
}>;
|
|
71
|
+
|
|
72
|
+
type RecordOperationInput = Readonly<{
|
|
73
|
+
taskRef: string;
|
|
74
|
+
kind: PlatformOperationKind;
|
|
75
|
+
target: string;
|
|
76
|
+
expectedDigest: string;
|
|
77
|
+
dependency: 'deferred' | 'required';
|
|
78
|
+
state: PlatformOperationState;
|
|
79
|
+
lastCode?: string | null;
|
|
80
|
+
issueMetadata?: PlatformIssueMetadataIntent;
|
|
81
|
+
issueCreate?: PlatformIssueCreateIntent;
|
|
82
|
+
pullRequest?: PlatformPullRequestIntent;
|
|
83
|
+
pullRequestSummary?: PlatformPullRequestSummaryIntent;
|
|
84
|
+
pullRequestReview?: PlatformPullRequestReviewIntent;
|
|
85
|
+
cwd?: string;
|
|
86
|
+
}>;
|
|
87
|
+
|
|
88
|
+
function operationId(input: Pick<RecordOperationInput, 'kind' | 'target' | 'expectedDigest'>): string {
|
|
89
|
+
return createHash('sha256')
|
|
90
|
+
.update(`${input.kind}\0${input.target}\0${input.expectedDigest}`)
|
|
91
|
+
.digest('hex');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function journalPath(taskDir: string): string {
|
|
95
|
+
return path.join(taskDir, JOURNAL_FILE);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function parseJournal(file: string, taskId: string): PlatformOperationJournal {
|
|
99
|
+
let value: unknown;
|
|
100
|
+
try { value = JSON.parse(fs.readFileSync(file, 'utf8')); }
|
|
101
|
+
catch (error) {
|
|
102
|
+
if ((error as NodeJS.ErrnoException).code === 'ENOENT') return { version: 1, taskId, operations: [] };
|
|
103
|
+
throw Object.assign(new Error(`Platform operation journal is unreadable: ${String(error)}`), { code: 'PLATFORM_OPERATION_JOURNAL_INVALID' });
|
|
104
|
+
}
|
|
105
|
+
if (!value || typeof value !== 'object') throw Object.assign(new Error('Platform operation journal is invalid'), { code: 'PLATFORM_OPERATION_JOURNAL_INVALID' });
|
|
106
|
+
const journal = value as Partial<PlatformOperationJournal>;
|
|
107
|
+
if (journal.version !== 1 || journal.taskId !== taskId || !Array.isArray(journal.operations)) {
|
|
108
|
+
throw Object.assign(new Error('Platform operation journal identity or version is invalid'), { code: 'PLATFORM_OPERATION_JOURNAL_INVALID' });
|
|
109
|
+
}
|
|
110
|
+
for (const item of journal.operations) {
|
|
111
|
+
if (!item || typeof item !== 'object' || !/^[a-f0-9]{64}$/u.test(item.id)
|
|
112
|
+
|| !['task-comment', 'artifact-comment', 'summary-comment', 'cancel-comment', 'issue-create', 'issue-metadata', 'pull-request', 'pull-request-summary', 'pull-request-review'].includes(item.kind)
|
|
113
|
+
|| typeof item.target !== 'string' || !item.target
|
|
114
|
+
|| !/^[a-f0-9]{64}$/u.test(item.expectedDigest)
|
|
115
|
+
|| !['deferred', 'required'].includes(item.dependency)
|
|
116
|
+
|| !['queued', 'pending', 'unknown', 'succeeded', 'failed'].includes(item.state)
|
|
117
|
+
|| !Number.isSafeInteger(item.attempts) || item.attempts < 0 || item.attempts > MAX_ATTEMPTS
|
|
118
|
+
|| item.maxAttempts !== MAX_ATTEMPTS
|
|
119
|
+
|| !(item.lastCode === null || typeof item.lastCode === 'string')
|
|
120
|
+
|| (item.kind === 'issue-metadata'
|
|
121
|
+
? !item.issueMetadata || typeof item.issueMetadata.requirements !== 'boolean'
|
|
122
|
+
|| typeof item.issueMetadata.issueType !== 'boolean' || typeof item.issueMetadata.fields !== 'boolean'
|
|
123
|
+
|| (item.issueMetadata.status !== undefined && typeof item.issueMetadata.status !== 'string')
|
|
124
|
+
|| (item.issueMetadata.assignees !== undefined && !['current', 'none'].includes(item.issueMetadata.assignees))
|
|
125
|
+
|| (item.issueMetadata.milestone !== undefined && !['initial', 'specific', 'none'].includes(item.issueMetadata.milestone))
|
|
126
|
+
|| (item.issueMetadata.inLabels !== undefined && !['from-diff', 'none'].includes(item.issueMetadata.inLabels))
|
|
127
|
+
|| (item.issueMetadata.base !== undefined && (typeof item.issueMetadata.base !== 'string' || !item.issueMetadata.base.trim()))
|
|
128
|
+
|| (item.issueMetadata.fromDiffFiles !== undefined && (!Array.isArray(item.issueMetadata.fromDiffFiles)
|
|
129
|
+
|| item.issueMetadata.fromDiffFiles.some((file: unknown) => typeof file !== 'string')))
|
|
130
|
+
|| (item.issueMetadata.inLabelMappingDigest !== undefined && !/^[a-f0-9]{64}$/u.test(item.issueMetadata.inLabelMappingDigest))
|
|
131
|
+
|| (item.issueMetadata.state !== undefined && !['open', 'closed'].includes(item.issueMetadata.state))
|
|
132
|
+
|| (item.issueMetadata.closeReason !== undefined && !['completed', 'not_planned'].includes(item.issueMetadata.closeReason))
|
|
133
|
+
|| (!item.issueMetadata.requirements && !item.issueMetadata.issueType && !item.issueMetadata.fields
|
|
134
|
+
&& item.issueMetadata.status === undefined && item.issueMetadata.assignees === undefined
|
|
135
|
+
&& item.issueMetadata.milestone === undefined && item.issueMetadata.inLabels === undefined && item.issueMetadata.state === undefined)
|
|
136
|
+
: item.issueMetadata !== undefined)
|
|
137
|
+
|| (item.kind === 'pull-request'
|
|
138
|
+
? !item.pullRequest || !['create', 'bind', 'sync'].includes(item.pullRequest.action)
|
|
139
|
+
|| Object.entries(item.pullRequest).some(([key, field]) => key !== 'action'
|
|
140
|
+
&& key !== 'metadata' && key !== 'closingIssue' && (typeof field !== 'string' || !field.trim()))
|
|
141
|
+
|| (item.pullRequest.metadata !== undefined && typeof item.pullRequest.metadata !== 'boolean')
|
|
142
|
+
|| (item.pullRequest.closingIssue !== undefined && typeof item.pullRequest.closingIssue !== 'boolean')
|
|
143
|
+
: item.pullRequest !== undefined)
|
|
144
|
+
|| (item.kind === 'issue-create'
|
|
145
|
+
? !item.issueCreate || typeof item.issueCreate.title !== 'string' || !item.issueCreate.title.trim()
|
|
146
|
+
|| !/^[a-f0-9]{64}$/u.test(item.issueCreate.bodyDigest)
|
|
147
|
+
: item.issueCreate !== undefined)
|
|
148
|
+
|| (item.kind === 'pull-request-summary'
|
|
149
|
+
? !item.pullRequestSummary || typeof item.pullRequestSummary.body !== 'string'
|
|
150
|
+
|| !item.pullRequestSummary.body.trim() || typeof item.pullRequestSummary.changeReportFile !== 'string'
|
|
151
|
+
|| !item.pullRequestSummary.changeReportFile.trim()
|
|
152
|
+
: item.pullRequestSummary !== undefined)
|
|
153
|
+
|| (item.kind === 'pull-request-review'
|
|
154
|
+
? !item.pullRequestReview || typeof item.pullRequestReview.prNumber !== 'string'
|
|
155
|
+
|| !isResourceIdentity(item.pullRequestReview.resource)
|
|
156
|
+
|| typeof item.pullRequestReview.providerScopeId !== 'string' || !item.pullRequestReview.providerScopeId.trim()
|
|
157
|
+
|| !item.pullRequestReview.prNumber.trim() || !/^TASK-\d{8}-\d{6}$|^pr\d+$/u.test(item.pullRequestReview.scope)
|
|
158
|
+
|| !Number.isSafeInteger(item.pullRequestReview.round) || item.pullRequestReview.round <= 0
|
|
159
|
+
|| !/^[0-9a-f]{7,40}$/iu.test(item.pullRequestReview.commitSha)
|
|
160
|
+
|| !['COMMENT', 'APPROVE', 'REQUEST_CHANGES'].includes(item.pullRequestReview.event)
|
|
161
|
+
|| typeof item.pullRequestReview.artifactFile !== 'string'
|
|
162
|
+
|| !/^pr-review(?:-r\d+)?\.md$/u.test(item.pullRequestReview.artifactFile)
|
|
163
|
+
|| (item.pullRequestReview.artifactFile === 'pr-review.md'
|
|
164
|
+
? item.pullRequestReview.round !== 1
|
|
165
|
+
: item.pullRequestReview.artifactFile !== `pr-review-r${item.pullRequestReview.round}.md`)
|
|
166
|
+
|| !/^[a-f0-9]{64}$/u.test(item.pullRequestReview.bodyDigest)
|
|
167
|
+
: item.pullRequestReview !== undefined)
|
|
168
|
+
|| typeof item.updatedAt !== 'string') {
|
|
169
|
+
throw Object.assign(new Error('Platform operation journal contains an invalid operation'), { code: 'PLATFORM_OPERATION_JOURNAL_INVALID' });
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return journal as PlatformOperationJournal;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function writeJournal(file: string, journal: PlatformOperationJournal): void {
|
|
176
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
177
|
+
const temporary = `${file}.${process.pid}.${randomUUID()}.tmp`;
|
|
178
|
+
try {
|
|
179
|
+
fs.writeFileSync(temporary, `${JSON.stringify(journal, null, 2)}\n`, { flag: 'wx', mode: 0o600 });
|
|
180
|
+
fs.renameSync(temporary, file);
|
|
181
|
+
} finally {
|
|
182
|
+
try { fs.unlinkSync(temporary); } catch { /* renamed or already removed */ }
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function resolveJournal(taskRef: string, cwd?: string): { taskId: string; file: string } {
|
|
187
|
+
const resolved = resolveTaskRef(taskRef, cwd ? { repoRoot: cwd } : {});
|
|
188
|
+
if (!resolved.ok) throw Object.assign(new Error(resolved.message), { code: resolved.code });
|
|
189
|
+
return { taskId: resolved.taskId, file: journalPath(resolved.taskDir) };
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function recordPlatformOperation(input: RecordOperationInput): PlatformOperation {
|
|
193
|
+
if (!/^[a-f0-9]{64}$/u.test(input.expectedDigest) || !input.target.trim()) {
|
|
194
|
+
throw Object.assign(new Error('Platform operation requires a stable target and SHA-256 digest'), { code: 'PLATFORM_OPERATION_PAYLOAD_INVALID' });
|
|
195
|
+
}
|
|
196
|
+
const { taskId, file } = resolveJournal(input.taskRef, input.cwd);
|
|
197
|
+
const journal = parseJournal(file, taskId);
|
|
198
|
+
const id = operationId(input);
|
|
199
|
+
const previous = journal.operations.find((item) => item.id === id);
|
|
200
|
+
const next: PlatformOperation = {
|
|
201
|
+
id,
|
|
202
|
+
kind: input.kind,
|
|
203
|
+
target: input.target,
|
|
204
|
+
expectedDigest: input.expectedDigest,
|
|
205
|
+
dependency: input.dependency,
|
|
206
|
+
state: input.state,
|
|
207
|
+
attempts: Math.min(MAX_ATTEMPTS, (previous?.attempts ?? 0) + (input.state === 'pending' ? 1 : 0)),
|
|
208
|
+
maxAttempts: MAX_ATTEMPTS,
|
|
209
|
+
lastCode: input.lastCode ?? null,
|
|
210
|
+
...(input.issueMetadata ? { issueMetadata: input.issueMetadata } : {}),
|
|
211
|
+
...(input.issueCreate ? { issueCreate: input.issueCreate } : {}),
|
|
212
|
+
...(input.pullRequest ? { pullRequest: input.pullRequest } : {}),
|
|
213
|
+
...(input.pullRequestSummary ? { pullRequestSummary: input.pullRequestSummary } : {}),
|
|
214
|
+
...(input.pullRequestReview ? { pullRequestReview: input.pullRequestReview } : {}),
|
|
215
|
+
updatedAt: new Date().toISOString()
|
|
216
|
+
};
|
|
217
|
+
const operations = previous
|
|
218
|
+
? journal.operations.map((item) => item.id === id ? next : item)
|
|
219
|
+
: [...journal.operations, next];
|
|
220
|
+
writeJournal(file, { version: 1, taskId, operations });
|
|
221
|
+
return next;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function readPlatformOperationJournal(taskRef: string, cwd?: string): PlatformOperationJournal {
|
|
225
|
+
const { taskId, file } = resolveJournal(taskRef, cwd);
|
|
226
|
+
return parseJournal(file, taskId);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export { JOURNAL_FILE as PLATFORM_OPERATION_JOURNAL_FILE, MAX_ATTEMPTS as PLATFORM_OPERATION_MAX_ATTEMPTS, operationId, recordPlatformOperation, readPlatformOperationJournal };
|
|
230
|
+
export type { PlatformIssueCreateIntent, PlatformIssueMetadataIntent, PlatformOperation, PlatformOperationJournal, PlatformOperationKind, PlatformOperationState, PlatformPullRequestIntent, PlatformPullRequestReviewIntent, PlatformPullRequestSummaryIntent, RecordOperationInput };
|