@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.
- package/dist/bin/internal-cli.js +2 -7
- package/dist/lib/agent-clients/adapter.js +0 -3
- package/dist/lib/agent-clients/adapters/codex-lifecycle/app-server.js +5 -9
- package/dist/lib/agent-clients/adapters/codex-lifecycle/cli.js +1 -5
- package/dist/lib/agent-clients/adapters/codex-lifecycle/recovery.js +1 -5
- package/dist/lib/agent-clients/adapters/codex-orchestration.js +1 -3
- package/dist/lib/agent-clients/adapters/codex.js +0 -2
- package/dist/lib/internal/cli-route-inventory.js +0 -1
- package/dist/lib/internal/task-finalization.js +7 -5
- package/dist/lib/internal/task-lifecycle.js +3 -1
- package/dist/lib/internal/task-operation-registry.js +1 -3
- package/dist/lib/internal/task-validate.js +14 -124
- package/dist/lib/platform/pr-change-report.js +10 -3
- package/dist/lib/platform/release-notes.js +2 -2
- package/dist/lib/process-data/index.js +15 -4
- package/dist/lib/sandbox/commands/control-status.js +0 -1
- package/dist/lib/sandbox/commands/create.js +5 -4
- package/dist/lib/sandbox/config.js +12 -0
- package/dist/lib/sandbox/control/audit.js +1 -2
- package/dist/lib/sandbox/control/client.js +12 -47
- package/dist/lib/sandbox/control/executor.js +6 -10
- package/dist/lib/sandbox/control/host-recovery.js +89 -27
- package/dist/lib/sandbox/control/lifecycle.js +22 -37
- package/dist/lib/sandbox/control/protocol.js +8 -36
- package/dist/lib/sandbox/control/server.js +2 -21
- package/dist/lib/sandbox/environment.js +7 -8
- package/dist/lib/sandbox/mounts.js +71 -59
- package/dist/lib/sandbox/recovery.js +2 -3
- package/dist/lib/sandbox/removal.js +19 -4
- package/dist/lib/sandbox/workspace-identity.js +30 -15
- package/dist/lib/sandbox/workspace-view.js +41 -4
- package/dist/lib/task/control-authority.js +39 -17
- package/dist/lib/task/control-recovery.js +1 -2
- package/dist/lib/task/finalization.js +13 -2
- package/dist/lib/task/lifecycle.js +122 -35
- package/dist/lib/task/short-id.js +2 -2
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-clients/adapter.ts +0 -27
- package/lib/agent-clients/adapters/codex-lifecycle/app-server.ts +6 -12
- package/lib/agent-clients/adapters/codex-lifecycle/cli.ts +1 -5
- package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +0 -7
- package/lib/agent-clients/adapters/codex-lifecycle/recovery.ts +1 -5
- package/lib/agent-clients/adapters/codex-lifecycle/store.ts +1 -4
- package/lib/agent-clients/adapters/codex-orchestration.ts +1 -3
- package/lib/agent-clients/adapters/codex.ts +0 -2
- package/lib/internal/cli-route-inventory.ts +0 -1
- package/lib/internal/task-finalization.ts +8 -6
- package/lib/internal/task-lifecycle.ts +3 -1
- package/lib/internal/task-operation-registry.ts +1 -3
- package/lib/internal/task-validate.ts +13 -127
- package/lib/platform/pr-change-report.ts +9 -3
- package/lib/platform/release-notes.ts +2 -2
- package/lib/process-data/index.ts +15 -4
- package/lib/sandbox/commands/control-status.ts +0 -1
- package/lib/sandbox/commands/create.ts +5 -4
- package/lib/sandbox/config.ts +13 -0
- package/lib/sandbox/control/audit.ts +1 -2
- package/lib/sandbox/control/client.ts +13 -61
- package/lib/sandbox/control/executor.ts +7 -13
- package/lib/sandbox/control/host-recovery.ts +94 -21
- package/lib/sandbox/control/lifecycle.ts +22 -37
- package/lib/sandbox/control/protocol.ts +9 -45
- package/lib/sandbox/control/server.ts +2 -17
- package/lib/sandbox/environment.ts +7 -8
- package/lib/sandbox/mounts.ts +92 -75
- package/lib/sandbox/recovery.ts +2 -3
- package/lib/sandbox/removal.ts +16 -4
- package/lib/sandbox/workspace-identity.ts +36 -13
- package/lib/sandbox/workspace-view.ts +40 -5
- package/lib/task/control-authority.ts +40 -26
- package/lib/task/control-recovery.ts +2 -3
- package/lib/task/finalization.ts +15 -2
- package/lib/task/lifecycle.ts +135 -33
- package/lib/task/short-id.ts +5 -2
- package/package.json +1 -1
- package/templates/.agents/rules/lifecycle-orchestration.en.md +3 -4
- package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +3 -4
- package/templates/.agents/rules/task-short-id.en.md +18 -12
- package/templates/.agents/rules/task-short-id.zh-CN.md +12 -7
- package/templates/.agents/skills/complete-task/SKILL.en.md +10 -2
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +10 -2
- package/templates/.agents/skills/create-pr/reference/change-report.en.md +12 -2
- package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +12 -2
- package/templates/.agents/skills/create-release-note/SKILL.en.md +3 -1
- package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +3 -1
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +5 -5
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.en.md +13 -2
- package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.zh-CN.md +13 -2
- package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +2 -0
- package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +2 -0
- package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/watch-pr/SKILL.en.md +19 -2
- package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +20 -3
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +14 -0
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +15 -1
- package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-client.js +0 -130
- package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-context.js +0 -127
- package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-operation.js +0 -131
- package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-registration.js +0 -275
- package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller-cli.js +0 -106
- package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +0 -345
- package/dist/lib/runtime/agent-runtime.js +0 -33
- package/dist/lib/sandbox/control/agent-operation.js +0 -34
- package/dist/lib/task/runtime-paths.js +0 -10
- package/lib/agent-clients/adapters/codex-lifecycle/controller-client.ts +0 -158
- package/lib/agent-clients/adapters/codex-lifecycle/controller-context.ts +0 -163
- package/lib/agent-clients/adapters/codex-lifecycle/controller-operation.ts +0 -151
- package/lib/agent-clients/adapters/codex-lifecycle/controller-registration.ts +0 -352
- package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller-cli.ts +0 -109
- package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +0 -437
- package/lib/runtime/agent-runtime.ts +0 -41
- package/lib/sandbox/control/agent-operation.ts +0 -49
- 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 =
|
|
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 =
|
|
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
|
-
|
|
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,
|
|
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.
|
|
4
|
-
"sourceInputHash": "
|
|
5
|
-
"internalExecutableBuildHash": "
|
|
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
|
-
&& (
|
|
207
|
-
|
|
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: '
|
|
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 =
|
|
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
|
|
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
|
|
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'
|
|
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 {
|
|
11
|
-
import {
|
|
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
|
-
|
|
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
|
-
|
|
100
|
-
|
|
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',
|
|
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',
|