@fitlab-ai/agent-infra 0.9.7 → 0.9.9
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/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/bin/internal-cli.js +11 -1
- package/dist/lib/agent-clients/adapters/claude-code.js +3 -3
- package/dist/lib/agent-clients/adapters/codex-lifecycle/app-server.js +60 -22
- package/dist/lib/agent-clients/adapters/codex-lifecycle/build-identity.js +8 -1
- package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +175 -11
- package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-context.js +147 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/evidence.js +14 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +86 -69
- package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +2 -1
- package/dist/lib/decide.js +68 -14
- package/dist/lib/git/workflow.js +10 -1
- package/dist/lib/init.js +1 -1
- package/dist/lib/internal/codex-lifecycle.js +19 -5
- package/dist/lib/internal/codex-sandbox-controller.js +6 -13
- package/dist/lib/internal/git-workflow.js +5 -7
- package/dist/lib/internal/platform-pr.js +20 -7
- package/dist/lib/internal/release-workflow.js +3 -4
- package/dist/lib/internal/sandbox-control.js +79 -2
- package/dist/lib/internal/task-activity.js +57 -7
- package/dist/lib/internal/task-event.js +57 -4
- package/dist/lib/internal/task-finalization.js +54 -0
- package/dist/lib/internal/task-ledger.js +59 -5
- package/dist/lib/internal/task-lifecycle.js +59 -8
- package/dist/lib/internal/task-orchestration.js +161 -288
- package/dist/lib/internal/task-override.js +131 -0
- package/dist/lib/internal/task-review.js +64 -6
- package/dist/lib/internal/task-verify.js +65 -3
- package/dist/lib/internal/task-warning.js +57 -4
- package/dist/lib/platform/pr-summary.js +53 -13
- package/dist/lib/platform/pull-requests.js +205 -41
- package/dist/lib/runtime/agent-runtime.js +56 -0
- package/dist/lib/sandbox/commands/create.js +389 -311
- package/dist/lib/sandbox/commands/rm.js +316 -55
- package/dist/lib/sandbox/control/client.js +190 -6
- package/dist/lib/sandbox/control/container-identity.js +1 -1
- package/dist/lib/sandbox/control/controller-registration.js +273 -0
- package/dist/lib/sandbox/control/executor.js +145 -5
- package/dist/lib/sandbox/control/lifecycle.js +395 -73
- package/dist/lib/sandbox/control/protocol.js +67 -6
- package/dist/lib/sandbox/control/state.js +27 -11
- package/dist/lib/sandbox/engine.js +14 -7
- package/dist/lib/sandbox/index.js +5 -3
- package/dist/lib/sandbox/managed-fs.js +5 -0
- package/dist/lib/sandbox/mounts.js +20 -5
- package/dist/lib/sandbox/recovery.js +78 -10
- package/dist/lib/sandbox/task-resolver.js +50 -6
- package/dist/lib/sandbox/workspace-identity.js +27 -0
- package/dist/lib/sandbox/workspace-view.js +62 -40
- package/dist/lib/sandbox/worktree-safety.js +215 -2
- package/dist/lib/server/process-state.js +51 -1
- package/dist/lib/task/activity-intent.js +4 -1
- package/dist/lib/task/codex-orchestration.js +158 -33
- package/dist/lib/task/commands/status.js +59 -1
- package/dist/lib/task/commit-operation.js +335 -0
- package/dist/lib/task/commit-policy.js +18 -0
- package/dist/lib/task/decision-intents.js +4 -2
- package/dist/lib/task/delegation-receipts.js +279 -17
- package/dist/lib/task/events.js +9 -5
- package/dist/lib/task/finalization.js +567 -0
- package/dist/lib/task/guard-override.js +31 -0
- package/dist/lib/task/human-override.js +1240 -0
- package/dist/lib/task/ledger-intents.js +6 -3
- package/dist/lib/task/lifecycle.js +69 -14
- package/dist/lib/task/operation-outcome.js +33 -0
- package/dist/lib/task/orchestration.js +199 -719
- package/dist/lib/task/review-finalization.js +3 -2
- package/dist/lib/task/short-id.js +59 -1
- package/dist/lib/task/task-execution-lock.js +6 -1
- package/dist/lib/task/verification-engine.js +138 -59
- package/dist/lib/task/verification.js +15 -2
- package/dist/lib/task/workflow-warning-intents.js +54 -3
- package/dist/lib/task/write.js +2 -1
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-clients/adapters/claude-code.ts +3 -3
- package/lib/agent-clients/adapters/codex-lifecycle/app-server.ts +63 -20
- package/lib/agent-clients/adapters/codex-lifecycle/build-identity.ts +12 -0
- package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +278 -13
- package/lib/agent-clients/adapters/codex-lifecycle/controller-context.ts +180 -0
- package/lib/agent-clients/adapters/codex-lifecycle/evidence.ts +19 -0
- package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +9 -1
- package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +112 -93
- package/lib/agent-clients/adapters/codex-lifecycle/store.ts +2 -1
- package/lib/decide.ts +59 -14
- package/lib/git/workflow.ts +11 -2
- package/lib/init.ts +1 -1
- package/lib/internal/codex-lifecycle.ts +18 -5
- package/lib/internal/codex-sandbox-controller.ts +6 -12
- package/lib/internal/git-workflow.ts +5 -6
- package/lib/internal/platform-pr.ts +14 -7
- package/lib/internal/release-workflow.ts +3 -4
- package/lib/internal/sandbox-control.ts +92 -2
- package/lib/internal/task-activity.ts +46 -6
- package/lib/internal/task-event.ts +53 -4
- package/lib/internal/task-finalization.ts +68 -0
- package/lib/internal/task-ledger.ts +48 -4
- package/lib/internal/task-lifecycle.ts +56 -12
- package/lib/internal/task-orchestration.ts +32 -107
- package/lib/internal/task-override.ts +138 -0
- package/lib/internal/task-review.ts +51 -6
- package/lib/internal/task-verify.ts +52 -3
- package/lib/internal/task-warning.ts +44 -3
- package/lib/platform/pr-summary.ts +65 -14
- package/lib/platform/pull-requests.ts +211 -41
- package/lib/runtime/agent-runtime.ts +76 -0
- package/lib/sandbox/commands/create.ts +88 -4
- package/lib/sandbox/commands/rm.ts +413 -61
- package/lib/sandbox/config.ts +2 -1
- package/lib/sandbox/control/client.ts +255 -6
- package/lib/sandbox/control/container-identity.ts +3 -3
- package/lib/sandbox/control/controller-registration.ts +347 -0
- package/lib/sandbox/control/executor.ts +172 -6
- package/lib/sandbox/control/lifecycle.ts +465 -77
- package/lib/sandbox/control/protocol.ts +85 -9
- package/lib/sandbox/control/state.ts +35 -18
- package/lib/sandbox/engine.ts +20 -9
- package/lib/sandbox/index.ts +5 -3
- package/lib/sandbox/managed-fs.ts +5 -0
- package/lib/sandbox/mounts.ts +23 -5
- package/lib/sandbox/recovery.ts +90 -9
- package/lib/sandbox/task-resolver.ts +58 -7
- package/lib/sandbox/workspace-identity.ts +50 -2
- package/lib/sandbox/workspace-view.ts +73 -43
- package/lib/sandbox/worktree-safety.ts +236 -2
- package/lib/server/process-state.ts +48 -1
- package/lib/task/activity-intent.ts +4 -2
- package/lib/task/codex-orchestration.ts +167 -33
- package/lib/task/commands/status.ts +78 -1
- package/lib/task/commit-operation.ts +430 -0
- package/lib/task/commit-policy.ts +30 -0
- package/lib/task/decision-intents.ts +4 -2
- package/lib/task/delegation-receipts.ts +304 -19
- package/lib/task/events.ts +10 -5
- package/lib/task/finalization.ts +719 -0
- package/lib/task/guard-override.ts +50 -0
- package/lib/task/human-override.ts +1503 -0
- package/lib/task/ledger-intents.ts +6 -3
- package/lib/task/lifecycle.ts +71 -14
- package/lib/task/operation-outcome.ts +48 -0
- package/lib/task/orchestration.ts +239 -922
- package/lib/task/review-finalization.ts +6 -2
- package/lib/task/short-id.ts +65 -1
- package/lib/task/task-execution-lock.ts +10 -1
- package/lib/task/verification-engine.ts +145 -64
- package/lib/task/verification.ts +18 -3
- package/lib/task/workflow-warning-intents.ts +61 -4
- package/lib/task/write.ts +4 -1
- package/package.json +2 -2
- package/templates/.agents/QUICKSTART.en.md +2 -2
- package/templates/.agents/QUICKSTART.zh-CN.md +2 -2
- package/templates/.agents/hooks/lifecycle-delegation.js +9 -24
- package/templates/.agents/rules/lifecycle-orchestration.en.md +4 -3
- package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +4 -3
- package/templates/.agents/rules/task-management.en.md +2 -2
- package/templates/.agents/rules/task-management.zh-CN.md +2 -2
- package/templates/.agents/skills/block-task/SKILL.en.md +2 -4
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -4
- package/templates/.agents/skills/cancel-task/SKILL.en.md +3 -3
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/close-codescan/SKILL.en.md +6 -2
- package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +6 -2
- package/templates/.agents/skills/close-dependabot/SKILL.en.md +6 -2
- package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +6 -2
- package/templates/.agents/skills/commit/SKILL.en.md +41 -125
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +52 -123
- package/templates/.agents/skills/commit/reference/commit-message.en.md +1 -1
- package/templates/.agents/skills/commit/reference/commit-message.zh-CN.md +1 -1
- package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +14 -43
- package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +14 -49
- package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +1 -1
- package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +1 -1
- package/templates/.agents/skills/commit/reference/task-status-update.en.md +2 -35
- package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +2 -35
- package/templates/.agents/skills/complete-task/SKILL.en.md +31 -38
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +31 -38
- package/templates/.agents/skills/complete-task/config/verify.en.json +1 -0
- package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +1 -0
- package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +1 -1
- package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +1 -1
- package/templates/.agents/skills/create-pr/SKILL.en.md +6 -4
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +6 -4
- package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +2 -2
- package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +2 -2
- package/templates/.agents/skills/run-task/SKILL.en.md +5 -5
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/run-task/reference/host-validation.en.md +3 -1
- package/templates/.agents/skills/run-task/reference/host-validation.zh-CN.md +3 -1
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +1 -1
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +1 -1
- package/templates/.codex/hooks.json +4 -4
- package/dist/lib/task/commit-finalization.js +0 -305
- package/dist/lib/task/commit-intent.js +0 -165
- package/lib/task/commit-finalization.ts +0 -419
- package/lib/task/commit-intent.ts +0 -230
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
5
|
+
import { getProcessIdentityState, type ProcessIdentity } from '../../../server/process-state.ts';
|
|
6
|
+
import type { CodexControllerLeaseProofV1, CodexControllerLeaseV1 } from '../../../sandbox/control/controller-registration.ts';
|
|
7
|
+
import { computeLifecycleBuildIdentity, type LifecycleBuildIdentity } from './build-identity.ts';
|
|
8
|
+
|
|
9
|
+
const HEX_256 = /^[a-f0-9]{64}$/u;
|
|
10
|
+
|
|
11
|
+
export type CodexSandboxControllerContextV2 = Readonly<{
|
|
12
|
+
version: 2;
|
|
13
|
+
taskId: string;
|
|
14
|
+
controlGeneration: string;
|
|
15
|
+
controllerInstanceDigest: string;
|
|
16
|
+
controllerProcess: ProcessIdentity;
|
|
17
|
+
controllerLease: Readonly<{ version: 1; leaseId: string; leaseSecret: string }>;
|
|
18
|
+
issuedAt: number;
|
|
19
|
+
expiresAt: number;
|
|
20
|
+
buildIdentity: LifecycleBuildIdentity;
|
|
21
|
+
hookDefinitionHash: string;
|
|
22
|
+
lifecycleProfilesHash: string;
|
|
23
|
+
}>;
|
|
24
|
+
|
|
25
|
+
function exactKeys(value: Record<string, unknown>, keys: readonly string[]): boolean {
|
|
26
|
+
return Object.keys(value).sort().join(',') === [...keys].sort().join(',');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function digestProfiles(repoRoot: string): string {
|
|
30
|
+
const files = [
|
|
31
|
+
path.join(repoRoot, '.codex', 'agents', 'agent-infra-lifecycle-executor.toml'),
|
|
32
|
+
path.join(repoRoot, '.codex', 'agents', 'agent-infra-lifecycle-reviewer.toml')
|
|
33
|
+
];
|
|
34
|
+
const hash = crypto.createHash('sha256');
|
|
35
|
+
for (const file of files.sort()) {
|
|
36
|
+
const stat = fs.lstatSync(file);
|
|
37
|
+
if (!stat.isFile() || stat.isSymbolicLink()) throw new Error('CODEX_SANDBOX_CONTROLLER_BUNDLE_MISMATCH');
|
|
38
|
+
hash.update(path.basename(file));
|
|
39
|
+
hash.update('\0');
|
|
40
|
+
hash.update(fs.readFileSync(file));
|
|
41
|
+
hash.update('\0');
|
|
42
|
+
}
|
|
43
|
+
return hash.digest('hex');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function validProcess(value: unknown): value is ProcessIdentity {
|
|
47
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
|
|
48
|
+
const identity = value as Record<string, unknown>;
|
|
49
|
+
return exactKeys(identity, ['pid', 'startTime'])
|
|
50
|
+
&& Number.isSafeInteger(identity.pid) && (identity.pid as number) > 0
|
|
51
|
+
&& Number.isSafeInteger(identity.startTime) && (identity.startTime as number) >= 0;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function validBuildIdentity(value: unknown): value is LifecycleBuildIdentity {
|
|
55
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
|
|
56
|
+
const identity = value as Record<string, unknown>;
|
|
57
|
+
return exactKeys(identity, [
|
|
58
|
+
'internalExecutableBuildHash', 'lifecycleContractHash', 'packageVersion', 'protocolVersion'
|
|
59
|
+
])
|
|
60
|
+
&& identity.protocolVersion === 3
|
|
61
|
+
&& typeof identity.packageVersion === 'string' && identity.packageVersion.length > 0
|
|
62
|
+
&& typeof identity.internalExecutableBuildHash === 'string' && HEX_256.test(identity.internalExecutableBuildHash)
|
|
63
|
+
&& typeof identity.lifecycleContractHash === 'string' && HEX_256.test(identity.lifecycleContractHash);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function validateContext(value: unknown): CodexSandboxControllerContextV2 {
|
|
67
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
68
|
+
throw new Error('CODEX_SANDBOX_CONTROLLER_CONTEXT_INVALID');
|
|
69
|
+
}
|
|
70
|
+
const context = value as Record<string, unknown>;
|
|
71
|
+
const lease = context.controllerLease as Record<string, unknown> | null;
|
|
72
|
+
if (!exactKeys(context, [
|
|
73
|
+
'buildIdentity', 'controlGeneration', 'controllerInstanceDigest', 'controllerLease',
|
|
74
|
+
'controllerProcess', 'expiresAt', 'hookDefinitionHash', 'issuedAt',
|
|
75
|
+
'lifecycleProfilesHash', 'taskId', 'version'
|
|
76
|
+
])
|
|
77
|
+
|| context.version !== 2
|
|
78
|
+
|| typeof context.taskId !== 'string' || context.taskId.length === 0
|
|
79
|
+
|| typeof context.controlGeneration !== 'string' || context.controlGeneration.length === 0
|
|
80
|
+
|| typeof context.controllerInstanceDigest !== 'string' || !HEX_256.test(context.controllerInstanceDigest)
|
|
81
|
+
|| !validProcess(context.controllerProcess)
|
|
82
|
+
|| !lease || !exactKeys(lease, ['leaseId', 'leaseSecret', 'version'])
|
|
83
|
+
|| lease.version !== 1
|
|
84
|
+
|| typeof lease.leaseId !== 'string' || !HEX_256.test(lease.leaseId)
|
|
85
|
+
|| typeof lease.leaseSecret !== 'string' || !HEX_256.test(lease.leaseSecret)
|
|
86
|
+
|| !Number.isSafeInteger(context.issuedAt)
|
|
87
|
+
|| !Number.isSafeInteger(context.expiresAt)
|
|
88
|
+
|| (context.expiresAt as number) <= (context.issuedAt as number)
|
|
89
|
+
|| !validBuildIdentity(context.buildIdentity)
|
|
90
|
+
|| typeof context.hookDefinitionHash !== 'string' || !HEX_256.test(context.hookDefinitionHash)
|
|
91
|
+
|| typeof context.lifecycleProfilesHash !== 'string' || !HEX_256.test(context.lifecycleProfilesHash)) {
|
|
92
|
+
throw new Error('CODEX_SANDBOX_CONTROLLER_CONTEXT_INVALID');
|
|
93
|
+
}
|
|
94
|
+
return context as unknown as CodexSandboxControllerContextV2;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function contextFromControllerLease(
|
|
98
|
+
lease: CodexControllerLeaseV1,
|
|
99
|
+
hashes: Readonly<{ hookDefinitionHash: string; lifecycleProfilesHash: string }>
|
|
100
|
+
): CodexSandboxControllerContextV2 {
|
|
101
|
+
return Object.freeze({
|
|
102
|
+
version: 2,
|
|
103
|
+
taskId: lease.taskId,
|
|
104
|
+
controlGeneration: lease.controlGeneration,
|
|
105
|
+
controllerInstanceDigest: lease.controllerInstanceDigest,
|
|
106
|
+
controllerProcess: lease.controllerProcess,
|
|
107
|
+
controllerLease: Object.freeze({ version: 1, leaseId: lease.leaseId, leaseSecret: lease.leaseSecret }),
|
|
108
|
+
issuedAt: lease.issuedAt,
|
|
109
|
+
expiresAt: lease.expiresAt,
|
|
110
|
+
buildIdentity: lease.buildIdentity,
|
|
111
|
+
hookDefinitionHash: hashes.hookDefinitionHash,
|
|
112
|
+
lifecycleProfilesHash: hashes.lifecycleProfilesHash
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function writeCodexSandboxControllerContext(
|
|
117
|
+
contextPath: string,
|
|
118
|
+
context: CodexSandboxControllerContextV2
|
|
119
|
+
): void {
|
|
120
|
+
validateContext(context);
|
|
121
|
+
fs.mkdirSync(path.dirname(contextPath), { recursive: true, mode: 0o700 });
|
|
122
|
+
const temporary = path.join(path.dirname(contextPath), `.${path.basename(contextPath)}.${process.pid}.${crypto.randomUUID()}.tmp`);
|
|
123
|
+
try {
|
|
124
|
+
fs.writeFileSync(temporary, `${JSON.stringify(context, null, 2)}\n`, { mode: 0o600, flag: 'wx' });
|
|
125
|
+
fs.chmodSync(temporary, 0o600);
|
|
126
|
+
fs.renameSync(temporary, contextPath);
|
|
127
|
+
} finally {
|
|
128
|
+
fs.rmSync(temporary, { force: true });
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function verifyCodexSandboxControllerContext(
|
|
133
|
+
contextPath: string,
|
|
134
|
+
options: Readonly<{
|
|
135
|
+
repoRoot?: string;
|
|
136
|
+
now?: number;
|
|
137
|
+
generation?: string;
|
|
138
|
+
probeProcess?: (identity: ProcessIdentity) => 'alive' | 'dead' | 'unknown';
|
|
139
|
+
}> = {}
|
|
140
|
+
): CodexSandboxControllerContextV2 {
|
|
141
|
+
let stat: fs.Stats;
|
|
142
|
+
try {
|
|
143
|
+
stat = fs.lstatSync(contextPath);
|
|
144
|
+
} catch {
|
|
145
|
+
throw new Error('CODEX_SANDBOX_CONTROLLER_CONTEXT_INVALID');
|
|
146
|
+
}
|
|
147
|
+
if (!stat.isFile() || stat.isSymbolicLink()
|
|
148
|
+
|| (process.platform !== 'win32' && (stat.mode & 0o077) !== 0)) {
|
|
149
|
+
throw new Error('CODEX_SANDBOX_CONTROLLER_CONTEXT_INVALID');
|
|
150
|
+
}
|
|
151
|
+
let context: CodexSandboxControllerContextV2;
|
|
152
|
+
try {
|
|
153
|
+
context = validateContext(JSON.parse(fs.readFileSync(contextPath, 'utf8')));
|
|
154
|
+
} catch {
|
|
155
|
+
throw new Error('CODEX_SANDBOX_CONTROLLER_CONTEXT_INVALID');
|
|
156
|
+
}
|
|
157
|
+
const repoRoot = options.repoRoot ?? process.cwd();
|
|
158
|
+
const generation = options.generation ?? process.env.AGENT_INFRA_CONTROL_GENERATION;
|
|
159
|
+
const state = (options.probeProcess ?? getProcessIdentityState)(context.controllerProcess);
|
|
160
|
+
if (context.controlGeneration !== generation
|
|
161
|
+
|| context.expiresAt <= (options.now ?? Date.now())
|
|
162
|
+
|| state !== 'alive') {
|
|
163
|
+
throw new Error('CODEX_SANDBOX_CONTROLLER_CONTEXT_INVALID');
|
|
164
|
+
}
|
|
165
|
+
if (JSON.stringify(computeLifecycleBuildIdentity(repoRoot)) !== JSON.stringify(context.buildIdentity)
|
|
166
|
+
|| crypto.createHash('sha256').update(fs.readFileSync(path.join(repoRoot, '.codex', 'hooks.json'))).digest('hex') !== context.hookDefinitionHash
|
|
167
|
+
|| digestProfiles(repoRoot) !== context.lifecycleProfilesHash) {
|
|
168
|
+
throw new Error('CODEX_SANDBOX_CONTROLLER_BUNDLE_MISMATCH');
|
|
169
|
+
}
|
|
170
|
+
return Object.freeze(context);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export function controllerProofFromContext(context: CodexSandboxControllerContextV2): CodexControllerLeaseProofV1 {
|
|
174
|
+
return Object.freeze({
|
|
175
|
+
version: 1,
|
|
176
|
+
leaseId: context.controllerLease.leaseId,
|
|
177
|
+
leaseSecret: context.controllerLease.leaseSecret,
|
|
178
|
+
controllerProcess: context.controllerProcess
|
|
179
|
+
});
|
|
180
|
+
}
|
|
@@ -147,6 +147,16 @@ function same(left: unknown, right: unknown): boolean {
|
|
|
147
147
|
return JSON.stringify(left) === JSON.stringify(right);
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
+
function sameChildCorrelation(
|
|
151
|
+
left: Extract<CodexLifecycleEvent, { type: 'hook-child' }>,
|
|
152
|
+
right: Extract<CodexLifecycleEvent, { type: 'hook-child' }>
|
|
153
|
+
): boolean {
|
|
154
|
+
return left.sessionId === right.sessionId
|
|
155
|
+
&& left.childThreadId === right.childThreadId
|
|
156
|
+
&& left.parentThreadId === right.parentThreadId
|
|
157
|
+
&& left.nativeAgent === right.nativeAgent;
|
|
158
|
+
}
|
|
159
|
+
|
|
150
160
|
function requiredStrings(event: CodexLifecycleEvent): boolean {
|
|
151
161
|
return Object.entries(event).every(([key, value]) =>
|
|
152
162
|
key === 'forkedFromId'
|
|
@@ -286,6 +296,15 @@ function reduceCodexLifecycleEvent(
|
|
|
286
296
|
const existing = state[key];
|
|
287
297
|
if (existing) {
|
|
288
298
|
if (same(existing, event)) return state;
|
|
299
|
+
if (
|
|
300
|
+
existing.type === 'hook-child'
|
|
301
|
+
&& event.type === 'hook-child'
|
|
302
|
+
&& sameChildCorrelation(existing, event)
|
|
303
|
+
&& (existing.source === 'parent-rollout') !== (event.source === 'parent-rollout')
|
|
304
|
+
) {
|
|
305
|
+
if (existing.source !== 'parent-rollout') return state;
|
|
306
|
+
return derive(Object.freeze({ ...state, child: Object.freeze({ ...event }) }));
|
|
307
|
+
}
|
|
289
308
|
return invalid(state, 'CODEX_EVIDENCE_REPLAY_CONFLICT', `conflicting '${event.type}' event`);
|
|
290
309
|
}
|
|
291
310
|
return derive(Object.freeze({ ...state, [key]: Object.freeze({ ...event }) }));
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
"bin/internal-cli.ts",
|
|
4
4
|
"lib/internal/codex-lifecycle.ts",
|
|
5
5
|
"lib/internal/codex-sandbox-controller.ts",
|
|
6
|
+
"lib/internal/sandbox-control.ts",
|
|
6
7
|
"lib/internal/task-orchestration.ts",
|
|
7
8
|
"lib/task/codex-orchestration.ts",
|
|
8
9
|
"lib/task/delegation-receipts.ts",
|
|
@@ -10,10 +11,17 @@
|
|
|
10
11
|
"lib/agent-clients/adapters/codex-lifecycle/build-identity.ts",
|
|
11
12
|
"lib/agent-clients/adapters/codex-lifecycle/capability-store.ts",
|
|
12
13
|
"lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts",
|
|
14
|
+
"lib/agent-clients/adapters/codex-lifecycle/controller-context.ts",
|
|
13
15
|
"lib/agent-clients/adapters/codex-lifecycle/app-server.ts",
|
|
14
16
|
"lib/agent-clients/adapters/codex-lifecycle/evidence.ts",
|
|
15
17
|
"lib/agent-clients/adapters/codex-lifecycle/store.ts",
|
|
16
|
-
"lib/agent-clients/adapters/codex-lifecycle/manifest-files.json"
|
|
18
|
+
"lib/agent-clients/adapters/codex-lifecycle/manifest-files.json",
|
|
19
|
+
"lib/sandbox/control/protocol.ts",
|
|
20
|
+
"lib/sandbox/control/client.ts",
|
|
21
|
+
"lib/sandbox/control/executor.ts",
|
|
22
|
+
"lib/sandbox/control/controller-registration.ts",
|
|
23
|
+
"lib/server/process-state.ts",
|
|
24
|
+
"lib/sandbox/shell.ts"
|
|
17
25
|
],
|
|
18
26
|
"contractFiles": [
|
|
19
27
|
".codex/hooks.json",
|
|
@@ -6,36 +6,32 @@ import path from 'node:path';
|
|
|
6
6
|
import semver from 'semver';
|
|
7
7
|
import * as toml from 'smol-toml';
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
requestCodexControllerClose,
|
|
11
|
+
requestCodexControllerOpen,
|
|
12
|
+
requestCodexControllerVerify,
|
|
13
|
+
requestSandboxControl
|
|
14
|
+
} from '../../../sandbox/control/client.ts';
|
|
10
15
|
import { readSandboxControlStatus } from '../../../sandbox/control/state.ts';
|
|
11
|
-
import { getProcessStartTime,
|
|
16
|
+
import { getProcessStartTime, type ProcessIdentity } from '../../../server/process-state.ts';
|
|
12
17
|
import { computeLifecycleBuildIdentity } from './build-identity.ts';
|
|
13
|
-
import
|
|
18
|
+
import {
|
|
19
|
+
contextFromControllerLease,
|
|
20
|
+
controllerProofFromContext,
|
|
21
|
+
verifyCodexSandboxControllerContext as verifyContextFile,
|
|
22
|
+
writeCodexSandboxControllerContext,
|
|
23
|
+
type CodexSandboxControllerContextV2
|
|
24
|
+
} from './controller-context.ts';
|
|
14
25
|
|
|
15
26
|
type ControllerControl = Readonly<{
|
|
16
27
|
token: string;
|
|
17
28
|
generation: string;
|
|
18
29
|
channelDir: string;
|
|
19
30
|
statusDir: string;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
type CodexSandboxControllerContext = Readonly<{
|
|
23
|
-
version: 1;
|
|
24
|
-
taskId: string;
|
|
25
|
-
controlGeneration: string;
|
|
26
|
-
controllerInstanceDigest: string;
|
|
27
|
-
parentPid: number;
|
|
28
|
-
parentStartTime: number;
|
|
29
|
-
issuedAt: number;
|
|
30
|
-
expiresAt: number;
|
|
31
|
-
buildIdentity: LifecycleBuildIdentity;
|
|
32
|
-
hookDefinitionHash: string;
|
|
33
|
-
lifecycleProfilesHash: string;
|
|
31
|
+
runtimeDir: string;
|
|
34
32
|
}>;
|
|
35
33
|
|
|
36
34
|
type ControllerInput = Readonly<{
|
|
37
|
-
taskId: string;
|
|
38
|
-
taskRef: string;
|
|
39
35
|
executorModel?: string;
|
|
40
36
|
executorReasoningEffort?: string;
|
|
41
37
|
reviewerModel?: string;
|
|
@@ -50,6 +46,8 @@ type ControllerOptions = Readonly<{
|
|
|
50
46
|
now?: () => number;
|
|
51
47
|
codexVersion?: () => string;
|
|
52
48
|
verifyTaskBinding?: (taskId: string, control: ControllerControl) => void;
|
|
49
|
+
openController?: typeof requestCodexControllerOpen;
|
|
50
|
+
closeController?: typeof requestCodexControllerClose;
|
|
53
51
|
environment?: NodeJS.ProcessEnv;
|
|
54
52
|
}>;
|
|
55
53
|
|
|
@@ -59,11 +57,18 @@ type PreparedCodexSandboxController = Readonly<{
|
|
|
59
57
|
env: NodeJS.ProcessEnv;
|
|
60
58
|
home: string;
|
|
61
59
|
contextPath: string;
|
|
62
|
-
context:
|
|
60
|
+
context: CodexSandboxControllerContextV2;
|
|
63
61
|
cleanup: () => void;
|
|
64
62
|
}>;
|
|
65
63
|
|
|
66
|
-
|
|
64
|
+
type VerifyContextOptions = Readonly<{
|
|
65
|
+
repoRoot?: string;
|
|
66
|
+
now?: number;
|
|
67
|
+
generation?: string;
|
|
68
|
+
probeProcess?: (identity: ProcessIdentity) => 'alive' | 'dead' | 'unknown';
|
|
69
|
+
control?: ControllerControl;
|
|
70
|
+
requestControllerVerify?: typeof requestCodexControllerVerify;
|
|
71
|
+
}>;
|
|
67
72
|
|
|
68
73
|
function digestFiles(files: readonly string[]): string {
|
|
69
74
|
const hash = crypto.createHash('sha256');
|
|
@@ -95,13 +100,38 @@ function controlFromEnvironment(): ControllerControl {
|
|
|
95
100
|
const generation = process.env.AGENT_INFRA_CONTROL_GENERATION;
|
|
96
101
|
const channelDir = process.env.AGENT_INFRA_CONTROL_DIR;
|
|
97
102
|
const statusDir = process.env.AGENT_INFRA_CONTROL_STATUS_DIR;
|
|
98
|
-
|
|
103
|
+
const runtimeDir = process.env.AGENT_INFRA_RUNTIME_DIR;
|
|
104
|
+
if (!token || !generation || !channelDir || !statusDir || !runtimeDir) {
|
|
99
105
|
throw new Error('CODEX_SANDBOX_CONTROLLER_CONTROL_MISSING');
|
|
100
106
|
}
|
|
101
|
-
return { token, generation, channelDir, statusDir };
|
|
107
|
+
return { token, generation, channelDir, statusDir, runtimeDir };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function verifyRuntime(runtimeDir: string): void {
|
|
111
|
+
let stat: fs.Stats;
|
|
112
|
+
try {
|
|
113
|
+
stat = fs.lstatSync(runtimeDir);
|
|
114
|
+
} catch {
|
|
115
|
+
throw new Error('CODEX_SANDBOX_CONTROLLER_RUNTIME_UNAVAILABLE');
|
|
116
|
+
}
|
|
117
|
+
if (!stat.isDirectory() || stat.isSymbolicLink()) {
|
|
118
|
+
throw new Error('CODEX_SANDBOX_CONTROLLER_RUNTIME_UNAVAILABLE');
|
|
119
|
+
}
|
|
120
|
+
const probe = path.join(runtimeDir, `.controller-${process.pid}-${crypto.randomUUID()}.probe`);
|
|
121
|
+
try {
|
|
122
|
+
fs.writeFileSync(probe, 'runtime-probe\n', { encoding: 'utf8', flag: 'wx', mode: 0o600 });
|
|
123
|
+
if (fs.readFileSync(probe, 'utf8') !== 'runtime-probe\n') {
|
|
124
|
+
throw new Error('probe mismatch');
|
|
125
|
+
}
|
|
126
|
+
} catch {
|
|
127
|
+
throw new Error('CODEX_SANDBOX_CONTROLLER_RUNTIME_UNAVAILABLE');
|
|
128
|
+
} finally {
|
|
129
|
+
fs.rmSync(probe, { force: true });
|
|
130
|
+
}
|
|
102
131
|
}
|
|
103
132
|
|
|
104
133
|
function verifyControl(taskId: string, control: ControllerControl): void {
|
|
134
|
+
verifyRuntime(control.runtimeDir);
|
|
105
135
|
let status: ReturnType<typeof readSandboxControlStatus> | null = null;
|
|
106
136
|
let lastError: unknown = null;
|
|
107
137
|
for (let attempt = 0; attempt < 5; attempt += 1) {
|
|
@@ -136,6 +166,30 @@ function verifyControl(taskId: string, control: ControllerControl): void {
|
|
|
136
166
|
if (payload.taskId !== taskId) throw new Error('CODEX_SANDBOX_CONTROLLER_TASK_BINDING_INVALID');
|
|
137
167
|
}
|
|
138
168
|
|
|
169
|
+
function verifyCodexSandboxControllerContext(
|
|
170
|
+
contextPath: string,
|
|
171
|
+
options: VerifyContextOptions = {}
|
|
172
|
+
): CodexSandboxControllerContextV2 {
|
|
173
|
+
const control = options.control ?? controlFromEnvironment();
|
|
174
|
+
const context = verifyContextFile(contextPath, {
|
|
175
|
+
repoRoot: options.repoRoot,
|
|
176
|
+
now: options.now,
|
|
177
|
+
generation: control.generation,
|
|
178
|
+
probeProcess: options.probeProcess
|
|
179
|
+
});
|
|
180
|
+
const verified = (options.requestControllerVerify ?? requestCodexControllerVerify)({
|
|
181
|
+
controllerProof: controllerProofFromContext(context),
|
|
182
|
+
...control,
|
|
183
|
+
timeoutMs: 30_000
|
|
184
|
+
});
|
|
185
|
+
if (verified.binding.taskId !== context.taskId
|
|
186
|
+
|| verified.binding.controlGeneration !== context.controlGeneration
|
|
187
|
+
|| verified.binding.controllerInstanceDigest !== context.controllerInstanceDigest) {
|
|
188
|
+
throw new Error('CODEX_SANDBOX_CONTROLLER_CONTEXT_INVALID');
|
|
189
|
+
}
|
|
190
|
+
return context;
|
|
191
|
+
}
|
|
192
|
+
|
|
139
193
|
function detectedCodexVersion(): string {
|
|
140
194
|
const result = spawnSync('codex', ['--version'], { encoding: 'utf8' });
|
|
141
195
|
const version = /codex-cli\s+(\d+\.\d+\.\d+)/u.exec(result.stdout ?? '')?.[1];
|
|
@@ -214,6 +268,7 @@ function isolatedEnvironment(
|
|
|
214
268
|
AGENT_INFRA_CONTROL_GENERATION: control.generation,
|
|
215
269
|
AGENT_INFRA_CONTROL_DIR: control.channelDir,
|
|
216
270
|
AGENT_INFRA_CONTROL_STATUS_DIR: control.statusDir,
|
|
271
|
+
AGENT_INFRA_RUNTIME_DIR: control.runtimeDir,
|
|
217
272
|
AGENT_INFRA_CODEX_CONTROLLER_CONTEXT: contextPath
|
|
218
273
|
};
|
|
219
274
|
}
|
|
@@ -229,7 +284,6 @@ function prepareCodexSandboxController(
|
|
|
229
284
|
const repoRoot = path.resolve(options.repoRoot ?? process.cwd());
|
|
230
285
|
const sourceHome = path.resolve(options.codexHome ?? process.env.CODEX_HOME ?? path.join(os.homedir(), '.codex'));
|
|
231
286
|
const control = options.control ?? controlFromEnvironment();
|
|
232
|
-
(options.verifyTaskBinding ?? verifyControl)(input.taskId, control);
|
|
233
287
|
const version = (options.codexVersion ?? detectedCodexVersion)();
|
|
234
288
|
if (!semver.gte(version, '0.147.0')) throw new Error('CODEX_SANDBOX_CONTROLLER_CODEX_UNSUPPORTED');
|
|
235
289
|
|
|
@@ -238,46 +292,33 @@ function prepareCodexSandboxController(
|
|
|
238
292
|
fs.mkdirSync(runtimeRoot, { recursive: true, mode: 0o700 });
|
|
239
293
|
fs.chmodSync(runtimeRoot, 0o700);
|
|
240
294
|
const key = crypto.createHash('sha256')
|
|
241
|
-
.update(`${
|
|
295
|
+
.update(`${process.pid}\0${control.generation}`)
|
|
242
296
|
.digest('hex');
|
|
243
|
-
const leasePath = path.join(runtimeRoot, `${key}.lease.json`);
|
|
244
|
-
if (fs.existsSync(leasePath)) {
|
|
245
|
-
const lease = JSON.parse(fs.readFileSync(leasePath, 'utf8')) as { pid?: number; startTime?: number };
|
|
246
|
-
if (Number.isSafeInteger(lease.pid) && typeof lease.startTime === 'number'
|
|
247
|
-
&& getProcessStartTime(lease.pid!) === lease.startTime) {
|
|
248
|
-
throw new Error('CODEX_SANDBOX_CONTROLLER_BUSY');
|
|
249
|
-
}
|
|
250
|
-
fs.unlinkSync(leasePath);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
297
|
const parentStartTime = getProcessStartTime(process.pid);
|
|
254
298
|
if (!parentStartTime) throw new Error('CODEX_SANDBOX_CONTROLLER_PROCESS_IDENTITY_INVALID');
|
|
255
|
-
const instanceDigest = crypto.randomBytes(32).toString('hex');
|
|
256
|
-
fs.writeFileSync(leasePath, `${JSON.stringify({
|
|
257
|
-
version: 1,
|
|
258
|
-
pid: process.pid,
|
|
259
|
-
startTime: parentStartTime,
|
|
260
|
-
taskId: input.taskId,
|
|
261
|
-
generation: control.generation,
|
|
262
|
-
instanceDigest
|
|
263
|
-
})}\n`, { mode: 0o600, flag: 'wx' });
|
|
264
|
-
|
|
265
299
|
const home = fs.mkdtempSync(path.join(runtimeRoot, `${key}-`));
|
|
266
300
|
fs.chmodSync(home, 0o700);
|
|
267
301
|
let cleaned = false;
|
|
302
|
+
let context: CodexSandboxControllerContextV2 | null = null;
|
|
268
303
|
const cleanup = () => {
|
|
269
304
|
if (cleaned) return;
|
|
270
305
|
cleaned = true;
|
|
306
|
+
if (context) {
|
|
307
|
+
try {
|
|
308
|
+
(options.closeController ?? requestCodexControllerClose)({
|
|
309
|
+
controllerProcess: context.controllerProcess,
|
|
310
|
+
controllerProof: controllerProofFromContext(context),
|
|
311
|
+
...control,
|
|
312
|
+
timeoutMs: 30_000
|
|
313
|
+
});
|
|
314
|
+
} catch {
|
|
315
|
+
// The wrapper is exiting; dead-process recovery safely handles an unknown close result.
|
|
316
|
+
}
|
|
317
|
+
}
|
|
271
318
|
const relative = path.relative(runtimeRoot, home);
|
|
272
319
|
if (!relative.startsWith('..') && !path.isAbsolute(relative)) {
|
|
273
320
|
fs.rmSync(home, { recursive: true, force: true });
|
|
274
321
|
}
|
|
275
|
-
try {
|
|
276
|
-
const lease = JSON.parse(fs.readFileSync(leasePath, 'utf8')) as { instanceDigest?: string };
|
|
277
|
-
if (lease.instanceDigest === instanceDigest) fs.unlinkSync(leasePath);
|
|
278
|
-
} catch {
|
|
279
|
-
// A missing or replaced lease is owned by another cleanup path.
|
|
280
|
-
}
|
|
281
322
|
};
|
|
282
323
|
|
|
283
324
|
try {
|
|
@@ -306,22 +347,25 @@ function prepareCodexSandboxController(
|
|
|
306
347
|
}
|
|
307
348
|
|
|
308
349
|
const buildIdentity = computeLifecycleBuildIdentity(repoRoot);
|
|
309
|
-
const
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
parentStartTime,
|
|
317
|
-
issuedAt: now,
|
|
318
|
-
expiresAt: now + CONTEXT_TTL_MS,
|
|
319
|
-
buildIdentity,
|
|
350
|
+
const opened = (options.openController ?? requestCodexControllerOpen)({
|
|
351
|
+
controllerProcess: { pid: process.pid, startTime: parentStartTime },
|
|
352
|
+
...control,
|
|
353
|
+
timeoutMs: 30_000
|
|
354
|
+
});
|
|
355
|
+
const taskId = opened.lease.taskId;
|
|
356
|
+
context = contextFromControllerLease(opened.lease, {
|
|
320
357
|
hookDefinitionHash: crypto.createHash('sha256').update(fs.readFileSync(hooks)).digest('hex'),
|
|
321
358
|
lifecycleProfilesHash: digestFiles([executor, reviewer])
|
|
322
359
|
});
|
|
360
|
+
if (opened.lease.controlGeneration !== control.generation
|
|
361
|
+
|| opened.lease.controllerProcess.pid !== process.pid
|
|
362
|
+
|| opened.lease.controllerProcess.startTime !== parentStartTime
|
|
363
|
+
|| JSON.stringify(opened.lease.buildIdentity) !== JSON.stringify(buildIdentity)) {
|
|
364
|
+
throw new Error('SANDBOX_CONTROL_RESULT_INVALID');
|
|
365
|
+
}
|
|
366
|
+
(options.verifyTaskBinding ?? verifyControl)(taskId, control);
|
|
323
367
|
const contextPath = path.join(home, 'controller-context.json');
|
|
324
|
-
|
|
368
|
+
writeCodexSandboxControllerContext(contextPath, context);
|
|
325
369
|
|
|
326
370
|
const shimDir = path.join(home, 'bin');
|
|
327
371
|
fs.mkdirSync(shimDir, { mode: 0o700 });
|
|
@@ -337,7 +381,7 @@ function prepareCodexSandboxController(
|
|
|
337
381
|
input.reviewerModel ? `--reviewer-model ${input.reviewerModel}` : '',
|
|
338
382
|
input.reviewerReasoningEffort ? `--reviewer-reasoning-effort ${input.reviewerReasoningEffort}` : ''
|
|
339
383
|
].filter(Boolean).join(' ');
|
|
340
|
-
const prompt = `$run-task ${
|
|
384
|
+
const prompt = `$run-task ${taskId}${policy ? ` ${policy}` : ''}`;
|
|
341
385
|
const args = Object.freeze([
|
|
342
386
|
'exec',
|
|
343
387
|
'--enable', 'hooks',
|
|
@@ -352,7 +396,7 @@ function prepareCodexSandboxController(
|
|
|
352
396
|
home,
|
|
353
397
|
shimDir,
|
|
354
398
|
contextPath,
|
|
355
|
-
|
|
399
|
+
taskId,
|
|
356
400
|
control,
|
|
357
401
|
providerEnvironment,
|
|
358
402
|
options.environment ?? process.env
|
|
@@ -372,32 +416,6 @@ function prepareCodexSandboxController(
|
|
|
372
416
|
}
|
|
373
417
|
}
|
|
374
418
|
|
|
375
|
-
function verifyCodexSandboxControllerContext(
|
|
376
|
-
contextPath: string,
|
|
377
|
-
taskId: string,
|
|
378
|
-
options: Readonly<{ repoRoot?: string; now?: number; generation?: string }> = {}
|
|
379
|
-
): CodexSandboxControllerContext {
|
|
380
|
-
const stat = fs.lstatSync(contextPath);
|
|
381
|
-
if (!stat.isFile() || stat.isSymbolicLink() || (stat.mode & 0o077) !== 0) {
|
|
382
|
-
throw new Error('CODEX_SANDBOX_CONTROLLER_CONTEXT_INVALID');
|
|
383
|
-
}
|
|
384
|
-
const context = JSON.parse(fs.readFileSync(contextPath, 'utf8')) as CodexSandboxControllerContext;
|
|
385
|
-
const generation = options.generation ?? process.env.AGENT_INFRA_CONTROL_GENERATION;
|
|
386
|
-
if (context.version !== 1
|
|
387
|
-
|| context.taskId !== taskId
|
|
388
|
-
|| context.controlGeneration !== generation
|
|
389
|
-
|| context.expiresAt < (options.now ?? Date.now())
|
|
390
|
-
|| getProcessStartTime(context.parentPid) !== context.parentStartTime
|
|
391
|
-
|| !isProcessAlive(context.parentPid)) {
|
|
392
|
-
throw new Error('CODEX_SANDBOX_CONTROLLER_CONTEXT_INVALID');
|
|
393
|
-
}
|
|
394
|
-
const current = computeLifecycleBuildIdentity(options.repoRoot ?? process.cwd());
|
|
395
|
-
if (JSON.stringify(current) !== JSON.stringify(context.buildIdentity)) {
|
|
396
|
-
throw new Error('CODEX_SANDBOX_CONTROLLER_BUNDLE_MISMATCH');
|
|
397
|
-
}
|
|
398
|
-
return Object.freeze(context);
|
|
399
|
-
}
|
|
400
|
-
|
|
401
419
|
async function runCodexSandboxController(
|
|
402
420
|
input: ControllerInput,
|
|
403
421
|
options: ControllerOptions = {}
|
|
@@ -442,10 +460,11 @@ async function runCodexSandboxController(
|
|
|
442
460
|
export {
|
|
443
461
|
prepareCodexSandboxController,
|
|
444
462
|
runCodexSandboxController,
|
|
445
|
-
verifyCodexSandboxControllerContext
|
|
463
|
+
verifyCodexSandboxControllerContext,
|
|
464
|
+
controllerProofFromContext
|
|
446
465
|
};
|
|
447
466
|
export type {
|
|
448
|
-
CodexSandboxControllerContext,
|
|
467
|
+
CodexSandboxControllerContextV2 as CodexSandboxControllerContext,
|
|
449
468
|
ControllerControl,
|
|
450
469
|
ControllerInput,
|
|
451
470
|
ControllerOptions,
|
|
@@ -11,6 +11,7 @@ import type {
|
|
|
11
11
|
CodexLifecycleEvent,
|
|
12
12
|
CodexLifecycleState
|
|
13
13
|
} from './evidence.ts';
|
|
14
|
+
import { resolveAgentRuntimeStoreRoot } from '../../../runtime/agent-runtime.ts';
|
|
14
15
|
|
|
15
16
|
type StoredCodexLifecycle = Readonly<{
|
|
16
17
|
schemaVersion: 1;
|
|
@@ -105,7 +106,7 @@ function writeRecord(file: string, record: StoredCodexLifecycle, expectedRevisio
|
|
|
105
106
|
}
|
|
106
107
|
|
|
107
108
|
function createCodexLifecycleStore(options: CodexLifecycleStoreOptions) {
|
|
108
|
-
const root = options.root ??
|
|
109
|
+
const root = options.root ?? resolveAgentRuntimeStoreRoot({ store: 'lifecycle' });
|
|
109
110
|
const now = options.now ?? (() => new Date().toISOString());
|
|
110
111
|
fs.mkdirSync(root, { recursive: true, mode: 0o700 });
|
|
111
112
|
fs.chmodSync(root, 0o700);
|