@fitlab-ai/agent-infra 0.11.7 → 0.11.9

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.
Files changed (128) hide show
  1. package/README.md +1 -1
  2. package/README.zh-CN.md +1 -1
  3. package/dist/bin/internal-cli.js +8 -2
  4. package/dist/lib/agent-clients/adapters/codex.js +1 -1
  5. package/dist/lib/agent-clients/config.js +11 -3
  6. package/dist/lib/agent-clients/custom-tool-invocations.js +92 -0
  7. package/dist/lib/agent-clients/next-steps.js +1 -1
  8. package/dist/lib/agent-clients/reconcile.js +3 -3
  9. package/dist/lib/agent-clients/schema.js +18 -10
  10. package/dist/lib/defaults.json +6 -3
  11. package/dist/lib/init.js +2 -2
  12. package/dist/lib/internal/agent-client.js +3 -3
  13. package/dist/lib/internal/cli-route-inventory.js +3 -0
  14. package/dist/lib/internal/platform-pr-review.js +20 -3
  15. package/dist/lib/internal/task-operation-registry.js +3 -1
  16. package/dist/lib/internal/task-platform-recovery.js +56 -0
  17. package/dist/lib/merge.js +25 -5
  18. package/dist/lib/platform/completion-backfill.js +2 -1
  19. package/dist/lib/platform/in-label-sync.js +12 -1
  20. package/dist/lib/platform/issue-comments.js +58 -4
  21. package/dist/lib/platform/issues.js +105 -5
  22. package/dist/lib/platform/operation-coordinator.js +74 -0
  23. package/dist/lib/platform/pr-review.js +107 -1
  24. package/dist/lib/platform/pr-summary.js +79 -57
  25. package/dist/lib/platform/pull-requests.js +96 -4
  26. package/dist/lib/sandbox/commands/create.js +6 -3
  27. package/dist/lib/sandbox/commands/ls.js +4 -3
  28. package/dist/lib/sandbox/commands/rm.js +1 -1
  29. package/dist/lib/sandbox/config.js +22 -4
  30. package/dist/lib/sandbox/git-metadata.js +50 -0
  31. package/dist/lib/sandbox/index.js +1 -1
  32. package/dist/lib/sandbox/mounts.js +12 -0
  33. package/dist/lib/sandbox/recovery.js +12 -22
  34. package/dist/lib/sandbox/removal.js +58 -20
  35. package/dist/lib/sandbox/tools.js +31 -7
  36. package/dist/lib/task/activity-intent.js +4 -3
  37. package/dist/lib/task/create-service.js +5 -3
  38. package/dist/lib/task/create.js +22 -5
  39. package/dist/lib/task/finalization-artifacts.js +4 -1
  40. package/dist/lib/task/finalization.js +243 -113
  41. package/dist/lib/task/lifecycle.js +38 -2
  42. package/dist/lib/task/platform-operation-journal.js +152 -0
  43. package/dist/lib/task/platform-operation-recovery.js +459 -0
  44. package/dist/lib/task/verification.js +0 -1
  45. package/dist/lib/task/workflow-warning-intents.js +7 -1
  46. package/dist/lib/update.js +2 -2
  47. package/dist/lifecycle-build-manifest.json +3 -3
  48. package/lib/agent-clients/adapters/codex.ts +1 -1
  49. package/lib/agent-clients/config.ts +9 -3
  50. package/lib/agent-clients/custom-tool-invocations.ts +129 -0
  51. package/lib/agent-clients/next-steps.ts +3 -3
  52. package/lib/agent-clients/reconcile.ts +3 -3
  53. package/lib/agent-clients/schema.ts +18 -10
  54. package/lib/defaults.json +6 -3
  55. package/lib/init.ts +2 -2
  56. package/lib/internal/agent-client.ts +3 -3
  57. package/lib/internal/cli-route-inventory.ts +2 -0
  58. package/lib/internal/platform-pr-review.ts +14 -3
  59. package/lib/internal/task-operation-registry.ts +3 -1
  60. package/lib/internal/task-platform-recovery.ts +53 -0
  61. package/lib/merge.ts +26 -6
  62. package/lib/platform/completion-backfill.ts +2 -1
  63. package/lib/platform/in-label-sync.ts +12 -0
  64. package/lib/platform/issue-comments.ts +59 -3
  65. package/lib/platform/issues.ts +103 -5
  66. package/lib/platform/operation-coordinator.ts +96 -0
  67. package/lib/platform/pr-review.ts +113 -1
  68. package/lib/platform/pr-summary.ts +22 -1
  69. package/lib/platform/pull-requests.ts +105 -5
  70. package/lib/sandbox/commands/create.ts +7 -8
  71. package/lib/sandbox/commands/ls.ts +4 -3
  72. package/lib/sandbox/commands/rm.ts +1 -1
  73. package/lib/sandbox/config.ts +23 -5
  74. package/lib/sandbox/git-metadata.ts +66 -0
  75. package/lib/sandbox/index.ts +1 -1
  76. package/lib/sandbox/mounts.ts +14 -0
  77. package/lib/sandbox/recovery.ts +13 -25
  78. package/lib/sandbox/removal.ts +57 -19
  79. package/lib/sandbox/tool-types.ts +2 -1
  80. package/lib/sandbox/tools.ts +38 -7
  81. package/lib/task/activity-intent.ts +4 -3
  82. package/lib/task/create-service.ts +5 -3
  83. package/lib/task/create.ts +17 -5
  84. package/lib/task/finalization-artifacts.ts +4 -1
  85. package/lib/task/finalization.ts +242 -106
  86. package/lib/task/lifecycle.ts +35 -3
  87. package/lib/task/platform-operation-journal.ts +230 -0
  88. package/lib/task/platform-operation-recovery.ts +435 -0
  89. package/lib/task/verification.ts +1 -2
  90. package/lib/task/workflow-warning-intents.ts +9 -1
  91. package/lib/update.ts +2 -3
  92. package/package.json +1 -1
  93. package/templates/.agents/README.en.md +20 -66
  94. package/templates/.agents/README.zh-CN.md +19 -65
  95. package/templates/.agents/rules/README.en.md +1 -1
  96. package/templates/.agents/rules/README.zh-CN.md +1 -1
  97. package/templates/.agents/rules/next-step-output.en.md +1 -1
  98. package/templates/.agents/rules/next-step-output.zh-CN.md +1 -1
  99. package/templates/.agents/rules/task-management.en.md +12 -0
  100. package/templates/.agents/rules/task-management.zh-CN.md +12 -0
  101. package/templates/.agents/scripts/lib/agent-infra-package.js +0 -7
  102. package/templates/.agents/skills/cancel-task/SKILL.en.md +4 -2
  103. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +4 -2
  104. package/templates/.agents/skills/code-task/SKILL.en.md +2 -2
  105. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +2 -2
  106. package/templates/.agents/skills/commit/SKILL.en.md +1 -1
  107. package/templates/.agents/skills/commit/SKILL.zh-CN.md +1 -1
  108. package/templates/.agents/skills/complete-task/SKILL.en.md +2 -0
  109. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +2 -0
  110. package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +1 -1
  111. package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +1 -1
  112. package/templates/.agents/skills/plan-task/config/verify.en.json +1 -1
  113. package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +1 -1
  114. package/templates/.agents/skills/review-code/SKILL.en.md +1 -1
  115. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +1 -1
  116. package/templates/.agents/skills/review-code/reference/output-templates.en.md +15 -6
  117. package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +15 -6
  118. package/templates/.agents/skills/review-pr/SKILL.en.md +11 -11
  119. package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +11 -11
  120. package/templates/.agents/skills/review-pr/reference/output-guidance.en.md +3 -3
  121. package/templates/.agents/skills/review-pr/reference/output-guidance.zh-CN.md +3 -3
  122. package/templates/.agents/skills/review-pr/reference/report-template.en.md +4 -0
  123. package/templates/.agents/skills/review-pr/reference/report-template.zh-CN.md +4 -0
  124. package/templates/.agents/skills/update-agent-infra/SKILL.en.md +1 -1
  125. package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +1 -1
  126. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +21 -274
  127. package/dist/lib/agent-clients/custom-tuis.js +0 -99
  128. package/lib/agent-clients/custom-tuis.ts +0 -149
@@ -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 };