@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.
Files changed (184) hide show
  1. package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +6 -2
  2. package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +43 -3
  3. package/dist/lib/fs/durable-write.js +2 -0
  4. package/dist/lib/internal/cli-route-inventory.js +1 -1
  5. package/dist/lib/internal/manual-validation.js +17 -8
  6. package/dist/lib/internal/task-artifact.js +1 -1
  7. package/dist/lib/internal/task-lifecycle.js +2 -2
  8. package/dist/lib/internal/task-operation-registry.js +0 -1
  9. package/dist/lib/internal/task-review.js +4 -4
  10. package/dist/lib/platform/issue-comments.js +16 -10
  11. package/dist/lib/platform/issues.js +1 -1
  12. package/dist/lib/platform/pr-change-report.js +5 -4
  13. package/dist/lib/platform/pr-summary.js +64 -32
  14. package/dist/lib/platform/pull-requests.js +3 -3
  15. package/dist/lib/platform/task-identities.js +2 -9
  16. package/dist/lib/platform/verification-sync.js +6 -2
  17. package/dist/lib/pr-review/evidence-grading.js +4 -8
  18. package/dist/lib/sandbox/commands/rm.js +21 -2
  19. package/dist/lib/sandbox/control/server.js +29 -9
  20. package/dist/lib/sandbox/control/state.js +7 -1
  21. package/dist/lib/sandbox/control/workflow-executor.js +4 -14
  22. package/dist/lib/sandbox/index.js +1 -2
  23. package/dist/lib/sandbox/removal.js +116 -1011
  24. package/dist/lib/sandbox/workspace-identity.js +17 -1
  25. package/dist/lib/task/artifact-command.js +6 -28
  26. package/dist/lib/task/artifact-operations.js +16 -230
  27. package/dist/lib/task/artifact-recovery.js +430 -0
  28. package/dist/lib/task/artifact-repair-intent.js +73 -39
  29. package/dist/lib/task/commit-operation.js +31 -4
  30. package/dist/lib/task/control-authority.js +38 -7
  31. package/dist/lib/task/control-recovery.js +14 -2
  32. package/dist/lib/task/create-service.js +1 -8
  33. package/dist/lib/task/delegation-receipts.js +64 -3
  34. package/dist/lib/task/events.js +164 -22
  35. package/dist/lib/task/human-override.js +5 -8
  36. package/dist/lib/task/intermediate-cleanup.js +66 -8
  37. package/dist/lib/task/lifecycle-recovery.js +549 -0
  38. package/dist/lib/task/lifecycle.js +12 -3
  39. package/dist/lib/task/local-artifact-finalization.js +148 -123
  40. package/dist/lib/task/manual-validation-receipt.js +5 -1
  41. package/dist/lib/task/orchestration.js +52 -2
  42. package/dist/lib/task/pr-delivery-fact.js +12 -56
  43. package/dist/lib/task/recovery-warning.js +19 -0
  44. package/dist/lib/task/review-command.js +1 -0
  45. package/dist/lib/task/review-finalization.js +104 -77
  46. package/dist/lib/task/short-id.js +15 -7
  47. package/dist/lib/task/task-execution-lock.js +1 -1
  48. package/dist/lib/task/verification-engine.js +3 -14
  49. package/dist/lib/task/workflow-command.js +0 -1
  50. package/dist/lib/version.js +1 -11
  51. package/dist/lifecycle-build-manifest.json +3 -3
  52. package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +7 -2
  53. package/lib/agent-clients/adapters/codex-lifecycle/store.ts +55 -3
  54. package/lib/fs/durable-write.ts +1 -0
  55. package/lib/internal/cli-route-inventory.ts +1 -1
  56. package/lib/internal/manual-validation.ts +17 -8
  57. package/lib/internal/task-artifact.ts +1 -1
  58. package/lib/internal/task-lifecycle.ts +4 -3
  59. package/lib/internal/task-operation-registry.ts +0 -1
  60. package/lib/internal/task-review.ts +4 -4
  61. package/lib/platform/issue-comments.ts +18 -10
  62. package/lib/platform/issues.ts +1 -1
  63. package/lib/platform/pr-change-report.ts +7 -5
  64. package/lib/platform/pr-summary.ts +64 -32
  65. package/lib/platform/pull-requests.ts +3 -3
  66. package/lib/platform/task-identities.ts +3 -14
  67. package/lib/platform/verification-sync.ts +6 -2
  68. package/lib/pr-review/evidence-grading.ts +4 -9
  69. package/lib/sandbox/commands/rm.ts +34 -2
  70. package/lib/sandbox/control/protocol.ts +5 -0
  71. package/lib/sandbox/control/server.ts +30 -10
  72. package/lib/sandbox/control/state.ts +9 -1
  73. package/lib/sandbox/control/workflow-executor.ts +4 -14
  74. package/lib/sandbox/index.ts +1 -2
  75. package/lib/sandbox/removal.ts +126 -1128
  76. package/lib/sandbox/workspace-identity.ts +19 -3
  77. package/lib/task/artifact-command.ts +8 -27
  78. package/lib/task/artifact-operations.ts +21 -251
  79. package/lib/task/artifact-recovery.ts +519 -0
  80. package/lib/task/artifact-repair-intent.ts +99 -51
  81. package/lib/task/commit-operation.ts +35 -4
  82. package/lib/task/control-authority.ts +47 -14
  83. package/lib/task/control-recovery.ts +13 -1
  84. package/lib/task/create-service.ts +1 -10
  85. package/lib/task/delegation-receipts.ts +73 -2
  86. package/lib/task/events.ts +187 -23
  87. package/lib/task/human-override.ts +5 -8
  88. package/lib/task/intermediate-cleanup.ts +50 -12
  89. package/lib/task/lifecycle-recovery.ts +691 -0
  90. package/lib/task/lifecycle.ts +13 -4
  91. package/lib/task/local-artifact-finalization.ts +176 -132
  92. package/lib/task/manual-validation-receipt.ts +5 -1
  93. package/lib/task/orchestration.ts +65 -0
  94. package/lib/task/pr-delivery-fact.ts +15 -62
  95. package/lib/task/recovery-warning.ts +21 -0
  96. package/lib/task/review-command.ts +1 -0
  97. package/lib/task/review-finalization.ts +106 -87
  98. package/lib/task/short-id.ts +27 -5
  99. package/lib/task/task-execution-lock.ts +1 -1
  100. package/lib/task/verification-engine.ts +3 -17
  101. package/lib/task/verification-types.ts +0 -2
  102. package/lib/task/workflow-command.ts +0 -1
  103. package/lib/version.ts +1 -15
  104. package/package.json +1 -1
  105. package/templates/.agents/rules/create-issue.en.md +1 -1
  106. package/templates/.agents/rules/create-issue.zh-CN.md +1 -1
  107. package/templates/.agents/rules/issue-sync.en.md +4 -2
  108. package/templates/.agents/rules/issue-sync.zh-CN.md +4 -3
  109. package/templates/.agents/rules/local-artifact-repair.en.md +46 -39
  110. package/templates/.agents/rules/local-artifact-repair.zh-CN.md +43 -36
  111. package/templates/.agents/skills/analyze-task/SKILL.en.md +6 -6
  112. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +6 -6
  113. package/templates/.agents/skills/analyze-task/config/verify.en.json +1 -1
  114. package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -1
  115. package/templates/.agents/skills/block-task/SKILL.en.md +2 -2
  116. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -2
  117. package/templates/.agents/skills/block-task/config/verify.json +1 -1
  118. package/templates/.agents/skills/cancel-task/SKILL.en.md +3 -3
  119. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +3 -3
  120. package/templates/.agents/skills/cancel-task/config/verify.json +1 -1
  121. package/templates/.agents/skills/code-task/SKILL.en.md +7 -7
  122. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +7 -7
  123. package/templates/.agents/skills/code-task/config/verify.en.json +1 -1
  124. package/templates/.agents/skills/code-task/config/verify.zh-CN.json +1 -1
  125. package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +1 -1
  126. package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +1 -1
  127. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +1 -1
  128. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +1 -1
  129. package/templates/.agents/skills/complete-task/SKILL.en.md +2 -2
  130. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +2 -2
  131. package/templates/.agents/skills/complete-task/config/verify.en.json +2 -2
  132. package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +2 -2
  133. package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +1 -1
  134. package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +1 -1
  135. package/templates/.agents/skills/create-pr/SKILL.en.md +2 -2
  136. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +2 -2
  137. package/templates/.agents/skills/create-pr/config/verify.json +1 -1
  138. package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +2 -2
  139. package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +2 -2
  140. package/templates/.agents/skills/create-task/SKILL.en.md +5 -5
  141. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +5 -5
  142. package/templates/.agents/skills/create-task/config/verify.json +1 -1
  143. package/templates/.agents/skills/import-issue/SKILL.en.md +8 -8
  144. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +8 -8
  145. package/templates/.agents/skills/import-issue/config/verify.json +2 -2
  146. package/templates/.agents/skills/plan-task/SKILL.en.md +4 -4
  147. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +4 -4
  148. package/templates/.agents/skills/plan-task/config/verify.en.json +1 -1
  149. package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +1 -1
  150. package/templates/.agents/skills/restore-task/SKILL.en.md +2 -2
  151. package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +2 -2
  152. package/templates/.agents/skills/restore-task/config/verify.json +1 -1
  153. package/templates/.agents/skills/review-analysis/SKILL.en.md +2 -2
  154. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +2 -2
  155. package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -1
  156. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -1
  157. package/templates/.agents/skills/review-analysis/reference/report-template.en.md +7 -7
  158. package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +6 -6
  159. package/templates/.agents/skills/review-code/SKILL.en.md +2 -2
  160. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +2 -2
  161. package/templates/.agents/skills/review-code/config/verify.en.json +1 -1
  162. package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -1
  163. package/templates/.agents/skills/review-code/reference/report-template.en.md +12 -12
  164. package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +12 -12
  165. package/templates/.agents/skills/review-plan/SKILL.en.md +2 -2
  166. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +2 -2
  167. package/templates/.agents/skills/review-plan/config/verify.en.json +1 -1
  168. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -1
  169. package/templates/.agents/skills/review-plan/reference/report-template.en.md +7 -7
  170. package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +7 -7
  171. package/templates/.agents/skills/review-pr/config/verify.en.json +1 -1
  172. package/templates/.agents/skills/review-pr/config/verify.zh-CN.json +1 -1
  173. package/templates/.agents/skills/review-pr/reference/evidence-grading.en.md +3 -3
  174. package/templates/.agents/skills/review-pr/reference/evidence-grading.zh-CN.md +3 -3
  175. package/templates/.agents/skills/run-manual-validation/config/verify.en.json +1 -1
  176. package/templates/.agents/skills/run-manual-validation/config/verify.zh-CN.json +1 -1
  177. package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
  178. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
  179. package/templates/.agents/templates/task.en.md +1 -1
  180. package/templates/.agents/templates/task.zh-CN.md +1 -1
  181. package/templates/.claude/commands/import-issue.en.md +1 -1
  182. package/templates/.claude/commands/import-issue.zh-CN.md +1 -1
  183. package/templates/.claude/commands/restore-task.en.md +1 -1
  184. 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, undefined, options.runtimeVersion);
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 issue_number', retryable: false }
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, undefined, options.runtimeVersion);
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, _declaration, runtimeVersion = VERSION) {
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
- const legacy = frontmatter.issue_number;
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 === "issue_number_exists" && !issueIdentity) {
100
- return { earlyReturn: shared.passResult(CHECK_TYPE, "Skipped: task has no issue_number") };
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-number reverse lookup.
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) ?? toPositiveNumber(frontmatter.issue_number);
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 issue_number is among the PR's closing issues. Ambiguous/none
137
- * hosts and tasks without a positive issue_number cannot be confirmed -> false
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 completed task-bound and branch-only sandboxes and verified auxiliary state; active, blocked, and archive tasks are protected
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 ? '' : cause === 'publish'
268
- ? 'SANDBOX_CONTROL_OUTPUT_UNAVAILABLE: output payload was not retained\n'
269
- : 'SANDBOX_CONTROL_OUTPUT_UNAVAILABLE: broker restarted after executor completion\n',
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, writeTaskArtifact } from './task-workflow.js';
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 && (input.operation === 'init' || input.operation === 'repair')) {
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 completed task-bound and branch-only sandboxes
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