@fitlab-ai/agent-infra 0.11.14 → 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-finalization.js +2 -2
- 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-change-report.js +10 -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 +18 -6
- 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/lifecycle.js +2 -21
- 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 +36 -20
- 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 +38 -49
- 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 +12 -10
- 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-finalization.ts +2 -2
- 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-change-report.ts +9 -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 +17 -5
- 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/lifecycle.ts +4 -21
- 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 +42 -18
- 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 +40 -51
- 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 +15 -10
- 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 +17 -9
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +18 -10
- 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 +13 -3
- package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +13 -3
- 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/SKILL.en.md +5 -5
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +5 -5
- 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-manual-validation/reference/discovery-and-execution.en.md +13 -2
- package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.zh-CN.md +13 -2
- package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +2 -0
- package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +2 -0
- 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 +21 -4
- package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +22 -5
- package/templates/.agents/skills/watch-pr/config/verify.json +0 -1
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +14 -0
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +15 -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/runtime/agent-runtime.js +0 -33
- package/dist/lib/task/runtime-paths.js +0 -10
- package/dist/lib/task/task-execution-lock.js +0 -250
- package/lib/runtime/agent-runtime.ts +0 -41
- package/lib/task/runtime-paths.ts +0 -12
- package/lib/task/task-execution-lock.ts +0 -320
package/dist/lib/merge.js
CHANGED
|
@@ -4,6 +4,7 @@ import path from 'node:path';
|
|
|
4
4
|
import crypto from 'node:crypto';
|
|
5
5
|
import { removeDirRecursive } from './remove-dir.js';
|
|
6
6
|
import { validateCurrentTaskContract } from './task/current-contract.js';
|
|
7
|
+
import { resolveProjectTaskWorkspaceRoot } from './task/workspace-root.js';
|
|
7
8
|
const TASK_ID_RE = /^TASK-\d{8}-\d{6}$/;
|
|
8
9
|
const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/;
|
|
9
10
|
const TITLE_RE = /^# (.+)$/m;
|
|
@@ -800,7 +801,7 @@ function mergeArchiveSection(sourceArchive, localArchive, report) {
|
|
|
800
801
|
return sourceTasks.length;
|
|
801
802
|
}
|
|
802
803
|
function printSection(lines, name, counts) {
|
|
803
|
-
const title = `${SECTION_LABELS[name].padEnd(9, ' ')} (
|
|
804
|
+
const title = `${SECTION_LABELS[name].padEnd(9, ' ')} (task workspace/${name}/):`;
|
|
804
805
|
lines.push(title);
|
|
805
806
|
const allEntries = [
|
|
806
807
|
['copied', '✓ Copied '],
|
|
@@ -820,7 +821,7 @@ function printSection(lines, name, counts) {
|
|
|
820
821
|
lines.push('');
|
|
821
822
|
}
|
|
822
823
|
function printArchiveSection(lines, counts) {
|
|
823
|
-
const title = `${SECTION_LABELS.archive.padEnd(9, ' ')} (
|
|
824
|
+
const title = `${SECTION_LABELS.archive.padEnd(9, ' ')} (task workspace/archive/):`;
|
|
824
825
|
lines.push(title);
|
|
825
826
|
if (counts.copied.length === 0 && counts.skipped.length === 0) {
|
|
826
827
|
lines.push(' (no changes)', '');
|
|
@@ -891,7 +892,7 @@ async function cmdMerge(args) {
|
|
|
891
892
|
throw new Error(`Source path is not a directory: ${sourcePath}`);
|
|
892
893
|
}
|
|
893
894
|
const repoRoot = process.cwd();
|
|
894
|
-
const workspaceDir =
|
|
895
|
+
const workspaceDir = resolveProjectTaskWorkspaceRoot(repoRoot);
|
|
895
896
|
const archiveDir = path.join(workspaceDir, 'archive');
|
|
896
897
|
const backupStamp = formatBackupTimestamp(new Date());
|
|
897
898
|
const projectKey = resolveRecoveryProjectKey(repoRoot);
|
|
@@ -379,19 +379,26 @@ function runMechanicalChangeReport(repoRoot, baseSha, headSha) {
|
|
|
379
379
|
throw new Error(`${validated.error.code}: ${validated.error.message}`);
|
|
380
380
|
return validated.value;
|
|
381
381
|
}
|
|
382
|
+
const DOCUMENTATION_CATEGORY = { label: '文档', prefixes: ['docs/'] };
|
|
382
383
|
const CHANGE_CATEGORIES = [
|
|
383
384
|
{ label: '运行时代码', prefixes: ['bin/', 'lib/', 'src/', 'scripts/'] },
|
|
384
385
|
{ label: '测试与校验', prefixes: ['test/', 'tests/'] },
|
|
385
386
|
{ label: '模板与生成内容', prefixes: ['templates/'] },
|
|
386
387
|
{ label: '技能与协作配置', prefixes: ['.agents/'] },
|
|
387
|
-
|
|
388
|
+
DOCUMENTATION_CATEGORY,
|
|
389
|
+
{ label: '其他', prefixes: [] }
|
|
388
390
|
];
|
|
389
391
|
function emptyChangeTotals() {
|
|
390
392
|
return { files: 0, textFiles: 0, binaryFiles: 0, additions: 0, deletions: 0, oldBytes: 0, newBytes: 0, netBytes: 0 };
|
|
391
393
|
}
|
|
392
394
|
function categoryForChangeFile(file) {
|
|
393
395
|
const filePath = file.newPath || file.oldPath || '';
|
|
394
|
-
|
|
396
|
+
const category = CHANGE_CATEGORIES.find((entry) => entry.prefixes.some((prefix) => prefix && filePath.startsWith(prefix)));
|
|
397
|
+
if (category)
|
|
398
|
+
return category;
|
|
399
|
+
if (/\.(?:md|mdx|markdown|rst)$/i.test(filePath))
|
|
400
|
+
return DOCUMENTATION_CATEGORY;
|
|
401
|
+
return CHANGE_CATEGORIES[CHANGE_CATEGORIES.length - 1];
|
|
395
402
|
}
|
|
396
403
|
function summarizeChangeCategories(files) {
|
|
397
404
|
const summaries = new Map();
|
|
@@ -492,7 +499,7 @@ function renderEvidence(evidence) {
|
|
|
492
499
|
function renderPrechecks(checks) {
|
|
493
500
|
return checks.flatMap((check) => [
|
|
494
501
|
`- **${PRECHECK_LABELS[check.id]}(${check.id})**:${check.verdict === 'pass' ? '通过' : '需复核'}。${escapeReportText(check.rationale)}`,
|
|
495
|
-
...check.evidence.
|
|
502
|
+
...check.evidence.map(renderEvidence)
|
|
496
503
|
]);
|
|
497
504
|
}
|
|
498
505
|
function renderCanonicalChangeReport(report) {
|
|
@@ -4,7 +4,6 @@ import { createHash } from 'node:crypto';
|
|
|
4
4
|
import { enumerateArtifacts } from '../task/artifacts.js';
|
|
5
5
|
import { parseTypedTaskFrontmatter } from '../task/frontmatter.js';
|
|
6
6
|
import { resolveTaskRef } from '../task/resolve-ref.js';
|
|
7
|
-
import { TaskExecutionLockError, withTaskExecutionLock } from '../task/task-execution-lock.js';
|
|
8
7
|
import { readPrDeliveryFact } from '../task/pr-delivery-fact.js';
|
|
9
8
|
import { resolvePlatformProviderContext } from './context.js';
|
|
10
9
|
import { normalizeCommentContent } from './issue-comments.js';
|
|
@@ -348,7 +347,7 @@ async function reportWrite(taskRef, options) {
|
|
|
348
347
|
return { ...context, report: null };
|
|
349
348
|
let knownPrNumber = null;
|
|
350
349
|
try {
|
|
351
|
-
return await
|
|
350
|
+
return await (async () => {
|
|
352
351
|
const { taskContent, boundFact, factError } = readBoundTaskSnapshot(resolved.taskMdPath);
|
|
353
352
|
if (factError)
|
|
354
353
|
return {
|
|
@@ -433,13 +432,10 @@ async function reportWrite(taskRef, options) {
|
|
|
433
432
|
operations: [{ name: 'change-report:write', status: 'applied', reasonCode: null }],
|
|
434
433
|
report: { ...reportInfo, status: 'written' }
|
|
435
434
|
};
|
|
436
|
-
});
|
|
435
|
+
})();
|
|
437
436
|
}
|
|
438
437
|
catch (error) {
|
|
439
|
-
|
|
440
|
-
? { code: error.code, message: error.message, retryable: error.code === 'ORCHESTRATION_LOCK_BUSY' }
|
|
441
|
-
: { code: 'PR_CHANGE_REPORT_FAILED', message: error instanceof Error ? error.message : String(error), retryable: false };
|
|
442
|
-
return { ...basePlatformResult(lockError.retryable ? 'blocked' : 'failed', context, resolved.taskId, knownPrNumber, lockError), report: null };
|
|
438
|
+
return { ...basePlatformResult('failed', context, resolved.taskId, knownPrNumber, { code: 'PR_CHANGE_REPORT_FAILED', message: error instanceof Error ? error.message : String(error), retryable: false }), report: null };
|
|
443
439
|
}
|
|
444
440
|
}
|
|
445
441
|
async function syncPullRequestSummary(taskRef, options) {
|
|
@@ -492,7 +488,7 @@ async function syncPullRequestSummary(taskRef, options) {
|
|
|
492
488
|
const loaded = await resolvePlatformProviderContext({ cwd: resolved.repoRoot, client: options.client });
|
|
493
489
|
const context = loaded.ok ? loaded.value.context : loaded.context;
|
|
494
490
|
if (!loaded.ok || !context.platform.repository || !['no-op', 'degraded'].includes(context.status)) {
|
|
495
|
-
//
|
|
491
|
+
// Preserve the known PR target for diagnostics when provider context resolution fails.
|
|
496
492
|
knownPrNumber = boundPrNumberForWarning(resolved.taskMdPath);
|
|
497
493
|
return preserveFailure(context);
|
|
498
494
|
}
|
|
@@ -737,15 +733,10 @@ async function syncPullRequestSummary(taskRef, options) {
|
|
|
737
733
|
persistenceFailure: (error) => fail('failed', context, error, prNumber)
|
|
738
734
|
});
|
|
739
735
|
};
|
|
740
|
-
return await (
|
|
741
|
-
? execute()
|
|
742
|
-
: withTaskExecutionLock(resolved.repoRoot, resolved.taskId, options.agent, execute));
|
|
736
|
+
return await execute();
|
|
743
737
|
}
|
|
744
738
|
catch (error) {
|
|
745
|
-
|
|
746
|
-
? { code: error.code, message: error.message, retryable: error.code === 'ORCHESTRATION_LOCK_BUSY' }
|
|
747
|
-
: { code: 'PR_SUMMARY_FAILED', message: error instanceof Error ? error.message : String(error), retryable: false };
|
|
748
|
-
return fail(lockError.retryable ? 'blocked' : 'failed', context, lockError);
|
|
739
|
+
return fail('failed', context, { code: 'PR_SUMMARY_FAILED', message: error instanceof Error ? error.message : String(error), retryable: false });
|
|
749
740
|
}
|
|
750
741
|
}
|
|
751
742
|
export { buildPullRequestSummary, reconcileSummaryComment, reportWrite, summaryCommentState, summaryContext, summaryMarker, syncPullRequestSummary, warningResultForPrimary };
|
|
@@ -14,7 +14,6 @@ import { planPullRequestMetadata } from './pull-request-metadata.js';
|
|
|
14
14
|
import { planInLabelUpdate, validateInLabelMapping } from './in-label-sync.js';
|
|
15
15
|
import { platformResult } from './types.js';
|
|
16
16
|
import { inspectCompletionArtifacts } from '../task/finalization-artifacts.js';
|
|
17
|
-
import { TaskExecutionLockError, withTaskExecutionLock } from '../task/task-execution-lock.js';
|
|
18
17
|
import { resolveDeliveryTarget } from '../task/delivery-target.js';
|
|
19
18
|
import { mergeOperationWarnings } from '../task/operation-outcome.js';
|
|
20
19
|
import { buildBoundFact, buildSkippedFact, factFrontmatterMutation, readPrDeliveryFact } from '../task/pr-delivery-fact.js';
|
|
@@ -1007,16 +1006,9 @@ async function createPlatformPullRequestImpl(taskRef, options) {
|
|
|
1007
1006
|
}), PRECONDITION_NOT_CREATED);
|
|
1008
1007
|
}
|
|
1009
1008
|
try {
|
|
1010
|
-
return
|
|
1009
|
+
return await createExternalPullRequest(taskRef, options, base);
|
|
1011
1010
|
}
|
|
1012
1011
|
catch (error) {
|
|
1013
|
-
if (error instanceof TaskExecutionLockError) {
|
|
1014
|
-
return withCreation(result('blocked', base.resolved.taskId, base.issueNumber, null, {
|
|
1015
|
-
platform: base.context.platform,
|
|
1016
|
-
capabilities: base.context.capabilities,
|
|
1017
|
-
error: { code: error.code, message: error.message, retryable: true }
|
|
1018
|
-
}), PRECONDITION_NOT_CREATED);
|
|
1019
|
-
}
|
|
1020
1012
|
return withCreation(result('failed', base.resolved.taskId, base.issueNumber, null, {
|
|
1021
1013
|
platform: base.context.platform,
|
|
1022
1014
|
capabilities: base.context.capabilities,
|
|
@@ -1456,7 +1448,7 @@ function readProjectPrFlow(repoRoot) {
|
|
|
1456
1448
|
return undefined;
|
|
1457
1449
|
}
|
|
1458
1450
|
}
|
|
1459
|
-
function
|
|
1451
|
+
function applySkippedPullRequestFact(resolved, options) {
|
|
1460
1452
|
let content;
|
|
1461
1453
|
let frontmatter;
|
|
1462
1454
|
try {
|
|
@@ -1533,13 +1525,9 @@ async function skipPlatformPullRequestFact(taskRef, options) {
|
|
|
1533
1525
|
error: { code: 'TASK_STATE_INVALID', message: 'platform-pr skip requires an active task', retryable: false }
|
|
1534
1526
|
});
|
|
1535
1527
|
try {
|
|
1536
|
-
return
|
|
1528
|
+
return await applySkippedPullRequestFact(resolved, options);
|
|
1537
1529
|
}
|
|
1538
1530
|
catch (error) {
|
|
1539
|
-
if (error instanceof TaskExecutionLockError)
|
|
1540
|
-
return result('blocked', resolved.taskId, null, null, {
|
|
1541
|
-
error: { code: error.code, message: error.message, retryable: true }
|
|
1542
|
-
});
|
|
1543
1531
|
return result('failed', resolved.taskId, null, null, {
|
|
1544
1532
|
error: { code: 'PR_SKIP_FAILED', message: error instanceof Error ? error.message : String(error), retryable: false }
|
|
1545
1533
|
});
|
|
@@ -3,6 +3,7 @@ import path from 'node:path';
|
|
|
3
3
|
import { parseTypedTaskFrontmatter } from '../task/frontmatter.js';
|
|
4
4
|
import { readPrDeliveryFact } from '../task/pr-delivery-fact.js';
|
|
5
5
|
import { resourceIdentityEquals, resourceIdentityNumber } from '../platform/resource-identity.js';
|
|
6
|
+
import { resolveProjectTaskWorkspaceRoot } from '../task/workspace-root.js';
|
|
6
7
|
import { taskIssueIdentity } from '../platform/task-identities.js';
|
|
7
8
|
export class HostResolutionError extends Error {
|
|
8
9
|
code;
|
|
@@ -35,12 +36,12 @@ export function extractClosingIssueNumbers(body) {
|
|
|
35
36
|
return result;
|
|
36
37
|
}
|
|
37
38
|
/**
|
|
38
|
-
* Scan
|
|
39
|
+
* Scan active project task workspace task.md files for tasks bound to the
|
|
39
40
|
* PR number or to one of the PR's closing issues. A task is emitted at most once;
|
|
40
41
|
* a verified fact identity hit takes priority over the issue-identity reverse lookup.
|
|
41
42
|
*/
|
|
42
43
|
export function collectHostCandidates(input) {
|
|
43
|
-
const activeRoot = path.join(input.workspaceRoot, '
|
|
44
|
+
const activeRoot = path.join(resolveProjectTaskWorkspaceRoot(input.workspaceRoot), 'active');
|
|
44
45
|
const candidates = [];
|
|
45
46
|
let entries;
|
|
46
47
|
try {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
2
3
|
import path from 'node:path';
|
|
3
4
|
import { detectRepoRoot } from '../task/resolve-ref.js';
|
|
5
|
+
import { resolveProjectTaskWorkspaceRoot } from '../task/workspace-root.js';
|
|
4
6
|
import { acquireGitHubCheckpoint, commitGitHubCheckpoint, readGitHubCheckpoint, releaseGitHubCheckpoint } from './checkpoint.js';
|
|
5
7
|
import { materializeJson, materializeSnapshot } from './materialize.js';
|
|
6
8
|
import { normalizeResources } from './normalize.js';
|
|
@@ -48,17 +50,27 @@ function parseArgs(args, valueOptions, booleanOptions) {
|
|
|
48
50
|
return { positionals, options };
|
|
49
51
|
}
|
|
50
52
|
function dataRoot(repoRoot, requested) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
let boundary = repoRoot;
|
|
54
|
+
let input = requested;
|
|
55
|
+
if (input === undefined) {
|
|
56
|
+
const config = JSON.parse(fs.readFileSync(path.join(repoRoot, '.agents', '.airc.json'), 'utf8'));
|
|
57
|
+
if (typeof config.project !== 'string' || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(config.project)) {
|
|
58
|
+
throw new Error('.agents/.airc.json must contain a valid project name in its project field');
|
|
59
|
+
}
|
|
60
|
+
boundary = path.resolve(os.homedir());
|
|
61
|
+
input = path.join(boundary, '.agent-infra', 'data-store', config.project);
|
|
62
|
+
}
|
|
63
|
+
const resolved = path.resolve(repoRoot, input);
|
|
64
|
+
const workspace = resolveProjectTaskWorkspaceRoot(repoRoot);
|
|
56
65
|
const taskRoots = ['active', 'blocked', 'completed', 'archive'].map((state) => path.join(workspace, state));
|
|
57
66
|
if (resolved === workspace || taskRoots.some((taskRoot) => resolved === taskRoot || resolved.startsWith(`${taskRoot}${path.sep}`))) {
|
|
58
67
|
throw new Error('data root cannot be the workspace root or a task directory');
|
|
59
68
|
}
|
|
69
|
+
const relative = path.relative(repoRoot, resolved);
|
|
70
|
+
if (requested !== undefined && (!relative || relative.startsWith('..') || path.isAbsolute(relative)))
|
|
71
|
+
throw new Error('data root must be inside the repository');
|
|
60
72
|
let current = resolved;
|
|
61
|
-
while (current !==
|
|
73
|
+
while (current !== boundary) {
|
|
62
74
|
if (fs.existsSync(current) && fs.lstatSync(current).isSymbolicLink())
|
|
63
75
|
throw new Error('data root cannot traverse a symlink');
|
|
64
76
|
current = path.dirname(current);
|
|
@@ -2,6 +2,7 @@ import fs from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { createGitHubClient } from '../platform/github-client.js';
|
|
4
4
|
import { enumerateAllTaskDirs } from '../task/resolve-ref.js';
|
|
5
|
+
import { resolveProjectTaskWorkspaceRoot } from '../task/workspace-root.js';
|
|
5
6
|
import { captureText, inspectPrivacy, redactExcerpt } from './privacy.js';
|
|
6
7
|
import { canonicalJsonBytes, sha256 } from './store.js';
|
|
7
8
|
function endpointForPage(endpoint, page, perPage) {
|
|
@@ -103,6 +104,11 @@ function fetchRestCollection(options) {
|
|
|
103
104
|
};
|
|
104
105
|
}
|
|
105
106
|
function relativeIdentity(repoRoot, filePath) {
|
|
107
|
+
const workspaceRoot = resolveProjectTaskWorkspaceRoot(repoRoot);
|
|
108
|
+
const taskRelative = path.relative(workspaceRoot, filePath);
|
|
109
|
+
if (taskRelative && !taskRelative.startsWith('..') && !path.isAbsolute(taskRelative)) {
|
|
110
|
+
return path.posix.join('task-workspace', taskRelative.split(path.sep).join('/'));
|
|
111
|
+
}
|
|
106
112
|
const relative = path.relative(repoRoot, filePath);
|
|
107
113
|
if (!relative || relative.startsWith('..') || path.isAbsolute(relative))
|
|
108
114
|
throw new Error(`path escapes repository: ${filePath}`);
|
package/dist/lib/run/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import { loadServerConfig } from '../server/config.js';
|
|
|
4
4
|
import { resolveSandboxTarget } from '../sandbox/workspace-identity.js';
|
|
5
5
|
import { createRunId, runInSandbox } from '../sandbox/capture.js';
|
|
6
6
|
import { loadShortIdByTaskId, normalizeShortIdInput } from '../task/short-id.js';
|
|
7
|
+
import { resolveProjectTaskWorkspaceRoot } from '../task/workspace-root.js';
|
|
7
8
|
import { buildTuiCommand, renderPrompt, selectTui } from './tui.js';
|
|
8
9
|
import { getSkillRunSpec } from './skills.js';
|
|
9
10
|
import { runHostCommand } from './host.js';
|
|
@@ -133,7 +134,7 @@ function readShortIdLength(repoRoot) {
|
|
|
133
134
|
return 2;
|
|
134
135
|
}
|
|
135
136
|
function readActiveShortIdRegistry(repoRoot) {
|
|
136
|
-
const registryPath = path.join(repoRoot, '
|
|
137
|
+
const registryPath = path.join(resolveProjectTaskWorkspaceRoot(repoRoot), 'active', '.short-ids.json');
|
|
137
138
|
try {
|
|
138
139
|
const data = JSON.parse(fs.readFileSync(registryPath, 'utf8'));
|
|
139
140
|
return data && typeof data === 'object' && data.ids && typeof data.ids === 'object' ? data.ids : {};
|
|
@@ -157,7 +158,7 @@ function resolveActiveTaskIdentity(taskRef, repoRoot) {
|
|
|
157
158
|
}
|
|
158
159
|
if (!taskId)
|
|
159
160
|
return null;
|
|
160
|
-
const taskDir = path.join(repoRoot, '
|
|
161
|
+
const taskDir = path.join(resolveProjectTaskWorkspaceRoot(repoRoot), 'active', taskId);
|
|
161
162
|
if (!fs.existsSync(path.join(taskDir, 'task.md')))
|
|
162
163
|
return null;
|
|
163
164
|
return { taskId, taskDir, taskRef: resolvedTaskRef };
|
|
@@ -1040,7 +1040,7 @@ export async function create(args, { runProjectInitCommand = true, workspace: wo
|
|
|
1040
1040
|
const completedTaskSource = target.workspace.mode === 'task-bound'
|
|
1041
1041
|
? prepareSandboxCompletedTaskMountSource(effectiveConfig.repoRoot, target.workspace.taskId)
|
|
1042
1042
|
: null;
|
|
1043
|
-
prepareSandboxWorkspaceMountTargets(
|
|
1043
|
+
prepareSandboxWorkspaceMountTargets(effectiveConfig.repoRoot);
|
|
1044
1044
|
if (previousCutoverSnapshot) {
|
|
1045
1045
|
assertSandboxControlCutoverSnapshot(previousCutoverSnapshot);
|
|
1046
1046
|
if (previousManifest) {
|
|
@@ -1177,6 +1177,10 @@ export async function create(args, { runProjectInitCommand = true, workspace: wo
|
|
|
1177
1177
|
: []),
|
|
1178
1178
|
'--label',
|
|
1179
1179
|
`${sandboxRuntimeCapabilityLabel(effectiveConfig)}=${capabilityPlan.runtimeSignature}`,
|
|
1180
|
+
// Every task sandbox runs without seccomp and AppArmor filtering;
|
|
1181
|
+
// capabilities and namespace isolation remain unchanged.
|
|
1182
|
+
'--security-opt', 'seccomp=unconfined',
|
|
1183
|
+
'--security-opt', 'apparmor=unconfined',
|
|
1180
1184
|
...coreVolumes,
|
|
1181
1185
|
...buildClipboardVolumeArgs(engine, effectiveConfig.home),
|
|
1182
1186
|
...dotfilesMount,
|
|
@@ -13,7 +13,7 @@ import { resolveSandboxTarget } from '../workspace-identity.js';
|
|
|
13
13
|
const USAGE = `Usage: ai sandbox exec [--recreate] <branch | TASK-id | N> [cmd...]
|
|
14
14
|
|
|
15
15
|
N references an active task short id from
|
|
16
|
-
|
|
16
|
+
the read-only task-workspace mount at /task-workspace/active/.short-ids.json. They resolve only via that
|
|
17
17
|
registry — they do not reference a container's row position in
|
|
18
18
|
'ai sandbox ls' output. --recreate rebuilds the matched container while
|
|
19
19
|
preserving its task or branch identity. The same token after the target is
|
|
@@ -5,6 +5,7 @@ import { runSafeEngine, runVerboseEngine } from '../shell.js';
|
|
|
5
5
|
import { resolveTaskBranch } from '../task-resolver.js';
|
|
6
6
|
import { isRemovedHashShortIdInput } from '../../task/short-id.js';
|
|
7
7
|
import { parseSandboxWorkspaceIdentity } from '../workspace-identity.js';
|
|
8
|
+
import { resolveProjectTaskWorkspaceRoot } from '../../task/workspace-root.js';
|
|
8
9
|
export function containerListFormat() {
|
|
9
10
|
return '{{.Names}}\t{{.Status}}\t{{.Labels}}';
|
|
10
11
|
}
|
|
@@ -111,7 +112,7 @@ function tryResolveFromRegistry(arg, repoRoot) {
|
|
|
111
112
|
throw new Error(`Registry returned malformed task id for '${arg}': ${JSON.stringify(taskId)}`);
|
|
112
113
|
}
|
|
113
114
|
for (const sub of ['active', 'completed', 'blocked', 'archive']) {
|
|
114
|
-
const taskMdPath = path.join(repoRoot,
|
|
115
|
+
const taskMdPath = path.join(resolveProjectTaskWorkspaceRoot(repoRoot), sub, taskId, 'task.md');
|
|
115
116
|
if (!fs.existsSync(taskMdPath))
|
|
116
117
|
continue;
|
|
117
118
|
const content = fs.readFileSync(taskMdPath, 'utf8');
|
|
@@ -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.js';
|
|
5
|
+
import { detectRepoRoot } from '../../task/resolve-ref.js';
|
|
4
6
|
import { SANDBOX_CONTROL_MAX_BYTES, SANDBOX_CONTROL_STATUS_STALE_MS, isSandboxControlFamily } from './protocol.js';
|
|
5
7
|
import { readSandboxControlPayload, readSandboxControlStatus } from './state.js';
|
|
6
8
|
import { accessSandboxTaskView, taskViewFromStatus } from './task-view.js';
|
|
@@ -127,19 +129,12 @@ const CONTROL_TASK_ID_RE = /^TASK-\d{8}-\d{6}$/;
|
|
|
127
129
|
const CONTROL_SHORT_ID_RE = /^\d+$/;
|
|
128
130
|
// Use the current short-id contract for the first visible active registry.
|
|
129
131
|
export function resolveVisibleActiveShortId(ref) {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}
|
|
137
|
-
const parent = path.dirname(dir);
|
|
138
|
-
if (parent === dir)
|
|
139
|
-
break;
|
|
140
|
-
dir = parent;
|
|
141
|
-
}
|
|
142
|
-
return null;
|
|
132
|
+
const repoRoot = detectRepoRoot();
|
|
133
|
+
const registryPath = path.join(resolveProjectTaskWorkspaceRoot(repoRoot), 'active', '.short-ids.json');
|
|
134
|
+
if (!fs.existsSync(registryPath))
|
|
135
|
+
return null;
|
|
136
|
+
const resolved = resolveShortIdReadOnly(ref, repoRoot, { shortIdLength: configuredShortIdLength(repoRoot) });
|
|
137
|
+
return resolved.ok ? resolved.taskId : null;
|
|
143
138
|
}
|
|
144
139
|
// A request's leading argument is only a task reference for some families and
|
|
145
140
|
// may be a documented short id. Compare canonical ids so a bound sandbox keeps
|
|
@@ -4,6 +4,7 @@ import { resolveTaskWorkspace } from '../task-resolver.js';
|
|
|
4
4
|
import { assertGitWorktreeBinding } from '../../git/worktree-identity.js';
|
|
5
5
|
import { parseTypedTaskFrontmatter } from '../../task/frontmatter.js';
|
|
6
6
|
import { readTaskFinalizationReceipt } from '../../task/finalization.js';
|
|
7
|
+
import { resolveProjectTaskWorkspaceRoot } from '../../task/workspace-root.js';
|
|
7
8
|
import { inspectSandboxControlContainer } from './container-identity.js';
|
|
8
9
|
import { atomicWriteJson, readSandboxControlStatus } from './state.js';
|
|
9
10
|
import { taskViewForManifest } from './task-view.js';
|
|
@@ -40,7 +41,7 @@ export async function prepareCompletedReentry(manifest, inspect = inspectSandbox
|
|
|
40
41
|
|| previous.receipt.requestId !== view.receipt.requestId
|
|
41
42
|
|| previous.receipt.revision > view.receipt.revision)
|
|
42
43
|
throw invalid();
|
|
43
|
-
const completedRoot = fs.realpathSync.native(path.join(manifest.repoRoot, '
|
|
44
|
+
const completedRoot = fs.realpathSync.native(path.join(resolveProjectTaskWorkspaceRoot(manifest.repoRoot), 'completed'));
|
|
44
45
|
const source = fs.realpathSync.native(path.join(completedRoot, manifest.taskId));
|
|
45
46
|
if (source !== path.join(completedRoot, manifest.taskId))
|
|
46
47
|
throw invalid();
|
|
@@ -518,27 +518,8 @@ function readTaskCompletionManifest(root, binding) {
|
|
|
518
518
|
}
|
|
519
519
|
return manifest;
|
|
520
520
|
}
|
|
521
|
-
export
|
|
522
|
-
|
|
523
|
-
const manifest = readTaskCompletionManifest(resolvedRoot, binding);
|
|
524
|
-
markSandboxControlRootQuiescing(resolvedRoot);
|
|
525
|
-
const deadline = Date.now() + timeoutMs;
|
|
526
|
-
const startupPath = path.join(resolvedRoot, BROKER_STARTING_FILE);
|
|
527
|
-
const brokerPath = path.join(resolvedRoot, 'broker.json');
|
|
528
|
-
while (true) {
|
|
529
|
-
const startup = readStartupOwner(startupPath);
|
|
530
|
-
if (fs.existsSync(startupPath) && !startup)
|
|
531
|
-
throw new Error('SANDBOX_CONTROL_BROKER_START_TRANSITION');
|
|
532
|
-
const broker = readBrokerOwner(brokerPath);
|
|
533
|
-
if (broker && (broker.token !== manifest.token || broker.generation !== manifest.generation)) {
|
|
534
|
-
throw new Error('SANDBOX_CONTROL_OWNER_MISMATCH');
|
|
535
|
-
}
|
|
536
|
-
if (!startup && !broker)
|
|
537
|
-
return;
|
|
538
|
-
if (Date.now() >= deadline)
|
|
539
|
-
throw new Error('SANDBOX_CONTROL_BROKER_DRAIN_TIMEOUT');
|
|
540
|
-
await new Promise((resolve) => setTimeout(resolve, 25));
|
|
541
|
-
}
|
|
521
|
+
export function validateSandboxControlTaskIdentity(root, binding) {
|
|
522
|
+
readTaskCompletionManifest(path.resolve(root), binding);
|
|
542
523
|
}
|
|
543
524
|
function recordSandboxRemovalPending(root, manifest, phase, journal) {
|
|
544
525
|
const pendingPath = path.join(root, 'removal-pending.json');
|
|
@@ -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.js';
|
|
3
4
|
export const SANDBOX_TASK_VIEW_STATES = ['not-applicable', 'current', 'finalized-stale', 'unknown'];
|
|
4
5
|
export const SANDBOX_TASK_VIEW_SOURCES = ['active', 'completed', 'unknown'];
|
|
5
6
|
function invalidView(message) {
|
|
@@ -71,8 +72,9 @@ export function taskViewForManifest(params) {
|
|
|
71
72
|
if (params.mode !== 'task-bound' || !params.taskId) {
|
|
72
73
|
return { state: 'not-applicable', taskId: null, observedSource: null, receipt: null, reasonCode: null };
|
|
73
74
|
}
|
|
74
|
-
const
|
|
75
|
-
const
|
|
75
|
+
const taskRoot = resolveProjectTaskWorkspaceRoot(params.repoRoot);
|
|
76
|
+
const active = path.join(taskRoot, 'active', params.taskId, 'task.md');
|
|
77
|
+
const completed = path.join(taskRoot, 'completed', params.taskId, 'task.md');
|
|
76
78
|
const activeExists = fs.existsSync(active);
|
|
77
79
|
const completedExists = fs.existsSync(completed);
|
|
78
80
|
if (activeExists === completedExists) {
|
|
@@ -11,12 +11,12 @@ function workspaceStateMounts(workspaceViewRoot, taskBound) {
|
|
|
11
11
|
.map(({ state, hostPath }) => taskBound && state === 'active'
|
|
12
12
|
? {
|
|
13
13
|
hostPaths: [path.join(hostPath, '.short-ids.json')],
|
|
14
|
-
containerPath: '/workspace
|
|
14
|
+
containerPath: '/task-workspace/active/.short-ids.json',
|
|
15
15
|
readOnly: true
|
|
16
16
|
}
|
|
17
17
|
: {
|
|
18
18
|
hostPaths: [hostPath],
|
|
19
|
-
containerPath: path.posix.join('/workspace
|
|
19
|
+
containerPath: path.posix.join('/task-workspace', state),
|
|
20
20
|
readOnly: true
|
|
21
21
|
});
|
|
22
22
|
}
|
|
@@ -37,13 +37,13 @@ function taskWorkspaceMounts(taskSources, taskBound, overrides) {
|
|
|
37
37
|
const taskId = overrides.taskId;
|
|
38
38
|
const mounts = [{
|
|
39
39
|
hostPaths: taskSources,
|
|
40
|
-
containerPath: path.posix.join('/workspace
|
|
40
|
+
containerPath: path.posix.join('/task-workspace/active', taskId),
|
|
41
41
|
readOnly: false
|
|
42
42
|
}];
|
|
43
43
|
if (overrides.completedTaskSource)
|
|
44
44
|
mounts.push({
|
|
45
45
|
hostPaths: [overrides.completedTaskSource],
|
|
46
|
-
containerPath: path.posix.join('/workspace
|
|
46
|
+
containerPath: path.posix.join('/task-workspace/completed', taskId),
|
|
47
47
|
readOnly: false
|
|
48
48
|
});
|
|
49
49
|
return mounts;
|
|
@@ -11,6 +11,7 @@ import { sandboxCoreBindMounts } from './mounts.js';
|
|
|
11
11
|
import { resolveSandboxGitMetadata } from './git-metadata.js';
|
|
12
12
|
import { assertSandboxTaskSource, sandboxCompletedTaskMountSource, sandboxControlPaths, sandboxWorkspaceViewPaths } from './workspace-view.js';
|
|
13
13
|
import { resolveTaskWorkspace } from './task-resolver.js';
|
|
14
|
+
import { resolveProjectTaskWorkspaceRoot } from '../task/workspace-root.js';
|
|
14
15
|
import { parseSandboxWorkspaceIdentity, sameSandboxWorkspaceIdentity } from './workspace-identity.js';
|
|
15
16
|
import { inspectWorktree } from './worktree-safety.js';
|
|
16
17
|
import { commandForEngine, runEngine, runOkEngine, runProbe, runVerboseEngine } from './shell.js';
|
|
@@ -277,11 +278,13 @@ export function classifySandboxRecovery(snapshot) {
|
|
|
277
278
|
});
|
|
278
279
|
}
|
|
279
280
|
if (snapshot.workspaceTopology === 'legacy-parent') {
|
|
281
|
+
const legacyRoot = snapshot.mounts.some((mount) => mount.path === '/task-workspace')
|
|
282
|
+
? '/task-workspace' : '/workspace/.agents/workspace';
|
|
280
283
|
findings.push({
|
|
281
284
|
repairKind: 'hard-failure',
|
|
282
285
|
code: 'SANDBOX_WORKSPACE_TOPOLOGY_MISMATCH',
|
|
283
286
|
message: 'Sandbox uses the legacy workspace parent-mount topology; recreate the container explicitly to migrate it.',
|
|
284
|
-
path:
|
|
287
|
+
path: legacyRoot
|
|
285
288
|
});
|
|
286
289
|
}
|
|
287
290
|
if (snapshot.taskView && !snapshot.taskView.readable) {
|
|
@@ -302,7 +305,8 @@ export function classifySandboxRecovery(snapshot) {
|
|
|
302
305
|
}
|
|
303
306
|
for (const mount of snapshot.mounts) {
|
|
304
307
|
if (snapshot.workspaceTopology === 'legacy-parent'
|
|
305
|
-
&& mount.path.startsWith('/workspace/.agents/workspace')
|
|
308
|
+
&& (mount.path.startsWith('/workspace/.agents/workspace')
|
|
309
|
+
|| mount.path.startsWith('/task-workspace')))
|
|
306
310
|
continue;
|
|
307
311
|
if (mount.actualType !== mount.expectedType
|
|
308
312
|
|| !mount.sourceMatches
|
|
@@ -386,14 +390,15 @@ function inspectContainer(engine, container, runFn) {
|
|
|
386
390
|
return parsed[0];
|
|
387
391
|
}
|
|
388
392
|
function workspaceTopology(mountsByDestination) {
|
|
389
|
-
if (mountsByDestination.has('/workspace/.agents/workspace')
|
|
393
|
+
if (mountsByDestination.has('/workspace/.agents/workspace')
|
|
394
|
+
|| mountsByDestination.has('/task-workspace'))
|
|
390
395
|
return 'legacy-parent';
|
|
391
396
|
const perState = [
|
|
392
|
-
'/workspace
|
|
393
|
-
'/workspace
|
|
394
|
-
'/workspace
|
|
395
|
-
'/workspace
|
|
396
|
-
'/workspace
|
|
397
|
+
'/task-workspace/active',
|
|
398
|
+
'/task-workspace/active/.short-ids.json',
|
|
399
|
+
'/task-workspace/completed',
|
|
400
|
+
'/task-workspace/blocked',
|
|
401
|
+
'/task-workspace/archive'
|
|
397
402
|
];
|
|
398
403
|
return perState.some((destination) => mountsByDestination.has(destination))
|
|
399
404
|
? 'per-state'
|
|
@@ -421,8 +426,9 @@ function sourceCandidates(engine, hostPath) {
|
|
|
421
426
|
return [...new Set(candidates.map((candidate) => normalizeMountSource(engine, candidate)))];
|
|
422
427
|
}
|
|
423
428
|
function recoveryTaskSources(repoRoot, taskId) {
|
|
424
|
-
const
|
|
425
|
-
const
|
|
429
|
+
const taskRoot = resolveProjectTaskWorkspaceRoot(repoRoot);
|
|
430
|
+
const activePath = path.join(taskRoot, 'active', taskId);
|
|
431
|
+
const completedPath = path.join(taskRoot, 'completed', taskId);
|
|
426
432
|
if (fs.existsSync(activePath) && !fs.existsSync(completedPath)) {
|
|
427
433
|
const source = assertSandboxTaskSource(repoRoot, taskId);
|
|
428
434
|
return { mountPaths: [source], accessiblePaths: [source] };
|
|
@@ -435,12 +441,12 @@ function recoveryTaskSources(repoRoot, taskId) {
|
|
|
435
441
|
if (task.state !== 'completed') {
|
|
436
442
|
throw new Error(`SANDBOX_TASK_SOURCE_STATE_UNSUPPORTED: task ${taskId} is ${task.state}`);
|
|
437
443
|
}
|
|
438
|
-
const completedRoot = fs.realpathSync.native(path.join(
|
|
444
|
+
const completedRoot = fs.realpathSync.native(path.join(taskRoot, 'completed'));
|
|
439
445
|
const completedSource = fs.realpathSync.native(path.dirname(task.taskMd));
|
|
440
446
|
if (path.dirname(completedSource) !== completedRoot) {
|
|
441
447
|
throw new Error(`SANDBOX_TASK_SOURCE_INVALID: ${completedSource} is not under completed workspace`);
|
|
442
448
|
}
|
|
443
|
-
const historicalActivePath = path.join(
|
|
449
|
+
const historicalActivePath = path.join(taskRoot, 'active', taskId);
|
|
444
450
|
const mountPaths = [completedSource, historicalActivePath];
|
|
445
451
|
if (fs.existsSync(historicalActivePath)) {
|
|
446
452
|
try {
|
|
@@ -456,7 +462,7 @@ function recoveryTaskSources(repoRoot, taskId) {
|
|
|
456
462
|
export function startCompletedSandboxContainer(params) {
|
|
457
463
|
const taskSources = recoveryTaskSources(params.config.repoRoot, params.taskId);
|
|
458
464
|
const completedSource = taskSources.accessiblePaths[0];
|
|
459
|
-
const activeRoot = path.join(params.config.repoRoot, '
|
|
465
|
+
const activeRoot = path.join(resolveProjectTaskWorkspaceRoot(params.config.repoRoot), 'active');
|
|
460
466
|
const historicalSource = path.join(activeRoot, params.taskId);
|
|
461
467
|
fs.mkdirSync(activeRoot, { recursive: true });
|
|
462
468
|
let replacedEmptyRoot = false;
|
|
@@ -559,11 +565,11 @@ function expectedMounts(params) {
|
|
|
559
565
|
expectedType: 'bind',
|
|
560
566
|
hostPaths: mount.hostPaths,
|
|
561
567
|
...(taskSources !== null && taskId !== null
|
|
562
|
-
&& mount.containerPath === `/workspace
|
|
568
|
+
&& mount.containerPath === `/task-workspace/active/${taskId}`
|
|
563
569
|
? { sourceAccessiblePaths: taskSources.accessiblePaths }
|
|
564
570
|
: {}),
|
|
565
571
|
...(completedTaskSource !== null && taskId !== null
|
|
566
|
-
&& mount.containerPath === `/workspace
|
|
572
|
+
&& mount.containerPath === `/task-workspace/completed/${taskId}`
|
|
567
573
|
? { sourceAccessiblePaths: [completedTaskSource] }
|
|
568
574
|
: {}),
|
|
569
575
|
expectedRW: !mount.readOnly
|
|
@@ -664,7 +670,7 @@ export function collectSandboxRecoverySnapshot(params) {
|
|
|
664
670
|
const tools = [...capabilityPlan.tools];
|
|
665
671
|
const topology = workspaceTopology(mountsByDestination);
|
|
666
672
|
const taskViewPath = workspace.mode === 'task-bound'
|
|
667
|
-
? `/workspace
|
|
673
|
+
? `/task-workspace/active/${workspace.taskId}/task.md`
|
|
668
674
|
: null;
|
|
669
675
|
const seeds = declaredTmpfsSeedEntries(tools).map((seed) => {
|
|
670
676
|
const mounted = mountsByDestination.get(seed.stagingPath)?.Type === 'bind';
|
|
@@ -856,6 +862,10 @@ function canRetryFreshReadiness(findings) {
|
|
|
856
862
|
&& finding.code !== 'SANDBOX_WORKSPACE_TOPOLOGY_MISMATCH'
|
|
857
863
|
&& finding.path !== undefined
|
|
858
864
|
&& (finding.path.startsWith('/workspace/.agents/workspace')
|
|
865
|
+
|| finding.path.startsWith('/task-workspace/active')
|
|
866
|
+
|| finding.path.startsWith('/task-workspace/blocked')
|
|
867
|
+
|| finding.path.startsWith('/task-workspace/completed')
|
|
868
|
+
|| finding.path.startsWith('/task-workspace/archive')
|
|
859
869
|
|| finding.path === '/home/devuser/.host-shell-config'
|
|
860
870
|
|| finding.path === '/home/devuser/.bash_aliases'
|
|
861
871
|
|| finding.path === '/run/agent-infra/runtime'));
|