@fitlab-ai/agent-infra 0.9.8 → 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/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/codex-lifecycle/app-server.js +58 -21
- package/dist/lib/agent-clients/adapters/codex-lifecycle/build-identity.js +25 -11
- package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +173 -10
- package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-context.js +127 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/evidence.js +14 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +63 -92
- 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/git/workflow.js +10 -1
- package/dist/lib/internal/codex-lifecycle.js +20 -6
- package/dist/lib/internal/codex-sandbox-controller.js +9 -15
- 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 +2 -1
- package/dist/lib/internal/sandbox-control.js +79 -2
- package/dist/lib/internal/task-finalization.js +54 -0
- package/dist/lib/internal/task-orchestration.js +156 -291
- package/dist/lib/{task/commands/validate.js → internal/task-validate.js} +75 -42
- package/dist/lib/platform/pr-summary.js +48 -10
- package/dist/lib/platform/pull-requests.js +205 -41
- package/dist/lib/run/tui.js +5 -2
- package/dist/lib/sandbox/commands/rm.js +316 -55
- package/dist/lib/sandbox/control/client.js +190 -6
- package/dist/lib/sandbox/control/controller-registration.js +275 -0
- package/dist/lib/sandbox/control/executor.js +140 -5
- package/dist/lib/sandbox/control/lifecycle.js +3 -1
- package/dist/lib/sandbox/control/protocol.js +67 -6
- package/dist/lib/sandbox/index.js +5 -3
- package/dist/lib/sandbox/managed-fs.js +5 -0
- package/dist/lib/sandbox/task-resolver.js +50 -6
- package/dist/lib/sandbox/workspace-identity.js +27 -0
- package/dist/lib/sandbox/worktree-safety.js +215 -2
- package/dist/lib/task/codex-orchestration.js +161 -39
- 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/delegation-receipts.js +248 -6
- package/dist/lib/task/finalization.js +593 -0
- package/dist/lib/task/index.js +0 -9
- package/dist/lib/task/operation-outcome.js +33 -0
- package/dist/lib/task/orchestration.js +187 -761
- 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 +110 -60
- package/dist/lib/task/verification.js +2 -1
- package/dist/lib/task/workflow-warning-intents.js +50 -1
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-clients/adapters/codex-lifecycle/app-server.ts +61 -19
- package/lib/agent-clients/adapters/codex-lifecycle/build-identity.ts +37 -10
- package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +276 -12
- package/lib/agent-clients/adapters/codex-lifecycle/controller-context.ts +163 -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 +91 -118
- 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/git/workflow.ts +11 -2
- package/lib/internal/codex-lifecycle.ts +19 -6
- package/lib/internal/codex-sandbox-controller.ts +9 -14
- package/lib/internal/git-workflow.ts +5 -6
- package/lib/internal/platform-pr.ts +14 -7
- package/lib/internal/release-workflow.ts +2 -1
- package/lib/internal/sandbox-control.ts +92 -2
- package/lib/internal/task-finalization.ts +68 -0
- package/lib/internal/task-orchestration.ts +20 -103
- package/lib/{task/commands/validate.ts → internal/task-validate.ts} +75 -40
- package/lib/platform/pr-summary.ts +55 -11
- package/lib/platform/pull-requests.ts +211 -41
- package/lib/run/tui.ts +5 -3
- package/lib/sandbox/commands/rm.ts +413 -61
- package/lib/sandbox/control/client.ts +255 -6
- package/lib/sandbox/control/controller-registration.ts +352 -0
- package/lib/sandbox/control/executor.ts +169 -5
- package/lib/sandbox/control/lifecycle.ts +2 -1
- package/lib/sandbox/control/protocol.ts +80 -7
- package/lib/sandbox/index.ts +5 -3
- package/lib/sandbox/managed-fs.ts +5 -0
- package/lib/sandbox/task-resolver.ts +58 -7
- package/lib/sandbox/workspace-identity.ts +50 -2
- package/lib/sandbox/workspace-view.ts +3 -3
- package/lib/sandbox/worktree-safety.ts +236 -2
- package/lib/task/codex-orchestration.ts +168 -40
- 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/delegation-receipts.ts +263 -4
- package/lib/task/finalization.ts +761 -0
- package/lib/task/index.ts +0 -8
- package/lib/task/operation-outcome.ts +48 -0
- package/lib/task/orchestration.ts +218 -982
- package/lib/task/short-id.ts +65 -1
- package/lib/task/task-execution-lock.ts +10 -1
- package/lib/task/verification-engine.ts +115 -63
- package/lib/task/verification.ts +3 -2
- package/lib/task/workflow-warning-intents.ts +57 -2
- package/package.json +2 -2
- package/templates/.agents/QUICKSTART.en.md +3 -4
- package/templates/.agents/QUICKSTART.zh-CN.md +3 -4
- package/templates/.agents/hooks/lifecycle-delegation.js +1 -21
- 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/rules/task-management.en.md +2 -2
- package/templates/.agents/rules/task-management.zh-CN.md +2 -2
- 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/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/code-task/SKILL.en.md +1 -0
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +2 -0
- 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 +17 -9
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +17 -9
- 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/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/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 +6 -6
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +6 -6
- 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/.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/templates/.trae/skills/update-agent-infra.en.md +8 -0
- package/templates/.trae/skills/update-agent-infra.zh-CN.md +8 -0
- 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
|
@@ -6,11 +6,22 @@ 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,
|
|
12
|
-
import {
|
|
13
|
-
import
|
|
16
|
+
import { getProcessStartTime, type ProcessIdentity } from '../../../server/process-state.ts';
|
|
17
|
+
import { LIFECYCLE_PROTOCOL_VERSION, type LifecycleIdentityWarning } from './build-identity.ts';
|
|
18
|
+
import {
|
|
19
|
+
contextFromControllerLease,
|
|
20
|
+
controllerProofFromContext,
|
|
21
|
+
verifyCodexSandboxControllerContextWithWarnings as verifyContextFileWithWarnings,
|
|
22
|
+
writeCodexSandboxControllerContext,
|
|
23
|
+
type CodexSandboxControllerContextV2
|
|
24
|
+
} from './controller-context.ts';
|
|
14
25
|
|
|
15
26
|
type ControllerControl = Readonly<{
|
|
16
27
|
token: string;
|
|
@@ -20,23 +31,7 @@ type ControllerControl = Readonly<{
|
|
|
20
31
|
runtimeDir: string;
|
|
21
32
|
}>;
|
|
22
33
|
|
|
23
|
-
type CodexSandboxControllerContext = Readonly<{
|
|
24
|
-
version: 1;
|
|
25
|
-
taskId: string;
|
|
26
|
-
controlGeneration: string;
|
|
27
|
-
controllerInstanceDigest: string;
|
|
28
|
-
parentPid: number;
|
|
29
|
-
parentStartTime: number;
|
|
30
|
-
issuedAt: number;
|
|
31
|
-
expiresAt: number;
|
|
32
|
-
buildIdentity: LifecycleBuildIdentity;
|
|
33
|
-
hookDefinitionHash: string;
|
|
34
|
-
lifecycleProfilesHash: string;
|
|
35
|
-
}>;
|
|
36
|
-
|
|
37
34
|
type ControllerInput = Readonly<{
|
|
38
|
-
taskId: string;
|
|
39
|
-
taskRef: string;
|
|
40
35
|
executorModel?: string;
|
|
41
36
|
executorReasoningEffort?: string;
|
|
42
37
|
reviewerModel?: string;
|
|
@@ -51,6 +46,8 @@ type ControllerOptions = Readonly<{
|
|
|
51
46
|
now?: () => number;
|
|
52
47
|
codexVersion?: () => string;
|
|
53
48
|
verifyTaskBinding?: (taskId: string, control: ControllerControl) => void;
|
|
49
|
+
openController?: typeof requestCodexControllerOpen;
|
|
50
|
+
closeController?: typeof requestCodexControllerClose;
|
|
54
51
|
environment?: NodeJS.ProcessEnv;
|
|
55
52
|
}>;
|
|
56
53
|
|
|
@@ -60,26 +57,18 @@ type PreparedCodexSandboxController = Readonly<{
|
|
|
60
57
|
env: NodeJS.ProcessEnv;
|
|
61
58
|
home: string;
|
|
62
59
|
contextPath: string;
|
|
63
|
-
context:
|
|
60
|
+
context: CodexSandboxControllerContextV2;
|
|
64
61
|
cleanup: () => void;
|
|
65
62
|
}>;
|
|
66
63
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
hash.update(path.basename(file));
|
|
77
|
-
hash.update('\0');
|
|
78
|
-
hash.update(fs.readFileSync(file));
|
|
79
|
-
hash.update('\0');
|
|
80
|
-
}
|
|
81
|
-
return hash.digest('hex');
|
|
82
|
-
}
|
|
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
|
+
}>;
|
|
83
72
|
|
|
84
73
|
function copyRegular(source: string, destination: string, mode: number): void {
|
|
85
74
|
const stat = fs.lstatSync(source);
|
|
@@ -162,6 +151,34 @@ function verifyControl(taskId: string, control: ControllerControl): void {
|
|
|
162
151
|
if (payload.taskId !== taskId) throw new Error('CODEX_SANDBOX_CONTROLLER_TASK_BINDING_INVALID');
|
|
163
152
|
}
|
|
164
153
|
|
|
154
|
+
function verifyCodexSandboxControllerContextWithWarnings(
|
|
155
|
+
contextPath: string,
|
|
156
|
+
options: VerifyContextOptions = {}
|
|
157
|
+
): Readonly<{ context: CodexSandboxControllerContextV2; warnings: readonly LifecycleIdentityWarning[] }> {
|
|
158
|
+
const control = options.control ?? controlFromEnvironment();
|
|
159
|
+
const fileVerification = verifyContextFileWithWarnings(contextPath, {
|
|
160
|
+
repoRoot: options.repoRoot,
|
|
161
|
+
now: options.now,
|
|
162
|
+
generation: control.generation,
|
|
163
|
+
probeProcess: options.probeProcess
|
|
164
|
+
});
|
|
165
|
+
const context = fileVerification.context;
|
|
166
|
+
const verified = (options.requestControllerVerify ?? requestCodexControllerVerify)({
|
|
167
|
+
controllerProof: controllerProofFromContext(context),
|
|
168
|
+
...control,
|
|
169
|
+
timeoutMs: 30_000
|
|
170
|
+
});
|
|
171
|
+
if (verified.binding.taskId !== context.taskId
|
|
172
|
+
|| verified.binding.controlGeneration !== context.controlGeneration
|
|
173
|
+
|| verified.binding.controllerInstanceDigest !== context.controllerInstanceDigest) {
|
|
174
|
+
throw new Error('CODEX_SANDBOX_CONTROLLER_CONTEXT_INVALID');
|
|
175
|
+
}
|
|
176
|
+
return Object.freeze({
|
|
177
|
+
context,
|
|
178
|
+
warnings: fileVerification.warnings
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
|
|
165
182
|
function detectedCodexVersion(): string {
|
|
166
183
|
const result = spawnSync('codex', ['--version'], { encoding: 'utf8' });
|
|
167
184
|
const version = /codex-cli\s+(\d+\.\d+\.\d+)/u.exec(result.stdout ?? '')?.[1];
|
|
@@ -256,7 +273,6 @@ function prepareCodexSandboxController(
|
|
|
256
273
|
const repoRoot = path.resolve(options.repoRoot ?? process.cwd());
|
|
257
274
|
const sourceHome = path.resolve(options.codexHome ?? process.env.CODEX_HOME ?? path.join(os.homedir(), '.codex'));
|
|
258
275
|
const control = options.control ?? controlFromEnvironment();
|
|
259
|
-
(options.verifyTaskBinding ?? verifyControl)(input.taskId, control);
|
|
260
276
|
const version = (options.codexVersion ?? detectedCodexVersion)();
|
|
261
277
|
if (!semver.gte(version, '0.147.0')) throw new Error('CODEX_SANDBOX_CONTROLLER_CODEX_UNSUPPORTED');
|
|
262
278
|
|
|
@@ -265,46 +281,33 @@ function prepareCodexSandboxController(
|
|
|
265
281
|
fs.mkdirSync(runtimeRoot, { recursive: true, mode: 0o700 });
|
|
266
282
|
fs.chmodSync(runtimeRoot, 0o700);
|
|
267
283
|
const key = crypto.createHash('sha256')
|
|
268
|
-
.update(`${
|
|
284
|
+
.update(`${process.pid}\0${control.generation}`)
|
|
269
285
|
.digest('hex');
|
|
270
|
-
const leasePath = path.join(runtimeRoot, `${key}.lease.json`);
|
|
271
|
-
if (fs.existsSync(leasePath)) {
|
|
272
|
-
const lease = JSON.parse(fs.readFileSync(leasePath, 'utf8')) as { pid?: number; startTime?: number };
|
|
273
|
-
if (Number.isSafeInteger(lease.pid) && typeof lease.startTime === 'number'
|
|
274
|
-
&& getProcessStartTime(lease.pid!) === lease.startTime) {
|
|
275
|
-
throw new Error('CODEX_SANDBOX_CONTROLLER_BUSY');
|
|
276
|
-
}
|
|
277
|
-
fs.unlinkSync(leasePath);
|
|
278
|
-
}
|
|
279
|
-
|
|
280
286
|
const parentStartTime = getProcessStartTime(process.pid);
|
|
281
287
|
if (!parentStartTime) throw new Error('CODEX_SANDBOX_CONTROLLER_PROCESS_IDENTITY_INVALID');
|
|
282
|
-
const instanceDigest = crypto.randomBytes(32).toString('hex');
|
|
283
|
-
fs.writeFileSync(leasePath, `${JSON.stringify({
|
|
284
|
-
version: 1,
|
|
285
|
-
pid: process.pid,
|
|
286
|
-
startTime: parentStartTime,
|
|
287
|
-
taskId: input.taskId,
|
|
288
|
-
generation: control.generation,
|
|
289
|
-
instanceDigest
|
|
290
|
-
})}\n`, { mode: 0o600, flag: 'wx' });
|
|
291
|
-
|
|
292
288
|
const home = fs.mkdtempSync(path.join(runtimeRoot, `${key}-`));
|
|
293
289
|
fs.chmodSync(home, 0o700);
|
|
294
290
|
let cleaned = false;
|
|
291
|
+
let context: CodexSandboxControllerContextV2 | null = null;
|
|
295
292
|
const cleanup = () => {
|
|
296
293
|
if (cleaned) return;
|
|
297
294
|
cleaned = true;
|
|
295
|
+
if (context) {
|
|
296
|
+
try {
|
|
297
|
+
(options.closeController ?? requestCodexControllerClose)({
|
|
298
|
+
controllerProcess: context.controllerProcess,
|
|
299
|
+
controllerProof: controllerProofFromContext(context),
|
|
300
|
+
...control,
|
|
301
|
+
timeoutMs: 30_000
|
|
302
|
+
});
|
|
303
|
+
} catch {
|
|
304
|
+
// The wrapper is exiting; dead-process recovery safely handles an unknown close result.
|
|
305
|
+
}
|
|
306
|
+
}
|
|
298
307
|
const relative = path.relative(runtimeRoot, home);
|
|
299
308
|
if (!relative.startsWith('..') && !path.isAbsolute(relative)) {
|
|
300
309
|
fs.rmSync(home, { recursive: true, force: true });
|
|
301
310
|
}
|
|
302
|
-
try {
|
|
303
|
-
const lease = JSON.parse(fs.readFileSync(leasePath, 'utf8')) as { instanceDigest?: string };
|
|
304
|
-
if (lease.instanceDigest === instanceDigest) fs.unlinkSync(leasePath);
|
|
305
|
-
} catch {
|
|
306
|
-
// A missing or replaced lease is owned by another cleanup path.
|
|
307
|
-
}
|
|
308
311
|
};
|
|
309
312
|
|
|
310
313
|
try {
|
|
@@ -323,32 +326,27 @@ function prepareCodexSandboxController(
|
|
|
323
326
|
copyRegular(hooks, path.join(home, 'hooks.json'), 0o600);
|
|
324
327
|
copyRegular(executor, path.join(home, 'agents', path.basename(executor)), 0o600);
|
|
325
328
|
copyRegular(reviewer, path.join(home, 'agents', path.basename(reviewer)), 0o600);
|
|
326
|
-
if (crypto.createHash('sha256').update(fs.readFileSync(path.join(home, 'hooks.json'))).digest('hex')
|
|
327
|
-
!== crypto.createHash('sha256').update(fs.readFileSync(hooks)).digest('hex')
|
|
328
|
-
|| digestFiles([
|
|
329
|
-
path.join(home, 'agents', path.basename(executor)),
|
|
330
|
-
path.join(home, 'agents', path.basename(reviewer))
|
|
331
|
-
]) !== digestFiles([executor, reviewer])) {
|
|
332
|
-
throw new Error('CODEX_SANDBOX_CONTROLLER_BUNDLE_MISMATCH');
|
|
333
|
-
}
|
|
334
329
|
|
|
335
|
-
const
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
buildIdentity,
|
|
347
|
-
hookDefinitionHash: crypto.createHash('sha256').update(fs.readFileSync(hooks)).digest('hex'),
|
|
348
|
-
lifecycleProfilesHash: digestFiles([executor, reviewer])
|
|
330
|
+
const opened = (options.openController ?? requestCodexControllerOpen)({
|
|
331
|
+
controllerProcess: { pid: process.pid, startTime: parentStartTime },
|
|
332
|
+
...control,
|
|
333
|
+
timeoutMs: 30_000
|
|
334
|
+
});
|
|
335
|
+
const taskId = opened.lease.taskId;
|
|
336
|
+
if (opened.lease.buildIdentity.protocolVersion !== LIFECYCLE_PROTOCOL_VERSION) {
|
|
337
|
+
throw new Error('CODEX_LIFECYCLE_PROTOCOL_MISMATCH: Codex lifecycle protocol version does not match');
|
|
338
|
+
}
|
|
339
|
+
context = contextFromControllerLease(opened.lease, {
|
|
340
|
+
hookDefinitionHash: crypto.createHash('sha256').update(fs.readFileSync(hooks)).digest('hex')
|
|
349
341
|
});
|
|
342
|
+
if (opened.lease.controlGeneration !== control.generation
|
|
343
|
+
|| opened.lease.controllerProcess.pid !== process.pid
|
|
344
|
+
|| opened.lease.controllerProcess.startTime !== parentStartTime) {
|
|
345
|
+
throw new Error('SANDBOX_CONTROL_RESULT_INVALID');
|
|
346
|
+
}
|
|
347
|
+
(options.verifyTaskBinding ?? verifyControl)(taskId, control);
|
|
350
348
|
const contextPath = path.join(home, 'controller-context.json');
|
|
351
|
-
|
|
349
|
+
writeCodexSandboxControllerContext(contextPath, context);
|
|
352
350
|
|
|
353
351
|
const shimDir = path.join(home, 'bin');
|
|
354
352
|
fs.mkdirSync(shimDir, { mode: 0o700 });
|
|
@@ -364,7 +362,7 @@ function prepareCodexSandboxController(
|
|
|
364
362
|
input.reviewerModel ? `--reviewer-model ${input.reviewerModel}` : '',
|
|
365
363
|
input.reviewerReasoningEffort ? `--reviewer-reasoning-effort ${input.reviewerReasoningEffort}` : ''
|
|
366
364
|
].filter(Boolean).join(' ');
|
|
367
|
-
const prompt = `$run-task ${
|
|
365
|
+
const prompt = `$run-task ${taskId}${policy ? ` ${policy}` : ''}`;
|
|
368
366
|
const args = Object.freeze([
|
|
369
367
|
'exec',
|
|
370
368
|
'--enable', 'hooks',
|
|
@@ -379,7 +377,7 @@ function prepareCodexSandboxController(
|
|
|
379
377
|
home,
|
|
380
378
|
shimDir,
|
|
381
379
|
contextPath,
|
|
382
|
-
|
|
380
|
+
taskId,
|
|
383
381
|
control,
|
|
384
382
|
providerEnvironment,
|
|
385
383
|
options.environment ?? process.env
|
|
@@ -399,32 +397,6 @@ function prepareCodexSandboxController(
|
|
|
399
397
|
}
|
|
400
398
|
}
|
|
401
399
|
|
|
402
|
-
function verifyCodexSandboxControllerContext(
|
|
403
|
-
contextPath: string,
|
|
404
|
-
taskId: string,
|
|
405
|
-
options: Readonly<{ repoRoot?: string; now?: number; generation?: string }> = {}
|
|
406
|
-
): CodexSandboxControllerContext {
|
|
407
|
-
const stat = fs.lstatSync(contextPath);
|
|
408
|
-
if (!stat.isFile() || stat.isSymbolicLink() || (stat.mode & 0o077) !== 0) {
|
|
409
|
-
throw new Error('CODEX_SANDBOX_CONTROLLER_CONTEXT_INVALID');
|
|
410
|
-
}
|
|
411
|
-
const context = JSON.parse(fs.readFileSync(contextPath, 'utf8')) as CodexSandboxControllerContext;
|
|
412
|
-
const generation = options.generation ?? process.env.AGENT_INFRA_CONTROL_GENERATION;
|
|
413
|
-
if (context.version !== 1
|
|
414
|
-
|| context.taskId !== taskId
|
|
415
|
-
|| context.controlGeneration !== generation
|
|
416
|
-
|| context.expiresAt < (options.now ?? Date.now())
|
|
417
|
-
|| getProcessStartTime(context.parentPid) !== context.parentStartTime
|
|
418
|
-
|| !isProcessAlive(context.parentPid)) {
|
|
419
|
-
throw new Error('CODEX_SANDBOX_CONTROLLER_CONTEXT_INVALID');
|
|
420
|
-
}
|
|
421
|
-
const current = computeLifecycleBuildIdentity(options.repoRoot ?? process.cwd());
|
|
422
|
-
if (JSON.stringify(current) !== JSON.stringify(context.buildIdentity)) {
|
|
423
|
-
throw new Error('CODEX_SANDBOX_CONTROLLER_BUNDLE_MISMATCH');
|
|
424
|
-
}
|
|
425
|
-
return Object.freeze(context);
|
|
426
|
-
}
|
|
427
|
-
|
|
428
400
|
async function runCodexSandboxController(
|
|
429
401
|
input: ControllerInput,
|
|
430
402
|
options: ControllerOptions = {}
|
|
@@ -469,10 +441,11 @@ async function runCodexSandboxController(
|
|
|
469
441
|
export {
|
|
470
442
|
prepareCodexSandboxController,
|
|
471
443
|
runCodexSandboxController,
|
|
472
|
-
|
|
444
|
+
verifyCodexSandboxControllerContextWithWarnings,
|
|
445
|
+
controllerProofFromContext
|
|
473
446
|
};
|
|
474
447
|
export type {
|
|
475
|
-
CodexSandboxControllerContext,
|
|
448
|
+
CodexSandboxControllerContextV2 as CodexSandboxControllerContext,
|
|
476
449
|
ControllerControl,
|
|
477
450
|
ControllerInput,
|
|
478
451
|
ControllerOptions,
|
|
@@ -37,6 +37,8 @@ function resolveHostCatalogPath(value: unknown, hostHomeDir: string): string | n
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
const CODEX_DISABLED_FEATURE_FLAGS = ['apps', 'enable_mcp_apps'] as const;
|
|
40
|
+
const SANDBOX_CODEX_HOOKS_PATH = '/workspace/.codex/hooks.json';
|
|
41
|
+
const CODEX_HOOK_TRUST_HASH = /^sha256:[a-f0-9]{64}$/;
|
|
40
42
|
|
|
41
43
|
function removeCodexMcpServers(sandboxParsed: JsonObject): boolean {
|
|
42
44
|
if (!Object.hasOwn(sandboxParsed, 'mcp_servers')) {
|
|
@@ -75,27 +77,75 @@ function inheritDisabledCodexFeatureFlags(
|
|
|
75
77
|
return changed;
|
|
76
78
|
}
|
|
77
79
|
|
|
80
|
+
function inheritCodexHookTrustState(
|
|
81
|
+
sandboxParsed: JsonObject,
|
|
82
|
+
hostParsed: JsonObject,
|
|
83
|
+
hostProjectDir?: string
|
|
84
|
+
): boolean {
|
|
85
|
+
if (!hostProjectDir) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (Object.hasOwn(sandboxParsed, 'hooks') && !isJsonObjectRecord(sandboxParsed.hooks)) {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
const sandboxHooks = (sandboxParsed.hooks as JsonObject | undefined) ?? {};
|
|
93
|
+
if (Object.hasOwn(sandboxHooks, 'state') && !isJsonObjectRecord(sandboxHooks.state)) {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const sandboxState = (sandboxHooks.state as JsonObject | undefined) ?? {};
|
|
98
|
+
const sandboxPrefix = `${SANDBOX_CODEX_HOOKS_PATH}:`;
|
|
99
|
+
const nextState = Object.fromEntries(
|
|
100
|
+
Object.entries(sandboxState).filter(([key]) => !key.startsWith(sandboxPrefix))
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
const hostState = isJsonObjectRecord(hostParsed.hooks)
|
|
104
|
+
&& isJsonObjectRecord(hostParsed.hooks.state)
|
|
105
|
+
? hostParsed.hooks.state
|
|
106
|
+
: {};
|
|
107
|
+
const hostPrefix = `${path.join(hostProjectDir, '.codex', 'hooks.json')}:`;
|
|
108
|
+
for (const [key, value] of Object.entries(hostState)) {
|
|
109
|
+
if (!key.startsWith(hostPrefix) || !isJsonObjectRecord(value)) {
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
const trustedHash = value.trusted_hash;
|
|
113
|
+
if (typeof trustedHash !== 'string' || !CODEX_HOOK_TRUST_HASH.test(trustedHash)) {
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
nextState[`${sandboxPrefix}${key.slice(hostPrefix.length)}`] = {
|
|
117
|
+
trusted_hash: trustedHash,
|
|
118
|
+
...(typeof value.enabled === 'boolean' ? { enabled: value.enabled } : {})
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (JSON.stringify(sandboxState) === JSON.stringify(nextState)) {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
if (Object.keys(nextState).length > 0) {
|
|
126
|
+
sandboxHooks.state = nextState;
|
|
127
|
+
sandboxParsed.hooks = sandboxHooks;
|
|
128
|
+
} else {
|
|
129
|
+
delete sandboxHooks.state;
|
|
130
|
+
if (Object.keys(sandboxHooks).length > 0) {
|
|
131
|
+
sandboxParsed.hooks = sandboxHooks;
|
|
132
|
+
} else {
|
|
133
|
+
delete sandboxParsed.hooks;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
|
|
78
139
|
function ensureCodexModelInheritance(
|
|
79
140
|
toolDir: string,
|
|
80
141
|
hostHomeDir?: string,
|
|
81
|
-
containerCodexDir: string = '/home/devuser/.codex'
|
|
142
|
+
containerCodexDir: string = '/home/devuser/.codex',
|
|
143
|
+
hostProjectDir?: string
|
|
82
144
|
): void {
|
|
83
145
|
if (!hostHomeDir) {
|
|
84
146
|
return;
|
|
85
147
|
}
|
|
86
148
|
|
|
87
|
-
const hostConfigPath = path.join(hostHomeDir, '.codex', 'config.toml');
|
|
88
|
-
if (!fs.existsSync(hostConfigPath)) {
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
let hostParsed: JsonObject;
|
|
93
|
-
try {
|
|
94
|
-
hostParsed = toml.parse(fs.readFileSync(hostConfigPath, 'utf8')) as JsonObject;
|
|
95
|
-
} catch {
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
149
|
const sandboxConfigPath = path.join(toolDir, 'config.toml');
|
|
100
150
|
// This rewrites sandbox-side TOML and drops comments; the host config stays untouched.
|
|
101
151
|
let sandboxParsed: JsonObject = {};
|
|
@@ -107,8 +157,25 @@ function ensureCodexModelInheritance(
|
|
|
107
157
|
}
|
|
108
158
|
}
|
|
109
159
|
|
|
160
|
+
const hostConfigPath = path.join(hostHomeDir, '.codex', 'config.toml');
|
|
161
|
+
let hostParsed: JsonObject | null = null;
|
|
162
|
+
if (fs.existsSync(hostConfigPath)) {
|
|
163
|
+
try {
|
|
164
|
+
hostParsed = toml.parse(fs.readFileSync(hostConfigPath, 'utf8')) as JsonObject;
|
|
165
|
+
} catch {
|
|
166
|
+
// An unavailable host trust source must not leave previously projected trust active.
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
if (!hostParsed) {
|
|
170
|
+
if (inheritCodexHookTrustState(sandboxParsed, {}, hostProjectDir)) {
|
|
171
|
+
fs.writeFileSync(sandboxConfigPath, `${toml.stringify(sandboxParsed)}\n`, 'utf8');
|
|
172
|
+
}
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
110
176
|
let changed = removeCodexMcpServers(sandboxParsed);
|
|
111
177
|
changed = inheritDisabledCodexFeatureFlags(sandboxParsed, hostParsed) || changed;
|
|
178
|
+
changed = inheritCodexHookTrustState(sandboxParsed, hostParsed, hostProjectDir) || changed;
|
|
112
179
|
|
|
113
180
|
const inheritSpecs: Array<readonly [string, 'string' | 'number']> = [
|
|
114
181
|
['model', 'string'],
|
|
@@ -180,7 +247,15 @@ const codexBeforeContainerCreateHook: AgentClientSandboxHook = {
|
|
|
180
247
|
message: 'Codex sandbox hook requires its resolved tool state.'
|
|
181
248
|
};
|
|
182
249
|
}
|
|
183
|
-
|
|
250
|
+
const hostProjectDir = typeof context.config?.repoRoot === 'string'
|
|
251
|
+
? context.config.repoRoot
|
|
252
|
+
: undefined;
|
|
253
|
+
ensureCodexModelInheritance(
|
|
254
|
+
entry.dir,
|
|
255
|
+
create.hostHome,
|
|
256
|
+
entry.tool.containerMount,
|
|
257
|
+
hostProjectDir
|
|
258
|
+
);
|
|
184
259
|
ensureCodexWorkspaceTrust(entry.dir);
|
|
185
260
|
return { status: 'ready' };
|
|
186
261
|
}
|
|
@@ -4,13 +4,15 @@ import { claudeCodeAdapter } from './claude-code.ts';
|
|
|
4
4
|
import { codexAdapter } from './codex.ts';
|
|
5
5
|
import { antigravityCliAdapter } from './antigravity-cli.ts';
|
|
6
6
|
import { opencodeAdapter } from './opencode.ts';
|
|
7
|
+
import { traecliAdapter } from './traecli.ts';
|
|
7
8
|
|
|
8
9
|
const adaptersById = new Map(
|
|
9
10
|
[
|
|
10
11
|
claudeCodeAdapter,
|
|
11
12
|
codexAdapter,
|
|
12
13
|
antigravityCliAdapter,
|
|
13
|
-
opencodeAdapter
|
|
14
|
+
opencodeAdapter,
|
|
15
|
+
traecliAdapter
|
|
14
16
|
].map((adapter) => [adapter.id, adapter])
|
|
15
17
|
);
|
|
16
18
|
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { defineAgentClientAdapter } from '../adapter.ts';
|
|
2
|
+
import { hostJoin } from '../../sandbox/engines/wsl2-paths.ts';
|
|
3
|
+
|
|
4
|
+
const traecliAdapter = defineAgentClientAdapter({
|
|
5
|
+
id: 'traecli',
|
|
6
|
+
displayName: 'TraeCode CLI',
|
|
7
|
+
invocation: '/${skillName}',
|
|
8
|
+
capabilities: {
|
|
9
|
+
instructions: { level: 'compatible' },
|
|
10
|
+
skills: { level: 'compatible' },
|
|
11
|
+
commands: { level: 'integrated' },
|
|
12
|
+
hooks: { level: 'compatible' },
|
|
13
|
+
subagents: { level: 'experimental' },
|
|
14
|
+
orchestration: { level: 'experimental' },
|
|
15
|
+
sandbox: { level: 'integrated' },
|
|
16
|
+
verification: { level: 'compatible' }
|
|
17
|
+
},
|
|
18
|
+
modelSelection: {
|
|
19
|
+
kind: 'interactive-only',
|
|
20
|
+
command: '/model',
|
|
21
|
+
guidance: 'Use the host model picker for the complete model and reasoning-effort catalog.'
|
|
22
|
+
},
|
|
23
|
+
delegationEvidence: {
|
|
24
|
+
actualModel: 'unavailable',
|
|
25
|
+
actualReasoningEffort: 'unavailable'
|
|
26
|
+
},
|
|
27
|
+
project: {
|
|
28
|
+
ownedPathPrefixes: ['.trae/'],
|
|
29
|
+
managed: ['.trae/skills/'],
|
|
30
|
+
merged: [],
|
|
31
|
+
ejected: [],
|
|
32
|
+
seedCommands: [{
|
|
33
|
+
templates: {
|
|
34
|
+
en: '.trae/skills/update-agent-infra.en.md',
|
|
35
|
+
'zh-CN': '.trae/skills/update-agent-infra.zh-CN.md'
|
|
36
|
+
},
|
|
37
|
+
target: '.trae/skills/update-agent-infra.md'
|
|
38
|
+
}],
|
|
39
|
+
customCommand: {
|
|
40
|
+
target: '.trae/skills/${skillName}.md',
|
|
41
|
+
frontmatter: {},
|
|
42
|
+
includeUsage: true,
|
|
43
|
+
inheritDisableModelInvocation: true
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
sandbox: {
|
|
47
|
+
createTool: ({ home }) => ({
|
|
48
|
+
id: 'traecli',
|
|
49
|
+
name: 'TraeCode CLI',
|
|
50
|
+
install: {
|
|
51
|
+
type: 'shell',
|
|
52
|
+
cmd: 'curl -fsSL https://trae.cn/trae-cli/install_v2.sh | bash'
|
|
53
|
+
},
|
|
54
|
+
sandboxBase: hostJoin(home, '.agent-infra', 'sandboxes', 'traecli'),
|
|
55
|
+
containerMount: '/home/devuser/.trae',
|
|
56
|
+
versionCmd: 'traecli --version',
|
|
57
|
+
setupHint: 'Run traecli login inside the container to authenticate.'
|
|
58
|
+
}),
|
|
59
|
+
aliases: [
|
|
60
|
+
{ name: 'traecli-yolo', command: 'traecli --dangerously-bypass-approvals-and-sandbox; tput ed' },
|
|
61
|
+
{ name: 'ty', command: 'traecli --dangerously-bypass-approvals-and-sandbox; tput ed' }
|
|
62
|
+
],
|
|
63
|
+
hooks: []
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
export { traecliAdapter };
|
|
@@ -3,7 +3,7 @@
|
|
|
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
5
|
export const KNOWN_AI_AGENTS: ReadonlySet<string> =
|
|
6
|
-
new Set(['claude', 'codex', 'antigravity', 'opencode', 'cursor']);
|
|
6
|
+
new Set(['claude', 'codex', 'antigravity', 'opencode', 'cursor', 'traecli']);
|
|
7
7
|
|
|
8
8
|
// Long name -> short name mapping (HD-4, decision A): `claude-code` reuses the
|
|
9
9
|
// existing orchestration precedent, and `antigravity-cli` is the active
|
|
@@ -14,7 +14,7 @@ export const AGENT_LONG_NAMES: Readonly<Record<string, string>> = {
|
|
|
14
14
|
};
|
|
15
15
|
|
|
16
16
|
export const AGENT_USAGE_HINT =
|
|
17
|
-
"agent must be a short token (claude/codex/antigravity/opencode/cursor), " +
|
|
17
|
+
"agent must be a short token (claude/codex/antigravity/opencode/cursor/traecli), " +
|
|
18
18
|
"a long name (claude-code -> claude, antigravity-cli -> antigravity), or 'human'";
|
|
19
19
|
|
|
20
20
|
// Read-side historical compatibility for activity-log rendering: `gemini` and
|
package/lib/defaults.json
CHANGED
package/lib/git/workflow.ts
CHANGED
|
@@ -105,7 +105,7 @@ function commitExplicitPaths(input: { cwd: string; paths: readonly string[]; mes
|
|
|
105
105
|
return { status: 'applied' as const, changed: true, snapshot: after.snapshot, operations: [{ name: 'stage', status: 'applied' as const }, { name: 'commit', status: 'applied' as const }], error: null };
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
function pushGitRefs(input: { cwd: string; remote: string; refs: readonly string[] }, runner: GitRunner = defaultRunner) {
|
|
108
|
+
function pushGitRefs(input: { cwd: string; remote: string; refs: readonly string[]; expectedSha?: string }, runner: GitRunner = defaultRunner) {
|
|
109
109
|
if (!input.remote || input.refs.length === 0) return { status: 'failed' as const, changed: false, snapshot: inspectGitWorkflow(input.cwd, runner).snapshot, operations: [], error: { code: 'GIT_PUSH_INPUT_INVALID', message: 'Remote and refs are required' } };
|
|
110
110
|
const operations: GitOperation[] = [];
|
|
111
111
|
for (const ref of input.refs) {
|
|
@@ -120,7 +120,16 @@ function pushGitRefs(input: { cwd: string; remote: string; refs: readonly string
|
|
|
120
120
|
}
|
|
121
121
|
const canonical = ref.startsWith('refs/') ? ref : `refs/heads/${ref}`;
|
|
122
122
|
const remoteFact = run(runner, input.cwd, ['ls-remote', '--exit-code', input.remote, canonical]);
|
|
123
|
-
|
|
123
|
+
const remoteSha = remoteFact.status === 0 ? remoteFact.stdout.trim().split(/\s+/)[0] ?? null : null;
|
|
124
|
+
const verified = remoteFact.status === 0 && (!input.expectedSha || remoteSha === input.expectedSha);
|
|
125
|
+
operations.push({
|
|
126
|
+
name: 'push',
|
|
127
|
+
ref,
|
|
128
|
+
status: verified ? 'applied' : 'failed',
|
|
129
|
+
message: verified ? undefined : input.expectedSha
|
|
130
|
+
? `Remote ref verification failed: expected ${input.expectedSha}, received ${remoteSha ?? 'unavailable'}`
|
|
131
|
+
: 'Remote ref verification failed'
|
|
132
|
+
});
|
|
124
133
|
}
|
|
125
134
|
const failures = operations.filter((item) => item.status === 'failed').length;
|
|
126
135
|
return { status: failures === 0 ? 'applied' as const : failures === operations.length ? 'failed' as const : 'degraded' as const, changed: failures < operations.length, snapshot: inspectGitWorkflow(input.cwd, runner).snapshot, operations, error: failures ? { code: 'GIT_PUSH_PARTIAL', message: `${failures} ref push operation(s) failed` } : null };
|
|
@@ -11,9 +11,10 @@ import {
|
|
|
11
11
|
import { createCodexLifecycleStore } from '../agent-clients/adapters/codex-lifecycle/store.ts';
|
|
12
12
|
import { createCodexCapabilityStore } from '../agent-clients/adapters/codex-lifecycle/capability-store.ts';
|
|
13
13
|
import { computeLifecycleBuildIdentity } from '../agent-clients/adapters/codex-lifecycle/build-identity.ts';
|
|
14
|
-
import {
|
|
14
|
+
import { verifyCodexSandboxControllerContextWithWarnings } from '../agent-clients/adapters/codex-lifecycle/sandbox-controller.ts';
|
|
15
15
|
import type { CodexLifecycleEvent } from '../agent-clients/adapters/codex-lifecycle/evidence.ts';
|
|
16
16
|
import { resolveTaskRef } from '../task/resolve-ref.ts';
|
|
17
|
+
import { hasSealableOrchestrationDelegation } from '../task/orchestration.ts';
|
|
17
18
|
import {
|
|
18
19
|
activateCodexOrchestrationDelegation,
|
|
19
20
|
activateCodexSpawnDelegation,
|
|
@@ -100,7 +101,7 @@ function hookDefinitionHash(): string {
|
|
|
100
101
|
function controllerBinding(taskId: string) {
|
|
101
102
|
const contextPath = process.env.AGENT_INFRA_CODEX_CONTROLLER_CONTEXT;
|
|
102
103
|
if (!contextPath) return undefined;
|
|
103
|
-
const context =
|
|
104
|
+
const context = verifyCodexSandboxControllerContextWithWarnings(contextPath, { repoRoot: process.cwd() }).context;
|
|
104
105
|
return {
|
|
105
106
|
instanceDigest: context.controllerInstanceDigest,
|
|
106
107
|
controlGeneration: context.controlGeneration
|
|
@@ -139,7 +140,8 @@ function recordFromPayload(phase: string, payload: unknown): CodexLifecycleEvent
|
|
|
139
140
|
sessionId: text('sessionId'),
|
|
140
141
|
turnId: text('turnId'),
|
|
141
142
|
childThreadId: text('childThreadId'),
|
|
142
|
-
nativeAgent: text('nativeAgent')
|
|
143
|
+
nativeAgent: text('nativeAgent'),
|
|
144
|
+
source: 'hook'
|
|
143
145
|
};
|
|
144
146
|
if (phase === 'subagent-stop') return {
|
|
145
147
|
type: 'hook-stop',
|
|
@@ -326,8 +328,17 @@ async function codexLifecycle(args: string[] = []): Promise<void> {
|
|
|
326
328
|
}
|
|
327
329
|
const result = store.apply(event);
|
|
328
330
|
if (parsed.values['--bridge'] === 'true' && event.type === 'hook-stop') {
|
|
329
|
-
|
|
330
|
-
|
|
331
|
+
if (!hasSealableOrchestrationDelegation('codex', event.childThreadId, { repoRoot: process.cwd() })) {
|
|
332
|
+
output({ status: 'ignored', changed: false, evidence: null, diagnostics: [], error: null });
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
output({
|
|
336
|
+
status: result.state.status,
|
|
337
|
+
changed: true,
|
|
338
|
+
evidence: result.state,
|
|
339
|
+
diagnostics: [],
|
|
340
|
+
error: result.state.error
|
|
341
|
+
});
|
|
331
342
|
return;
|
|
332
343
|
}
|
|
333
344
|
output({ status: result.state.status, changed: true, evidence: result.state, diagnostics: [], error: result.state.error });
|
|
@@ -350,7 +361,9 @@ async function codexLifecycle(args: string[] = []): Promise<void> {
|
|
|
350
361
|
return;
|
|
351
362
|
}
|
|
352
363
|
if (parsed.operation === 'resolve-stop') {
|
|
353
|
-
const
|
|
364
|
+
const stopTurnId = store.read(childThreadId).state.stop?.turnId;
|
|
365
|
+
if (!stopTurnId) throw new Error('Codex lifecycle stop hook is not available');
|
|
366
|
+
const terminal = await resolveCodexTerminal(childThreadId, stopTurnId);
|
|
354
367
|
const latest = store.apply(terminal);
|
|
355
368
|
if (latest.state.status !== 'stop-ready') {
|
|
356
369
|
throw new Error(`Codex lifecycle stop evidence is not ready (status=${latest.state.status})`);
|