@fitlab-ai/agent-infra 0.9.15 → 0.10.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/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +6 -2
- package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +43 -3
- package/dist/lib/fs/durable-write.js +2 -0
- package/dist/lib/internal/cli-route-inventory.js +1 -1
- package/dist/lib/internal/manual-validation.js +17 -8
- package/dist/lib/internal/task-artifact.js +1 -1
- package/dist/lib/internal/task-lifecycle.js +2 -2
- package/dist/lib/internal/task-operation-registry.js +0 -1
- package/dist/lib/internal/task-review.js +4 -4
- package/dist/lib/platform/issue-comments.js +16 -10
- package/dist/lib/platform/issues.js +1 -1
- package/dist/lib/platform/pr-change-report.js +5 -4
- package/dist/lib/platform/pr-summary.js +64 -32
- package/dist/lib/platform/pull-requests.js +3 -3
- package/dist/lib/platform/task-identities.js +2 -9
- package/dist/lib/platform/verification-sync.js +6 -2
- package/dist/lib/pr-review/evidence-grading.js +4 -8
- package/dist/lib/sandbox/commands/rm.js +21 -2
- package/dist/lib/sandbox/control/server.js +29 -9
- package/dist/lib/sandbox/control/state.js +7 -1
- package/dist/lib/sandbox/control/workflow-executor.js +4 -14
- package/dist/lib/sandbox/index.js +1 -2
- package/dist/lib/sandbox/removal.js +116 -1011
- package/dist/lib/sandbox/workspace-identity.js +17 -1
- package/dist/lib/task/artifact-command.js +6 -28
- package/dist/lib/task/artifact-operations.js +16 -230
- package/dist/lib/task/artifact-recovery.js +430 -0
- package/dist/lib/task/artifact-repair-intent.js +73 -39
- package/dist/lib/task/commit-operation.js +31 -4
- package/dist/lib/task/control-authority.js +38 -7
- package/dist/lib/task/control-recovery.js +14 -2
- package/dist/lib/task/create-service.js +1 -8
- package/dist/lib/task/delegation-receipts.js +64 -3
- package/dist/lib/task/events.js +164 -22
- package/dist/lib/task/human-override.js +5 -8
- package/dist/lib/task/intermediate-cleanup.js +66 -8
- package/dist/lib/task/lifecycle-recovery.js +549 -0
- package/dist/lib/task/lifecycle.js +12 -3
- package/dist/lib/task/local-artifact-finalization.js +148 -123
- package/dist/lib/task/manual-validation-receipt.js +5 -1
- package/dist/lib/task/orchestration.js +52 -2
- package/dist/lib/task/pr-delivery-fact.js +12 -56
- package/dist/lib/task/recovery-warning.js +19 -0
- package/dist/lib/task/review-command.js +1 -0
- package/dist/lib/task/review-finalization.js +104 -77
- package/dist/lib/task/short-id.js +15 -7
- package/dist/lib/task/task-execution-lock.js +1 -1
- package/dist/lib/task/verification-engine.js +3 -14
- package/dist/lib/task/workflow-command.js +0 -1
- package/dist/lib/version.js +1 -11
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +7 -2
- package/lib/agent-clients/adapters/codex-lifecycle/store.ts +55 -3
- package/lib/fs/durable-write.ts +1 -0
- package/lib/internal/cli-route-inventory.ts +1 -1
- package/lib/internal/manual-validation.ts +17 -8
- package/lib/internal/task-artifact.ts +1 -1
- package/lib/internal/task-lifecycle.ts +4 -3
- package/lib/internal/task-operation-registry.ts +0 -1
- package/lib/internal/task-review.ts +4 -4
- package/lib/platform/issue-comments.ts +18 -10
- package/lib/platform/issues.ts +1 -1
- package/lib/platform/pr-change-report.ts +7 -5
- package/lib/platform/pr-summary.ts +64 -32
- package/lib/platform/pull-requests.ts +3 -3
- package/lib/platform/task-identities.ts +3 -14
- package/lib/platform/verification-sync.ts +6 -2
- package/lib/pr-review/evidence-grading.ts +4 -9
- package/lib/sandbox/commands/rm.ts +34 -2
- package/lib/sandbox/control/protocol.ts +5 -0
- package/lib/sandbox/control/server.ts +30 -10
- package/lib/sandbox/control/state.ts +9 -1
- package/lib/sandbox/control/workflow-executor.ts +4 -14
- package/lib/sandbox/index.ts +1 -2
- package/lib/sandbox/removal.ts +126 -1128
- package/lib/sandbox/workspace-identity.ts +19 -3
- package/lib/task/artifact-command.ts +8 -27
- package/lib/task/artifact-operations.ts +21 -251
- package/lib/task/artifact-recovery.ts +519 -0
- package/lib/task/artifact-repair-intent.ts +99 -51
- package/lib/task/commit-operation.ts +35 -4
- package/lib/task/control-authority.ts +47 -14
- package/lib/task/control-recovery.ts +13 -1
- package/lib/task/create-service.ts +1 -10
- package/lib/task/delegation-receipts.ts +73 -2
- package/lib/task/events.ts +187 -23
- package/lib/task/human-override.ts +5 -8
- package/lib/task/intermediate-cleanup.ts +50 -12
- package/lib/task/lifecycle-recovery.ts +691 -0
- package/lib/task/lifecycle.ts +13 -4
- package/lib/task/local-artifact-finalization.ts +176 -132
- package/lib/task/manual-validation-receipt.ts +5 -1
- package/lib/task/orchestration.ts +65 -0
- package/lib/task/pr-delivery-fact.ts +15 -62
- package/lib/task/recovery-warning.ts +21 -0
- package/lib/task/review-command.ts +1 -0
- package/lib/task/review-finalization.ts +106 -87
- package/lib/task/short-id.ts +27 -5
- package/lib/task/task-execution-lock.ts +1 -1
- package/lib/task/verification-engine.ts +3 -17
- package/lib/task/verification-types.ts +0 -2
- package/lib/task/workflow-command.ts +0 -1
- package/lib/version.ts +1 -15
- package/package.json +1 -1
- package/templates/.agents/rules/create-issue.en.md +1 -1
- package/templates/.agents/rules/create-issue.zh-CN.md +1 -1
- package/templates/.agents/rules/issue-sync.en.md +4 -2
- package/templates/.agents/rules/issue-sync.zh-CN.md +4 -3
- package/templates/.agents/rules/local-artifact-repair.en.md +46 -39
- package/templates/.agents/rules/local-artifact-repair.zh-CN.md +43 -36
- 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 +1 -1
- package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/block-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/block-task/config/verify.json +1 -1
- package/templates/.agents/skills/cancel-task/SKILL.en.md +3 -3
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/cancel-task/config/verify.json +1 -1
- package/templates/.agents/skills/code-task/SKILL.en.md +7 -7
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +7 -7
- package/templates/.agents/skills/code-task/config/verify.en.json +1 -1
- package/templates/.agents/skills/code-task/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +1 -1
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +1 -1
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +1 -1
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/complete-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/complete-task/config/verify.en.json +2 -2
- package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +2 -2
- package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +1 -1
- package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +1 -1
- package/templates/.agents/skills/create-pr/SKILL.en.md +2 -2
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/create-pr/config/verify.json +1 -1
- package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +2 -2
- package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +2 -2
- package/templates/.agents/skills/create-task/SKILL.en.md +5 -5
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/create-task/config/verify.json +1 -1
- package/templates/.agents/skills/import-issue/SKILL.en.md +8 -8
- package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +8 -8
- package/templates/.agents/skills/import-issue/config/verify.json +2 -2
- package/templates/.agents/skills/plan-task/SKILL.en.md +4 -4
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/plan-task/config/verify.en.json +1 -1
- package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/restore-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/restore-task/config/verify.json +1 -1
- package/templates/.agents/skills/review-analysis/SKILL.en.md +2 -2
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -1
- package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/review-analysis/reference/report-template.en.md +7 -7
- package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +6 -6
- 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/config/verify.en.json +1 -1
- package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/review-code/reference/report-template.en.md +12 -12
- package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +12 -12
- package/templates/.agents/skills/review-plan/SKILL.en.md +2 -2
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/review-plan/config/verify.en.json +1 -1
- package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/review-plan/reference/report-template.en.md +7 -7
- package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +7 -7
- package/templates/.agents/skills/review-pr/config/verify.en.json +1 -1
- package/templates/.agents/skills/review-pr/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/review-pr/reference/evidence-grading.en.md +3 -3
- package/templates/.agents/skills/review-pr/reference/evidence-grading.zh-CN.md +3 -3
- package/templates/.agents/skills/run-manual-validation/config/verify.en.json +1 -1
- package/templates/.agents/skills/run-manual-validation/config/verify.zh-CN.json +1 -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/templates/task.en.md +1 -1
- package/templates/.agents/templates/task.zh-CN.md +1 -1
- package/templates/.claude/commands/import-issue.en.md +1 -1
- package/templates/.claude/commands/import-issue.zh-CN.md +1 -1
- package/templates/.claude/commands/restore-task.en.md +1 -1
- package/templates/.claude/commands/restore-task.zh-CN.md +1 -1
|
@@ -252,7 +252,7 @@ async function resolvedContext(taskRef, options) {
|
|
|
252
252
|
const fact = factRead.status === 'valid' ? factRead.fact : null;
|
|
253
253
|
let issueIdentity;
|
|
254
254
|
try {
|
|
255
|
-
issueIdentity = taskIssueIdentity(frontmatter
|
|
255
|
+
issueIdentity = taskIssueIdentity(frontmatter);
|
|
256
256
|
}
|
|
257
257
|
catch (error) {
|
|
258
258
|
return { ok: false, output: result('failed', resolved.taskId, null, null, { error: { ...taskIssueIdentityError(error), retryable: false } }) };
|
|
@@ -776,7 +776,7 @@ async function resolveExternalPullRequest(taskRef, options) {
|
|
|
776
776
|
return externalResult(result('failed', base.resolved.taskId, null, base.prNumber, {
|
|
777
777
|
platform: base.context.platform,
|
|
778
778
|
capabilities: base.context.capabilities,
|
|
779
|
-
error: { code: 'EXTERNAL_DELIVERY_ISSUE_REQUIRED', message: 'External delivery requires a valid
|
|
779
|
+
error: { code: 'EXTERNAL_DELIVERY_ISSUE_REQUIRED', message: 'External delivery requires a valid platform issue identity', retryable: false }
|
|
780
780
|
}));
|
|
781
781
|
const inspected = base.provider.changeRequests?.listClosing
|
|
782
782
|
? await base.provider.changeRequests.listClosing({
|
|
@@ -1285,7 +1285,7 @@ function skipPlatformPullRequestFactUnlocked(resolved, options) {
|
|
|
1285
1285
|
}
|
|
1286
1286
|
let issueIdentity;
|
|
1287
1287
|
try {
|
|
1288
|
-
issueIdentity = taskIssueIdentity(frontmatter
|
|
1288
|
+
issueIdentity = taskIssueIdentity(frontmatter);
|
|
1289
1289
|
}
|
|
1290
1290
|
catch (error) {
|
|
1291
1291
|
return result('failed', resolved.taskId, null, null, { error: { ...taskIssueIdentityError(error), retryable: false } });
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { parseResourceIdentity } from './resource-identity.js';
|
|
2
|
-
import { isLegacyCompatibilityEnabled, legacyCompatibilityError, VERSION } from '../version.js';
|
|
3
2
|
import { parse as parseYaml } from 'yaml';
|
|
4
|
-
function taskIssueIdentity(frontmatter
|
|
3
|
+
function taskIssueIdentity(frontmatter) {
|
|
5
4
|
const serialized = frontmatter.platform_issue_identity;
|
|
6
5
|
if (typeof serialized === 'string' && serialized.trim()) {
|
|
7
6
|
try {
|
|
@@ -12,13 +11,7 @@ function taskIssueIdentity(frontmatter, _declaration, runtimeVersion = VERSION)
|
|
|
12
11
|
return null;
|
|
13
12
|
}
|
|
14
13
|
}
|
|
15
|
-
|
|
16
|
-
const number = typeof legacy === 'number' ? legacy : typeof legacy === 'string' && /^[1-9]\d*$/u.test(legacy) ? Number(legacy) : NaN;
|
|
17
|
-
// TODO(compat): Remove the numeric issue_number fallback before the first stable v1.0.0 release.
|
|
18
|
-
if (Number.isSafeInteger(number) && number > 0 && !isLegacyCompatibilityEnabled(runtimeVersion)) {
|
|
19
|
-
throw legacyCompatibilityError('issue_number', runtimeVersion);
|
|
20
|
-
}
|
|
21
|
-
return Number.isSafeInteger(number) && number > 0 ? { kind: 'number', value: number } : null;
|
|
14
|
+
return null;
|
|
22
15
|
}
|
|
23
16
|
function taskIssueIdentityError(error) {
|
|
24
17
|
const value = error && typeof error === 'object' ? error : {};
|
|
@@ -10,6 +10,7 @@ import { planInLabelUpdate, validateInLabelMapping } from "./in-label-sync.js";
|
|
|
10
10
|
import { readPrDeliveryFact } from "../task/pr-delivery-fact.js";
|
|
11
11
|
import { providerError, providerOperationContext, resourceIdentityNumber, unsupportedProviderOperation } from "./provider-bridge.js";
|
|
12
12
|
import { taskIssueIdentity } from "./task-identities.js";
|
|
13
|
+
import { isTaskCommentTooLarge } from "./issue-comments.js";
|
|
13
14
|
import { CONTROL_MARKER_PATTERN, canonicalizeCommentBody, renderSafeCodeFence, sanitizeMarkdownDocument } from "./comment-safety.js";
|
|
14
15
|
const CHECK_TYPE = "platform-sync";
|
|
15
16
|
const VERSION_LINE_REGEX = /^[0-9]+\.[0-9]+\.x$/;
|
|
@@ -96,8 +97,8 @@ async function buildSyncContext({ taskDir, config, artifactFile }, shared) {
|
|
|
96
97
|
}
|
|
97
98
|
const prIdentity = fact.status === "valid" && fact.fact.state === "bound" ? fact.fact.identity.resource : null;
|
|
98
99
|
const prNumber = resourceIdentityNumber(prIdentity);
|
|
99
|
-
if (config.when === "
|
|
100
|
-
return { earlyReturn: shared.passResult(CHECK_TYPE, "Skipped: task has no
|
|
100
|
+
if (config.when === "platform_issue_identity_exists" && !issueIdentity) {
|
|
101
|
+
return { earlyReturn: shared.passResult(CHECK_TYPE, "Skipped: task has no platform_issue_identity") };
|
|
101
102
|
}
|
|
102
103
|
if (config.when === "pr_fact_bound" && !prIdentity) {
|
|
103
104
|
return { earlyReturn: shared.passResult(CHECK_TYPE, "Skipped: task has no verified bound pull request") };
|
|
@@ -427,6 +428,9 @@ function checkTaskCommentContent(context, remoteData, shared) {
|
|
|
427
428
|
if (!context.config.verify_task_comment_content) {
|
|
428
429
|
return null;
|
|
429
430
|
}
|
|
431
|
+
if (isTaskCommentTooLarge(context.task.content)) {
|
|
432
|
+
return null;
|
|
433
|
+
}
|
|
430
434
|
const taskMarker = `<!-- sync-issue:${context.task.metadata.id}:task -->`;
|
|
431
435
|
const comment = findCommentByMarker(remoteData.comments, taskMarker);
|
|
432
436
|
if (!comment) {
|
|
@@ -12,10 +12,6 @@ export class HostResolutionError extends Error {
|
|
|
12
12
|
this.code = code;
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
|
-
function toPositiveNumber(value) {
|
|
16
|
-
const n = Number(value);
|
|
17
|
-
return Number.isInteger(n) && n > 0 ? n : null;
|
|
18
|
-
}
|
|
19
15
|
/**
|
|
20
16
|
* Parse the PR body for closing-issue references. Handles the conventional
|
|
21
17
|
* `Closes #1` / `Fixes: #2, #3` shapes (case-insensitive, comma/space
|
|
@@ -41,7 +37,7 @@ export function extractClosingIssueNumbers(body) {
|
|
|
41
37
|
/**
|
|
42
38
|
* Scan `.agents/workspace/active/{task-id}/task.md` files for tasks bound to the
|
|
43
39
|
* PR number or to one of the PR's closing issues. A task is emitted at most once;
|
|
44
|
-
* a verified fact identity hit takes priority over the issue-
|
|
40
|
+
* a verified fact identity hit takes priority over the issue-identity reverse lookup.
|
|
45
41
|
*/
|
|
46
42
|
export function collectHostCandidates(input) {
|
|
47
43
|
const activeRoot = path.join(input.workspaceRoot, '.agents', 'workspace', 'active');
|
|
@@ -73,7 +69,7 @@ export function collectHostCandidates(input) {
|
|
|
73
69
|
continue;
|
|
74
70
|
}
|
|
75
71
|
const issueIdentity = taskIssueIdentity(frontmatter);
|
|
76
|
-
const issueNumber = resourceIdentityNumber(issueIdentity)
|
|
72
|
+
const issueNumber = resourceIdentityNumber(issueIdentity);
|
|
77
73
|
const fact = readPrDeliveryFact(frontmatter);
|
|
78
74
|
const prIdentity = fact.status === 'valid' && fact.fact.state === 'bound' ? fact.fact.identity.resource : null;
|
|
79
75
|
const prNumber = resourceIdentityNumber(prIdentity);
|
|
@@ -133,8 +129,8 @@ function isTrusted(head) {
|
|
|
133
129
|
/**
|
|
134
130
|
* Derive the S1(a) trust factor `taskIssueMatches` (PL-3) mechanically from
|
|
135
131
|
* resolved data: a unique host is trusted on the issue dimension only when the
|
|
136
|
-
* task's bound
|
|
137
|
-
* hosts and tasks without a
|
|
132
|
+
* task's bound issue identity is among the PR's closing issues. Ambiguous/none
|
|
133
|
+
* hosts and tasks without a numeric issue identity cannot be confirmed -> false
|
|
138
134
|
* (fail closed to S2/audit). `resolve-host` emits this value so the prompt layer
|
|
139
135
|
* does not derive the trust factor ad hoc (CD-2).
|
|
140
136
|
*/
|
|
@@ -2,11 +2,27 @@ import { parseArgs } from 'node:util';
|
|
|
2
2
|
import { loadConfig } from '../config.js';
|
|
3
3
|
import { createSandboxCapabilityPlan } from '../agent-client-reconciler.js';
|
|
4
4
|
import { resolveSandboxCleanupTarget } from '../workspace-identity.js';
|
|
5
|
+
import { detectEngine } from '../engine.js';
|
|
6
|
+
import { sandboxBranchLabel, sandboxLabel, sandboxTaskIdLabel, sandboxWorkspaceModeLabel } from '../constants.js';
|
|
7
|
+
import { fetchSandboxRows } from './list-running.js';
|
|
5
8
|
import { rmOne, rmPurge, rmUnbound } from '../removal.js';
|
|
6
9
|
const USAGE = `Usage:
|
|
7
10
|
ai sandbox rm <branch | TASK-id | short id> Remove one sandbox; use a full TASK-id for a task-bound sandbox and a branch for branch-only sandboxes
|
|
8
|
-
ai sandbox rm --unbound [--dry-run] [--yes] Remove
|
|
11
|
+
ai sandbox rm --unbound [--dry-run] [--yes] Remove discovered task-bound and branch-only sandboxes
|
|
9
12
|
ai sandbox rm --purge Tear down ALL sandboxes for the project (containers, worktrees, image, VM)`;
|
|
13
|
+
function resolveMissingTaskSandboxBranch(taskId, config) {
|
|
14
|
+
const engine = detectEngine(config);
|
|
15
|
+
const listed = fetchSandboxRows(engine, sandboxLabel(config), sandboxBranchLabel(config), { mode: sandboxWorkspaceModeLabel(config), taskId: sandboxTaskIdLabel(config) });
|
|
16
|
+
const branches = new Set([...listed.running, ...listed.nonRunning]
|
|
17
|
+
.filter((row) => row.workspaceMode === 'task-bound' && row.taskId === taskId && row.branch)
|
|
18
|
+
.map((row) => row.branch));
|
|
19
|
+
if (branches.size === 0)
|
|
20
|
+
return null;
|
|
21
|
+
if (branches.size > 1) {
|
|
22
|
+
throw new Error(`SANDBOX_CLEANUP_TASK_ID_AMBIGUOUS: task ${taskId} matches multiple sandbox branches`);
|
|
23
|
+
}
|
|
24
|
+
return branches.values().next().value ?? null;
|
|
25
|
+
}
|
|
10
26
|
export async function rm(args) {
|
|
11
27
|
const { values, positionals } = parseArgs({
|
|
12
28
|
args,
|
|
@@ -53,7 +69,10 @@ export async function rm(args) {
|
|
|
53
69
|
});
|
|
54
70
|
return;
|
|
55
71
|
}
|
|
56
|
-
const cleanupTarget = resolveSandboxCleanupTarget(positionals[0] ?? '', config.repoRoot
|
|
72
|
+
const cleanupTarget = resolveSandboxCleanupTarget(positionals[0] ?? '', config.repoRoot, {
|
|
73
|
+
allowProtected: true,
|
|
74
|
+
resolveMissingTask: (taskId) => resolveMissingTaskSandboxBranch(taskId, config)
|
|
75
|
+
});
|
|
57
76
|
await rmOne(config, tools, cleanupTarget.branch, { cleanupTarget });
|
|
58
77
|
}
|
|
59
78
|
//# sourceMappingURL=rm.js.map
|
|
@@ -14,6 +14,7 @@ import { requestAuditFields, resultAuditFields, appendCriticalAudit, appendDiagn
|
|
|
14
14
|
import { parseTaskControlOperation } from '../../task/control-authority.js';
|
|
15
15
|
import { parseControlOutput, classifySandboxControlRecovery, findSandboxControlRecoveryOperation, operationRecoveryBinding, SANDBOX_CONTROL_REQUIRED_COMPLETION_PHASES } from '../../task/control-recovery.js';
|
|
16
16
|
import { readRun } from '../../task/orchestration.js';
|
|
17
|
+
import { readLifecycleRecoveryDomainEvidence } from '../../task/lifecycle-recovery.js';
|
|
17
18
|
import { captureRepositorySnapshot } from '../../task/workspace-snapshot.js';
|
|
18
19
|
import { parseTypedTaskFrontmatter } from '../../task/frontmatter.js';
|
|
19
20
|
import { readLifecycleJournalEvidence } from '../../task/lifecycle.js';
|
|
@@ -256,7 +257,7 @@ function outputMatchesEvidence(output, bytes, sha256) {
|
|
|
256
257
|
return Buffer.byteLength(output, 'utf8') === bytes
|
|
257
258
|
&& createHash('sha256').update(output, 'utf8').digest('hex') === sha256;
|
|
258
259
|
}
|
|
259
|
-
function genericRecoveryResponse(request, exitCode, payload, cause = 'recovery') {
|
|
260
|
+
export function genericRecoveryResponse(request, exitCode, payload, cause = 'recovery', warning = null) {
|
|
260
261
|
const outputUnavailable = request.family === 'task-create' && !payload;
|
|
261
262
|
return {
|
|
262
263
|
version: 2,
|
|
@@ -264,9 +265,11 @@ function genericRecoveryResponse(request, exitCode, payload, cause = 'recovery')
|
|
|
264
265
|
phase: 'completed',
|
|
265
266
|
exitCode,
|
|
266
267
|
stdout: outputUnavailable ? `${JSON.stringify(taskCreateOutputUnavailableResult(request.id))}\n` : '',
|
|
267
|
-
stderr: payload ? '' :
|
|
268
|
-
?
|
|
269
|
-
:
|
|
268
|
+
stderr: payload ? '' : warning
|
|
269
|
+
? `${warning.code}: ${warning.message}\nAction: ${warning.action}\n`
|
|
270
|
+
: cause === 'publish'
|
|
271
|
+
? 'SANDBOX_CONTROL_OUTPUT_UNAVAILABLE: output payload was not retained\n'
|
|
272
|
+
: 'SANDBOX_CONTROL_OUTPUT_UNAVAILABLE: broker restarted after executor completion\n',
|
|
270
273
|
error: null,
|
|
271
274
|
outputState: payload ? 'available' : 'unavailable',
|
|
272
275
|
payload: payload ? payloadReference(payload) : null
|
|
@@ -365,6 +368,23 @@ function readRecoveryDomain(manifest, manifestPath, request, operation, terminal
|
|
|
365
368
|
return { domain: null };
|
|
366
369
|
}
|
|
367
370
|
if (operation.family === 'task-lifecycle') {
|
|
371
|
+
if (operation.intent === 'recover-started') {
|
|
372
|
+
try {
|
|
373
|
+
if (!('args' in request))
|
|
374
|
+
return { domain: null };
|
|
375
|
+
const parsed = parseTaskControlOperation('task-lifecycle', request.args);
|
|
376
|
+
if (parsed.family !== 'task-lifecycle' || parsed.request.intent !== 'recover-started') {
|
|
377
|
+
return { domain: null };
|
|
378
|
+
}
|
|
379
|
+
return {
|
|
380
|
+
domain: readLifecycleRecoveryDomainEvidence(manifest.repoRoot, parsed.request, terminalResult),
|
|
381
|
+
journal: readLifecycleJournalEvidence(manifest.repoRoot, taskRef)
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
catch {
|
|
385
|
+
return { domain: null };
|
|
386
|
+
}
|
|
387
|
+
}
|
|
368
388
|
if (!taskRef || !terminalResult.targetState)
|
|
369
389
|
return { domain: null };
|
|
370
390
|
const journal = readLifecycleJournalEvidence(manifest.repoRoot, taskRef);
|
|
@@ -441,7 +461,7 @@ function readCommittedCriticalPhases(manifest, requestId) {
|
|
|
441
461
|
}
|
|
442
462
|
});
|
|
443
463
|
}
|
|
444
|
-
function recoveryResponse(manifest, manifestPath, request, evidence, payload, terminalResult) {
|
|
464
|
+
export function recoveryResponse(manifest, manifestPath, request, evidence, payload, terminalResult) {
|
|
445
465
|
const operationName = recoveryOperationKey(request, terminalResult, payload?.stdout);
|
|
446
466
|
const operation = operationName ? findSandboxControlRecoveryOperation(request.family, operationName) : null;
|
|
447
467
|
if (!operation)
|
|
@@ -503,7 +523,7 @@ function recoveryResponse(manifest, manifestPath, request, evidence, payload, te
|
|
|
503
523
|
return null;
|
|
504
524
|
return finalization.response ?? null;
|
|
505
525
|
}
|
|
506
|
-
return genericRecoveryResponse(request, evidence.exitCode, payload);
|
|
526
|
+
return genericRecoveryResponse(request, evidence.exitCode, payload, 'recovery', terminalResult.warning ?? null);
|
|
507
527
|
}
|
|
508
528
|
function terminalMatchesEvidence(manifest, manifestPath, request, response, evidence, payload, payloadInvalid, terminalResult) {
|
|
509
529
|
if (request.family === 'task-finalization' && evidence.exitCode === 0) {
|
|
@@ -538,7 +558,7 @@ function terminalMatchesEvidence(manifest, manifestPath, request, response, evid
|
|
|
538
558
|
if (response.outputState === 'unavailable') {
|
|
539
559
|
const causes = ['recovery', 'publish'];
|
|
540
560
|
const valid = causes.some((cause) => JSON.stringify(response)
|
|
541
|
-
=== JSON.stringify(genericRecoveryResponse(request, evidence.exitCode, null, cause)));
|
|
561
|
+
=== JSON.stringify(genericRecoveryResponse(request, evidence.exitCode, null, cause, terminalResult?.warning ?? null)));
|
|
542
562
|
return { valid, payloadReferenced: false };
|
|
543
563
|
}
|
|
544
564
|
return {
|
|
@@ -550,7 +570,7 @@ function terminalMatchesEvidence(manifest, manifestPath, request, response, evid
|
|
|
550
570
|
}
|
|
551
571
|
function publishExecutionResult(manifest, request, result, broker, brokerOwns) {
|
|
552
572
|
const normalized = sanitizeSandboxControlResult(manifest, result);
|
|
553
|
-
writeSandboxControlTerminalResult(manifest, {
|
|
573
|
+
const terminalResult = writeSandboxControlTerminalResult(manifest, {
|
|
554
574
|
id: request.id,
|
|
555
575
|
family: request.family,
|
|
556
576
|
operation: operationKey(request, normalized.stdout)
|
|
@@ -590,7 +610,7 @@ function publishExecutionResult(manifest, request, result, broker, brokerOwns) {
|
|
|
590
610
|
catch {
|
|
591
611
|
payload = null;
|
|
592
612
|
}
|
|
593
|
-
terminal = genericRecoveryResponse(request, normalized.exitCode, payload, 'publish');
|
|
613
|
+
terminal = genericRecoveryResponse(request, normalized.exitCode, payload, 'publish', terminalResult.warning ?? null);
|
|
594
614
|
}
|
|
595
615
|
}
|
|
596
616
|
if (!brokerOwns())
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { isCompletionEvidence } from '../../task/orchestration.js';
|
|
2
2
|
import { parseControlOutput } from '../../task/control-recovery.js';
|
|
3
|
+
import { isRecoveryWarning } from '../../task/recovery-warning.js';
|
|
3
4
|
import { execFileSync } from 'node:child_process';
|
|
4
5
|
import fs from 'node:fs';
|
|
5
6
|
import path from 'node:path';
|
|
@@ -83,6 +84,7 @@ export function createSandboxControlTerminalResult(manifest, request, output) {
|
|
|
83
84
|
? nested.run : null;
|
|
84
85
|
const rawCompletion = nested.completionEvidence ?? run?.completionEvidence;
|
|
85
86
|
const operation = request.operation ?? (typeof nested.intent === 'string' ? nested.intent : null) ?? '';
|
|
87
|
+
const warning = isRecoveryWarning(nested.warning) ? nested.warning : null;
|
|
86
88
|
return {
|
|
87
89
|
version: 1,
|
|
88
90
|
requestId: request.id,
|
|
@@ -92,7 +94,8 @@ export function createSandboxControlTerminalResult(manifest, request, output) {
|
|
|
92
94
|
targetState: typeof nested.targetState === 'string' ? nested.targetState : null,
|
|
93
95
|
status: typeof nested.status === 'string' ? nested.status : 'completed',
|
|
94
96
|
changed: typeof nested.changed === 'boolean' ? nested.changed : null,
|
|
95
|
-
completionEvidence: isCompletionEvidence(rawCompletion) ? rawCompletion : null
|
|
97
|
+
completionEvidence: isCompletionEvidence(rawCompletion) ? rawCompletion : null,
|
|
98
|
+
...(warning ? { warning } : {})
|
|
96
99
|
};
|
|
97
100
|
}
|
|
98
101
|
export function writeSandboxControlTerminalResult(manifest, request, output) {
|
|
@@ -123,6 +126,9 @@ export function readSandboxControlTerminalResult(filePath) {
|
|
|
123
126
|
if (value.completionEvidence !== null && !isCompletionEvidence(value.completionEvidence)) {
|
|
124
127
|
throw new Error('SANDBOX_CONTROL_TERMINAL_RESULT_INVALID');
|
|
125
128
|
}
|
|
129
|
+
if (value.warning !== undefined && value.warning !== null && !isRecoveryWarning(value.warning)) {
|
|
130
|
+
throw new Error('SANDBOX_CONTROL_TERMINAL_RESULT_INVALID');
|
|
131
|
+
}
|
|
126
132
|
if (value.changed === undefined) {
|
|
127
133
|
throw new Error('SANDBOX_CONTROL_TERMINAL_RESULT_INVALID');
|
|
128
134
|
}
|
|
@@ -7,7 +7,7 @@ import { withTaskExecutionLock } from '../../task/task-execution-lock.js';
|
|
|
7
7
|
import { dispatchHostControlCommand } from '../../host-control/command.js';
|
|
8
8
|
import { assertSandboxTaskSource } from '../workspace-view.js';
|
|
9
9
|
import { appendDiagnosticAudit } from './audit.js';
|
|
10
|
-
import { readTaskArtifact
|
|
10
|
+
import { readTaskArtifact } from './task-workflow.js';
|
|
11
11
|
import { applyTaskEvent } from '../../task/events.js';
|
|
12
12
|
import { parseTaskEventRequest } from '../../internal/task-event.js';
|
|
13
13
|
function executionResult(result) {
|
|
@@ -43,7 +43,7 @@ export async function executeTaskWorkflow(manifest, request, lifecycleRecoveryAt
|
|
|
43
43
|
if (command === 'task-artifact' && 'operation' in input && input.operation === 'inspect') {
|
|
44
44
|
return executionResult(executeArtifactCommand(input, { repoRoot: manifest.repoRoot }));
|
|
45
45
|
}
|
|
46
|
-
if ('operation' in input &&
|
|
46
|
+
if ('operation' in input && input.operation === 'init') {
|
|
47
47
|
return executionResult(executeArtifactCommand(input, { repoRoot: manifest.repoRoot, artifactDir: taskDir }));
|
|
48
48
|
}
|
|
49
49
|
return await withTaskExecutionLock(manifest.repoRoot, request.taskId, `sandbox-control.${request.operation}`, async () => {
|
|
@@ -54,32 +54,22 @@ export async function executeTaskWorkflow(manifest, request, lifecycleRecoveryAt
|
|
|
54
54
|
const { family } = input;
|
|
55
55
|
if (family !== 'analysis' && family !== 'plan' && family !== 'code')
|
|
56
56
|
throw new Error('ARTIFACT_IDENTITY_INVALID');
|
|
57
|
-
const local = { taskRef: request.taskId, family, artifact: input.artifact, repoRoot: manifest.repoRoot };
|
|
57
|
+
const local = { taskRef: request.taskId, family, artifact: input.artifact, repoRoot: manifest.repoRoot, recoveryId: input.recoveryId, lockAlreadyHeld: true };
|
|
58
58
|
const prepared = prepareLocalArtifact(local, content, lifecycleRecoveryAttestation ?? undefined);
|
|
59
59
|
if (prepared.result.status === 'failed')
|
|
60
60
|
return executionResult(commitLocalArtifactProvenance(prepared));
|
|
61
61
|
publicationStarted = true;
|
|
62
|
-
await writeTaskArtifact(taskDir, {
|
|
63
|
-
artifact: artifact.artifact,
|
|
64
|
-
bytes: artifact.bytes,
|
|
65
|
-
expectedSha256: artifact.sha256
|
|
66
|
-
});
|
|
67
62
|
result = commitLocalArtifactProvenance(prepared);
|
|
68
63
|
}
|
|
69
64
|
else {
|
|
70
65
|
if (input.overrideTicket)
|
|
71
66
|
throw new Error('TASK_WORKFLOW_OVERRIDE_UNSUPPORTED');
|
|
72
|
-
const prepared = prepareReviewSummaryCandidate(input, content, { repoRoot: manifest.repoRoot });
|
|
67
|
+
const prepared = prepareReviewSummaryCandidate(input, content, { repoRoot: manifest.repoRoot, lockAlreadyHeld: true, startRecovery: true });
|
|
73
68
|
if (input.dryRun || prepared.result.status === 'planned')
|
|
74
69
|
return executionResult(prepared.result);
|
|
75
70
|
if (prepared.result.status === 'failed')
|
|
76
71
|
return executionResult(commitReviewSummaryProvenance(prepared, manifest.repoRoot));
|
|
77
72
|
publicationStarted = true;
|
|
78
|
-
await writeTaskArtifact(taskDir, {
|
|
79
|
-
artifact: input.artifact,
|
|
80
|
-
bytes: Buffer.from(prepared.content, 'utf8'),
|
|
81
|
-
expectedSha256: artifact.sha256
|
|
82
|
-
});
|
|
83
73
|
result = commitReviewSummaryProvenance(prepared, manifest.repoRoot);
|
|
84
74
|
}
|
|
85
75
|
if (result.status !== 'failed')
|
|
@@ -19,8 +19,7 @@ Commands:
|
|
|
19
19
|
rm <branch | TASK-id> | --unbound | --purge
|
|
20
20
|
Remove one sandbox; use a full TASK-id for task-bound
|
|
21
21
|
cleanup and a branch for branch-only cleanup. --unbound
|
|
22
|
-
removes
|
|
23
|
-
plus verified auxiliary state; active, blocked, and archive tasks are protected.
|
|
22
|
+
removes discovered task-bound and branch-only sandboxes.
|
|
24
23
|
start [--recreate] <branch | TASK-id | N>
|
|
25
24
|
Verify or recover an existing sandbox container;
|
|
26
25
|
optionally replace only the container on failure
|