@fitlab-ai/agent-infra 0.11.13 → 0.11.15

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 (115) hide show
  1. package/dist/bin/internal-cli.js +2 -7
  2. package/dist/lib/agent-clients/adapter.js +0 -3
  3. package/dist/lib/agent-clients/adapters/codex-lifecycle/app-server.js +5 -9
  4. package/dist/lib/agent-clients/adapters/codex-lifecycle/cli.js +1 -5
  5. package/dist/lib/agent-clients/adapters/codex-lifecycle/recovery.js +1 -5
  6. package/dist/lib/agent-clients/adapters/codex-orchestration.js +1 -3
  7. package/dist/lib/agent-clients/adapters/codex.js +0 -2
  8. package/dist/lib/internal/cli-route-inventory.js +0 -1
  9. package/dist/lib/internal/task-finalization.js +7 -5
  10. package/dist/lib/internal/task-lifecycle.js +3 -1
  11. package/dist/lib/internal/task-operation-registry.js +1 -3
  12. package/dist/lib/internal/task-validate.js +14 -124
  13. package/dist/lib/platform/pr-change-report.js +10 -3
  14. package/dist/lib/platform/release-notes.js +2 -2
  15. package/dist/lib/process-data/index.js +15 -4
  16. package/dist/lib/sandbox/commands/control-status.js +0 -1
  17. package/dist/lib/sandbox/commands/create.js +5 -4
  18. package/dist/lib/sandbox/config.js +12 -0
  19. package/dist/lib/sandbox/control/audit.js +1 -2
  20. package/dist/lib/sandbox/control/client.js +12 -47
  21. package/dist/lib/sandbox/control/executor.js +6 -10
  22. package/dist/lib/sandbox/control/host-recovery.js +89 -27
  23. package/dist/lib/sandbox/control/lifecycle.js +22 -37
  24. package/dist/lib/sandbox/control/protocol.js +8 -36
  25. package/dist/lib/sandbox/control/server.js +2 -21
  26. package/dist/lib/sandbox/environment.js +7 -8
  27. package/dist/lib/sandbox/mounts.js +71 -59
  28. package/dist/lib/sandbox/recovery.js +2 -3
  29. package/dist/lib/sandbox/removal.js +19 -4
  30. package/dist/lib/sandbox/workspace-identity.js +30 -15
  31. package/dist/lib/sandbox/workspace-view.js +41 -4
  32. package/dist/lib/task/control-authority.js +39 -17
  33. package/dist/lib/task/control-recovery.js +1 -2
  34. package/dist/lib/task/finalization.js +13 -2
  35. package/dist/lib/task/lifecycle.js +122 -35
  36. package/dist/lib/task/short-id.js +2 -2
  37. package/dist/lifecycle-build-manifest.json +3 -3
  38. package/lib/agent-clients/adapter.ts +0 -27
  39. package/lib/agent-clients/adapters/codex-lifecycle/app-server.ts +6 -12
  40. package/lib/agent-clients/adapters/codex-lifecycle/cli.ts +1 -5
  41. package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +0 -7
  42. package/lib/agent-clients/adapters/codex-lifecycle/recovery.ts +1 -5
  43. package/lib/agent-clients/adapters/codex-lifecycle/store.ts +1 -4
  44. package/lib/agent-clients/adapters/codex-orchestration.ts +1 -3
  45. package/lib/agent-clients/adapters/codex.ts +0 -2
  46. package/lib/internal/cli-route-inventory.ts +0 -1
  47. package/lib/internal/task-finalization.ts +8 -6
  48. package/lib/internal/task-lifecycle.ts +3 -1
  49. package/lib/internal/task-operation-registry.ts +1 -3
  50. package/lib/internal/task-validate.ts +13 -127
  51. package/lib/platform/pr-change-report.ts +9 -3
  52. package/lib/platform/release-notes.ts +2 -2
  53. package/lib/process-data/index.ts +15 -4
  54. package/lib/sandbox/commands/control-status.ts +0 -1
  55. package/lib/sandbox/commands/create.ts +5 -4
  56. package/lib/sandbox/config.ts +13 -0
  57. package/lib/sandbox/control/audit.ts +1 -2
  58. package/lib/sandbox/control/client.ts +13 -61
  59. package/lib/sandbox/control/executor.ts +7 -13
  60. package/lib/sandbox/control/host-recovery.ts +94 -21
  61. package/lib/sandbox/control/lifecycle.ts +22 -37
  62. package/lib/sandbox/control/protocol.ts +9 -45
  63. package/lib/sandbox/control/server.ts +2 -17
  64. package/lib/sandbox/environment.ts +7 -8
  65. package/lib/sandbox/mounts.ts +92 -75
  66. package/lib/sandbox/recovery.ts +2 -3
  67. package/lib/sandbox/removal.ts +16 -4
  68. package/lib/sandbox/workspace-identity.ts +36 -13
  69. package/lib/sandbox/workspace-view.ts +40 -5
  70. package/lib/task/control-authority.ts +40 -26
  71. package/lib/task/control-recovery.ts +2 -3
  72. package/lib/task/finalization.ts +15 -2
  73. package/lib/task/lifecycle.ts +135 -33
  74. package/lib/task/short-id.ts +5 -2
  75. package/package.json +1 -1
  76. package/templates/.agents/rules/lifecycle-orchestration.en.md +3 -4
  77. package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +3 -4
  78. package/templates/.agents/rules/task-short-id.en.md +18 -12
  79. package/templates/.agents/rules/task-short-id.zh-CN.md +12 -7
  80. package/templates/.agents/skills/complete-task/SKILL.en.md +10 -2
  81. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +10 -2
  82. package/templates/.agents/skills/create-pr/reference/change-report.en.md +12 -2
  83. package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +12 -2
  84. package/templates/.agents/skills/create-release-note/SKILL.en.md +3 -1
  85. package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +3 -1
  86. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +5 -5
  87. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +5 -5
  88. package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.en.md +13 -2
  89. package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.zh-CN.md +13 -2
  90. package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +2 -0
  91. package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +2 -0
  92. package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
  93. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
  94. package/templates/.agents/skills/watch-pr/SKILL.en.md +19 -2
  95. package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +20 -3
  96. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +14 -0
  97. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +15 -1
  98. package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-client.js +0 -130
  99. package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-context.js +0 -127
  100. package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-operation.js +0 -131
  101. package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-registration.js +0 -275
  102. package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller-cli.js +0 -106
  103. package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +0 -345
  104. package/dist/lib/runtime/agent-runtime.js +0 -33
  105. package/dist/lib/sandbox/control/agent-operation.js +0 -34
  106. package/dist/lib/task/runtime-paths.js +0 -10
  107. package/lib/agent-clients/adapters/codex-lifecycle/controller-client.ts +0 -158
  108. package/lib/agent-clients/adapters/codex-lifecycle/controller-context.ts +0 -163
  109. package/lib/agent-clients/adapters/codex-lifecycle/controller-operation.ts +0 -151
  110. package/lib/agent-clients/adapters/codex-lifecycle/controller-registration.ts +0 -352
  111. package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller-cli.ts +0 -109
  112. package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +0 -437
  113. package/lib/runtime/agent-runtime.ts +0 -41
  114. package/lib/sandbox/control/agent-operation.ts +0 -49
  115. package/lib/task/runtime-paths.ts +0 -12
