@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.
Files changed (271) hide show
  1. package/bin/cli.ts +1 -1
  2. package/dist/bin/cli.js +1 -1
  3. package/dist/bin/internal-cli.js +6 -1
  4. package/dist/lib/agent-clients/adapters/codex-lifecycle/recovery.js +6 -6
  5. package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +8 -18
  6. package/dist/lib/agent-clients/adapters/codex-orchestration.js +1 -1
  7. package/dist/lib/decide.js +2 -7
  8. package/dist/lib/internal/cli-route-inventory.js +3 -0
  9. package/dist/lib/internal/manual-validation.js +8 -16
  10. package/dist/lib/internal/platform-comment.js +3 -2
  11. package/dist/lib/internal/task-activity.js +1 -12
  12. package/dist/lib/internal/task-event.js +1 -12
  13. package/dist/lib/internal/task-finalization.js +2 -2
  14. package/dist/lib/internal/task-ledger.js +1 -12
  15. package/dist/lib/internal/task-operation-registry.js +2 -1
  16. package/dist/lib/internal/task-qualification.js +6 -15
  17. package/dist/lib/internal/task-review.js +3 -16
  18. package/dist/lib/internal/task-short-id.js +2 -1
  19. package/dist/lib/internal/task-verify.js +1 -11
  20. package/dist/lib/internal/task-warning.js +1 -12
  21. package/dist/lib/internal/task-workspace.js +43 -0
  22. package/dist/lib/merge.js +4 -3
  23. package/dist/lib/platform/pr-change-report.js +10 -3
  24. package/dist/lib/platform/pr-summary.js +6 -15
  25. package/dist/lib/platform/pull-requests.js +3 -15
  26. package/dist/lib/pr-review/evidence-grading.js +3 -2
  27. package/dist/lib/process-data/index.js +18 -6
  28. package/dist/lib/process-data/sources.js +6 -0
  29. package/dist/lib/run/index.js +3 -2
  30. package/dist/lib/sandbox/commands/create.js +5 -1
  31. package/dist/lib/sandbox/commands/enter.js +1 -1
  32. package/dist/lib/sandbox/commands/list-running.js +2 -1
  33. package/dist/lib/sandbox/control/client.js +8 -13
  34. package/dist/lib/sandbox/control/completed-reentry.js +2 -1
  35. package/dist/lib/sandbox/control/lifecycle.js +2 -21
  36. package/dist/lib/sandbox/control/task-view.js +4 -2
  37. package/dist/lib/sandbox/mounts.js +4 -4
  38. package/dist/lib/sandbox/recovery.js +26 -16
  39. package/dist/lib/sandbox/removal.js +3 -10
  40. package/dist/lib/sandbox/task-resolver.js +9 -6
  41. package/dist/lib/sandbox/workspace-identity.js +36 -20
  42. package/dist/lib/sandbox/workspace-view.js +29 -22
  43. package/dist/lib/task/activity-intent.js +2 -12
  44. package/dist/lib/task/artifact-lifecycle.js +17 -11
  45. package/dist/lib/task/artifact-operations.js +41 -49
  46. package/dist/lib/task/capabilities.js +2 -2
  47. package/dist/lib/task/commands/ls.js +4 -3
  48. package/dist/lib/task/commands/status.js +1 -1
  49. package/dist/lib/task/commit-operation.js +6 -6
  50. package/dist/lib/task/control-authority.js +32 -32
  51. package/dist/lib/task/create-service.js +2 -4
  52. package/dist/lib/task/create.js +12 -65
  53. package/dist/lib/task/delegation-receipts.js +1 -1
  54. package/dist/lib/task/delivery.js +2 -8
  55. package/dist/lib/task/events.js +5 -31
  56. package/dist/lib/task/finalization.js +17 -22
  57. package/dist/lib/task/lifecycle.js +38 -49
  58. package/dist/lib/task/local-artifact-finalization.js +1 -2
  59. package/dist/lib/task/orchestration.js +24 -57
  60. package/dist/lib/task/platform-operation-recovery.js +1 -2
  61. package/dist/lib/task/resolve-ref.js +17 -12
  62. package/dist/lib/task/review-finalization.js +2 -35
  63. package/dist/lib/task/review-identity.js +3 -1
  64. package/dist/lib/task/short-id.js +12 -10
  65. package/dist/lib/task/verification-engine.js +19 -58
  66. package/dist/lib/task/workspace-root.js +59 -0
  67. package/dist/lib/task/workspace-snapshot.js +4 -3
  68. package/dist/lifecycle-build-manifest.json +3 -3
  69. package/lib/agent-clients/adapters/codex-lifecycle/recovery.ts +6 -6
  70. package/lib/agent-clients/adapters/codex-lifecycle/store.ts +8 -34
  71. package/lib/agent-clients/adapters/codex-orchestration.ts +1 -1
  72. package/lib/decide.ts +2 -7
  73. package/lib/internal/cli-route-inventory.ts +2 -0
  74. package/lib/internal/manual-validation.ts +8 -19
  75. package/lib/internal/platform-comment.ts +3 -2
  76. package/lib/internal/task-activity.ts +1 -10
  77. package/lib/internal/task-event.ts +1 -10
  78. package/lib/internal/task-finalization.ts +2 -2
  79. package/lib/internal/task-ledger.ts +1 -10
  80. package/lib/internal/task-operation-registry.ts +2 -1
  81. package/lib/internal/task-qualification.ts +5 -11
  82. package/lib/internal/task-review.ts +3 -14
  83. package/lib/internal/task-short-id.ts +2 -1
  84. package/lib/internal/task-verify.ts +1 -9
  85. package/lib/internal/task-warning.ts +1 -10
  86. package/lib/internal/task-workspace.ts +39 -0
  87. package/lib/merge.ts +4 -3
  88. package/lib/platform/pr-change-report.ts +9 -3
  89. package/lib/platform/pr-summary.ts +7 -16
  90. package/lib/platform/pull-requests.ts +3 -24
  91. package/lib/pr-review/evidence-grading.ts +3 -2
  92. package/lib/process-data/index.ts +17 -5
  93. package/lib/process-data/sources.ts +6 -0
  94. package/lib/run/index.ts +3 -2
  95. package/lib/sandbox/commands/create.ts +5 -1
  96. package/lib/sandbox/commands/enter.ts +1 -1
  97. package/lib/sandbox/commands/list-running.ts +2 -1
  98. package/lib/sandbox/control/client.ts +7 -12
  99. package/lib/sandbox/control/completed-reentry.ts +2 -1
  100. package/lib/sandbox/control/lifecycle.ts +4 -21
  101. package/lib/sandbox/control/task-view.ts +4 -2
  102. package/lib/sandbox/mounts.ts +4 -4
  103. package/lib/sandbox/recovery.ts +26 -18
  104. package/lib/sandbox/removal.ts +3 -26
  105. package/lib/sandbox/task-resolver.ts +9 -6
  106. package/lib/sandbox/workspace-identity.ts +42 -18
  107. package/lib/sandbox/workspace-view.ts +28 -20
  108. package/lib/task/activity-intent.ts +3 -17
  109. package/lib/task/artifact-lifecycle.ts +39 -11
  110. package/lib/task/artifact-operations.ts +28 -35
  111. package/lib/task/capabilities.ts +2 -2
  112. package/lib/task/commands/ls.ts +4 -3
  113. package/lib/task/commands/status.ts +1 -1
  114. package/lib/task/commit-operation.ts +5 -6
  115. package/lib/task/control-authority.ts +29 -35
  116. package/lib/task/create-service.ts +2 -4
  117. package/lib/task/create.ts +10 -51
  118. package/lib/task/delegation-receipts.ts +1 -1
  119. package/lib/task/delivery.ts +2 -10
  120. package/lib/task/events.ts +5 -32
  121. package/lib/task/finalization.ts +16 -24
  122. package/lib/task/lifecycle.ts +40 -51
  123. package/lib/task/local-artifact-finalization.ts +1 -4
  124. package/lib/task/orchestration.ts +31 -80
  125. package/lib/task/platform-operation-recovery.ts +1 -2
  126. package/lib/task/resolve-ref.ts +18 -12
  127. package/lib/task/review-finalization.ts +2 -52
  128. package/lib/task/review-identity.ts +3 -1
  129. package/lib/task/short-id.ts +15 -10
  130. package/lib/task/verification-engine.ts +20 -62
  131. package/lib/task/workspace-root.ts +78 -0
  132. package/lib/task/workspace-snapshot.ts +4 -3
  133. package/package.json +1 -1
  134. package/templates/.agents/QUICKSTART.en.md +19 -8
  135. package/templates/.agents/QUICKSTART.zh-CN.md +19 -8
  136. package/templates/.agents/README.en.md +10 -15
  137. package/templates/.agents/README.zh-CN.md +9 -13
  138. package/templates/.agents/rules/issue-sync.en.md +1 -1
  139. package/templates/.agents/rules/issue-sync.zh-CN.md +1 -1
  140. package/templates/.agents/rules/lifecycle-orchestration.en.md +1 -1
  141. package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +1 -1
  142. package/templates/.agents/rules/next-step-output.en.md +2 -2
  143. package/templates/.agents/rules/next-step-output.zh-CN.md +2 -2
  144. package/templates/.agents/rules/pr-sync.en.md +1 -1
  145. package/templates/.agents/rules/pr-sync.zh-CN.md +1 -1
  146. package/templates/.agents/rules/release-commands.en.md +3 -0
  147. package/templates/.agents/rules/release-commands.zh-CN.md +3 -0
  148. package/templates/.agents/rules/task-management.en.md +0 -2
  149. package/templates/.agents/rules/task-management.zh-CN.md +0 -2
  150. package/templates/.agents/rules/task-short-id.en.md +2 -2
  151. package/templates/.agents/rules/task-short-id.zh-CN.md +2 -2
  152. package/templates/.agents/scripts/task-short-id.js +2 -8
  153. package/templates/.agents/skills/analyze-task/SKILL.en.md +6 -6
  154. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +6 -6
  155. package/templates/.agents/skills/analyze-task/config/verify.en.json +0 -1
  156. package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +0 -1
  157. package/templates/.agents/skills/archive-tasks/SKILL.en.md +5 -5
  158. package/templates/.agents/skills/archive-tasks/SKILL.zh-CN.md +5 -5
  159. package/templates/.agents/skills/archive-tasks/scripts/archive-tasks.sh +10 -2
  160. package/templates/.agents/skills/block-task/SKILL.en.md +4 -4
  161. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +5 -5
  162. package/templates/.agents/skills/block-task/config/verify.json +0 -1
  163. package/templates/.agents/skills/cancel-task/SKILL.en.md +6 -6
  164. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +6 -6
  165. package/templates/.agents/skills/cancel-task/config/verify.json +0 -1
  166. package/templates/.agents/skills/code-task/SKILL.en.md +1 -1
  167. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +3 -3
  168. package/templates/.agents/skills/code-task/config/verify.en.json +0 -1
  169. package/templates/.agents/skills/code-task/config/verify.zh-CN.json +0 -1
  170. package/templates/.agents/skills/code-task/reference/output-template.en.md +1 -1
  171. package/templates/.agents/skills/code-task/reference/output-template.zh-CN.md +1 -1
  172. package/templates/.agents/skills/commit/SKILL.en.md +1 -1
  173. package/templates/.agents/skills/commit/SKILL.zh-CN.md +1 -1
  174. package/templates/.agents/skills/commit/config/verify.json +0 -1
  175. package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +2 -2
  176. package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +2 -2
  177. package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +1 -1
  178. package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +1 -1
  179. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +2 -2
  180. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +2 -2
  181. package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +0 -1
  182. package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +0 -1
  183. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +1 -1
  184. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +1 -1
  185. package/templates/.agents/skills/complete-task/SKILL.en.md +17 -9
  186. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +18 -10
  187. package/templates/.agents/skills/complete-task/config/verify.en.json +0 -1
  188. package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +0 -1
  189. package/templates/.agents/skills/create-pr/SKILL.en.md +3 -3
  190. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +3 -3
  191. package/templates/.agents/skills/create-pr/config/verify.json +0 -1
  192. package/templates/.agents/skills/create-pr/reference/change-report.en.md +13 -3
  193. package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +13 -3
  194. package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +1 -1
  195. package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +1 -1
  196. package/templates/.agents/skills/create-release-note/SKILL.en.md +2 -2
  197. package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +2 -2
  198. package/templates/.agents/skills/create-task/SKILL.en.md +4 -4
  199. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +4 -4
  200. package/templates/.agents/skills/create-task/config/verify.json +0 -1
  201. package/templates/.agents/skills/import-codescan/SKILL.en.md +1 -1
  202. package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +1 -1
  203. package/templates/.agents/skills/import-codescan/config/verify.json +0 -1
  204. package/templates/.agents/skills/import-dependabot/SKILL.en.md +3 -3
  205. package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +3 -3
  206. package/templates/.agents/skills/import-dependabot/config/verify.json +0 -1
  207. package/templates/.agents/skills/import-issue/SKILL.en.md +5 -5
  208. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +5 -5
  209. package/templates/.agents/skills/import-issue/config/verify.json +0 -1
  210. package/templates/.agents/skills/plan-task/SKILL.en.md +5 -5
  211. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +5 -5
  212. package/templates/.agents/skills/plan-task/config/verify.en.json +0 -1
  213. package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +0 -1
  214. package/templates/.agents/skills/restore-task/SKILL.en.md +2 -2
  215. package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +3 -3
  216. package/templates/.agents/skills/restore-task/config/verify.json +0 -1
  217. package/templates/.agents/skills/review-analysis/SKILL.en.md +1 -1
  218. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +2 -2
  219. package/templates/.agents/skills/review-analysis/config/verify.en.json +0 -1
  220. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +0 -1
  221. package/templates/.agents/skills/review-analysis/reference/output-templates.en.md +5 -5
  222. package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +5 -5
  223. package/templates/.agents/skills/review-code/SKILL.en.md +2 -2
  224. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +2 -2
  225. package/templates/.agents/skills/review-code/reference/output-templates.en.md +4 -4
  226. package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +4 -4
  227. package/templates/.agents/skills/review-plan/SKILL.en.md +1 -1
  228. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +2 -2
  229. package/templates/.agents/skills/review-plan/config/verify.en.json +0 -1
  230. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +0 -1
  231. package/templates/.agents/skills/review-plan/reference/output-templates.en.md +5 -5
  232. package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +5 -5
  233. package/templates/.agents/skills/review-pr/SKILL.en.md +2 -2
  234. package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +2 -2
  235. package/templates/.agents/skills/review-pr/config/verify.en.json +0 -1
  236. package/templates/.agents/skills/review-pr/config/verify.zh-CN.json +0 -1
  237. package/templates/.agents/skills/review-pr/reference/output-guidance.en.md +1 -1
  238. package/templates/.agents/skills/review-pr/reference/output-guidance.zh-CN.md +1 -1
  239. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +5 -5
  240. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +5 -5
  241. package/templates/.agents/skills/run-manual-validation/config/verify.en.json +0 -1
  242. package/templates/.agents/skills/run-manual-validation/config/verify.zh-CN.json +0 -1
  243. package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.en.md +13 -2
  244. package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.zh-CN.md +13 -2
  245. package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +2 -0
  246. package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +2 -0
  247. package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
  248. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
  249. package/templates/.agents/skills/test/SKILL.en.md +0 -3
  250. package/templates/.agents/skills/test/SKILL.zh-CN.md +0 -3
  251. package/templates/.agents/skills/test-integration/SKILL.en.md +0 -3
  252. package/templates/.agents/skills/test-integration/SKILL.zh-CN.md +0 -3
  253. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +13 -6
  254. package/templates/.agents/skills/upgrade-dependency/SKILL.en.md +0 -3
  255. package/templates/.agents/skills/upgrade-dependency/SKILL.zh-CN.md +0 -3
  256. package/templates/.agents/skills/watch-pr/SKILL.en.md +21 -4
  257. package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +22 -5
  258. package/templates/.agents/skills/watch-pr/config/verify.json +0 -1
  259. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +14 -0
  260. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +15 -1
  261. package/templates/.agents/templates/handoff.en.md +1 -1
  262. package/templates/.agents/templates/handoff.zh-CN.md +1 -1
  263. package/templates/.agents/workflows/bug-fix.en.yaml +1 -1
  264. package/templates/.agents/workflows/feature-development.en.yaml +1 -1
  265. package/templates/.agents/workflows/refactoring.en.yaml +1 -1
  266. package/dist/lib/runtime/agent-runtime.js +0 -33
  267. package/dist/lib/task/runtime-paths.js +0 -10
  268. package/dist/lib/task/task-execution-lock.js +0 -250
  269. package/lib/runtime/agent-runtime.ts +0 -41
  270. package/lib/task/runtime-paths.ts +0 -12
  271. package/lib/task/task-execution-lock.ts +0 -320
