@fitlab-ai/agent-infra 0.11.11 → 0.11.13

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 (154) hide show
  1. package/dist/bin/internal-cli.js +2 -2
  2. package/dist/lib/agent-clients/adapter.js +7 -1
  3. package/dist/lib/agent-clients/adapters/claude-code.js +5 -1
  4. package/dist/lib/agent-clients/adapters/codex-lifecycle/app-server.js +78 -63
  5. package/dist/lib/agent-clients/adapters/codex-lifecycle/binding.js +114 -0
  6. package/dist/lib/agent-clients/adapters/codex-lifecycle/cli.js +447 -0
  7. package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-client.js +130 -0
  8. package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-operation.js +131 -0
  9. package/dist/lib/{sandbox/control → agent-clients/adapters/codex-lifecycle}/controller-registration.js +2 -2
  10. package/dist/lib/agent-clients/adapters/codex-lifecycle/evidence.js +48 -1
  11. package/dist/lib/agent-clients/adapters/codex-lifecycle/recovery.js +124 -62
  12. package/dist/lib/{internal/codex-sandbox-controller.js → agent-clients/adapters/codex-lifecycle/sandbox-controller-cli.js} +3 -3
  13. package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +3 -5
  14. package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +211 -213
  15. package/dist/lib/agent-clients/adapters/codex-orchestration.js +468 -0
  16. package/dist/lib/agent-clients/adapters/codex.js +40 -1
  17. package/dist/lib/agent-clients/adapters/index.js +2 -10
  18. package/dist/lib/agent-clients/tokens.js +3 -2
  19. package/dist/lib/internal/agent-client.js +132 -72
  20. package/dist/lib/internal/cli-route-inventory.js +2 -2
  21. package/dist/lib/internal/sandbox-control.js +1 -81
  22. package/dist/lib/internal/task-finalization.js +26 -19
  23. package/dist/lib/internal/task-lifecycle.js +1 -1
  24. package/dist/lib/internal/task-operation-registry.js +1 -1
  25. package/dist/lib/internal/task-validate.js +1 -1
  26. package/dist/lib/platform/github-client.js +33 -3
  27. package/dist/lib/platform/issues.js +18 -6
  28. package/dist/lib/runtime/agent-runtime.js +6 -29
  29. package/dist/lib/sandbox/commands/control-status.js +1 -0
  30. package/dist/lib/sandbox/commands/create.js +7 -2
  31. package/dist/lib/sandbox/commands/ls.js +4 -5
  32. package/dist/lib/sandbox/commands/rm.js +1 -1
  33. package/dist/lib/sandbox/control/agent-operation.js +34 -0
  34. package/dist/lib/sandbox/control/client.js +14 -201
  35. package/dist/lib/sandbox/control/executor.js +12 -125
  36. package/dist/lib/sandbox/control/lifecycle.js +66 -4
  37. package/dist/lib/sandbox/control/protocol.js +15 -54
  38. package/dist/lib/sandbox/control/server.js +216 -281
  39. package/dist/lib/sandbox/control/task-view.js +0 -13
  40. package/dist/lib/sandbox/mounts.js +9 -1
  41. package/dist/lib/sandbox/recovery.js +31 -10
  42. package/dist/lib/sandbox/removal.js +28 -5
  43. package/dist/lib/sandbox/workspace-identity.js +18 -6
  44. package/dist/lib/sandbox/workspace-view.js +56 -19
  45. package/dist/lib/task/commands/ls.js +3 -3
  46. package/dist/lib/task/control-authority.js +56 -264
  47. package/dist/lib/task/control-recovery.js +2 -3
  48. package/dist/lib/task/create.js +2 -2
  49. package/dist/lib/task/delegation-receipts.js +74 -161
  50. package/dist/lib/task/events.js +88 -44
  51. package/dist/lib/task/finalization.js +35 -6
  52. package/dist/lib/task/lifecycle.js +274 -115
  53. package/dist/lib/task/orchestration.js +201 -194
  54. package/dist/lib/task/runtime-paths.js +10 -0
  55. package/dist/lib/task/short-id.js +37 -27
  56. package/dist/lib/task/verification-engine.js +32 -26
  57. package/dist/lib/task/workspace-snapshot.js +1 -1
  58. package/dist/lifecycle-build-manifest.json +3 -3
  59. package/lib/agent-clients/adapter.ts +39 -0
  60. package/lib/agent-clients/adapters/claude-code.ts +5 -1
  61. package/lib/agent-clients/adapters/codex-lifecycle/app-server.ts +105 -80
  62. package/lib/agent-clients/adapters/codex-lifecycle/binding.ts +147 -0
  63. package/lib/agent-clients/adapters/codex-lifecycle/cli.ts +510 -0
  64. package/lib/agent-clients/adapters/codex-lifecycle/controller-client.ts +158 -0
  65. package/lib/agent-clients/adapters/codex-lifecycle/controller-context.ts +1 -1
  66. package/lib/agent-clients/adapters/codex-lifecycle/controller-operation.ts +151 -0
  67. package/lib/{sandbox/control → agent-clients/adapters/codex-lifecycle}/controller-registration.ts +4 -4
  68. package/lib/agent-clients/adapters/codex-lifecycle/evidence.ts +43 -0
  69. package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +8 -5
  70. package/lib/agent-clients/adapters/codex-lifecycle/recovery.ts +190 -65
  71. package/lib/{internal/codex-sandbox-controller.ts → agent-clients/adapters/codex-lifecycle/sandbox-controller-cli.ts} +2 -2
  72. package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +3 -5
  73. package/lib/agent-clients/adapters/codex-lifecycle/store.ts +270 -224
  74. package/lib/agent-clients/adapters/codex-orchestration.ts +632 -0
  75. package/lib/agent-clients/adapters/codex.ts +44 -1
  76. package/lib/agent-clients/adapters/index.ts +7 -20
  77. package/lib/agent-clients/tokens.ts +4 -3
  78. package/lib/internal/agent-client.ts +142 -73
  79. package/lib/internal/cli-route-inventory.ts +2 -2
  80. package/lib/internal/sandbox-control.ts +0 -97
  81. package/lib/internal/task-finalization.ts +30 -18
  82. package/lib/internal/task-lifecycle.ts +1 -1
  83. package/lib/internal/task-operation-registry.ts +1 -1
  84. package/lib/internal/task-validate.ts +1 -1
  85. package/lib/platform/github-client.ts +34 -3
  86. package/lib/platform/issues.ts +18 -6
  87. package/lib/runtime/agent-runtime.ts +6 -41
  88. package/lib/sandbox/commands/control-status.ts +1 -0
  89. package/lib/sandbox/commands/create.ts +7 -1
  90. package/lib/sandbox/commands/ls.ts +4 -5
  91. package/lib/sandbox/commands/rm.ts +1 -1
  92. package/lib/sandbox/control/agent-operation.ts +49 -0
  93. package/lib/sandbox/control/client.ts +20 -254
  94. package/lib/sandbox/control/executor.ts +13 -136
  95. package/lib/sandbox/control/lifecycle.ts +67 -4
  96. package/lib/sandbox/control/protocol.ts +22 -66
  97. package/lib/sandbox/control/server.ts +21 -95
  98. package/lib/sandbox/control/task-view.ts +0 -19
  99. package/lib/sandbox/mounts.ts +10 -1
  100. package/lib/sandbox/recovery.ts +38 -17
  101. package/lib/sandbox/removal.ts +33 -7
  102. package/lib/sandbox/workspace-identity.ts +16 -6
  103. package/lib/sandbox/workspace-view.ts +50 -18
  104. package/lib/task/commands/ls.ts +3 -3
  105. package/lib/task/control-authority.ts +66 -378
  106. package/lib/task/control-recovery.ts +3 -4
  107. package/lib/task/create.ts +3 -3
  108. package/lib/task/delegation-receipts.ts +87 -231
  109. package/lib/task/events.ts +100 -42
  110. package/lib/task/finalization.ts +37 -7
  111. package/lib/task/lifecycle.ts +257 -109
  112. package/lib/task/orchestration.ts +260 -237
  113. package/lib/task/runtime-paths.ts +12 -0
  114. package/lib/task/short-id.ts +33 -40
  115. package/lib/task/verification-engine.ts +30 -25
  116. package/lib/task/workspace-snapshot.ts +1 -1
  117. package/package.json +1 -1
  118. package/templates/.agents/hooks/lifecycle-delegation.js +1 -25
  119. package/templates/.agents/rules/README.en.md +2 -1
  120. package/templates/.agents/rules/README.zh-CN.md +2 -1
  121. package/templates/.agents/rules/next-step-output.en.md +6 -19
  122. package/templates/.agents/rules/next-step-output.zh-CN.md +6 -19
  123. package/templates/.agents/rules/strategy-pattern.en.md +25 -0
  124. package/templates/.agents/rules/strategy-pattern.zh-CN.md +25 -0
  125. package/templates/.agents/skills/analyze-task/SKILL.en.md +1 -1
  126. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +1 -1
  127. package/templates/.agents/skills/check-task/SKILL.en.md +1 -1
  128. package/templates/.agents/skills/check-task/SKILL.zh-CN.md +1 -1
  129. package/templates/.agents/skills/code-task/SKILL.en.md +1 -1
  130. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +1 -1
  131. package/templates/.agents/skills/code-task/reference/code-rules.en.md +4 -0
  132. package/templates/.agents/skills/code-task/reference/code-rules.zh-CN.md +4 -0
  133. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +0 -1
  134. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +0 -1
  135. package/templates/.agents/skills/complete-task/SKILL.en.md +5 -29
  136. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +5 -26
  137. package/templates/.agents/skills/plan-task/SKILL.en.md +7 -4
  138. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +7 -4
  139. package/templates/.agents/skills/review-analysis/SKILL.en.md +1 -1
  140. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +1 -1
  141. package/templates/.agents/skills/review-code/SKILL.en.md +1 -1
  142. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +1 -1
  143. package/templates/.agents/skills/review-plan/SKILL.en.md +1 -1
  144. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +1 -1
  145. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +1 -1
  146. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +1 -1
  147. package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
  148. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
  149. package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +0 -546
  150. package/dist/lib/internal/codex-lifecycle.js +0 -385
  151. package/dist/lib/task/codex-orchestration.js +0 -206
  152. package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +0 -776
  153. package/lib/internal/codex-lifecycle.ts +0 -390
  154. package/lib/task/codex-orchestration.ts +0 -276
