@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
@@ -1,320 +0,0 @@
1
- import fs from 'node:fs';
2
- import os from 'node:os';
3
- import path from 'node:path';
4
- import { createHash, randomUUID } from 'node:crypto';
5
-
6
- import { getProcessStartTime, processIdentityMatches } from '../server/process-state.ts';
7
- import type { ProcessIdentity } from '../server/process-state.ts';
8
-
9
- type TaskExecutionLockErrorCode =
10
- | 'ORCHESTRATION_LOCK_BUSY'
11
- | 'ORCHESTRATION_LOCK_UNSUPPORTED'
12
- | 'ORCHESTRATION_LOCK_FAILED';
13
-
14
- type TaskExecutionLockOwner = Readonly<{
15
- version: 2;
16
- pid: number;
17
- startTime: number;
18
- token: string;
19
- owner: string;
20
- canonicalRepoRoot: string;
21
- taskId: string;
22
- acquiredAt: string;
23
- }>;
24
-
25
- type LinkOperation = 'acquire' | 'quarantine';
26
- type LinkDisposition = 'exists' | 'missing' | TaskExecutionLockError;
27
-
28
- type TaskExecutionLockOptions = Readonly<{
29
- lockRoot?: string;
30
- identityScope?: string;
31
- token?: () => string;
32
- now?: () => string;
33
- getStartTime?: (pid: number) => number | null;
34
- identityMatches?: (identity: ProcessIdentity) => boolean;
35
- linkSync?: (existingPath: string, newPath: string) => void;
36
- }>;
37
-
38
- class TaskExecutionLockError extends Error {
39
- readonly code: TaskExecutionLockErrorCode;
40
- readonly detail: Readonly<{ operation: string; errno: string | null; key: string }>;
41
-
42
- constructor(
43
- code: TaskExecutionLockErrorCode,
44
- message: string,
45
- detail: Readonly<{ operation: string; errno: string | null; key: string }>
46
- ) {
47
- super(message);
48
- this.name = 'TaskExecutionLockError';
49
- this.code = code;
50
- this.detail = detail;
51
- }
52
- }
53
-
54
- function errno(error: unknown): string | null {
55
- const value = (error as NodeJS.ErrnoException | null)?.code;
56
- return typeof value === 'string' ? value : null;
57
- }
58
-
59
- function mapLinkError(operation: LinkOperation, error: unknown, key: string): LinkDisposition {
60
- const code = errno(error);
61
- if (code === 'EEXIST') return 'exists';
62
- if (operation === 'quarantine' && code === 'ENOENT') return 'missing';
63
- const detail = { operation, errno: code, key };
64
- if (['EPERM', 'EACCES', 'ENOSYS', 'ENOTSUP', 'EOPNOTSUPP', 'EXDEV'].includes(code ?? '')) {
65
- return new TaskExecutionLockError(
66
- 'ORCHESTRATION_LOCK_UNSUPPORTED',
67
- `lifecycle lock hard-link operation '${operation}' is unsupported`,
68
- detail
69
- );
70
- }
71
- return new TaskExecutionLockError(
72
- 'ORCHESTRATION_LOCK_FAILED',
73
- `lifecycle lock hard-link operation '${operation}' failed`,
74
- detail
75
- );
76
- }
77
-
78
- function lockKey(repoRoot: string, taskId: string, identityScope?: string): Readonly<{ canonicalRepoRoot: string; key: string }> {
79
- const canonicalRepoRoot = fs.realpathSync.native(repoRoot);
80
- const identity = identityScope === undefined
81
- ? `${canonicalRepoRoot}\0${taskId}`
82
- : `${identityScope}\0${taskId}`;
83
- const key = createHash('sha256').update(identity).digest('hex');
84
- return { canonicalRepoRoot, key };
85
- }
86
-
87
- function parseOwner(raw: string, key: string): TaskExecutionLockOwner {
88
- try {
89
- const value = JSON.parse(raw) as Partial<TaskExecutionLockOwner> | null;
90
- if (
91
- value !== null
92
- && value.version === 2
93
- && Number.isSafeInteger(value.pid)
94
- && (value.pid ?? 0) > 0
95
- && typeof value.startTime === 'number'
96
- && Number.isSafeInteger(value.startTime)
97
- && value.startTime >= 0
98
- && typeof value.token === 'string'
99
- && value.token.length > 0
100
- && typeof value.owner === 'string'
101
- && typeof value.canonicalRepoRoot === 'string'
102
- && typeof value.taskId === 'string'
103
- && typeof value.acquiredAt === 'string'
104
- ) {
105
- return value as TaskExecutionLockOwner;
106
- }
107
- } catch {
108
- // Fall through to the stable lock failure below.
109
- }
110
- throw new TaskExecutionLockError(
111
- 'ORCHESTRATION_LOCK_FAILED',
112
- 'lifecycle lock owner record is invalid',
113
- { operation: 'read', errno: null, key }
114
- );
115
- }
116
-
117
- function unlinkIfPresent(file: string): void {
118
- try {
119
- fs.unlinkSync(file);
120
- } catch (error) {
121
- if (errno(error) !== 'ENOENT') throw error;
122
- }
123
- }
124
-
125
- function sameInode(left: fs.Stats, right: fs.Stats): boolean {
126
- return left.dev === right.dev && left.ino === right.ino;
127
- }
128
-
129
- function reclaimStaleLock(
130
- fixed: string,
131
- quarantine: string,
132
- snapshot: string,
133
- key: string,
134
- link: (existingPath: string, newPath: string) => void
135
- ): void {
136
- let linked = false;
137
- try {
138
- link(fixed, quarantine);
139
- linked = true;
140
- } catch (error) {
141
- const disposition = mapLinkError('quarantine', error, key);
142
- if (disposition === 'missing') return;
143
- if (disposition === 'exists') {
144
- try {
145
- const quarantineSnapshot = fs.readFileSync(quarantine, 'utf8');
146
- const fixedStat = fs.statSync(fixed);
147
- const quarantineStat = fs.statSync(quarantine);
148
- if (quarantineSnapshot === snapshot && sameInode(fixedStat, quarantineStat)) {
149
- unlinkIfPresent(quarantine);
150
- }
151
- } catch (readError) {
152
- if (errno(readError) !== 'ENOENT') throw readError;
153
- }
154
- return;
155
- }
156
- if (disposition instanceof TaskExecutionLockError) throw disposition;
157
- }
158
- try {
159
- const fixedSnapshot = fs.readFileSync(fixed, 'utf8');
160
- const fixedStat = fs.statSync(fixed);
161
- const quarantineStat = fs.statSync(quarantine);
162
- if (fixedSnapshot === snapshot && sameInode(fixedStat, quarantineStat)) unlinkIfPresent(fixed);
163
- } catch (error) {
164
- if (errno(error) !== 'ENOENT') throw error;
165
- } finally {
166
- if (linked) unlinkIfPresent(quarantine);
167
- }
168
- }
169
-
170
- function cleanStaleCandidates(
171
- lockRoot: string,
172
- key: string,
173
- identityMatches: (identity: ProcessIdentity) => boolean
174
- ): void {
175
- const prefix = `${key}.candidate.`;
176
- for (const name of fs.readdirSync(lockRoot)) {
177
- if (!name.startsWith(prefix)) continue;
178
- const candidate = path.join(lockRoot, name);
179
- try {
180
- const owner = parseOwner(fs.readFileSync(candidate, 'utf8'), key);
181
- if (!identityMatches(owner)) unlinkIfPresent(candidate);
182
- } catch {
183
- // A malformed candidate never owns the fixed lock and is left for manual diagnosis.
184
- }
185
- }
186
- }
187
-
188
- function withTaskExecutionLock<T>(
189
- repoRoot: string,
190
- taskId: string,
191
- ownerName: string,
192
- callback: () => T,
193
- options: TaskExecutionLockOptions = {}
194
- ): T {
195
- let identity: Readonly<{ canonicalRepoRoot: string; key: string }>;
196
- try {
197
- identity = lockKey(repoRoot, taskId, options.identityScope);
198
- } catch (error) {
199
- throw new TaskExecutionLockError(
200
- 'ORCHESTRATION_LOCK_FAILED',
201
- 'failed to canonicalize the repository for lifecycle locking',
202
- { operation: 'canonicalize', errno: errno(error), key: 'unresolved' }
203
- );
204
- }
205
- const { canonicalRepoRoot, key } = identity;
206
- const lockRoot = options.lockRoot ?? path.join(os.tmpdir(), 'agent-infra-lifecycle-locks');
207
- const token = (options.token ?? randomUUID)();
208
- const candidate = path.join(lockRoot, `${key}.candidate.${process.pid}.${token}`);
209
- const fixed = path.join(lockRoot, `${key}.lock`);
210
- const link = options.linkSync ?? fs.linkSync;
211
- const startTime = (options.getStartTime ?? getProcessStartTime)(process.pid);
212
- if (startTime === null) {
213
- throw new TaskExecutionLockError(
214
- 'ORCHESTRATION_LOCK_FAILED',
215
- 'current process start time is unavailable',
216
- { operation: 'identity', errno: null, key }
217
- );
218
- }
219
- const owner: TaskExecutionLockOwner = {
220
- version: 2,
221
- pid: process.pid,
222
- startTime,
223
- token,
224
- owner: ownerName,
225
- canonicalRepoRoot,
226
- taskId,
227
- acquiredAt: (options.now ?? (() => new Date().toISOString()))()
228
- };
229
- const serialized = `${JSON.stringify(owner)}\n`;
230
- let ownsFixed = false;
231
- let deferRelease = false;
232
- const release = (): void => {
233
- try {
234
- unlinkIfPresent(candidate);
235
- } catch {
236
- // Candidate cleanup is best-effort and must not replace the primary result.
237
- }
238
- if (ownsFixed) {
239
- try {
240
- const current = parseOwner(fs.readFileSync(fixed, 'utf8'), key);
241
- if (current.token === token) unlinkIfPresent(fixed);
242
- } catch {
243
- // A missing or replaced fixed lock is not owned by this invocation.
244
- }
245
- }
246
- };
247
- try {
248
- fs.mkdirSync(lockRoot, { recursive: true });
249
- cleanStaleCandidates(lockRoot, key, options.identityMatches ?? processIdentityMatches);
250
- const descriptor = fs.openSync(candidate, 'wx', 0o600);
251
- try {
252
- fs.writeFileSync(descriptor, serialized);
253
- fs.fsyncSync(descriptor);
254
- } finally {
255
- fs.closeSync(descriptor);
256
- }
257
- for (let attempt = 0; attempt < 16; attempt += 1) {
258
- try {
259
- link(candidate, fixed);
260
- ownsFixed = true;
261
- break;
262
- } catch (error) {
263
- const disposition = mapLinkError('acquire', error, key);
264
- if (disposition instanceof TaskExecutionLockError) throw disposition;
265
- let snapshot: string;
266
- let current: TaskExecutionLockOwner;
267
- try {
268
- snapshot = fs.readFileSync(fixed, 'utf8');
269
- current = parseOwner(snapshot, key);
270
- } catch (readError) {
271
- if (errno(readError) === 'ENOENT') continue;
272
- throw readError;
273
- }
274
- if ((options.identityMatches ?? processIdentityMatches)(current)) {
275
- throw new TaskExecutionLockError(
276
- 'ORCHESTRATION_LOCK_BUSY',
277
- 'another lifecycle operation holds the task lock',
278
- { operation: 'acquire', errno: 'EEXIST', key }
279
- );
280
- }
281
- reclaimStaleLock(fixed, `${fixed}.quarantine.${current.token}`, snapshot, key, link);
282
- }
283
- }
284
- if (!ownsFixed) {
285
- throw new TaskExecutionLockError(
286
- 'ORCHESTRATION_LOCK_BUSY',
287
- 'lifecycle task lock could not be acquired after stale recovery',
288
- { operation: 'acquire', errno: 'EEXIST', key }
289
- );
290
- }
291
- unlinkIfPresent(candidate);
292
- const value = callback();
293
- if (value && typeof (value as { then?: unknown }).then === 'function') {
294
- deferRelease = true;
295
- return (value as unknown as Promise<unknown>).finally(release) as T;
296
- }
297
- return value;
298
- } catch (error) {
299
- if (error instanceof TaskExecutionLockError) throw error;
300
- if (error instanceof Error && ['OrchestrationStateError', 'ArtifactRecoveryError'].includes(error.name)) throw error;
301
- throw new TaskExecutionLockError(
302
- 'ORCHESTRATION_LOCK_FAILED',
303
- 'lifecycle task lock operation failed',
304
- { operation: ownsFixed ? 'callback' : 'acquire', errno: errno(error), key }
305
- );
306
- } finally {
307
- if (!deferRelease) release();
308
- }
309
- }
310
-
311
- function withRepositoryMutationLock<T>(
312
- repoRoot: string,
313
- callback: () => T,
314
- options: TaskExecutionLockOptions = {}
315
- ): T {
316
- return withTaskExecutionLock(repoRoot, '__repository_mutation__', 'repository-mutation', callback, options);
317
- }
318
-
319
- export { TaskExecutionLockError, lockKey, mapLinkError, withRepositoryMutationLock, withTaskExecutionLock };
320
- export type { LinkDisposition, LinkOperation, TaskExecutionLockErrorCode, TaskExecutionLockOptions, TaskExecutionLockOwner };