@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.
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- 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/agent-clients/custom-tool-invocations.js +92 -0
- package/dist/lib/agent-clients/next-steps.js +1 -1
- package/dist/lib/agent-clients/reconcile.js +3 -3
- package/dist/lib/agent-clients/schema.js +18 -10
- package/dist/lib/defaults.json +6 -3
- package/dist/lib/init.js +2 -2
- package/dist/lib/internal/agent-client.js +3 -3
- 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 +96 -4
- 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/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 +12 -22
- package/dist/lib/sandbox/removal.js +58 -20
- package/dist/lib/sandbox/tools.js +31 -7
- package/dist/lib/task/activity-intent.js +4 -3
- package/dist/lib/task/create-service.js +5 -3
- package/dist/lib/task/create.js +22 -5
- package/dist/lib/task/finalization-artifacts.js +4 -1
- package/dist/lib/task/finalization.js +243 -113
- package/dist/lib/task/lifecycle.js +38 -2
- package/dist/lib/task/platform-operation-journal.js +152 -0
- package/dist/lib/task/platform-operation-recovery.js +459 -0
- package/dist/lib/task/verification.js +0 -1
- package/dist/lib/task/workflow-warning-intents.js +7 -1
- package/dist/lib/update.js +2 -2
- 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/agent-clients/custom-tool-invocations.ts +129 -0
- package/lib/agent-clients/next-steps.ts +3 -3
- package/lib/agent-clients/reconcile.ts +3 -3
- package/lib/agent-clients/schema.ts +18 -10
- package/lib/defaults.json +6 -3
- package/lib/init.ts +2 -2
- package/lib/internal/agent-client.ts +3 -3
- 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 +105 -5
- 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/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 -25
- package/lib/sandbox/removal.ts +57 -19
- package/lib/sandbox/tool-types.ts +2 -1
- package/lib/sandbox/tools.ts +38 -7
- package/lib/task/activity-intent.ts +4 -3
- package/lib/task/create-service.ts +5 -3
- package/lib/task/create.ts +17 -5
- package/lib/task/finalization-artifacts.ts +4 -1
- package/lib/task/finalization.ts +242 -106
- package/lib/task/lifecycle.ts +35 -3
- package/lib/task/platform-operation-journal.ts +230 -0
- package/lib/task/platform-operation-recovery.ts +435 -0
- package/lib/task/verification.ts +1 -2
- package/lib/task/workflow-warning-intents.ts +9 -1
- package/lib/update.ts +2 -3
- package/package.json +1 -1
- package/templates/.agents/README.en.md +20 -66
- package/templates/.agents/README.zh-CN.md +19 -65
- package/templates/.agents/rules/README.en.md +1 -1
- package/templates/.agents/rules/README.zh-CN.md +1 -1
- package/templates/.agents/rules/next-step-output.en.md +1 -1
- package/templates/.agents/rules/next-step-output.zh-CN.md +1 -1
- package/templates/.agents/rules/task-management.en.md +12 -0
- package/templates/.agents/rules/task-management.zh-CN.md +12 -0
- package/templates/.agents/scripts/lib/agent-infra-package.js +0 -7
- package/templates/.agents/skills/cancel-task/SKILL.en.md +4 -2
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +4 -2
- package/templates/.agents/skills/code-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +2 -2
- 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-task/SKILL.en.md +2 -0
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +2 -0
- package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +1 -1
- package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +1 -1
- 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-code/SKILL.en.md +1 -1
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/review-code/reference/output-templates.en.md +15 -6
- package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +15 -6
- package/templates/.agents/skills/review-pr/SKILL.en.md +11 -11
- package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +11 -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/update-agent-infra/SKILL.en.md +1 -1
- package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +21 -274
- package/dist/lib/agent-clients/custom-tuis.js +0 -99
- package/lib/agent-clients/custom-tuis.ts +0 -149
package/lib/sandbox/tools.ts
CHANGED
|
@@ -85,6 +85,16 @@ function asOptionalNonEmptyString(value: unknown, field: string, context: string
|
|
|
85
85
|
return value;
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
+
function asOptionalBoolean(value: unknown, field: string, context: string): boolean | undefined {
|
|
89
|
+
if (value === undefined) {
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
if (typeof value !== 'boolean') {
|
|
93
|
+
throw new Error(`${context}: field "${field}" must be a boolean when provided`);
|
|
94
|
+
}
|
|
95
|
+
return value;
|
|
96
|
+
}
|
|
97
|
+
|
|
88
98
|
function asStringRecord(value: unknown, field: string, context: string): Record<string, string> | undefined {
|
|
89
99
|
if (value === undefined) {
|
|
90
100
|
return undefined;
|
|
@@ -193,18 +203,20 @@ function parseTmpfs(value: unknown, context: string): SandboxTool['tmpfs'] {
|
|
|
193
203
|
if (!isPlainObject(value)) {
|
|
194
204
|
throw new Error(`${context}: field "tmpfs" must be an object when provided`);
|
|
195
205
|
}
|
|
206
|
+
const exec = asOptionalBoolean(value.exec, 'tmpfs.exec', context);
|
|
196
207
|
return {
|
|
197
208
|
size: asOptionalNonEmptyString(value.size, 'tmpfs.size', context),
|
|
198
|
-
seed: asStringArray(value.seed, 'tmpfs.seed', context)
|
|
209
|
+
seed: asStringArray(value.seed, 'tmpfs.seed', context),
|
|
210
|
+
...(exec === undefined ? {} : { exec })
|
|
199
211
|
};
|
|
200
212
|
}
|
|
201
213
|
|
|
202
214
|
export function parseCustomTool(
|
|
203
215
|
entry: unknown,
|
|
204
216
|
index: number,
|
|
205
|
-
options: { home: string }
|
|
217
|
+
options: { home: string },
|
|
218
|
+
context = `customTools[${index}]`
|
|
206
219
|
): SandboxTool {
|
|
207
|
-
const context = `customTools[${index}]`;
|
|
208
220
|
if (!isPlainObject(entry)) {
|
|
209
221
|
throw new Error(`${context} must be an object`);
|
|
210
222
|
}
|
|
@@ -219,6 +231,7 @@ export function parseCustomTool(
|
|
|
219
231
|
if (!containerMount.startsWith('/')) {
|
|
220
232
|
throw new Error(`${context}: field "containerMount" must be an absolute path`);
|
|
221
233
|
}
|
|
234
|
+
const invoke = asOptionalNonEmptyString(entry.invoke, 'invoke', context);
|
|
222
235
|
|
|
223
236
|
const tool: SandboxTool = {
|
|
224
237
|
id,
|
|
@@ -229,6 +242,7 @@ export function parseCustomTool(
|
|
|
229
242
|
versionCmd: asOptionalNonEmptyString(entry.versionCmd, 'versionCmd', context) ?? `which ${id}`,
|
|
230
243
|
setupHint: asOptionalNonEmptyString(entry.setupHint, 'setupHint', context)
|
|
231
244
|
?? `Run \`${id}\` inside the container to set up.`,
|
|
245
|
+
...(invoke === undefined ? {} : { invoke }),
|
|
232
246
|
envVars: asStringRecord(entry.envVars, 'envVars', context),
|
|
233
247
|
hostPreSeedFiles: parseHostPreSeedFiles(entry.hostPreSeedFiles, context),
|
|
234
248
|
hostPreSeedDirs: parseHostPreSeedDirs(entry.hostPreSeedDirs, context),
|
|
@@ -243,13 +257,30 @@ export function parseCustomTool(
|
|
|
243
257
|
}
|
|
244
258
|
|
|
245
259
|
export function parseCustomTools(value: unknown, options: { home: string }): SandboxTool[] {
|
|
246
|
-
if (value === undefined
|
|
260
|
+
if (value === undefined) {
|
|
247
261
|
return [];
|
|
248
262
|
}
|
|
249
|
-
if (!
|
|
250
|
-
throw new Error('sandbox
|
|
263
|
+
if (!isPlainObject(value)) {
|
|
264
|
+
throw new Error('sandbox.tools.definitions must be an object');
|
|
251
265
|
}
|
|
252
|
-
return value.map((
|
|
266
|
+
return Object.entries(value).map(([id, definition], index) => {
|
|
267
|
+
const context = `sandbox.tools.definitions.${id}`;
|
|
268
|
+
if (!TOOL_ID_PATTERN.test(id)) {
|
|
269
|
+
throw new Error(`${context}: tool id must match ${TOOL_ID_PATTERN.source}`);
|
|
270
|
+
}
|
|
271
|
+
if (!isPlainObject(definition)) {
|
|
272
|
+
throw new Error(`${context} must be an object`);
|
|
273
|
+
}
|
|
274
|
+
if (Object.hasOwn(definition, 'id')) {
|
|
275
|
+
throw new Error(`${context}.id is not allowed; the definition key is the tool id`);
|
|
276
|
+
}
|
|
277
|
+
return parseCustomTool(
|
|
278
|
+
{ ...definition, id },
|
|
279
|
+
index,
|
|
280
|
+
options,
|
|
281
|
+
context
|
|
282
|
+
);
|
|
283
|
+
});
|
|
253
284
|
}
|
|
254
285
|
|
|
255
286
|
export function resolveTools(config: ToolsConfig): SandboxTool[] {
|
|
@@ -108,6 +108,7 @@ const REVIEW_ACTION_RE = /^Review PR \(Round ([1-9]\d*)\)( \[started\])?$/;
|
|
|
108
108
|
const VERDICTS: ReadonlySet<string> = new Set(['approved', 'changes-requested', 'commented']);
|
|
109
109
|
const OUTCOMES: ReadonlySet<string> = new Set(['aborted', 'superseded']);
|
|
110
110
|
const SUCCESS_STATUSES: ReadonlySet<PrReviewArtifactStatus> = new Set(['applied', 'no-op']);
|
|
111
|
+
const LOCAL_COMPLETION_STATUSES: ReadonlySet<PrReviewArtifactStatus> = new Set(['applied', 'no-op', 'blocked', 'failed']);
|
|
111
112
|
const ARTIFACT_STATUSES: ReadonlySet<string> = new Set([
|
|
112
113
|
'pending', 'applied', 'no-op', 'blocked', 'failed', 'aborted', 'superseded'
|
|
113
114
|
]);
|
|
@@ -209,7 +210,7 @@ function readSnapshot(taskRef: string, repoRoot?: string):
|
|
|
209
210
|
if (terminal.length === 1) {
|
|
210
211
|
const note = terminal[0]!.note;
|
|
211
212
|
const terminalMatchesStatus =
|
|
212
|
-
(note.startsWith('Verdict: ') &&
|
|
213
|
+
(note.startsWith('Verdict: ') && LOCAL_COMPLETION_STATUSES.has(parsed.status))
|
|
213
214
|
|| (note.startsWith('Outcome: Aborted, ') && parsed.status === 'aborted')
|
|
214
215
|
|| (note.startsWith('Outcome: Superseded, ') && parsed.status === 'superseded')
|
|
215
216
|
|| (note.startsWith('receipt ') && SUCCESS_STATUSES.has(parsed.status));
|
|
@@ -338,8 +339,8 @@ function applyLocked(intent: PrReviewActivityIntent, normalizedAgent: string, op
|
|
|
338
339
|
if (roundState.startedAgent === null) {
|
|
339
340
|
return failed(intent, 'ACTIVITY_STATE_CONFLICT', `${base} has no matching started row`, state.taskId);
|
|
340
341
|
}
|
|
341
|
-
if (intent.kind === 'pr-review-complete' && !
|
|
342
|
-
return failed(intent, 'ACTIVITY_STATE_CONFLICT', `${intent.artifact} must
|
|
342
|
+
if (intent.kind === 'pr-review-complete' && !LOCAL_COMPLETION_STATUSES.has(roundState.status)) {
|
|
343
|
+
return failed(intent, 'ACTIVITY_STATE_CONFLICT', `${intent.artifact} must record a publication outcome before completion`, state.taskId);
|
|
343
344
|
}
|
|
344
345
|
if (intent.kind === 'pr-review-terminate' && roundState.status !== intent.outcome) {
|
|
345
346
|
return failed(intent, 'ACTIVITY_STATE_CONFLICT', `${intent.artifact} status must match outcome ${intent.outcome}`, state.taskId);
|
|
@@ -4,6 +4,7 @@ import type { PlatformResult } from '../platform/types.ts';
|
|
|
4
4
|
import { VERSION } from '../version.ts';
|
|
5
5
|
import { applyWorkflowWarningIntent } from './workflow-warning-intents.ts';
|
|
6
6
|
import { createLocalTask, type TaskCreateCandidateV1 } from './create.ts';
|
|
7
|
+
import { TaskExecutionLockError } from './task-execution-lock.ts';
|
|
7
8
|
import { verifyTaskEvent } from './verification.ts';
|
|
8
9
|
|
|
9
10
|
type TaskCreateStatus = 'applied' | 'no-op' | 'degraded' | 'failed' | 'blocked';
|
|
@@ -200,8 +201,9 @@ async function createTask(value: unknown, options: CreateTaskOptions): Promise<T
|
|
|
200
201
|
});
|
|
201
202
|
} catch (error) {
|
|
202
203
|
const message = error instanceof Error ? error.message : String(error);
|
|
203
|
-
const
|
|
204
|
-
|
|
204
|
+
const lockBusy = error instanceof TaskExecutionLockError && error.code === 'ORCHESTRATION_LOCK_BUSY';
|
|
205
|
+
const code = lockBusy ? error.code : /^([A-Z][A-Z0-9_]+)/.exec(message)?.[1] ?? 'TASK_CREATE_FAILED';
|
|
206
|
+
return taskCreateFailure({ code, message, retryable: lockBusy || code === 'TASK_CREATE_LOCK_TIMEOUT' });
|
|
205
207
|
}
|
|
206
208
|
|
|
207
209
|
const operations: TaskCreateOperation[] = [{
|
|
@@ -250,7 +252,7 @@ async function createTask(value: unknown, options: CreateTaskOptions): Promise<T
|
|
|
250
252
|
task: local.task, issue, operations, warnings: [], error: verified.error
|
|
251
253
|
};
|
|
252
254
|
return {
|
|
253
|
-
status:
|
|
255
|
+
status: 'degraded',
|
|
254
256
|
changed: local.changed,
|
|
255
257
|
task: local.task,
|
|
256
258
|
issue,
|
package/lib/task/create.ts
CHANGED
|
@@ -5,7 +5,7 @@ import path from 'node:path';
|
|
|
5
5
|
|
|
6
6
|
import { isValidAgentInfraVersion, VERSION } from '../version.ts';
|
|
7
7
|
import { loadShortIdByTaskId, mutateShortIdRegistry } from './short-id.ts';
|
|
8
|
-
import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.ts';
|
|
8
|
+
import { TaskExecutionLockError, withRepositoryMutationLock, withTaskExecutionLock } from './task-execution-lock.ts';
|
|
9
9
|
import { readDeliveryDefaults, validateBaseRef, validateRemote } from './delivery-target.ts';
|
|
10
10
|
import { buildUnboundFact, encodePrDeliveryFact } from './pr-delivery-fact.ts';
|
|
11
11
|
import { CANDIDATE_COLUMNS, CONSTRAINT_COLUMNS, parseTaskQualification } from './qualification-audit.ts';
|
|
@@ -419,7 +419,7 @@ function createLocalTask(value: unknown, options: LocalTaskCreateOptions): Local
|
|
|
419
419
|
const canonical = canonicalTaskCreateCandidate(candidate);
|
|
420
420
|
const candidateDigest = sha256(canonical);
|
|
421
421
|
const keyDigest = sha256(candidate.idempotencyKey);
|
|
422
|
-
|
|
422
|
+
function createUnderRepositoryLock(): LocalTaskCreateResult {
|
|
423
423
|
const recovered = recoverPublishedTask(repoRoot, keyDigest, candidateDigest);
|
|
424
424
|
if (recovered) {
|
|
425
425
|
return { status: 'no-op', changed: false, task: { id: recovered.taskId, shortId: recovered.shortId, state: recovered.state } };
|
|
@@ -446,12 +446,24 @@ function createLocalTask(value: unknown, options: LocalTaskCreateOptions): Local
|
|
|
446
446
|
fs.writeFileSync(path.join(temporary, 'task.md'), rendered, { flag: 'wx', mode: 0o600 });
|
|
447
447
|
const destination = path.join(activeRoot, taskId);
|
|
448
448
|
fs.renameSync(temporary, destination);
|
|
449
|
-
let shortId: string;
|
|
450
449
|
const allocated = mutateShortIdRegistry(repoRoot, taskId, 'alloc');
|
|
451
450
|
if (!allocated.shortId) throw new Error('TASK_CREATE_SHORT_ID_FAILED');
|
|
452
|
-
shortId
|
|
453
|
-
|
|
451
|
+
return { status: 'applied', changed: true, task: { id: taskId, shortId: allocated.shortId, state: 'active' } };
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
return withCreateLock(repoRoot, projectConfig.project, () => {
|
|
455
|
+
let callbackError: unknown;
|
|
456
|
+
try {
|
|
457
|
+
return withRepositoryMutationLock(repoRoot, () => {
|
|
458
|
+
try { return createUnderRepositoryLock(); }
|
|
459
|
+
catch (error) { callbackError = error; throw error; }
|
|
460
|
+
});
|
|
461
|
+
} catch (error) {
|
|
462
|
+
if (callbackError !== undefined) throw callbackError;
|
|
463
|
+
throw error;
|
|
464
|
+
}
|
|
454
465
|
});
|
|
466
|
+
|
|
455
467
|
}
|
|
456
468
|
|
|
457
469
|
export {
|
|
@@ -15,7 +15,10 @@ const COMPLETION_BACKFILL_FAMILIES = [
|
|
|
15
15
|
'plan',
|
|
16
16
|
'review-plan',
|
|
17
17
|
'code',
|
|
18
|
-
'review-code'
|
|
18
|
+
'review-code',
|
|
19
|
+
'manual-validation',
|
|
20
|
+
'validation-run',
|
|
21
|
+
'pr-review'
|
|
19
22
|
] as const satisfies readonly ArtifactFamily[];
|
|
20
23
|
|
|
21
24
|
type CompletionArtifactResult = {
|