@fitlab-ai/agent-infra 0.9.4 → 0.9.6
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 +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/bin/internal-cli.js +13 -3
- package/dist/lib/agent-clients/adapter.js +2 -2
- package/dist/lib/agent-clients/adapters/codex-lifecycle/app-server.js +492 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/evidence.js +175 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +255 -0
- package/dist/lib/agent-clients/adapters/codex.js +3 -3
- package/dist/lib/git/workflow.js +0 -1
- package/dist/lib/git/worktree-identity.js +64 -0
- package/dist/lib/internal/codex-lifecycle.js +300 -0
- package/dist/lib/internal/release-workflow.js +108 -11
- package/dist/lib/internal/sandbox-control.js +43 -6
- package/dist/lib/internal/task-create.js +56 -0
- package/dist/lib/internal/task-orchestration.js +71 -31
- package/dist/lib/platform/issue-comments.js +2 -1
- package/dist/lib/release/workflow.js +29 -21
- package/dist/lib/sandbox/commands/create.js +16 -16
- package/dist/lib/sandbox/commands/prune.js +40 -16
- package/dist/lib/sandbox/commands/rm.js +318 -89
- package/dist/lib/sandbox/control/client.js +152 -22
- package/dist/lib/sandbox/control/executor.js +136 -0
- package/dist/lib/sandbox/control/lifecycle.js +348 -0
- package/dist/lib/sandbox/control/protocol.js +73 -18
- package/dist/lib/sandbox/control/server.js +235 -80
- package/dist/lib/sandbox/control/state.js +181 -0
- package/dist/lib/sandbox/index.js +2 -2
- package/dist/lib/sandbox/managed-fs.js +14 -2
- package/dist/lib/sandbox/mounts.js +35 -24
- package/dist/lib/sandbox/recovery.js +262 -45
- package/dist/lib/sandbox/workspace-view.js +80 -11
- package/dist/lib/sandbox/worktree-safety.js +196 -0
- package/dist/lib/server/process-control.js +6 -0
- package/dist/lib/task/activity-log.js +32 -5
- package/dist/lib/task/artifact-lifecycle.js +5 -4
- package/dist/lib/task/codex-orchestration.js +161 -0
- package/dist/lib/task/commands/status.js +3 -0
- package/dist/lib/task/commands/validate.js +289 -0
- package/dist/lib/task/commit-finalization.js +50 -12
- package/dist/lib/task/create-service.js +137 -0
- package/dist/lib/task/create.js +333 -0
- package/dist/lib/task/delegation-receipts.js +25 -0
- package/dist/lib/task/events.js +10 -4
- package/dist/lib/task/finalization-artifacts.js +2 -1
- package/dist/lib/task/implementation-inputs.js +1 -7
- package/dist/lib/task/index.js +9 -0
- package/dist/lib/task/orchestration.js +283 -18
- package/dist/lib/task/review-fingerprint.js +4 -0
- package/dist/lib/task/verification-engine.js +38 -6
- package/dist/lib/task/verification.js +1 -0
- package/dist/lib/task/workspace-snapshot.js +78 -3
- package/lib/agent-clients/adapter.ts +4 -4
- package/lib/agent-clients/adapters/codex-lifecycle/app-server.ts +569 -0
- package/lib/agent-clients/adapters/codex-lifecycle/evidence.ts +323 -0
- package/lib/agent-clients/adapters/codex-lifecycle/store.ts +298 -0
- package/lib/agent-clients/adapters/codex.ts +3 -3
- package/lib/git/workflow.ts +0 -1
- package/lib/git/worktree-identity.ts +86 -0
- package/lib/internal/codex-lifecycle.ts +308 -0
- package/lib/internal/release-workflow.ts +107 -11
- package/lib/internal/sandbox-control.ts +38 -6
- package/lib/internal/task-create.ts +61 -0
- package/lib/internal/task-orchestration.ts +57 -31
- package/lib/platform/issue-comments.ts +2 -1
- package/lib/release/workflow.ts +69 -20
- package/lib/sandbox/commands/create.ts +18 -21
- package/lib/sandbox/commands/prune.ts +53 -15
- package/lib/sandbox/commands/rm.ts +368 -103
- package/lib/sandbox/control/client.ts +173 -25
- package/lib/sandbox/control/executor.ts +163 -0
- package/lib/sandbox/control/lifecycle.ts +342 -0
- package/lib/sandbox/control/protocol.ts +110 -48
- package/lib/sandbox/control/server.ts +252 -85
- package/lib/sandbox/control/state.ts +196 -0
- package/lib/sandbox/index.ts +2 -2
- package/lib/sandbox/managed-fs.ts +17 -2
- package/lib/sandbox/mounts.ts +31 -13
- package/lib/sandbox/recovery.ts +301 -60
- package/lib/sandbox/workspace-view.ts +89 -12
- package/lib/sandbox/worktree-safety.ts +234 -0
- package/lib/server/process-control.ts +11 -0
- package/lib/task/activity-log.ts +46 -7
- package/lib/task/artifact-lifecycle.ts +5 -4
- package/lib/task/codex-orchestration.ts +233 -0
- package/lib/task/commands/status.ts +2 -0
- package/lib/task/commands/validate.ts +282 -0
- package/lib/task/commit-finalization.ts +75 -12
- package/lib/task/create-service.ts +171 -0
- package/lib/task/create.ts +372 -0
- package/lib/task/delegation-receipts.ts +52 -3
- package/lib/task/events.ts +9 -4
- package/lib/task/finalization-artifacts.ts +2 -1
- package/lib/task/implementation-inputs.ts +1 -5
- package/lib/task/index.ts +8 -0
- package/lib/task/orchestration.ts +381 -22
- package/lib/task/review-fingerprint.ts +9 -0
- package/lib/task/verification-engine.ts +42 -8
- package/lib/task/verification.ts +2 -1
- package/lib/task/workspace-snapshot.ts +98 -4
- package/package.json +7 -7
- package/templates/.agents/hooks/lifecycle-delegation.js +88 -8
- package/templates/.agents/rules/lifecycle-orchestration.en.md +1 -1
- package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +1 -1
- package/templates/.agents/rules/no-mid-flow-questions.en.md +1 -0
- package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +1 -0
- package/templates/.agents/rules/task-management.en.md +1 -0
- package/templates/.agents/rules/task-management.zh-CN.md +1 -0
- package/templates/.agents/skills/commit/SKILL.en.md +3 -9
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +6 -10
- package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +4 -3
- package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +5 -4
- package/templates/.agents/skills/create-task/SKILL.en.md +26 -43
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +26 -43
- package/templates/.agents/skills/post-release/SKILL.en.md +32 -6
- package/templates/.agents/skills/post-release/SKILL.zh-CN.md +32 -6
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +42 -0
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +42 -0
- package/templates/.agents/skills/run-manual-validation/config/verify.en.json +14 -0
- package/templates/.agents/skills/run-manual-validation/config/verify.zh-CN.json +14 -0
- package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +44 -0
- package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +44 -0
- package/templates/.agents/skills/run-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/run-task/reference/host-validation.en.md +3 -3
- package/templates/.agents/skills/run-task/reference/host-validation.zh-CN.md +3 -3
- package/templates/.claude/commands/run-manual-validation.en.md +9 -0
- package/templates/.claude/commands/run-manual-validation.zh-CN.md +9 -0
- package/templates/.codex/hooks.json +50 -0
- package/templates/.opencode/commands/run-manual-validation.en.md +11 -0
- package/templates/.opencode/commands/run-manual-validation.zh-CN.md +11 -0
|
@@ -2,15 +2,22 @@ import { execFileSync, spawnSync } from 'node:child_process';
|
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
locateActivityLog,
|
|
7
|
+
appendActivityEntry,
|
|
8
|
+
pairEntries,
|
|
9
|
+
parseCommitAttemptStarted
|
|
10
|
+
} from './activity-log.ts';
|
|
11
|
+
import type { CommitAttempt } from './activity-log.ts';
|
|
6
12
|
import { commitIntentPath, digest, readCommitIntent } from './commit-intent.ts';
|
|
7
13
|
import type { CommitIntent } from './commit-intent.ts';
|
|
8
14
|
import { parseTypedTaskFrontmatter } from './frontmatter.ts';
|
|
9
|
-
import { parseReviewSummary } from './review-artifacts.ts';
|
|
15
|
+
import { artifactName, maxRound, parseReviewSummary } from './review-artifacts.ts';
|
|
10
16
|
import {
|
|
11
17
|
extractReviewBaseline,
|
|
12
18
|
extractReviewedSnapshotTree,
|
|
13
|
-
findAuthoritativeReviewCodeArtifact
|
|
19
|
+
findAuthoritativeReviewCodeArtifact,
|
|
20
|
+
parseReviewedGitTree
|
|
14
21
|
} from './review-fingerprint.ts';
|
|
15
22
|
import type { TaskMutation } from './write.ts';
|
|
16
23
|
|
|
@@ -20,6 +27,7 @@ type CommitFinalizationDisposition =
|
|
|
20
27
|
| 'recoverable'
|
|
21
28
|
| 'invalid'
|
|
22
29
|
| 'conflict'
|
|
30
|
+
| 'retryable-start'
|
|
23
31
|
| 'orphaned-start';
|
|
24
32
|
|
|
25
33
|
type CommitFinalizationCode =
|
|
@@ -35,10 +43,12 @@ type CommitFinalizationInspection = Readonly<{
|
|
|
35
43
|
currentHead: string;
|
|
36
44
|
committedHead: string | null;
|
|
37
45
|
commitNote: string | null;
|
|
46
|
+
activityNote: string | null;
|
|
38
47
|
needsAnchor: boolean;
|
|
39
48
|
needsLog: boolean;
|
|
40
49
|
intent: CommitIntent | null;
|
|
41
50
|
intentDigest: string | null;
|
|
51
|
+
attempt: CommitAttempt | null;
|
|
42
52
|
}>;
|
|
43
53
|
|
|
44
54
|
type CreatePrCommitGate = Readonly<{
|
|
@@ -78,6 +88,28 @@ function isAncestor(repoRoot: string, ancestor: string, descendant: string): boo
|
|
|
78
88
|
}).status === 0;
|
|
79
89
|
}
|
|
80
90
|
|
|
91
|
+
function hasMatchingApprovedReview(
|
|
92
|
+
taskDir: string,
|
|
93
|
+
beforeRound: number,
|
|
94
|
+
baseline: string,
|
|
95
|
+
tree: string
|
|
96
|
+
): boolean {
|
|
97
|
+
const entries = fs.readdirSync(taskDir);
|
|
98
|
+
for (let round = Math.min(beforeRound - 1, maxRound(entries, 'review-code')); round >= 1; round -= 1) {
|
|
99
|
+
const file = path.join(taskDir, artifactName('review-code', round));
|
|
100
|
+
if (!fs.existsSync(file)) continue;
|
|
101
|
+
const content = fs.readFileSync(file, 'utf8');
|
|
102
|
+
const summary = parseReviewSummary(content);
|
|
103
|
+
if (
|
|
104
|
+
summary.ok
|
|
105
|
+
&& summary.summary.verdict === 'Approved'
|
|
106
|
+
&& extractReviewBaseline(content) === baseline
|
|
107
|
+
&& extractReviewedSnapshotTree(content) === tree
|
|
108
|
+
) return true;
|
|
109
|
+
}
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
|
|
81
113
|
function outcome(
|
|
82
114
|
disposition: CommitFinalizationDisposition,
|
|
83
115
|
overrides: Partial<CommitFinalizationInspection> = {}
|
|
@@ -89,10 +121,12 @@ function outcome(
|
|
|
89
121
|
currentHead: '',
|
|
90
122
|
committedHead: null,
|
|
91
123
|
commitNote: null,
|
|
124
|
+
activityNote: null,
|
|
92
125
|
needsAnchor: false,
|
|
93
126
|
needsLog: false,
|
|
94
127
|
intent: null,
|
|
95
128
|
intentDigest: null,
|
|
129
|
+
attempt: null,
|
|
96
130
|
...overrides
|
|
97
131
|
};
|
|
98
132
|
}
|
|
@@ -128,6 +162,16 @@ function inspectCommitFinalization(
|
|
|
128
162
|
const openRows = commitRows.filter((row) => row.started !== '' && row.done === '');
|
|
129
163
|
const file = commitIntentPath(taskDir);
|
|
130
164
|
if (!fs.existsSync(file)) {
|
|
165
|
+
if (openRows.length === 1) {
|
|
166
|
+
const attempt = parseCommitAttemptStarted(openRows[0]!.note);
|
|
167
|
+
if (attempt && attempt.baseline === currentHead) {
|
|
168
|
+
return outcome('retryable-start', {
|
|
169
|
+
message: 'a structured Commit attempt can be safely reused',
|
|
170
|
+
currentHead,
|
|
171
|
+
attempt
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
131
175
|
if (openRows.length > 0) {
|
|
132
176
|
return outcome('orphaned-start', {
|
|
133
177
|
code: 'COMMIT_FINALIZATION_EVIDENCE_MISSING',
|
|
@@ -192,6 +236,12 @@ function inspectCommitFinalization(
|
|
|
192
236
|
});
|
|
193
237
|
}
|
|
194
238
|
|
|
239
|
+
const commitNote = git(repoRoot, ['show', '-s', '--format=%h%x20%s', intent.committedHead]);
|
|
240
|
+
const matchingDone = commitRows.filter((row) => row.done !== '' && row.note === commitNote);
|
|
241
|
+
const pushOnlyRetry = intent.phase === 'pushed'
|
|
242
|
+
&& intent.baselineHead === intent.committedHead
|
|
243
|
+
&& matchingDone.length === 1
|
|
244
|
+
&& openRows.length === 1;
|
|
195
245
|
const review = findAuthoritativeReviewCodeArtifact(taskDir);
|
|
196
246
|
let needsAnchor = false;
|
|
197
247
|
if (review.ok && review.path) {
|
|
@@ -204,11 +254,20 @@ function inspectCommitFinalization(
|
|
|
204
254
|
const summary = parseReviewSummary(reviewContent);
|
|
205
255
|
const reviewBaseline = extractReviewBaseline(reviewContent);
|
|
206
256
|
const reviewTree = extractReviewedSnapshotTree(reviewContent);
|
|
207
|
-
if (!summary.ok || summary.summary.verdict !== 'Approved' || !commitExists(repoRoot, reviewBaseline) || !
|
|
257
|
+
if (!summary.ok || summary.summary.verdict !== 'Approved' || !commitExists(repoRoot, reviewBaseline) || !parseReviewedGitTree(reviewTree)) {
|
|
208
258
|
return invalid('authoritative review-code evidence is not a valid approved snapshot', currentHead, 'COMMIT_FINALIZATION_EVIDENCE_INVALID', shared);
|
|
209
259
|
}
|
|
210
260
|
const committedTree = git(repoRoot, ['rev-parse', `${intent.committedHead}^{tree}`]);
|
|
211
|
-
|
|
261
|
+
const matchingCurrentReview = reviewBaseline === intent.baselineHead && reviewTree === committedTree;
|
|
262
|
+
if (
|
|
263
|
+
!matchingCurrentReview
|
|
264
|
+
&& !hasMatchingApprovedReview(
|
|
265
|
+
taskDir,
|
|
266
|
+
review.round,
|
|
267
|
+
intent.baselineHead,
|
|
268
|
+
committedTree
|
|
269
|
+
)
|
|
270
|
+
) {
|
|
212
271
|
return outcome('conflict', {
|
|
213
272
|
...shared,
|
|
214
273
|
code: 'COMMIT_FINALIZATION_CONFLICT',
|
|
@@ -217,18 +276,16 @@ function inspectCommitFinalization(
|
|
|
217
276
|
}
|
|
218
277
|
|
|
219
278
|
const anchor = String(metadata.last_reviewed_commit ?? '').trim();
|
|
220
|
-
if (anchor && anchor !== intent.committedHead) {
|
|
279
|
+
if (anchor && anchor !== intent.committedHead && anchor !== intent.baselineHead) {
|
|
221
280
|
return outcome('conflict', {
|
|
222
281
|
...shared,
|
|
223
282
|
code: 'COMMIT_FINALIZATION_CONFLICT',
|
|
224
283
|
message: 'task review anchor conflicts with the committed intent'
|
|
225
284
|
});
|
|
226
285
|
}
|
|
227
|
-
needsAnchor = anchor
|
|
286
|
+
needsAnchor = anchor !== intent.committedHead;
|
|
228
287
|
}
|
|
229
|
-
const
|
|
230
|
-
const matchingDone = commitRows.filter((row) => row.done !== '' && row.note === commitNote);
|
|
231
|
-
const needsLog = matchingDone.length === 0 && openRows.length === 1;
|
|
288
|
+
const needsLog = (matchingDone.length === 0 && openRows.length === 1) || pushOnlyRetry;
|
|
232
289
|
if (!(needsLog || (matchingDone.length === 1 && openRows.length === 0))) {
|
|
233
290
|
return outcome('conflict', {
|
|
234
291
|
...shared,
|
|
@@ -241,6 +298,7 @@ function inspectCommitFinalization(
|
|
|
241
298
|
...shared,
|
|
242
299
|
message: 'commit finalization can be recovered',
|
|
243
300
|
commitNote,
|
|
301
|
+
activityNote: pushOnlyRetry ? `Pushed ${commitNote}` : commitNote,
|
|
244
302
|
needsAnchor,
|
|
245
303
|
needsLog
|
|
246
304
|
});
|
|
@@ -252,7 +310,12 @@ function planCommitTaskFinalization(
|
|
|
252
310
|
agent: string,
|
|
253
311
|
timestamp: string
|
|
254
312
|
): CommitTaskFinalizationPlan {
|
|
255
|
-
if (
|
|
313
|
+
if (
|
|
314
|
+
inspection.disposition !== 'recoverable'
|
|
315
|
+
|| !inspection.committedHead
|
|
316
|
+
|| !inspection.commitNote
|
|
317
|
+
|| !inspection.activityNote
|
|
318
|
+
) {
|
|
256
319
|
throw new Error('commit finalization task mutations require a recoverable inspection');
|
|
257
320
|
}
|
|
258
321
|
const taskContent = fs.readFileSync(path.join(taskDir, 'task.md'), 'utf8');
|
|
@@ -273,7 +336,7 @@ function planCommitTaskFinalization(
|
|
|
273
336
|
time: timestamp,
|
|
274
337
|
step: 'Commit',
|
|
275
338
|
agent,
|
|
276
|
-
note: inspection.
|
|
339
|
+
note: inspection.activityNote
|
|
277
340
|
})
|
|
278
341
|
});
|
|
279
342
|
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
import { syncPlatformComment } from '../platform/issue-comments.ts';
|
|
5
|
+
import { createPlatformIssue, syncPlatformIssue } from '../platform/issues.ts';
|
|
6
|
+
import type { PlatformResult } from '../platform/types.ts';
|
|
7
|
+
import { VERSION } from '../version.ts';
|
|
8
|
+
import { applyWorkflowWarningIntent } from './workflow-warning-intents.ts';
|
|
9
|
+
import { createLocalTask, type TaskCreateCandidateV1 } from './create.ts';
|
|
10
|
+
import { verifyTaskEvent } from './verification.ts';
|
|
11
|
+
|
|
12
|
+
type TaskCreateStatus = 'applied' | 'no-op' | 'degraded' | 'failed' | 'blocked';
|
|
13
|
+
type TaskCreateOperation = { name: string; status: string; reasonCode: string | null };
|
|
14
|
+
type TaskCreateWarning = { code: string; severity: string; action: string };
|
|
15
|
+
type TaskCreateResult = Readonly<{
|
|
16
|
+
status: TaskCreateStatus;
|
|
17
|
+
changed: boolean;
|
|
18
|
+
task: { id: string | null; shortId: string | null };
|
|
19
|
+
issue: { number: number; url: string } | null;
|
|
20
|
+
operations: readonly TaskCreateOperation[];
|
|
21
|
+
warnings: readonly TaskCreateWarning[];
|
|
22
|
+
error: { code: string; message: string; retryable: boolean } | null;
|
|
23
|
+
}>;
|
|
24
|
+
|
|
25
|
+
type CreateTaskOptions = Readonly<{
|
|
26
|
+
repoRoot: string;
|
|
27
|
+
agentInfraVersion?: string;
|
|
28
|
+
dependencies?: Partial<TaskCreateDependencies>;
|
|
29
|
+
}>;
|
|
30
|
+
|
|
31
|
+
type TaskCreateDependencies = Readonly<{
|
|
32
|
+
createIssue: typeof createPlatformIssue;
|
|
33
|
+
syncIssue: typeof syncPlatformIssue;
|
|
34
|
+
syncComment: typeof syncPlatformComment;
|
|
35
|
+
addWarning: typeof applyWorkflowWarningIntent;
|
|
36
|
+
}>;
|
|
37
|
+
|
|
38
|
+
const DEFAULT_DEPENDENCIES: TaskCreateDependencies = {
|
|
39
|
+
createIssue: createPlatformIssue,
|
|
40
|
+
syncIssue: syncPlatformIssue,
|
|
41
|
+
syncComment: syncPlatformComment,
|
|
42
|
+
addWarning: applyWorkflowWarningIntent
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
function verifyCreatedTask(repoRoot: string, taskId: string): { operation: TaskCreateOperation; error: TaskCreateResult['error'] } {
|
|
46
|
+
const verification = verifyTaskEvent(
|
|
47
|
+
{ taskRef: taskId, event: 'create-task.completed' },
|
|
48
|
+
{ repoRoot }
|
|
49
|
+
);
|
|
50
|
+
if (verification.status === 'pass') {
|
|
51
|
+
return { operation: { name: 'task:verify', status: 'pass', reasonCode: null }, error: null };
|
|
52
|
+
}
|
|
53
|
+
const blocked = verification.status === 'blocked';
|
|
54
|
+
return {
|
|
55
|
+
operation: {
|
|
56
|
+
name: 'task:verify', status: verification.status,
|
|
57
|
+
reasonCode: verification.error?.code ?? 'TASK_CREATE_VERIFICATION_FAILED'
|
|
58
|
+
},
|
|
59
|
+
error: {
|
|
60
|
+
code: verification.error?.code ?? 'TASK_CREATE_VERIFICATION_FAILED',
|
|
61
|
+
message: `Created task did not pass the ${verification.event} gate`,
|
|
62
|
+
retryable: blocked
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function platformOperations(prefix: string, operations: readonly { name: string; status: string; reasonCode?: string | null }[]): TaskCreateOperation[] {
|
|
68
|
+
return operations.map((operation) => ({
|
|
69
|
+
name: `${prefix}:${operation.name}`,
|
|
70
|
+
status: operation.status,
|
|
71
|
+
reasonCode: operation.reasonCode ?? null
|
|
72
|
+
}));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function issueFromTask(repoRoot: string, taskId: string): { number: number; url: string } | null {
|
|
76
|
+
const content = fs.readFileSync(path.join(repoRoot, '.agents', 'workspace', 'active', taskId, 'task.md'), 'utf8');
|
|
77
|
+
const match = /^issue_number:\s*(\d+)\s*$/m.exec(content);
|
|
78
|
+
return match ? { number: Number(match[1]), url: '' } : null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function createTask(value: unknown, options: CreateTaskOptions): TaskCreateResult {
|
|
82
|
+
const dependencies = { ...DEFAULT_DEPENDENCIES, ...options.dependencies };
|
|
83
|
+
let candidate: TaskCreateCandidateV1;
|
|
84
|
+
let local;
|
|
85
|
+
try {
|
|
86
|
+
candidate = value as TaskCreateCandidateV1;
|
|
87
|
+
local = createLocalTask(candidate, {
|
|
88
|
+
repoRoot: options.repoRoot,
|
|
89
|
+
agentInfraVersion: options.agentInfraVersion ?? VERSION
|
|
90
|
+
});
|
|
91
|
+
} catch (error) {
|
|
92
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
93
|
+
const code = /^([A-Z][A-Z0-9_]+)/.exec(message)?.[1] ?? 'TASK_CREATE_FAILED';
|
|
94
|
+
return {
|
|
95
|
+
status: 'failed', changed: false, task: { id: null, shortId: null }, issue: null,
|
|
96
|
+
operations: [], warnings: [], error: { code, message, retryable: code === 'TASK_CREATE_LOCK_TIMEOUT' }
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const operations: TaskCreateOperation[] = [{
|
|
101
|
+
name: 'task:local', status: local.status, reasonCode: null
|
|
102
|
+
}];
|
|
103
|
+
let issue = issueFromTask(options.repoRoot, local.task.id);
|
|
104
|
+
const created = dependencies.createIssue(local.task.id, { cwd: options.repoRoot, agent: candidate.agent });
|
|
105
|
+
operations.push(...platformOperations('platform-create', created.operations));
|
|
106
|
+
if (created.issue) issue = { number: created.issue.number, url: created.issue.url };
|
|
107
|
+
|
|
108
|
+
let platformFailure: PlatformResult | null = created.status === 'failed' || created.status === 'blocked' ? created : null;
|
|
109
|
+
if (!platformFailure && (created.task.issueNumber || issue)) {
|
|
110
|
+
const synced = dependencies.syncIssue(local.task.id, {
|
|
111
|
+
cwd: options.repoRoot,
|
|
112
|
+
agent: candidate.agent,
|
|
113
|
+
status: 'waiting-for-triage',
|
|
114
|
+
assignees: 'current',
|
|
115
|
+
milestone: 'initial',
|
|
116
|
+
issueType: true,
|
|
117
|
+
fields: true
|
|
118
|
+
});
|
|
119
|
+
operations.push(...platformOperations('platform-sync', synced.operations));
|
|
120
|
+
if (synced.issue) issue = { number: synced.issue.number, url: synced.issue.url };
|
|
121
|
+
if (synced.status === 'failed' || synced.status === 'blocked') platformFailure = synced;
|
|
122
|
+
if (!platformFailure) {
|
|
123
|
+
const commented = dependencies.syncComment(local.task.id, { cwd: options.repoRoot, kind: 'task', agent: candidate.agent });
|
|
124
|
+
operations.push(...platformOperations('platform-comment', commented.operations));
|
|
125
|
+
if (commented.status === 'failed' || commented.status === 'blocked') platformFailure = commented;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (platformFailure) {
|
|
130
|
+
const error = platformFailure.error ?? { code: 'ISSUE_CREATE_FAILED', message: 'Platform operation failed', retryable: false };
|
|
131
|
+
dependencies.addWarning({
|
|
132
|
+
kind: 'add', taskRef: local.task.id, step: 'create-task', severity: 'ACTION_REQUIRED',
|
|
133
|
+
code: 'ISSUE_CREATE_FAILED', target: 'issue', message: `${error.code}: ${error.message}`,
|
|
134
|
+
action: 'Fix platform authentication or connectivity, then retry Issue creation for this task.'
|
|
135
|
+
}, { repoRoot: options.repoRoot });
|
|
136
|
+
const verified = verifyCreatedTask(options.repoRoot, local.task.id);
|
|
137
|
+
operations.push(verified.operation);
|
|
138
|
+
if (verified.error) return {
|
|
139
|
+
status: verified.error.retryable ? 'blocked' : 'failed', changed: local.changed,
|
|
140
|
+
task: local.task, issue, operations, warnings: [], error: verified.error
|
|
141
|
+
};
|
|
142
|
+
return {
|
|
143
|
+
status: platformFailure.status === 'blocked' ? 'blocked' : 'degraded',
|
|
144
|
+
changed: local.changed,
|
|
145
|
+
task: local.task,
|
|
146
|
+
issue,
|
|
147
|
+
operations,
|
|
148
|
+
warnings: [{ code: 'ISSUE_CREATE_FAILED', severity: 'ACTION_REQUIRED', action: 'Retry Issue creation after fixing the platform failure.' }],
|
|
149
|
+
error
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const verified = verifyCreatedTask(options.repoRoot, local.task.id);
|
|
154
|
+
operations.push(verified.operation);
|
|
155
|
+
if (verified.error) return {
|
|
156
|
+
status: verified.error.retryable ? 'blocked' : 'failed', changed: local.changed,
|
|
157
|
+
task: local.task, issue, operations, warnings: [], error: verified.error
|
|
158
|
+
};
|
|
159
|
+
return {
|
|
160
|
+
status: local.status,
|
|
161
|
+
changed: local.changed,
|
|
162
|
+
task: local.task,
|
|
163
|
+
issue,
|
|
164
|
+
operations,
|
|
165
|
+
warnings: [],
|
|
166
|
+
error: null
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export { createTask };
|
|
171
|
+
export type { CreateTaskOptions, TaskCreateDependencies, TaskCreateOperation, TaskCreateResult, TaskCreateStatus, TaskCreateWarning };
|