@@ -54,8 +54,24 @@ const DEFAULT_IO = {
54
54
  renameSync: (source, target) => fs.renameSync(source, target),
55
55
  readFileSync: (file) => fs.readFileSync(file, 'utf8'),
56
56
  writeFileSync: (file, content, flag) => fs.writeFileSync(file, content, { encoding: 'utf8', flag: flag ?? 'w' }),
57
- unlinkSync: (file) => fs.unlinkSync(file)
57
+ unlinkSync: (file) => fs.unlinkSync(file),
58
+ rmdirSync: (directory) => fs.rmdirSync(directory),
59
+ readdirSync: (directory) => fs.readdirSync(directory),
60
+ readMountInfoSync: () => fs.readFileSync('/proc/self/mountinfo', 'utf8')
58
61
  };
62
+ function isVirtiofsMountPoint(directory, mountInfo) {
63
+ const decodeMountInfoPath = (value) => value.replace(/\\([0-7]{3})/gu, (_match, octal) => String.fromCharCode(Number.parseInt(octal, 8)));
64
+ const expected = path.resolve(directory);
65
+ return mountInfo.split('\n').some((line) => {
66
+ if (!line)
67
+ return false;
68
+ const fields = line.split(' ');
69
+ const separator = fields.indexOf('-');
70
+ return separator >= 0
71
+ && fields[separator + 1] === 'virtiofs'
72
+ && path.resolve(decodeMountInfoPath(fields[4] ?? '')) === expected;
73
+ });
74
+ }
59
75
  function failed(request, error, extra = {}) {
60
76
  return {
61
77
  status: 'failed', changed: false, requestRef: request.taskRef, taskId: null,
@@ -215,6 +231,96 @@ function transferTaskContents(sourcePath, targetPath) {
215
231
  throw new Error('completed target does not match task source after copy');
216
232
  }
217
233
  }
234
+ function pathExists(candidate) {
235
+ try {
236
+ fs.lstatSync(candidate);
237
+ return true;
238
+ }
239
+ catch (error) {
240
+ if (error.code === 'ENOENT')
241
+ return false;
242
+ throw error;
243
+ }
244
+ }
245
+ function assertTaskControlRootPath(controlRoot, taskId, expectedProject) {
246
+ if (path.basename(controlRoot) !== taskId || path.basename(path.dirname(controlRoot)) !== 'tasks'
247
+ || (expectedProject !== undefined && path.basename(path.dirname(path.dirname(controlRoot))) !== expectedProject)) {
248
+ throw new Error('control root path does not match the task/project layout');
249
+ }
250
+ const managedBase = path.dirname(path.dirname(path.dirname(controlRoot)));
251
+ let current = managedBase;
252
+ for (const segment of path.relative(managedBase, controlRoot).split(path.sep).filter(Boolean)) {
253
+ const stat = fs.lstatSync(current);
254
+ if (stat.isSymbolicLink())
255
+ throw new Error('control root ancestor is a symbolic link');
256
+ current = path.join(current, segment);
257
+ }
258
+ const stat = fs.lstatSync(controlRoot);
259
+ if (!stat.isDirectory() || stat.isSymbolicLink())
260
+ throw new Error('control root is not a real directory');
261
+ }
262
+ function assertTaskControlManifestIdentity(controlRoot, taskId, repoRoot, expectedProject) {
263
+ const controlManifest = path.join(controlRoot, 'manifest.json');
264
+ if (!fs.existsSync(controlManifest))
265
+ throw new Error('control manifest is missing');
266
+ const manifestStat = fs.lstatSync(controlManifest);
267
+ if (!manifestStat.isFile() || manifestStat.isSymbolicLink())
268
+ throw new Error('control manifest is not a real file');
269
+ const manifest = JSON.parse(fs.readFileSync(controlManifest, 'utf8'));
270
+ const project = expectedProject ?? path.basename(path.dirname(path.dirname(controlRoot)));
271
+ if (manifest.mode !== 'task-bound' || manifest.taskId !== taskId || manifest.project !== project
272
+ || typeof manifest.repoRoot !== 'string' || fs.realpathSync.native(manifest.repoRoot) !== fs.realpathSync.native(repoRoot)) {
273
+ throw new Error('control manifest identity does not match the task');
274
+ }
275
+ }
276
+ function taskBoundControlMoveSafety(request, controlRoot, options, taskId, repoRoot, validateManifestIdentity) {
277
+ if (!controlRoot)
278
+ return null;
279
+ const controlManifest = path.join(controlRoot, 'manifest.json');
280
+ const controlLease = path.join(controlRoot, 'lease.json');
281
+ const stateExists = pathExists(controlRoot) || pathExists(controlManifest) || pathExists(controlLease);
282
+ if (stateExists) {
283
+ try {
284
+ assertTaskControlRootPath(controlRoot, taskId, options.controlProject);
285
+ if (validateManifestIdentity) {
286
+ assertTaskControlManifestIdentity(controlRoot, taskId, repoRoot, options.controlProject);
287
+ }
288
+ else {
289
+ const manifestStat = fs.lstatSync(controlManifest);
290
+ if (!manifestStat.isFile() || manifestStat.isSymbolicLink())
291
+ throw new Error('control manifest is not a real file');
292
+ }
293
+ }
294
+ catch {
295
+ return { code: 'LIFECYCLE_RUNTIME_MOVE_UNSAFE', message: 'task-bound sandbox control root is missing, malformed, or belongs to another task' };
296
+ }
297
+ }
298
+ if (request.intent !== 'complete' && (pathExists(controlManifest) || pathExists(controlLease))) {
299
+ return { code: 'LIFECYCLE_RUNTIME_MOVE_UNSAFE', message: 'task-bound sandbox control state exists; stop and remove the verified sandbox before moving the task directory' };
300
+ }
301
+ return null;
302
+ }
303
+ function orchestrationMoveSafety(sourcePath) {
304
+ const orchestrationPath = path.join(sourcePath, '.runtime', 'orchestration.json');
305
+ if (!pathExists(orchestrationPath))
306
+ return null;
307
+ try {
308
+ const run = JSON.parse(fs.readFileSync(orchestrationPath, 'utf8'));
309
+ if (run.status === 'running' || run.pendingDelegation !== null && run.pendingDelegation !== undefined) {
310
+ return { code: 'LIFECYCLE_RUNTIME_MOVE_UNSAFE', message: 'task orchestration has an active or pending delegation; settle it before moving the task directory' };
311
+ }
312
+ }
313
+ catch {
314
+ return { code: 'LIFECYCLE_RUNTIME_MOVE_UNSAFE', message: 'task orchestration state is unreadable; refusing to move the task directory' };
315
+ }
316
+ return null;
317
+ }
318
+ function runtimeMoveSafety(request, movingTaskDirectory, options, taskId, repoRoot, sourcePath, validateControlManifestIdentity) {
319
+ if (!movingTaskDirectory)
320
+ return null;
321
+ const controlSafety = taskBoundControlMoveSafety(request, options.controlRoot, options, taskId, repoRoot, validateControlManifestIdentity);
322
+ return controlSafety ?? orchestrationMoveSafety(sourcePath);
323
+ }
218
324
  function targetContainsTask(targetPath, taskId, request) {
219
325
  try {
220
326
  const stat = fs.lstatSync(targetPath);
@@ -546,37 +652,6 @@ function applyTaskLifecycleUnlocked(requestInput, options = {}) {
546
652
  const targetPath = path.join(repoRoot, '.agents', 'workspace', spec.target, taskId);
547
653
  const requiredSteps = stepsForIntent(request.intent);
548
654
  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
- };
580
655
  const currentTaskFile = path.join(sourcePath, 'task.md');
581
656
  const existingJournal = fs.existsSync(path.join(sourcePath, '.task-lifecycle.json'));
582
657
  if (sourceState === spec.target && !existingJournal) {
@@ -599,7 +674,7 @@ function applyTaskLifecycleUnlocked(requestInput, options = {}) {
599
674
  && !(request.intent === 'complete' && targetContainsTask(targetPath, taskId, request))) {
600
675
  return failed(request, { code: 'LIFECYCLE_TARGET_CONFLICT', message: `target directory already exists: ${targetPath}` }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath });
601
676
  }
602
- const moveSafety = assertRuntimeMoveSafe();
677
+ const moveSafety = runtimeMoveSafety(request, movingTaskDirectory, options, taskId, repoRoot, sourcePath, true);
603
678
  if (moveSafety)
604
679
  return failed(request, moveSafety, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath });
605
680
  const content = io.readFileSync(currentTaskFile);
@@ -757,7 +832,7 @@ function applyTaskLifecycleUnlocked(requestInput, options = {}) {
757
832
  if (fs.existsSync(targetPath) && !fs.existsSync(path.join(sourcePath, 'task.md')))
758
833
  completed.add('directory-moved');
759
834
  if (!completed.has('directory-moved')) {
760
- const moveSafety = assertRuntimeMoveSafe();
835
+ const moveSafety = runtimeMoveSafety(request, movingTaskDirectory, options, taskId, repoRoot, sourcePath, false);
761
836
  if (moveSafety)
762
837
  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 });
763
838
  fs.mkdirSync(path.dirname(targetPath), { recursive: true });
@@ -833,7 +908,19 @@ function applyTaskLifecycleUnlocked(requestInput, options = {}) {
833
908
  const activeTaskPath = path.join(repoRoot, '.agents', 'workspace', 'active', taskId);
834
909
  if (request.intent === 'complete' && fs.existsSync(activeTaskPath)) {
835
910
  try {
836
- fs.rmdirSync(activeTaskPath);
911
+ let sourceIsVirtiofsMount = false;
912
+ try {
913
+ sourceIsVirtiofsMount = isVirtiofsMountPoint(activeTaskPath, io.readMountInfoSync());
914
+ }
915
+ catch { /* Platforms without mountinfo retain the ordinary directory cleanup path. */ }
916
+ if (sourceIsVirtiofsMount) {
917
+ if (io.readdirSync(activeTaskPath).length > 0) {
918
+ throw new Error('virtiofs source directory is not empty after task contents were transferred');
919
+ }
920
+ }
921
+ else {
922
+ io.rmdirSync(activeTaskPath);
923
+ }
837
924
  }
838
925
  catch (error) {
839
926
  return failed(request, { code: 'LIFECYCLE_SOURCE_DIRECTORY_CLEANUP_FAILED', message: String(error) }, {
@@ -361,7 +361,7 @@ function loadShortIdByTaskId(repoRoot) {
361
361
  }
362
362
  return map;
363
363
  }
364
- function inspectShortIdRegistry(repoRoot) {
364
+ function inspectShortIdRegistry(repoRoot, shortIdLength = configuredShortIdLength(repoRoot)) {
365
365
  const registryPath = path.join(repoRoot, '.agents', 'workspace', 'active', REGISTRY_NAME);
366
366
  if (!fs.existsSync(registryPath)) {
367
367
  return {
@@ -399,7 +399,7 @@ function inspectShortIdRegistry(repoRoot) {
399
399
  };
400
400
  }
401
401
  try {
402
- const registry = validateRegistry(parsed, registryPath, configuredShortIdLength(repoRoot));
402
+ const registry = validateRegistry(parsed, registryPath, shortIdLength);
403
403
  const shortIds = new Map();
404
404
  for (const [key, taskId] of Object.entries(registry.ids))
405
405
  shortIds.set(taskId, key);
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "protocolVersion": 3,
3
- "packageVersion": "0.11.13",
4
- "sourceInputHash": "70e89f362895c1f8f976011715a86b47e06c105ca4285eacbb3fa51d0459a282",
5
- "internalExecutableBuildHash": "f183e6d39ba985dbb2962eb951b4019af9594361ca3c46b88bf65fff325d7ad4"
3
+ "packageVersion": "0.11.15",
4
+ "sourceInputHash": "2d62f5ec22e0e0024a0e11258cef192b9d4c80764386bc112e4c2a3a03d72a3f",
5
+ "internalExecutableBuildHash": "9b88774ed654efea7d31aea3790aa1194f30df1f4487f95664de03f48815123f"
6
6
  }
@@ -23,28 +23,6 @@ import type {
23
23
  SandboxTool,
24
24
  SandboxToolContext
25
25
  } from '../sandbox/tool-types.ts';
26
- import type { SandboxControlManifest } from '../sandbox/control/protocol.ts';
27
-
28
- type AgentClientSandboxControlOperation = Readonly<{
29
- validate: (params: Readonly<{
30
- operation: string;
31
- payload: Record<string, unknown>;
32
- manifest: SandboxControlManifest;
33
- }>) => void;
34
- execute: (params: Readonly<{
35
- operation: string;
36
- payload: Record<string, unknown>;
37
- manifest: SandboxControlManifest;
38
- manifestPath: string;
39
- }>) => Promise<Readonly<{ exitCode: number; stdout: string; stderr: string }>>;
40
- recover?: (params: Readonly<{
41
- operation: string;
42
- manifest: SandboxControlManifest;
43
- manifestPath: string;
44
- stdout: string | null;
45
- }>) => Readonly<Record<string, unknown>>;
46
- }>;
47
-
48
26
  type AgentClientCapabilities = AgentClientCapabilityMap;
49
27
 
50
28
  type AgentClientModelSelectionContext =
@@ -147,7 +125,6 @@ type AgentClientAdapter = Readonly<{
147
125
  modelSelection: AgentClientModelSelectionContext;
148
126
  delegationEvidence: AgentClientDelegationEvidence;
149
127
  orchestrationAdapter?: AgentClientOrchestrationAdapter;
150
- sandboxControlOperation?: AgentClientSandboxControlOperation;
151
128
  project: AgentClientProjectDescriptor;
152
129
  sandbox: AgentClientSandboxDescriptor;
153
130
  }>;
@@ -728,9 +705,6 @@ function defineAgentClientAdapter(
728
705
  ...(candidate.orchestrationAdapter === undefined
729
706
  ? {}
730
707
  : { orchestrationAdapter: Object.freeze({ ...candidate.orchestrationAdapter }) }),
731
- ...(candidate.sandboxControlOperation === undefined
732
- ? {}
733
- : { sandboxControlOperation: Object.freeze({ ...candidate.sandboxControlOperation }) }),
734
708
  project: Object.freeze({
735
709
  ownedPathPrefixes: Object.freeze(paths),
736
710
  ...projectAssets,
@@ -753,7 +727,6 @@ export type {
753
727
  AgentClientCapabilities,
754
728
  AgentClientCustomCommandDescriptor,
755
729
  AgentClientDelegationEvidence,
756
- AgentClientSandboxControlOperation,
757
730
  AgentClientLifecycleRecoveryRequest,
758
731
  AgentClientLifecycleRecoveryResult,
759
732
  AgentClientOrchestrationAdapter,
@@ -170,8 +170,7 @@ function validateCodexLifecycleHookConfig(value: unknown): void {
170
170
 
171
171
  function parseCodexHooksList(
172
172
  value: unknown,
173
- repoRoot: string,
174
- expectedSource: 'direct-host' | 'isolated-user' = 'direct-host'
173
+ repoRoot: string
175
174
  ): readonly DiscoveredHook[] {
176
175
  const root = object(value);
177
176
  const entries = Array.isArray(root?.data) ? root.data : [];
@@ -203,10 +202,8 @@ function parseCodexHooksList(
203
202
  && hook.currentHash.length > 0
204
203
  && hook.sourcePathDigest.length === 64
205
204
  && hook.pluginId === null
206
- && (expectedSource === 'isolated-user'
207
- ? hook.source === 'user' && hook.trustStatus !== 'modified'
208
- : (hook.source === 'project' && hook.trustStatus === 'trusted')
209
- || hook.isManaged && hook.trustStatus === 'managed')
205
+ && (hook.source === 'project' && hook.trustStatus === 'trusted'
206
+ || hook.isManaged && hook.trustStatus === 'managed')
210
207
  ));
211
208
  if (!valid) throw new Error('CODEX_PREFLIGHT_HOOKS_NOT_LOADED: lifecycle hooks are not loaded for this workspace');
212
209
  return Object.freeze(hooks);
@@ -628,7 +625,7 @@ function validateStaticCodexPreflight(repoRoot: string): Readonly<{ cliVersion:
628
625
  async function discoverLifecycleHooks(repoRoot: string): Promise<Readonly<{
629
626
  discoveredHooks: readonly DiscoveredHook[];
630
627
  hookProvenance: Readonly<{
631
- hookSource: 'isolated-user' | 'managed' | 'project';
628
+ hookSource: 'managed' | 'project';
632
629
  hookSourcePathDigest: string;
633
630
  hookSourceHash: string;
634
631
  }>;
@@ -641,8 +638,7 @@ async function discoverLifecycleHooks(repoRoot: string): Promise<Readonly<{
641
638
  await transport.initialize();
642
639
  discoveredHooks = parseCodexHooksList(
643
640
  await transport.request('hooks/list', { cwds: [repoRoot] }),
644
- repoRoot,
645
- process.env.AGENT_INFRA_CODEX_CONTROLLER_CONTEXT ? 'isolated-user' : 'direct-host'
641
+ repoRoot
646
642
  );
647
643
  } finally { transport.close(); }
648
644
  const lifecycleHooks = LIFECYCLE_HOOKS.map(({ event, matcher, phase }) => discoveredHooks.filter((hook) =>
@@ -660,9 +656,7 @@ async function discoverLifecycleHooks(repoRoot: string): Promise<Readonly<{
660
656
  throw new Error('CODEX_PREFLIGHT_HOOK_SOURCE_AMBIGUOUS: lifecycle hooks do not share one exact source');
661
657
  }
662
658
  const source = [...sources][0];
663
- const hookSource = process.env.AGENT_INFRA_CODEX_CONTROLLER_CONTEXT
664
- ? 'isolated-user'
665
- : source === 'managed' ? 'managed' : 'project';
659
+ const hookSource = source === 'managed' ? 'managed' : 'project';
666
660
  return {
667
661
  discoveredHooks,
668
662
  hookProvenance: Object.freeze({
@@ -9,7 +9,6 @@ import {
9
9
  resolveCodexThread
10
10
  } from './app-server.ts';
11
11
  import { createCodexLifecycleStore } from './store.ts';
12
- import { verifyCodexSandboxControllerContextWithWarnings } from './sandbox-controller.ts';
13
12
  import type { CodexLifecycleEvent } from './evidence.ts';
14
13
  import { resolveTaskRef } from '../../../task/resolve-ref.ts';
15
14
  import { resolveTaskContext } from '../../../task/resolve-ref.ts';
@@ -103,10 +102,7 @@ function hookDefinitionHash(): string {
103
102
  }
104
103
 
105
104
  function resolveLifecycleTaskContext() {
106
- const controllerPath = process.env.AGENT_INFRA_CODEX_CONTROLLER_CONTEXT;
107
- const taskId = controllerPath
108
- ? verifyCodexSandboxControllerContextWithWarnings(controllerPath, { repoRoot: process.cwd() }).context.taskId
109
- : process.env.AGENT_INFRA_TASK_ID;
105
+ const taskId = process.env.AGENT_INFRA_TASK_ID;
110
106
  const context = taskId
111
107
  ? resolveTaskRef(taskId, { repoRoot: process.cwd() })
112
108
  : resolveTaskContext(undefined, { repoRoot: process.cwd() });
@@ -2,25 +2,18 @@
2
2
  "executableFiles": [
3
3
  "bin/internal-cli.ts",
4
4
  "lib/agent-clients/adapters/codex-lifecycle/cli.ts",
5
- "lib/agent-clients/adapters/codex-lifecycle/sandbox-controller-cli.ts",
6
5
  "lib/internal/sandbox-control.ts",
7
6
  "lib/internal/task-orchestration.ts",
8
7
  "lib/agent-clients/adapters/codex-orchestration.ts",
9
8
  "lib/task/delegation-receipts.ts",
10
9
  "lib/task/orchestration.ts",
11
10
  "lib/agent-clients/adapters/codex-lifecycle/build-identity.ts",
12
- "lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts",
13
- "lib/agent-clients/adapters/codex-lifecycle/controller-client.ts",
14
- "lib/agent-clients/adapters/codex-lifecycle/controller-operation.ts",
15
- "lib/agent-clients/adapters/codex-lifecycle/controller-context.ts",
16
11
  "lib/agent-clients/adapters/codex-lifecycle/app-server.ts",
17
12
  "lib/agent-clients/adapters/codex-lifecycle/binding.ts",
18
13
  "lib/agent-clients/adapters/codex-lifecycle/evidence.ts",
19
14
  "lib/agent-clients/adapters/codex-lifecycle/store.ts",
20
15
  "lib/agent-clients/adapters/codex-lifecycle/manifest-files.json",
21
- "lib/agent-clients/adapters/codex-lifecycle/controller-registration.ts",
22
16
  "lib/sandbox/control/protocol.ts",
23
- "lib/sandbox/control/agent-operation.ts",
24
17
  "lib/sandbox/control/client.ts",
25
18
  "lib/sandbox/control/executor.ts",
26
19
  "lib/server/process-state.ts",
@@ -216,15 +216,11 @@ function hostEvidenceMatches(
216
216
  && host.spawnToolUseId === start.spawnToolUseId
217
217
  && Boolean(host.spawnObservedAt)
218
218
  && host.spawnObservedAt === record.spawnObservedAt
219
- && typeof host.controllerInstanceDigest === 'string'
220
- && typeof host.controlGeneration === 'string'
221
219
  && hostRevisionMatches(record, receipt, host);
222
220
  }
223
221
 
224
222
  function hostRevisionMatches(record: StoredCodexLifecycle, receipt: DelegationReceipt, host: CodexLifecycleActivationEvidence): boolean {
225
- return typeof host.controllerInstanceDigest === 'string'
226
- && typeof host.controlGeneration === 'string'
227
- && host.startRevision >= 1
223
+ return host.startRevision >= 1
228
224
  && record.revision > host.startRevision
229
225
  && record.state.child?.childThreadId === receipt.childId
230
226
  && record.state.stop?.childThreadId === receipt.childId;
@@ -35,16 +35,13 @@ type CodexLifecycleActivationEvidence = Readonly<{
35
35
  packageVersion?: string;
36
36
  internalExecutableBuildHash?: string;
37
37
  lifecycleContractHash?: string;
38
- hookSource?: 'project' | 'managed' | 'isolated-user';
38
+ hookSource?: 'project' | 'managed';
39
39
  hookSourcePathDigest?: string;
40
40
  hookSourceHash?: string;
41
41
  capabilitySessionId?: string;
42
42
  capabilityTurnId?: string;
43
- capabilityToolUseId?: string;
44
43
  spawnToolUseId?: string;
45
44
  spawnObservedAt?: string;
46
- controllerInstanceDigest?: string | null;
47
- controlGeneration?: string | null;
48
45
  }>;
49
46
 
50
47
  type CodexAdapterState = Readonly<{
@@ -327,9 +327,7 @@ async function activateCodexOrchestrationDelegation(
327
327
  capabilitySessionId: evidence.parentThreadId,
328
328
  capabilityTurnId: evidence.parentTurnId,
329
329
  spawnToolUseId: evidence.spawnToolUseId,
330
- spawnObservedAt: record.spawnObservedAt ?? undefined,
331
- controllerInstanceDigest: null,
332
- controlGeneration: null
330
+ spawnObservedAt: record.spawnObservedAt ?? undefined
333
331
  }
334
332
  }
335
333
  }, coreOptions(options));
@@ -6,7 +6,6 @@ import {
6
6
  } from './codex-sandbox.ts';
7
7
  import { parseCodexLifecycleBinding } from './codex-lifecycle/binding.ts';
8
8
  import { isCodexDelegationReceiptEvidence, isCodexLifecycleActivationEvidence } from './codex-lifecycle/evidence.ts';
9
- import { codexControllerOperation } from './codex-lifecycle/controller-operation.ts';
10
9
 
11
10
  function codexCarrierError(code: string): Error {
12
11
  const error = new Error(code);
@@ -80,7 +79,6 @@ const codexAdapter = defineAgentClientAdapter({
80
79
  return recoverStartedLifecycleFromAdapter(...args);
81
80
  }
82
81
  },
83
- sandboxControlOperation: codexControllerOperation,
84
82
  project: {
85
83
  ownedPathPrefixes: ['.codex/'],
86
84
  managed: ['.codex/hooks.json', '.codex/agents/'],
@@ -38,7 +38,6 @@ export const INTERNAL_HANDLER_ROUTE_SELECTORS = Object.freeze({
38
38
  'sandbox-control': ['serve', 'execute', 'recover', 'client'],
39
39
  'agent-client': ['next-steps', 'model-selection', 'launch-carrier'],
40
40
  'codex-lifecycle': ['preflight', 'hook-event', 'resolve-start', 'resolve-stop', 'consume'],
41
- 'codex-sandbox-controller': ['verify-context', 'run'],
42
41
  'git-workflow': ['inspect', 'preview-tree', 'snapshot', 'compare-trees', 'commit', 'push-rebased'],
43
42
  'task-delivery': ['deliver'],
44
43
  'release-workflow': ['inspect', 'prepare', 'publish', 'post-prepare', 'post-publish'],
@@ -7,8 +7,8 @@ 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';
11
- import { resolveTaskRuntimeRoot } from '../task/runtime-paths.ts';
10
+ import { validateSandboxControlTaskIdentity } from '../sandbox/control/lifecycle.ts';
11
+ import { loadSandboxControlLocation } from '../sandbox/config.ts';
12
12
  import { serializeTaskFinalizationEnvelope } from '../task/finalization-envelope.ts';
13
13
  import { verifyTaskEvent } from '../task/verification.ts';
14
14
  import { ensureInternalHandlerRoute, internalHandlerRoute } from './cli-route-inventory.ts';
@@ -86,7 +86,8 @@ async function taskFinalization(args: string[] = []): Promise<void> {
86
86
  const taskId = process.env.AGENT_INFRA_TASK_ID;
87
87
  const generation = process.env.AGENT_INFRA_CONTROL_GENERATION;
88
88
  const controlRootId = process.env.AGENT_INFRA_CONTROL_ROOT_ID;
89
- if (!statusDir || !taskId || !generation || !controlRootId || taskId !== resolved.taskId) {
89
+ const controlRoot = process.env.AGENT_INFRA_CONTROL_ROOT_DIR;
90
+ if (!statusDir || !taskId || !generation || !controlRootId || !controlRoot || taskId !== resolved.taskId) {
90
91
  const detail = { code: 'TASK_CONTROL_TRANSPORT_INVALID', message: 'sandbox task control identity is incomplete', retryable: false };
91
92
  process.stdout.write(envelope('failed', false, true, null, detail));
92
93
  process.exitCode = 1;
@@ -96,8 +97,8 @@ async function taskFinalization(args: string[] = []): Promise<void> {
96
97
  repoRoot,
97
98
  beforeLifecycle: async (currentTaskId) => {
98
99
  if (currentTaskId !== taskId) throw new Error('SANDBOX_CONTROL_IDENTITY_MISMATCH');
99
- await quiesceSandboxControlForTaskCompletion(
100
- path.join(resolveTaskRuntimeRoot(taskId, { repoRoot }), 'sandbox-control'),
100
+ validateSandboxControlTaskIdentity(
101
+ controlRoot,
101
102
  { taskId, generation, controlRootId, statusDir }
102
103
  );
103
104
  },
@@ -111,8 +112,9 @@ async function taskFinalization(args: string[] = []): Promise<void> {
111
112
  process.exitCode = exitCode(status);
112
113
  return;
113
114
  }
115
+ const controlLocation = loadSandboxControlLocation(repoRoot);
114
116
  const result = await dispatchTaskControlOperation(
115
- createDirectHostExecutionContext({ repoRoot }),
117
+ createDirectHostExecutionContext({ repoRoot, ...(controlLocation ?? {}) }),
116
118
  boundOperation
117
119
  );
118
120
  if (result.status === 'prepared') {
@@ -9,6 +9,7 @@ import { detectRepoRoot, resolveTaskRef } from '../task/resolve-ref.ts';
9
9
  import type { TaskLifecycleResult } from '../task/lifecycle.ts';
10
10
  import type { AgentClientLifecycleRecoveryResult as LifecycleRecoveryResult } from '../agent-clients/adapter.ts';
11
11
  import { ensureInternalHandlerRoute, internalHandlerRoute } from './cli-route-inventory.ts';
12
+ import { loadSandboxControlLocation } from '../sandbox/config.ts';
12
13
 
13
14
  const USAGE = `Usage: agent-infra-internal task-lifecycle <N | TASK-id> <intent> --agent <agent> [intent flags] [--dry-run]\n\nIntents: ${lifecycleIntentCatalog.filter((intent) => intent !== 'complete').join(', ')}\nrecover-started: --auto\n`;
14
15
 
@@ -59,8 +60,9 @@ async function taskLifecycle(args: string[] = []): Promise<void> {
59
60
  repoRoot = resolved.repoRoot;
60
61
  }
61
62
 
63
+ const controlLocation = loadSandboxControlLocation(repoRoot);
62
64
  const result = await dispatchTaskControlOperation(
63
- createDirectHostExecutionContext({ repoRoot }),
65
+ createDirectHostExecutionContext({ repoRoot, ...(controlLocation ?? {}) }),
64
66
  operation
65
67
  ) as TaskLifecycleResult | LifecycleRecoveryResult;
66
68
  process.stdout.write(`${JSON.stringify(result)}\n`);
@@ -60,8 +60,6 @@ function internalTaskRoutes(): TaskOperationDescriptor[] {
60
60
  descriptor('internal', 'codex-lifecycle', 'resolve-start', 'conditional', 'progress'),
61
61
  descriptor('internal', 'codex-lifecycle', 'resolve-stop', 'conditional', 'progress'),
62
62
  descriptor('internal', 'codex-lifecycle', 'consume', 'conditional', 'progress'),
63
- descriptor('internal', 'codex-sandbox-controller', 'verify-context', 'task-bound', 'diagnostic'),
64
- descriptor('internal', 'codex-sandbox-controller', 'run', 'task-bound', 'progress'),
65
63
  descriptor('internal', 'git-workflow', 'inspect', 'conditional', 'diagnostic'),
66
64
  descriptor('internal', 'git-workflow', 'preview-tree', 'conditional', 'diagnostic'),
67
65
  descriptor('internal', 'git-workflow', 'snapshot', 'conditional', 'diagnostic'),
@@ -213,7 +211,7 @@ export const TASK_OPERATION_DESCRIPTORS = Object.freeze([
213
211
  ]);
214
212
 
215
213
  export const INTERNAL_DISPATCHER_ROUTES = Object.freeze([
216
- 'task-create', 'task-qualification', 'sandbox-control', 'agent-client', 'codex-lifecycle', 'codex-sandbox-controller',
214
+ 'task-create', 'task-qualification', 'sandbox-control', 'agent-client', 'codex-lifecycle',
217
215
  'git-workflow', 'task-delivery', 'release-workflow', 'platform-release-notes', 'platform-security', 'platform-metadata', 'platform-context',
218
216
  'platform-comment', 'platform-issue', 'platform-pr', 'platform-pr-review', 'pr-review-grade',
219
217
  'platform-checks', 'task-context', 'task-ledger', 'task-warning', 'task-activity', 'task-artifact',