@fitlab-ai/agent-infra 0.11.15 → 0.12.0
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 +8 -16
- package/dist/lib/internal/platform-comment.js +3 -2
- 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/pr-summary.js +6 -15
- package/dist/lib/platform/pull-requests.js +3 -15
- 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 +3 -10
- 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 +17 -22
- 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 +1 -2
- 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 +8 -19
- package/lib/internal/platform-comment.ts +3 -2
- 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/pr-summary.ts +7 -16
- package/lib/platform/pull-requests.ts +3 -24
- 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 +3 -26
- 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 +16 -24
- 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 +1 -2
- 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 +1 -1
- package/templates/.agents/rules/pr-sync.zh-CN.md +1 -1
- 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 +6 -6
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +6 -6
- 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 +1 -1
- package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +1 -1
- 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 +1 -1
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +1 -1
- package/templates/.agents/skills/complete-task/SKILL.en.md +7 -7
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +8 -8
- 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 +1 -1
- package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +1 -1
- package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +1 -1
- package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +1 -1
- 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 -4
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +4 -4
- 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 +3 -3
- package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/watch-pr/config/verify.json +0 -1
- 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
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { detectRepoRoot } from '../task/resolve-ref.ts';
|
|
2
|
+
import { resolveProjectTaskWorkspaceLayout, type ExecutionBoundary } from '../task/workspace-root.ts';
|
|
3
|
+
import { ensureInternalHandlerRoute, internalHandlerRoute } from './cli-route-inventory.ts';
|
|
4
|
+
|
|
5
|
+
const USAGE = `Usage: agent-infra-internal task-workspace resolve [--boundary host|container]\n`;
|
|
6
|
+
|
|
7
|
+
function fail(code: string, message: string): void {
|
|
8
|
+
process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code, message } })}\n`);
|
|
9
|
+
process.exitCode = 1;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function taskWorkspace(args: string[] = []): void {
|
|
13
|
+
if (!ensureInternalHandlerRoute('task-workspace', args)) return;
|
|
14
|
+
const operation = args[0] ?? '';
|
|
15
|
+
if (operation === '--help' || operation === '-h') { process.stdout.write(USAGE); return; }
|
|
16
|
+
if (!internalHandlerRoute('task-workspace', 'resolve', operation)) {
|
|
17
|
+
fail('TASK_WORKSPACE_PAYLOAD_INVALID', "operation must be 'resolve'");
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
let boundary: ExecutionBoundary = 'host';
|
|
21
|
+
const options = args.slice(1);
|
|
22
|
+
if (options.length !== 0) {
|
|
23
|
+
if (options.length !== 2 || options[0] !== '--boundary') {
|
|
24
|
+
fail('TASK_WORKSPACE_PAYLOAD_INVALID', 'invalid or duplicate task-workspace option');
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
boundary = options[1] as ExecutionBoundary;
|
|
28
|
+
}
|
|
29
|
+
if (boundary !== 'host' && boundary !== 'container') {
|
|
30
|
+
fail('TASK_WORKSPACE_PAYLOAD_INVALID', 'resolve accepts only --boundary host|container');
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
const layout = resolveProjectTaskWorkspaceLayout(detectRepoRoot(), boundary);
|
|
35
|
+
process.stdout.write(`${JSON.stringify({ status: 'resolved', changed: false, projectKey: layout.projectKey, boundary: layout.boundary, root: layout.root, error: null })}\n`);
|
|
36
|
+
} catch (error) { fail('TASK_WORKSPACE_OPERATION_FAILED', error instanceof Error ? error.message : String(error)); }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export { taskWorkspace };
|
package/lib/merge.ts
CHANGED
|
@@ -4,6 +4,7 @@ import path from 'node:path';
|
|
|
4
4
|
import crypto from 'node:crypto';
|
|
5
5
|
import { removeDirRecursive } from './remove-dir.ts';
|
|
6
6
|
import { validateCurrentTaskContract } from './task/current-contract.ts';
|
|
7
|
+
import { resolveProjectTaskWorkspaceRoot } from './task/workspace-root.ts';
|
|
7
8
|
|
|
8
9
|
const TASK_ID_RE = /^TASK-\d{8}-\d{6}$/;
|
|
9
10
|
const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/;
|
|
@@ -988,7 +989,7 @@ function mergeArchiveSection(sourceArchive: string, localArchive: string, report
|
|
|
988
989
|
}
|
|
989
990
|
|
|
990
991
|
function printSection(lines: string[], name: MutableSection, counts: MutableCounts): void {
|
|
991
|
-
const title = `${SECTION_LABELS[name].padEnd(9, ' ')} (
|
|
992
|
+
const title = `${SECTION_LABELS[name].padEnd(9, ' ')} (task workspace/${name}/):`;
|
|
992
993
|
lines.push(title);
|
|
993
994
|
|
|
994
995
|
const allEntries: Array<[MutableAction, string]> = [
|
|
@@ -1013,7 +1014,7 @@ function printSection(lines: string[], name: MutableSection, counts: MutableCoun
|
|
|
1013
1014
|
}
|
|
1014
1015
|
|
|
1015
1016
|
function printArchiveSection(lines: string[], counts: ArchiveCounts): void {
|
|
1016
|
-
const title = `${SECTION_LABELS.archive.padEnd(9, ' ')} (
|
|
1017
|
+
const title = `${SECTION_LABELS.archive.padEnd(9, ' ')} (task workspace/archive/):`;
|
|
1017
1018
|
lines.push(title);
|
|
1018
1019
|
|
|
1019
1020
|
if (counts.copied.length === 0 && counts.skipped.length === 0) {
|
|
@@ -1105,7 +1106,7 @@ async function cmdMerge(args: string[]): Promise<void> {
|
|
|
1105
1106
|
}
|
|
1106
1107
|
|
|
1107
1108
|
const repoRoot = process.cwd();
|
|
1108
|
-
const workspaceDir =
|
|
1109
|
+
const workspaceDir = resolveProjectTaskWorkspaceRoot(repoRoot);
|
|
1109
1110
|
const archiveDir = path.join(workspaceDir, 'archive');
|
|
1110
1111
|
const backupStamp = formatBackupTimestamp(new Date());
|
|
1111
1112
|
const projectKey = resolveRecoveryProjectKey(repoRoot);
|
|
@@ -5,7 +5,6 @@ import { createHash } from 'node:crypto';
|
|
|
5
5
|
import { enumerateArtifacts } from '../task/artifacts.ts';
|
|
6
6
|
import { parseTypedTaskFrontmatter } from '../task/frontmatter.ts';
|
|
7
7
|
import { resolveTaskRef } from '../task/resolve-ref.ts';
|
|
8
|
-
import { TaskExecutionLockError, withTaskExecutionLock } from '../task/task-execution-lock.ts';
|
|
9
8
|
import { readPrDeliveryFact } from '../task/pr-delivery-fact.ts';
|
|
10
9
|
import type { PrDeliveryFact } from '../task/pr-delivery-fact.ts';
|
|
11
10
|
import { resolvePlatformProviderContext } from './context.ts';
|
|
@@ -433,7 +432,7 @@ async function reportWrite(taskRef: string, options: ReportWriteOptions): Promis
|
|
|
433
432
|
if (!loaded.ok || !context.platform.repository || !['no-op', 'degraded'].includes(context.status)) return { ...context, report: null };
|
|
434
433
|
let knownPrNumber: number | null = null;
|
|
435
434
|
try {
|
|
436
|
-
return await
|
|
435
|
+
return await (async () => {
|
|
437
436
|
const { taskContent, boundFact, factError } = readBoundTaskSnapshot(resolved.taskMdPath);
|
|
438
437
|
if (factError) return {
|
|
439
438
|
...basePlatformResult('failed', context, resolved.taskId, null, { code: factError.code, message: factError.message, retryable: false }),
|
|
@@ -502,18 +501,15 @@ async function reportWrite(taskRef: string, options: ReportWriteOptions): Promis
|
|
|
502
501
|
operations: [{ name: 'change-report:write', status: 'applied', reasonCode: null }],
|
|
503
502
|
report: { ...reportInfo, status: 'written' }
|
|
504
503
|
};
|
|
505
|
-
});
|
|
504
|
+
})();
|
|
506
505
|
} catch (error) {
|
|
507
|
-
|
|
508
|
-
? { code: error.code, message: error.message, retryable: error.code === 'ORCHESTRATION_LOCK_BUSY' }
|
|
509
|
-
: { code: 'PR_CHANGE_REPORT_FAILED', message: error instanceof Error ? error.message : String(error), retryable: false };
|
|
510
|
-
return { ...basePlatformResult(lockError.retryable ? 'blocked' : 'failed', context, resolved.taskId, knownPrNumber, lockError), report: null };
|
|
506
|
+
return { ...basePlatformResult('failed', context, resolved.taskId, knownPrNumber, { code: 'PR_CHANGE_REPORT_FAILED', message: error instanceof Error ? error.message : String(error), retryable: false }), report: null };
|
|
511
507
|
}
|
|
512
508
|
}
|
|
513
509
|
|
|
514
510
|
async function syncPullRequestSummary(
|
|
515
511
|
taskRef: string,
|
|
516
|
-
options: { agent: string; body: string; changeReportFile?: string; cwd?: string; client?: PlatformClient; dryRun?: boolean; strict?: boolean; primaryResult: PullRequestPrimaryResult; runtimeVersion?: string; manualValidation?: ManualValidationSummaryOptions;
|
|
512
|
+
options: { agent: string; body: string; changeReportFile?: string; cwd?: string; client?: PlatformClient; dryRun?: boolean; strict?: boolean; primaryResult: PullRequestPrimaryResult; runtimeVersion?: string; manualValidation?: ManualValidationSummaryOptions; skipQueue?: boolean }
|
|
517
513
|
): Promise<PullRequestSummaryResult> {
|
|
518
514
|
const warningResult = warningResultForPrimary(options.primaryResult);
|
|
519
515
|
let knownPrNumber: number | null = null;
|
|
@@ -561,7 +557,7 @@ async function syncPullRequestSummary(
|
|
|
561
557
|
const loaded = await resolvePlatformProviderContext({ cwd: resolved.repoRoot, client: options.client });
|
|
562
558
|
const context = loaded.ok ? loaded.value.context : loaded.context;
|
|
563
559
|
if (!loaded.ok || !context.platform.repository || !['no-op', 'degraded'].includes(context.status)) {
|
|
564
|
-
//
|
|
560
|
+
// Preserve the known PR target for diagnostics when provider context resolution fails.
|
|
565
561
|
knownPrNumber = boundPrNumberForWarning(resolved.taskMdPath);
|
|
566
562
|
return preserveFailure(context);
|
|
567
563
|
}
|
|
@@ -780,14 +776,9 @@ async function syncPullRequestSummary(
|
|
|
780
776
|
persistenceFailure: (error) => fail('failed', context, error, prNumber)
|
|
781
777
|
});
|
|
782
778
|
};
|
|
783
|
-
return await (
|
|
784
|
-
? execute()
|
|
785
|
-
: withTaskExecutionLock(resolved.repoRoot, resolved.taskId, options.agent, execute));
|
|
779
|
+
return await execute();
|
|
786
780
|
} catch (error) {
|
|
787
|
-
|
|
788
|
-
? { code: error.code, message: error.message, retryable: error.code === 'ORCHESTRATION_LOCK_BUSY' }
|
|
789
|
-
: { code: 'PR_SUMMARY_FAILED', message: error instanceof Error ? error.message : String(error), retryable: false };
|
|
790
|
-
return fail(lockError.retryable ? 'blocked' : 'failed', context, lockError);
|
|
781
|
+
return fail('failed', context, { code: 'PR_SUMMARY_FAILED', message: error instanceof Error ? error.message : String(error), retryable: false });
|
|
791
782
|
}
|
|
792
783
|
}
|
|
793
784
|
|
|
@@ -21,7 +21,6 @@ import {
|
|
|
21
21
|
import { platformResult } from './types.ts';
|
|
22
22
|
import type { PlatformOperation, PlatformResult } from './types.ts';
|
|
23
23
|
import { inspectCompletionArtifacts } from '../task/finalization-artifacts.ts';
|
|
24
|
-
import { TaskExecutionLockError, withTaskExecutionLock } from '../task/task-execution-lock.ts';
|
|
25
24
|
import { resolveDeliveryTarget } from '../task/delivery-target.ts';
|
|
26
25
|
import { mergeOperationWarnings, type OperationWarning } from '../task/operation-outcome.ts';
|
|
27
26
|
import {
|
|
@@ -1174,20 +1173,8 @@ async function createPlatformPullRequestImpl(taskRef: string, options: CreateOpt
|
|
|
1174
1173
|
}), PRECONDITION_NOT_CREATED);
|
|
1175
1174
|
}
|
|
1176
1175
|
try {
|
|
1177
|
-
return
|
|
1178
|
-
base.resolved.repoRoot,
|
|
1179
|
-
base.resolved.taskId,
|
|
1180
|
-
'platform-pr.create',
|
|
1181
|
-
() => createExternalPullRequest(taskRef, options, base)
|
|
1182
|
-
);
|
|
1176
|
+
return await createExternalPullRequest(taskRef, options, base);
|
|
1183
1177
|
} catch (error) {
|
|
1184
|
-
if (error instanceof TaskExecutionLockError) {
|
|
1185
|
-
return withCreation(result('blocked', base.resolved.taskId, base.issueNumber, null, {
|
|
1186
|
-
platform: base.context.platform,
|
|
1187
|
-
capabilities: base.context.capabilities,
|
|
1188
|
-
error: { code: error.code, message: error.message, retryable: true }
|
|
1189
|
-
}), PRECONDITION_NOT_CREATED);
|
|
1190
|
-
}
|
|
1191
1178
|
return withCreation(result('failed', base.resolved.taskId, base.issueNumber, null, {
|
|
1192
1179
|
platform: base.context.platform,
|
|
1193
1180
|
capabilities: base.context.capabilities,
|
|
@@ -1602,7 +1589,7 @@ function readProjectPrFlow(repoRoot: string): 'required' | 'disabled' | undefine
|
|
|
1602
1589
|
}
|
|
1603
1590
|
}
|
|
1604
1591
|
|
|
1605
|
-
function
|
|
1592
|
+
function applySkippedPullRequestFact(
|
|
1606
1593
|
resolved: Extract<ReturnType<typeof resolveTaskRef>, { ok: true }>,
|
|
1607
1594
|
options: SkipFactOptions
|
|
1608
1595
|
): PullRequestResult {
|
|
@@ -1674,16 +1661,8 @@ async function skipPlatformPullRequestFact(taskRef: string, options: SkipFactOpt
|
|
|
1674
1661
|
error: { code: 'TASK_STATE_INVALID', message: 'platform-pr skip requires an active task', retryable: false }
|
|
1675
1662
|
});
|
|
1676
1663
|
try {
|
|
1677
|
-
return
|
|
1678
|
-
resolved.repoRoot,
|
|
1679
|
-
resolved.taskId,
|
|
1680
|
-
'platform-pr.skip',
|
|
1681
|
-
() => skipPlatformPullRequestFactUnlocked(resolved, options)
|
|
1682
|
-
);
|
|
1664
|
+
return await applySkippedPullRequestFact(resolved, options);
|
|
1683
1665
|
} catch (error) {
|
|
1684
|
-
if (error instanceof TaskExecutionLockError) return result('blocked', resolved.taskId, null, null, {
|
|
1685
|
-
error: { code: error.code, message: error.message, retryable: true }
|
|
1686
|
-
});
|
|
1687
1666
|
return result('failed', resolved.taskId, null, null, {
|
|
1688
1667
|
error: { code: 'PR_SKIP_FAILED', message: error instanceof Error ? error.message : String(error), retryable: false }
|
|
1689
1668
|
});
|
|
@@ -5,6 +5,7 @@ import { parseTypedTaskFrontmatter } from '../task/frontmatter.ts';
|
|
|
5
5
|
import { readPrDeliveryFact } from '../task/pr-delivery-fact.ts';
|
|
6
6
|
import { resourceIdentityEquals, resourceIdentityNumber } from '../platform/resource-identity.ts';
|
|
7
7
|
import type { ResourceIdentity } from '../platform/resource-identity.ts';
|
|
8
|
+
import { resolveProjectTaskWorkspaceRoot } from '../task/workspace-root.ts';
|
|
8
9
|
import { taskIssueIdentity } from '../platform/task-identities.ts';
|
|
9
10
|
|
|
10
11
|
// ---------------------------------------------------------------------------
|
|
@@ -61,7 +62,7 @@ export function extractClosingIssueNumbers(body: string): number[] {
|
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
/**
|
|
64
|
-
* Scan
|
|
65
|
+
* Scan active project task workspace task.md files for tasks bound to the
|
|
65
66
|
* PR number or to one of the PR's closing issues. A task is emitted at most once;
|
|
66
67
|
* a verified fact identity hit takes priority over the issue-identity reverse lookup.
|
|
67
68
|
*/
|
|
@@ -71,7 +72,7 @@ export function collectHostCandidates(input: {
|
|
|
71
72
|
closingIssues: number[];
|
|
72
73
|
workspaceRoot: string;
|
|
73
74
|
}): HostCandidate[] {
|
|
74
|
-
const activeRoot = path.join(input.workspaceRoot, '
|
|
75
|
+
const activeRoot = path.join(resolveProjectTaskWorkspaceRoot(input.workspaceRoot), 'active');
|
|
75
76
|
const candidates: HostCandidate[] = [];
|
|
76
77
|
let entries: fs.Dirent[];
|
|
77
78
|
try {
|
|
@@ -3,6 +3,7 @@ import os from 'node:os';
|
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
|
|
5
5
|
import { detectRepoRoot } from '../task/resolve-ref.ts';
|
|
6
|
+
import { resolveProjectTaskWorkspaceRoot } from '../task/workspace-root.ts';
|
|
6
7
|
import { acquireGitHubCheckpoint, commitGitHubCheckpoint, readGitHubCheckpoint, releaseGitHubCheckpoint } from './checkpoint.ts';
|
|
7
8
|
import { materializeJson, materializeSnapshot } from './materialize.ts';
|
|
8
9
|
import { normalizeResources } from './normalize.ts';
|
|
@@ -64,13 +65,13 @@ function dataRoot(repoRoot: string, requested?: string): string {
|
|
|
64
65
|
input = path.join(boundary, '.agent-infra', 'data-store', config.project);
|
|
65
66
|
}
|
|
66
67
|
const resolved = path.resolve(repoRoot, input);
|
|
67
|
-
const
|
|
68
|
-
if (requested !== undefined && (!relative || relative.startsWith('..') || path.isAbsolute(relative))) throw new Error('data root must be inside the repository');
|
|
69
|
-
const workspace = path.resolve(repoRoot, '.agents', 'workspace');
|
|
68
|
+
const workspace = resolveProjectTaskWorkspaceRoot(repoRoot);
|
|
70
69
|
const taskRoots = ['active', 'blocked', 'completed', 'archive'].map((state) => path.join(workspace, state));
|
|
71
70
|
if (resolved === workspace || taskRoots.some((taskRoot) => resolved === taskRoot || resolved.startsWith(`${taskRoot}${path.sep}`))) {
|
|
72
71
|
throw new Error('data root cannot be the workspace root or a task directory');
|
|
73
72
|
}
|
|
73
|
+
const relative = path.relative(repoRoot, resolved);
|
|
74
|
+
if (requested !== undefined && (!relative || relative.startsWith('..') || path.isAbsolute(relative))) throw new Error('data root must be inside the repository');
|
|
74
75
|
let current = resolved;
|
|
75
76
|
while (current !== boundary) {
|
|
76
77
|
if (fs.existsSync(current) && fs.lstatSync(current).isSymbolicLink()) throw new Error('data root cannot traverse a symlink');
|
|
@@ -3,6 +3,7 @@ import path from 'node:path';
|
|
|
3
3
|
|
|
4
4
|
import { createGitHubClient } from '../platform/github-client.ts';
|
|
5
5
|
import { enumerateAllTaskDirs } from '../task/resolve-ref.ts';
|
|
6
|
+
import { resolveProjectTaskWorkspaceRoot } from '../task/workspace-root.ts';
|
|
6
7
|
import { captureText, inspectPrivacy, redactExcerpt } from './privacy.ts';
|
|
7
8
|
import { canonicalJsonBytes, sha256 } from './store.ts';
|
|
8
9
|
|
|
@@ -157,6 +158,11 @@ function fetchRestCollection<T>(options: FetchRestCollectionOptions<T>): Process
|
|
|
157
158
|
}
|
|
158
159
|
|
|
159
160
|
function relativeIdentity(repoRoot: string, filePath: string): string {
|
|
161
|
+
const workspaceRoot = resolveProjectTaskWorkspaceRoot(repoRoot);
|
|
162
|
+
const taskRelative = path.relative(workspaceRoot, filePath);
|
|
163
|
+
if (taskRelative && !taskRelative.startsWith('..') && !path.isAbsolute(taskRelative)) {
|
|
164
|
+
return path.posix.join('task-workspace', taskRelative.split(path.sep).join('/'));
|
|
165
|
+
}
|
|
160
166
|
const relative = path.relative(repoRoot, filePath);
|
|
161
167
|
if (!relative || relative.startsWith('..') || path.isAbsolute(relative)) throw new Error(`path escapes repository: ${filePath}`);
|
|
162
168
|
return relative.split(path.sep).join('/');
|
package/lib/run/index.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { loadServerConfig } from '../server/config.ts';
|
|
|
4
4
|
import { resolveSandboxTarget, type SandboxWorkspaceIdentity } from '../sandbox/workspace-identity.ts';
|
|
5
5
|
import { createRunId, runInSandbox, type SandboxRunMetadata } from '../sandbox/capture.ts';
|
|
6
6
|
import { loadShortIdByTaskId, normalizeShortIdInput } from '../task/short-id.ts';
|
|
7
|
+
import { resolveProjectTaskWorkspaceRoot } from '../task/workspace-root.ts';
|
|
7
8
|
import { buildTuiCommand, renderPrompt, selectTui } from './tui.ts';
|
|
8
9
|
import { getSkillRunSpec } from './skills.ts';
|
|
9
10
|
import { runHostCommand, type RunProcessResult } from './host.ts';
|
|
@@ -157,7 +158,7 @@ function readShortIdLength(repoRoot: string): number {
|
|
|
157
158
|
}
|
|
158
159
|
|
|
159
160
|
function readActiveShortIdRegistry(repoRoot: string): Record<string, string> {
|
|
160
|
-
const registryPath = path.join(repoRoot, '
|
|
161
|
+
const registryPath = path.join(resolveProjectTaskWorkspaceRoot(repoRoot), 'active', '.short-ids.json');
|
|
161
162
|
try {
|
|
162
163
|
const data = JSON.parse(fs.readFileSync(registryPath, 'utf8'));
|
|
163
164
|
return data && typeof data === 'object' && data.ids && typeof data.ids === 'object' ? data.ids : {};
|
|
@@ -186,7 +187,7 @@ function resolveActiveTaskIdentity(taskRef: string, repoRoot: string): ActiveTas
|
|
|
186
187
|
}
|
|
187
188
|
|
|
188
189
|
if (!taskId) return null;
|
|
189
|
-
const taskDir = path.join(repoRoot, '
|
|
190
|
+
const taskDir = path.join(resolveProjectTaskWorkspaceRoot(repoRoot), 'active', taskId);
|
|
190
191
|
if (!fs.existsSync(path.join(taskDir, 'task.md'))) return null;
|
|
191
192
|
return { taskId, taskDir, taskRef: resolvedTaskRef };
|
|
192
193
|
}
|
|
@@ -1454,7 +1454,7 @@ export async function create(
|
|
|
1454
1454
|
const completedTaskSource = target.workspace.mode === 'task-bound'
|
|
1455
1455
|
? prepareSandboxCompletedTaskMountSource(effectiveConfig.repoRoot, target.workspace.taskId)
|
|
1456
1456
|
: null;
|
|
1457
|
-
prepareSandboxWorkspaceMountTargets(
|
|
1457
|
+
prepareSandboxWorkspaceMountTargets(effectiveConfig.repoRoot);
|
|
1458
1458
|
if (previousCutoverSnapshot) {
|
|
1459
1459
|
assertSandboxControlCutoverSnapshot(previousCutoverSnapshot);
|
|
1460
1460
|
if (previousManifest) {
|
|
@@ -1603,6 +1603,10 @@ export async function create(
|
|
|
1603
1603
|
: []),
|
|
1604
1604
|
'--label',
|
|
1605
1605
|
`${sandboxRuntimeCapabilityLabel(effectiveConfig)}=${capabilityPlan.runtimeSignature}`,
|
|
1606
|
+
// Every task sandbox runs without seccomp and AppArmor filtering;
|
|
1607
|
+
// capabilities and namespace isolation remain unchanged.
|
|
1608
|
+
'--security-opt', 'seccomp=unconfined',
|
|
1609
|
+
'--security-opt', 'apparmor=unconfined',
|
|
1606
1610
|
...coreVolumes,
|
|
1607
1611
|
...buildClipboardVolumeArgs(engine, effectiveConfig.home),
|
|
1608
1612
|
...dotfilesMount,
|
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
const USAGE = `Usage: ai sandbox exec [--recreate] <branch | TASK-id | N> [cmd...]
|
|
32
32
|
|
|
33
33
|
N references an active task short id from
|
|
34
|
-
|
|
34
|
+
the read-only task-workspace mount at /task-workspace/active/.short-ids.json. They resolve only via that
|
|
35
35
|
registry — they do not reference a container's row position in
|
|
36
36
|
'ai sandbox ls' output. --recreate rebuilds the matched container while
|
|
37
37
|
preserving its task or branch identity. The same token after the target is
|
|
@@ -5,6 +5,7 @@ import { runSafeEngine, runVerboseEngine } from '../shell.ts';
|
|
|
5
5
|
import { resolveTaskBranch } from '../task-resolver.ts';
|
|
6
6
|
import { isRemovedHashShortIdInput } from '../../task/short-id.ts';
|
|
7
7
|
import { parseSandboxWorkspaceIdentity } from '../workspace-identity.ts';
|
|
8
|
+
import { resolveProjectTaskWorkspaceRoot } from '../../task/workspace-root.ts';
|
|
8
9
|
|
|
9
10
|
export type SandboxRow = {
|
|
10
11
|
name: string;
|
|
@@ -143,7 +144,7 @@ function tryResolveFromRegistry(arg: string, repoRoot: string): RegistryLookup {
|
|
|
143
144
|
);
|
|
144
145
|
}
|
|
145
146
|
for (const sub of ['active', 'completed', 'blocked', 'archive']) {
|
|
146
|
-
const taskMdPath = path.join(repoRoot,
|
|
147
|
+
const taskMdPath = path.join(resolveProjectTaskWorkspaceRoot(repoRoot), sub, taskId, 'task.md');
|
|
147
148
|
if (!fs.existsSync(taskMdPath)) continue;
|
|
148
149
|
const content = fs.readFileSync(taskMdPath, 'utf8');
|
|
149
150
|
const fm = content.match(/^branch:\s*(.+)$/m);
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { randomUUID } from 'node:crypto';
|
|
4
|
+
import { resolveProjectTaskWorkspaceRoot } from '../../task/workspace-root.ts';
|
|
5
|
+
import { detectRepoRoot } from '../../task/resolve-ref.ts';
|
|
4
6
|
import {
|
|
5
7
|
SANDBOX_CONTROL_MAX_BYTES,
|
|
6
8
|
SANDBOX_CONTROL_STATUS_STALE_MS,
|
|
@@ -158,18 +160,11 @@ type CanonicalRequestTask =
|
|
|
158
160
|
|
|
159
161
|
// Use the current short-id contract for the first visible active registry.
|
|
160
162
|
export function resolveVisibleActiveShortId(ref: string): string | null {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
return resolved.ok ? resolved.taskId : null;
|
|
167
|
-
}
|
|
168
|
-
const parent = path.dirname(dir);
|
|
169
|
-
if (parent === dir) break;
|
|
170
|
-
dir = parent;
|
|
171
|
-
}
|
|
172
|
-
return null;
|
|
163
|
+
const repoRoot = detectRepoRoot();
|
|
164
|
+
const registryPath = path.join(resolveProjectTaskWorkspaceRoot(repoRoot), 'active', '.short-ids.json');
|
|
165
|
+
if (!fs.existsSync(registryPath)) return null;
|
|
166
|
+
const resolved = resolveShortIdReadOnly(ref, repoRoot, { shortIdLength: configuredShortIdLength(repoRoot) });
|
|
167
|
+
return resolved.ok ? resolved.taskId : null;
|
|
173
168
|
}
|
|
174
169
|
|
|
175
170
|
// A request's leading argument is only a task reference for some families and
|
|
@@ -4,6 +4,7 @@ import { resolveTaskWorkspace } from '../task-resolver.ts';
|
|
|
4
4
|
import { assertGitWorktreeBinding } from '../../git/worktree-identity.ts';
|
|
5
5
|
import { parseTypedTaskFrontmatter } from '../../task/frontmatter.ts';
|
|
6
6
|
import { readTaskFinalizationReceipt } from '../../task/finalization.ts';
|
|
7
|
+
import { resolveProjectTaskWorkspaceRoot } from '../../task/workspace-root.ts';
|
|
7
8
|
import { inspectSandboxControlContainer } from './container-identity.ts';
|
|
8
9
|
import type { SandboxControlManifest } from './protocol.ts';
|
|
9
10
|
import { atomicWriteJson, readSandboxControlStatus } from './state.ts';
|
|
@@ -53,7 +54,7 @@ export async function prepareCompletedReentry(
|
|
|
53
54
|
|| previous.receipt.generation !== view.receipt.generation
|
|
54
55
|
|| previous.receipt.requestId !== view.receipt.requestId
|
|
55
56
|
|| previous.receipt.revision > view.receipt.revision) throw invalid();
|
|
56
|
-
const completedRoot = fs.realpathSync.native(path.join(manifest.repoRoot, '
|
|
57
|
+
const completedRoot = fs.realpathSync.native(path.join(resolveProjectTaskWorkspaceRoot(manifest.repoRoot), 'completed'));
|
|
57
58
|
const source = fs.realpathSync.native(path.join(completedRoot, manifest.taskId));
|
|
58
59
|
if (source !== path.join(completedRoot, manifest.taskId)) throw invalid();
|
|
59
60
|
const stat = fs.statSync(source, { bigint: true });
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
+
import { resolveProjectTaskWorkspaceRoot } from '../../task/workspace-root.ts';
|
|
3
4
|
|
|
4
5
|
export const SANDBOX_TASK_VIEW_STATES = ['not-applicable', 'current', 'finalized-stale', 'unknown'] as const;
|
|
5
6
|
export const SANDBOX_TASK_VIEW_SOURCES = ['active', 'completed', 'unknown'] as const;
|
|
@@ -119,8 +120,9 @@ export function taskViewForManifest(params: Readonly<{
|
|
|
119
120
|
if (params.mode !== 'task-bound' || !params.taskId) {
|
|
120
121
|
return { state: 'not-applicable', taskId: null, observedSource: null, receipt: null, reasonCode: null };
|
|
121
122
|
}
|
|
122
|
-
const
|
|
123
|
-
const
|
|
123
|
+
const taskRoot = resolveProjectTaskWorkspaceRoot(params.repoRoot);
|
|
124
|
+
const active = path.join(taskRoot, 'active', params.taskId, 'task.md');
|
|
125
|
+
const completed = path.join(taskRoot, 'completed', params.taskId, 'task.md');
|
|
124
126
|
const activeExists = fs.existsSync(active);
|
|
125
127
|
const completedExists = fs.existsSync(completed);
|
|
126
128
|
if (activeExists === completedExists) {
|
package/lib/sandbox/mounts.ts
CHANGED
|
@@ -24,12 +24,12 @@ function workspaceStateMounts(workspaceViewRoot: string, taskBound: boolean): Sa
|
|
|
24
24
|
.map(({ state, hostPath }) => taskBound && state === 'active'
|
|
25
25
|
? {
|
|
26
26
|
hostPaths: [path.join(hostPath, '.short-ids.json')],
|
|
27
|
-
containerPath: '/workspace
|
|
27
|
+
containerPath: '/task-workspace/active/.short-ids.json',
|
|
28
28
|
readOnly: true
|
|
29
29
|
}
|
|
30
30
|
: {
|
|
31
31
|
hostPaths: [hostPath],
|
|
32
|
-
containerPath: path.posix.join('/workspace
|
|
32
|
+
containerPath: path.posix.join('/task-workspace', state),
|
|
33
33
|
readOnly: true
|
|
34
34
|
}
|
|
35
35
|
);
|
|
@@ -60,12 +60,12 @@ function taskWorkspaceMounts(taskSources: string[], taskBound: boolean, override
|
|
|
60
60
|
const taskId = overrides.taskId!;
|
|
61
61
|
const mounts: SandboxBindMountDeclaration[] = [{
|
|
62
62
|
hostPaths: taskSources,
|
|
63
|
-
containerPath: path.posix.join('/workspace
|
|
63
|
+
containerPath: path.posix.join('/task-workspace/active', taskId),
|
|
64
64
|
readOnly: false
|
|
65
65
|
}];
|
|
66
66
|
if (overrides.completedTaskSource) mounts.push({
|
|
67
67
|
hostPaths: [overrides.completedTaskSource],
|
|
68
|
-
containerPath: path.posix.join('/workspace
|
|
68
|
+
containerPath: path.posix.join('/task-workspace/completed', taskId),
|
|
69
69
|
readOnly: false
|
|
70
70
|
});
|
|
71
71
|
return mounts;
|
package/lib/sandbox/recovery.ts
CHANGED
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
sandboxWorkspaceViewPaths
|
|
31
31
|
} from './workspace-view.ts';
|
|
32
32
|
import { resolveTaskWorkspace } from './task-resolver.ts';
|
|
33
|
+
import { resolveProjectTaskWorkspaceRoot } from '../task/workspace-root.ts';
|
|
33
34
|
import {
|
|
34
35
|
parseSandboxWorkspaceIdentity,
|
|
35
36
|
sameSandboxWorkspaceIdentity,
|
|
@@ -450,11 +451,13 @@ export function classifySandboxRecovery(snapshot: SandboxRecoverySnapshot): Sand
|
|
|
450
451
|
});
|
|
451
452
|
}
|
|
452
453
|
if (snapshot.workspaceTopology === 'legacy-parent') {
|
|
454
|
+
const legacyRoot = snapshot.mounts.some((mount) => mount.path === '/task-workspace')
|
|
455
|
+
? '/task-workspace' : '/workspace/.agents/workspace';
|
|
453
456
|
findings.push({
|
|
454
457
|
repairKind: 'hard-failure',
|
|
455
458
|
code: 'SANDBOX_WORKSPACE_TOPOLOGY_MISMATCH',
|
|
456
459
|
message: 'Sandbox uses the legacy workspace parent-mount topology; recreate the container explicitly to migrate it.',
|
|
457
|
-
path:
|
|
460
|
+
path: legacyRoot
|
|
458
461
|
});
|
|
459
462
|
}
|
|
460
463
|
if (snapshot.taskView && !snapshot.taskView.readable) {
|
|
@@ -476,7 +479,8 @@ export function classifySandboxRecovery(snapshot: SandboxRecoverySnapshot): Sand
|
|
|
476
479
|
for (const mount of snapshot.mounts) {
|
|
477
480
|
if (
|
|
478
481
|
snapshot.workspaceTopology === 'legacy-parent'
|
|
479
|
-
&& mount.path.startsWith('/workspace/.agents/workspace')
|
|
482
|
+
&& (mount.path.startsWith('/workspace/.agents/workspace')
|
|
483
|
+
|| mount.path.startsWith('/task-workspace'))
|
|
480
484
|
) continue;
|
|
481
485
|
if (
|
|
482
486
|
mount.actualType !== mount.expectedType
|
|
@@ -571,13 +575,14 @@ function inspectContainer(
|
|
|
571
575
|
}
|
|
572
576
|
|
|
573
577
|
function workspaceTopology(mountsByDestination: ReadonlyMap<string, DockerMount>): SandboxRecoverySnapshot['workspaceTopology'] {
|
|
574
|
-
if (mountsByDestination.has('/workspace/.agents/workspace')
|
|
578
|
+
if (mountsByDestination.has('/workspace/.agents/workspace')
|
|
579
|
+
|| mountsByDestination.has('/task-workspace')) return 'legacy-parent';
|
|
575
580
|
const perState = [
|
|
576
|
-
'/workspace
|
|
577
|
-
'/workspace
|
|
578
|
-
'/workspace
|
|
579
|
-
'/workspace
|
|
580
|
-
'/workspace
|
|
581
|
+
'/task-workspace/active',
|
|
582
|
+
'/task-workspace/active/.short-ids.json',
|
|
583
|
+
'/task-workspace/completed',
|
|
584
|
+
'/task-workspace/blocked',
|
|
585
|
+
'/task-workspace/archive'
|
|
581
586
|
];
|
|
582
587
|
return perState.some((destination) => mountsByDestination.has(destination))
|
|
583
588
|
? 'per-state'
|
|
@@ -618,8 +623,9 @@ function recoveryTaskSources(repoRoot: string, taskId: string): {
|
|
|
618
623
|
mountPaths: string[];
|
|
619
624
|
accessiblePaths: string[];
|
|
620
625
|
} {
|
|
621
|
-
const
|
|
622
|
-
const
|
|
626
|
+
const taskRoot = resolveProjectTaskWorkspaceRoot(repoRoot);
|
|
627
|
+
const activePath = path.join(taskRoot, 'active', taskId);
|
|
628
|
+
const completedPath = path.join(taskRoot, 'completed', taskId);
|
|
623
629
|
if (fs.existsSync(activePath) && !fs.existsSync(completedPath)) {
|
|
624
630
|
const source = assertSandboxTaskSource(repoRoot, taskId);
|
|
625
631
|
return { mountPaths: [source], accessiblePaths: [source] };
|
|
@@ -635,7 +641,7 @@ function recoveryTaskSources(repoRoot: string, taskId: string): {
|
|
|
635
641
|
}
|
|
636
642
|
|
|
637
643
|
const completedRoot = fs.realpathSync.native(
|
|
638
|
-
path.join(
|
|
644
|
+
path.join(taskRoot, 'completed')
|
|
639
645
|
);
|
|
640
646
|
const completedSource = fs.realpathSync.native(path.dirname(task.taskMd));
|
|
641
647
|
if (path.dirname(completedSource) !== completedRoot) {
|
|
@@ -643,9 +649,7 @@ function recoveryTaskSources(repoRoot: string, taskId: string): {
|
|
|
643
649
|
}
|
|
644
650
|
|
|
645
651
|
const historicalActivePath = path.join(
|
|
646
|
-
|
|
647
|
-
'.agents',
|
|
648
|
-
'workspace',
|
|
652
|
+
taskRoot,
|
|
649
653
|
'active',
|
|
650
654
|
taskId
|
|
651
655
|
);
|
|
@@ -670,7 +674,7 @@ export function startCompletedSandboxContainer(params: {
|
|
|
670
674
|
}): void {
|
|
671
675
|
const taskSources = recoveryTaskSources(params.config.repoRoot, params.taskId);
|
|
672
676
|
const completedSource = taskSources.accessiblePaths[0]!;
|
|
673
|
-
const activeRoot = path.join(params.config.repoRoot, '
|
|
677
|
+
const activeRoot = path.join(resolveProjectTaskWorkspaceRoot(params.config.repoRoot), 'active');
|
|
674
678
|
const historicalSource = path.join(activeRoot, params.taskId);
|
|
675
679
|
fs.mkdirSync(activeRoot, { recursive: true });
|
|
676
680
|
let replacedEmptyRoot = false;
|
|
@@ -791,11 +795,11 @@ function expectedMounts(params: {
|
|
|
791
795
|
expectedType: 'bind' as const,
|
|
792
796
|
hostPaths: mount.hostPaths,
|
|
793
797
|
...(taskSources !== null && taskId !== null
|
|
794
|
-
&& mount.containerPath === `/workspace
|
|
798
|
+
&& mount.containerPath === `/task-workspace/active/${taskId}`
|
|
795
799
|
? { sourceAccessiblePaths: taskSources!.accessiblePaths }
|
|
796
800
|
: {}),
|
|
797
801
|
...(completedTaskSource !== null && taskId !== null
|
|
798
|
-
&& mount.containerPath === `/workspace
|
|
802
|
+
&& mount.containerPath === `/task-workspace/completed/${taskId}`
|
|
799
803
|
? { sourceAccessiblePaths: [completedTaskSource] }
|
|
800
804
|
: {}),
|
|
801
805
|
expectedRW: !mount.readOnly
|
|
@@ -929,7 +933,7 @@ export function collectSandboxRecoverySnapshot(params: {
|
|
|
929
933
|
const tools = [...capabilityPlan.tools];
|
|
930
934
|
const topology = workspaceTopology(mountsByDestination);
|
|
931
935
|
const taskViewPath = workspace.mode === 'task-bound'
|
|
932
|
-
? `/workspace
|
|
936
|
+
? `/task-workspace/active/${workspace.taskId}/task.md`
|
|
933
937
|
: null;
|
|
934
938
|
const seeds = declaredTmpfsSeedEntries(tools).map((seed) => {
|
|
935
939
|
const mounted = mountsByDestination.get(seed.stagingPath)?.Type === 'bind';
|
|
@@ -1215,6 +1219,10 @@ function canRetryFreshReadiness(findings: SandboxRecoveryFinding[]): boolean {
|
|
|
1215
1219
|
&& finding.path !== undefined
|
|
1216
1220
|
&& (
|
|
1217
1221
|
finding.path.startsWith('/workspace/.agents/workspace')
|
|
1222
|
+
|| finding.path.startsWith('/task-workspace/active')
|
|
1223
|
+
|| finding.path.startsWith('/task-workspace/blocked')
|
|
1224
|
+
|| finding.path.startsWith('/task-workspace/completed')
|
|
1225
|
+
|| finding.path.startsWith('/task-workspace/archive')
|
|
1218
1226
|
|| finding.path === '/home/devuser/.host-shell-config'
|
|
1219
1227
|
|| finding.path === '/home/devuser/.bash_aliases'
|
|
1220
1228
|
|| finding.path === '/run/agent-infra/runtime'
|
package/lib/sandbox/removal.ts
CHANGED
|
@@ -48,7 +48,6 @@ import { toolConfigDirCandidates, toolProjectDirCandidates } from './tools.ts';
|
|
|
48
48
|
import type { SandboxTool } from './tools.ts';
|
|
49
49
|
import { getProcessStartTime } from '../server/process-state.ts';
|
|
50
50
|
import { releaseStaleShortIdRegistry } from '../task/short-id.ts';
|
|
51
|
-
import { withRepositoryMutationLock } from '../task/task-execution-lock.ts';
|
|
52
51
|
import { fetchSandboxRows, type SandboxRow } from './commands/list-running.ts';
|
|
53
52
|
import { createSandboxControlBindingEvidence } from './task-cleanup.ts';
|
|
54
53
|
import {
|
|
@@ -933,7 +932,7 @@ async function authorizeWorktrees(
|
|
|
933
932
|
return permits;
|
|
934
933
|
}
|
|
935
934
|
|
|
936
|
-
async function
|
|
935
|
+
async function runRmOne(
|
|
937
936
|
config: SandboxConfig,
|
|
938
937
|
tools: SandboxTool[],
|
|
939
938
|
branch: string,
|
|
@@ -953,10 +952,7 @@ async function rmOne(
|
|
|
953
952
|
branch: string,
|
|
954
953
|
options: RmOneOptions = {}
|
|
955
954
|
): Promise<void> {
|
|
956
|
-
await
|
|
957
|
-
config.repoRoot,
|
|
958
|
-
() => runRmOneUnderRepositoryLock(config, tools, branch, options)
|
|
959
|
-
);
|
|
955
|
+
await runRmOne(config, tools, branch, options);
|
|
960
956
|
}
|
|
961
957
|
|
|
962
958
|
async function removeUncheckedSandbox(
|
|
@@ -1060,14 +1056,6 @@ async function rmPurge(
|
|
|
1060
1056
|
config: SandboxConfig,
|
|
1061
1057
|
tools: SandboxTool[],
|
|
1062
1058
|
prompt: PromptDependencies = {}
|
|
1063
|
-
): Promise<void> {
|
|
1064
|
-
return withRepositoryMutationLock(config.repoRoot, () => rmPurgeCore(config, tools, prompt));
|
|
1065
|
-
}
|
|
1066
|
-
|
|
1067
|
-
async function rmPurgeCore(
|
|
1068
|
-
config: SandboxConfig,
|
|
1069
|
-
tools: SandboxTool[],
|
|
1070
|
-
prompt: PromptDependencies = {}
|
|
1071
1059
|
): Promise<void> {
|
|
1072
1060
|
const engine = detectEngine(config);
|
|
1073
1061
|
const confirm = prompt.confirm ?? p.confirm;
|
|
@@ -1142,17 +1130,6 @@ async function rmUnbound(
|
|
|
1142
1130
|
config: SandboxConfig,
|
|
1143
1131
|
tools: SandboxTool[],
|
|
1144
1132
|
options: { dryRun: boolean; assumeYes: boolean }
|
|
1145
|
-
): Promise<void> {
|
|
1146
|
-
return withRepositoryMutationLock(
|
|
1147
|
-
config.repoRoot,
|
|
1148
|
-
() => rmUnboundCore(config, tools, options)
|
|
1149
|
-
);
|
|
1150
|
-
}
|
|
1151
|
-
|
|
1152
|
-
async function rmUnboundCore(
|
|
1153
|
-
config: SandboxConfig,
|
|
1154
|
-
tools: SandboxTool[],
|
|
1155
|
-
options: { dryRun: boolean; assumeYes: boolean }
|
|
1156
1133
|
): Promise<void> {
|
|
1157
1134
|
const engine = detectEngine(config);
|
|
1158
1135
|
const listed = fetchSandboxRows(
|
|
@@ -1215,7 +1192,7 @@ async function rmUnboundCore(
|
|
|
1215
1192
|
return;
|
|
1216
1193
|
}
|
|
1217
1194
|
for (const { row, cleanupTarget, target } of targets) {
|
|
1218
|
-
await
|
|
1195
|
+
await runRmOne(config, tools, cleanupTarget.branch, {
|
|
1219
1196
|
assumeYes: options.assumeYes,
|
|
1220
1197
|
target,
|
|
1221
1198
|
cleanupTarget
|