@@ -7,7 +7,6 @@ import type {
7
7
  PrReviewInspectIntent
8
8
  } from '../task/activity-intent.ts';
9
9
  import { resolveTaskRef } from '../task/resolve-ref.ts';
10
- import { TaskExecutionLockError, withTaskExecutionLock } from '../task/task-execution-lock.ts';
11
10
  import { ensureInternalHandlerRoute, internalHandlerRoute } from './cli-route-inventory.ts';
12
11
 
13
12
  const USAGE = `Usage: agent-infra-internal task-activity <task-ref> pr-review-inspect
@@ -95,15 +94,7 @@ async function taskActivity(args: string[] = []): Promise<void> {
95
94
  }
96
95
  const resolved = resolveTaskRef(taskRef);
97
96
  if (!resolved.ok) { usageFailure(resolved.message); return; }
98
- let result;
99
- try {
100
- result = await withTaskExecutionLock(resolved.repoRoot, resolved.taskId, `task-activity.${kind}`, async () => applyPrReviewActivityIntent(values as PrReviewActivityIntent, { lockAlreadyHeld: true }));
101
- } catch (error) {
102
- if (!(error instanceof TaskExecutionLockError)) throw error;
103
- process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: error.code, message: error.message } })}\n`);
104
- process.exitCode = 1;
105
- return;
106
- }
97
+ const result = await applyPrReviewActivityIntent(values as PrReviewActivityIntent);
107
98
  process.stdout.write(`${JSON.stringify(result)}\n`);
