@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
|
@@ -3,28 +3,28 @@ import fs from 'node:fs';
|
|
|
3
3
|
import os from 'node:os';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { randomUUID } from 'node:crypto';
|
|
6
|
-
import { loadConfig } from '
|
|
7
|
-
import { detectEngine } from '
|
|
8
|
-
import { runEngine } from '
|
|
9
|
-
import { sandboxBranchLabel, sandboxLabel } from '
|
|
10
|
-
import { fetchSandboxRows, selectSandboxContainer } from '
|
|
11
|
-
import { containerNameCandidates } from '
|
|
12
|
-
import { resolveSandboxTarget } from '
|
|
13
|
-
import { sandboxControlPaths } from '
|
|
6
|
+
import { loadConfig } from '../sandbox/config.ts';
|
|
7
|
+
import { detectEngine } from '../sandbox/engine.ts';
|
|
8
|
+
import { runEngine } from '../sandbox/shell.ts';
|
|
9
|
+
import { sandboxBranchLabel, sandboxLabel } from '../sandbox/constants.ts';
|
|
10
|
+
import { fetchSandboxRows, selectSandboxContainer } from '../sandbox/commands/list-running.ts';
|
|
11
|
+
import { containerNameCandidates } from '../sandbox/constants.ts';
|
|
12
|
+
import { resolveSandboxTarget } from '../sandbox/workspace-identity.ts';
|
|
13
|
+
import { sandboxControlPaths } from '../sandbox/workspace-view.ts';
|
|
14
14
|
import {
|
|
15
15
|
appendSandboxControlAudit,
|
|
16
16
|
readActiveLease,
|
|
17
17
|
readSandboxControlStatus
|
|
18
|
-
} from '
|
|
19
|
-
import type { SandboxControlLease, SandboxControlManifest } from '
|
|
18
|
+
} from '../sandbox/control/state.ts';
|
|
19
|
+
import type { SandboxControlLease, SandboxControlManifest } from '../sandbox/control/protocol.ts';
|
|
20
20
|
import {
|
|
21
21
|
SANDBOX_CONTROL_FUTURE_SKEW_MS,
|
|
22
22
|
SANDBOX_CONTROL_STATUS_STALE_MS
|
|
23
|
-
} from '
|
|
24
|
-
import { getProcessStartTime } from '
|
|
25
|
-
import { assertGitWorktreeBinding } from '
|
|
23
|
+
} from '../sandbox/control/protocol.ts';
|
|
24
|
+
import { getProcessStartTime } from '../server/process-state.ts';
|
|
25
|
+
import { assertGitWorktreeBinding } from '../git/worktree-identity.ts';
|
|
26
26
|
|
|
27
|
-
const USAGE = `Usage:
|
|
27
|
+
const USAGE = `Usage: agent-infra-internal task-validate <branch | TASK-id | N> [--scope snapshot|inplace] [--timeout <ms>] [--format text|json] -- <command> [args...]`;
|
|
28
28
|
const MAX_TIMEOUT_MS = 60 * 60 * 1000;
|
|
29
29
|
const RECOVERY_GRACE_MS = 30_000;
|
|
30
30
|
|
|
@@ -250,33 +250,68 @@ function inplaceValidation(config: ReturnType<typeof loadConfig>, target: Return
|
|
|
250
250
|
}
|
|
251
251
|
}
|
|
252
252
|
|
|
253
|
-
|
|
254
|
-
const
|
|
253
|
+
function errorCode(message: string): string {
|
|
254
|
+
const match = message.match(/^([A-Z][A-Z0-9_]*)/);
|
|
255
|
+
return match && match[1]!.length >= 3 ? match[1]! : 'TASK_VALIDATE_FAILED';
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function fail(format: 'text' | 'json', code: string, message: string): void {
|
|
259
|
+
if (format === 'json') {
|
|
260
|
+
process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code, message } })}\n`);
|
|
261
|
+
} else {
|
|
262
|
+
process.stderr.write(`${message}\n`);
|
|
263
|
+
}
|
|
264
|
+
process.exitCode = 1;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function sniffFormat(args: string[]): 'text' | 'json' {
|
|
268
|
+
const index = args.indexOf('--format');
|
|
269
|
+
return index >= 0 && args[index + 1] === 'json' ? 'json' : 'text';
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function taskValidate(args: string[]): void {
|
|
273
|
+
let options: ValidateOptions;
|
|
274
|
+
try {
|
|
275
|
+
options = parseValidateArgs(args);
|
|
276
|
+
} catch (error) {
|
|
277
|
+
fail(sniffFormat(args), 'TASK_VALIDATE_ARGS_INVALID', error instanceof Error ? error.message : String(error));
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
255
280
|
if (options.help) {
|
|
256
281
|
process.stdout.write(`${USAGE}\n`);
|
|
257
|
-
return
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
try {
|
|
285
|
+
const config = loadConfig();
|
|
286
|
+
const target = resolveSandboxTarget(options.target, config.repoRoot);
|
|
287
|
+
const commit = git(config.repoRoot, ['rev-parse', target.branch]);
|
|
288
|
+
const startedAt = new Date().toISOString();
|
|
289
|
+
const result = options.scope === 'snapshot'
|
|
290
|
+
? snapshotValidation(config.repoRoot, commit, options)
|
|
291
|
+
: inplaceValidation(config, target, options);
|
|
292
|
+
const evidence = {
|
|
293
|
+
version: 1,
|
|
294
|
+
taskId: target.workspace.mode === 'task-bound' ? target.workspace.taskId : null,
|
|
295
|
+
branch: target.branch,
|
|
296
|
+
scope: options.scope,
|
|
297
|
+
commit,
|
|
298
|
+
command: path.basename(options.command[0]!),
|
|
299
|
+
startedAt,
|
|
300
|
+
completedAt: new Date().toISOString(),
|
|
301
|
+
exitCode: result.exitCode,
|
|
302
|
+
signal: result.signal,
|
|
303
|
+
cleanup: result.cleanup()
|
|
304
|
+
};
|
|
305
|
+
if (options.format === 'json') {
|
|
306
|
+
process.stdout.write(`${JSON.stringify({ status: 'applied', changed: false, evidence, error: null })}\n`);
|
|
307
|
+
} else {
|
|
308
|
+
process.stdout.write(`Validation ${result.exitCode === 0 ? 'passed' : 'failed'} (${options.scope}, ${evidence.command}, cleanup=${evidence.cleanup}).\n`);
|
|
309
|
+
}
|
|
310
|
+
process.exitCode = result.exitCode;
|
|
311
|
+
} catch (error) {
|
|
312
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
313
|
+
fail(options.format, errorCode(message), message);
|
|
258
314
|
}
|
|
259
|
-
const config = loadConfig();
|
|
260
|
-
const target = resolveSandboxTarget(options.target, config.repoRoot);
|
|
261
|
-
const commit = git(config.repoRoot, ['rev-parse', target.branch]);
|
|
262
|
-
const startedAt = new Date().toISOString();
|
|
263
|
-
const result = options.scope === 'snapshot'
|
|
264
|
-
? snapshotValidation(config.repoRoot, commit, options)
|
|
265
|
-
: inplaceValidation(config, target, options);
|
|
266
|
-
const evidence = {
|
|
267
|
-
version: 1,
|
|
268
|
-
taskId: target.workspace.mode === 'task-bound' ? target.workspace.taskId : null,
|
|
269
|
-
branch: target.branch,
|
|
270
|
-
scope: options.scope,
|
|
271
|
-
commit,
|
|
272
|
-
command: path.basename(options.command[0]!),
|
|
273
|
-
startedAt,
|
|
274
|
-
completedAt: new Date().toISOString(),
|
|
275
|
-
exitCode: result.exitCode,
|
|
276
|
-
signal: result.signal,
|
|
277
|
-
cleanup: result.cleanup()
|
|
278
|
-
};
|
|
279
|
-
if (options.format === 'json') process.stdout.write(`${JSON.stringify(evidence)}\n`);
|
|
280
|
-
else process.stdout.write(`Validation ${result.exitCode === 0 ? 'passed' : 'failed'} (${options.scope}, ${evidence.command}, cleanup=${evidence.cleanup}).\n`);
|
|
281
|
-
return result.exitCode;
|
|
282
315
|
}
|
|
316
|
+
|
|
317
|
+
export { taskValidate };
|
package/lib/run/tui.ts
CHANGED
|
@@ -2,14 +2,15 @@ import { renderAgentClientInvocation } from '../agent-clients/invocation.ts';
|
|
|
2
2
|
import { getAgentClientAdapter } from '../agent-clients/registry.ts';
|
|
3
3
|
import type { AgentClientId } from '../agent-clients/types.ts';
|
|
4
4
|
|
|
5
|
-
export type TuiName = 'claude' | 'codex' | 'antigravity' | 'opencode';
|
|
5
|
+
export type TuiName = 'claude' | 'codex' | 'antigravity' | 'opencode' | 'traecli';
|
|
6
6
|
|
|
7
|
-
const TUI_NAMES = new Set(['claude', 'codex', 'antigravity', 'opencode']);
|
|
7
|
+
const TUI_NAMES = new Set(['claude', 'codex', 'antigravity', 'opencode', 'traecli']);
|
|
8
8
|
const TUI_AGENT_CLIENT_IDS: Readonly<Record<TuiName, AgentClientId>> = Object.freeze({
|
|
9
9
|
claude: 'claude-code',
|
|
10
10
|
codex: 'codex',
|
|
11
11
|
antigravity: 'antigravity-cli',
|
|
12
|
-
opencode: 'opencode'
|
|
12
|
+
opencode: 'opencode',
|
|
13
|
+
traecli: 'traecli'
|
|
13
14
|
});
|
|
14
15
|
|
|
15
16
|
export type CommandConfig = {
|
|
@@ -46,5 +47,6 @@ export function buildTuiCommand(tui: TuiName, prompt: string): [string, string[]
|
|
|
46
47
|
if (tui === 'claude') return ['claude', ['--dangerously-skip-permissions', '--print', prompt]];
|
|
47
48
|
if (tui === 'antigravity') return ['agy', ['--dangerously-skip-permissions', '--print', prompt]];
|
|
48
49
|
if (tui === 'opencode') return ['opencode', ['run', '--dangerously-skip-permissions', prompt]];
|
|
50
|
+
if (tui === 'traecli') return ['traecli', ['exec', '--dangerously-bypass-approvals-and-sandbox', prompt]];
|
|
49
51
|
return ['codex', ['exec', '--dangerously-bypass-approvals-and-sandbox', prompt]];
|
|
50
52
|
}
|
|
@@ -188,10 +188,12 @@ function assertRegistrationBinding(
|
|
|
188
188
|
): void {
|
|
189
189
|
if (registration.taskId !== manifest.taskId
|
|
190
190
|
|| registration.controlGeneration !== manifest.generation
|
|
191
|
-
|| registration.containerId !== manifest.containerIdentity.id
|
|
192
|
-
|| JSON.stringify(registration.buildIdentity) !== JSON.stringify(buildIdentity)) {
|
|
191
|
+
|| registration.containerId !== manifest.containerIdentity.id) {
|
|
193
192
|
fail('CODEX_SANDBOX_CONTROLLER_REGISTRATION_INVALID', 'controller registration binding is invalid');
|
|
194
193
|
}
|
|
194
|
+
if (registration.buildIdentity.protocolVersion !== buildIdentity.protocolVersion) {
|
|
195
|
+
fail('CODEX_LIFECYCLE_PROTOCOL_MISMATCH', 'Codex lifecycle protocol version does not match');
|
|
196
|
+
}
|
|
195
197
|
}
|
|
196
198
|
|
|
197
199
|
function atomicWrite(file: string, value: CodexControllerRegistrationV1): void {
|
|
@@ -322,7 +324,10 @@ export function resolveCodexControllerBinding(params: Readonly<{
|
|
|
322
324
|
buildIdentity: LifecycleBuildIdentity;
|
|
323
325
|
now?: number;
|
|
324
326
|
probeProcess?: (identity: ProcessIdentity) => ProcessIdentityState;
|
|
325
|
-
}>): Readonly<{
|
|
327
|
+
}>): Readonly<{
|
|
328
|
+
instanceDigest: string;
|
|
329
|
+
controlGeneration: string;
|
|
330
|
+
}> {
|
|
326
331
|
if (params.manifest.mode !== 'task-bound' || !params.manifest.taskId) {
|
|
327
332
|
fail('SANDBOX_CONTROL_BRANCH_ONLY', 'branch-only sandboxes cannot resolve a Codex controller registration');
|
|
328
333
|
}
|
|
@@ -146,12 +146,15 @@ function controllerFailure(error: unknown): SandboxControlExecutionResult {
|
|
|
146
146
|
? error.code
|
|
147
147
|
: /^([A-Z][A-Z0-9_]+)/u.exec(error instanceof Error ? error.message : String(error))?.[1]
|
|
148
148
|
?? 'CODEX_SANDBOX_CONTROLLER_FAILED';
|
|
149
|
+
const message = error instanceof CodexControllerRegistrationError
|
|
150
|
+
? error.message
|
|
151
|
+
: `${code}: controller operation failed; inspect the sandbox controller and rebuild the sandbox if needed`;
|
|
149
152
|
const payload = {
|
|
150
153
|
version: 1,
|
|
151
154
|
status: 'failed',
|
|
152
155
|
changed: false,
|
|
153
156
|
lease: null,
|
|
154
|
-
error: { code, message
|
|
157
|
+
error: { code, message, retryable: false }
|
|
155
158
|
};
|
|
156
159
|
return { exitCode: 1, stdout: `${JSON.stringify(payload)}\n`, stderr: '' };
|
|
157
160
|
}
|
|
@@ -265,7 +268,10 @@ export function executeRequest(
|
|
|
265
268
|
));
|
|
266
269
|
}
|
|
267
270
|
const boundArgs = bindSandboxControlTask(request, manifest.taskId!);
|
|
268
|
-
let controllerBinding: Readonly<{
|
|
271
|
+
let controllerBinding: Readonly<{
|
|
272
|
+
instanceDigest: string;
|
|
273
|
+
controlGeneration: string;
|
|
274
|
+
}> | null = null;
|
|
269
275
|
if (request.family === 'task-orchestration') {
|
|
270
276
|
if (isCodexPrepare(request.args)) {
|
|
271
277
|
if (!request.controllerProof) {
|
|
@@ -10,10 +10,16 @@ import {
|
|
|
10
10
|
isCodexCapabilityProvenanceDetail
|
|
11
11
|
} from '../agent-clients/adapters/codex-lifecycle/capability-store.ts';
|
|
12
12
|
import type { CodexCapabilityProvenanceDetail } from '../agent-clients/adapters/codex-lifecycle/capability-store.ts';
|
|
13
|
-
import {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
import {
|
|
14
|
+
computeLifecycleBuildIdentity,
|
|
15
|
+
verifyLifecycleBuildIdentity,
|
|
16
|
+
type LifecycleBuildIdentity,
|
|
17
|
+
type LifecycleIdentityWarning
|
|
18
|
+
} from '../agent-clients/adapters/codex-lifecycle/build-identity.ts';
|
|
19
|
+
import { verifyCodexSandboxControllerContextWithWarnings as verifySandboxControllerWithWarnings } from '../agent-clients/adapters/codex-lifecycle/sandbox-controller.ts';
|
|
20
|
+
import {
|
|
21
|
+
verifyCodexSandboxControllerContextWithWarnings as verifyControllerContextFileWithWarnings
|
|
22
|
+
} from '../agent-clients/adapters/codex-lifecycle/controller-context.ts';
|
|
17
23
|
import type { AgentClientId } from '../agent-clients/types.ts';
|
|
18
24
|
import { resolveTaskRef } from './resolve-ref.ts';
|
|
19
25
|
import {
|
|
@@ -92,35 +98,6 @@ function requiredStore(options: CodexBridgeOptions): LifecycleStore {
|
|
|
92
98
|
return options.store;
|
|
93
99
|
}
|
|
94
100
|
|
|
95
|
-
function controllerContext(
|
|
96
|
-
repoRoot: string,
|
|
97
|
-
brokerController: Readonly<{ instanceDigest: string; controlGeneration: string }> | null
|
|
98
|
-
) {
|
|
99
|
-
const contextPath = process.env.AGENT_INFRA_CODEX_CONTROLLER_CONTEXT;
|
|
100
|
-
if (!contextPath) return null;
|
|
101
|
-
if (brokerController) {
|
|
102
|
-
return verifyControllerContextFile(contextPath, {
|
|
103
|
-
repoRoot,
|
|
104
|
-
generation: process.env.AGENT_INFRA_CONTROL_GENERATION ?? brokerController.controlGeneration
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
return verifyCodexSandboxControllerContext(contextPath, { repoRoot });
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
function brokerBindingConflictsWithContext(
|
|
111
|
-
repoRoot: string,
|
|
112
|
-
brokerController: Readonly<{ instanceDigest: string; controlGeneration: string }> | null
|
|
113
|
-
): boolean {
|
|
114
|
-
const contextPath = process.env.AGENT_INFRA_CODEX_CONTROLLER_CONTEXT;
|
|
115
|
-
if (!contextPath || !brokerController) return false;
|
|
116
|
-
const context = verifyControllerContextFile(contextPath, {
|
|
117
|
-
repoRoot,
|
|
118
|
-
generation: process.env.AGENT_INFRA_CONTROL_GENERATION ?? brokerController.controlGeneration
|
|
119
|
-
});
|
|
120
|
-
return context.controllerInstanceDigest !== brokerController.instanceDigest
|
|
121
|
-
|| context.controlGeneration !== brokerController.controlGeneration;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
101
|
function brokerControllerBinding(): Readonly<{ instanceDigest: string; controlGeneration: string }> | null {
|
|
125
102
|
const raw = process.env.AGENT_INFRA_CONTROL_CONTROLLER_BINDING;
|
|
126
103
|
if (!raw) return null;
|
|
@@ -166,13 +143,17 @@ async function prepareCodexOrchestrationDelegation(
|
|
|
166
143
|
}
|
|
167
144
|
const buildIdentity = options.buildIdentity ?? computeLifecycleBuildIdentity(repoRoot);
|
|
168
145
|
const brokerController = brokerControllerBinding();
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
146
|
+
const contextPath = process.env.AGENT_INFRA_CODEX_CONTROLLER_CONTEXT;
|
|
147
|
+
const contextVerification = contextPath
|
|
148
|
+
? (brokerController
|
|
149
|
+
? verifyControllerContextFileWithWarnings(contextPath, {
|
|
150
|
+
repoRoot,
|
|
151
|
+
generation: process.env.AGENT_INFRA_CONTROL_GENERATION ?? brokerController.controlGeneration
|
|
152
|
+
})
|
|
153
|
+
: verifySandboxControllerWithWarnings(contextPath, { repoRoot }))
|
|
154
|
+
: null;
|
|
155
|
+
const identityWarnings: LifecycleIdentityWarning[] = [...(contextVerification?.warnings ?? [])];
|
|
156
|
+
const localController = contextVerification?.context ?? null;
|
|
176
157
|
if (localController && brokerController
|
|
177
158
|
&& (localController.controllerInstanceDigest !== brokerController.instanceDigest
|
|
178
159
|
|| localController.controlGeneration !== brokerController.controlGeneration)) {
|
|
@@ -187,6 +168,11 @@ async function prepareCodexOrchestrationDelegation(
|
|
|
187
168
|
} : null);
|
|
188
169
|
const capabilityStore = options.capabilityStore ?? createCodexCapabilityStore();
|
|
189
170
|
const attested = capabilityStore.inspect(input.capabilityToken);
|
|
171
|
+
const capabilityIdentity = verifyLifecycleBuildIdentity(attested.buildIdentity, buildIdentity);
|
|
172
|
+
if (!capabilityIdentity.ok) {
|
|
173
|
+
return bridgeFailure(capabilityIdentity.code!, capabilityIdentity.message!);
|
|
174
|
+
}
|
|
175
|
+
identityWarnings.push(...capabilityIdentity.warnings);
|
|
190
176
|
const lifecycleProvenance = {
|
|
191
177
|
...attested.buildIdentity,
|
|
192
178
|
hookDefinitionHash: preflight.hookDefinitionHash,
|
|
@@ -206,7 +192,7 @@ async function prepareCodexOrchestrationDelegation(
|
|
|
206
192
|
controlGeneration: controller.controlGeneration
|
|
207
193
|
} } : {})
|
|
208
194
|
};
|
|
209
|
-
|
|
195
|
+
const prepared = prepareOrchestrationDelegation(taskRef, {
|
|
210
196
|
...input,
|
|
211
197
|
lifecycleProvenance
|
|
212
198
|
}, {
|
|
@@ -244,6 +230,9 @@ async function prepareCodexOrchestrationDelegation(
|
|
|
244
230
|
}
|
|
245
231
|
}
|
|
246
232
|
});
|
|
233
|
+
return identityWarnings.length > 0 && prepared.status !== 'failed'
|
|
234
|
+
? { ...prepared, warnings: Object.freeze(identityWarnings) }
|
|
235
|
+
: prepared;
|
|
247
236
|
} catch (error) {
|
|
248
237
|
if (error instanceof OrchestrationStateError) return bridgeFailure(error.code, error.message);
|
|
249
238
|
const detailValue = error instanceof Error
|
|
@@ -290,10 +279,11 @@ async function activateCodexOrchestrationDelegation(
|
|
|
290
279
|
});
|
|
291
280
|
const buildIdentity = options.buildIdentity ?? computeLifecycleBuildIdentity(repoRoot);
|
|
292
281
|
const contextPath = process.env.AGENT_INFRA_CODEX_CONTROLLER_CONTEXT;
|
|
293
|
-
const
|
|
294
|
-
?
|
|
282
|
+
const contextVerification = contextPath
|
|
283
|
+
? verifySandboxControllerWithWarnings(contextPath, { repoRoot })
|
|
295
284
|
: null;
|
|
296
|
-
|
|
285
|
+
const controller = contextVerification?.context ?? null;
|
|
286
|
+
const activated = activateMatchingOrchestrationDelegation('codex', {
|
|
297
287
|
nativeAgent: evidence.nativeAgent,
|
|
298
288
|
childId: evidence.childThreadId,
|
|
299
289
|
parentId: evidence.parentThreadId,
|
|
@@ -318,6 +308,10 @@ async function activateCodexOrchestrationDelegation(
|
|
|
318
308
|
controlGeneration: controller?.controlGeneration ?? null
|
|
319
309
|
}
|
|
320
310
|
}, coreOptions(options));
|
|
311
|
+
const warnings = contextVerification?.warnings ?? [];
|
|
312
|
+
return warnings.length > 0 && activated.status !== 'failed'
|
|
313
|
+
? { ...activated, warnings: Object.freeze(warnings) }
|
|
314
|
+
: activated;
|
|
321
315
|
} catch (error) {
|
|
322
316
|
if (error instanceof OrchestrationStateError) return bridgeFailure(error.code, error.message);
|
|
323
317
|
return pauseBridge('ORCHESTRATION_CODEX_START_FAILED', error instanceof Error ? error.message : String(error), options);
|
|
@@ -108,6 +108,13 @@ function exactText(value: unknown): value is string {
|
|
|
108
108
|
return typeof value === 'string' && value.length > 0 && value.trim() === value;
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
+
function validCodexBuildMetadata(value: Readonly<Record<string, unknown>>): boolean {
|
|
112
|
+
return Number.isSafeInteger(value.protocolVersion) && (value.protocolVersion as number) > 0
|
|
113
|
+
&& exactText(value.packageVersion)
|
|
114
|
+
&& exactText(value.internalExecutableBuildHash)
|
|
115
|
+
&& exactText(value.lifecycleContractHash);
|
|
116
|
+
}
|
|
117
|
+
|
|
111
118
|
function nullableText(value: unknown): value is string | null {
|
|
112
119
|
return value === null || exactText(value);
|
|
113
120
|
}
|
|
@@ -185,9 +192,6 @@ function hasCurrentCodexEvidence(receipt: DelegationReceipt): boolean {
|
|
|
185
192
|
if (
|
|
186
193
|
host?.kind !== 'codex-lifecycle-v2'
|
|
187
194
|
|| host.protocolVersion !== provenance.protocolVersion
|
|
188
|
-
|| host.packageVersion !== provenance.packageVersion
|
|
189
|
-
|| host.internalExecutableBuildHash !== provenance.internalExecutableBuildHash
|
|
190
|
-
|| host.lifecycleContractHash !== provenance.lifecycleContractHash
|
|
191
195
|
|| host.hookDefinitionHash !== provenance.hookDefinitionHash
|
|
192
196
|
|| host.hookSource !== provenance.hookSource
|
|
193
197
|
|| host.hookSourcePathDigest !== provenance.hookSourcePathDigest
|
|
@@ -531,9 +535,6 @@ function activateDelegation(
|
|
|
531
535
|
if (
|
|
532
536
|
!expected
|
|
533
537
|
|| event.hostEvidence.protocolVersion !== expected.protocolVersion
|
|
534
|
-
|| event.hostEvidence.packageVersion !== expected.packageVersion
|
|
535
|
-
|| event.hostEvidence.internalExecutableBuildHash !== expected.internalExecutableBuildHash
|
|
536
|
-
|| event.hostEvidence.lifecycleContractHash !== expected.lifecycleContractHash
|
|
537
538
|
|| event.hostEvidence.hookDefinitionHash !== expected.hookDefinitionHash
|
|
538
539
|
|| event.hostEvidence.hookSource !== expected.hookSource
|
|
539
540
|
|| event.hostEvidence.hookSourcePathDigest !== expected.hookSourcePathDigest
|
|
@@ -548,6 +549,7 @@ function activateDelegation(
|
|
|
548
549
|
|| event.parentId !== expected.capabilitySessionId
|
|
549
550
|
|| (event.hostEvidence.controllerInstanceDigest ?? null) !== expected.controllerInstanceDigest
|
|
550
551
|
|| (event.hostEvidence.controlGeneration ?? null) !== expected.controlGeneration
|
|
552
|
+
|| !validCodexBuildMetadata(event.hostEvidence)
|
|
551
553
|
) return fail('DELEGATION_HOST_EVIDENCE_INVALID', 'Codex lifecycle provenance does not match the prepared receipt');
|
|
552
554
|
}
|
|
553
555
|
return { ok: true, receipt: Object.freeze({
|
package/lib/task/finalization.ts
CHANGED
|
@@ -85,6 +85,13 @@ type TaskFinalizationStep = Readonly<{
|
|
|
85
85
|
pendingSteps?: readonly string[];
|
|
86
86
|
}>;
|
|
87
87
|
|
|
88
|
+
type TaskCommentSyncOutcome = Readonly<{
|
|
89
|
+
receipt: TaskFinalizationReceipt;
|
|
90
|
+
step: TaskFinalizationStep;
|
|
91
|
+
changed: boolean;
|
|
92
|
+
error: FinalizationError | null;
|
|
93
|
+
}>;
|
|
94
|
+
|
|
88
95
|
type TaskFinalizationResult = Readonly<{
|
|
89
96
|
status: 'completed' | 'failed' | 'blocked';
|
|
90
97
|
changed: boolean;
|
|
@@ -390,11 +397,18 @@ function mutationPatch(current: TaskFinalizationReceipt, mutation: FinalizationM
|
|
|
390
397
|
}
|
|
391
398
|
if (mutation.scope === 'verification') {
|
|
392
399
|
if (mutation.operation === 'succeeded') {
|
|
400
|
+
const resolvedTaskWarning = current.warnings.some((warning) => warning.step === 'verification' && warning.status === 'open');
|
|
393
401
|
const warnings = resolveStepWarnings(current, 'verification');
|
|
394
|
-
return {
|
|
402
|
+
return {
|
|
403
|
+
verification: 'done',
|
|
404
|
+
taskComment: resolvedTaskWarning ? 'pending' : current.taskComment,
|
|
405
|
+
warningProjection: warnings.length > 0 ? 'pending' : 'done',
|
|
406
|
+
warnings,
|
|
407
|
+
lastError: null
|
|
408
|
+
};
|
|
395
409
|
}
|
|
396
410
|
const warnings = replaceWarning(current, warningFromError('verification', mutation.error), 'open');
|
|
397
|
-
return { verification: 'pending', warningProjection: 'pending', warnings, lastError: mutation.error };
|
|
411
|
+
return { verification: 'pending', taskComment: 'pending', warningProjection: 'pending', warnings, lastError: mutation.error };
|
|
398
412
|
}
|
|
399
413
|
return mutation.operation === 'succeeded'
|
|
400
414
|
? { warningProjection: 'done', lastError: null }
|
|
@@ -472,6 +486,56 @@ function reconcileWarningProjection(repoRoot: string, taskId: string, receipt: T
|
|
|
472
486
|
catch { return receipt; }
|
|
473
487
|
}
|
|
474
488
|
|
|
489
|
+
function syncPendingTaskComment(input: {
|
|
490
|
+
repoRoot: string;
|
|
491
|
+
taskId: string;
|
|
492
|
+
agent: string;
|
|
493
|
+
receipt: TaskFinalizationReceipt;
|
|
494
|
+
commentSync: typeof syncPlatformComment;
|
|
495
|
+
consumedCapabilities: Set<string>;
|
|
496
|
+
}): TaskCommentSyncOutcome {
|
|
497
|
+
const { repoRoot, taskId, agent, commentSync, consumedCapabilities } = input;
|
|
498
|
+
let receipt = input.receipt;
|
|
499
|
+
if (receipt.taskComment !== 'pending') {
|
|
500
|
+
return {
|
|
501
|
+
receipt,
|
|
502
|
+
step: { status: receipt.taskComment === 'skipped' ? 'skipped' : 'no-op', changed: false, error: null },
|
|
503
|
+
changed: false,
|
|
504
|
+
error: null
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
try {
|
|
508
|
+
const result = commentSync(taskId, { kind: 'task', agent, cwd: repoRoot });
|
|
509
|
+
const step = commentStep(result);
|
|
510
|
+
if (result.status === 'applied' || result.status === 'no-op') {
|
|
511
|
+
const skipped = result.error?.code === 'ISSUE_NOT_LINKED';
|
|
512
|
+
const capability = issueCapability(receipt, 'task-comment');
|
|
513
|
+
receipt = applyFinalizationReceiptMutationUnderLock(repoRoot, receipt, capability, {
|
|
514
|
+
scope: 'task-comment', operation: 'succeeded', state: skipped ? 'skipped' : 'done'
|
|
515
|
+
}, consumedCapabilities);
|
|
516
|
+
receipt = reconcileWarningProjection(repoRoot, taskId, receipt, consumedCapabilities);
|
|
517
|
+
return { receipt, step: skipped ? { ...step, status: 'skipped' } : step, changed: result.changed, error: null };
|
|
518
|
+
}
|
|
519
|
+
const detail = step.error ?? { code: 'COMMENT_SYNC_FAILED', message: 'task comment synchronization failed', retryable: true };
|
|
520
|
+
const capability = issueCapability(receipt, 'task-comment');
|
|
521
|
+
receipt = applyFinalizationReceiptMutationUnderLock(repoRoot, receipt, capability, {
|
|
522
|
+
scope: 'task-comment', operation: 'failed', error: detail
|
|
523
|
+
}, consumedCapabilities);
|
|
524
|
+
receipt = reconcileWarningProjection(repoRoot, taskId, receipt, consumedCapabilities);
|
|
525
|
+
return { receipt, step, changed: result.changed, error: detail };
|
|
526
|
+
} catch (error) {
|
|
527
|
+
const detail = errorOf(error, 'COMMENT_SYNC_FAILED', true);
|
|
528
|
+
try {
|
|
529
|
+
const capability = issueCapability(receipt, 'task-comment');
|
|
530
|
+
receipt = applyFinalizationReceiptMutationUnderLock(repoRoot, receipt, capability, {
|
|
531
|
+
scope: 'task-comment', operation: 'failed', error: detail
|
|
532
|
+
}, consumedCapabilities);
|
|
533
|
+
receipt = reconcileWarningProjection(repoRoot, taskId, receipt, consumedCapabilities);
|
|
534
|
+
} catch { /* preserve the primary error */ }
|
|
535
|
+
return { receipt, step: { status: 'blocked', changed: false, error: detail }, changed: false, error: detail };
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
475
539
|
function terminalResult(
|
|
476
540
|
taskId: string,
|
|
477
541
|
receipt: TaskFinalizationReceipt,
|
|
@@ -603,46 +667,15 @@ function applyUnderLock(
|
|
|
603
667
|
}
|
|
604
668
|
|
|
605
669
|
let taskComment: TaskFinalizationStep | null = null;
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
}
|
|
615
|
-
const result = commentSync(taskId, { kind: 'task', agent: request.agent, cwd: repoRoot });
|
|
616
|
-
taskComment = commentStep(result);
|
|
617
|
-
if (result.status === 'applied' || result.status === 'no-op') {
|
|
618
|
-
const skipped = result.error?.code === 'ISSUE_NOT_LINKED';
|
|
619
|
-
const capability = issueCapability(receipt, 'task-comment');
|
|
620
|
-
receipt = applyFinalizationReceiptMutationUnderLock(repoRoot, receipt, capability, {
|
|
621
|
-
scope: 'task-comment', operation: 'succeeded', state: skipped ? 'skipped' : 'done'
|
|
622
|
-
}, consumedCapabilities);
|
|
623
|
-
receipt = reconcileWarningProjection(repoRoot, taskId, receipt, consumedCapabilities);
|
|
624
|
-
taskComment = skipped ? { ...taskComment, status: 'skipped' } : taskComment;
|
|
625
|
-
changed = changed || result.changed;
|
|
626
|
-
} else {
|
|
627
|
-
const detail = taskComment.error ?? { code: 'COMMENT_SYNC_FAILED', message: 'task comment synchronization failed', retryable: true };
|
|
628
|
-
const capability = issueCapability(receipt, 'task-comment');
|
|
629
|
-
receipt = applyFinalizationReceiptMutationUnderLock(repoRoot, receipt, capability, {
|
|
630
|
-
scope: 'task-comment', operation: 'failed', error: detail
|
|
631
|
-
}, consumedCapabilities);
|
|
632
|
-
receipt = reconcileWarningProjection(repoRoot, taskId, receipt, consumedCapabilities);
|
|
633
|
-
return terminalResult(taskId, receipt, { lifecycle: lifecycleResult, taskComment, verification: null }, changed, taskComment.error);
|
|
634
|
-
}
|
|
635
|
-
}
|
|
636
|
-
} catch (error) {
|
|
637
|
-
const detail = errorOf(error, 'COMMENT_SYNC_FAILED', true);
|
|
638
|
-
try {
|
|
639
|
-
const capability = issueCapability(receipt, 'task-comment');
|
|
640
|
-
receipt = applyFinalizationReceiptMutationUnderLock(repoRoot, receipt, capability, {
|
|
641
|
-
scope: 'task-comment', operation: 'failed', error: detail
|
|
642
|
-
}, consumedCapabilities);
|
|
643
|
-
receipt = reconcileWarningProjection(repoRoot, taskId, receipt, consumedCapabilities);
|
|
644
|
-
} catch { /* preserve the primary error */ }
|
|
645
|
-
return terminalResult(taskId, receipt, { lifecycle: lifecycleResult, taskComment: { status: 'blocked', changed: false, error: detail }, verification: null }, changed, detail);
|
|
670
|
+
receipt = reconcileWarningProjection(repoRoot, taskId, receipt, consumedCapabilities);
|
|
671
|
+
const initialComment = syncPendingTaskComment({
|
|
672
|
+
repoRoot, taskId, agent: request.agent, receipt, commentSync, consumedCapabilities
|
|
673
|
+
});
|
|
674
|
+
receipt = initialComment.receipt;
|
|
675
|
+
taskComment = initialComment.step;
|
|
676
|
+
changed = changed || initialComment.changed;
|
|
677
|
+
if (initialComment.error) {
|
|
678
|
+
return terminalResult(taskId, receipt, { lifecycle: lifecycleResult, taskComment, verification: null }, changed, initialComment.error);
|
|
646
679
|
}
|
|
647
680
|
|
|
648
681
|
let verification: TaskFinalizationStep | null = null;
|
|
@@ -662,6 +695,15 @@ function applyUnderLock(
|
|
|
662
695
|
scope: 'verification', operation: 'succeeded'
|
|
663
696
|
}, consumedCapabilities);
|
|
664
697
|
receipt = reconcileWarningProjection(repoRoot, taskId, receipt, consumedCapabilities);
|
|
698
|
+
const finalComment = syncPendingTaskComment({
|
|
699
|
+
repoRoot, taskId, agent: request.agent, receipt, commentSync, consumedCapabilities
|
|
700
|
+
});
|
|
701
|
+
receipt = finalComment.receipt;
|
|
702
|
+
if (finalComment.step.status !== 'no-op') taskComment = finalComment.step;
|
|
703
|
+
changed = changed || finalComment.changed;
|
|
704
|
+
if (finalComment.error) {
|
|
705
|
+
return terminalResult(taskId, receipt, { lifecycle: lifecycleResult, taskComment, verification }, changed, finalComment.error);
|
|
706
|
+
}
|
|
665
707
|
} else {
|
|
666
708
|
const detail = verification.error ?? { code: 'VERIFY_FAILED', message: 'verification failed', retryable: true };
|
|
667
709
|
const capability = issueCapability(receipt, 'verification');
|
package/lib/task/index.ts
CHANGED
|
@@ -10,7 +10,6 @@ Commands:
|
|
|
10
10
|
ls [--all | --blocked | --completed] List tasks (default: active)
|
|
11
11
|
show [--task <ref>] Print a task.md
|
|
12
12
|
status [--task <ref>] Aggregated status view (metadata / artifacts / git / platform)
|
|
13
|
-
validate <ref> [options] -- <command> Run host validation in snapshot or inplace scope
|
|
14
13
|
|
|
15
14
|
Examples:
|
|
16
15
|
ai task cat 11 analysis
|
|
@@ -30,7 +29,6 @@ Examples:
|
|
|
30
29
|
ai task show 11
|
|
31
30
|
ai task show TASK-20260612-162737
|
|
32
31
|
ai task status 11
|
|
33
|
-
ai task validate 11 -- npm test
|
|
34
32
|
|
|
35
33
|
Run 'ai task <command> --help' for details.`;
|
|
36
34
|
|
|
@@ -95,12 +93,6 @@ export async function runTask(args: string[]): Promise<void> {
|
|
|
95
93
|
status(rest);
|
|
96
94
|
break;
|
|
97
95
|
}
|
|
98
|
-
case 'validate': {
|
|
99
|
-
const { validate } = await import('./commands/validate.ts');
|
|
100
|
-
const exitCode = await validate(rest);
|
|
101
|
-
if (exitCode !== 0) process.exitCode = exitCode;
|
|
102
|
-
break;
|
|
103
|
-
}
|
|
104
96
|
default:
|
|
105
97
|
process.stderr.write(`Unknown task command: ${subcommand}\n\n`);
|
|
106
98
|
process.stdout.write(`${USAGE}\n`);
|
|
@@ -119,6 +119,7 @@ type OrchestrationResult = Readonly<{
|
|
|
119
119
|
taskId: string | null;
|
|
120
120
|
run: OrchestrationRun | null;
|
|
121
121
|
next: OrchestrationNext | null;
|
|
122
|
+
warnings?: readonly Readonly<{ code: string; message: string; action: string }>[];
|
|
122
123
|
error: Readonly<{
|
|
123
124
|
code: string;
|
|
124
125
|
message: string;
|
|
@@ -181,7 +182,7 @@ function orchestrationPath(taskDir: string): string {
|
|
|
181
182
|
return path.join(taskDir, 'orchestration.json');
|
|
182
183
|
}
|
|
183
184
|
|
|
184
|
-
const ORCHESTRATION_STATE_INVALID_MESSAGE = 'orchestration.json does not match the current runtime structure;
|
|
185
|
+
const ORCHESTRATION_STATE_INVALID_MESSAGE = 'orchestration.json does not match the current runtime structure; the file was left unchanged; rebuild the sandbox or manually repair the state before retrying';
|
|
185
186
|
|
|
186
187
|
class OrchestrationStateError extends Error {
|
|
187
188
|
readonly code = 'ORCHESTRATION_STATE_INVALID';
|
|
@@ -341,9 +341,6 @@ function checkOrchestrationEvidence({ taskDir }: any): any {
|
|
|
341
341
|
|| (activatedCodex && (
|
|
342
342
|
host?.kind !== 'codex-lifecycle-v2'
|
|
343
343
|
|| host.protocolVersion !== provenance.protocolVersion
|
|
344
|
-
|| host.packageVersion !== provenance.packageVersion
|
|
345
|
-
|| host.internalExecutableBuildHash !== provenance.internalExecutableBuildHash
|
|
346
|
-
|| host.lifecycleContractHash !== provenance.lifecycleContractHash
|
|
347
344
|
|| host.hookDefinitionHash !== provenance.hookDefinitionHash
|
|
348
345
|
|| host.hookSource !== provenance.hookSource
|
|
349
346
|
|| host.hookSourcePathDigest !== provenance.hookSourcePathDigest
|
package/package.json
CHANGED
|
@@ -27,11 +27,10 @@ npm install
|
|
|
27
27
|
npm run build
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
Lifecycle hooks always invoke `agent-infra-internal` through `PATH`; they do not implicitly switch to checkout source or `dist`. In a Codex sandbox, the controller's `PATH`-first shim is authoritative and binds the controller's selected executable. Skills, rules, hooks, and task data continue to come from the current checkout. The commands above develop the CLI itself and do not change hook executable selection.
|
|
30
|
+
Lifecycle hooks always invoke `agent-infra-internal` through `PATH`; they do not implicitly switch to checkout source or `dist`. In a Codex sandbox, the controller's `PATH`-first shim is authoritative and binds the controller's selected executable. Skills, rules, hooks, and task data continue to come from the current checkout. The commands above develop the CLI itself and do not change hook executable selection. To run the CLI from the current checkout, invoke the build output directly and compare its version with the package version. Do not run `npm link` on the host: it writes to npm's global prefix and can shadow a stable installation managed by Homebrew or npm.
|
|
31
31
|
|
|
32
32
|
```bash
|
|
33
|
-
|
|
34
|
-
ai version --raw
|
|
33
|
+
node ./dist/bin/cli.js version --raw
|
|
35
34
|
node -p "'v' + require('./package.json').version"
|
|
36
35
|
```
|
|
37
36
|
|