@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
|
@@ -1,345 +0,0 @@
|
|
|
1
|
-
import crypto from 'node:crypto';
|
|
2
|
-
import { spawn, spawnSync } from 'node:child_process';
|
|
3
|
-
import fs from 'node:fs';
|
|
4
|
-
import os from 'node:os';
|
|
5
|
-
import path from 'node:path';
|
|
6
|
-
import semver from 'semver';
|
|
7
|
-
import * as toml from 'smol-toml';
|
|
8
|
-
import { requestCodexControllerClose, requestCodexControllerOpen, requestCodexControllerVerify } from './controller-client.js';
|
|
9
|
-
import { getProcessStartTime } from '../../../server/process-state.js';
|
|
10
|
-
import { LIFECYCLE_PROTOCOL_VERSION } from './build-identity.js';
|
|
11
|
-
import { contextFromControllerLease, controllerProofFromContext, verifyCodexSandboxControllerContextWithWarnings as verifyContextFileWithWarnings, writeCodexSandboxControllerContext } from './controller-context.js';
|
|
12
|
-
function copyRegular(source, destination, mode) {
|
|
13
|
-
const stat = fs.lstatSync(source);
|
|
14
|
-
if (!stat.isFile() || stat.isSymbolicLink()) {
|
|
15
|
-
throw new Error('CODEX_SANDBOX_CONTROLLER_INPUT_INVALID');
|
|
16
|
-
}
|
|
17
|
-
fs.mkdirSync(path.dirname(destination), { recursive: true, mode: 0o700 });
|
|
18
|
-
fs.copyFileSync(source, destination);
|
|
19
|
-
fs.chmodSync(destination, mode);
|
|
20
|
-
}
|
|
21
|
-
function controlFromEnvironment() {
|
|
22
|
-
const token = process.env.AGENT_INFRA_CONTROL_TOKEN;
|
|
23
|
-
const generation = process.env.AGENT_INFRA_CONTROL_GENERATION;
|
|
24
|
-
const rootId = process.env.AGENT_INFRA_CONTROL_ROOT_ID;
|
|
25
|
-
const channelDir = process.env.AGENT_INFRA_CONTROL_DIR;
|
|
26
|
-
const statusDir = process.env.AGENT_INFRA_CONTROL_STATUS_DIR;
|
|
27
|
-
const runtimeDir = process.env.AGENT_INFRA_RUNTIME_DIR;
|
|
28
|
-
if (!token || !generation || !rootId || !channelDir || !statusDir || !runtimeDir) {
|
|
29
|
-
throw new Error('CODEX_SANDBOX_CONTROLLER_CONTROL_MISSING');
|
|
30
|
-
}
|
|
31
|
-
return { token, generation, rootId, channelDir, statusDir, runtimeDir };
|
|
32
|
-
}
|
|
33
|
-
function verifyRuntime(runtimeDir) {
|
|
34
|
-
let stat;
|
|
35
|
-
try {
|
|
36
|
-
stat = fs.lstatSync(runtimeDir);
|
|
37
|
-
}
|
|
38
|
-
catch {
|
|
39
|
-
throw new Error('CODEX_SANDBOX_CONTROLLER_RUNTIME_UNAVAILABLE');
|
|
40
|
-
}
|
|
41
|
-
if (!stat.isDirectory() || stat.isSymbolicLink()) {
|
|
42
|
-
throw new Error('CODEX_SANDBOX_CONTROLLER_RUNTIME_UNAVAILABLE');
|
|
43
|
-
}
|
|
44
|
-
const probe = path.join(runtimeDir, `.controller-${process.pid}-${crypto.randomUUID()}.probe`);
|
|
45
|
-
try {
|
|
46
|
-
fs.writeFileSync(probe, 'runtime-probe\n', { encoding: 'utf8', flag: 'wx', mode: 0o600 });
|
|
47
|
-
if (fs.readFileSync(probe, 'utf8') !== 'runtime-probe\n') {
|
|
48
|
-
throw new Error('probe mismatch');
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
catch {
|
|
52
|
-
throw new Error('CODEX_SANDBOX_CONTROLLER_RUNTIME_UNAVAILABLE');
|
|
53
|
-
}
|
|
54
|
-
finally {
|
|
55
|
-
fs.rmSync(probe, { force: true });
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
function verifyControllerBinding(context, control, requestControllerVerify) {
|
|
59
|
-
verifyRuntime(control.runtimeDir);
|
|
60
|
-
const verified = requestControllerVerify({
|
|
61
|
-
controllerProof: controllerProofFromContext(context),
|
|
62
|
-
...control,
|
|
63
|
-
timeoutMs: 30_000
|
|
64
|
-
});
|
|
65
|
-
if (verified.binding.controlGeneration !== context.controlGeneration
|
|
66
|
-
|| verified.binding.controllerInstanceDigest !== context.controllerInstanceDigest) {
|
|
67
|
-
throw new Error('CODEX_SANDBOX_CONTROLLER_TASK_BINDING_INVALID');
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
function verifyCodexSandboxControllerContextWithWarnings(contextPath, options = {}) {
|
|
71
|
-
const control = options.control ?? controlFromEnvironment();
|
|
72
|
-
const fileVerification = verifyContextFileWithWarnings(contextPath, {
|
|
73
|
-
repoRoot: options.repoRoot,
|
|
74
|
-
now: options.now,
|
|
75
|
-
generation: control.generation,
|
|
76
|
-
probeProcess: options.probeProcess
|
|
77
|
-
});
|
|
78
|
-
const context = fileVerification.context;
|
|
79
|
-
const verified = (options.requestControllerVerify ?? requestCodexControllerVerify)({
|
|
80
|
-
controllerProof: controllerProofFromContext(context),
|
|
81
|
-
...control,
|
|
82
|
-
timeoutMs: 30_000
|
|
83
|
-
});
|
|
84
|
-
if (verified.binding.controlGeneration !== context.controlGeneration
|
|
85
|
-
|| verified.binding.controllerInstanceDigest !== context.controllerInstanceDigest) {
|
|
86
|
-
throw new Error('CODEX_SANDBOX_CONTROLLER_CONTEXT_INVALID');
|
|
87
|
-
}
|
|
88
|
-
return Object.freeze({
|
|
89
|
-
context,
|
|
90
|
-
warnings: fileVerification.warnings
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
function detectedCodexVersion() {
|
|
94
|
-
const result = spawnSync('codex', ['--version'], { encoding: 'utf8' });
|
|
95
|
-
const version = /codex-cli\s+(\d+\.\d+\.\d+)/u.exec(result.stdout ?? '')?.[1];
|
|
96
|
-
if (result.status !== 0 || !version)
|
|
97
|
-
throw new Error('CODEX_SANDBOX_CONTROLLER_CODEX_UNAVAILABLE');
|
|
98
|
-
return version;
|
|
99
|
-
}
|
|
100
|
-
function sanitizedConfig(source, destination, sourceHome, targetHome) {
|
|
101
|
-
if (!fs.existsSync(source))
|
|
102
|
-
return [];
|
|
103
|
-
const parsed = toml.parse(fs.readFileSync(source, 'utf8'));
|
|
104
|
-
const allowed = new Set([
|
|
105
|
-
'model',
|
|
106
|
-
'model_provider',
|
|
107
|
-
'model_providers',
|
|
108
|
-
'model_reasoning_effort',
|
|
109
|
-
'model_auto_compact_token_limit',
|
|
110
|
-
'preferred_auth_method'
|
|
111
|
-
]);
|
|
112
|
-
const output = Object.fromEntries(Object.entries(parsed).filter(([key]) => allowed.has(key)));
|
|
113
|
-
const catalog = parsed.model_catalog_json;
|
|
114
|
-
if (typeof catalog === 'string' && catalog.trim()) {
|
|
115
|
-
const resolved = path.resolve(sourceHome, catalog.replace(/^~[/\\]/u, ''));
|
|
116
|
-
const relative = path.relative(sourceHome, resolved);
|
|
117
|
-
if (relative.startsWith('..') || path.isAbsolute(relative)) {
|
|
118
|
-
throw new Error('CODEX_SANDBOX_CONTROLLER_CONFIG_PATH_INVALID');
|
|
119
|
-
}
|
|
120
|
-
const target = path.join(targetHome, 'model-catalogs', path.basename(resolved));
|
|
121
|
-
copyRegular(resolved, target, 0o600);
|
|
122
|
-
output.model_catalog_json = target;
|
|
123
|
-
}
|
|
124
|
-
fs.writeFileSync(destination, `${toml.stringify(output)}\n`, { mode: 0o600 });
|
|
125
|
-
const providerEnvironment = new Set();
|
|
126
|
-
const providers = output.model_providers;
|
|
127
|
-
if (providers && typeof providers === 'object' && !Array.isArray(providers)) {
|
|
128
|
-
for (const provider of Object.values(providers)) {
|
|
129
|
-
if (!provider || typeof provider !== 'object' || Array.isArray(provider))
|
|
130
|
-
continue;
|
|
131
|
-
for (const key of ['env_key', 'envKey']) {
|
|
132
|
-
const name = provider[key];
|
|
133
|
-
if (typeof name === 'string' && /^[A-Z][A-Z0-9_]{1,127}$/u.test(name)) {
|
|
134
|
-
providerEnvironment.add(name);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
return Object.freeze([...providerEnvironment]);
|
|
140
|
-
}
|
|
141
|
-
function isolatedEnvironment(home, shimDir, contextPath, taskId, control, providerEnvironment, sourceEnvironment) {
|
|
142
|
-
const inherited = new Set([
|
|
143
|
-
'LANG', 'LC_ALL', 'TERM', 'COLORTERM',
|
|
144
|
-
'HTTP_PROXY', 'HTTPS_PROXY', 'NO_PROXY',
|
|
145
|
-
'http_proxy', 'https_proxy', 'no_proxy',
|
|
146
|
-
'SSL_CERT_FILE', 'SSL_CERT_DIR', 'NODE_EXTRA_CA_CERTS',
|
|
147
|
-
'CODEX_CI',
|
|
148
|
-
...providerEnvironment
|
|
149
|
-
]);
|
|
150
|
-
const env = {};
|
|
151
|
-
for (const key of inherited) {
|
|
152
|
-
if (sourceEnvironment[key] !== undefined)
|
|
153
|
-
env[key] = sourceEnvironment[key];
|
|
154
|
-
}
|
|
155
|
-
return {
|
|
156
|
-
...env,
|
|
157
|
-
HOME: home,
|
|
158
|
-
CODEX_HOME: home,
|
|
159
|
-
PATH: `${shimDir}${path.delimiter}${sourceEnvironment.PATH ?? ''}`,
|
|
160
|
-
AGENT_INFRA_TASK_ID: taskId,
|
|
161
|
-
AGENT_INFRA_CONTROL_TOKEN: control.token,
|
|
162
|
-
AGENT_INFRA_CONTROL_GENERATION: control.generation,
|
|
163
|
-
...(control.rootId || sourceEnvironment.AGENT_INFRA_CONTROL_ROOT_ID
|
|
164
|
-
? { AGENT_INFRA_CONTROL_ROOT_ID: control.rootId ?? sourceEnvironment.AGENT_INFRA_CONTROL_ROOT_ID }
|
|
165
|
-
: {}),
|
|
166
|
-
AGENT_INFRA_CONTROL_DIR: control.channelDir,
|
|
167
|
-
AGENT_INFRA_CONTROL_STATUS_DIR: control.statusDir,
|
|
168
|
-
AGENT_INFRA_RUNTIME_DIR: control.runtimeDir,
|
|
169
|
-
AGENT_INFRA_CODEX_CONTROLLER_CONTEXT: contextPath
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
function shellQuote(value) {
|
|
173
|
-
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
174
|
-
}
|
|
175
|
-
function prepareCodexSandboxController(input, options = {}) {
|
|
176
|
-
const repoRoot = path.resolve(options.repoRoot ?? process.cwd());
|
|
177
|
-
const sourceHome = path.resolve(options.codexHome ?? process.env.CODEX_HOME ?? path.join(os.homedir(), '.codex'));
|
|
178
|
-
const control = options.control ?? controlFromEnvironment();
|
|
179
|
-
const version = (options.codexVersion ?? detectedCodexVersion)();
|
|
180
|
-
if (!semver.gte(version, '0.147.0'))
|
|
181
|
-
throw new Error('CODEX_SANDBOX_CONTROLLER_CODEX_UNSUPPORTED');
|
|
182
|
-
const runtimeRoot = path.resolve(options.temporaryRoot
|
|
183
|
-
?? path.join(os.tmpdir(), 'agent-infra-codex-controllers'));
|
|
184
|
-
fs.mkdirSync(runtimeRoot, { recursive: true, mode: 0o700 });
|
|
185
|
-
fs.chmodSync(runtimeRoot, 0o700);
|
|
186
|
-
const key = crypto.createHash('sha256')
|
|
187
|
-
.update(`${process.pid}\0${control.generation}`)
|
|
188
|
-
.digest('hex');
|
|
189
|
-
const parentStartTime = getProcessStartTime(process.pid);
|
|
190
|
-
if (!parentStartTime)
|
|
191
|
-
throw new Error('CODEX_SANDBOX_CONTROLLER_PROCESS_IDENTITY_INVALID');
|
|
192
|
-
const home = fs.mkdtempSync(path.join(runtimeRoot, `${key}-`));
|
|
193
|
-
fs.chmodSync(home, 0o700);
|
|
194
|
-
let cleaned = false;
|
|
195
|
-
let context = null;
|
|
196
|
-
const cleanup = () => {
|
|
197
|
-
if (cleaned)
|
|
198
|
-
return;
|
|
199
|
-
cleaned = true;
|
|
200
|
-
if (context) {
|
|
201
|
-
try {
|
|
202
|
-
(options.closeController ?? requestCodexControllerClose)({
|
|
203
|
-
controllerProcess: context.controllerProcess,
|
|
204
|
-
controllerProof: controllerProofFromContext(context),
|
|
205
|
-
...control,
|
|
206
|
-
timeoutMs: 30_000
|
|
207
|
-
});
|
|
208
|
-
}
|
|
209
|
-
catch {
|
|
210
|
-
// The wrapper is exiting; dead-process recovery safely handles an unknown close result.
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
const relative = path.relative(runtimeRoot, home);
|
|
214
|
-
if (!relative.startsWith('..') && !path.isAbsolute(relative)) {
|
|
215
|
-
fs.rmSync(home, { recursive: true, force: true });
|
|
216
|
-
}
|
|
217
|
-
};
|
|
218
|
-
try {
|
|
219
|
-
const auth = path.join(sourceHome, 'auth.json');
|
|
220
|
-
if (fs.existsSync(auth))
|
|
221
|
-
copyRegular(auth, path.join(home, 'auth.json'), 0o600);
|
|
222
|
-
const providerEnvironment = sanitizedConfig(path.join(sourceHome, 'config.toml'), path.join(home, 'config.toml'), sourceHome, home);
|
|
223
|
-
const hooks = path.join(repoRoot, '.codex', 'hooks.json');
|
|
224
|
-
const executor = path.join(repoRoot, '.codex', 'agents', 'agent-infra-lifecycle-executor.toml');
|
|
225
|
-
const reviewer = path.join(repoRoot, '.codex', 'agents', 'agent-infra-lifecycle-reviewer.toml');
|
|
226
|
-
copyRegular(hooks, path.join(home, 'hooks.json'), 0o600);
|
|
227
|
-
copyRegular(executor, path.join(home, 'agents', path.basename(executor)), 0o600);
|
|
228
|
-
copyRegular(reviewer, path.join(home, 'agents', path.basename(reviewer)), 0o600);
|
|
229
|
-
const opened = (options.openController ?? requestCodexControllerOpen)({
|
|
230
|
-
controllerProcess: { pid: process.pid, startTime: parentStartTime },
|
|
231
|
-
...control,
|
|
232
|
-
timeoutMs: 30_000
|
|
233
|
-
});
|
|
234
|
-
const taskId = opened.lease.taskId;
|
|
235
|
-
if (opened.lease.buildIdentity.protocolVersion !== LIFECYCLE_PROTOCOL_VERSION) {
|
|
236
|
-
throw new Error('CODEX_LIFECYCLE_PROTOCOL_MISMATCH: Codex lifecycle protocol version does not match');
|
|
237
|
-
}
|
|
238
|
-
context = contextFromControllerLease(opened.lease, {
|
|
239
|
-
hookDefinitionHash: crypto.createHash('sha256').update(fs.readFileSync(hooks)).digest('hex')
|
|
240
|
-
});
|
|
241
|
-
if (opened.lease.controlGeneration !== control.generation
|
|
242
|
-
|| opened.lease.controllerProcess.pid !== process.pid
|
|
243
|
-
|| opened.lease.controllerProcess.startTime !== parentStartTime) {
|
|
244
|
-
throw new Error('SANDBOX_CONTROL_RESULT_INVALID');
|
|
245
|
-
}
|
|
246
|
-
verifyControllerBinding(context, control, options.verifyController ?? requestCodexControllerVerify);
|
|
247
|
-
const contextPath = path.join(home, 'controller-context.json');
|
|
248
|
-
writeCodexSandboxControllerContext(contextPath, context);
|
|
249
|
-
const shimDir = path.join(home, 'bin');
|
|
250
|
-
fs.mkdirSync(shimDir, { mode: 0o700 });
|
|
251
|
-
const internalCli = path.resolve(process.argv[1] ?? path.join(repoRoot, 'bin', 'internal-cli.ts'));
|
|
252
|
-
const launcherPrefix = ['#!/bin/sh', 'set -eu', 'unset NODE_OPTIONS NODE_PATH'].join('\n') + '\n';
|
|
253
|
-
const source = internalCli.endsWith('.ts')
|
|
254
|
-
? `${launcherPrefix}exec ${shellQuote(process.execPath)} --experimental-strip-types ${shellQuote(internalCli)} "$@"\n`
|
|
255
|
-
: `${launcherPrefix}exec ${shellQuote(process.execPath)} ${shellQuote(internalCli)} "$@"\n`;
|
|
256
|
-
fs.writeFileSync(path.join(shimDir, 'agent-infra-internal'), source, { mode: 0o700 });
|
|
257
|
-
const policy = [
|
|
258
|
-
input.executorModel ? `--executor-model ${input.executorModel}` : '',
|
|
259
|
-
input.executorReasoningEffort ? `--executor-reasoning-effort ${input.executorReasoningEffort}` : '',
|
|
260
|
-
input.reviewerModel ? `--reviewer-model ${input.reviewerModel}` : '',
|
|
261
|
-
input.reviewerReasoningEffort ? `--reviewer-reasoning-effort ${input.reviewerReasoningEffort}` : ''
|
|
262
|
-
].filter(Boolean).join(' ');
|
|
263
|
-
const prompt = `$run-task ${taskId}${policy ? ` ${policy}` : ''}`;
|
|
264
|
-
const args = Object.freeze([
|
|
265
|
-
'exec',
|
|
266
|
-
'--enable', 'hooks',
|
|
267
|
-
'--enable', 'multi_agent',
|
|
268
|
-
'--dangerously-bypass-hook-trust',
|
|
269
|
-
'--dangerously-bypass-approvals-and-sandbox',
|
|
270
|
-
'--json',
|
|
271
|
-
'-C', repoRoot,
|
|
272
|
-
prompt
|
|
273
|
-
]);
|
|
274
|
-
const env = isolatedEnvironment(home, shimDir, contextPath, taskId, control, providerEnvironment, options.environment ?? process.env);
|
|
275
|
-
return Object.freeze({
|
|
276
|
-
command: 'codex',
|
|
277
|
-
args,
|
|
278
|
-
env,
|
|
279
|
-
home,
|
|
280
|
-
contextPath,
|
|
281
|
-
context,
|
|
282
|
-
cleanup
|
|
283
|
-
});
|
|
284
|
-
}
|
|
285
|
-
catch (error) {
|
|
286
|
-
cleanup();
|
|
287
|
-
throw error;
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
async function runCodexSandboxController(input, options = {}) {
|
|
291
|
-
const prepared = prepareCodexSandboxController(input, options);
|
|
292
|
-
try {
|
|
293
|
-
const child = spawn(prepared.command, [...prepared.args], {
|
|
294
|
-
cwd: options.repoRoot ?? process.cwd(),
|
|
295
|
-
env: prepared.env,
|
|
296
|
-
stdio: 'inherit',
|
|
297
|
-
detached: process.platform !== 'win32'
|
|
298
|
-
});
|
|
299
|
-
let forceKill;
|
|
300
|
-
const terminate = () => {
|
|
301
|
-
if (child.pid && process.platform !== 'win32') {
|
|
302
|
-
try {
|
|
303
|
-
process.kill(-child.pid, 'SIGTERM');
|
|
304
|
-
}
|
|
305
|
-
catch {
|
|
306
|
-
child.kill('SIGTERM');
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
else
|
|
310
|
-
child.kill('SIGTERM');
|
|
311
|
-
forceKill = setTimeout(() => {
|
|
312
|
-
if (child.pid && process.platform !== 'win32') {
|
|
313
|
-
try {
|
|
314
|
-
process.kill(-child.pid, 'SIGKILL');
|
|
315
|
-
}
|
|
316
|
-
catch {
|
|
317
|
-
child.kill('SIGKILL');
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
else
|
|
321
|
-
child.kill('SIGKILL');
|
|
322
|
-
}, 5_000);
|
|
323
|
-
forceKill.unref();
|
|
324
|
-
};
|
|
325
|
-
process.once('SIGINT', terminate);
|
|
326
|
-
process.once('SIGTERM', terminate);
|
|
327
|
-
try {
|
|
328
|
-
return await new Promise((resolve, reject) => {
|
|
329
|
-
child.once('error', reject);
|
|
330
|
-
child.once('exit', (code, signal) => resolve(code ?? (signal ? 1 : 0)));
|
|
331
|
-
});
|
|
332
|
-
}
|
|
333
|
-
finally {
|
|
334
|
-
if (forceKill)
|
|
335
|
-
clearTimeout(forceKill);
|
|
336
|
-
process.off('SIGINT', terminate);
|
|
337
|
-
process.off('SIGTERM', terminate);
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
finally {
|
|
341
|
-
prepared.cleanup();
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
export { prepareCodexSandboxController, runCodexSandboxController, verifyCodexSandboxControllerContextWithWarnings, controllerProofFromContext };
|
|
345
|
-
//# sourceMappingURL=sandbox-controller.js.map
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
2
|
-
import { resolveTaskRuntimeRoot } from '../task/runtime-paths.js';
|
|
3
|
-
function runtimeError(code, message) {
|
|
4
|
-
const error = new Error(`${code}: ${message}`);
|
|
5
|
-
error.name = code;
|
|
6
|
-
return error;
|
|
7
|
-
}
|
|
8
|
-
function boundControlContext(env) {
|
|
9
|
-
return [
|
|
10
|
-
'AGENT_INFRA_CONTROL_TOKEN',
|
|
11
|
-
'AGENT_INFRA_CONTROL_GENERATION',
|
|
12
|
-
'AGENT_INFRA_EXECUTOR_MANIFEST'
|
|
13
|
-
].some((key) => typeof env[key] === 'string' && env[key].length > 0);
|
|
14
|
-
}
|
|
15
|
-
export function resolveAgentRuntimeRoot(options = {}) {
|
|
16
|
-
const env = options.env ?? process.env;
|
|
17
|
-
const configured = env.AGENT_INFRA_RUNTIME_DIR;
|
|
18
|
-
if (configured !== undefined && configured.length > 0) {
|
|
19
|
-
if (!path.isAbsolute(configured)) {
|
|
20
|
-
throw runtimeError('AGENT_INFRA_RUNTIME_DIR_INVALID', 'runtime directory must be absolute');
|
|
21
|
-
}
|
|
22
|
-
return path.resolve(configured);
|
|
23
|
-
}
|
|
24
|
-
const taskId = env.AGENT_INFRA_TASK_ID;
|
|
25
|
-
if (taskId) {
|
|
26
|
-
return resolveTaskRuntimeRoot(taskId, { repoRoot: options.repoRoot });
|
|
27
|
-
}
|
|
28
|
-
if (boundControlContext(env)) {
|
|
29
|
-
throw runtimeError('AGENT_INFRA_RUNTIME_DIR_REQUIRED', 'task-bound control context requires AGENT_INFRA_RUNTIME_DIR');
|
|
30
|
-
}
|
|
31
|
-
return path.join(path.resolve(options.repoRoot ?? process.cwd()), '.agents', 'workspace', '.runtime');
|
|
32
|
-
}
|
|
33
|
-
//# sourceMappingURL=agent-runtime.js.map
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { getAgentClientAdapter } from '../../agent-clients/registry.js';
|
|
2
|
-
function operationFor(request) {
|
|
3
|
-
const adapter = getAgentClientAdapter(request.agentClient);
|
|
4
|
-
const operation = adapter.sandboxControlOperation;
|
|
5
|
-
if (!operation)
|
|
6
|
-
throw new Error('SANDBOX_CONTROL_AGENT_OPERATION_UNSUPPORTED');
|
|
7
|
-
return operation;
|
|
8
|
-
}
|
|
9
|
-
function validateAgentClientOperation(request, manifest) {
|
|
10
|
-
operationFor(request).validate({
|
|
11
|
-
operation: request.operation,
|
|
12
|
-
payload: request.payload,
|
|
13
|
-
manifest
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
function executeAgentClientOperation(request, manifest, manifestPath, override) {
|
|
17
|
-
return (override ?? operationFor(request)).execute({
|
|
18
|
-
operation: request.operation,
|
|
19
|
-
payload: request.payload,
|
|
20
|
-
manifest,
|
|
21
|
-
manifestPath
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
function recoverAgentClientOperation(params) {
|
|
25
|
-
const operation = operationFor(params.request);
|
|
26
|
-
return operation.recover?.({
|
|
27
|
-
operation: params.request.operation,
|
|
28
|
-
manifest: params.manifest,
|
|
29
|
-
manifestPath: params.manifestPath,
|
|
30
|
-
stdout: params.stdout
|
|
31
|
-
}) ?? { consistent: false };
|
|
32
|
-
}
|
|
33
|
-
export { executeAgentClientOperation, recoverAgentClientOperation, validateAgentClientOperation };
|
|
34
|
-
//# sourceMappingURL=agent-operation.js.map
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { resolveTaskRef } from './resolve-ref.js';
|
|
4
|
-
export function resolveTaskRuntimeRoot(taskRef, options = {}) {
|
|
5
|
-
const resolved = resolveTaskRef(taskRef, options);
|
|
6
|
-
if (!resolved.ok)
|
|
7
|
-
throw new Error(`${resolved.code}: ${resolved.message}`);
|
|
8
|
-
return path.join(fs.realpathSync.native(resolved.taskDir), '.runtime');
|
|
9
|
-
}
|
|
10
|
-
//# sourceMappingURL=runtime-paths.js.map
|
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
import { SandboxControlClientError, requestSandboxAgentOperation } from '../../../sandbox/control/client.ts';
|
|
2
|
-
import type { SandboxControlResponse } from '../../../sandbox/control/protocol.ts';
|
|
3
|
-
import type { ProcessIdentity } from '../../../server/process-state.ts';
|
|
4
|
-
import type { CodexControllerLeaseProofV1, CodexControllerOpened } from './controller-registration.ts';
|
|
5
|
-
|
|
6
|
-
type ControllerClosed = Readonly<{ version: 1; status: 'closed'; changed: boolean; lease: null; error: null }>;
|
|
7
|
-
type ControllerVerified = Readonly<{
|
|
8
|
-
version: 1; status: 'verified'; changed: false; lease: null;
|
|
9
|
-
binding: Readonly<{ taskId: string; controlGeneration: string; controllerInstanceDigest: string }>;
|
|
10
|
-
error: null;
|
|
11
|
-
}>;
|
|
12
|
-
type ControllerResult = CodexControllerOpened | ControllerClosed | ControllerVerified;
|
|
13
|
-
|
|
14
|
-
function fail(code: string, message: string, accepted = true): never {
|
|
15
|
-
throw new SandboxControlClientError({ code, message: `${code}: ${message}`, retryable: false }, accepted);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function exactKeys(value: Record<string, unknown>, keys: readonly string[]): boolean {
|
|
19
|
-
return Object.keys(value).sort().join(',') === [...keys].sort().join(',');
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function validProcess(value: unknown): value is ProcessIdentity {
|
|
23
|
-
if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
|
|
24
|
-
const process = value as Record<string, unknown>;
|
|
25
|
-
return exactKeys(process, ['pid', 'startTime'])
|
|
26
|
-
&& Number.isSafeInteger(process.pid) && (process.pid as number) > 0
|
|
27
|
-
&& Number.isSafeInteger(process.startTime) && (process.startTime as number) >= 0;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function validBuild(value: unknown): boolean {
|
|
31
|
-
if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
|
|
32
|
-
const build = value as Record<string, unknown>;
|
|
33
|
-
return exactKeys(build, ['internalExecutableBuildHash', 'lifecycleContractHash', 'packageVersion', 'protocolVersion'])
|
|
34
|
-
&& build.protocolVersion === 3
|
|
35
|
-
&& typeof build.packageVersion === 'string' && build.packageVersion.length > 0
|
|
36
|
-
&& typeof build.internalExecutableBuildHash === 'string' && /^[a-f0-9]{64}$/u.test(build.internalExecutableBuildHash)
|
|
37
|
-
&& typeof build.lifecycleContractHash === 'string' && /^[a-f0-9]{64}$/u.test(build.lifecycleContractHash);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function parseCodexControllerResult(response: SandboxControlResponse): ControllerResult {
|
|
41
|
-
if (response.phase !== 'completed' || response.error !== null || response.stderr !== '') {
|
|
42
|
-
fail('SANDBOX_CONTROL_RESULT_INVALID', 'controller result outer response is invalid');
|
|
43
|
-
}
|
|
44
|
-
let value: unknown;
|
|
45
|
-
try {
|
|
46
|
-
if (!response.stdout.endsWith('\n') || response.stdout.slice(0, -1).includes('\n')) throw new Error('not canonical');
|
|
47
|
-
value = JSON.parse(response.stdout);
|
|
48
|
-
} catch {
|
|
49
|
-
fail('SANDBOX_CONTROL_RESULT_INVALID', 'controller result payload is invalid');
|
|
50
|
-
}
|
|
51
|
-
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
52
|
-
fail('SANDBOX_CONTROL_RESULT_INVALID', 'controller result payload is invalid');
|
|
53
|
-
}
|
|
54
|
-
const result = value as Record<string, unknown>;
|
|
55
|
-
const keys = result.status === 'verified'
|
|
56
|
-
? ['binding', 'changed', 'error', 'lease', 'status', 'version']
|
|
57
|
-
: ['changed', 'error', 'lease', 'status', 'version'];
|
|
58
|
-
if (!exactKeys(result, keys) || result.version !== 1
|
|
59
|
-
|| !['opened', 'closed', 'failed', 'verified'].includes(result.status as string)) {
|
|
60
|
-
fail('SANDBOX_CONTROL_RESULT_INVALID', 'controller result schema is invalid');
|
|
61
|
-
}
|
|
62
|
-
if (result.status === 'failed') {
|
|
63
|
-
const error = result.error as Record<string, unknown> | null;
|
|
64
|
-
if (response.exitCode !== 1 || result.changed !== false || result.lease !== null || !error
|
|
65
|
-
|| !exactKeys(error, ['code', 'message', 'retryable'])
|
|
66
|
-
|| typeof error.code !== 'string' || !/^[A-Z][A-Z0-9_]+$/u.test(error.code)
|
|
67
|
-
|| typeof error.message !== 'string' || error.retryable !== false) {
|
|
68
|
-
fail('SANDBOX_CONTROL_RESULT_INVALID', 'controller failure result is invalid');
|
|
69
|
-
}
|
|
70
|
-
fail(error.code, error.message);
|
|
71
|
-
}
|
|
72
|
-
if (response.exitCode !== 0 || result.error !== null) {
|
|
73
|
-
fail('SANDBOX_CONTROL_RESULT_INVALID', 'controller success result is invalid');
|
|
74
|
-
}
|
|
75
|
-
if (result.status === 'closed') {
|
|
76
|
-
if (typeof result.changed !== 'boolean' || result.lease !== null) fail('SANDBOX_CONTROL_RESULT_INVALID', 'controller close result is invalid');
|
|
77
|
-
return result as unknown as ControllerClosed;
|
|
78
|
-
}
|
|
79
|
-
if (result.status === 'verified') {
|
|
80
|
-
const binding = result.binding as Record<string, unknown> | null;
|
|
81
|
-
if (result.changed !== false || result.lease !== null || !binding
|
|
82
|
-
|| !exactKeys(binding, ['controllerInstanceDigest', 'controlGeneration', 'taskId'])
|
|
83
|
-
|| typeof binding.taskId !== 'string' || binding.taskId.length === 0
|
|
84
|
-
|| typeof binding.controlGeneration !== 'string' || binding.controlGeneration.length === 0
|
|
85
|
-
|| typeof binding.controllerInstanceDigest !== 'string' || !/^[a-f0-9]{64}$/u.test(binding.controllerInstanceDigest)) {
|
|
86
|
-
fail('SANDBOX_CONTROL_RESULT_INVALID', 'controller verify result is invalid');
|
|
87
|
-
}
|
|
88
|
-
return result as unknown as ControllerVerified;
|
|
89
|
-
}
|
|
90
|
-
const lease = result.lease as Record<string, unknown> | null;
|
|
91
|
-
if (result.changed !== true || !lease
|
|
92
|
-
|| !exactKeys(lease, ['buildIdentity', 'controlGeneration', 'controllerInstanceDigest', 'controllerProcess', 'expiresAt', 'issuedAt', 'leaseId', 'leaseSecret', 'taskId', 'version'])
|
|
93
|
-
|| lease.version !== 1 || typeof lease.leaseId !== 'string' || !/^[a-f0-9]{64}$/u.test(lease.leaseId)
|
|
94
|
-
|| typeof lease.leaseSecret !== 'string' || !/^[a-f0-9]{64}$/u.test(lease.leaseSecret)
|
|
95
|
-
|| typeof lease.taskId !== 'string' || lease.taskId.length === 0
|
|
96
|
-
|| typeof lease.controlGeneration !== 'string' || lease.controlGeneration.length === 0
|
|
97
|
-
|| typeof lease.controllerInstanceDigest !== 'string' || !/^[a-f0-9]{64}$/u.test(lease.controllerInstanceDigest)
|
|
98
|
-
|| !validProcess(lease.controllerProcess) || !validBuild(lease.buildIdentity)
|
|
99
|
-
|| !Number.isSafeInteger(lease.issuedAt) || !Number.isSafeInteger(lease.expiresAt)
|
|
100
|
-
|| (lease.expiresAt as number) <= (lease.issuedAt as number)) {
|
|
101
|
-
fail('SANDBOX_CONTROL_RESULT_INVALID', 'controller open result is invalid');
|
|
102
|
-
}
|
|
103
|
-
return result as unknown as CodexControllerOpened;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function requestController(params: Readonly<{
|
|
107
|
-
command: 'open' | 'close' | 'verify';
|
|
108
|
-
controllerProcess: ProcessIdentity;
|
|
109
|
-
controllerProof: CodexControllerLeaseProofV1 | null;
|
|
110
|
-
channelDir?: string;
|
|
111
|
-
statusDir?: string;
|
|
112
|
-
token?: string;
|
|
113
|
-
generation?: string;
|
|
114
|
-
timeoutMs?: number;
|
|
115
|
-
}>): ControllerResult {
|
|
116
|
-
const result = parseCodexControllerResult(requestSandboxAgentOperation({
|
|
117
|
-
agentClient: 'codex',
|
|
118
|
-
operation: `controller.${params.command}`,
|
|
119
|
-
payload: params.command === 'open'
|
|
120
|
-
? { controllerProcess: params.controllerProcess }
|
|
121
|
-
: { proof: params.controllerProof },
|
|
122
|
-
...params
|
|
123
|
-
}));
|
|
124
|
-
if (result.status === 'opened'
|
|
125
|
-
&& (result.lease.controlGeneration !== (params.generation ?? process.env.AGENT_INFRA_CONTROL_GENERATION)
|
|
126
|
-
|| result.lease.controllerProcess.pid !== params.controllerProcess.pid
|
|
127
|
-
|| result.lease.controllerProcess.startTime !== params.controllerProcess.startTime)) {
|
|
128
|
-
fail('SANDBOX_CONTROL_RESULT_INVALID', 'controller result does not match the request');
|
|
129
|
-
}
|
|
130
|
-
return result;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
function requestCodexControllerOpen(params: Omit<Parameters<typeof requestController>[0], 'command' | 'controllerProof'>): CodexControllerOpened {
|
|
134
|
-
const result = requestController({ ...params, command: 'open', controllerProof: null });
|
|
135
|
-
if (result.status !== 'opened') fail('SANDBOX_CONTROL_RESULT_INVALID', 'controller open returned the wrong result');
|
|
136
|
-
return result;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
function requestCodexControllerClose(params: Omit<Parameters<typeof requestController>[0], 'command'>): ControllerClosed {
|
|
140
|
-
const result = requestController({ ...params, command: 'close' });
|
|
141
|
-
if (result.status !== 'closed') fail('SANDBOX_CONTROL_RESULT_INVALID', 'controller close returned the wrong result');
|
|
142
|
-
return result;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
function requestCodexControllerVerify(params: Readonly<{
|
|
146
|
-
controllerProof: CodexControllerLeaseProofV1;
|
|
147
|
-
channelDir?: string;
|
|
148
|
-
statusDir?: string;
|
|
149
|
-
token?: string;
|
|
150
|
-
generation?: string;
|
|
151
|
-
timeoutMs?: number;
|
|
152
|
-
}>): ControllerVerified {
|
|
153
|
-
const result = requestController({ ...params, controllerProcess: params.controllerProof.controllerProcess, command: 'verify' });
|
|
154
|
-
if (result.status !== 'verified') fail('SANDBOX_CONTROL_RESULT_INVALID', 'controller verify returned the wrong result');
|
|
155
|
-
return result;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
export { parseCodexControllerResult, requestCodexControllerClose, requestCodexControllerOpen, requestCodexControllerVerify };
|