108
99
  if (result.status === 'failed') process.exitCode = 1;
109
100
  }
@@ -2,7 +2,6 @@ import { normalizeAgentToken, AGENT_USAGE_HINT } from '../agent-clients/tokens.t
2
2
  import { applyTaskEvent, eventCatalog } from '../task/events.ts';
3
3
  import type { TaskEventRequest, Verdict } from '../task/events.ts';
4
4
  import { resolveTaskRef } from '../task/resolve-ref.ts';
5
- import { TaskExecutionLockError, withTaskExecutionLock } from '../task/task-execution-lock.ts';
6
5
  import { ensureInternalHandlerRoute, internalHandlerRoute } from './cli-route-inventory.ts';
7
6
 
8
7
  const USAGE = `Usage: agent-infra-internal task-event <N | TASK-id> <event> --agent <agent> [event options] [--orchestrated] [--dry-run]
@@ -73,15 +72,7 @@ async function taskEvent(args: string[] = []): Promise<void> {
73
72
  process.exitCode = 1;
74
73
  return;
75
74
  }
76
- let result;
77
- try {
78
- result = await withTaskExecutionLock(resolved.repoRoot, resolved.taskId, `task-event.${request.event}`, async () => applyTaskEvent(request, { lockAlreadyHeld: true }));
79
- } catch (error) {
80
- if (!(error instanceof TaskExecutionLockError)) throw error;
81
- process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: error.code, message: error.message } })}\n`);
82
- process.exitCode = 1;
83
- return;
84
- }
75
+ const result = await applyTaskEvent(request);
85
76
  process.stdout.write(`${JSON.stringify(result)}\n`);
86
77
  if (result.status === 'failed') process.exitCode = 1;
87
78
  }