@@ -13,12 +13,13 @@ import { validateCurrentTaskContract } from './current-contract.js';
13
13
  import { isValidAgentInfraVersion } from '../version.js';
14
14
  import { taskIssueIdentity } from '../platform/task-identities.js';
15
15
  import { resourceIdentityEquals } from '../platform/resource-identity.js';
16
+ import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.js';
16
17
  const lifecycleIntentCatalog = [
17
18
  'block', 'activate', 'cancel', 'complete', 'close-codescan', 'close-dependabot', 'restore', 'recover-started'
18
19
  ];
19
20
  const lifecycleFailureCatalog = [
21
+ 'LIFECYCLE_DIRECTORY_TRANSFER_FAILED',
20
22
  'LIFECYCLE_DIRECTORY_RENAME_FAILED',
21
- 'LIFECYCLE_DIRECTORY_COPY_FAILED',
22
23
  'LIFECYCLE_DOCUMENT_INVALID',
23
24
  'LIFECYCLE_FINAL_STATE_INVALID',
24
25
  'LIFECYCLE_IDENTITY_CONFLICT',
@@ -32,6 +33,7 @@ const lifecycleFailureCatalog = [
32
33
  'LIFECYCLE_PAYLOAD_INVALID',
33
34
  'LIFECYCLE_SHORT_ID_FAILED',
34
35
  'LIFECYCLE_SHORT_ID_PRECONDITION',
36
+ 'LIFECYCLE_RUNTIME_MOVE_UNSAFE',
35
37
  'LIFECYCLE_SOURCE_INVALID',
36
38
  'LIFECYCLE_STAGING_IDENTITY_INVALID',
37
39
  'LIFECYCLE_STAGING_INVALID',
@@ -45,6 +47,9 @@ const lifecycleProducerCatalog = lifecycleFailureCatalog.map((code) => ({
45
47
  code
46
48
  }));
47
49
  const STEPS = ['task-written', 'directory-moved', 'registry-committed'];
50
+ function stepsForIntent(intent) {
51
+ return intent === 'complete' ? ['task-written', 'directory-moved'] : STEPS;
52
+ }
48
53
  const DEFAULT_IO = {
49
54
  renameSync: (source, target) => fs.renameSync(source, target),
50
55
  readFileSync: (file) => fs.readFileSync(file, 'utf8'),
@@ -57,7 +62,7 @@ function failed(request, error, extra = {}) {
57
62
  intent: request.intent, sourceState: null, targetState: null, sourcePath: null,
58
63
  targetPath: null, task: { operations: [] }, directory: { effect: 'unchanged', changed: false },
59
64
  shortId: { effect: 'unchanged', shortId: null, changed: false }, timestamp: null,
60
- agentInfraVersion: null, journalPath: null, completedSteps: [], pendingSteps: STEPS,
65
+ agentInfraVersion: null, journalPath: null, completedSteps: [], pendingSteps: stepsForIntent(request.intent),
61
66
  error, ...extra
62
67
  };
63
68
  }
@@ -133,8 +138,105 @@ function transition(request) {
133
138
  return { sources: ['active', 'blocked'], target: 'completed', registry: 'release' };
134
139
  if (request.intent === 'restore')
135
140
  return { sources: ['staging'], target: 'active', registry: 'alloc' };
141
+ if (request.intent === 'complete')
142
+ return { sources: ['active'], target: 'completed', registry: 'none' };
136
143
  return { sources: ['active'], target: 'completed', registry: 'release' };
137
144
  }
145
+ function sameTaskEntry(source, target) {
146
+ const sourceStat = fs.lstatSync(source);
147
+ let targetStat;
148
+ try {
149
+ targetStat = fs.lstatSync(target);
150
+ }
151
+ catch (error) {
152
+ if (error.code === 'ENOENT')
153
+ return false;
154
+ throw error;
155
+ }
156
+ if (sourceStat.isSymbolicLink() || targetStat.isSymbolicLink()) {
157
+ return sourceStat.isSymbolicLink() && targetStat.isSymbolicLink()
158
+ && fs.readlinkSync(source) === fs.readlinkSync(target);
159
+ }
160
+ if (sourceStat.isDirectory() && targetStat.isDirectory()) {
161
+ const sourceNames = fs.readdirSync(source).sort();
162
+ const targetNames = fs.readdirSync(target).sort();
163
+ return sourceNames.join('\0') === targetNames.join('\0')
164
+ && sourceNames.every((name) => sameTaskEntry(path.join(source, name), path.join(target, name)))
165
+ && (sourceStat.mode & 0o777) === (targetStat.mode & 0o777);
166
+ }
167
+ return sourceStat.isFile() && targetStat.isFile()
168
+ && (sourceStat.mode & 0o777) === (targetStat.mode & 0o777)
169
+ && fs.readFileSync(source).equals(fs.readFileSync(target));
170
+ }
171
+ function copyTaskEntry(source, target) {
172
+ const sourceStat = fs.lstatSync(source);
173
+ if (fs.existsSync(target)) {
174
+ const targetStat = fs.lstatSync(target);
175
+ if (sourceStat.isDirectory() && targetStat.isDirectory() && !targetStat.isSymbolicLink()) {
176
+ for (const name of fs.readdirSync(source).sort())
177
+ copyTaskEntry(path.join(source, name), path.join(target, name));
178
+ fs.chmodSync(target, sourceStat.mode & 0o777);
179
+ return;
180
+ }
181
+ if (sameTaskEntry(source, target))
182
+ return;
183
+ throw new Error(`task content conflict at ${path.relative(path.dirname(source), source)}`);
184
+ }
185
+ if (sourceStat.isSymbolicLink()) {
186
+ fs.symlinkSync(fs.readlinkSync(source), target);
187
+ return;
188
+ }
189
+ if (sourceStat.isDirectory()) {
190
+ fs.mkdirSync(target, { mode: sourceStat.mode & 0o777 });
191
+ for (const name of fs.readdirSync(source).sort())
192
+ copyTaskEntry(path.join(source, name), path.join(target, name));
193
+ fs.chmodSync(target, sourceStat.mode & 0o777);
194
+ return;
195
+ }
196
+ if (!sourceStat.isFile())
197
+ throw new Error(`unsupported task entry: ${source}`);
198
+ fs.copyFileSync(source, target, fs.constants.COPYFILE_EXCL);
199
+ fs.chmodSync(target, sourceStat.mode & 0o777);
200
+ }
201
+ function transferTaskContents(sourcePath, targetPath) {
202
+ const sourceRoot = fs.lstatSync(sourcePath);
203
+ if (!sourceRoot.isDirectory() || sourceRoot.isSymbolicLink())
204
+ throw new Error('task source is not a real directory');
205
+ const targetRoot = fs.lstatSync(targetPath);
206
+ if (!targetRoot.isDirectory() || targetRoot.isSymbolicLink())
207
+ throw new Error('completed target is not a real directory');
208
+ for (const name of fs.readdirSync(sourcePath).sort()) {
209
+ copyTaskEntry(path.join(sourcePath, name), path.join(targetPath, name));
210
+ }
211
+ const sourceNames = fs.readdirSync(sourcePath).sort();
212
+ const targetNames = fs.readdirSync(targetPath).sort();
213
+ if (sourceNames.join('\0') !== targetNames.join('\0')
214
+ || !sourceNames.every((name) => sameTaskEntry(path.join(sourcePath, name), path.join(targetPath, name)))) {
215
+ throw new Error('completed target does not match task source after copy');
216
+ }
217
+ }
218
+ function targetContainsTask(targetPath, taskId, request) {
219
+ try {
220
+ const stat = fs.lstatSync(targetPath);
221
+ if (!stat.isDirectory() || stat.isSymbolicLink())
222
+ return false;
223
+ const entries = fs.readdirSync(targetPath);
224
+ if (entries.length === 0)
225
+ return true;
226
+ const taskPath = path.join(targetPath, 'task.md');
227
+ const journalPath = path.join(targetPath, '.task-lifecycle.json');
228
+ if (!fs.existsSync(taskPath) || !fs.existsSync(journalPath))
229
+ return false;
230
+ const frontmatter = parseTypedTaskFrontmatter(fs.readFileSync(taskPath, 'utf8'));
231
+ const journal = parseJournal(fs.readFileSync(journalPath, 'utf8'));
232
+ return frontmatter.id === taskId && frontmatter.status === 'completed'
233
+ && journal.taskId === taskId && journal.intent === request.intent
234
+ && journal.intentDigest === intentIdentity(request, taskId);
235
+ }
236
+ catch {
237
+ return false;
238
+ }
239
+ }
138
240
  function actionAndNote(request, restoredFiles = 0) {
139
241
  if (request.intent === 'block')
140
242
  return { action: 'Block Task', note: request.reason };
@@ -366,7 +468,7 @@ export function inspectTaskLifecycleProgress(repoRoot, taskId, agent) {
366
468
  || journal.intentDigest !== intentIdentity(request, taskId)
367
469
  || journal.sourceState !== 'active' || journal.targetState !== 'completed')
368
470
  return 'unknown';
369
- if (journal.completedSteps.some((step, index) => STEPS[index] !== step))
471
+ if (journal.completedSteps.some((step, index) => stepsForIntent(journal.intent)[index] !== step))
370
472
  return 'unknown';
371
473
  const directoryMoved = journal.completedSteps.includes('directory-moved');
372
474
  const registryCommitted = journal.completedSteps.includes('registry-committed');
@@ -374,7 +476,7 @@ export function inspectTaskLifecycleProgress(repoRoot, taskId, agent) {
374
476
  if (state === 'active' && (directoryMoved || !hasShortId))
375
477
  return 'unknown';
376
478
  if (state === 'completed' && (!journal.completedSteps.includes('task-written')
377
- || !matchingCompletion(content, request) || (registryCommitted && hasShortId)))
479
+ || !matchingCompletion(content, request) || registryCommitted || !hasShortId))
378
480
  return 'unknown';
379
481
  if (frontmatter.status !== state
380
482
  && !(state === 'active' && frontmatter.status === 'completed' && matchingCompletion(content, request))) {
@@ -386,66 +488,7 @@ export function inspectTaskLifecycleProgress(repoRoot, taskId, agent) {
386
488
  return 'unknown';
387
489
  }
388
490
  }
389
- function directoryManifest(root) {
390
- const entries = [];
391
- const visit = (current, relative = '') => {
392
- for (const entry of fs.readdirSync(current, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name))) {
393
- const relativePath = relative ? path.join(relative, entry.name) : entry.name;
394
- const absolute = path.join(current, entry.name);
395
- const stat = fs.lstatSync(absolute);
396
- if (stat.isSymbolicLink())
397
- throw new Error(`symbolic link is not allowed in lifecycle directory: ${relativePath}`);
398
- if (entry.isDirectory()) {
399
- entries.push({ relativePath, kind: 'directory', size: 0, digest: '' });
400
- visit(absolute, relativePath);
401
- }
402
- else if (entry.isFile()) {
403
- const digest = createHash('sha256').update(fs.readFileSync(absolute)).digest('hex');
404
- entries.push({ relativePath, kind: 'file', size: stat.size, digest });
405
- }
406
- else {
407
- throw new Error(`unsupported lifecycle directory entry: ${relativePath}`);
408
- }
409
- }
410
- };
411
- visit(root);
412
- return entries;
413
- }
414
- function copyDirectoryVerified(source, target) {
415
- if (fs.existsSync(target))
416
- throw new Error(`target directory already exists: ${target}`);
417
- const copy = (currentSource, currentTarget) => {
418
- fs.mkdirSync(currentTarget);
419
- for (const entry of fs.readdirSync(currentSource, { withFileTypes: true })) {
420
- const sourcePath = path.join(currentSource, entry.name);
421
- const targetPath = path.join(currentTarget, entry.name);
422
- const stat = fs.lstatSync(sourcePath);
423
- if (stat.isSymbolicLink())
424
- throw new Error(`symbolic link is not allowed in lifecycle directory: ${entry.name}`);
425
- if (entry.isDirectory())
426
- copy(sourcePath, targetPath);
427
- else if (entry.isFile())
428
- fs.copyFileSync(sourcePath, targetPath);
429
- else
430
- throw new Error(`unsupported lifecycle directory entry: ${entry.name}`);
431
- }
432
- };
433
- copy(source, target);
434
- const sourceManifest = directoryManifest(source);
435
- const targetManifest = directoryManifest(target);
436
- if (JSON.stringify(sourceManifest) !== JSON.stringify(targetManifest)) {
437
- throw new Error('copied lifecycle directory failed manifest verification');
438
- }
439
- }
440
- function removeCopiedSource(source, target) {
441
- const sourceManifest = directoryManifest(source);
442
- const targetManifest = directoryManifest(target);
443
- if (JSON.stringify(sourceManifest) !== JSON.stringify(targetManifest)) {
444
- throw new Error('lifecycle source and target diverged before source cleanup');
445
- }
446
- fs.rmSync(source, { recursive: true, force: false });
447
- }
448
- function applyTaskLifecycle(requestInput, options = {}) {
491
+ function applyTaskLifecycleUnlocked(requestInput, options = {}) {
449
492
  if (requestInput.intent === 'recover-started') {
450
493
  return failed(requestInput, { code: 'LIFECYCLE_RECOVERY_REQUIRES_AUTHORITY', message: 'recover-started must be dispatched through task control authority' });
451
494
  }
@@ -469,7 +512,12 @@ function applyTaskLifecycle(requestInput, options = {}) {
469
512
  }
470
513
  const spec = transition(request);
471
514
  const hot = locateHotTaskDirs(repoRoot, taskId);
472
- if (hot.length > 1)
515
+ const activeSource = hot.find((candidate) => candidate.state === 'active');
516
+ const completedTarget = hot.find((candidate) => candidate.state === 'completed');
517
+ const transferInProgress = request.intent === 'complete' && Boolean(activeSource && completedTarget)
518
+ && targetContainsTask(completedTarget.taskDir, taskId, request)
519
+ && fs.existsSync(path.join(activeSource.taskDir, '.task-lifecycle.json'));
520
+ if (hot.length > 1 && !transferInProgress)
473
521
  return failed(request, { code: 'LIFECYCLE_IDENTITY_CONFLICT', message: `task ${taskId} exists in multiple hot states` }, { taskId });
474
522
  let sourceState;
475
523
  let sourcePath;
@@ -482,6 +530,11 @@ function applyTaskLifecycle(requestInput, options = {}) {
482
530
  sourcePath = validation.stagingDir;
483
531
  restoredFiles = validation.fileCount;
484
532
  }
533
+ else if (activeSource && request.intent === 'complete') {
534
+ sourceState = activeSource.state;
535
+ sourcePath = activeSource.taskDir;
536
+ restoredFiles = fs.readdirSync(sourcePath).filter((name) => !name.startsWith('.')).length;
537
+ }
485
538
  else if (hot[0]) {
486
539
  sourceState = hot[0].state;
487
540
  sourcePath = hot[0].taskDir;
@@ -491,6 +544,39 @@ function applyTaskLifecycle(requestInput, options = {}) {
491
544
  return failed(request, { code: 'LIFECYCLE_TASK_NOT_FOUND', message: `task ${taskId} has no lifecycle source` }, { taskId });
492
545
  }
493
546
  const targetPath = path.join(repoRoot, '.agents', 'workspace', spec.target, taskId);
547
+ const requiredSteps = stepsForIntent(request.intent);
548
+ const movingTaskDirectory = sourcePath !== targetPath;
549
+ const assertRuntimeMoveSafe = () => {
550
+ if (!movingTaskDirectory)
551
+ return null;
552
+ const controlManifest = path.join(sourcePath, '.runtime', 'sandbox-control', 'manifest.json');
553
+ const controlLease = path.join(sourcePath, '.runtime', 'sandbox-control', 'lease.json');
554
+ if (request.intent !== 'complete' && (fs.existsSync(controlManifest) || fs.existsSync(controlLease))) {
555
+ return {
556
+ code: 'LIFECYCLE_RUNTIME_MOVE_UNSAFE',
557
+ message: 'task-bound sandbox control state exists; stop and remove the verified sandbox before moving the task directory'
558
+ };
559
+ }
560
+ const orchestrationPath = path.join(sourcePath, '.runtime', 'orchestration.json');
561
+ if (fs.existsSync(orchestrationPath)) {
562
+ try {
563
+ const run = JSON.parse(fs.readFileSync(orchestrationPath, 'utf8'));
564
+ if (run.status === 'running' || run.pendingDelegation !== null && run.pendingDelegation !== undefined) {
565
+ return {
566
+ code: 'LIFECYCLE_RUNTIME_MOVE_UNSAFE',
567
+ message: 'task orchestration has an active or pending delegation; settle it before moving the task directory'
568
+ };
569
+ }
570
+ }
571
+ catch {
572
+ return {
573
+ code: 'LIFECYCLE_RUNTIME_MOVE_UNSAFE',
574
+ message: 'task orchestration state is unreadable; refusing to move the task directory'
575
+ };
576
+ }
577
+ }
578
+ return null;
579
+ };
494
580
  const currentTaskFile = path.join(sourcePath, 'task.md');
495
581
  const existingJournal = fs.existsSync(path.join(sourcePath, '.task-lifecycle.json'));
496
582
  if (sourceState === spec.target && !existingJournal) {
@@ -500,7 +586,7 @@ function applyTaskLifecycle(requestInput, options = {}) {
500
586
  return {
501
587
  ...failed(request, { code: '', message: '' }), status: 'no-op', error: null, taskId,
502
588
  sourceState, targetState: spec.target, sourcePath, targetPath, pendingSteps: [],
503
- completedSteps: STEPS, timestamp: typeof frontmatter.updated_at === 'string' ? frontmatter.updated_at : null,
589
+ completedSteps: requiredSteps, timestamp: typeof frontmatter.updated_at === 'string' ? frontmatter.updated_at : null,
504
590
  agentInfraVersion: typeof frontmatter.agent_infra_version === 'string' ? frontmatter.agent_infra_version : null
505
591
  };
506
592
  }
@@ -509,9 +595,13 @@ function applyTaskLifecycle(requestInput, options = {}) {
509
595
  if (!spec.sources.includes(sourceState) && !existingJournal) {
510
596
  return failed(request, { code: 'LIFECYCLE_SOURCE_INVALID', message: `${request.intent} is not allowed from ${sourceState}` }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath });
511
597
  }
512
- if (sourcePath !== targetPath && fs.existsSync(targetPath) && !existingJournal) {
598
+ if (sourcePath !== targetPath && fs.existsSync(targetPath) && !existingJournal
599
+ && !(request.intent === 'complete' && targetContainsTask(targetPath, taskId, request))) {
513
600
  return failed(request, { code: 'LIFECYCLE_TARGET_CONFLICT', message: `target directory already exists: ${targetPath}` }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath });
514
601
  }
602
+ const moveSafety = assertRuntimeMoveSafe();
603
+ if (moveSafety)
604
+ return failed(request, moveSafety, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath });
515
605
  const content = io.readFileSync(currentTaskFile);
516
606
  let frontmatter;
517
607
  try {
@@ -538,7 +628,7 @@ function applyTaskLifecycle(requestInput, options = {}) {
538
628
  }
539
629
  const registry = JSON.parse(registryRequest.output || '{"version":1,"ids":{}}');
540
630
  const registeredKey = Object.entries(registry.ids).find(([, candidate]) => candidate === taskId)?.[0];
541
- if (spec.registry === 'release' && sourceState === 'active' && !registeredKey) {
631
+ if ((spec.registry === 'release' || request.intent === 'complete') && sourceState === 'active' && !registeredKey) {
542
632
  return failed(request, { code: 'LIFECYCLE_SHORT_ID_PRECONDITION', message: 'active source has no short-id registry entry' }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath });
543
633
  }
544
634
  if (spec.registry === 'alloc' && !registeredKey && Object.keys(registry.ids).length >= 10 ** configuredShortIdLength(repoRoot) - 1) {
@@ -563,7 +653,7 @@ function applyTaskLifecycle(requestInput, options = {}) {
563
653
  if (request.dryRun) {
564
654
  if (!journal.failure)
565
655
  return failed(request, { code: 'LIFECYCLE_JOURNAL_INVALID', message: 'existing lifecycle journal has no durable failure evidence for a read-only probe' }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath: initialJournalPath });
566
- return failed(request, journal.failure, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath: initialJournalPath, completedSteps: journal.completedSteps, pendingSteps: STEPS.filter((step) => !journal.completedSteps.includes(step)) });
656
+ return failed(request, journal.failure, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath: initialJournalPath, completedSteps: journal.completedSteps, pendingSteps: requiredSteps.filter((step) => !journal.completedSteps.includes(step)) });
567
657
  }
568
658
  if (journal.intentDigest !== digest)
569
659
  return failed(request, { code: 'LIFECYCLE_INTENT_CONFLICT', message: 'an in-progress lifecycle journal belongs to a different request' }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath: initialJournalPath });
@@ -614,7 +704,8 @@ function applyTaskLifecycle(requestInput, options = {}) {
614
704
  if (sourceState === spec.target && journal.sourceState !== spec.target)
615
705
  completed.add('directory-moved');
616
706
  const currentMappings = loadShortIdByTaskId(repoRoot);
617
- if ((spec.target === 'active' && currentMappings.has(taskId)) || (spec.target !== 'active' && !currentMappings.has(taskId))) {
707
+ if (requiredSteps.includes('registry-committed')
708
+ && ((spec.target === 'active' && currentMappings.has(taskId)) || (spec.target !== 'active' && !currentMappings.has(taskId)))) {
618
709
  if (completed.has('directory-moved'))
619
710
  completed.add('registry-committed');
620
711
  }
@@ -626,7 +717,7 @@ function applyTaskLifecycle(requestInput, options = {}) {
626
717
  task: { operations: [] }, directory: { effect: 'unchanged', changed: false },
627
718
  shortId: { effect: 'unchanged', shortId: null, changed: false },
628
719
  timestamp: journal.metadata.timestamp, agentInfraVersion: journal.metadata.agentInfraVersion,
629
- journalPath, completedSteps: [...completed], pendingSteps: STEPS.filter((step) => !completed.has(step))
720
+ journalPath, completedSteps: [...completed], pendingSteps: requiredSteps.filter((step) => !completed.has(step))
630
721
  };
631
722
  }
632
723
  if (matchingCompletion(io.readFileSync(path.join(sourcePath, 'task.md')), request, restoredFiles))
@@ -660,48 +751,42 @@ function applyTaskLifecycle(requestInput, options = {}) {
660
751
  task: { operations: taskOperations }, directory: { effect: 'unchanged', changed: false },
661
752
  shortId: { effect: 'unchanged', shortId: null, changed: false },
662
753
  timestamp: journal.metadata.timestamp, agentInfraVersion: journal.metadata.agentInfraVersion,
663
- journalPath, completedSteps: [...completed], pendingSteps: STEPS.filter((step) => !completed.has(step))
754
+ journalPath, completedSteps: [...completed], pendingSteps: requiredSteps.filter((step) => !completed.has(step))
664
755
  };
665
756
  }
666
- if (fs.existsSync(targetPath) && !fs.existsSync(sourcePath))
757
+ if (fs.existsSync(targetPath) && !fs.existsSync(path.join(sourcePath, 'task.md')))
667
758
  completed.add('directory-moved');
668
759
  if (!completed.has('directory-moved')) {
760
+ const moveSafety = assertRuntimeMoveSafe();
761
+ if (moveSafety)
762
+ return failed(request, moveSafety, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath, completedSteps: [...completed], changed: completed.size > 0, timestamp: journal.metadata.timestamp, agentInfraVersion: journal.metadata.agentInfraVersion });
669
763
  fs.mkdirSync(path.dirname(targetPath), { recursive: true });
670
- if (fs.existsSync(targetPath))
671
- return failed(request, { code: 'LIFECYCLE_TARGET_CONFLICT', message: `target directory already exists: ${targetPath}` }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath, task: { operations: taskOperations }, changed: true, completedSteps: [...completed], timestamp: journal.metadata.timestamp, agentInfraVersion: journal.metadata.agentInfraVersion });
672
- try {
673
- (options.directoryRenameSync ?? io.renameSync)(sourcePath, targetPath);
674
- }
675
- catch (error) {
676
- if (error.code === 'EXDEV') {
677
- try {
678
- copyDirectoryVerified(sourcePath, targetPath);
679
- completed.add('directory-moved');
680
- journal.completedSteps = [...completed];
681
- const copiedJournalPath = path.join(targetPath, '.task-lifecycle.json');
682
- writeJournal(initialJournalPath, journal, io);
683
- writeJournal(copiedJournalPath, journal, io);
684
- removeCopiedSource(sourcePath, targetPath);
685
- journalPath = copiedJournalPath;
764
+ if (request.intent === 'complete') {
765
+ try {
766
+ if (!fs.existsSync(targetPath))
767
+ fs.mkdirSync(targetPath, { recursive: false, mode: 0o700 });
768
+ if (!targetContainsTask(targetPath, taskId, request)) {
769
+ throw new Error('completed target has no matching task identity');
686
770
  }
687
- catch (copyError) {
688
- if (fs.existsSync(targetPath))
689
- fs.rmSync(targetPath, { recursive: true, force: false });
690
- const failure = { code: 'LIFECYCLE_DIRECTORY_COPY_FAILED', message: String(copyError) };
691
- rememberJournalFailure(initialJournalPath, journal, io, failure);
692
- return failed(request, failure, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath: initialJournalPath, task: { operations: taskOperations }, changed: true, completedSteps: [...completed], timestamp: journal.metadata.timestamp, agentInfraVersion: journal.metadata.agentInfraVersion });
771
+ transferTaskContents(sourcePath, targetPath);
772
+ for (const name of fs.readdirSync(sourcePath).filter((entry) => entry !== '.task-lifecycle.json' && entry !== 'task.md').sort()) {
773
+ fs.rmSync(path.join(sourcePath, name), { recursive: true, force: false });
693
774
  }
775
+ fs.rmSync(path.join(sourcePath, 'task.md'), { force: false });
776
+ fs.rmSync(path.join(sourcePath, '.task-lifecycle.json'), { force: false });
694
777
  }
695
- else {
696
- const failure = { code: 'LIFECYCLE_DIRECTORY_RENAME_FAILED', message: String(error) };
697
- rememberJournalFailure(journalPath, journal, io, failure);
698
- return failed(request, failure, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath, task: { operations: taskOperations }, changed: true, completedSteps: [...completed], timestamp: journal.metadata.timestamp, agentInfraVersion: journal.metadata.agentInfraVersion });
778
+ catch (error) {
779
+ const failure = { code: 'LIFECYCLE_DIRECTORY_TRANSFER_FAILED', message: String(error) };
780
+ const sourceJournalPath = path.join(sourcePath, '.task-lifecycle.json');
781
+ rememberJournalFailure(sourceJournalPath, journal, io, failure);
782
+ return failed(request, failure, {
783
+ taskId, sourceState, targetState: spec.target, sourcePath, targetPath,
784
+ journalPath: sourceJournalPath, task: { operations: taskOperations },
785
+ changed: true, completedSteps: [...completed].filter((step) => step !== 'directory-moved'),
786
+ pendingSteps: requiredSteps.filter((step) => !completed.has(step) || step === 'directory-moved'),
787
+ timestamp: journal.metadata.timestamp, agentInfraVersion: journal.metadata.agentInfraVersion
788
+ });
699
789
  }
700
- }
701
- if (completed.has('directory-moved') && !fs.existsSync(sourcePath)) {
702
- journalPath = path.join(targetPath, '.task-lifecycle.json');
703
- }
704
- else if (!completed.has('directory-moved')) {
705
790
  completed.add('directory-moved');
706
791
  journal.completedSteps = [...completed];
707
792
  journalPath = path.join(targetPath, '.task-lifecycle.json');
@@ -709,23 +794,58 @@ function applyTaskLifecycle(requestInput, options = {}) {
709
794
  writeJournal(journalPath, journal, io);
710
795
  }
711
796
  catch (error) {
712
- return failed(request, { code: 'LIFECYCLE_JOURNAL_WRITE_FAILED', message: String(error) }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath, task: { operations: taskOperations }, directory: { effect: 'move', changed: true }, changed: true, completedSteps: [...completed], timestamp: journal.metadata.timestamp, agentInfraVersion: journal.metadata.agentInfraVersion });
797
+ return failed(request, { code: 'LIFECYCLE_JOURNAL_WRITE_FAILED', message: String(error) }, {
798
+ taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath,
799
+ task: { operations: taskOperations }, directory: { effect: 'move', changed: true },
800
+ changed: true, completedSteps: [...completed].filter((step) => step !== 'directory-moved'),
801
+ pendingSteps: requiredSteps.filter((step) => step !== 'task-written'),
802
+ timestamp: journal.metadata.timestamp, agentInfraVersion: journal.metadata.agentInfraVersion
803
+ });
804
+ }
805
+ }
806
+ else {
807
+ if (fs.existsSync(targetPath))
808
+ return failed(request, { code: 'LIFECYCLE_TARGET_CONFLICT', message: `target directory already exists: ${targetPath}` }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath, task: { operations: taskOperations }, changed: true, completedSteps: [...completed], timestamp: journal.metadata.timestamp, agentInfraVersion: journal.metadata.agentInfraVersion });
809
+ try {
810
+ (options.directoryRenameSync ?? io.renameSync)(sourcePath, targetPath);
811
+ }
812
+ catch (error) {
813
+ const failure = { code: 'LIFECYCLE_DIRECTORY_RENAME_FAILED', message: String(error) };
814
+ rememberJournalFailure(journalPath, journal, io, failure);
815
+ return failed(request, failure, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath, task: { operations: taskOperations }, changed: true, completedSteps: [...completed], timestamp: journal.metadata.timestamp, agentInfraVersion: journal.metadata.agentInfraVersion });
816
+ }
817
+ if (completed.has('directory-moved') && !fs.existsSync(sourcePath)) {
818
+ journalPath = path.join(targetPath, '.task-lifecycle.json');
819
+ }
820
+ else if (!completed.has('directory-moved')) {
821
+ completed.add('directory-moved');
822
+ journal.completedSteps = [...completed];
823
+ journalPath = path.join(targetPath, '.task-lifecycle.json');
824
+ try {
825
+ writeJournal(journalPath, journal, io);
826
+ }
827
+ catch (error) {
828
+ return failed(request, { code: 'LIFECYCLE_JOURNAL_WRITE_FAILED', message: String(error) }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath, task: { operations: taskOperations }, directory: { effect: 'move', changed: true }, changed: true, completedSteps: [...completed], timestamp: journal.metadata.timestamp, agentInfraVersion: journal.metadata.agentInfraVersion });
829
+ }
713
830
  }
714
831
  }
715
832
  }
