@fitlab-ai/agent-infra 0.11.15 → 0.12.1
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/bin/cli.ts +1 -1
- package/dist/bin/cli.js +1 -1
- package/dist/bin/internal-cli.js +6 -1
- package/dist/lib/agent-clients/adapters/codex-lifecycle/recovery.js +6 -6
- package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +8 -18
- package/dist/lib/agent-clients/adapters/codex-orchestration.js +1 -1
- package/dist/lib/decide.js +2 -7
- package/dist/lib/internal/cli-route-inventory.js +3 -0
- package/dist/lib/internal/manual-validation.js +19 -33
- package/dist/lib/internal/platform-comment.js +59 -9
- package/dist/lib/internal/task-activity.js +1 -12
- package/dist/lib/internal/task-event.js +1 -12
- package/dist/lib/internal/task-ledger.js +1 -12
- package/dist/lib/internal/task-operation-registry.js +2 -1
- package/dist/lib/internal/task-qualification.js +6 -15
- package/dist/lib/internal/task-review.js +3 -16
- package/dist/lib/internal/task-short-id.js +2 -1
- package/dist/lib/internal/task-verify.js +1 -11
- package/dist/lib/internal/task-warning.js +1 -12
- package/dist/lib/internal/task-workspace.js +43 -0
- package/dist/lib/merge.js +4 -3
- package/dist/lib/platform/issue-comments.js +373 -267
- package/dist/lib/platform/issue-summary-content.js +112 -0
- package/dist/lib/platform/pr-change-report.js +175 -26
- package/dist/lib/platform/pr-summary-content.js +54 -0
- package/dist/lib/platform/pr-summary.js +321 -296
- package/dist/lib/platform/pull-requests.js +3 -15
- package/dist/lib/platform/summary-content-pipeline.js +18 -0
- package/dist/lib/pr-review/evidence-grading.js +3 -2
- package/dist/lib/process-data/index.js +5 -4
- package/dist/lib/process-data/sources.js +6 -0
- package/dist/lib/run/index.js +3 -2
- package/dist/lib/sandbox/commands/create.js +5 -1
- package/dist/lib/sandbox/commands/enter.js +1 -1
- package/dist/lib/sandbox/commands/list-running.js +2 -1
- package/dist/lib/sandbox/control/client.js +8 -13
- package/dist/lib/sandbox/control/completed-reentry.js +2 -1
- package/dist/lib/sandbox/control/task-view.js +4 -2
- package/dist/lib/sandbox/mounts.js +4 -4
- package/dist/lib/sandbox/recovery.js +26 -16
- package/dist/lib/sandbox/removal.js +62 -12
- package/dist/lib/sandbox/task-resolver.js +9 -6
- package/dist/lib/sandbox/workspace-identity.js +6 -5
- package/dist/lib/sandbox/workspace-view.js +29 -22
- package/dist/lib/task/activity-intent.js +2 -12
- package/dist/lib/task/artifact-lifecycle.js +17 -11
- package/dist/lib/task/artifact-operations.js +41 -49
- package/dist/lib/task/capabilities.js +2 -2
- package/dist/lib/task/commands/ls.js +4 -3
- package/dist/lib/task/commands/status.js +1 -1
- package/dist/lib/task/commit-operation.js +6 -6
- package/dist/lib/task/control-authority.js +32 -32
- package/dist/lib/task/create-service.js +2 -4
- package/dist/lib/task/create.js +12 -65
- package/dist/lib/task/delegation-receipts.js +1 -1
- package/dist/lib/task/delivery.js +2 -8
- package/dist/lib/task/events.js +5 -31
- package/dist/lib/task/finalization.js +33 -29
- package/dist/lib/task/lifecycle.js +9 -48
- package/dist/lib/task/local-artifact-finalization.js +1 -2
- package/dist/lib/task/orchestration.js +24 -57
- package/dist/lib/task/platform-operation-recovery.js +87 -88
- package/dist/lib/task/resolve-ref.js +17 -12
- package/dist/lib/task/review-finalization.js +2 -35
- package/dist/lib/task/review-identity.js +3 -1
- package/dist/lib/task/short-id.js +10 -8
- package/dist/lib/task/verification-engine.js +19 -58
- package/dist/lib/task/workspace-root.js +59 -0
- package/dist/lib/task/workspace-snapshot.js +4 -3
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-clients/adapters/codex-lifecycle/recovery.ts +6 -6
- package/lib/agent-clients/adapters/codex-lifecycle/store.ts +8 -34
- package/lib/agent-clients/adapters/codex-orchestration.ts +1 -1
- package/lib/decide.ts +2 -7
- package/lib/internal/cli-route-inventory.ts +2 -0
- package/lib/internal/manual-validation.ts +20 -37
- package/lib/internal/platform-comment.ts +49 -6
- package/lib/internal/task-activity.ts +1 -10
- package/lib/internal/task-event.ts +1 -10
- package/lib/internal/task-ledger.ts +1 -10
- package/lib/internal/task-operation-registry.ts +2 -1
- package/lib/internal/task-qualification.ts +5 -11
- package/lib/internal/task-review.ts +3 -14
- package/lib/internal/task-short-id.ts +2 -1
- package/lib/internal/task-verify.ts +1 -9
- package/lib/internal/task-warning.ts +1 -10
- package/lib/internal/task-workspace.ts +39 -0
- package/lib/merge.ts +4 -3
- package/lib/platform/issue-comments.ts +478 -280
- package/lib/platform/issue-summary-content.ts +132 -0
- package/lib/platform/pr-change-report.ts +165 -20
- package/lib/platform/pr-summary-content.ts +60 -0
- package/lib/platform/pr-summary.ts +367 -269
- package/lib/platform/pull-requests.ts +3 -24
- package/lib/platform/summary-content-pipeline.ts +23 -0
- package/lib/pr-review/evidence-grading.ts +3 -2
- package/lib/process-data/index.ts +4 -3
- package/lib/process-data/sources.ts +6 -0
- package/lib/run/index.ts +3 -2
- package/lib/sandbox/commands/create.ts +5 -1
- package/lib/sandbox/commands/enter.ts +1 -1
- package/lib/sandbox/commands/list-running.ts +2 -1
- package/lib/sandbox/control/client.ts +7 -12
- package/lib/sandbox/control/completed-reentry.ts +2 -1
- package/lib/sandbox/control/task-view.ts +4 -2
- package/lib/sandbox/mounts.ts +4 -4
- package/lib/sandbox/recovery.ts +26 -18
- package/lib/sandbox/removal.ts +60 -27
- package/lib/sandbox/task-resolver.ts +9 -6
- package/lib/sandbox/workspace-identity.ts +6 -5
- package/lib/sandbox/workspace-view.ts +28 -20
- package/lib/task/activity-intent.ts +3 -17
- package/lib/task/artifact-lifecycle.ts +39 -11
- package/lib/task/artifact-operations.ts +28 -35
- package/lib/task/capabilities.ts +2 -2
- package/lib/task/commands/ls.ts +4 -3
- package/lib/task/commands/status.ts +1 -1
- package/lib/task/commit-operation.ts +5 -6
- package/lib/task/control-authority.ts +29 -35
- package/lib/task/create-service.ts +2 -4
- package/lib/task/create.ts +10 -51
- package/lib/task/delegation-receipts.ts +1 -1
- package/lib/task/delivery.ts +2 -10
- package/lib/task/events.ts +5 -32
- package/lib/task/finalization.ts +27 -30
- package/lib/task/lifecycle.ts +9 -50
- package/lib/task/local-artifact-finalization.ts +1 -4
- package/lib/task/orchestration.ts +31 -80
- package/lib/task/platform-operation-recovery.ts +70 -82
- package/lib/task/resolve-ref.ts +18 -12
- package/lib/task/review-finalization.ts +2 -52
- package/lib/task/review-identity.ts +3 -1
- package/lib/task/short-id.ts +10 -8
- package/lib/task/verification-engine.ts +20 -62
- package/lib/task/workspace-root.ts +78 -0
- package/lib/task/workspace-snapshot.ts +4 -3
- package/package.json +1 -1
- package/templates/.agents/QUICKSTART.en.md +19 -8
- package/templates/.agents/QUICKSTART.zh-CN.md +19 -8
- package/templates/.agents/README.en.md +10 -15
- package/templates/.agents/README.zh-CN.md +9 -13
- package/templates/.agents/rules/issue-sync.en.md +1 -1
- package/templates/.agents/rules/issue-sync.zh-CN.md +1 -1
- 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/next-step-output.en.md +2 -2
- package/templates/.agents/rules/next-step-output.zh-CN.md +2 -2
- package/templates/.agents/rules/pr-sync.en.md +5 -3
- package/templates/.agents/rules/pr-sync.zh-CN.md +7 -10
- package/templates/.agents/rules/release-commands.en.md +3 -0
- package/templates/.agents/rules/release-commands.zh-CN.md +3 -0
- package/templates/.agents/rules/task-management.en.md +0 -2
- package/templates/.agents/rules/task-management.zh-CN.md +0 -2
- package/templates/.agents/rules/task-short-id.en.md +2 -2
- package/templates/.agents/rules/task-short-id.zh-CN.md +2 -2
- package/templates/.agents/scripts/task-short-id.js +2 -8
- package/templates/.agents/skills/analyze-task/SKILL.en.md +6 -6
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +6 -6
- package/templates/.agents/skills/analyze-task/config/verify.en.json +0 -1
- package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +0 -1
- package/templates/.agents/skills/archive-tasks/SKILL.en.md +5 -5
- package/templates/.agents/skills/archive-tasks/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/archive-tasks/scripts/archive-tasks.sh +10 -2
- package/templates/.agents/skills/block-task/SKILL.en.md +4 -4
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/block-task/config/verify.json +0 -1
- package/templates/.agents/skills/cancel-task/SKILL.en.md +7 -7
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +7 -7
- package/templates/.agents/skills/cancel-task/config/verify.json +0 -1
- package/templates/.agents/skills/code-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/code-task/config/verify.en.json +0 -1
- package/templates/.agents/skills/code-task/config/verify.zh-CN.json +0 -1
- package/templates/.agents/skills/code-task/reference/output-template.en.md +1 -1
- package/templates/.agents/skills/code-task/reference/output-template.zh-CN.md +1 -1
- 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/commit/config/verify.json +0 -1
- package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +2 -2
- package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +2 -2
- package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +2 -2
- package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +2 -2
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +2 -2
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +0 -1
- package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +0 -1
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +3 -3
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +3 -3
- package/templates/.agents/skills/complete-task/SKILL.en.md +15 -30
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +17 -32
- package/templates/.agents/skills/complete-task/config/verify.en.json +0 -1
- package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +0 -1
- package/templates/.agents/skills/create-pr/SKILL.en.md +3 -3
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/create-pr/config/verify.json +0 -1
- package/templates/.agents/skills/create-pr/reference/change-report.en.md +10 -3
- package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +10 -3
- package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +3 -3
- package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +3 -3
- package/templates/.agents/skills/create-release-note/SKILL.en.md +2 -2
- package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/create-task/SKILL.en.md +4 -22
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +4 -22
- package/templates/.agents/skills/create-task/config/verify.json +0 -1
- package/templates/.agents/skills/import-codescan/SKILL.en.md +1 -1
- package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/import-codescan/config/verify.json +0 -1
- package/templates/.agents/skills/import-dependabot/SKILL.en.md +3 -3
- package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/import-dependabot/config/verify.json +0 -1
- package/templates/.agents/skills/import-issue/SKILL.en.md +5 -5
- package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/import-issue/config/verify.json +0 -1
- package/templates/.agents/skills/plan-task/SKILL.en.md +5 -5
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/plan-task/config/verify.en.json +0 -1
- package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +0 -1
- package/templates/.agents/skills/restore-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/restore-task/config/verify.json +0 -1
- package/templates/.agents/skills/review-analysis/SKILL.en.md +1 -1
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/review-analysis/config/verify.en.json +0 -1
- package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +0 -1
- package/templates/.agents/skills/review-analysis/reference/output-templates.en.md +5 -5
- package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +5 -5
- package/templates/.agents/skills/review-code/SKILL.en.md +2 -2
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/review-code/reference/output-templates.en.md +4 -4
- package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +4 -4
- package/templates/.agents/skills/review-plan/SKILL.en.md +1 -1
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/review-plan/config/verify.en.json +0 -1
- package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +0 -1
- package/templates/.agents/skills/review-plan/reference/output-templates.en.md +5 -5
- package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +5 -5
- package/templates/.agents/skills/review-pr/SKILL.en.md +2 -2
- package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/review-pr/config/verify.en.json +0 -1
- package/templates/.agents/skills/review-pr/config/verify.zh-CN.json +0 -1
- package/templates/.agents/skills/review-pr/reference/output-guidance.en.md +1 -1
- package/templates/.agents/skills/review-pr/reference/output-guidance.zh-CN.md +1 -1
- package/templates/.agents/skills/run-manual-validation/config/verify.en.json +0 -1
- package/templates/.agents/skills/run-manual-validation/config/verify.zh-CN.json +0 -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/test/SKILL.en.md +0 -3
- package/templates/.agents/skills/test/SKILL.zh-CN.md +0 -3
- package/templates/.agents/skills/test-integration/SKILL.en.md +0 -3
- package/templates/.agents/skills/test-integration/SKILL.zh-CN.md +0 -3
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +13 -6
- package/templates/.agents/skills/upgrade-dependency/SKILL.en.md +0 -3
- package/templates/.agents/skills/upgrade-dependency/SKILL.zh-CN.md +0 -3
- package/templates/.agents/skills/watch-pr/SKILL.en.md +4 -4
- package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/watch-pr/config/verify.json +0 -1
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +3 -3
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +3 -3
- package/templates/.agents/templates/handoff.en.md +1 -1
- package/templates/.agents/templates/handoff.zh-CN.md +1 -1
- package/templates/.agents/workflows/bug-fix.en.yaml +1 -1
- package/templates/.agents/workflows/feature-development.en.yaml +1 -1
- package/templates/.agents/workflows/refactoring.en.yaml +1 -1
- package/dist/lib/task/task-execution-lock.js +0 -250
- package/lib/task/task-execution-lock.ts +0 -320
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
1
2
|
import path from 'node:path';
|
|
2
3
|
|
|
3
4
|
import { normalizeAgentToken } from '../agent-clients/tokens.ts';
|
|
@@ -29,8 +30,6 @@ import {
|
|
|
29
30
|
} from './finalization.ts';
|
|
30
31
|
import {
|
|
31
32
|
applyTaskLifecycle,
|
|
32
|
-
applyTaskLifecycleWithinTaskLock,
|
|
33
|
-
taskLifecycleFailure,
|
|
34
33
|
type TaskLifecycleRequest,
|
|
35
34
|
type TaskLifecycleResult
|
|
36
35
|
} from './lifecycle.ts';
|
|
@@ -39,8 +38,8 @@ import type {
|
|
|
39
38
|
AgentClientLifecycleRecoveryResult as LifecycleRecoveryResult
|
|
40
39
|
} from '../agent-clients/adapter.ts';
|
|
41
40
|
import { resolveTaskContext, resolveTaskRef, TASK_ID_RE } from './resolve-ref.ts';
|
|
42
|
-
import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.ts';
|
|
43
41
|
import { verifyTaskEvent } from './verification.ts';
|
|
42
|
+
import { releaseStaleShortIdRegistry } from './short-id.ts';
|
|
44
43
|
|
|
45
44
|
export type TaskControlExecutionContext =
|
|
46
45
|
| Readonly<{
|
|
@@ -215,7 +214,7 @@ async function applyLifecycleWithAuthority(
|
|
|
215
214
|
): Promise<TaskLifecycleResult | LifecycleRecoveryResult> {
|
|
216
215
|
const taskId = lifecycleTaskId(request, context.repoRoot);
|
|
217
216
|
const controlRoot = taskId ? controlRootForTask(context, taskId) : undefined;
|
|
218
|
-
const execute = async (
|
|
217
|
+
const execute = async (): Promise<TaskLifecycleResult | LifecycleRecoveryResult> => {
|
|
219
218
|
if (request.intent === 'recover-started') {
|
|
220
219
|
const adapter = isAgentClientId(request.agent)
|
|
221
220
|
? getAgentClientAdapter(request.agent).orchestrationAdapter
|
|
@@ -229,38 +228,13 @@ async function applyLifecycleWithAuthority(
|
|
|
229
228
|
}
|
|
230
229
|
return adapter.recoverStarted(request as LifecycleRecoveryRequest, { repoRoot: context.repoRoot });
|
|
231
230
|
}
|
|
232
|
-
return
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
}, taskId!)
|
|
238
|
-
: applyTaskLifecycle(request, {
|
|
239
|
-
repoRoot: context.repoRoot,
|
|
240
|
-
...(controlRoot ? { controlRoot } : {}),
|
|
241
|
-
...(context.source === 'direct-host' && context.project ? { controlProject: context.project } : {})
|
|
242
|
-
});
|
|
231
|
+
return applyTaskLifecycle(request, {
|
|
232
|
+
repoRoot: context.repoRoot,
|
|
233
|
+
...(controlRoot ? { controlRoot } : {}),
|
|
234
|
+
...(context.source === 'direct-host' && context.project ? { controlProject: context.project } : {})
|
|
235
|
+
});
|
|
243
236
|
};
|
|
244
|
-
|
|
245
|
-
try {
|
|
246
|
-
return await withTaskExecutionLock(
|
|
247
|
-
context.repoRoot,
|
|
248
|
-
taskId,
|
|
249
|
-
`task-lifecycle.${request.intent}`,
|
|
250
|
-
() => execute(true)
|
|
251
|
-
);
|
|
252
|
-
} catch (error) {
|
|
253
|
-
if (!(error instanceof TaskExecutionLockError)) throw error;
|
|
254
|
-
if (request.intent === 'recover-started') {
|
|
255
|
-
return {
|
|
256
|
-
status: 'owner-unknown', changed: false, targetState: 'active',
|
|
257
|
-
requestRef: request.taskRef, intent: 'recover-started', taskId,
|
|
258
|
-
receiptId: null, childId: null,
|
|
259
|
-
error: { code: error.code, message: error.message }
|
|
260
|
-
};
|
|
261
|
-
}
|
|
262
|
-
return taskLifecycleFailure(request, { code: error.code, message: error.message }, taskId);
|
|
263
|
-
}
|
|
237
|
+
return await execute();
|
|
264
238
|
}
|
|
265
239
|
|
|
266
240
|
function controlRootForTask(context: TaskControlExecutionContext, taskId: string): string | undefined {
|
|
@@ -393,6 +367,26 @@ export function dispatchTaskControlOperation(
|
|
|
393
367
|
preflight: (request, options) => verifyTaskEvent(
|
|
394
368
|
{ ...request, event: 'complete-task.hard-preflight' }, options
|
|
395
369
|
)
|
|
370
|
+
}).then((result) => {
|
|
371
|
+
if (context.source !== 'direct-host' || !controlRoot || result.status !== 'completed' || !result.taskId) return result;
|
|
372
|
+
try {
|
|
373
|
+
if (fs.lstatSync(controlRoot, { throwIfNoEntry: false }) === undefined) {
|
|
374
|
+
const released = releaseStaleShortIdRegistry(context.repoRoot, result.taskId);
|
|
375
|
+
return { ...result, changed: result.changed || released.changed };
|
|
376
|
+
}
|
|
377
|
+
return result;
|
|
378
|
+
} catch (error) {
|
|
379
|
+
return {
|
|
380
|
+
...result,
|
|
381
|
+
status: 'blocked',
|
|
382
|
+
result: 'blocked',
|
|
383
|
+
error: {
|
|
384
|
+
code: 'TASK_FINALIZATION_SHORT_ID_RELEASE_FAILED',
|
|
385
|
+
message: error instanceof Error ? error.message : String(error),
|
|
386
|
+
retryable: true
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
}
|
|
396
390
|
});
|
|
397
391
|
}
|
|
398
392
|
try {
|
|
@@ -4,7 +4,6 @@ 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';
|
|
8
7
|
import { verifyTaskEvent } from './verification.ts';
|
|
9
8
|
|
|
10
9
|
type TaskCreateStatus = 'applied' | 'no-op' | 'degraded' | 'failed' | 'blocked';
|
|
@@ -201,9 +200,8 @@ async function createTask(value: unknown, options: CreateTaskOptions): Promise<T
|
|
|
201
200
|
});
|
|
202
201
|
} catch (error) {
|
|
203
202
|
const message = error instanceof Error ? error.message : String(error);
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
return taskCreateFailure({ code, message, retryable: lockBusy || code === 'TASK_CREATE_LOCK_TIMEOUT' });
|
|
203
|
+
const code = /^([A-Z][A-Z0-9_]+)/.exec(message)?.[1] ?? 'TASK_CREATE_FAILED';
|
|
204
|
+
return taskCreateFailure({ code, message, retryable: false });
|
|
207
205
|
}
|
|
208
206
|
|
|
209
207
|
const operations: TaskCreateOperation[] = [{
|
package/lib/task/create.ts
CHANGED
|
@@ -5,13 +5,13 @@ 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, withRepositoryMutationLock, withTaskExecutionLock } from './task-execution-lock.ts';
|
|
9
8
|
import { readDeliveryDefaults, validateBaseRef, validateRemote } from './delivery-target.ts';
|
|
10
9
|
import { buildUnboundFact, encodePrDeliveryFact } from './pr-delivery-fact.ts';
|
|
11
10
|
import { CANDIDATE_COLUMNS, CONSTRAINT_COLUMNS, parseTaskQualification } from './qualification-audit.ts';
|
|
12
11
|
import { enumerateAllTaskDirs } from './resolve-ref.ts';
|
|
13
12
|
import { parseTaskFrontmatter } from './frontmatter.ts';
|
|
14
13
|
import { AGENT_TOKEN_VALUES, type AgentToken } from '../agent-clients/tokens.ts';
|
|
14
|
+
import { resolveProjectTaskWorkspaceRoot } from './workspace-root.ts';
|
|
15
15
|
|
|
16
16
|
const TYPES = ['feature', 'bugfix', 'refactor', 'docs', 'chore'] as const;
|
|
17
17
|
const PRIORITIES = ['Urgent', 'High', 'Medium', 'Low'] as const;
|
|
@@ -348,42 +348,6 @@ function ensureRealDirectory(directory: string, code: string): void {
|
|
|
348
348
|
fs.mkdirSync(directory, { mode: 0o700 });
|
|
349
349
|
}
|
|
350
350
|
|
|
351
|
-
function createLockRoot(project: string): string {
|
|
352
|
-
const root = path.join(os.homedir(), '.agent-infra');
|
|
353
|
-
const directories = [root, path.join(root, 'run'), path.join(root, 'run', project), path.join(root, 'run', project, 'task-create-locks')];
|
|
354
|
-
for (const directory of directories) {
|
|
355
|
-
if (fs.existsSync(directory)) {
|
|
356
|
-
assertRealDirectory(directory, 'TASK_CREATE_LOCK_FAILED');
|
|
357
|
-
continue;
|
|
358
|
-
}
|
|
359
|
-
try {
|
|
360
|
-
fs.mkdirSync(directory, { mode: 0o700 });
|
|
361
|
-
} catch (error) {
|
|
362
|
-
if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error;
|
|
363
|
-
assertRealDirectory(directory, 'TASK_CREATE_LOCK_FAILED');
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
return directories.at(-1)!;
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
function withCreateLock<T>(repoRoot: string, project: string, operation: () => T): T {
|
|
370
|
-
const lockRoot = createLockRoot(project);
|
|
371
|
-
let callbackError: unknown;
|
|
372
|
-
try {
|
|
373
|
-
return withTaskExecutionLock(repoRoot, 'task-create', 'task-create', () => {
|
|
374
|
-
try { return operation(); }
|
|
375
|
-
catch (error) { callbackError = error; throw error; }
|
|
376
|
-
}, { lockRoot, identityScope: `task-create\0${project}` });
|
|
377
|
-
} catch (error) {
|
|
378
|
-
if (callbackError !== undefined) throw callbackError;
|
|
379
|
-
if (error instanceof TaskExecutionLockError && error.code === 'ORCHESTRATION_LOCK_BUSY') {
|
|
380
|
-
throw new Error('TASK_CREATE_LOCK_TIMEOUT');
|
|
381
|
-
}
|
|
382
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
383
|
-
throw new Error(`TASK_CREATE_LOCK_FAILED: ${message}`);
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
|
|
387
351
|
function recoverPublishedTask(repoRoot: string, keyDigest: string, candidateDigest: string): { taskId: string; shortId: string | null; state: 'active' | 'blocked' | 'completed' | 'archive' } | null {
|
|
388
352
|
for (const task of enumerateAllTaskDirs(repoRoot)) {
|
|
389
353
|
const taskMd = path.join(task.taskDir, 'task.md');
|
|
@@ -410,16 +374,22 @@ function createLocalTask(value: unknown, options: LocalTaskCreateOptions): Local
|
|
|
410
374
|
throw new Error('TASK_CREATE_VERSION_INVALID: agentInfraVersion must be a valid v-prefixed semver');
|
|
411
375
|
}
|
|
412
376
|
const repoRoot = fs.realpathSync.native(options.repoRoot);
|
|
413
|
-
const workspaceRoot =
|
|
377
|
+
const workspaceRoot = resolveProjectTaskWorkspaceRoot(repoRoot);
|
|
414
378
|
const activeRoot = path.join(workspaceRoot, 'active');
|
|
415
379
|
ensureRealDirectory(path.join(repoRoot, '.agents'), 'TASK_CREATE_WORKSPACE_INVALID');
|
|
380
|
+
const home = path.resolve(process.env.HOME || os.homedir());
|
|
381
|
+
const managedRoots = [path.join(home, '.agent-infra'), path.join(home, '.agent-infra', 'task-workspaces')];
|
|
382
|
+
for (const root of managedRoots) {
|
|
383
|
+
if (fs.existsSync(root)) assertRealDirectory(root, 'TASK_CREATE_WORKSPACE_INVALID');
|
|
384
|
+
else fs.mkdirSync(root, { mode: 0o700 });
|
|
385
|
+
}
|
|
416
386
|
ensureRealDirectory(workspaceRoot, 'TASK_CREATE_WORKSPACE_INVALID');
|
|
417
387
|
ensureRealDirectory(activeRoot, 'TASK_CREATE_WORKSPACE_INVALID');
|
|
418
388
|
const projectConfig = readProject(repoRoot);
|
|
419
389
|
const canonical = canonicalTaskCreateCandidate(candidate);
|
|
420
390
|
const candidateDigest = sha256(canonical);
|
|
421
391
|
const keyDigest = sha256(candidate.idempotencyKey);
|
|
422
|
-
function
|
|
392
|
+
function createOnce(): LocalTaskCreateResult {
|
|
423
393
|
const recovered = recoverPublishedTask(repoRoot, keyDigest, candidateDigest);
|
|
424
394
|
if (recovered) {
|
|
425
395
|
return { status: 'no-op', changed: false, task: { id: recovered.taskId, shortId: recovered.shortId, state: recovered.state } };
|
|
@@ -451,18 +421,7 @@ function createLocalTask(value: unknown, options: LocalTaskCreateOptions): Local
|
|
|
451
421
|
return { status: 'applied', changed: true, task: { id: taskId, shortId: allocated.shortId, state: 'active' } };
|
|
452
422
|
}
|
|
453
423
|
|
|
454
|
-
return
|
|
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
|
-
}
|
|
465
|
-
});
|
|
424
|
+
return createOnce();
|
|
466
425
|
|
|
467
426
|
}
|
|
468
427
|
|
|
@@ -263,7 +263,7 @@ function validateSealRequest(
|
|
|
263
263
|
|
|
264
264
|
function validateReviewerSealPaths(receipt: DelegationReceipt, changedPaths: readonly string[]): ReceiptFailure | null {
|
|
265
265
|
if (receipt.role !== 'reviewer') return null;
|
|
266
|
-
const taskRoot =
|
|
266
|
+
const taskRoot = `task-workspace/active/${receipt.taskId}/`;
|
|
267
267
|
const allowed = new Set([
|
|
268
268
|
`${taskRoot}${receipt.artifact}`,
|
|
269
269
|
`${taskRoot}task.md`,
|
package/lib/task/delivery.ts
CHANGED
|
@@ -6,7 +6,6 @@ import { parseTaskFrontmatter } from './frontmatter.ts';
|
|
|
6
6
|
import { resolveTaskRef } from './resolve-ref.ts';
|
|
7
7
|
import { resolveDeliveryTarget, validateBaseRef, validateRemote } from './delivery-target.ts';
|
|
8
8
|
import { captureTaskWriteMetadata, writeTask } from './write.ts';
|
|
9
|
-
import { withRepositoryMutationLock, withTaskExecutionLock } from './task-execution-lock.ts';
|
|
10
9
|
|
|
11
10
|
export type DeliveryState = 'absent' | 'same' | 'known-old' | 'unknown-drift';
|
|
12
11
|
|
|
@@ -50,7 +49,7 @@ function failure(taskId: string | null, code: string, message: string, retryable
|
|
|
50
49
|
return { status, changed: false, taskId, remoteHead: null, localHead: null, state: null, error: { code, message, retryable } };
|
|
51
50
|
}
|
|
52
51
|
|
|
53
|
-
function
|
|
52
|
+
function deliverTaskBranchCore(
|
|
54
53
|
taskId: string,
|
|
55
54
|
taskDir: string,
|
|
56
55
|
repoRoot: string,
|
|
@@ -108,14 +107,7 @@ function deliverUnlocked(
|
|
|
108
107
|
function deliverTaskBranch(taskRef: string, options: { repoRoot?: string; agent: string; remote?: string; baseRef?: string; dryRun?: boolean }): TaskBranchDeliveryResult {
|
|
109
108
|
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
110
109
|
if (!resolved.ok) return failure(resolved.taskId, resolved.code, resolved.message);
|
|
111
|
-
|
|
112
|
-
return withRepositoryMutationLock(resolved.repoRoot, () => withTaskExecutionLock(
|
|
113
|
-
resolved.repoRoot, resolved.taskId, 'task-delivery',
|
|
114
|
-
() => deliverUnlocked(resolved.taskId, resolved.taskDir, resolved.repoRoot, options)
|
|
115
|
-
));
|
|
116
|
-
} catch (error) {
|
|
117
|
-
return failure(resolved.taskId, 'DELIVERY_LOCK_FAILED', error instanceof Error ? error.message : String(error), true, 'blocked');
|
|
118
|
-
}
|
|
110
|
+
return deliverTaskBranchCore(resolved.taskId, resolved.taskDir, resolved.repoRoot, options);
|
|
119
111
|
}
|
|
120
112
|
|
|
121
113
|
export { classifyDeliveryState, deliverTaskBranch };
|
package/lib/task/events.ts
CHANGED
|
@@ -31,7 +31,6 @@ import { findSectionHeading } from './sections.ts';
|
|
|
31
31
|
import { validateLifecycleExecution } from './lifecycle-execution.ts';
|
|
32
32
|
import { commitOrchestrationStageCompletion } from './orchestration.ts';
|
|
33
33
|
import type { OrchestrationStageCompletion } from './orchestration.ts';
|
|
34
|
-
import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.ts';
|
|
35
34
|
import { captureTaskWriteMetadata, writeTask } from './write.ts';
|
|
36
35
|
import type { TaskOperationSummary, TaskWriteErrorCode, TaskWriteOptions } from './write.ts';
|
|
37
36
|
import { validateLocalArtifact } from './local-artifact-finalization.ts';
|
|
@@ -86,7 +85,6 @@ type TaskEventRequest = {
|
|
|
86
85
|
type TaskEventError = { code: TaskEventErrorCode; message: string };
|
|
87
86
|
type TaskEventOptions = TaskWriteOptions & {
|
|
88
87
|
commitOrchestrationCompletion?: (plan: OrchestrationStageCompletion) => void;
|
|
89
|
-
lockAlreadyHeld?: boolean;
|
|
90
88
|
};
|
|
91
89
|
type TaskEventResult = {
|
|
92
90
|
status: 'planned' | 'applied' | 'no-op' | 'failed'; changed: boolean;
|
|
@@ -519,6 +517,7 @@ function reworkIntentMutationForCompletion(
|
|
|
519
517
|
function buildCompletionReceipt(
|
|
520
518
|
content: string,
|
|
521
519
|
taskDir: string,
|
|
520
|
+
repositoryRoot: string | undefined,
|
|
522
521
|
family: EventFamily,
|
|
523
522
|
artifact: ArtifactIdentity,
|
|
524
523
|
completedAt: string,
|
|
@@ -527,7 +526,7 @@ function buildCompletionReceipt(
|
|
|
527
526
|
): { ok: true; receipts: readonly ArtifactReceipt[] } | { ok: false; message: string } | null {
|
|
528
527
|
if (!['analyze', 'plan', 'code'].includes(family) && !family.startsWith('review-')) return null;
|
|
529
528
|
if (family === 'review-code') {
|
|
530
|
-
const identity = inspectReviewIdentity(taskDir, fs.readFileSync(artifact.path, 'utf8'));
|
|
529
|
+
const identity = inspectReviewIdentity(taskDir, fs.readFileSync(artifact.path, 'utf8'), repositoryRoot);
|
|
531
530
|
if (identity.status !== 'matched') return { ok: false, message: `review-code identity is ${identity.status}: ${identity.message}` };
|
|
532
531
|
}
|
|
533
532
|
const event = family === 'analyze' ? 'analysis.completed'
|
|
@@ -627,7 +626,7 @@ function replaceCompletionFact(facts: readonly CompletionFact[], next: Completio
|
|
|
627
626
|
return [...facts.filter((fact) => !(fact.event === next.event && fact.output === next.output)), next];
|
|
628
627
|
}
|
|
629
628
|
|
|
630
|
-
function
|
|
629
|
+
function applyTaskEvent(request: TaskEventRequest, options: TaskEventOptions = {}): TaskEventResult {
|
|
631
630
|
const invalid = validateTaskEventRequest(request);
|
|
632
631
|
if (invalid) return failed(request, invalid);
|
|
633
632
|
const resolved = resolveTaskRef(request.taskRef, { repoRoot: options.repoRoot });
|
|
@@ -757,7 +756,7 @@ function applyTaskEventUnlocked(request: TaskEventRequest, options: TaskEventOpt
|
|
|
757
756
|
}
|
|
758
757
|
if (eventIdentity.phase === 'started') {
|
|
759
758
|
const trigger = eventTrigger(normalized, eventIdentity.family);
|
|
760
|
-
const facts = buildLifecycleFacts(resolved.taskDir, content, resolved.state);
|
|
759
|
+
const facts = buildLifecycleFacts(resolved.taskDir, content, resolved.state, false, resolved.repoRoot);
|
|
761
760
|
if (!facts.ok) 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 });
|
|
762
761
|
const capability = canStart(lifecycleAction(eventIdentity.family), facts.facts, trigger);
|
|
763
762
|
const safetyFailure = capability.reasonCode === 'TASK_NOT_ACTIVE'
|
|
@@ -804,7 +803,7 @@ function applyTaskEventUnlocked(request: TaskEventRequest, options: TaskEventOpt
|
|
|
804
803
|
let currentFact: CompletionFact | null = null;
|
|
805
804
|
let completionRework: ReturnType<typeof reworkIntentMutation> | null = null;
|
|
806
805
|
if (eventIdentity.phase === 'completed' && completedArtifact) {
|
|
807
|
-
const receipt = buildCompletionReceipt(content, resolved.taskDir, eventIdentity.family, completedArtifact, metadata.timestamp, frontmatter, normalized);
|
|
806
|
+
const receipt = buildCompletionReceipt(content, resolved.taskDir, resolved.repoRoot, eventIdentity.family, completedArtifact, metadata.timestamp, frontmatter, normalized);
|
|
808
807
|
if (receipt && !receipt.ok) {
|
|
809
808
|
return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: receipt.message }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
|
|
810
809
|
}
|
|
@@ -978,32 +977,6 @@ function applyTaskEventUnlocked(request: TaskEventRequest, options: TaskEventOpt
|
|
|
978
977
|
};
|
|
979
978
|
}
|
|
980
979
|
|
|
981
|
-
function applyTaskEvent(request: TaskEventRequest, options: TaskEventOptions = {}): TaskEventResult {
|
|
982
|
-
const invalid = validateTaskEventRequest(request);
|
|
983
|
-
if (invalid) return failed(request, invalid);
|
|
984
|
-
const parts = eventParts(request.event);
|
|
985
|
-
if (options.lockAlreadyHeld) return applyTaskEventUnlocked(request, options);
|
|
986
|
-
if (request.dryRun || !['started', 'completed'].includes(parts.phase)) {
|
|
987
|
-
return applyTaskEventUnlocked(request, options);
|
|
988
|
-
}
|
|
989
|
-
const resolved = resolveTaskRef(request.taskRef, { repoRoot: options.repoRoot });
|
|
990
|
-
if (!resolved.ok) return applyTaskEventUnlocked(request, options);
|
|
991
|
-
try {
|
|
992
|
-
return withTaskExecutionLock(
|
|
993
|
-
resolved.repoRoot,
|
|
994
|
-
resolved.taskId,
|
|
995
|
-
`task-event.${request.event}`,
|
|
996
|
-
() => applyTaskEventUnlocked(request, { ...options, lockAlreadyHeld: true })
|
|
997
|
-
);
|
|
998
|
-
} catch (error) {
|
|
999
|
-
if (!(error instanceof TaskExecutionLockError)) throw error;
|
|
1000
|
-
return failed(request, {
|
|
1001
|
-
code: 'EVENT_TRANSITION_INVALID',
|
|
1002
|
-
message: `${error.code}: ${error.message}`
|
|
1003
|
-
}, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
|
|
1004
|
-
}
|
|
1005
|
-
}
|
|
1006
|
-
|
|
1007
980
|
function successNoOp(
|
|
1008
981
|
request: TaskEventRequest, taskId: string, taskMdPath: string, currentStep: string,
|
|
1009
982
|
eventIdentity: ReturnType<typeof identity>, timestamp: string,
|
package/lib/task/finalization.ts
CHANGED
|
@@ -14,7 +14,6 @@ import { parseTaskFrontmatter } from './frontmatter.ts';
|
|
|
14
14
|
import { recoverPlatformOperations } from './platform-operation-recovery.ts';
|
|
15
15
|
import {
|
|
16
16
|
applyTaskLifecycle,
|
|
17
|
-
applyTaskLifecycleWithinTaskLock,
|
|
18
17
|
inspectTaskLifecycleProgress,
|
|
19
18
|
type TaskLifecycleOptions,
|
|
20
19
|
type TaskLifecycleRequest,
|
|
@@ -24,7 +23,6 @@ import {
|
|
|
24
23
|
} from './lifecycle.ts';
|
|
25
24
|
import { enumerateAllTaskDirs, resolveTaskRef } from './resolve-ref.ts';
|
|
26
25
|
import { inspectShortIdRegistry } from './short-id.ts';
|
|
27
|
-
import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.ts';
|
|
28
26
|
import { verifyTaskEvent } from './verification.ts';
|
|
29
27
|
import type { TaskVerificationResult } from './verification.ts';
|
|
30
28
|
import { projectFinalizationWarning } from './workflow-warning-intents.ts';
|
|
@@ -35,6 +33,7 @@ import {
|
|
|
35
33
|
} from './operation-outcome.ts';
|
|
36
34
|
import { readSandboxControlManifest } from '../sandbox/control/lifecycle.ts';
|
|
37
35
|
import { validateSandboxControlIdentity } from '../sandbox/control/identity-sentinel.ts';
|
|
36
|
+
import { parseDeliverySummaryContent, renderDeliverySummaryContent } from '../platform/issue-summary-content.ts';
|
|
38
37
|
|
|
39
38
|
const RECEIPT_VERSION = 4 as const;
|
|
40
39
|
const FINALIZATION_STEPS = ['lifecycle', 'task-comment', 'verification', 'summary'] as const;
|
|
@@ -586,7 +585,7 @@ function mutationPatch(current: TaskFinalizationReceipt, mutation: FinalizationM
|
|
|
586
585
|
: { warningProjection: 'pending', lastError: mutation.error };
|
|
587
586
|
}
|
|
588
587
|
|
|
589
|
-
function
|
|
588
|
+
function mutateFinalizationReceipt(
|
|
590
589
|
repoRoot: string,
|
|
591
590
|
receipt: TaskFinalizationReceipt,
|
|
592
591
|
capability: FinalizationCapability,
|
|
@@ -615,9 +614,7 @@ function applyFinalizationReceiptMutation(
|
|
|
615
614
|
if (!hasFinalizationTaskDocument(repoRoot, receipt.taskId)) {
|
|
616
615
|
throw capabilityError('FINALIZATION_SCOPE_INVALID', 'finalization capability requires an active task or a completed task');
|
|
617
616
|
}
|
|
618
|
-
return
|
|
619
|
-
applyFinalizationReceiptMutationUnderLock(repoRoot, receipt, capability, mutation, new Set<string>())
|
|
620
|
-
);
|
|
617
|
+
return mutateFinalizationReceipt(repoRoot, receipt, capability, mutation, new Set<string>());
|
|
621
618
|
}
|
|
622
619
|
|
|
623
620
|
function warningKey(warning: Pick<FinalizationWarning, 'step' | 'code' | 'target'>): string {
|
|
@@ -640,20 +637,20 @@ function reconcileWarningProjection(repoRoot: string, taskId: string, receipt: T
|
|
|
640
637
|
retryable: true
|
|
641
638
|
};
|
|
642
639
|
const capability = issueCapability(receipt, 'warning-projection');
|
|
643
|
-
return
|
|
640
|
+
return mutateFinalizationReceipt(repoRoot, receipt, capability, { scope: 'warning-projection', operation: 'failed', error: detail }, consumed);
|
|
644
641
|
}
|
|
645
642
|
} catch (error) {
|
|
646
643
|
const detail = errorOf(error, 'FINALIZATION_WARNING_PROJECTION_FAILED', true);
|
|
647
644
|
try {
|
|
648
645
|
const capability = issueCapability(receipt, 'warning-projection');
|
|
649
|
-
return
|
|
646
|
+
return mutateFinalizationReceipt(repoRoot, receipt, capability, { scope: 'warning-projection', operation: 'failed', error: detail }, consumed);
|
|
650
647
|
}
|
|
651
648
|
catch { return receipt; }
|
|
652
649
|
}
|
|
653
650
|
}
|
|
654
651
|
try {
|
|
655
652
|
const capability = issueCapability(receipt, 'warning-projection');
|
|
656
|
-
return
|
|
653
|
+
return mutateFinalizationReceipt(repoRoot, receipt, capability, { scope: 'warning-projection', operation: 'succeeded' }, consumed);
|
|
657
654
|
}
|
|
658
655
|
catch { return receipt; }
|
|
659
656
|
}
|
|
@@ -692,7 +689,7 @@ async function syncPendingTaskComment(input: {
|
|
|
692
689
|
if (result.status === 'applied' || result.status === 'no-op') {
|
|
693
690
|
const skipped = result.error?.code === 'ISSUE_NOT_LINKED';
|
|
694
691
|
const capability = issueCapability(receipt, 'task-comment');
|
|
695
|
-
receipt =
|
|
692
|
+
receipt = mutateFinalizationReceipt(repoRoot, receipt, capability, {
|
|
696
693
|
scope: 'task-comment', operation: 'succeeded', state: skipped ? 'skipped' : 'done'
|
|
697
694
|
}, consumedCapabilities);
|
|
698
695
|
receipt = reconcileWarningProjection(repoRoot, taskId, receipt, consumedCapabilities);
|
|
@@ -708,7 +705,7 @@ async function syncPendingTaskComment(input: {
|
|
|
708
705
|
}
|
|
709
706
|
const detail = step.error ?? { code: 'COMMENT_SYNC_FAILED', message: 'task comment synchronization failed', retryable: true };
|
|
710
707
|
const capability = issueCapability(receipt, 'task-comment');
|
|
711
|
-
receipt =
|
|
708
|
+
receipt = mutateFinalizationReceipt(repoRoot, receipt, capability, {
|
|
712
709
|
scope: 'task-comment', operation: 'failed', error: detail
|
|
713
710
|
}, consumedCapabilities);
|
|
714
711
|
receipt = reconcileWarningProjection(repoRoot, taskId, receipt, consumedCapabilities);
|
|
@@ -717,7 +714,7 @@ async function syncPendingTaskComment(input: {
|
|
|
717
714
|
const detail = errorOf(error, 'COMMENT_SYNC_FAILED', true);
|
|
718
715
|
try {
|
|
719
716
|
const capability = issueCapability(receipt, 'task-comment');
|
|
720
|
-
receipt =
|
|
717
|
+
receipt = mutateFinalizationReceipt(repoRoot, receipt, capability, {
|
|
721
718
|
scope: 'task-comment', operation: 'failed', error: detail
|
|
722
719
|
}, consumedCapabilities);
|
|
723
720
|
receipt = reconcileWarningProjection(repoRoot, taskId, receipt, consumedCapabilities);
|
|
@@ -734,16 +731,20 @@ function readDeliverySummary(taskDir: string, taskId: string): { body: string; s
|
|
|
734
731
|
} catch {
|
|
735
732
|
throw Object.assign(new Error('delivery summary staging record is unavailable'), { code: 'SUMMARY_STAGING_INVALID' });
|
|
736
733
|
}
|
|
737
|
-
if (value.taskId !== taskId ||
|
|
734
|
+
if (value.taskId !== taskId || !value.content || typeof value.contentSha256 !== 'string') {
|
|
738
735
|
throw Object.assign(new Error('delivery summary staging record is invalid'), { code: 'SUMMARY_STAGING_INVALID' });
|
|
739
736
|
}
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
737
|
+
let content;
|
|
738
|
+
try { content = parseDeliverySummaryContent(value.content); }
|
|
739
|
+
catch { throw Object.assign(new Error('delivery summary staging payload is invalid'), { code: 'SUMMARY_STAGING_INVALID' }); }
|
|
740
|
+
if (content.taskId !== taskId || createHash('sha256').update(JSON.stringify(content)).digest('hex') !== value.contentSha256) {
|
|
741
|
+
throw Object.assign(new Error('delivery summary staging digest does not match payload'), { code: 'SUMMARY_STAGING_INVALID' });
|
|
743
742
|
}
|
|
743
|
+
const body = renderDeliverySummaryContent(content);
|
|
744
|
+
const canonical = canonicalizeSummaryBody(body);
|
|
745
|
+
if (!canonical.ok) throw Object.assign(new Error('delivery summary staging body is not canonical'), { code: 'SUMMARY_STAGING_INVALID' });
|
|
744
746
|
const digest = createHash('sha256').update(canonical.value).digest('hex');
|
|
745
|
-
|
|
746
|
-
return { body: canonical.value, sha256: value.sha256 };
|
|
747
|
+
return { body: canonical.value, sha256: digest };
|
|
747
748
|
}
|
|
748
749
|
|
|
749
750
|
async function syncPendingSummary(input: {
|
|
@@ -863,14 +864,14 @@ function preparedResult(
|
|
|
863
864
|
};
|
|
864
865
|
}
|
|
865
866
|
|
|
866
|
-
async function
|
|
867
|
+
async function prepareTaskFinalizationCore(
|
|
867
868
|
request: TaskFinalizationRequest,
|
|
868
869
|
taskId: string,
|
|
869
870
|
options: TaskFinalizationOptions
|
|
870
871
|
): Promise<TaskFinalizationResult> {
|
|
871
872
|
const repoRoot = path.resolve(options.repoRoot);
|
|
872
873
|
const lifecycle = options.lifecycle ?? ((lifecycleRequest, lifecycleOptions) => (
|
|
873
|
-
|
|
874
|
+
applyTaskLifecycle(lifecycleRequest, lifecycleOptions ?? { repoRoot })
|
|
874
875
|
));
|
|
875
876
|
const backfill = options.backfill ?? backfillCompletionComments;
|
|
876
877
|
const commentSync = options.commentSync ?? syncPlatformComment;
|
|
@@ -1142,11 +1143,9 @@ async function prepareTaskFinalization(request: TaskFinalizationRequest, options
|
|
|
1142
1143
|
const resolved = resolveTaskRef(request.taskRef, { repoRoot });
|
|
1143
1144
|
if (!resolved.ok) return failed(resolved.taskId, { code: resolved.code, message: resolved.message, retryable: false });
|
|
1144
1145
|
try {
|
|
1145
|
-
return await
|
|
1146
|
+
return await prepareTaskFinalizationCore(request, resolved.taskId, options);
|
|
1146
1147
|
} catch (error) {
|
|
1147
|
-
const detail = error
|
|
1148
|
-
? { code: error.code, message: error.message, retryable: error.code === 'ORCHESTRATION_LOCK_BUSY' }
|
|
1149
|
-
: errorOf(error, 'TASK_FINALIZATION_FAILED');
|
|
1148
|
+
const detail = errorOf(error, 'TASK_FINALIZATION_FAILED');
|
|
1150
1149
|
return failed(resolved.taskId, detail);
|
|
1151
1150
|
}
|
|
1152
1151
|
}
|
|
@@ -1159,7 +1158,7 @@ async function commitPreparedTaskFinalization(request: TaskFinalizationRequest,
|
|
|
1159
1158
|
const resolved = resolveTaskRef(request.taskRef, { repoRoot });
|
|
1160
1159
|
if (!resolved.ok) return failed(resolved.taskId, { code: resolved.code, message: resolved.message, retryable: false });
|
|
1161
1160
|
try {
|
|
1162
|
-
return await
|
|
1161
|
+
return await (async () => {
|
|
1163
1162
|
if (options.controlBinding && options.manifestPath) {
|
|
1164
1163
|
try {
|
|
1165
1164
|
const manifest = readSandboxControlManifest(options.manifestPath);
|
|
@@ -1223,7 +1222,7 @@ async function commitPreparedTaskFinalization(request: TaskFinalizationRequest,
|
|
|
1223
1222
|
};
|
|
1224
1223
|
const result = options.lifecycle
|
|
1225
1224
|
? options.lifecycle(lifecycleRequest, lifecycleOptions)
|
|
1226
|
-
:
|
|
1225
|
+
: applyTaskLifecycle(lifecycleRequest, lifecycleOptions);
|
|
1227
1226
|
const step = lifecycleStep(result);
|
|
1228
1227
|
if (result.status !== 'applied' && result.status !== 'no-op') {
|
|
1229
1228
|
const updated = updateReceipt(repoRoot, receipt, { lifecycle: 'pending', lastError: step.error });
|
|
@@ -1305,11 +1304,9 @@ async function commitPreparedTaskFinalization(request: TaskFinalizationRequest,
|
|
|
1305
1304
|
});
|
|
1306
1305
|
}
|
|
1307
1306
|
return terminalResult(resolved.taskId, updated, { lifecycle: step, verification }, result.changed, detail);
|
|
1308
|
-
});
|
|
1307
|
+
})();
|
|
1309
1308
|
} catch (error) {
|
|
1310
|
-
const detail = error
|
|
1311
|
-
? { code: error.code, message: error.message, retryable: error.code === 'ORCHESTRATION_LOCK_BUSY' }
|
|
1312
|
-
: errorOf(error, 'TASK_FINALIZATION_FAILED');
|
|
1309
|
+
const detail = errorOf(error, 'TASK_FINALIZATION_FAILED');
|
|
1313
1310
|
return failed(resolved.taskId, detail);
|
|
1314
1311
|
}
|
|
1315
1312
|
}
|