@@ -7,7 +7,7 @@ import {
7
7
  import { applyTaskFinalization } from '../task/finalization.ts';
8
8
  import { detectRepoRoot, resolveTaskRef } from '../task/resolve-ref.ts';
9
9
  import { resolveSandboxControlTransport } from './task-operation-registry.ts';
10
- import { quiesceSandboxControlForTaskCompletion } from '../sandbox/control/lifecycle.ts';
10
+ import { validateSandboxControlTaskIdentity } from '../sandbox/control/lifecycle.ts';
11
11
  import { loadSandboxControlLocation } from '../sandbox/config.ts';
12
12
  import { serializeTaskFinalizationEnvelope } from '../task/finalization-envelope.ts';
13
13
  import { verifyTaskEvent } from '../task/verification.ts';
@@ -97,7 +97,7 @@ async function taskFinalization(args: string[] = []): Promise<void> {
97
97
  repoRoot,
98
98
  beforeLifecycle: async (currentTaskId) => {
99
99
  if (currentTaskId !== taskId) throw new Error('SANDBOX_CONTROL_IDENTITY_MISMATCH');
100
- await quiesceSandboxControlForTaskCompletion(
100
+ validateSandboxControlTaskIdentity(
101
101
  controlRoot,
102
102
  { taskId, generation, controlRootId, statusDir }
103
103
  );
@@ -4,7 +4,6 @@ import { applyLedgerIntent } from '../task/ledger-intents.ts';
4
4
  import type { LedgerIntent } from '../task/ledger-intents.ts';
5
5
  import { LEDGER_SECTION_MISSING_CODE, LEDGER_SECTION_MISSING_MESSAGE, isReviewStage, parseLedgerDocument, summarizeLedgerStage, validateLedgerRows } from '../task/ledger.ts';
6
6
  import { resolveTaskRef } from '../task/resolve-ref.ts';
7
- import { TaskExecutionLockError, withTaskExecutionLock } from '../task/task-execution-lock.ts';
8
7
  import { ensureInternalHandlerRoute, internalHandlerRoute } from './cli-route-inventory.ts';
9
8
 
10
9
  const USAGE = `Usage: agent-infra-internal task-ledger <task-ref> <intent> [intent flags] [--dry-run]\n\nIntents: finding-upsert, finding-respond, finding-review, decision-next-id, decision-upsert, rework-intent-upsert, rework-intent-rebuild, stage-status\n`;
@@ -116,15 +115,7 @@ async function taskLedger(args: string[] = []): Promise<void> {
116
115
  }
117
116
  const lockResolved = resolveTaskRef(taskRef);
118
117
  if (!lockResolved.ok) { usageFailure(lockResolved.message); return; }
119
- let result;
120
- try {
121
- result = await withTaskExecutionLock(lockResolved.repoRoot, lockResolved.taskId, `task-ledger.${kind}`, async () => applyLedgerIntent(values as LedgerIntent));
122
- } catch (error) {
123
- if (!(error instanceof TaskExecutionLockError)) throw error;
124
- process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: error.code, message: error.message } })}\n`);
125
- process.exitCode = 1;
126
- return;
127
- }
118
+ const result = await applyLedgerIntent(values as LedgerIntent);
128
119
  process.stdout.write(`${JSON.stringify(result)}\n`);
129
120
  if (result.status === 'failed') process.exitCode = 1;
130
121
  }
@@ -112,6 +112,7 @@ function internalTaskRoutes(): TaskOperationDescriptor[] {
112
112
  descriptor('internal', 'platform-checks', 'resolve-run', 'conditional', 'diagnostic'),
113
113
  descriptor('internal', 'platform-checks', 'logs', 'conditional', 'diagnostic'),
114
114
  descriptor('internal', 'task-context', 'resolve', 'task-bound', 'diagnostic'),
115
+ descriptor('internal', 'task-workspace', 'resolve', 'non-task', 'diagnostic', 'none'),
115
116
  descriptor('internal', 'task-ledger', 'decision-next-id', 'task-bound', 'diagnostic'),
116
117
  descriptor('internal', 'task-ledger', 'stage-status', 'task-bound', 'diagnostic'),
117
118
  descriptor('internal', 'task-ledger', 'finding-upsert', 'task-bound', 'progress'),
@@ -214,7 +215,7 @@ export const INTERNAL_DISPATCHER_ROUTES = Object.freeze([
214
215
  'task-create', 'task-qualification', 'sandbox-control', 'agent-client', 'codex-lifecycle',
215
216
  'git-workflow', 'task-delivery', 'release-workflow', 'platform-release-notes', 'platform-security', 'platform-metadata', 'platform-context',
216
217
  'platform-comment', 'platform-issue', 'platform-pr', 'platform-pr-review', 'pr-review-grade',
217
- 'platform-checks', 'task-context', 'task-ledger', 'task-warning', 'task-activity', 'task-artifact',
218
+ 'platform-checks', 'task-context', 'task-workspace', 'task-ledger', 'task-warning', 'task-activity', 'task-artifact',
218
219
  'task-orchestration', 'task-review', 'task-event', 'task-lifecycle',
219
220
  'task-finalization', 'task-platform-recovery', 'task-short-id', 'task-snapshot', 'task-verify', 'task-validate', 'manual-validation'
220
221
  ]);
@@ -5,7 +5,6 @@ import type { QualificationConfirmationRequest } from '../task/qualification-con
5
5
  import { applyQualificationProposal } from '../task/qualification-intents.ts';
6
6
  import type { QualificationProposalRequest } from '../task/qualification-intents.ts';
7
7
  import { resolveTaskRef } from '../task/resolve-ref.ts';
8
- import { TaskExecutionLockError, withTaskExecutionLock } from '../task/task-execution-lock.ts';
9
8
  import { ensureInternalHandlerRoute, internalHandlerRoute } from './cli-route-inventory.ts';
10
9
 
11
10
  const USAGE = `Usage: agent-infra-internal task-qualification <task-ref> <proposal|confirm|supersede|revoke> --input <json-file> [--dry-run]\n`;
@@ -46,16 +45,11 @@ async function taskQualification(args: string[] = []): Promise<void> {
46
45
  if (!payload || typeof payload !== 'object' || Array.isArray(payload)) { fail('qualification input must be an object'); return; }
47
46
  const resolved = resolveTaskRef(taskRef);
48
47
  if (!resolved.ok) { fail(resolved.message, resolved.code); return; }
49
- try {
50
- const result = await withTaskExecutionLock(resolved.repoRoot, resolved.taskId, `task-qualification.${operation}`, () => operation === 'proposal'
51
- ? applyQualificationProposal({ ...(payload as Record<string, unknown>), taskRef: resolved.taskId, dryRun } as QualificationProposalRequest, { repoRoot: resolved.repoRoot })
52
- : applyQualificationConfirmation({ ...(payload as Record<string, unknown>), taskRef: resolved.taskId, operation, dryRun } as QualificationConfirmationRequest, { repoRoot: resolved.repoRoot }));
53
- process.stdout.write(`${JSON.stringify(result)}\n`);
54
- if (result.status === 'failed') process.exitCode = 1;
55
- } catch (error) {
56
- if (error instanceof TaskExecutionLockError) fail(`${error.code}: ${error.message}`, error.code);
57
- else throw error;
58
- }
48
+ const result = await (operation === 'proposal'
49
+ ? applyQualificationProposal({ ...(payload as Record<string, unknown>), taskRef: resolved.taskId, dryRun } as QualificationProposalRequest, { repoRoot: resolved.repoRoot })
50
+ : applyQualificationConfirmation({ ...(payload as Record<string, unknown>), taskRef: resolved.taskId, operation, dryRun } as QualificationConfirmationRequest, { repoRoot: resolved.repoRoot }));
51
+ process.stdout.write(`${JSON.stringify(result)}\n`);
52
+ if (result.status === 'failed') process.exitCode = 1;
59
53
  }
60
54
 
61
55
  export { taskQualification };
@@ -1,7 +1,6 @@
1
1
  import { parseReviewCommand } from '../task/review-command.ts';
2
2
  import { finalizeReviewSummary, preflightReviewSummary } from '../task/review-finalization.ts';
3
3
  import { resolveTaskRef } from '../task/resolve-ref.ts';
4
- import { TaskExecutionLockError, withTaskExecutionLock } from '../task/task-execution-lock.ts';
5
4
  import { ensureInternalHandlerRoute } from './cli-route-inventory.ts';
6
5
 
7
6
  const USAGE = 'Usage: agent-infra-internal task-review <task-ref> <preflight|finalize-summary> --stage <analysis|plan|code> --artifact <review-*.md> [--orchestrated] [--dry-run]\n';
@@ -30,19 +29,9 @@ async function taskReview(args: string[] = []): Promise<void> {
30
29
  const operation = args[1]!;
31
30
  const resolved = resolveTaskRef(args[0]!);
32
31
  if (!resolved.ok) { failUsage(resolved.message); return; }
33
- let result;
34
- try {
35
- result = await withTaskExecutionLock(resolved.repoRoot, resolved.taskId, `task-review.${operation}`, async () => {
36
- return operation === 'preflight'
37
- ? preflightReviewSummary({ taskRef: args[0]!, stage, artifact, orchestrated, dryRun }, { lockAlreadyHeld: true })
38
- : finalizeReviewSummary({ taskRef: args[0]!, stage, artifact, orchestrated, dryRun }, { lockAlreadyHeld: true });
39
- });
40
- } catch (error) {
41
- if (!(error instanceof TaskExecutionLockError)) throw error;
42
- process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: error.code, message: error.message } })}\n`);
43
- process.exitCode = 1;
44
- return;
45
- }
32
+ const result = operation === 'preflight'
33
+ ? preflightReviewSummary({ taskRef: args[0]!, stage, artifact, orchestrated, dryRun })
34
+ : finalizeReviewSummary({ taskRef: args[0]!, stage, artifact, orchestrated, dryRun });
46
35
  process.stdout.write(`${JSON.stringify(result)}\n`);
47
36
  if (result.status === 'failed') process.exitCode = 1;
48
37
  }
@@ -1,6 +1,7 @@
1
1
  import path from 'node:path';
2
2
 
3
3
  import { detectRepoRoot } from '../task/resolve-ref.ts';