716
- if (completed.has('directory-moved') && sourcePath !== targetPath && fs.existsSync(sourcePath) && fs.existsSync(targetPath)) {
833
+ const activeTaskPath = path.join(repoRoot, '.agents', 'workspace', 'active', taskId);
834
+ if (request.intent === 'complete' && fs.existsSync(activeTaskPath)) {
717
835
  try {
718
- removeCopiedSource(sourcePath, targetPath);
719
- journalPath = path.join(targetPath, '.task-lifecycle.json');
836
+ fs.rmdirSync(activeTaskPath);
720
837
  }
721
838
  catch (error) {
722
- const failure = { code: 'LIFECYCLE_DIRECTORY_COPY_FAILED', message: String(error) };
723
- rememberJournalFailure(journalPath, journal, io, failure);
724
- return failed(request, failure, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath, task: { operations: taskOperations }, changed: true, completedSteps: [...completed], timestamp: journal.metadata.timestamp, agentInfraVersion: journal.metadata.agentInfraVersion });
839
+ return failed(request, { code: 'LIFECYCLE_SOURCE_DIRECTORY_CLEANUP_FAILED', message: String(error) }, {
840
+ taskId, sourceState, targetState: spec.target, sourcePath: activeTaskPath, targetPath, journalPath,
841
+ task: { operations: taskOperations }, directory: { effect: 'move', changed: true },
842
+ changed: true, completedSteps: [...completed], pendingSteps: [],
843
+ timestamp: journal.metadata.timestamp, agentInfraVersion: journal.metadata.agentInfraVersion
844
+ });
725
845
  }
726
846
  }
