@fitlab-ai/agent-infra 0.9.14 → 0.9.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/README.zh-CN.md +10 -0
- package/bin/internal-cli.sh +45 -0
- package/dist/bin/internal-cli.js +93 -30
- package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +165 -27
- package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +3 -2
- package/dist/lib/defaults.json +103 -103
- package/dist/lib/fs/durable-write.js +37 -0
- package/dist/lib/fs/file-lock.js +111 -0
- package/dist/lib/host-control/audit.js +19 -0
- package/dist/lib/host-control/client.js +142 -0
- package/dist/lib/host-control/command.js +49 -0
- package/dist/lib/host-control/path.js +160 -0
- package/dist/lib/host-control/secure-fs.js +140 -0
- package/dist/lib/host-control/server.js +159 -0
- package/dist/lib/host-control/service.js +112 -0
- package/dist/lib/init.js +11 -2
- package/dist/lib/internal/cli-route-inventory.js +3 -1
- package/dist/lib/internal/codex-lifecycle.js +6 -6
- package/dist/lib/internal/demo-promotion.js +322 -0
- package/dist/lib/internal/demo-transcript.js +454 -0
- package/dist/lib/internal/host-control.js +28 -0
- package/dist/lib/internal/manual-validation.js +322 -0
- package/dist/lib/internal/platform-pr.js +18 -4
- package/dist/lib/internal/release-workflow.js +63 -84
- package/dist/lib/internal/sandbox-control.js +149 -2
- package/dist/lib/internal/task-artifact.js +15 -262
- package/dist/lib/internal/task-create.js +6 -5
- package/dist/lib/internal/task-event.js +37 -37
- package/dist/lib/internal/task-operation-registry.js +101 -3
- package/dist/lib/internal/task-review.js +8 -53
- package/dist/lib/internal/task-validate.js +30 -10
- package/dist/lib/owned-path.js +33 -0
- package/dist/lib/platform/check-status.js +11 -0
- package/dist/lib/platform/comment-safety.js +4 -1
- package/dist/lib/platform/completion-backfill.js +1 -1
- package/dist/lib/platform/github-data.js +241 -0
- package/dist/lib/platform/github-provider.js +15 -20
- package/dist/lib/platform/issue-comments.js +2 -2
- package/dist/lib/platform/issues.js +1 -179
- package/dist/lib/platform/pr-checks.js +12 -65
- package/dist/lib/platform/pr-summary.js +55 -3
- package/dist/lib/platform/pull-requests.js +2 -105
- package/dist/lib/platform/snapshots.js +2 -0
- package/dist/lib/platform/verification-sync.js +127 -142
- package/dist/lib/release/workflow.js +1 -1
- package/dist/lib/sandbox/commands/create.js +7 -2
- package/dist/lib/sandbox/commands/rm.js +3 -1514
- package/dist/lib/sandbox/control/audit.js +203 -0
- package/dist/lib/sandbox/control/client.js +93 -54
- package/dist/lib/sandbox/control/executor.js +129 -65
- package/dist/lib/sandbox/control/finalization-response.js +27 -0
- package/dist/lib/sandbox/control/identity-sentinel.js +107 -0
- package/dist/lib/sandbox/control/lifecycle.js +36 -2
- package/dist/lib/sandbox/control/native-file-lock.js +6 -98
- package/dist/lib/sandbox/control/protocol.js +55 -4
- package/dist/lib/sandbox/control/server.js +491 -120
- package/dist/lib/sandbox/control/state.js +58 -0
- package/dist/lib/sandbox/control/task-workflow.js +74 -0
- package/dist/lib/sandbox/control/workflow-executor.js +99 -0
- package/dist/lib/sandbox/index.js +2 -2
- package/dist/lib/sandbox/recovery.js +28 -4
- package/dist/lib/sandbox/removal.js +1717 -0
- package/dist/lib/sandbox/task-cleanup.js +217 -0
- package/dist/lib/sandbox/workspace-view.js +13 -14
- package/dist/lib/task/activity-intent.js +2 -1
- package/dist/lib/task/activity-log.js +30 -24
- package/dist/lib/task/artifact-command.js +101 -0
- package/dist/lib/task/artifact-lifecycle.js +30 -28
- package/dist/lib/task/artifact-name.js +36 -0
- package/dist/lib/task/artifact-operations.js +23 -69
- package/dist/lib/task/artifact-receipts.js +3 -15
- package/dist/lib/task/artifact-repair-intent.js +87 -16
- package/dist/lib/task/artifact-schema.js +6 -14
- package/dist/lib/task/capabilities.js +12 -19
- package/dist/lib/task/codex-orchestration.js +71 -25
- package/dist/lib/task/commit-identity.js +28 -0
- package/dist/lib/task/commit-operation.js +2 -16
- package/dist/lib/task/control-authority.js +371 -5
- package/dist/lib/task/control-recovery.js +128 -0
- package/dist/lib/task/create.js +62 -9
- package/dist/lib/task/decision-details.js +2 -46
- package/dist/lib/task/events.js +120 -9
- package/dist/lib/task/finalization-state.js +9 -0
- package/dist/lib/task/human-override.js +3 -6
- package/dist/lib/task/implementation-inputs.js +3 -3
- package/dist/lib/task/intermediate-cleanup.js +383 -0
- package/dist/lib/task/invalidation.js +18 -65
- package/dist/lib/task/ledger-intents.js +7 -22
- package/dist/lib/task/ledger.js +1 -3
- package/dist/lib/task/lifecycle.js +19 -1
- package/dist/lib/task/local-artifact-finalization.js +132 -157
- package/dist/lib/task/manual-validation-completion.js +26 -0
- package/dist/lib/task/manual-validation-evidence.js +130 -0
- package/dist/lib/task/manual-validation-receipt.js +101 -0
- package/dist/lib/task/manual-validation-shared.js +40 -0
- package/dist/lib/task/manual-validation-transaction.js +243 -0
- package/dist/lib/task/markdown.js +47 -0
- package/dist/lib/task/orchestration.js +14 -17
- package/dist/lib/task/qualification-audit.js +20 -34
- package/dist/lib/task/review-artifacts.js +2 -18
- package/dist/lib/task/review-command.js +25 -0
- package/dist/lib/task/review-finalization.js +125 -193
- package/dist/lib/task/review-fingerprint.js +2 -2
- package/dist/lib/task/sections.js +34 -56
- package/dist/lib/task/verification-engine.js +116 -323
- package/dist/lib/task/verification.js +1 -1
- package/dist/lib/task/workflow-command.js +17 -0
- package/dist/lib/task/workflow-warning-intents.js +0 -16
- package/dist/lib/task/workflow-warnings.js +28 -2
- package/dist/lib/update.js +2 -0
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +223 -33
- package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +3 -2
- package/lib/fs/durable-write.ts +29 -0
- package/lib/fs/file-lock.ts +113 -0
- package/lib/host-control/audit.ts +22 -0
- package/lib/host-control/client.ts +172 -0
- package/lib/host-control/command.ts +60 -0
- package/lib/host-control/path.ts +167 -0
- package/lib/host-control/secure-fs.ts +135 -0
- package/lib/host-control/server.ts +180 -0
- package/lib/host-control/service.ts +133 -0
- package/lib/init.ts +15 -2
- package/lib/internal/cli-route-inventory.ts +3 -1
- package/lib/internal/codex-lifecycle.ts +6 -6
- package/lib/internal/demo-promotion.ts +354 -0
- package/lib/internal/demo-transcript.ts +419 -0
- package/lib/internal/host-control.ts +28 -0
- package/lib/internal/manual-validation.ts +306 -0
- package/lib/internal/platform-pr.ts +15 -4
- package/lib/internal/release-workflow.ts +74 -82
- package/lib/internal/sandbox-control.ts +162 -1
- package/lib/internal/task-artifact.ts +13 -216
- package/lib/internal/task-create.ts +5 -5
- package/lib/internal/task-event.ts +20 -11
- package/lib/internal/task-operation-registry.ts +111 -3
- package/lib/internal/task-review.ts +6 -51
- package/lib/internal/task-validate.ts +26 -10
- package/lib/owned-path.ts +26 -0
- package/lib/platform/check-status.ts +9 -0
- package/lib/platform/comment-safety.ts +5 -0
- package/lib/platform/completion-backfill.ts +1 -1
- package/lib/platform/github-data.ts +322 -0
- package/lib/platform/github-provider.ts +15 -20
- package/lib/platform/issue-comments.ts +3 -4
- package/lib/platform/issues.ts +4 -245
- package/lib/platform/pr-checks.ts +13 -77
- package/lib/platform/pr-summary.ts +61 -5
- package/lib/platform/pull-requests.ts +3 -170
- package/lib/platform/snapshots.ts +53 -0
- package/lib/platform/verification-sync.ts +145 -155
- package/lib/release/workflow.ts +3 -3
- package/lib/sandbox/commands/create.ts +7 -6
- package/lib/sandbox/commands/rm.ts +3 -1909
- package/lib/sandbox/control/audit.ts +253 -0
- package/lib/sandbox/control/client.ts +109 -66
- package/lib/sandbox/control/executor.ts +134 -74
- package/lib/sandbox/control/finalization-response.ts +29 -0
- package/lib/sandbox/control/identity-sentinel.ts +137 -0
- package/lib/sandbox/control/lifecycle.ts +45 -3
- package/lib/sandbox/control/native-file-lock.ts +6 -107
- package/lib/sandbox/control/protocol.ts +75 -6
- package/lib/sandbox/control/server.ts +538 -123
- package/lib/sandbox/control/state.ts +78 -0
- package/lib/sandbox/control/task-workflow.ts +95 -0
- package/lib/sandbox/control/workflow-executor.ts +102 -0
- package/lib/sandbox/index.ts +2 -2
- package/lib/sandbox/recovery.ts +28 -5
- package/lib/sandbox/removal.ts +2144 -0
- package/lib/sandbox/task-cleanup.ts +209 -0
- package/lib/sandbox/workspace-view.ts +17 -15
- package/lib/task/activity-intent.ts +2 -2
- package/lib/task/activity-log.ts +27 -19
- package/lib/task/artifact-command.ts +104 -0
- package/lib/task/artifact-lifecycle.ts +31 -26
- package/lib/task/artifact-name.ts +55 -0
- package/lib/task/artifact-operations.ts +22 -68
- package/lib/task/artifact-receipts.ts +3 -14
- package/lib/task/artifact-repair-intent.ts +99 -18
- package/lib/task/artifact-schema.ts +5 -34
- package/lib/task/capabilities.ts +13 -22
- package/lib/task/codex-orchestration.ts +85 -24
- package/lib/task/commit-identity.ts +31 -0
- package/lib/task/commit-operation.ts +2 -16
- package/lib/task/control-authority.ts +502 -5
- package/lib/task/control-recovery.ts +174 -0
- package/lib/task/create.ts +63 -8
- package/lib/task/decision-details.ts +3 -71
- package/lib/task/events.ts +131 -10
- package/lib/task/finalization-state.ts +11 -0
- package/lib/task/human-override.ts +3 -8
- package/lib/task/implementation-inputs.ts +3 -3
- package/lib/task/intermediate-cleanup.ts +455 -0
- package/lib/task/invalidation.ts +18 -62
- package/lib/task/ledger-intents.ts +7 -24
- package/lib/task/ledger.ts +1 -3
- package/lib/task/lifecycle.ts +17 -1
- package/lib/task/local-artifact-finalization.ts +148 -182
- package/lib/task/manual-validation-completion.ts +40 -0
- package/lib/task/manual-validation-evidence.ts +209 -0
- package/lib/task/manual-validation-receipt.ts +152 -0
- package/lib/task/manual-validation-shared.ts +50 -0
- package/lib/task/manual-validation-transaction.ts +304 -0
- package/lib/task/markdown.ts +70 -0
- package/lib/task/orchestration.ts +19 -19
- package/lib/task/qualification-audit.ts +22 -33
- package/lib/task/review-artifacts.ts +1 -20
- package/lib/task/review-command.ts +23 -0
- package/lib/task/review-finalization.ts +131 -259
- package/lib/task/review-fingerprint.ts +2 -2
- package/lib/task/sections.ts +33 -54
- package/lib/task/verification-engine.ts +110 -366
- package/lib/task/verification-types.ts +17 -0
- package/lib/task/verification.ts +1 -1
- package/lib/task/workflow-command.ts +18 -0
- package/lib/task/workflow-warning-intents.ts +0 -13
- package/lib/task/workflow-warnings.ts +21 -2
- package/lib/update.ts +6 -0
- package/package.json +6 -6
- package/templates/.agents/hooks/lifecycle-delegation.js +6 -6
- package/templates/.agents/rules/testing-discipline.en.md +2 -0
- package/templates/.agents/rules/testing-discipline.zh-CN.md +2 -0
- package/templates/.agents/scripts/package.json +3 -0
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +32 -20
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +32 -20
- package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +1 -1
- package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/complete-manual-validation/reference/report-template.en.md +2 -0
- package/templates/.agents/skills/complete-manual-validation/reference/report-template.zh-CN.md +2 -0
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +16 -7
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +16 -7
- package/templates/.agents/skills/complete-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/release/SKILL.en.md +7 -3
- package/templates/.agents/skills/release/SKILL.zh-CN.md +7 -3
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +20 -3
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +20 -3
- package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.en.md +4 -1
- package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.zh-CN.md +4 -1
- package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +2 -0
- package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +2 -0
- package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/test/SKILL.en.md +17 -6
- package/templates/.agents/skills/test/SKILL.zh-CN.md +17 -6
- package/templates/.claude/commands/run-manual-validation.en.md +1 -1
- package/templates/.claude/commands/run-manual-validation.zh-CN.md +1 -1
- package/dist/lib/platform/adapters.js +0 -56
- package/lib/platform/adapters.ts +0 -197
|
@@ -1,26 +1,10 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
|
-
import { createRequire } from 'node:module';
|
|
3
2
|
import fs from 'node:fs';
|
|
4
3
|
import os from 'node:os';
|
|
5
4
|
import path from 'node:path';
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
lockFileExSync: (fd: number, flags: number, offsetLow: number, offsetHigh: number, lengthLow: number, lengthHigh: number) => void;
|
|
10
|
-
unlockFileExSync: (fd: number, offsetLow: number, offsetHigh: number, lengthLow: number, lengthHigh: number) => void;
|
|
11
|
-
constants: Readonly<{
|
|
12
|
-
LOCKFILE_EXCLUSIVE_LOCK: number;
|
|
13
|
-
LOCKFILE_FAIL_IMMEDIATELY: number;
|
|
14
|
-
}>;
|
|
15
|
-
getNativeModuleSource?: () => string;
|
|
16
|
-
}>;
|
|
17
|
-
|
|
18
|
-
export type SandboxLockCapability = Readonly<{
|
|
19
|
-
supported: boolean;
|
|
20
|
-
primitive: 'flock' | 'LockFileEx' | 'unavailable';
|
|
21
|
-
binarySource: string | null;
|
|
22
|
-
reason?: string;
|
|
23
|
-
}>;
|
|
6
|
+
import { acquireFileLock, NATIVE_LOCK_PRIMITIVE, probeNativeLockCapability } from '../../fs/file-lock.ts';
|
|
7
|
+
export { probeNativeLockCapability };
|
|
24
8
|
|
|
25
9
|
export type SandboxLockNamespace = Readonly<{
|
|
26
10
|
lockRoot: string;
|
|
@@ -36,17 +20,8 @@ export type SandboxResourceLock = Readonly<{
|
|
|
36
20
|
release(): void;
|
|
37
21
|
}>;
|
|
38
22
|
|
|
39
|
-
const require = createRequire(import.meta.url);
|
|
40
23
|
const LOCK_ROOT_NAME = path.join('.agent-infra', 'sandbox-locks');
|
|
41
24
|
|
|
42
|
-
function loadNative(): NativeFsExt {
|
|
43
|
-
try {
|
|
44
|
-
return require('fs-ext-extra-prebuilt') as NativeFsExt;
|
|
45
|
-
} catch {
|
|
46
|
-
throw new Error('SANDBOX_LOCK_UNSUPPORTED: native lock module is unavailable');
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
25
|
function digest(value: string): string {
|
|
51
26
|
return createHash('sha256').update(value).digest('hex');
|
|
52
27
|
}
|
|
@@ -97,8 +72,7 @@ export function resolveSandboxLockNamespace(
|
|
|
97
72
|
const home = path.resolve(options.home ?? os.homedir());
|
|
98
73
|
const lockRoot = path.join(home, LOCK_ROOT_NAME);
|
|
99
74
|
assertPrivateDirectory(lockRoot, home);
|
|
100
|
-
const
|
|
101
|
-
const lockDomain = options.lockDomain ?? digest(`${process.platform}\0${process.arch}\0${primitive}`);
|
|
75
|
+
const lockDomain = options.lockDomain ?? digest(`${process.platform}\0${process.arch}\0${NATIVE_LOCK_PRIMITIVE}`);
|
|
102
76
|
if (!/^[a-f0-9]{64}$/u.test(lockDomain)) throw new Error('SANDBOX_LOCK_DOMAIN_INVALID');
|
|
103
77
|
const domainDirectory = path.join(lockRoot, lockDomain);
|
|
104
78
|
assertPrivateDirectory(domainDirectory, home);
|
|
@@ -120,90 +94,15 @@ export function stableSandboxLockPath(
|
|
|
120
94
|
return resolveSandboxLockNamespace(carrierIdentity, { lockDomain, home: options.home }).lockPath;
|
|
121
95
|
}
|
|
122
96
|
|
|
123
|
-
export function probeNativeLockCapability(): SandboxLockCapability {
|
|
124
|
-
try {
|
|
125
|
-
const native = loadNative();
|
|
126
|
-
return {
|
|
127
|
-
supported: true,
|
|
128
|
-
primitive: process.platform === 'win32' ? 'LockFileEx' : 'flock',
|
|
129
|
-
binarySource: native.getNativeModuleSource?.() ?? null
|
|
130
|
-
};
|
|
131
|
-
} catch (error) {
|
|
132
|
-
return {
|
|
133
|
-
supported: false,
|
|
134
|
-
primitive: 'unavailable',
|
|
135
|
-
binarySource: null,
|
|
136
|
-
reason: error instanceof Error ? error.message : String(error)
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
97
|
export function acquireSandboxResourceLock(
|
|
142
98
|
carrierIdentity: string,
|
|
143
99
|
options: Readonly<{ lockDomain?: string; home?: string }> = {}
|
|
144
100
|
): SandboxResourceLock {
|
|
145
|
-
const native = loadNative();
|
|
146
101
|
const namespace = resolveSandboxLockNamespace(carrierIdentity, options);
|
|
147
|
-
if (fs.existsSync(namespace.lockPath)) {
|
|
148
|
-
const existing = fs.lstatSync(namespace.lockPath);
|
|
149
|
-
if (existing.isSymbolicLink() || !existing.isFile()) {
|
|
150
|
-
throw new Error('SANDBOX_LOCK_UNSUPPORTED: lock object is not a regular file');
|
|
151
|
-
}
|
|
152
|
-
if (process.platform !== 'win32' && typeof process.getuid === 'function'
|
|
153
|
-
&& existing.uid !== process.getuid()) {
|
|
154
|
-
throw new Error('SANDBOX_LOCK_UNSUPPORTED: lock object owner mismatch');
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
let fd: number | undefined;
|
|
158
102
|
try {
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
fd = fs.openSync(namespace.lockPath, fs.constants.O_CREAT | fs.constants.O_RDWR | noFollow, 0o600);
|
|
162
|
-
if (process.platform !== 'win32') fs.chmodSync(namespace.lockPath, 0o600);
|
|
163
|
-
if (process.platform === 'win32') {
|
|
164
|
-
native.lockFileExSync(
|
|
165
|
-
fd,
|
|
166
|
-
native.constants.LOCKFILE_EXCLUSIVE_LOCK | native.constants.LOCKFILE_FAIL_IMMEDIATELY,
|
|
167
|
-
0, 0, 1, 0
|
|
168
|
-
);
|
|
169
|
-
} else {
|
|
170
|
-
native.flockSync(fd, 'exnb');
|
|
171
|
-
}
|
|
103
|
+
const lock = acquireFileLock(namespace.lockPath);
|
|
104
|
+
return { path: namespace.lockPath, lockDomain: namespace.lockDomain, release: lock.release };
|
|
172
105
|
} catch (error) {
|
|
173
|
-
|
|
174
|
-
if (fd !== undefined) fs.closeSync(fd);
|
|
175
|
-
} catch {
|
|
176
|
-
// Preserve the lock acquisition error.
|
|
177
|
-
}
|
|
178
|
-
const code = error && typeof error === 'object' && 'code' in error ? String((error as { code?: unknown }).code) : '';
|
|
179
|
-
if (['EAGAIN', 'EACCES', 'EWOULDBLOCK'].includes(code)) {
|
|
180
|
-
throw new Error('SANDBOX_LOCK_BUSY');
|
|
181
|
-
}
|
|
182
|
-
throw new Error(`SANDBOX_LOCK_UNSUPPORTED: ${error instanceof Error ? error.message : String(error)}`);
|
|
106
|
+
throw new Error(String(error instanceof Error ? error.message : error).replaceAll('FILE_LOCK_', 'SANDBOX_LOCK_'));
|
|
183
107
|
}
|
|
184
|
-
|
|
185
|
-
let released = false;
|
|
186
|
-
return {
|
|
187
|
-
path: namespace.lockPath,
|
|
188
|
-
lockDomain: namespace.lockDomain,
|
|
189
|
-
release() {
|
|
190
|
-
if (released) return;
|
|
191
|
-
released = true;
|
|
192
|
-
let failure: unknown = null;
|
|
193
|
-
try {
|
|
194
|
-
if (fd === undefined) throw new Error('descriptor is missing');
|
|
195
|
-
if (process.platform === 'win32') native.unlockFileExSync(fd, 0, 0, 1, 0);
|
|
196
|
-
else native.flockSync(fd, 'un');
|
|
197
|
-
} catch (error) {
|
|
198
|
-
failure = error;
|
|
199
|
-
} finally {
|
|
200
|
-
try {
|
|
201
|
-
if (fd !== undefined) fs.closeSync(fd);
|
|
202
|
-
} catch (error) {
|
|
203
|
-
failure ??= error;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
if (failure) throw new Error(`SANDBOX_LOCK_RELEASE_FAILED: ${failure instanceof Error ? failure.message : String(failure)}`);
|
|
207
|
-
}
|
|
208
|
-
};
|
|
209
108
|
}
|
|
@@ -5,6 +5,12 @@ import type { ProcessIdentity } from '../../server/process-state.ts';
|
|
|
5
5
|
import type { CodexControllerLeaseProofV1 } from './controller-registration.ts';
|
|
6
6
|
import type { SandboxAuthorityEvidenceV1 } from '../engines/authority.ts';
|
|
7
7
|
import type { SandboxTaskView } from './task-view.ts';
|
|
8
|
+
import { validateTaskWorkflowRequest, type TaskWorkflowRequest } from './task-workflow.ts';
|
|
9
|
+
import {
|
|
10
|
+
validateLifecycleAuthorityRequest,
|
|
11
|
+
type LifecycleAuthorityRequestV1,
|
|
12
|
+
type LifecycleRecoveryAttestationV1
|
|
13
|
+
} from '../../task/control-authority.ts';
|
|
8
14
|
|
|
9
15
|
export const SANDBOX_CONTROL_MAX_BYTES = 64 * 1024;
|
|
10
16
|
export const SANDBOX_CONTROL_MAX_LOGICAL_RECORDS = 1024;
|
|
@@ -15,7 +21,7 @@ export const SANDBOX_CONTROL_ADMISSION_WINDOW_MS = 2_000;
|
|
|
15
21
|
export const SANDBOX_CONTROL_STATUS_INTERVAL_MS = 250;
|
|
16
22
|
export const SANDBOX_CONTROL_STATUS_STALE_MS = 1_500;
|
|
17
23
|
export const SANDBOX_CONTROL_FUTURE_SKEW_MS = 1_000;
|
|
18
|
-
export const SANDBOX_CONTROL_FAMILIES = ['task-lifecycle', 'task-orchestration', 'task-finalization', 'task-create', 'codex-controller'] as const;
|
|
24
|
+
export const SANDBOX_CONTROL_FAMILIES = ['task-lifecycle', 'task-orchestration', 'task-finalization', 'task-create', 'codex-controller', 'task-workflow'] as const;
|
|
19
25
|
export type SandboxControlTimingPolicy = Readonly<{
|
|
20
26
|
controlTickMs: number;
|
|
21
27
|
parkedBindingInitialMs: number;
|
|
@@ -41,7 +47,7 @@ export type SandboxControlManifestBase = Readonly<{
|
|
|
41
47
|
containerIdentity: SandboxControlContainerIdentity;
|
|
42
48
|
authorityEvidence: SandboxAuthorityEvidenceV1;
|
|
43
49
|
branch: string; mode: 'task-bound' | 'branch-only'; taskId: string | null; token: string;
|
|
44
|
-
generation: string; channelDir: string; publicStatusDir: string; processingDir: string;
|
|
50
|
+
generation: string; controlRootId: string; channelDir: string; publicStatusDir: string; processingDir: string;
|
|
45
51
|
}>;
|
|
46
52
|
export type SandboxControlManifest = SandboxControlManifestBase & Readonly<{ runtimeDir: string }>;
|
|
47
53
|
export type SandboxControlBrokerOwner = ProcessIdentity & Readonly<{
|
|
@@ -50,6 +56,15 @@ export type SandboxControlBrokerOwner = ProcessIdentity & Readonly<{
|
|
|
50
56
|
token: string;
|
|
51
57
|
generation: string;
|
|
52
58
|
}>;
|
|
59
|
+
export type SandboxControlExecutorGateV2 = Readonly<{
|
|
60
|
+
version: 2;
|
|
61
|
+
nonce: string;
|
|
62
|
+
owner: SandboxControlBrokerOwner;
|
|
63
|
+
requestId: string;
|
|
64
|
+
operationId: string;
|
|
65
|
+
phase: 'orchestration.prepare' | 'artifact.finalize-local' | 'task-event.completed';
|
|
66
|
+
authority: LifecycleRecoveryAttestationV1 | null;
|
|
67
|
+
}>;
|
|
53
68
|
type RequestBase = Readonly<{
|
|
54
69
|
version: 3; id: string; token: string; generation: string; issuedAt: number; expiresAt: number;
|
|
55
70
|
controllerProcess: ProcessIdentity | null;
|
|
@@ -57,6 +72,7 @@ type RequestBase = Readonly<{
|
|
|
57
72
|
}>;
|
|
58
73
|
export type SandboxTaskCommandRequest = RequestBase & Readonly<{
|
|
59
74
|
family: 'task-lifecycle' | 'task-orchestration'; args: string[];
|
|
75
|
+
authority?: LifecycleAuthorityRequestV1;
|
|
60
76
|
}>;
|
|
61
77
|
export type SandboxTaskFinalizationRequest = RequestBase & Readonly<{
|
|
62
78
|
family: 'task-finalization'; operation: 'complete'; agent: string; args: [];
|
|
@@ -69,7 +85,13 @@ export type SandboxCodexControllerRequest = RequestBase & Readonly<{
|
|
|
69
85
|
command: 'open' | 'close' | 'verify';
|
|
70
86
|
args: [];
|
|
71
87
|
}>;
|
|
72
|
-
export type
|
|
88
|
+
export type SandboxTaskWorkflowRequest = RequestBase & Readonly<{
|
|
89
|
+
family: 'task-workflow';
|
|
90
|
+
args: [];
|
|
91
|
+
workflow: TaskWorkflowRequest;
|
|
92
|
+
authority?: LifecycleAuthorityRequestV1;
|
|
93
|
+
}>;
|
|
94
|
+
export type SandboxControlRequest = SandboxTaskCommandRequest | SandboxTaskFinalizationRequest | SandboxTaskCreateRequest | SandboxCodexControllerRequest | SandboxTaskWorkflowRequest;
|
|
73
95
|
export type SandboxControlError = Readonly<{ code: string; message: string; retryable: boolean }>;
|
|
74
96
|
export type SandboxControlResultEvidence = Readonly<{
|
|
75
97
|
version: 1;
|
|
@@ -286,8 +308,25 @@ export function validateSandboxControlRequest(
|
|
|
286
308
|
}
|
|
287
309
|
return request as SandboxTaskFinalizationRequest;
|
|
288
310
|
}
|
|
289
|
-
|
|
290
|
-
|
|
311
|
+
if (request.family === 'task-workflow') {
|
|
312
|
+
const expected = ['args', 'authority', 'controllerProcess', 'controllerProof', 'expiresAt', 'family', 'generation', 'id', 'issuedAt', 'token', 'version', 'workflow'];
|
|
313
|
+
const baseExpected = expected.filter((key) => key !== 'authority');
|
|
314
|
+
if (![baseExpected, expected].some((keys) => Object.keys(request).sort().join(',') === keys.sort().join(','))
|
|
315
|
+
|| !Array.isArray(request.args) || request.args.length !== 0
|
|
316
|
+
|| request.controllerProcess !== null || request.controllerProof !== null) {
|
|
317
|
+
fail('SANDBOX_CONTROL_REQUEST_INVALID', 'task-workflow request schema is invalid');
|
|
318
|
+
}
|
|
319
|
+
if (manifest.mode !== 'task-bound' || !manifest.taskId) fail('SANDBOX_CONTROL_BRANCH_ONLY', 'branch-only sandboxes cannot use task-workflow');
|
|
320
|
+
const workflow = validateTaskWorkflowRequest(request.workflow);
|
|
321
|
+
if (workflow.taskId !== manifest.taskId || workflow.generation !== manifest.generation || workflow.id !== request.id) {
|
|
322
|
+
fail('SANDBOX_CONTROL_REQUEST_INVALID', 'task-workflow binding does not match the sandbox manifest');
|
|
323
|
+
}
|
|
324
|
+
if (request.authority !== undefined) validateAuthoritySelector(request.authority, request, manifest);
|
|
325
|
+
return { ...request, workflow } as SandboxTaskWorkflowRequest;
|
|
326
|
+
}
|
|
327
|
+
const expected = ['args', 'authority', 'controllerProcess', 'controllerProof', 'expiresAt', 'family', 'generation', 'id', 'issuedAt', 'token', 'version'];
|
|
328
|
+
const baseExpected = expected.filter((key) => key !== 'authority');
|
|
329
|
+
if (![baseExpected, expected].some((keys) => Object.keys(request).sort().join(',') === keys.sort().join(','))
|
|
291
330
|
|| !Array.isArray(request.args) || !request.args.every((arg) => typeof arg === 'string')) {
|
|
292
331
|
fail('SANDBOX_CONTROL_REQUEST_INVALID', 'request schema or authorization is invalid');
|
|
293
332
|
}
|
|
@@ -308,11 +347,41 @@ export function validateSandboxControlRequest(
|
|
|
308
347
|
if (request.family !== 'task-orchestration' && request.controllerProof !== null) {
|
|
309
348
|
fail('SANDBOX_CONTROL_REQUEST_INVALID', 'controller proof is not allowed for this family');
|
|
310
349
|
}
|
|
350
|
+
if (request.authority !== undefined) validateAuthoritySelector(request.authority, request, manifest);
|
|
311
351
|
return request as SandboxTaskCommandRequest;
|
|
312
352
|
}
|
|
313
353
|
|
|
354
|
+
function validateAuthoritySelector(
|
|
355
|
+
value: unknown,
|
|
356
|
+
request: Record<string, unknown>,
|
|
357
|
+
manifest: SandboxControlManifest
|
|
358
|
+
): asserts value is LifecycleAuthorityRequestV1 {
|
|
359
|
+
let authority: LifecycleAuthorityRequestV1;
|
|
360
|
+
try { authority = validateLifecycleAuthorityRequest(value); }
|
|
361
|
+
catch { fail('SANDBOX_CONTROL_AUTHORITY_INVALID', 'lifecycle authority selector is invalid'); }
|
|
362
|
+
if (authority.requestId !== request.id
|
|
363
|
+
|| authority.taskId !== manifest.taskId
|
|
364
|
+
|| authority.expectedControlGeneration !== manifest.generation) {
|
|
365
|
+
fail('SANDBOX_CONTROL_AUTHORITY_INVALID', 'lifecycle authority selector is not bound to this request');
|
|
366
|
+
}
|
|
367
|
+
const args = Array.isArray(request.args) ? request.args : [];
|
|
368
|
+
if (request.family === 'task-orchestration'
|
|
369
|
+
&& (args[1] !== 'prepare' || authority.phase !== 'orchestration.prepare')) {
|
|
370
|
+
fail('SANDBOX_CONTROL_AUTHORITY_INVALID', 'orchestration authority is only valid for prepare');
|
|
371
|
+
}
|
|
372
|
+
const workflow = request.workflow as TaskWorkflowRequest | undefined;
|
|
373
|
+
if (request.family === 'task-workflow') {
|
|
374
|
+
const expectedPhase = workflow?.operation === 'artifact-finalize-local'
|
|
375
|
+
? 'artifact.finalize-local'
|
|
376
|
+
: workflow?.operation === 'event' ? 'task-event.completed' : null;
|
|
377
|
+
if (authority.phase !== expectedPhase) {
|
|
378
|
+
fail('SANDBOX_CONTROL_AUTHORITY_INVALID', 'workflow authority phase does not match the workflow operation');
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
314
383
|
export function bindSandboxControlTask(request: SandboxControlRequest, taskId: string): string[] {
|
|
315
|
-
if (request.family === 'task-create' || request.family === 'codex-controller' || request.family === 'task-finalization') {
|
|
384
|
+
if (request.family === 'task-create' || request.family === 'codex-controller' || request.family === 'task-finalization' || request.family === 'task-workflow') {
|
|
316
385
|
fail('SANDBOX_CONTROL_REQUEST_INVALID', `${request.family} requests do not bind a current task`);
|
|
317
386
|
}
|
|
318
387
|
if (request.args.length === 0) fail('SANDBOX_CONTROL_REQUEST_INVALID', 'command arguments are required');
|