4
+ import { resolveProjectTaskWorkspaceRoot } from '../task/workspace-root.ts';
4
5
  import { configuredShortIdLength, executeShortIdCommand } from '../task/short-id.ts';
5
6
  import { ensureInternalHandlerRoute, internalHandlerRoute } from './cli-route-inventory.ts';
6
7
 
@@ -50,7 +51,7 @@ function taskShortId(args: string[] = []): void {
50
51
  if (positional.length > (operation === 'list' ? 0 : 1)) { failure('too many positional arguments'); return; }
51
52
  let repoRoot: string | null = null;
52
53
  try { repoRoot = detectRepoRoot(); } catch { /* custom active-dir mode */ }
53
- const resolvedActiveDir = activeDir ?? (repoRoot ? path.join(repoRoot, '.agents', 'workspace', 'active') : null);
54
+ const resolvedActiveDir = activeDir ?? (repoRoot ? path.join(resolveProjectTaskWorkspaceRoot(repoRoot), 'active') : null);
54
55
  if (!resolvedActiveDir) { failure('cannot determine active task directory'); return; }
55
56
  const width = shortIdLength ?? (repoRoot ? configuredShortIdLength(repoRoot) : 2);
56
57
  const result = executeShortIdCommand({
@@ -1,6 +1,5 @@
1
1
  import { renderTaskVerification, verifyTaskEvent } from '../task/verification.ts';
2
2
  import { resolveTaskRef } from '../task/resolve-ref.ts';
3
- import { TaskExecutionLockError, withTaskExecutionLock } from '../task/task-execution-lock.ts';
4
3
  import { ensureInternalHandlerRoute, internalHandlerRoute } from './cli-route-inventory.ts';
5
4
 
6
5
  const USAGE = 'Usage: agent-infra-internal task-verify <N | TASK-id> <verification-event> [--artifact <canonical.md>] [--format json|text]\n';
@@ -34,14 +33,7 @@ async function taskVerify(args: string[] = []): Promise<void> {
34
33
  }
35
34
  const resolved = resolveTaskRef(taskRef);
36
35
  if (!resolved.ok) { fail(resolved.message); return; }
37
- let result;
38
- try {
39
- result = await withTaskExecutionLock(resolved.repoRoot, resolved.taskId, `task-verify.${event}`, async () => verifyTaskEvent({ taskRef, event, ...(artifact ? { artifact } : {}) }));
40
- } catch (error) {
41
- if (!(error instanceof TaskExecutionLockError)) throw error;
42
- fail(`${error.code}: ${error.message}`);
43
- return;
44
- }
36
+ const result = await verifyTaskEvent({ taskRef, event, ...(artifact ? { artifact } : {}) });
45
37
  process.stdout.write(format === 'text' ? renderTaskVerification(result) : `${JSON.stringify(result)}\n`);
46
38
  process.exitCode = result.status === 'pass' ? 0 : result.status === 'blocked' ? 2 : 1;
47
39
  }
@@ -1,7 +1,6 @@
1
1
  import { applyWorkflowWarningIntent } from '../task/workflow-warning-intents.ts';
2
2
  import type { WorkflowWarningIntent } from '../task/workflow-warning-intents.ts';
3
3
  import { resolveTaskRef } from '../task/resolve-ref.ts';
4
- import { TaskExecutionLockError, withTaskExecutionLock } from '../task/task-execution-lock.ts';
5
4
  import { ensureInternalHandlerRoute, internalHandlerRoute } from './cli-route-inventory.ts';
6
5
 
7
6
  const USAGE = `Usage: agent-infra-internal task-warning <task-ref> <add|set-status|list> [intent flags] [--dry-run]\n`;
@@ -52,15 +51,7 @@ async function taskWarning(args: string[] = []): Promise<void> {
52
51
  if (missing) { usageFailure(`${kind} requires '${missing}'`); return; }
53
52
  const resolved = resolveTaskRef(taskRef);
54
53
  if (!resolved.ok) { usageFailure(resolved.message); return; }
55
- let result;
56
- try {
57
- result = await withTaskExecutionLock(resolved.repoRoot, resolved.taskId, `task-warning.${kind}`, async () => applyWorkflowWarningIntent(values as WorkflowWarningIntent));
58
- } catch (error) {
59
- if (!(error instanceof TaskExecutionLockError)) throw error;
60
- process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: error.code, message: error.message } })}\n`);
61
- process.exitCode = 1;
62
- return;
63
- }
54
+ const result = await applyWorkflowWarningIntent(values as WorkflowWarningIntent);
64
55
  process.stdout.write(`${JSON.stringify(result)}\n`);
65
56
  if (result.status === 'failed') process.exitCode = 1;
66
57
  }
@@ -0,0 +1,39 @@
1
+ import { detectRepoRoot } from '../task/resolve-ref.ts';
2
+ import { resolveProjectTaskWorkspaceLayout, type ExecutionBoundary } from '../task/workspace-root.ts';
3
+ import { ensureInternalHandlerRoute, internalHandlerRoute } from './cli-route-inventory.ts';
4
+
5
+ const USAGE = `Usage: agent-infra-internal task-workspace resolve [--boundary host|container]\n`;
6
+
7
+ function fail(code: string, message: string): void {
8
+ process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code, message } })}\n`);
9
+ process.exitCode = 1;
10
+ }
11
+
12
+ function taskWorkspace(args: string[] = []): void {
13
+ if (!ensureInternalHandlerRoute('task-workspace', args)) return;
14
+ const operation = args[0] ?? '';
15
+ if (operation === '--help' || operation === '-h') { process.stdout.write(USAGE); return; }
16
+ if (!internalHandlerRoute('task-workspace', 'resolve', operation)) {
17
+ fail('TASK_WORKSPACE_PAYLOAD_INVALID', "operation must be 'resolve'");
18
+ return;
19
+ }
20
+ let boundary: ExecutionBoundary = 'host';
21
+ const options = args.slice(1);
22
+ if (options.length !== 0) {
23
+ if (options.length !== 2 || options[0] !== '--boundary') {
24
+ fail('TASK_WORKSPACE_PAYLOAD_INVALID', 'invalid or duplicate task-workspace option');
25
+ return;
26
+ }
27
+ boundary = options[1] as ExecutionBoundary;
28
+ }
29
+ if (boundary !== 'host' && boundary !== 'container') {
30
+ fail('TASK_WORKSPACE_PAYLOAD_INVALID', 'resolve accepts only --boundary host|container');
31
+ return;
32
+ }
33
+ try {
34
+ const layout = resolveProjectTaskWorkspaceLayout(detectRepoRoot(), boundary);
35
+ process.stdout.write(`${JSON.stringify({ status: 'resolved', changed: false, projectKey: layout.projectKey, boundary: layout.boundary, root: layout.root, error: null })}\n`);
36
+ } catch (error) { fail('TASK_WORKSPACE_OPERATION_FAILED', error instanceof Error ? error.message : String(error)); }
37
+ }
38
+
39
+ export { taskWorkspace };
package/lib/merge.ts CHANGED
@@ -4,6 +4,7 @@ import path from 'node:path';
4
4
  import crypto from 'node:crypto';
5
5
  import { removeDirRecursive } from './remove-dir.ts';
6
6
  import { validateCurrentTaskContract } from './task/current-contract.ts';
7
+ import { resolveProjectTaskWorkspaceRoot } from './task/workspace-root.ts';
7
8
 
8
9
  const TASK_ID_RE = /^TASK-\d{8}-\d{6}$/;
9
10
  const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/;
@@ -988,7 +989,7 @@ function mergeArchiveSection(sourceArchive: string, localArchive: string, report
988
989
  }
989
990
 
