@fitlab-ai/agent-infra 0.9.9 → 0.9.10
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 +5 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/build-identity.js +17 -10
- package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-context.js +11 -31
- package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +15 -33
- package/dist/lib/agent-clients/adapters/codex-sandbox.js +72 -11
- package/dist/lib/agent-clients/adapters/index.js +3 -1
- package/dist/lib/agent-clients/adapters/traecli.js +66 -0
- package/dist/lib/agent-clients/tokens.js +2 -2
- package/dist/lib/agent-clients/types.js +2 -1
- package/dist/lib/defaults.json +5 -0
- package/dist/lib/internal/codex-lifecycle.js +2 -2
- package/dist/lib/internal/codex-sandbox-controller.js +4 -3
- package/dist/lib/internal/sandbox-control.js +2 -2
- package/dist/lib/{task/commands/validate.js → internal/task-validate.js} +75 -42
- package/dist/lib/run/tui.js +5 -2
- package/dist/lib/sandbox/control/controller-registration.js +4 -2
- package/dist/lib/sandbox/control/executor.js +4 -1
- package/dist/lib/task/codex-orchestration.js +31 -34
- package/dist/lib/task/delegation-receipts.js +8 -7
- package/dist/lib/task/finalization.js +72 -46
- package/dist/lib/task/index.js +0 -9
- package/dist/lib/task/orchestration.js +1 -1
- package/dist/lib/task/verification-engine.js +0 -3
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-clients/adapters/codex-lifecycle/build-identity.ts +25 -10
- package/lib/agent-clients/adapters/codex-lifecycle/controller-context.ts +16 -33
- package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +16 -35
- package/lib/agent-clients/adapters/codex-sandbox.ts +89 -14
- package/lib/agent-clients/adapters/index.ts +3 -1
- package/lib/agent-clients/adapters/traecli.ts +67 -0
- package/lib/agent-clients/tokens.ts +2 -2
- package/lib/agent-clients/types.ts +2 -1
- package/lib/defaults.json +5 -0
- package/lib/internal/codex-lifecycle.ts +2 -2
- package/lib/internal/codex-sandbox-controller.ts +4 -3
- package/lib/internal/sandbox-control.ts +2 -2
- package/lib/{task/commands/validate.ts → internal/task-validate.ts} +75 -40
- package/lib/run/tui.ts +5 -3
- package/lib/sandbox/control/controller-registration.ts +8 -3
- package/lib/sandbox/control/executor.ts +8 -2
- package/lib/task/codex-orchestration.ts +38 -44
- package/lib/task/delegation-receipts.ts +8 -6
- package/lib/task/finalization.ts +84 -42
- package/lib/task/index.ts +0 -8
- package/lib/task/orchestration.ts +2 -1
- package/lib/task/verification-engine.ts +0 -3
- package/package.json +1 -1
- package/templates/.agents/QUICKSTART.en.md +2 -3
- package/templates/.agents/QUICKSTART.zh-CN.md +2 -3
- package/templates/.agents/rules/README.en.md +1 -0
- package/templates/.agents/rules/README.zh-CN.md +1 -0
- package/templates/.agents/rules/compatibility-policy.en.md +39 -0
- package/templates/.agents/rules/compatibility-policy.zh-CN.md +39 -0
- package/templates/.agents/rules/lifecycle-orchestration.en.md +4 -4
- package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +4 -4
- package/templates/.agents/rules/pr-sync.en.md +1 -1
- package/templates/.agents/rules/pr-sync.github.en.md +2 -1
- package/templates/.agents/rules/pr-sync.github.zh-CN.md +2 -0
- package/templates/.agents/rules/pr-sync.zh-CN.md +1 -0
- package/templates/.agents/rules/review-method.en.md +1 -0
- package/templates/.agents/rules/review-method.zh-CN.md +1 -0
- package/templates/.agents/skills/analyze-task/SKILL.en.md +1 -0
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +1 -0
- package/templates/.agents/skills/code-task/SKILL.en.md +1 -0
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +2 -0
- package/templates/.agents/skills/create-pr/SKILL.en.md +12 -6
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +12 -6
- package/templates/.agents/skills/create-pr/reference/change-report.en.md +41 -0
- package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +41 -0
- package/templates/.agents/skills/plan-task/SKILL.en.md +2 -1
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +2 -1
- package/templates/.agents/skills/review-code/reference/migration-risks.en.md +8 -6
- package/templates/.agents/skills/review-code/reference/migration-risks.zh-CN.md +8 -6
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +3 -3
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.en.md +1 -1
- package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.zh-CN.md +1 -1
- package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +2 -2
- package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +2 -2
- package/templates/.agents/skills/run-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/run-task/reference/host-validation.en.md +2 -2
- package/templates/.agents/skills/run-task/reference/host-validation.zh-CN.md +2 -2
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +24 -0
- package/templates/.trae/skills/update-agent-infra.en.md +8 -0
- package/templates/.trae/skills/update-agent-infra.zh-CN.md +8 -0
package/dist/bin/internal-cli.js
CHANGED
|
@@ -157,6 +157,11 @@ else
|
|
|
157
157
|
taskVerify(process.argv.slice(3));
|
|
158
158
|
break;
|
|
159
159
|
}
|
|
160
|
+
case 'task-validate': {
|
|
161
|
+
const { taskValidate } = await import('../lib/internal/task-validate.js');
|
|
162
|
+
taskValidate(process.argv.slice(3));
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
160
165
|
default:
|
|
161
166
|
process.stdout.write(`${JSON.stringify({
|
|
162
167
|
status: 'failed',
|
|
@@ -145,25 +145,32 @@ function verifyLifecycleBuildIdentity(expected, actual) {
|
|
|
145
145
|
return {
|
|
146
146
|
ok: false,
|
|
147
147
|
code: 'CODEX_LIFECYCLE_PROTOCOL_MISMATCH',
|
|
148
|
-
message: 'Codex lifecycle protocol version does not match'
|
|
148
|
+
message: 'Codex lifecycle protocol version does not match',
|
|
149
|
+
warnings: []
|
|
149
150
|
};
|
|
150
151
|
}
|
|
152
|
+
const warnings = [];
|
|
151
153
|
if (actual.packageVersion !== expected.packageVersion
|
|
152
154
|
|| actual.internalExecutableBuildHash !== expected.internalExecutableBuildHash) {
|
|
153
|
-
|
|
154
|
-
ok: false,
|
|
155
|
+
warnings.push({
|
|
155
156
|
code: 'CODEX_LIFECYCLE_BUILD_MISMATCH',
|
|
156
|
-
message: 'Codex lifecycle executable build identity
|
|
157
|
-
|
|
157
|
+
message: 'Codex lifecycle executable build identity differs; rebuild the sandbox if the runtime is stale',
|
|
158
|
+
action: 'rebuild-sandbox'
|
|
159
|
+
});
|
|
158
160
|
}
|
|
159
161
|
if (actual.lifecycleContractHash !== expected.lifecycleContractHash) {
|
|
160
|
-
|
|
161
|
-
ok: false,
|
|
162
|
+
warnings.push({
|
|
162
163
|
code: 'CODEX_LIFECYCLE_CONTRACT_MISMATCH',
|
|
163
|
-
message: 'Codex lifecycle contract identity
|
|
164
|
-
|
|
164
|
+
message: 'Codex lifecycle contract identity differs; rebuild the sandbox if the runtime is stale',
|
|
165
|
+
action: 'rebuild-sandbox'
|
|
166
|
+
});
|
|
165
167
|
}
|
|
166
|
-
return {
|
|
168
|
+
return {
|
|
169
|
+
ok: true,
|
|
170
|
+
code: null,
|
|
171
|
+
message: null,
|
|
172
|
+
warnings: Object.freeze(warnings)
|
|
173
|
+
};
|
|
167
174
|
}
|
|
168
175
|
export { LIFECYCLE_PROTOCOL_VERSION, computeLifecycleBuildIdentity, isCanonicalLifecyclePackageVersion, isLifecycleProtocolVersion, readLifecycleManifestFiles, resolveLifecycleExecutableFiles, verifyLifecycleBuildIdentity };
|
|
169
176
|
//# sourceMappingURL=build-identity.js.map
|
|
@@ -2,28 +2,11 @@ import crypto from 'node:crypto';
|
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { getProcessIdentityState } from '../../../server/process-state.js';
|
|
5
|
-
import { computeLifecycleBuildIdentity } from './build-identity.js';
|
|
5
|
+
import { computeLifecycleBuildIdentity, verifyLifecycleBuildIdentity } from './build-identity.js';
|
|
6
6
|
const HEX_256 = /^[a-f0-9]{64}$/u;
|
|
7
7
|
function exactKeys(value, keys) {
|
|
8
8
|
return Object.keys(value).sort().join(',') === [...keys].sort().join(',');
|
|
9
9
|
}
|
|
10
|
-
function digestProfiles(repoRoot) {
|
|
11
|
-
const files = [
|
|
12
|
-
path.join(repoRoot, '.codex', 'agents', 'agent-infra-lifecycle-executor.toml'),
|
|
13
|
-
path.join(repoRoot, '.codex', 'agents', 'agent-infra-lifecycle-reviewer.toml')
|
|
14
|
-
];
|
|
15
|
-
const hash = crypto.createHash('sha256');
|
|
16
|
-
for (const file of files.sort()) {
|
|
17
|
-
const stat = fs.lstatSync(file);
|
|
18
|
-
if (!stat.isFile() || stat.isSymbolicLink())
|
|
19
|
-
throw new Error('CODEX_SANDBOX_CONTROLLER_BUNDLE_MISMATCH');
|
|
20
|
-
hash.update(path.basename(file));
|
|
21
|
-
hash.update('\0');
|
|
22
|
-
hash.update(fs.readFileSync(file));
|
|
23
|
-
hash.update('\0');
|
|
24
|
-
}
|
|
25
|
-
return hash.digest('hex');
|
|
26
|
-
}
|
|
27
10
|
function validProcess(value) {
|
|
28
11
|
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
29
12
|
return false;
|
|
@@ -53,7 +36,7 @@ function validateContext(value) {
|
|
|
53
36
|
if (!exactKeys(context, [
|
|
54
37
|
'buildIdentity', 'controlGeneration', 'controllerInstanceDigest', 'controllerLease',
|
|
55
38
|
'controllerProcess', 'expiresAt', 'hookDefinitionHash', 'issuedAt',
|
|
56
|
-
'
|
|
39
|
+
'taskId', 'version'
|
|
57
40
|
])
|
|
58
41
|
|| context.version !== 2
|
|
59
42
|
|| typeof context.taskId !== 'string' || context.taskId.length === 0
|
|
@@ -68,13 +51,12 @@ function validateContext(value) {
|
|
|
68
51
|
|| !Number.isSafeInteger(context.expiresAt)
|
|
69
52
|
|| context.expiresAt <= context.issuedAt
|
|
70
53
|
|| !validBuildIdentity(context.buildIdentity)
|
|
71
|
-
|| typeof context.hookDefinitionHash !== 'string' || !HEX_256.test(context.hookDefinitionHash)
|
|
72
|
-
|| typeof context.lifecycleProfilesHash !== 'string' || !HEX_256.test(context.lifecycleProfilesHash)) {
|
|
54
|
+
|| typeof context.hookDefinitionHash !== 'string' || !HEX_256.test(context.hookDefinitionHash)) {
|
|
73
55
|
throw new Error('CODEX_SANDBOX_CONTROLLER_CONTEXT_INVALID');
|
|
74
56
|
}
|
|
75
57
|
return context;
|
|
76
58
|
}
|
|
77
|
-
export function contextFromControllerLease(lease,
|
|
59
|
+
export function contextFromControllerLease(lease, input) {
|
|
78
60
|
return Object.freeze({
|
|
79
61
|
version: 2,
|
|
80
62
|
taskId: lease.taskId,
|
|
@@ -85,8 +67,7 @@ export function contextFromControllerLease(lease, hashes) {
|
|
|
85
67
|
issuedAt: lease.issuedAt,
|
|
86
68
|
expiresAt: lease.expiresAt,
|
|
87
69
|
buildIdentity: lease.buildIdentity,
|
|
88
|
-
hookDefinitionHash:
|
|
89
|
-
lifecycleProfilesHash: hashes.lifecycleProfilesHash
|
|
70
|
+
hookDefinitionHash: input.hookDefinitionHash
|
|
90
71
|
});
|
|
91
72
|
}
|
|
92
73
|
export function writeCodexSandboxControllerContext(contextPath, context) {
|
|
@@ -102,7 +83,7 @@ export function writeCodexSandboxControllerContext(contextPath, context) {
|
|
|
102
83
|
fs.rmSync(temporary, { force: true });
|
|
103
84
|
}
|
|
104
85
|
}
|
|
105
|
-
export function
|
|
86
|
+
export function verifyCodexSandboxControllerContextWithWarnings(contextPath, options = {}) {
|
|
106
87
|
let stat;
|
|
107
88
|
try {
|
|
108
89
|
stat = fs.lstatSync(contextPath);
|
|
@@ -129,12 +110,11 @@ export function verifyCodexSandboxControllerContext(contextPath, options = {}) {
|
|
|
129
110
|
|| state !== 'alive') {
|
|
130
111
|
throw new Error('CODEX_SANDBOX_CONTROLLER_CONTEXT_INVALID');
|
|
131
112
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
throw new Error(
|
|
136
|
-
}
|
|
137
|
-
return Object.freeze(context);
|
|
113
|
+
const currentBuildIdentity = computeLifecycleBuildIdentity(repoRoot);
|
|
114
|
+
const identity = verifyLifecycleBuildIdentity(context.buildIdentity, currentBuildIdentity);
|
|
115
|
+
if (!identity.ok)
|
|
116
|
+
throw new Error(`${identity.code}: ${identity.message}`);
|
|
117
|
+
return Object.freeze({ context: Object.freeze(context), warnings: identity.warnings });
|
|
138
118
|
}
|
|
139
119
|
export function controllerProofFromContext(context) {
|
|
140
120
|
return Object.freeze({
|
|
@@ -8,22 +8,8 @@ import * as toml from 'smol-toml';
|
|
|
8
8
|
import { requestCodexControllerClose, requestCodexControllerOpen, requestCodexControllerVerify, requestSandboxControl } from '../../../sandbox/control/client.js';
|
|
9
9
|
import { readSandboxControlStatus } from '../../../sandbox/control/state.js';
|
|
10
10
|
import { getProcessStartTime } from '../../../server/process-state.js';
|
|
11
|
-
import {
|
|
12
|
-
import { contextFromControllerLease, controllerProofFromContext,
|
|
13
|
-
function digestFiles(files) {
|
|
14
|
-
const hash = crypto.createHash('sha256');
|
|
15
|
-
for (const file of [...files].sort()) {
|
|
16
|
-
const stat = fs.lstatSync(file);
|
|
17
|
-
if (!stat.isFile() || stat.isSymbolicLink()) {
|
|
18
|
-
throw new Error('CODEX_SANDBOX_CONTROLLER_BUNDLE_MISMATCH');
|
|
19
|
-
}
|
|
20
|
-
hash.update(path.basename(file));
|
|
21
|
-
hash.update('\0');
|
|
22
|
-
hash.update(fs.readFileSync(file));
|
|
23
|
-
hash.update('\0');
|
|
24
|
-
}
|
|
25
|
-
return hash.digest('hex');
|
|
26
|
-
}
|
|
11
|
+
import { LIFECYCLE_PROTOCOL_VERSION } from './build-identity.js';
|
|
12
|
+
import { contextFromControllerLease, controllerProofFromContext, verifyCodexSandboxControllerContextWithWarnings as verifyContextFileWithWarnings, writeCodexSandboxControllerContext } from './controller-context.js';
|
|
27
13
|
function copyRegular(source, destination, mode) {
|
|
28
14
|
const stat = fs.lstatSync(source);
|
|
29
15
|
if (!stat.isFile() || stat.isSymbolicLink()) {
|
|
@@ -108,14 +94,15 @@ function verifyControl(taskId, control) {
|
|
|
108
94
|
if (payload.taskId !== taskId)
|
|
109
95
|
throw new Error('CODEX_SANDBOX_CONTROLLER_TASK_BINDING_INVALID');
|
|
110
96
|
}
|
|
111
|
-
function
|
|
97
|
+
function verifyCodexSandboxControllerContextWithWarnings(contextPath, options = {}) {
|
|
112
98
|
const control = options.control ?? controlFromEnvironment();
|
|
113
|
-
const
|
|
99
|
+
const fileVerification = verifyContextFileWithWarnings(contextPath, {
|
|
114
100
|
repoRoot: options.repoRoot,
|
|
115
101
|
now: options.now,
|
|
116
102
|
generation: control.generation,
|
|
117
103
|
probeProcess: options.probeProcess
|
|
118
104
|
});
|
|
105
|
+
const context = fileVerification.context;
|
|
119
106
|
const verified = (options.requestControllerVerify ?? requestCodexControllerVerify)({
|
|
120
107
|
controllerProof: controllerProofFromContext(context),
|
|
121
108
|
...control,
|
|
@@ -126,7 +113,10 @@ function verifyCodexSandboxControllerContext(contextPath, options = {}) {
|
|
|
126
113
|
|| verified.binding.controllerInstanceDigest !== context.controllerInstanceDigest) {
|
|
127
114
|
throw new Error('CODEX_SANDBOX_CONTROLLER_CONTEXT_INVALID');
|
|
128
115
|
}
|
|
129
|
-
return
|
|
116
|
+
return Object.freeze({
|
|
117
|
+
context,
|
|
118
|
+
warnings: fileVerification.warnings
|
|
119
|
+
});
|
|
130
120
|
}
|
|
131
121
|
function detectedCodexVersion() {
|
|
132
122
|
const result = spawnSync('codex', ['--version'], { encoding: 'utf8' });
|
|
@@ -261,29 +251,21 @@ function prepareCodexSandboxController(input, options = {}) {
|
|
|
261
251
|
copyRegular(hooks, path.join(home, 'hooks.json'), 0o600);
|
|
262
252
|
copyRegular(executor, path.join(home, 'agents', path.basename(executor)), 0o600);
|
|
263
253
|
copyRegular(reviewer, path.join(home, 'agents', path.basename(reviewer)), 0o600);
|
|
264
|
-
if (crypto.createHash('sha256').update(fs.readFileSync(path.join(home, 'hooks.json'))).digest('hex')
|
|
265
|
-
!== crypto.createHash('sha256').update(fs.readFileSync(hooks)).digest('hex')
|
|
266
|
-
|| digestFiles([
|
|
267
|
-
path.join(home, 'agents', path.basename(executor)),
|
|
268
|
-
path.join(home, 'agents', path.basename(reviewer))
|
|
269
|
-
]) !== digestFiles([executor, reviewer])) {
|
|
270
|
-
throw new Error('CODEX_SANDBOX_CONTROLLER_BUNDLE_MISMATCH');
|
|
271
|
-
}
|
|
272
|
-
const buildIdentity = computeLifecycleBuildIdentity(repoRoot);
|
|
273
254
|
const opened = (options.openController ?? requestCodexControllerOpen)({
|
|
274
255
|
controllerProcess: { pid: process.pid, startTime: parentStartTime },
|
|
275
256
|
...control,
|
|
276
257
|
timeoutMs: 30_000
|
|
277
258
|
});
|
|
278
259
|
const taskId = opened.lease.taskId;
|
|
260
|
+
if (opened.lease.buildIdentity.protocolVersion !== LIFECYCLE_PROTOCOL_VERSION) {
|
|
261
|
+
throw new Error('CODEX_LIFECYCLE_PROTOCOL_MISMATCH: Codex lifecycle protocol version does not match');
|
|
262
|
+
}
|
|
279
263
|
context = contextFromControllerLease(opened.lease, {
|
|
280
|
-
hookDefinitionHash: crypto.createHash('sha256').update(fs.readFileSync(hooks)).digest('hex')
|
|
281
|
-
lifecycleProfilesHash: digestFiles([executor, reviewer])
|
|
264
|
+
hookDefinitionHash: crypto.createHash('sha256').update(fs.readFileSync(hooks)).digest('hex')
|
|
282
265
|
});
|
|
283
266
|
if (opened.lease.controlGeneration !== control.generation
|
|
284
267
|
|| opened.lease.controllerProcess.pid !== process.pid
|
|
285
|
-
|| opened.lease.controllerProcess.startTime !== parentStartTime
|
|
286
|
-
|| JSON.stringify(opened.lease.buildIdentity) !== JSON.stringify(buildIdentity)) {
|
|
268
|
+
|| opened.lease.controllerProcess.startTime !== parentStartTime) {
|
|
287
269
|
throw new Error('SANDBOX_CONTROL_RESULT_INVALID');
|
|
288
270
|
}
|
|
289
271
|
(options.verifyTaskBinding ?? verifyControl)(taskId, control);
|
|
@@ -383,5 +365,5 @@ async function runCodexSandboxController(input, options = {}) {
|
|
|
383
365
|
prepared.cleanup();
|
|
384
366
|
}
|
|
385
367
|
}
|
|
386
|
-
export { prepareCodexSandboxController, runCodexSandboxController,
|
|
368
|
+
export { prepareCodexSandboxController, runCodexSandboxController, verifyCodexSandboxControllerContextWithWarnings, controllerProofFromContext };
|
|
387
369
|
//# sourceMappingURL=sandbox-controller.js.map
|
|
@@ -30,6 +30,8 @@ function resolveHostCatalogPath(value, hostHomeDir) {
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
const CODEX_DISABLED_FEATURE_FLAGS = ['apps', 'enable_mcp_apps'];
|
|
33
|
+
const SANDBOX_CODEX_HOOKS_PATH = '/workspace/.codex/hooks.json';
|
|
34
|
+
const CODEX_HOOK_TRUST_HASH = /^sha256:[a-f0-9]{64}$/;
|
|
33
35
|
function removeCodexMcpServers(sandboxParsed) {
|
|
34
36
|
if (!Object.hasOwn(sandboxParsed, 'mcp_servers')) {
|
|
35
37
|
return false;
|
|
@@ -61,19 +63,58 @@ function inheritDisabledCodexFeatureFlags(sandboxParsed, hostParsed) {
|
|
|
61
63
|
}
|
|
62
64
|
return changed;
|
|
63
65
|
}
|
|
64
|
-
function
|
|
65
|
-
if (!
|
|
66
|
-
return;
|
|
66
|
+
function inheritCodexHookTrustState(sandboxParsed, hostParsed, hostProjectDir) {
|
|
67
|
+
if (!hostProjectDir) {
|
|
68
|
+
return false;
|
|
67
69
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
return;
|
|
70
|
+
if (Object.hasOwn(sandboxParsed, 'hooks') && !isJsonObjectRecord(sandboxParsed.hooks)) {
|
|
71
|
+
return false;
|
|
71
72
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
const sandboxHooks = sandboxParsed.hooks ?? {};
|
|
74
|
+
if (Object.hasOwn(sandboxHooks, 'state') && !isJsonObjectRecord(sandboxHooks.state)) {
|
|
75
|
+
return false;
|
|
75
76
|
}
|
|
76
|
-
|
|
77
|
+
const sandboxState = sandboxHooks.state ?? {};
|
|
78
|
+
const sandboxPrefix = `${SANDBOX_CODEX_HOOKS_PATH}:`;
|
|
79
|
+
const nextState = Object.fromEntries(Object.entries(sandboxState).filter(([key]) => !key.startsWith(sandboxPrefix)));
|
|
80
|
+
const hostState = isJsonObjectRecord(hostParsed.hooks)
|
|
81
|
+
&& isJsonObjectRecord(hostParsed.hooks.state)
|
|
82
|
+
? hostParsed.hooks.state
|
|
83
|
+
: {};
|
|
84
|
+
const hostPrefix = `${path.join(hostProjectDir, '.codex', 'hooks.json')}:`;
|
|
85
|
+
for (const [key, value] of Object.entries(hostState)) {
|
|
86
|
+
if (!key.startsWith(hostPrefix) || !isJsonObjectRecord(value)) {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
const trustedHash = value.trusted_hash;
|
|
90
|
+
if (typeof trustedHash !== 'string' || !CODEX_HOOK_TRUST_HASH.test(trustedHash)) {
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
nextState[`${sandboxPrefix}${key.slice(hostPrefix.length)}`] = {
|
|
94
|
+
trusted_hash: trustedHash,
|
|
95
|
+
...(typeof value.enabled === 'boolean' ? { enabled: value.enabled } : {})
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
if (JSON.stringify(sandboxState) === JSON.stringify(nextState)) {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
if (Object.keys(nextState).length > 0) {
|
|
102
|
+
sandboxHooks.state = nextState;
|
|
103
|
+
sandboxParsed.hooks = sandboxHooks;
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
delete sandboxHooks.state;
|
|
107
|
+
if (Object.keys(sandboxHooks).length > 0) {
|
|
108
|
+
sandboxParsed.hooks = sandboxHooks;
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
delete sandboxParsed.hooks;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return true;
|
|
115
|
+
}
|
|
116
|
+
function ensureCodexModelInheritance(toolDir, hostHomeDir, containerCodexDir = '/home/devuser/.codex', hostProjectDir) {
|
|
117
|
+
if (!hostHomeDir) {
|
|
77
118
|
return;
|
|
78
119
|
}
|
|
79
120
|
const sandboxConfigPath = path.join(toolDir, 'config.toml');
|
|
@@ -87,8 +128,25 @@ function ensureCodexModelInheritance(toolDir, hostHomeDir, containerCodexDir = '
|
|
|
87
128
|
return;
|
|
88
129
|
}
|
|
89
130
|
}
|
|
131
|
+
const hostConfigPath = path.join(hostHomeDir, '.codex', 'config.toml');
|
|
132
|
+
let hostParsed = null;
|
|
133
|
+
if (fs.existsSync(hostConfigPath)) {
|
|
134
|
+
try {
|
|
135
|
+
hostParsed = toml.parse(fs.readFileSync(hostConfigPath, 'utf8'));
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
// An unavailable host trust source must not leave previously projected trust active.
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (!hostParsed) {
|
|
142
|
+
if (inheritCodexHookTrustState(sandboxParsed, {}, hostProjectDir)) {
|
|
143
|
+
fs.writeFileSync(sandboxConfigPath, `${toml.stringify(sandboxParsed)}\n`, 'utf8');
|
|
144
|
+
}
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
90
147
|
let changed = removeCodexMcpServers(sandboxParsed);
|
|
91
148
|
changed = inheritDisabledCodexFeatureFlags(sandboxParsed, hostParsed) || changed;
|
|
149
|
+
changed = inheritCodexHookTrustState(sandboxParsed, hostParsed, hostProjectDir) || changed;
|
|
92
150
|
const inheritSpecs = [
|
|
93
151
|
['model', 'string'],
|
|
94
152
|
['model_reasoning_effort', 'string'],
|
|
@@ -151,7 +209,10 @@ const codexBeforeContainerCreateHook = {
|
|
|
151
209
|
message: 'Codex sandbox hook requires its resolved tool state.'
|
|
152
210
|
};
|
|
153
211
|
}
|
|
154
|
-
|
|
212
|
+
const hostProjectDir = typeof context.config?.repoRoot === 'string'
|
|
213
|
+
? context.config.repoRoot
|
|
214
|
+
: undefined;
|
|
215
|
+
ensureCodexModelInheritance(entry.dir, create.hostHome, entry.tool.containerMount, hostProjectDir);
|
|
155
216
|
ensureCodexWorkspaceTrust(entry.dir);
|
|
156
217
|
return { status: 'ready' };
|
|
157
218
|
}
|
|
@@ -3,11 +3,13 @@ import { claudeCodeAdapter } from './claude-code.js';
|
|
|
3
3
|
import { codexAdapter } from './codex.js';
|
|
4
4
|
import { antigravityCliAdapter } from './antigravity-cli.js';
|
|
5
5
|
import { opencodeAdapter } from './opencode.js';
|
|
6
|
+
import { traecliAdapter } from './traecli.js';
|
|
6
7
|
const adaptersById = new Map([
|
|
7
8
|
claudeCodeAdapter,
|
|
8
9
|
codexAdapter,
|
|
9
10
|
antigravityCliAdapter,
|
|
10
|
-
opencodeAdapter
|
|
11
|
+
opencodeAdapter,
|
|
12
|
+
traecliAdapter
|
|
11
13
|
].map((adapter) => [adapter.id, adapter]));
|
|
12
14
|
const BUILTIN_AGENT_CLIENT_ADAPTERS = Object.freeze(AGENT_CLIENT_IDS.map((id) => {
|
|
13
15
|
const adapter = adaptersById.get(id);
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { defineAgentClientAdapter } from '../adapter.js';
|
|
2
|
+
import { hostJoin } from '../../sandbox/engines/wsl2-paths.js';
|
|
3
|
+
const traecliAdapter = defineAgentClientAdapter({
|
|
4
|
+
id: 'traecli',
|
|
5
|
+
displayName: 'TraeCode CLI',
|
|
6
|
+
invocation: '/${skillName}',
|
|
7
|
+
capabilities: {
|
|
8
|
+
instructions: { level: 'compatible' },
|
|
9
|
+
skills: { level: 'compatible' },
|
|
10
|
+
commands: { level: 'integrated' },
|
|
11
|
+
hooks: { level: 'compatible' },
|
|
12
|
+
subagents: { level: 'experimental' },
|
|
13
|
+
orchestration: { level: 'experimental' },
|
|
14
|
+
sandbox: { level: 'integrated' },
|
|
15
|
+
verification: { level: 'compatible' }
|
|
16
|
+
},
|
|
17
|
+
modelSelection: {
|
|
18
|
+
kind: 'interactive-only',
|
|
19
|
+
command: '/model',
|
|
20
|
+
guidance: 'Use the host model picker for the complete model and reasoning-effort catalog.'
|
|
21
|
+
},
|
|
22
|
+
delegationEvidence: {
|
|
23
|
+
actualModel: 'unavailable',
|
|
24
|
+
actualReasoningEffort: 'unavailable'
|
|
25
|
+
},
|
|
26
|
+
project: {
|
|
27
|
+
ownedPathPrefixes: ['.trae/'],
|
|
28
|
+
managed: ['.trae/skills/'],
|
|
29
|
+
merged: [],
|
|
30
|
+
ejected: [],
|
|
31
|
+
seedCommands: [{
|
|
32
|
+
templates: {
|
|
33
|
+
en: '.trae/skills/update-agent-infra.en.md',
|
|
34
|
+
'zh-CN': '.trae/skills/update-agent-infra.zh-CN.md'
|
|
35
|
+
},
|
|
36
|
+
target: '.trae/skills/update-agent-infra.md'
|
|
37
|
+
}],
|
|
38
|
+
customCommand: {
|
|
39
|
+
target: '.trae/skills/${skillName}.md',
|
|
40
|
+
frontmatter: {},
|
|
41
|
+
includeUsage: true,
|
|
42
|
+
inheritDisableModelInvocation: true
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
sandbox: {
|
|
46
|
+
createTool: ({ home }) => ({
|
|
47
|
+
id: 'traecli',
|
|
48
|
+
name: 'TraeCode CLI',
|
|
49
|
+
install: {
|
|
50
|
+
type: 'shell',
|
|
51
|
+
cmd: 'curl -fsSL https://trae.cn/trae-cli/install_v2.sh | bash'
|
|
52
|
+
},
|
|
53
|
+
sandboxBase: hostJoin(home, '.agent-infra', 'sandboxes', 'traecli'),
|
|
54
|
+
containerMount: '/home/devuser/.trae',
|
|
55
|
+
versionCmd: 'traecli --version',
|
|
56
|
+
setupHint: 'Run traecli login inside the container to authenticate.'
|
|
57
|
+
}),
|
|
58
|
+
aliases: [
|
|
59
|
+
{ name: 'traecli-yolo', command: 'traecli --dangerously-bypass-approvals-and-sandbox; tput ed' },
|
|
60
|
+
{ name: 'ty', command: 'traecli --dangerously-bypass-approvals-and-sandbox; tput ed' }
|
|
61
|
+
],
|
|
62
|
+
hooks: []
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
export { traecliAdapter };
|
|
66
|
+
//# sourceMappingURL=traecli.js.map
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// activity log. Migrated from lib/task/commands/log.ts so that the write-side
|
|
3
3
|
// internal commands and the rendering side share one definition. Historical
|
|
4
4
|
// Gemini tokens remain read-only and are handled by classifyAgent below.
|
|
5
|
-
export const KNOWN_AI_AGENTS = new Set(['claude', 'codex', 'antigravity', 'opencode', 'cursor']);
|
|
5
|
+
export const KNOWN_AI_AGENTS = new Set(['claude', 'codex', 'antigravity', 'opencode', 'cursor', 'traecli']);
|
|
6
6
|
// Long name -> short name mapping (HD-4, decision A): `claude-code` reuses the
|
|
7
7
|
// existing orchestration precedent, and `antigravity-cli` is the active
|
|
8
8
|
// client. `gemini-cli` is intentionally not writable.
|
|
@@ -10,7 +10,7 @@ export const AGENT_LONG_NAMES = {
|
|
|
10
10
|
'claude-code': 'claude',
|
|
11
11
|
'antigravity-cli': 'antigravity'
|
|
12
12
|
};
|
|
13
|
-
export const AGENT_USAGE_HINT = "agent must be a short token (claude/codex/antigravity/opencode/cursor), " +
|
|
13
|
+
export const AGENT_USAGE_HINT = "agent must be a short token (claude/codex/antigravity/opencode/cursor/traecli), " +
|
|
14
14
|
"a long name (claude-code -> claude, antigravity-cli -> antigravity), or 'human'";
|
|
15
15
|
// Read-side historical compatibility for activity-log rendering: `gemini` and
|
|
16
16
|
// `gemini-cli` stay recognizable when displaying old logs, but are never
|
package/dist/lib/defaults.json
CHANGED
|
@@ -6,7 +6,7 @@ import { preflightCodexLifecycleEvidence, resolveCodexTerminal, resolveCodexThre
|
|
|
6
6
|
import { createCodexLifecycleStore } from '../agent-clients/adapters/codex-lifecycle/store.js';
|
|
7
7
|
import { createCodexCapabilityStore } from '../agent-clients/adapters/codex-lifecycle/capability-store.js';
|
|
8
8
|
import { computeLifecycleBuildIdentity } from '../agent-clients/adapters/codex-lifecycle/build-identity.js';
|
|
9
|
-
import {
|
|
9
|
+
import { verifyCodexSandboxControllerContextWithWarnings } from '../agent-clients/adapters/codex-lifecycle/sandbox-controller.js';
|
|
10
10
|
import { resolveTaskRef } from '../task/resolve-ref.js';
|
|
11
11
|
import { hasSealableOrchestrationDelegation } from '../task/orchestration.js';
|
|
12
12
|
import { activateCodexOrchestrationDelegation, activateCodexSpawnDelegation, reconcileCodexOrchestrationDelegation, sealCodexOrchestrationDelegation, sealCodexParentDelegation } from '../task/codex-orchestration.js';
|
|
@@ -81,7 +81,7 @@ function controllerBinding(taskId) {
|
|
|
81
81
|
const contextPath = process.env.AGENT_INFRA_CODEX_CONTROLLER_CONTEXT;
|
|
82
82
|
if (!contextPath)
|
|
83
83
|
return undefined;
|
|
84
|
-
const context =
|
|
84
|
+
const context = verifyCodexSandboxControllerContextWithWarnings(contextPath, { repoRoot: process.cwd() }).context;
|
|
85
85
|
return {
|
|
86
86
|
instanceDigest: context.controllerInstanceDigest,
|
|
87
87
|
controlGeneration: context.controlGeneration
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { runCodexSandboxController,
|
|
1
|
+
import { runCodexSandboxController, verifyCodexSandboxControllerContextWithWarnings } from '../agent-clients/adapters/codex-lifecycle/sandbox-controller.js';
|
|
2
2
|
const USAGE = 'Usage: agent-infra-internal codex-sandbox-controller <run|verify-context> [options]\n';
|
|
3
3
|
function fail(code, message, exitCode = 1) {
|
|
4
4
|
process.stdout.write(`${JSON.stringify({
|
|
@@ -53,7 +53,8 @@ async function codexSandboxController(args = []) {
|
|
|
53
53
|
?? process.env.AGENT_INFRA_CODEX_CONTROLLER_CONTEXT;
|
|
54
54
|
if (!contextPath)
|
|
55
55
|
throw new Error('CODEX_SANDBOX_CONTROLLER_CONTEXT_MISSING');
|
|
56
|
-
const
|
|
56
|
+
const verified = verifyCodexSandboxControllerContextWithWarnings(contextPath);
|
|
57
|
+
const context = verified.context;
|
|
57
58
|
process.stdout.write(`${JSON.stringify({
|
|
58
59
|
status: 'ready',
|
|
59
60
|
changed: false,
|
|
@@ -64,8 +65,8 @@ async function codexSandboxController(args = []) {
|
|
|
64
65
|
expiresAt: context.expiresAt,
|
|
65
66
|
buildIdentity: context.buildIdentity,
|
|
66
67
|
hookDefinitionHash: context.hookDefinitionHash,
|
|
67
|
-
lifecycleProfilesHash: context.lifecycleProfilesHash
|
|
68
68
|
},
|
|
69
|
+
...(verified.warnings.length > 0 ? { warnings: verified.warnings } : {}),
|
|
69
70
|
error: null
|
|
70
71
|
})}\n`);
|
|
71
72
|
return;
|
|
@@ -2,7 +2,7 @@ import { requestSandboxControl, requestSandboxTaskFinalization, requestSandboxTa
|
|
|
2
2
|
import { normalizeAgentToken, AGENT_USAGE_HINT } from '../agent-clients/tokens.js';
|
|
3
3
|
import { serveSandboxControl } from '../sandbox/control/server.js';
|
|
4
4
|
import { runSandboxControlExecutor } from '../sandbox/control/executor.js';
|
|
5
|
-
import { controllerProofFromContext,
|
|
5
|
+
import { controllerProofFromContext, verifyCodexSandboxControllerContextWithWarnings } from '../agent-clients/adapters/codex-lifecycle/sandbox-controller.js';
|
|
6
6
|
function isCanonicalCodexPrepare(args) {
|
|
7
7
|
if (args[1] !== 'prepare')
|
|
8
8
|
return false;
|
|
@@ -106,7 +106,7 @@ async function sandboxControl(args) {
|
|
|
106
106
|
if (family === 'task-orchestration' && isCanonicalCodexPrepare(commandArgs)) {
|
|
107
107
|
const contextPath = process.env.AGENT_INFRA_CODEX_CONTROLLER_CONTEXT;
|
|
108
108
|
const proof = contextPath
|
|
109
|
-
? controllerProofFromContext(
|
|
109
|
+
? controllerProofFromContext(verifyCodexSandboxControllerContextWithWarnings(contextPath).context)
|
|
110
110
|
: null;
|
|
111
111
|
response = requestSandboxTaskControl({ family, args: commandArgs, controllerProof: proof });
|
|
112
112
|
}
|