727
- let shortId = { effect: 'unchanged', shortId: null, changed: false };
728
- if (!completed.has('registry-committed')) {
847
+ let shortId = { effect: 'unchanged', shortId: request.intent === 'complete' ? registeredKey ?? null : null, changed: false };
848
+ if (requiredSteps.includes('registry-committed') && !completed.has('registry-committed')) {
729
849
  try {
730
850
  shortId = mutateShortIdRegistry(repoRoot, taskId, spec.registry);
731
851
  }
@@ -744,7 +864,7 @@ function applyTaskLifecycle(requestInput, options = {}) {
744
864
  const mappings = loadShortIdByTaskId(repoRoot);
745
865
  if (spec.target === 'active' && !mappings.has(taskId))
746
866
  return failed(request, { code: 'LIFECYCLE_FINAL_STATE_INVALID', message: 'active lifecycle target has no short id' }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, changed: true, completedSteps: [...completed], pendingSteps: [], timestamp: journal.metadata.timestamp, agentInfraVersion: journal.metadata.agentInfraVersion });
747
- if (spec.target !== 'active' && mappings.has(taskId))
867
+ if (request.intent !== 'complete' && spec.target !== 'active' && mappings.has(taskId))
748
868
  return failed(request, { code: 'LIFECYCLE_FINAL_STATE_INVALID', message: 'non-active lifecycle target retained a short id' }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, changed: true, completedSteps: [...completed], pendingSteps: [], timestamp: journal.metadata.timestamp, agentInfraVersion: journal.metadata.agentInfraVersion });
749
869
  try {
750
870
  io.unlinkSync(journalPath);
@@ -760,5 +880,44 @@ function applyTaskLifecycle(requestInput, options = {}) {
760
880
  journalPath, completedSteps: [...completed], pendingSteps: []
761
881
  };
762
882
  }
763
- export { lifecycleIntentCatalog, lifecycleFailureCatalog, lifecycleProducerCatalog, applyTaskLifecycle, previewTaskCompletion };
883
+ function applyTaskLifecycle(request, options = {}) {
884
+ const normalized = normalizedRequest(request);
885
+ if ('code' in normalized)
886
+ return applyTaskLifecycleUnlocked(request, options);
887
+ const repoRoot = path.resolve(options.repoRoot ?? process.cwd());
888
+ const taskId = normalized.intent === 'restore'
889
+ ? normalized.taskRef
890
+ : (() => {
891
+ const resolved = resolveTaskRef(normalized.taskRef, { repoRoot });
892
+ return resolved.ok ? resolved.taskId : null;
893
+ })();
894
+ if (!taskId)
895
+ return applyTaskLifecycleUnlocked(request, options);
896
+ try {
897
+ return withTaskExecutionLock(repoRoot, taskId, 'task-lifecycle.move', () => applyTaskLifecycleUnlocked(request, options));
898
+ }
899
+ catch (error) {
900
+ if (error instanceof TaskExecutionLockError) {
901
+ return failed(request, { code: error.code, message: error.message }, { taskId });
902
+ }
903
+ throw error;
904
+ }
905
+ }
906
+ function applyTaskLifecycleWithinTaskLock(request, options, lockedTaskId) {
907
+ const normalized = normalizedRequest(request);
908
+ if ('code' in normalized)
909
+ return failed(request, normalized);
910
+ const repoRoot = path.resolve(options.repoRoot ?? process.cwd());
911
+ const resolved = normalized.intent === 'restore'
912
+ ? { ok: true, taskId: normalized.taskRef }
913
+ : resolveTaskRef(normalized.taskRef, { repoRoot });
914
+ if (!resolved.ok || resolved.taskId !== lockedTaskId) {
915
+ return failed(request, {
916
+ code: 'LIFECYCLE_LOCK_SCOPE_MISMATCH',
917
+ message: 'task lifecycle request does not match the held task lock'
918
+ }, { taskId: resolved.ok ? resolved.taskId : resolved.taskId });
919
+ }
920
+ return applyTaskLifecycleUnlocked(request, options);
921
+ }
922
+ export { lifecycleIntentCatalog, lifecycleFailureCatalog, lifecycleProducerCatalog, applyTaskLifecycle, applyTaskLifecycleWithinTaskLock, previewTaskCompletion };
764
923
  //# sourceMappingURL=lifecycle.js.map