990
991
  function printSection(lines: string[], name: MutableSection, counts: MutableCounts): void {
991
- const title = `${SECTION_LABELS[name].padEnd(9, ' ')} (.agents/workspace/${name}/):`;
992
+ const title = `${SECTION_LABELS[name].padEnd(9, ' ')} (task workspace/${name}/):`;
992
993
  lines.push(title);
993
994
 
994
995
  const allEntries: Array<[MutableAction, string]> = [
@@ -1013,7 +1014,7 @@ function printSection(lines: string[], name: MutableSection, counts: MutableCoun
1013
1014
  }
1014
1015
 
1015
1016
  function printArchiveSection(lines: string[], counts: ArchiveCounts): void {
1016
- const title = `${SECTION_LABELS.archive.padEnd(9, ' ')} (.agents/workspace/archive/):`;
1017
+ const title = `${SECTION_LABELS.archive.padEnd(9, ' ')} (task workspace/archive/):`;
1017
1018
  lines.push(title);
1018
1019
 
1019
1020
  if (counts.copied.length === 0 && counts.skipped.length === 0) {
@@ -1105,7 +1106,7 @@ async function cmdMerge(args: string[]): Promise<void> {
1105
1106
  }
1106
1107
 
1107
1108
  const repoRoot = process.cwd();
1108
- const workspaceDir = path.join(repoRoot, '.agents', 'workspace');
1109
+ const workspaceDir = resolveProjectTaskWorkspaceRoot(repoRoot);
1109
1110
  const archiveDir = path.join(workspaceDir, 'archive');
1110
1111
  const backupStamp = formatBackupTimestamp(new Date());
1111
1112
  const projectKey = resolveRecoveryProjectKey(repoRoot);
@@ -429,12 +429,15 @@ function runMechanicalChangeReport(repoRoot: string, baseSha: string, headSha: s
429
429
  return validated.value;
430
430
  }
431
431
 
432
+ const DOCUMENTATION_CATEGORY = { label: '文档', prefixes: ['docs/'] } as const;
433
+
432
434
  const CHANGE_CATEGORIES = [
433
435
  { label: '运行时代码', prefixes: ['bin/', 'lib/', 'src/', 'scripts/'] },
434
436
  { label: '测试与校验', prefixes: ['test/', 'tests/'] },
435
437
  { label: '模板与生成内容', prefixes: ['templates/'] },
436
438
  { label: '技能与协作配置', prefixes: ['.agents/'] },
437
- { label: '文档与其他', prefixes: [] }
439
+ DOCUMENTATION_CATEGORY,
440
+ { label: '其他', prefixes: [] }
438
441
  ] as const;
439
442
 
440
443
  function emptyChangeTotals(): ChangeTotals {
@@ -443,7 +446,10 @@ function emptyChangeTotals(): ChangeTotals {
443
446
 
444
447
  function categoryForChangeFile(file: ChangeFile): (typeof CHANGE_CATEGORIES)[number] {
445
448
  const filePath = file.newPath || file.oldPath || '';
446
- return CHANGE_CATEGORIES.find((category) => category.prefixes.some((prefix) => filePath.startsWith(prefix))) || CHANGE_CATEGORIES[CHANGE_CATEGORIES.length - 1]!;
449
+ const category = CHANGE_CATEGORIES.find((entry) => entry.prefixes.some((prefix) => prefix && filePath.startsWith(prefix)));
450
+ if (category) return category;
451
+ if (/\.(?:md|mdx|markdown|rst)$/i.test(filePath)) return DOCUMENTATION_CATEGORY;
452
+ return CHANGE_CATEGORIES[CHANGE_CATEGORIES.length - 1]!;
447
453
  }
448
454
 
449
455
  function summarizeChangeCategories(files: ChangeFile[]): Array<{ label: string; totals: ChangeTotals }> {
@@ -553,7 +559,7 @@ function renderEvidence(evidence: Evidence): string {
553
559
  function renderPrechecks(checks: Precheck[]): string[] {
554
560
  return checks.flatMap((check) => [
555
561
  `- **${PRECHECK_LABELS[check.id]}(${check.id})**:${check.verdict === 'pass' ? '通过' : '需复核'}。${escapeReportText(check.rationale)}`,
556
- ...check.evidence.slice(0, 1).map(renderEvidence)
562
+ ...check.evidence.map(renderEvidence)
557
563
  ]);
558
564
  }
559
565
 
@@ -5,7 +5,6 @@ import { createHash } from 'node:crypto';
5
5
  import { enumerateArtifacts } from '../task/artifacts.ts';
6
6
  import { parseTypedTaskFrontmatter } from '../task/frontmatter.ts';
7
7
  import { resolveTaskRef } from '../task/resolve-ref.ts';
8
- import { TaskExecutionLockError, withTaskExecutionLock } from '../task/task-execution-lock.ts';
9
8
  import { readPrDeliveryFact } from '../task/pr-delivery-fact.ts';
10
9
  import type { PrDeliveryFact } from '../task/pr-delivery-fact.ts';
11
10
  import { resolvePlatformProviderContext } from './context.ts';
@@ -433,7 +432,7 @@ async function reportWrite(taskRef: string, options: ReportWriteOptions): Promis
433
432
  if (!loaded.ok || !context.platform.repository || !['no-op', 'degraded'].includes(context.status)) return { ...context, report: null };
434
433
  let knownPrNumber: number | null = null;
435
434
  try {
436
- return await withTaskExecutionLock(resolved.repoRoot, resolved.taskId, options.agent, async () => {
435
+ return await (async () => {
437
436
  const { taskContent, boundFact, factError } = readBoundTaskSnapshot(resolved.taskMdPath);
438
437
  if (factError) return {
439
438
  ...basePlatformResult('failed', context, resolved.taskId, null, { code: factError.code, message: factError.message, retryable: false }),
@@ -502,18 +501,15 @@ async function reportWrite(taskRef: string, options: ReportWriteOptions): Promis
502
501
  operations: [{ name: 'change-report:write', status: 'applied', reasonCode: null }],
503
502
  report: { ...reportInfo, status: 'written' }
504
503
  };
505
- });
504
+ })();
506
505
  } catch (error) {
507
- const lockError = error instanceof TaskExecutionLockError
508
- ? { code: error.code, message: error.message, retryable: error.code === 'ORCHESTRATION_LOCK_BUSY' }
509
- : { code: 'PR_CHANGE_REPORT_FAILED', message: error instanceof Error ? error.message : String(error), retryable: false };
510
- return { ...basePlatformResult(lockError.retryable ? 'blocked' : 'failed', context, resolved.taskId, knownPrNumber, lockError), report: null };
506
+ return { ...basePlatformResult('failed', context, resolved.taskId, knownPrNumber, { code: 'PR_CHANGE_REPORT_FAILED', message: error instanceof Error ? error.message : String(error), retryable: false }), report: null };
511
507
  }
512
508
  }
513
509
 
514
510
  async function syncPullRequestSummary(
515
511
  taskRef: string,
516
- options: { agent: string; body: string; changeReportFile?: string; cwd?: string; client?: PlatformClient; dryRun?: boolean; strict?: boolean; primaryResult: PullRequestPrimaryResult; runtimeVersion?: string; manualValidation?: ManualValidationSummaryOptions; lockAlreadyHeld?: boolean; skipQueue?: boolean }
512
+ options: { agent: string; body: string; changeReportFile?: string; cwd?: string; client?: PlatformClient; dryRun?: boolean; strict?: boolean; primaryResult: PullRequestPrimaryResult; runtimeVersion?: string; manualValidation?: ManualValidationSummaryOptions; skipQueue?: boolean }
517
513
  ): Promise<PullRequestSummaryResult> {
518
514
  const warningResult = warningResultForPrimary(options.primaryResult);
519
515
  let knownPrNumber: number | null = null;
@@ -561,7 +557,7 @@ async function syncPullRequestSummary(
561
557
  const loaded = await resolvePlatformProviderContext({ cwd: resolved.repoRoot, client: options.client });
562
558
  const context = loaded.ok ? loaded.value.context : loaded.context;
563
559
  if (!loaded.ok || !context.platform.repository || !['no-op', 'degraded'].includes(context.status)) {
564
- // This diagnostic-only read preserves the known PR target when context resolution fails before the execution lock.
560
+ // Preserve the known PR target for diagnostics when provider context resolution fails.
565
561
  knownPrNumber = boundPrNumberForWarning(resolved.taskMdPath);
566
562
  return preserveFailure(context);
567
563
  }
@@ -780,14 +776,9 @@ async function syncPullRequestSummary(
780
776
  persistenceFailure: (error) => fail('failed', context, error, prNumber)
781
777
  });
782
778
  };
783
- return await (options.lockAlreadyHeld
784
- ? execute()
785
- : withTaskExecutionLock(resolved.repoRoot, resolved.taskId, options.agent, execute));
779
+ return await execute();
786
780
  } catch (error) {
787
- const lockError = error instanceof TaskExecutionLockError
788
- ? { code: error.code, message: error.message, retryable: error.code === 'ORCHESTRATION_LOCK_BUSY' }
789
- : { code: 'PR_SUMMARY_FAILED', message: error instanceof Error ? error.message : String(error), retryable: false };
790
- return fail(lockError.retryable ? 'blocked' : 'failed', context, lockError);
781
+ return fail('failed', context, { code: 'PR_SUMMARY_FAILED', message: error instanceof Error ? error.message : String(error), retryable: false });
791
782
  }
792
783
  }
793
784
 
@@ -21,7 +21,6 @@ import {
21
21
  import { platformResult } from './types.ts';
22
22
  import type { PlatformOperation, PlatformResult } from './types.ts';
23
23
  import { inspectCompletionArtifacts } from '../task/finalization-artifacts.ts';
24
- import { TaskExecutionLockError, withTaskExecutionLock } from '../task/task-execution-lock.ts';
25
24
  import { resolveDeliveryTarget } from '../task/delivery-target.ts';
26
25
  import { mergeOperationWarnings, type OperationWarning } from '../task/operation-outcome.ts';
27
26
  import {
@@ -1174,20 +1173,8 @@ async function createPlatformPullRequestImpl(taskRef: string, options: CreateOpt
1174
1173
  }), PRECONDITION_NOT_CREATED);
1175
1174
  }
1176
1175
  try {
1177
- return withTaskExecutionLock(
1178
- base.resolved.repoRoot,
1179
- base.resolved.taskId,
1180
- 'platform-pr.create',
1181
- () => createExternalPullRequest(taskRef, options, base)
1182
- );
1176
+ return await createExternalPullRequest(taskRef, options, base);
1183
1177
  } catch (error) {
1184
- if (error instanceof TaskExecutionLockError) {
1185
- return withCreation(result('blocked', base.resolved.taskId, base.issueNumber, null, {
1186
- platform: base.context.platform,
1187
- capabilities: base.context.capabilities,
1188
- error: { code: error.code, message: error.message, retryable: true }
1189
- }), PRECONDITION_NOT_CREATED);
1190
- }
1191
1178
  return withCreation(result('failed', base.resolved.taskId, base.issueNumber, null, {
1192
1179
  platform: base.context.platform,
1193
1180
  capabilities: base.context.capabilities,
@@ -1602,7 +1589,7 @@ function readProjectPrFlow(repoRoot: string): 'required' | 'disabled' | undefine
1602
1589
  }
1603
1590
  }
1604
1591
 
1605
- function skipPlatformPullRequestFactUnlocked(
1592
+ function applySkippedPullRequestFact(
1606
1593
  resolved: Extract<ReturnType<typeof resolveTaskRef>, { ok: true }>,
1607
1594
  options: SkipFactOptions
1608
1595
  ): PullRequestResult {
@@ -1674,16 +1661,8 @@ async function skipPlatformPullRequestFact(taskRef: string, options: SkipFactOpt
1674
1661
  error: { code: 'TASK_STATE_INVALID', message: 'platform-pr skip requires an active task', retryable: false }
1675
1662
  });
1676
1663
  try {
1677
- return withTaskExecutionLock(
1678
- resolved.repoRoot,
1679
- resolved.taskId,
1680
- 'platform-pr.skip',
1681
- () => skipPlatformPullRequestFactUnlocked(resolved, options)
1682
- );
1664
+ return await applySkippedPullRequestFact(resolved, options);
1683
1665
  } catch (error) {
1684
- if (error instanceof TaskExecutionLockError) return result('blocked', resolved.taskId, null, null, {
1685
- error: { code: error.code, message: error.message, retryable: true }
1686
- });
1687
1666
  return result('failed', resolved.taskId, null, null, {
1688
1667
  error: { code: 'PR_SKIP_FAILED', message: error instanceof Error ? error.message : String(error), retryable: false }
1689
1668
  });
@@ -5,6 +5,7 @@ import { parseTypedTaskFrontmatter } from '../task/frontmatter.ts';
5
5
  import { readPrDeliveryFact } from '../task/pr-delivery-fact.ts';
6
6
  import { resourceIdentityEquals, resourceIdentityNumber } from '../platform/resource-identity.ts';
7
7
  import type { ResourceIdentity } from '../platform/resource-identity.ts';
8
+ import { resolveProjectTaskWorkspaceRoot } from '../task/workspace-root.ts';
8
9
  import { taskIssueIdentity } from '../platform/task-identities.ts';
9
10
 
10
11
  // ---------------------------------------------------------------------------
@@ -61,7 +62,7 @@ export function extractClosingIssueNumbers(body: string): number[] {
61
62
  }
62
63
 
63
64
  /**
64
- * Scan `.agents/workspace/active/{task-id}/task.md` files for tasks bound to the
65
+ * Scan active project task workspace task.md files for tasks bound to the
65
66
  * PR number or to one of the PR's closing issues. A task is emitted at most once;
66
67
  * a verified fact identity hit takes priority over the issue-identity reverse lookup.
67
68
  */
@@ -71,7 +72,7 @@ export function collectHostCandidates(input: {
71
72
  closingIssues: number[];
72
73
  workspaceRoot: string;
73
74
  }): HostCandidate[] {
74
- const activeRoot = path.join(input.workspaceRoot, '.agents', 'workspace', 'active');
75
+ const activeRoot = path.join(resolveProjectTaskWorkspaceRoot(input.workspaceRoot), 'active');
75
76
  const candidates: HostCandidate[] = [];
76
77
  let entries: fs.Dirent[];
77
78
  try {
@@ -1,7 +1,9 @@
1
1
  import fs from 'node:fs';
2
+ import os from 'node:os';
2
3
  import path from 'node:path';
3
4
 
4
5
  import { detectRepoRoot } from '../task/resolve-ref.ts';
6
+ import { resolveProjectTaskWorkspaceRoot } from '../task/workspace-root.ts';
5
7
  import { acquireGitHubCheckpoint, commitGitHubCheckpoint, readGitHubCheckpoint, releaseGitHubCheckpoint } from './checkpoint.ts';
6
8
  import { materializeJson, materializeSnapshot } from './materialize.ts';
7
9
  import { normalizeResources } from './normalize.ts';
@@ -52,16 +54,26 @@ function parseArgs(args: string[], valueOptions: Set<string>, booleanOptions: Se
52
54
  }
53
55
 
54
56
  function dataRoot(repoRoot: string, requested?: string): string {
55
- const workspace = path.resolve(repoRoot, '.agents', 'workspace');
56
- const resolved = path.resolve(repoRoot, requested ?? path.join('.agents', 'workspace', 'process-data'));
57
- const relative = path.relative(repoRoot, resolved);
58
- if (!relative || relative.startsWith('..') || path.isAbsolute(relative)) throw new Error('data root must be inside the repository');
57
+ let boundary = repoRoot;
58
+ let input = requested;
59
+ if (input === undefined) {
60
+ const config = JSON.parse(fs.readFileSync(path.join(repoRoot, '.agents', '.airc.json'), 'utf8')) as { project?: unknown };
61
+ if (typeof config.project !== 'string' || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(config.project)) {
62
+ throw new Error('.agents/.airc.json must contain a valid project name in its project field');
63
+ }
64
+ boundary = path.resolve(os.homedir());
65
+ input = path.join(boundary, '.agent-infra', 'data-store', config.project);
66
+ }
67
+ const resolved = path.resolve(repoRoot, input);
68
+ const workspace = resolveProjectTaskWorkspaceRoot(repoRoot);
59
69
  const taskRoots = ['active', 'blocked', 'completed', 'archive'].map((state) => path.join(workspace, state));
60
70
  if (resolved === workspace || taskRoots.some((taskRoot) => resolved === taskRoot || resolved.startsWith(`${taskRoot}${path.sep}`))) {
61
71
  throw new Error('data root cannot be the workspace root or a task directory');
62
72
  }
73
+ const relative = path.relative(repoRoot, resolved);
74
+ if (requested !== undefined && (!relative || relative.startsWith('..') || path.isAbsolute(relative))) throw new Error('data root must be inside the repository');
63
75
  let current = resolved;
64
- while (current !== repoRoot) {
76
+ while (current !== boundary) {
65
77
  if (fs.existsSync(current) && fs.lstatSync(current).isSymbolicLink()) throw new Error('data root cannot traverse a symlink');
66
78
  current = path.dirname(current);
67
79
  }
@@ -3,6 +3,7 @@ import path from 'node:path';
3
3
 
4
4
  import { createGitHubClient } from '../platform/github-client.ts';
5
5
  import { enumerateAllTaskDirs } from '../task/resolve-ref.ts';
6
+ import { resolveProjectTaskWorkspaceRoot } from '../task/workspace-root.ts';
6
7
  import { captureText, inspectPrivacy, redactExcerpt } from './privacy.ts';
7
8
  import { canonicalJsonBytes, sha256 } from './store.ts';
8
9
 
@@ -157,6 +158,11 @@ function fetchRestCollection<T>(options: FetchRestCollectionOptions<T>): Process
157
158
  }
158
159
 
159
160
  function relativeIdentity(repoRoot: string, filePath: string): string {
161
+ const workspaceRoot = resolveProjectTaskWorkspaceRoot(repoRoot);
162
+ const taskRelative = path.relative(workspaceRoot, filePath);
163
+ if (taskRelative && !taskRelative.startsWith('..') && !path.isAbsolute(taskRelative)) {
164
+ return path.posix.join('task-workspace', taskRelative.split(path.sep).join('/'));
165
+ }
160
166
  const relative = path.relative(repoRoot, filePath);
161
167
  if (!relative || relative.startsWith('..') || path.isAbsolute(relative)) throw new Error(`path escapes repository: ${filePath}`);
162
168
  return relative.split(path.sep).join('/');
package/lib/run/index.ts CHANGED
@@ -4,6 +4,7 @@ import { loadServerConfig } from '../server/config.ts';
4
4
  import { resolveSandboxTarget, type SandboxWorkspaceIdentity } from '../sandbox/workspace-identity.ts';
5
5
  import { createRunId, runInSandbox, type SandboxRunMetadata } from '../sandbox/capture.ts';
6
6
  import { loadShortIdByTaskId, normalizeShortIdInput } from '../task/short-id.ts';
7
+ import { resolveProjectTaskWorkspaceRoot } from '../task/workspace-root.ts';
7
8
  import { buildTuiCommand, renderPrompt, selectTui } from './tui.ts';
8
9
  import { getSkillRunSpec } from './skills.ts';
9
10
  import { runHostCommand, type RunProcessResult } from './host.ts';
@@ -157,7 +158,7 @@ function readShortIdLength(repoRoot: string): number {
157
158
  }
158
159
 
159
160
  function readActiveShortIdRegistry(repoRoot: string): Record<string, string> {
160
- const registryPath = path.join(repoRoot, '.agents', 'workspace', 'active', '.short-ids.json');
161
+ const registryPath = path.join(resolveProjectTaskWorkspaceRoot(repoRoot), 'active', '.short-ids.json');
161
162
  try {
162
163
  const data = JSON.parse(fs.readFileSync(registryPath, 'utf8'));
163
164
  return data && typeof data === 'object' && data.ids && typeof data.ids === 'object' ? data.ids : {};
@@ -186,7 +187,7 @@ function resolveActiveTaskIdentity(taskRef: string, repoRoot: string): ActiveTas
186
187
  }
187
188
 
188
189
  if (!taskId) return null;
189
- const taskDir = path.join(repoRoot, '.agents', 'workspace', 'active', taskId);
190
+ const taskDir = path.join(resolveProjectTaskWorkspaceRoot(repoRoot), 'active', taskId);
190
191
  if (!fs.existsSync(path.join(taskDir, 'task.md'))) return null;
191
192
  return { taskId, taskDir, taskRef: resolvedTaskRef };
192
193
  }
@@ -1454,7 +1454,7 @@ export async function create(
1454
1454
  const completedTaskSource = target.workspace.mode === 'task-bound'
1455
1455
  ? prepareSandboxCompletedTaskMountSource(effectiveConfig.repoRoot, target.workspace.taskId)
1456
1456
  : null;
1457
- prepareSandboxWorkspaceMountTargets(worktree, target.workspace.mode === 'task-bound' ? target.workspace.taskId : undefined);
1457
+ prepareSandboxWorkspaceMountTargets(effectiveConfig.repoRoot);
1458
1458
  if (previousCutoverSnapshot) {
1459
1459
  assertSandboxControlCutoverSnapshot(previousCutoverSnapshot);
1460
1460
  if (previousManifest) {
@@ -1603,6 +1603,10 @@ export async function create(
1603
1603
  : []),
1604
1604
  '--label',
1605
1605
  `${sandboxRuntimeCapabilityLabel(effectiveConfig)}=${capabilityPlan.runtimeSignature}`,
1606
+ // Every task sandbox runs without seccomp and AppArmor filtering;
1607
+ // capabilities and namespace isolation remain unchanged.
1608
+ '--security-opt', 'seccomp=unconfined',
1609
+ '--security-opt', 'apparmor=unconfined',
1606
1610
  ...coreVolumes,
1607
1611
  ...buildClipboardVolumeArgs(engine, effectiveConfig.home),
1608
1612
  ...dotfilesMount,
@@ -31,7 +31,7 @@ import {
31
31
  const USAGE = `Usage: ai sandbox exec [--recreate] <branch | TASK-id | N> [cmd...]
32
32
 
33
33
  N references an active task short id from
34
- .agents/workspace/active/.short-ids.json. They resolve only via that
34
+ the read-only task-workspace mount at /task-workspace/active/.short-ids.json. They resolve only via that
35
35
  registry — they do not reference a container's row position in
36
36
  'ai sandbox ls' output. --recreate rebuilds the matched container while
37
37
  preserving its task or branch identity. The same token after the target is