@fitlab-ai/agent-infra 0.11.11 → 0.11.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/internal-cli.js +2 -2
- package/dist/lib/agent-clients/adapter.js +7 -1
- package/dist/lib/agent-clients/adapters/claude-code.js +5 -1
- package/dist/lib/agent-clients/adapters/codex-lifecycle/app-server.js +78 -63
- package/dist/lib/agent-clients/adapters/codex-lifecycle/binding.js +114 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/cli.js +447 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-client.js +130 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-operation.js +131 -0
- package/dist/lib/{sandbox/control → agent-clients/adapters/codex-lifecycle}/controller-registration.js +2 -2
- package/dist/lib/agent-clients/adapters/codex-lifecycle/evidence.js +48 -1
- package/dist/lib/agent-clients/adapters/codex-lifecycle/recovery.js +124 -62
- package/dist/lib/{internal/codex-sandbox-controller.js → agent-clients/adapters/codex-lifecycle/sandbox-controller-cli.js} +3 -3
- package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +3 -5
- package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +211 -213
- package/dist/lib/agent-clients/adapters/codex-orchestration.js +468 -0
- package/dist/lib/agent-clients/adapters/codex.js +40 -1
- package/dist/lib/agent-clients/adapters/index.js +2 -10
- package/dist/lib/agent-clients/tokens.js +3 -2
- package/dist/lib/internal/agent-client.js +132 -72
- package/dist/lib/internal/cli-route-inventory.js +2 -2
- package/dist/lib/internal/sandbox-control.js +1 -81
- package/dist/lib/internal/task-finalization.js +26 -19
- package/dist/lib/internal/task-lifecycle.js +1 -1
- package/dist/lib/internal/task-operation-registry.js +1 -1
- package/dist/lib/internal/task-validate.js +1 -1
- package/dist/lib/platform/github-client.js +33 -3
- package/dist/lib/platform/issues.js +18 -6
- package/dist/lib/runtime/agent-runtime.js +6 -29
- package/dist/lib/sandbox/commands/control-status.js +1 -0
- package/dist/lib/sandbox/commands/create.js +7 -2
- package/dist/lib/sandbox/commands/ls.js +4 -5
- package/dist/lib/sandbox/commands/rm.js +1 -1
- package/dist/lib/sandbox/control/agent-operation.js +34 -0
- package/dist/lib/sandbox/control/client.js +14 -201
- package/dist/lib/sandbox/control/executor.js +12 -125
- package/dist/lib/sandbox/control/lifecycle.js +66 -4
- package/dist/lib/sandbox/control/protocol.js +15 -54
- package/dist/lib/sandbox/control/server.js +216 -281
- package/dist/lib/sandbox/control/task-view.js +0 -13
- package/dist/lib/sandbox/mounts.js +9 -1
- package/dist/lib/sandbox/recovery.js +31 -10
- package/dist/lib/sandbox/removal.js +28 -5
- package/dist/lib/sandbox/workspace-identity.js +18 -6
- package/dist/lib/sandbox/workspace-view.js +56 -19
- package/dist/lib/task/commands/ls.js +3 -3
- package/dist/lib/task/control-authority.js +56 -264
- package/dist/lib/task/control-recovery.js +2 -3
- package/dist/lib/task/create.js +2 -2
- package/dist/lib/task/delegation-receipts.js +74 -161
- package/dist/lib/task/events.js +88 -44
- package/dist/lib/task/finalization.js +35 -6
- package/dist/lib/task/lifecycle.js +274 -115
- package/dist/lib/task/orchestration.js +201 -194
- package/dist/lib/task/runtime-paths.js +10 -0
- package/dist/lib/task/short-id.js +37 -27
- package/dist/lib/task/verification-engine.js +32 -26
- package/dist/lib/task/workspace-snapshot.js +1 -1
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-clients/adapter.ts +39 -0
- package/lib/agent-clients/adapters/claude-code.ts +5 -1
- package/lib/agent-clients/adapters/codex-lifecycle/app-server.ts +105 -80
- package/lib/agent-clients/adapters/codex-lifecycle/binding.ts +147 -0
- package/lib/agent-clients/adapters/codex-lifecycle/cli.ts +510 -0
- package/lib/agent-clients/adapters/codex-lifecycle/controller-client.ts +158 -0
- package/lib/agent-clients/adapters/codex-lifecycle/controller-context.ts +1 -1
- package/lib/agent-clients/adapters/codex-lifecycle/controller-operation.ts +151 -0
- package/lib/{sandbox/control → agent-clients/adapters/codex-lifecycle}/controller-registration.ts +4 -4
- package/lib/agent-clients/adapters/codex-lifecycle/evidence.ts +43 -0
- package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +8 -5
- package/lib/agent-clients/adapters/codex-lifecycle/recovery.ts +190 -65
- package/lib/{internal/codex-sandbox-controller.ts → agent-clients/adapters/codex-lifecycle/sandbox-controller-cli.ts} +2 -2
- package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +3 -5
- package/lib/agent-clients/adapters/codex-lifecycle/store.ts +270 -224
- package/lib/agent-clients/adapters/codex-orchestration.ts +632 -0
- package/lib/agent-clients/adapters/codex.ts +44 -1
- package/lib/agent-clients/adapters/index.ts +7 -20
- package/lib/agent-clients/tokens.ts +4 -3
- package/lib/internal/agent-client.ts +142 -73
- package/lib/internal/cli-route-inventory.ts +2 -2
- package/lib/internal/sandbox-control.ts +0 -97
- package/lib/internal/task-finalization.ts +30 -18
- package/lib/internal/task-lifecycle.ts +1 -1
- package/lib/internal/task-operation-registry.ts +1 -1
- package/lib/internal/task-validate.ts +1 -1
- package/lib/platform/github-client.ts +34 -3
- package/lib/platform/issues.ts +18 -6
- package/lib/runtime/agent-runtime.ts +6 -41
- package/lib/sandbox/commands/control-status.ts +1 -0
- package/lib/sandbox/commands/create.ts +7 -1
- package/lib/sandbox/commands/ls.ts +4 -5
- package/lib/sandbox/commands/rm.ts +1 -1
- package/lib/sandbox/control/agent-operation.ts +49 -0
- package/lib/sandbox/control/client.ts +20 -254
- package/lib/sandbox/control/executor.ts +13 -136
- package/lib/sandbox/control/lifecycle.ts +67 -4
- package/lib/sandbox/control/protocol.ts +22 -66
- package/lib/sandbox/control/server.ts +21 -95
- package/lib/sandbox/control/task-view.ts +0 -19
- package/lib/sandbox/mounts.ts +10 -1
- package/lib/sandbox/recovery.ts +38 -17
- package/lib/sandbox/removal.ts +33 -7
- package/lib/sandbox/workspace-identity.ts +16 -6
- package/lib/sandbox/workspace-view.ts +50 -18
- package/lib/task/commands/ls.ts +3 -3
- package/lib/task/control-authority.ts +66 -378
- package/lib/task/control-recovery.ts +3 -4
- package/lib/task/create.ts +3 -3
- package/lib/task/delegation-receipts.ts +87 -231
- package/lib/task/events.ts +100 -42
- package/lib/task/finalization.ts +37 -7
- package/lib/task/lifecycle.ts +257 -109
- package/lib/task/orchestration.ts +260 -237
- package/lib/task/runtime-paths.ts +12 -0
- package/lib/task/short-id.ts +33 -40
- package/lib/task/verification-engine.ts +30 -25
- package/lib/task/workspace-snapshot.ts +1 -1
- package/package.json +1 -1
- package/templates/.agents/hooks/lifecycle-delegation.js +1 -25
- package/templates/.agents/rules/README.en.md +2 -1
- package/templates/.agents/rules/README.zh-CN.md +2 -1
- package/templates/.agents/rules/next-step-output.en.md +6 -19
- package/templates/.agents/rules/next-step-output.zh-CN.md +6 -19
- package/templates/.agents/rules/strategy-pattern.en.md +25 -0
- package/templates/.agents/rules/strategy-pattern.zh-CN.md +25 -0
- package/templates/.agents/skills/analyze-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/check-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/check-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/code-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/code-task/reference/code-rules.en.md +4 -0
- package/templates/.agents/skills/code-task/reference/code-rules.zh-CN.md +4 -0
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +0 -1
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +0 -1
- package/templates/.agents/skills/complete-task/SKILL.en.md +5 -29
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +5 -26
- package/templates/.agents/skills/plan-task/SKILL.en.md +7 -4
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +7 -4
- package/templates/.agents/skills/review-analysis/SKILL.en.md +1 -1
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/review-code/SKILL.en.md +1 -1
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/review-plan/SKILL.en.md +1 -1
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +1 -1
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
- package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +0 -546
- package/dist/lib/internal/codex-lifecycle.js +0 -385
- package/dist/lib/task/codex-orchestration.js +0 -206
- package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +0 -776
- package/lib/internal/codex-lifecycle.ts +0 -390
- package/lib/task/codex-orchestration.ts +0 -276
|
@@ -51,6 +51,33 @@ function boundedFailureDetail(result: RunResult): string {
|
|
|
51
51
|
: `${diagnostic.slice(0, ERROR_DETAIL_LIMIT)}… [truncated]`;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
function requestDiagnostics(args: string[], request: RequestOptions, attempt: number, errorMessage: string): string {
|
|
55
|
+
if (process.env.AGENT_INFRA_GH_DIAGNOSTICS !== '1' && !/rate.?limit/iu.test(errorMessage)) return '';
|
|
56
|
+
|
|
57
|
+
const apiIndex = args.indexOf('api');
|
|
58
|
+
const endpoint = apiIndex < 0
|
|
59
|
+
? args.slice(0, 2).filter((arg) => !arg.startsWith('-')).join(' ')
|
|
60
|
+
: args.slice(apiIndex + 1).find((arg) => !arg.startsWith('-') && !arg.includes('=')) || 'unknown';
|
|
61
|
+
const safeEndpoint = endpoint.startsWith('http')
|
|
62
|
+
? (() => {
|
|
63
|
+
try {
|
|
64
|
+
const url = new URL(endpoint);
|
|
65
|
+
return `${url.host}${url.pathname}`;
|
|
66
|
+
} catch {
|
|
67
|
+
return 'unknown';
|
|
68
|
+
}
|
|
69
|
+
})()
|
|
70
|
+
: endpoint.split('?')[0] || 'unknown';
|
|
71
|
+
const methodIndex = args.findIndex((arg) => arg === '-X' || arg === '--method');
|
|
72
|
+
const methodFromArgs = methodIndex >= 0 ? args[methodIndex + 1] : undefined;
|
|
73
|
+
const method = methodFromArgs || request.method || (safeEndpoint === 'graphql' ? 'POST' : 'GET');
|
|
74
|
+
const tokenEnvironment = ['GH_TOKEN', 'GITHUB_TOKEN', 'GH_ENTERPRISE_TOKEN', 'GITHUB_ENTERPRISE_TOKEN']
|
|
75
|
+
.filter((name) => Boolean(process.env[name]));
|
|
76
|
+
const configDirOverride = Boolean(process.env.GH_CONFIG_DIR);
|
|
77
|
+
|
|
78
|
+
return ` [gh-diagnostic endpoint=${safeEndpoint} method=${method} attempt=${attempt} tokenEnv=${tokenEnvironment.join(',') || 'none'} ghConfigDirOverride=${configDirOverride}]`;
|
|
79
|
+
}
|
|
80
|
+
|
|
54
81
|
function defaultRunner(args: string[], options: RunOptions): RunResult {
|
|
55
82
|
const command = process.env.AGENT_INFRA_GH_BIN || 'gh';
|
|
56
83
|
let prefix: string[] = [];
|
|
@@ -168,13 +195,17 @@ function createGitHubClient(options: ClientOptions = {}): GitHubClient {
|
|
|
168
195
|
const result = runner(args, request);
|
|
169
196
|
if (result.status === 0) return { ok: true, value: result.stdout };
|
|
170
197
|
const error = classifyGitHubFailure(result);
|
|
171
|
-
|
|
198
|
+
const requestDetail = requestDiagnostics(args, request, attempt + 1, error.message);
|
|
199
|
+
const contextualError = requestDetail
|
|
200
|
+
? { ...error, message: `${error.message}${requestDetail}` }
|
|
201
|
+
: error;
|
|
202
|
+
if (!retryableMethod || !error.requestRetryable) return { ok: false, error: clientError(contextualError) };
|
|
172
203
|
if (attempt >= delays.length) {
|
|
173
204
|
return {
|
|
174
205
|
ok: false,
|
|
175
206
|
error: error.code === 'NETWORK_TRANSIENT'
|
|
176
|
-
? { ...clientError(
|
|
177
|
-
: clientError(
|
|
207
|
+
? { ...clientError(contextualError), code: 'NETWORK_RETRY_EXHAUSTED', message: `${contextualError.message} (retry exhausted)` }
|
|
208
|
+
: clientError(contextualError)
|
|
178
209
|
};
|
|
179
210
|
}
|
|
180
211
|
sleep(delays[attempt]!);
|
package/lib/platform/issues.ts
CHANGED
|
@@ -516,7 +516,8 @@ async function syncPlatformIssueImpl(taskRef: string, options: SyncOptions): Pro
|
|
|
516
516
|
let providerMetadata: RepositoryMetadataSnapshot | null = null;
|
|
517
517
|
let repositoryLabels: string[] = [];
|
|
518
518
|
let milestones: string[] = [];
|
|
519
|
-
if (base.context.capabilities.triage && (options.status !== undefined || options.inLabels !== undefined || options.milestone !== undefined
|
|
519
|
+
if (base.context.capabilities.triage && (options.status !== undefined || options.inLabels !== undefined || options.milestone !== undefined
|
|
520
|
+
|| (base.context.capabilities.push && (options.issueType || options.fields)))) {
|
|
520
521
|
const metadata = base.provider.issues?.describeRepository
|
|
521
522
|
? await base.provider.issues.describeRepository({ context: providerOperationContext(base.loadedContext) })
|
|
522
523
|
: unsupportedProviderOperation(base.provider, 'issues.describeRepository');
|
|
@@ -592,15 +593,26 @@ async function syncPlatformIssueImpl(taskRef: string, options: SyncOptions): Pro
|
|
|
592
593
|
}
|
|
593
594
|
}
|
|
594
595
|
if (options.issueType || options.fields) {
|
|
595
|
-
plan.operations.push(...(
|
|
596
|
-
?
|
|
597
|
-
|
|
596
|
+
plan.operations.push(...(!base.context.capabilities.push
|
|
597
|
+
? [
|
|
598
|
+
...(options.issueType ? [{ name: 'issue-type', status: 'skipped' as const, reasonCode: 'PUSH_REQUIRED' }] : []),
|
|
599
|
+
...(options.fields ? [{ name: 'fields', status: 'skipped' as const, reasonCode: 'PUSH_REQUIRED' }] : [])
|
|
600
|
+
]
|
|
601
|
+
: providerMetadata
|
|
602
|
+
? planProviderMetadata(inspected.value, providerMetadata, base.frontmatter, options, base.context.capabilities)
|
|
603
|
+
: [{ name: 'issue-type', status: 'skipped' as const, reasonCode: 'ISSUE_TYPES_UNSUPPORTED' }]));
|
|
598
604
|
}
|
|
599
605
|
const failure = plan.operations.find((operation) => operation.status === 'failed');
|
|
600
606
|
if (failure) return result('failed', base.resolved.taskId, base.issueNumber, { issue: snapshot, operations: plan.operations, error: { code: failure.reasonCode || 'ISSUE_SYNC_FAILED', message: `Operation ${failure.name} failed`, retryable: false } });
|
|
601
607
|
const planned = plan.operations.filter((operation) => operation.status === 'planned');
|
|
602
|
-
if (options.dryRun) return result(planned.length ? 'planned' : 'no-op', base.resolved.taskId, base.issueNumber, {
|
|
603
|
-
|
|
608
|
+
if (options.dryRun) return result(planned.length ? 'planned' : 'no-op', base.resolved.taskId, base.issueNumber, {
|
|
609
|
+
platform: base.context.platform, capabilities: base.context.capabilities,
|
|
610
|
+
issue: snapshot, operations: plan.operations, error: null
|
|
611
|
+
});
|
|
612
|
+
if (planned.length === 0) return result('no-op', base.resolved.taskId, base.issueNumber, {
|
|
613
|
+
platform: base.context.platform, capabilities: base.context.capabilities,
|
|
614
|
+
issue: snapshot, operations: plan.operations, error: null
|
|
615
|
+
});
|
|
604
616
|
const payload = applyRestOperations(snapshot, planned) as Partial<{
|
|
605
617
|
title: string; body: string; labels: string[]; assignees: string[];
|
|
606
618
|
milestone: string | null; state: 'open' | 'closed'; fields: Record<string, string | number | null>;
|
|
@@ -1,16 +1,5 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
-
|
|
3
|
-
export type AgentRuntimeStore = 'capabilities' | 'lifecycle';
|
|
4
|
-
|
|
5
|
-
type RuntimeResolutionOptions = Readonly<{
|
|
6
|
-
repoRoot?: string;
|
|
7
|
-
env?: NodeJS.ProcessEnv;
|
|
8
|
-
explicitRoot?: string;
|
|
9
|
-
client?: string;
|
|
10
|
-
store?: AgentRuntimeStore;
|
|
11
|
-
}>;
|
|
12
|
-
|
|
13
|
-
const CLIENT_NAMESPACE = /^[a-z][a-z0-9-]{0,31}$/u;
|
|
2
|
+
import { resolveTaskRuntimeRoot } from '../task/runtime-paths.ts';
|
|
14
3
|
|
|
15
4
|
function runtimeError(code: string, message: string): Error {
|
|
16
5
|
const error = new Error(`${code}: ${message}`);
|
|
@@ -22,18 +11,10 @@ function boundControlContext(env: NodeJS.ProcessEnv): boolean {
|
|
|
22
11
|
return [
|
|
23
12
|
'AGENT_INFRA_CONTROL_TOKEN',
|
|
24
13
|
'AGENT_INFRA_CONTROL_GENERATION',
|
|
25
|
-
'AGENT_INFRA_EXECUTOR_MANIFEST'
|
|
26
|
-
'AGENT_INFRA_CODEX_CONTROLLER_CONTEXT'
|
|
14
|
+
'AGENT_INFRA_EXECUTOR_MANIFEST'
|
|
27
15
|
].some((key) => typeof env[key] === 'string' && env[key]!.length > 0);
|
|
28
16
|
}
|
|
29
17
|
|
|
30
|
-
function clientNamespace(client: string): string {
|
|
31
|
-
if (!CLIENT_NAMESPACE.test(client)) {
|
|
32
|
-
throw runtimeError('AGENT_INFRA_RUNTIME_CLIENT_INVALID', `invalid runtime client namespace '${client}'`);
|
|
33
|
-
}
|
|
34
|
-
return client;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
18
|
export function resolveAgentRuntimeRoot(options: Readonly<{
|
|
38
19
|
repoRoot?: string;
|
|
39
20
|
env?: NodeJS.ProcessEnv;
|
|
@@ -46,6 +27,10 @@ export function resolveAgentRuntimeRoot(options: Readonly<{
|
|
|
46
27
|
}
|
|
47
28
|
return path.resolve(configured);
|
|
48
29
|
}
|
|
30
|
+
const taskId = env.AGENT_INFRA_TASK_ID;
|
|
31
|
+
if (taskId) {
|
|
32
|
+
return resolveTaskRuntimeRoot(taskId, { repoRoot: options.repoRoot });
|
|
33
|
+
}
|
|
49
34
|
if (boundControlContext(env)) {
|
|
50
35
|
throw runtimeError(
|
|
51
36
|
'AGENT_INFRA_RUNTIME_DIR_REQUIRED',
|
|
@@ -54,23 +39,3 @@ export function resolveAgentRuntimeRoot(options: Readonly<{
|
|
|
54
39
|
}
|
|
55
40
|
return path.join(path.resolve(options.repoRoot ?? process.cwd()), '.agents', 'workspace', '.runtime');
|
|
56
41
|
}
|
|
57
|
-
|
|
58
|
-
export function resolveAgentRuntimeStoreRoot(options: RuntimeResolutionOptions = {}): string {
|
|
59
|
-
const client = clientNamespace(options.client ?? 'codex');
|
|
60
|
-
if (options.explicitRoot) return path.resolve(options.explicitRoot);
|
|
61
|
-
const env = options.env ?? process.env;
|
|
62
|
-
const runtimeRoot = resolveAgentRuntimeRoot({ repoRoot: options.repoRoot, env });
|
|
63
|
-
if (env.AGENT_INFRA_RUNTIME_DIR) {
|
|
64
|
-
if (!options.store) {
|
|
65
|
-
throw runtimeError('AGENT_INFRA_RUNTIME_STORE_INVALID', 'store kind is required for task-bound runtime');
|
|
66
|
-
}
|
|
67
|
-
return path.join(runtimeRoot, 'clients', client, options.store);
|
|
68
|
-
}
|
|
69
|
-
if (client === 'codex' && options.store) {
|
|
70
|
-
return path.join(runtimeRoot, `codex-${options.store}`);
|
|
71
|
-
}
|
|
72
|
-
if (!options.store) {
|
|
73
|
-
throw runtimeError('AGENT_INFRA_RUNTIME_STORE_INVALID', 'store kind is required');
|
|
74
|
-
}
|
|
75
|
-
return path.join(runtimeRoot, 'clients', client, options.store);
|
|
76
|
-
}
|
|
@@ -91,6 +91,7 @@ import {
|
|
|
91
91
|
materializeSandboxControl,
|
|
92
92
|
materializeSandboxWorkspaceView,
|
|
93
93
|
prepareSandboxWorkspaceMountTargets,
|
|
94
|
+
prepareSandboxCompletedTaskMountSource,
|
|
94
95
|
sandboxControlPaths,
|
|
95
96
|
} from '../workspace-view.ts';
|
|
96
97
|
import { clipboardHostDir, CONTAINER_CLIPBOARD_MOUNT } from '../clipboard/paths.ts';
|
|
@@ -1107,6 +1108,7 @@ export async function create(
|
|
|
1107
1108
|
const container = containerName(effectiveConfig, branch);
|
|
1108
1109
|
const controlPaths = sandboxControlPaths({
|
|
1109
1110
|
base: effectiveConfig.controlBase,
|
|
1111
|
+
repoRoot: effectiveConfig.repoRoot,
|
|
1110
1112
|
project: effectiveConfig.project,
|
|
1111
1113
|
container,
|
|
1112
1114
|
identity: target.workspace
|
|
@@ -1450,7 +1452,10 @@ export async function create(
|
|
|
1450
1452
|
const taskSource = target.workspace.mode === 'task-bound'
|
|
1451
1453
|
? assertSandboxTaskSource(effectiveConfig.repoRoot, target.workspace.taskId)
|
|
1452
1454
|
: null;
|
|
1453
|
-
|
|
1455
|
+
const completedTaskSource = target.workspace.mode === 'task-bound'
|
|
1456
|
+
? prepareSandboxCompletedTaskMountSource(effectiveConfig.repoRoot, target.workspace.taskId)
|
|
1457
|
+
: null;
|
|
1458
|
+
prepareSandboxWorkspaceMountTargets(worktree, target.workspace.mode === 'task-bound' ? target.workspace.taskId : undefined);
|
|
1454
1459
|
if (previousCutoverSnapshot) {
|
|
1455
1460
|
assertSandboxControlCutoverSnapshot(previousCutoverSnapshot);
|
|
1456
1461
|
if (previousManifest) {
|
|
@@ -1493,6 +1498,7 @@ export async function create(
|
|
|
1493
1498
|
...(target.workspace.mode === 'task-bound'
|
|
1494
1499
|
? {
|
|
1495
1500
|
taskSources: [taskSource!],
|
|
1501
|
+
completedTaskSource: completedTaskSource!,
|
|
1496
1502
|
taskId: target.workspace.taskId
|
|
1497
1503
|
}
|
|
1498
1504
|
: {})
|
|
@@ -18,11 +18,10 @@ export { containerListFormat, parseLabels } from './list-running.ts';
|
|
|
18
18
|
const USAGE = `Usage: ai sandbox ls
|
|
19
19
|
|
|
20
20
|
Lists all containers for the current project. The '#' column is a
|
|
21
|
-
display-only row number; the 'SHORT' column shows the
|
|
22
|
-
|
|
23
|
-
.
|
|
24
|
-
|
|
25
|
-
A SHORT value of '-' means this container has no active task short id and may
|
|
21
|
+
display-only row number; the 'SHORT' column shows the task short id bound to
|
|
22
|
+
each task-bound container, or '-' if no short id is registered. Pass the
|
|
23
|
+
SHORT value to "ai sandbox exec" (e.g. 'ai sandbox exec 11').
|
|
24
|
+
A SHORT value of '-' means this container has no task short id and may
|
|
26
25
|
be eligible for "ai sandbox rm --unbound" cleanup. Use a full TASK-id to
|
|
27
26
|
remove a specific task-bound sandbox.
|
|
28
27
|
|
|
@@ -13,7 +13,7 @@ import { fetchSandboxRows } from './list-running.ts';
|
|
|
13
13
|
import { rmOne, rmPurge, rmUnbound } from '../removal.ts';
|
|
14
14
|
|
|
15
15
|
const USAGE = `Usage:
|
|
16
|
-
ai sandbox rm <branch | TASK-id | short id> Remove one sandbox; use a full TASK-id for a task-bound sandbox and a branch for branch-only sandboxes
|
|
16
|
+
ai sandbox rm <branch | TASK-id | short id> Remove one sandbox; use a retained short id or full TASK-id for a task-bound sandbox and a branch for branch-only sandboxes
|
|
17
17
|
ai sandbox rm --unbound [--dry-run] [--yes] Remove sandboxes not bound to an active task short id
|
|
18
18
|
ai sandbox rm --purge Tear down ALL sandboxes for the project (containers, worktrees, image, VM)`;
|
|
19
19
|
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { getAgentClientAdapter } from '../../agent-clients/registry.ts';
|
|
2
|
+
import type { AgentClientSandboxControlOperation } from '../../agent-clients/adapter.ts';
|
|
3
|
+
import type { SandboxControlManifest, SandboxAgentClientRequest } from './protocol.ts';
|
|
4
|
+
|
|
5
|
+
function operationFor(request: SandboxAgentClientRequest) {
|
|
6
|
+
const adapter = getAgentClientAdapter(request.agentClient);
|
|
7
|
+
const operation = adapter.sandboxControlOperation;
|
|
8
|
+
if (!operation) throw new Error('SANDBOX_CONTROL_AGENT_OPERATION_UNSUPPORTED');
|
|
9
|
+
return operation;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function validateAgentClientOperation(request: SandboxAgentClientRequest, manifest: SandboxControlManifest): void {
|
|
13
|
+
operationFor(request).validate({
|
|
14
|
+
operation: request.operation,
|
|
15
|
+
payload: request.payload,
|
|
16
|
+
manifest
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function executeAgentClientOperation(
|
|
21
|
+
request: SandboxAgentClientRequest,
|
|
22
|
+
manifest: SandboxControlManifest,
|
|
23
|
+
manifestPath: string,
|
|
24
|
+
override?: AgentClientSandboxControlOperation
|
|
25
|
+
) {
|
|
26
|
+
return (override ?? operationFor(request)).execute({
|
|
27
|
+
operation: request.operation,
|
|
28
|
+
payload: request.payload,
|
|
29
|
+
manifest,
|
|
30
|
+
manifestPath
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function recoverAgentClientOperation(params: Readonly<{
|
|
35
|
+
request: SandboxAgentClientRequest;
|
|
36
|
+
manifest: SandboxControlManifest;
|
|
37
|
+
manifestPath: string;
|
|
38
|
+
stdout: string | null;
|
|
39
|
+
}>): Readonly<Record<string, unknown>> {
|
|
40
|
+
const operation = operationFor(params.request);
|
|
41
|
+
return operation.recover?.({
|
|
42
|
+
operation: params.request.operation,
|
|
43
|
+
manifest: params.manifest,
|
|
44
|
+
manifestPath: params.manifestPath,
|
|
45
|
+
stdout: params.stdout
|
|
46
|
+
}) ?? { consistent: false };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export { executeAgentClientOperation, recoverAgentClientOperation, validateAgentClientOperation };
|
|
@@ -9,16 +9,10 @@ import {
|
|
|
9
9
|
type SandboxControlError,
|
|
10
10
|
type SandboxControlRequest,
|
|
11
11
|
type SandboxControlResponse,
|
|
12
|
-
type
|
|
13
|
-
type
|
|
14
|
-
type SandboxCodexControllerRequest
|
|
12
|
+
type SandboxAgentClientRequest,
|
|
13
|
+
type SandboxTaskCreateRequest
|
|
15
14
|
} from './protocol.ts';
|
|
16
|
-
import
|
|
17
|
-
CodexControllerLeaseProofV1,
|
|
18
|
-
CodexControllerOpened
|
|
19
|
-
} from './controller-registration.ts';
|
|
20
|
-
import type { ProcessIdentity } from '../../server/process-state.ts';
|
|
21
|
-
import { normalizeAgentToken } from '../../agent-clients/tokens.ts';
|
|
15
|
+
import { isAgentClientId } from '../../agent-clients/types.ts';
|
|
22
16
|
import { readSandboxControlPayload, readSandboxControlStatus } from './state.ts';
|
|
23
17
|
import type { TaskCreateCandidateV1 } from '../../task/create.ts';
|
|
24
18
|
import { accessSandboxTaskView, taskViewFromStatus, type TaskViewAccessEffect } from './task-view.ts';
|
|
@@ -29,7 +23,6 @@ import { loadConfig } from '../config.ts';
|
|
|
29
23
|
import { recoverSandboxControlFromHost } from './host-recovery.ts';
|
|
30
24
|
|
|
31
25
|
const SANDBOX_CONTROL_RESPONSE_SETTLE_MS = 250;
|
|
32
|
-
const SANDBOX_TASK_FINALIZATION_RECOVERY_BUDGET_MS = 5 * 60_000;
|
|
33
26
|
|
|
34
27
|
export class SandboxControlClientError extends Error {
|
|
35
28
|
readonly detail: SandboxControlError;
|
|
@@ -118,7 +111,6 @@ function preflight(
|
|
|
118
111
|
|
|
119
112
|
function taskViewEffectForRequest(request: SandboxControlRequest): TaskViewAccessEffect | null {
|
|
120
113
|
if (request.family === 'task-lifecycle') return 'progress';
|
|
121
|
-
if (request.family === 'task-finalization') return 'recovery';
|
|
122
114
|
return null;
|
|
123
115
|
}
|
|
124
116
|
|
|
@@ -220,9 +212,6 @@ function exchangeSandboxControl(request: SandboxControlRequest, params: Readonly
|
|
|
220
212
|
|| (requestTask.kind === 'task' && requestTask.taskId !== identity.taskId))) {
|
|
221
213
|
clientError('SANDBOX_CONTROL_IDENTITY_TOPOLOGY_MISMATCH', 'request task does not match the sandbox identity', false);
|
|
222
214
|
}
|
|
223
|
-
if (identity.mode === 'branch-only' && request.family === 'task-finalization') {
|
|
224
|
-
clientError('SANDBOX_CONTROL_BRANCH_ONLY', 'branch-only sandboxes cannot finalize tasks', false);
|
|
225
|
-
}
|
|
226
215
|
}
|
|
227
216
|
const encoded = `${JSON.stringify(request)}\n`;
|
|
228
217
|
if (Buffer.byteLength(encoded, 'utf8') > SANDBOX_CONTROL_MAX_BYTES) {
|
|
@@ -425,270 +414,49 @@ export function requestSandboxControl(params: Readonly<{
|
|
|
425
414
|
token?: string; generation?: string; timeoutMs?: number;
|
|
426
415
|
}>): SandboxControlResponse {
|
|
427
416
|
if (!isSandboxControlFamily(params.family)) clientError('SANDBOX_CONTROL_COMMAND_DENIED', `'${params.family}' is not allowed`, false);
|
|
428
|
-
if (params.family === 'task-create' || params.family === '
|
|
417
|
+
if (params.family === 'task-create' || params.family === 'agent-client') {
|
|
429
418
|
clientError('SANDBOX_CONTROL_COMMAND_DENIED', `'${params.family}' requires a typed request`, false);
|
|
430
419
|
}
|
|
420
|
+
if (params.args[1] === 'complete') {
|
|
421
|
+
clientError('SANDBOX_CONTROL_COMMAND_DENIED', 'complete must use task-finalization', false);
|
|
422
|
+
}
|
|
431
423
|
const auth = authority(params);
|
|
432
424
|
const issuedAt = Date.now();
|
|
433
425
|
const request: SandboxControlRequest = {
|
|
434
426
|
version: 4, id: randomUUID(), ...auth, issuedAt,
|
|
435
427
|
expiresAt: issuedAt + SANDBOX_CONTROL_ADMISSION_WINDOW_MS,
|
|
436
|
-
family: params.family as 'task-lifecycle', args: params.args
|
|
437
|
-
controllerProcess: null,
|
|
438
|
-
controllerProof: null
|
|
428
|
+
family: params.family as 'task-lifecycle', args: params.args
|
|
439
429
|
};
|
|
440
430
|
return exchangeSandboxControl(request, params);
|
|
441
431
|
}
|
|
442
432
|
|
|
443
|
-
export function
|
|
444
|
-
|
|
433
|
+
export function requestSandboxAgentOperation(params: Readonly<{
|
|
434
|
+
agentClient: string;
|
|
435
|
+
operation: string;
|
|
436
|
+
payload: Record<string, unknown>;
|
|
445
437
|
channelDir?: string;
|
|
446
438
|
statusDir?: string;
|
|
447
439
|
token?: string;
|
|
448
440
|
generation?: string;
|
|
449
441
|
timeoutMs?: number;
|
|
450
|
-
recoveryBudgetMs?: number;
|
|
451
442
|
}>): SandboxControlResponse {
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
const auth = authority(params);
|
|
455
|
-
const issuedAt = Date.now();
|
|
456
|
-
const id = randomUUID();
|
|
457
|
-
const request: SandboxTaskFinalizationRequest = {
|
|
458
|
-
version: 4,
|
|
459
|
-
id,
|
|
460
|
-
...auth,
|
|
461
|
-
issuedAt,
|
|
462
|
-
expiresAt: issuedAt + SANDBOX_CONTROL_ADMISSION_WINDOW_MS,
|
|
463
|
-
family: 'task-finalization',
|
|
464
|
-
operation: 'complete',
|
|
465
|
-
agent,
|
|
466
|
-
args: [],
|
|
467
|
-
controllerProcess: null,
|
|
468
|
-
controllerProof: null
|
|
469
|
-
};
|
|
470
|
-
let response: SandboxControlResponse;
|
|
471
|
-
try {
|
|
472
|
-
response = exchangeSandboxControl(request, params);
|
|
473
|
-
} catch (error) {
|
|
474
|
-
if (!(error instanceof SandboxControlClientError)
|
|
475
|
-
|| error.detail.code !== 'SANDBOX_CONTROL_RESULT_UNKNOWN'
|
|
476
|
-
|| !error.accepted || !error.requestId) throw error;
|
|
477
|
-
return recoverAcceptedTaskFinalization(error.requestId, params);
|
|
478
|
-
}
|
|
479
|
-
if (response.phase === 'rejected' && response.error?.code === 'SANDBOX_CONTROL_RESULT_UNKNOWN') {
|
|
480
|
-
return recoverAcceptedTaskFinalization(response.id, params);
|
|
481
|
-
}
|
|
482
|
-
return response;
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
export function recoverAcceptedTaskFinalization(
|
|
486
|
-
requestId: string,
|
|
487
|
-
params: Readonly<{ recoveryBudgetMs?: number }>,
|
|
488
|
-
recover: typeof recoverSandboxControl = recoverSandboxControl
|
|
489
|
-
): SandboxControlResponse {
|
|
490
|
-
const deadline = Date.now() + (params.recoveryBudgetMs ?? SANDBOX_TASK_FINALIZATION_RECOVERY_BUDGET_MS);
|
|
491
|
-
while (Date.now() < deadline) {
|
|
492
|
-
const timeoutMs = Math.min(30_000, deadline - Date.now());
|
|
493
|
-
if (timeoutMs <= 0) break;
|
|
494
|
-
try {
|
|
495
|
-
const response = recover(requestId, { timeoutMs });
|
|
496
|
-
if (response.phase === 'rejected' && response.error?.code === 'SANDBOX_CONTROL_RESULT_UNKNOWN') continue;
|
|
497
|
-
return response;
|
|
498
|
-
} catch (error) {
|
|
499
|
-
if (error instanceof SandboxControlClientError
|
|
500
|
-
&& error.detail.code === 'SANDBOX_CONTROL_RESULT_UNKNOWN'
|
|
501
|
-
&& error.requestId === requestId) continue;
|
|
502
|
-
throw error;
|
|
503
|
-
}
|
|
443
|
+
if (!isAgentClientId(params.agentClient)) {
|
|
444
|
+
clientError('SANDBOX_CONTROL_REQUEST_INVALID', 'agent-client id is invalid', false);
|
|
504
445
|
}
|
|
505
|
-
clientError(
|
|
506
|
-
'SANDBOX_CONTROL_RESULT_UNKNOWN',
|
|
507
|
-
'accepted finalization request remained unknown after the automatic recovery budget; inspect the receipt and resume by request id',
|
|
508
|
-
false,
|
|
509
|
-
true,
|
|
510
|
-
requestId
|
|
511
|
-
);
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
type CodexControllerClosed = Readonly<{
|
|
515
|
-
version: 1;
|
|
516
|
-
status: 'closed';
|
|
517
|
-
changed: boolean;
|
|
518
|
-
lease: null;
|
|
519
|
-
error: null;
|
|
520
|
-
}>;
|
|
521
|
-
|
|
522
|
-
type CodexControllerVerified = Readonly<{
|
|
523
|
-
version: 1;
|
|
524
|
-
status: 'verified';
|
|
525
|
-
changed: false;
|
|
526
|
-
lease: null;
|
|
527
|
-
binding: Readonly<{
|
|
528
|
-
taskId: string;
|
|
529
|
-
controlGeneration: string;
|
|
530
|
-
controllerInstanceDigest: string;
|
|
531
|
-
}>;
|
|
532
|
-
error: null;
|
|
533
|
-
}>;
|
|
534
|
-
|
|
535
|
-
function exactKeys(value: Record<string, unknown>, keys: readonly string[]): boolean {
|
|
536
|
-
return Object.keys(value).sort().join(',') === [...keys].sort().join(',');
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
function validProcess(value: unknown): value is ProcessIdentity {
|
|
540
|
-
if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
|
|
541
|
-
const identity = value as Record<string, unknown>;
|
|
542
|
-
return exactKeys(identity, ['pid', 'startTime'])
|
|
543
|
-
&& Number.isSafeInteger(identity.pid) && (identity.pid as number) > 0
|
|
544
|
-
&& Number.isSafeInteger(identity.startTime) && (identity.startTime as number) >= 0;
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
function validBuild(value: unknown): boolean {
|
|
548
|
-
if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
|
|
549
|
-
const build = value as Record<string, unknown>;
|
|
550
|
-
return exactKeys(build, ['internalExecutableBuildHash', 'lifecycleContractHash', 'packageVersion', 'protocolVersion'])
|
|
551
|
-
&& build.protocolVersion === 3
|
|
552
|
-
&& typeof build.packageVersion === 'string' && build.packageVersion.length > 0
|
|
553
|
-
&& typeof build.internalExecutableBuildHash === 'string' && /^[a-f0-9]{64}$/u.test(build.internalExecutableBuildHash)
|
|
554
|
-
&& typeof build.lifecycleContractHash === 'string' && /^[a-f0-9]{64}$/u.test(build.lifecycleContractHash);
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
export function parseCodexControllerResult(response: SandboxControlResponse): CodexControllerOpened | CodexControllerClosed | CodexControllerVerified {
|
|
558
|
-
if (response.phase !== 'completed' || response.error !== null || response.stderr !== '') {
|
|
559
|
-
clientError('SANDBOX_CONTROL_RESULT_INVALID', 'controller result outer response is invalid', false, true);
|
|
560
|
-
}
|
|
561
|
-
let value: unknown;
|
|
562
|
-
try {
|
|
563
|
-
if (!response.stdout.endsWith('\n') || response.stdout.slice(0, -1).includes('\n')) throw new Error('not canonical');
|
|
564
|
-
value = JSON.parse(response.stdout);
|
|
565
|
-
} catch {
|
|
566
|
-
clientError('SANDBOX_CONTROL_RESULT_INVALID', 'controller result payload is invalid', false, true);
|
|
567
|
-
}
|
|
568
|
-
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
569
|
-
clientError('SANDBOX_CONTROL_RESULT_INVALID', 'controller result payload is invalid', false, true);
|
|
570
|
-
}
|
|
571
|
-
const result = value as Record<string, unknown>;
|
|
572
|
-
const resultKeys = result.status === 'verified'
|
|
573
|
-
? ['binding', 'changed', 'error', 'lease', 'status', 'version']
|
|
574
|
-
: ['changed', 'error', 'lease', 'status', 'version'];
|
|
575
|
-
if (!exactKeys(result, resultKeys)
|
|
576
|
-
|| result.version !== 1 || !['opened', 'closed', 'failed', 'verified'].includes(result.status as string)) {
|
|
577
|
-
clientError('SANDBOX_CONTROL_RESULT_INVALID', 'controller result schema is invalid', false, true);
|
|
578
|
-
}
|
|
579
|
-
if (result.status === 'failed') {
|
|
580
|
-
const error = result.error as { code?: unknown; message?: unknown; retryable?: unknown } | null;
|
|
581
|
-
if (response.exitCode !== 1 || result.changed !== false || result.lease !== null
|
|
582
|
-
|| !error || !exactKeys(error as Record<string, unknown>, ['code', 'message', 'retryable'])
|
|
583
|
-
|| typeof error.code !== 'string' || !/^[A-Z][A-Z0-9_]+$/u.test(error.code)
|
|
584
|
-
|| typeof error.message !== 'string' || error.retryable !== false) {
|
|
585
|
-
clientError('SANDBOX_CONTROL_RESULT_INVALID', 'controller failure result is invalid', false, true);
|
|
586
|
-
}
|
|
587
|
-
clientError(error.code, error.message, false, true);
|
|
588
|
-
}
|
|
589
|
-
if (response.exitCode !== 0 || result.error !== null) {
|
|
590
|
-
clientError('SANDBOX_CONTROL_RESULT_INVALID', 'controller success result is invalid', false, true);
|
|
591
|
-
}
|
|
592
|
-
if (result.status === 'closed') {
|
|
593
|
-
if (typeof result.changed !== 'boolean' || result.lease !== null) {
|
|
594
|
-
clientError('SANDBOX_CONTROL_RESULT_INVALID', 'controller close result is invalid', false, true);
|
|
595
|
-
}
|
|
596
|
-
return result as unknown as CodexControllerClosed;
|
|
597
|
-
}
|
|
598
|
-
if (result.status === 'verified') {
|
|
599
|
-
const binding = result.binding as Record<string, unknown> | null;
|
|
600
|
-
if (result.changed !== false || result.lease !== null || !binding
|
|
601
|
-
|| !exactKeys(binding, ['controllerInstanceDigest', 'controlGeneration', 'taskId'])
|
|
602
|
-
|| typeof binding.taskId !== 'string' || binding.taskId.length === 0
|
|
603
|
-
|| typeof binding.controlGeneration !== 'string' || binding.controlGeneration.length === 0
|
|
604
|
-
|| typeof binding.controllerInstanceDigest !== 'string' || !/^[a-f0-9]{64}$/u.test(binding.controllerInstanceDigest)) {
|
|
605
|
-
clientError('SANDBOX_CONTROL_RESULT_INVALID', 'controller verify result is invalid', false, true);
|
|
606
|
-
}
|
|
607
|
-
return result as unknown as CodexControllerVerified;
|
|
608
|
-
}
|
|
609
|
-
const lease = result.lease as Record<string, unknown> | null;
|
|
610
|
-
if (result.changed !== true || !lease
|
|
611
|
-
|| !exactKeys(lease, [
|
|
612
|
-
'buildIdentity', 'controlGeneration', 'controllerInstanceDigest', 'controllerProcess',
|
|
613
|
-
'expiresAt', 'issuedAt', 'leaseId', 'leaseSecret', 'taskId', 'version'
|
|
614
|
-
])
|
|
615
|
-
|| lease.version !== 1
|
|
616
|
-
|| typeof lease.leaseId !== 'string' || !/^[a-f0-9]{64}$/u.test(lease.leaseId)
|
|
617
|
-
|| typeof lease.leaseSecret !== 'string' || !/^[a-f0-9]{64}$/u.test(lease.leaseSecret)
|
|
618
|
-
|| typeof lease.taskId !== 'string' || lease.taskId.length === 0
|
|
619
|
-
|| typeof lease.controlGeneration !== 'string' || lease.controlGeneration.length === 0
|
|
620
|
-
|| typeof lease.controllerInstanceDigest !== 'string' || !/^[a-f0-9]{64}$/u.test(lease.controllerInstanceDigest)
|
|
621
|
-
|| !validProcess(lease.controllerProcess) || !validBuild(lease.buildIdentity)
|
|
622
|
-
|| !Number.isSafeInteger(lease.issuedAt) || !Number.isSafeInteger(lease.expiresAt)
|
|
623
|
-
|| (lease.expiresAt as number) <= (lease.issuedAt as number)) {
|
|
624
|
-
clientError('SANDBOX_CONTROL_RESULT_INVALID', 'controller open result is invalid', false, true);
|
|
625
|
-
}
|
|
626
|
-
return result as unknown as CodexControllerOpened | CodexControllerClosed;
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
function requestCodexController(params: Readonly<{
|
|
630
|
-
command: 'open' | 'close' | 'verify';
|
|
631
|
-
controllerProcess: ProcessIdentity;
|
|
632
|
-
controllerProof: CodexControllerLeaseProofV1 | null;
|
|
633
|
-
channelDir?: string;
|
|
634
|
-
statusDir?: string;
|
|
635
|
-
token?: string;
|
|
636
|
-
generation?: string;
|
|
637
|
-
timeoutMs?: number;
|
|
638
|
-
}>): CodexControllerOpened | CodexControllerClosed | CodexControllerVerified {
|
|
639
446
|
const auth = authority(params);
|
|
640
447
|
const issuedAt = Date.now();
|
|
641
|
-
const request:
|
|
448
|
+
const request: SandboxAgentClientRequest = {
|
|
642
449
|
version: 4,
|
|
643
450
|
id: randomUUID(),
|
|
644
451
|
...auth,
|
|
645
452
|
issuedAt,
|
|
646
453
|
expiresAt: issuedAt + SANDBOX_CONTROL_ADMISSION_WINDOW_MS,
|
|
647
|
-
family: '
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
controllerProof: params.controllerProof
|
|
454
|
+
family: 'agent-client',
|
|
455
|
+
agentClient: params.agentClient,
|
|
456
|
+
operation: params.operation,
|
|
457
|
+
payload: params.payload
|
|
652
458
|
};
|
|
653
|
-
|
|
654
|
-
if (result.status === 'opened'
|
|
655
|
-
&& (result.lease.controlGeneration !== auth.generation
|
|
656
|
-
|| result.lease.controllerProcess.pid !== params.controllerProcess.pid
|
|
657
|
-
|| result.lease.controllerProcess.startTime !== params.controllerProcess.startTime)) {
|
|
658
|
-
clientError('SANDBOX_CONTROL_RESULT_INVALID', 'controller result does not match the request', false, true);
|
|
659
|
-
}
|
|
660
|
-
return result;
|
|
661
|
-
}
|
|
662
|
-
|
|
663
|
-
export function requestCodexControllerOpen(params: Omit<Parameters<typeof requestCodexController>[0], 'command' | 'controllerProof'>): CodexControllerOpened {
|
|
664
|
-
const result = requestCodexController({ ...params, command: 'open', controllerProof: null });
|
|
665
|
-
if (result.status !== 'opened') clientError('SANDBOX_CONTROL_RESULT_INVALID', 'controller open returned the wrong result', false, true);
|
|
666
|
-
return result;
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
export function requestCodexControllerClose(params: Omit<Parameters<typeof requestCodexController>[0], 'command'>): CodexControllerClosed {
|
|
670
|
-
const result = requestCodexController({ ...params, command: 'close' });
|
|
671
|
-
if (result.status !== 'closed') clientError('SANDBOX_CONTROL_RESULT_INVALID', 'controller close returned the wrong result', false, true);
|
|
672
|
-
return result;
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
export function requestCodexControllerVerify(params: Readonly<{
|
|
676
|
-
controllerProof: CodexControllerLeaseProofV1;
|
|
677
|
-
channelDir?: string;
|
|
678
|
-
statusDir?: string;
|
|
679
|
-
token?: string;
|
|
680
|
-
generation?: string;
|
|
681
|
-
timeoutMs?: number;
|
|
682
|
-
}>): CodexControllerVerified {
|
|
683
|
-
const result = requestCodexController({
|
|
684
|
-
...params,
|
|
685
|
-
controllerProcess: params.controllerProof.controllerProcess,
|
|
686
|
-
command: 'verify'
|
|
687
|
-
});
|
|
688
|
-
if (result.status !== 'verified') {
|
|
689
|
-
clientError('SANDBOX_CONTROL_RESULT_INVALID', 'controller verify returned the wrong result', false, true);
|
|
690
|
-
}
|
|
691
|
-
return result;
|
|
459
|
+
return exchangeSandboxControl(request, params);
|
|
692
460
|
}
|
|
693
461
|
|
|
694
462
|
export function requestSandboxTaskCreate(params: Readonly<{
|
|
@@ -701,8 +469,6 @@ export function requestSandboxTaskCreate(params: Readonly<{
|
|
|
701
469
|
version: 4, id: randomUUID(), ...auth, issuedAt,
|
|
702
470
|
expiresAt: issuedAt + SANDBOX_CONTROL_ADMISSION_WINDOW_MS,
|
|
703
471
|
family: 'task-create', candidate: params.candidate,
|
|
704
|
-
controllerProcess: null,
|
|
705
|
-
controllerProof: null
|
|
706
472
|
};
|
|
707
473
|
return exchangeSandboxControl(request, { ...params, timeoutMs: params.timeoutMs ?? 120_000 });
|
|
708
474
|
}
|