@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.
Files changed (250) hide show
  1. package/README.md +10 -0
  2. package/README.zh-CN.md +10 -0
  3. package/bin/internal-cli.sh +45 -0
  4. package/dist/bin/internal-cli.js +93 -30
  5. package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +165 -27
  6. package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +3 -2
  7. package/dist/lib/defaults.json +103 -103
  8. package/dist/lib/fs/durable-write.js +37 -0
  9. package/dist/lib/fs/file-lock.js +111 -0
  10. package/dist/lib/host-control/audit.js +19 -0
  11. package/dist/lib/host-control/client.js +142 -0
  12. package/dist/lib/host-control/command.js +49 -0
  13. package/dist/lib/host-control/path.js +160 -0
  14. package/dist/lib/host-control/secure-fs.js +140 -0
  15. package/dist/lib/host-control/server.js +159 -0
  16. package/dist/lib/host-control/service.js +112 -0
  17. package/dist/lib/init.js +11 -2
  18. package/dist/lib/internal/cli-route-inventory.js +3 -1
  19. package/dist/lib/internal/codex-lifecycle.js +6 -6
  20. package/dist/lib/internal/demo-promotion.js +322 -0
  21. package/dist/lib/internal/demo-transcript.js +454 -0
  22. package/dist/lib/internal/host-control.js +28 -0
  23. package/dist/lib/internal/manual-validation.js +322 -0
  24. package/dist/lib/internal/platform-pr.js +18 -4
  25. package/dist/lib/internal/release-workflow.js +63 -84
  26. package/dist/lib/internal/sandbox-control.js +149 -2
  27. package/dist/lib/internal/task-artifact.js +15 -262
  28. package/dist/lib/internal/task-create.js +6 -5
  29. package/dist/lib/internal/task-event.js +37 -37
  30. package/dist/lib/internal/task-operation-registry.js +101 -3
  31. package/dist/lib/internal/task-review.js +8 -53
  32. package/dist/lib/internal/task-validate.js +30 -10
  33. package/dist/lib/owned-path.js +33 -0
  34. package/dist/lib/platform/check-status.js +11 -0
  35. package/dist/lib/platform/comment-safety.js +4 -1
  36. package/dist/lib/platform/completion-backfill.js +1 -1
  37. package/dist/lib/platform/github-data.js +241 -0
  38. package/dist/lib/platform/github-provider.js +15 -20
  39. package/dist/lib/platform/issue-comments.js +2 -2
  40. package/dist/lib/platform/issues.js +1 -179
  41. package/dist/lib/platform/pr-checks.js +12 -65
  42. package/dist/lib/platform/pr-summary.js +55 -3
  43. package/dist/lib/platform/pull-requests.js +2 -105
  44. package/dist/lib/platform/snapshots.js +2 -0
  45. package/dist/lib/platform/verification-sync.js +127 -142
  46. package/dist/lib/release/workflow.js +1 -1
  47. package/dist/lib/sandbox/commands/create.js +7 -2
  48. package/dist/lib/sandbox/commands/rm.js +3 -1514
  49. package/dist/lib/sandbox/control/audit.js +203 -0
  50. package/dist/lib/sandbox/control/client.js +93 -54
  51. package/dist/lib/sandbox/control/executor.js +129 -65
  52. package/dist/lib/sandbox/control/finalization-response.js +27 -0
  53. package/dist/lib/sandbox/control/identity-sentinel.js +107 -0
  54. package/dist/lib/sandbox/control/lifecycle.js +36 -2
  55. package/dist/lib/sandbox/control/native-file-lock.js +6 -98
  56. package/dist/lib/sandbox/control/protocol.js +55 -4
  57. package/dist/lib/sandbox/control/server.js +491 -120
  58. package/dist/lib/sandbox/control/state.js +58 -0
  59. package/dist/lib/sandbox/control/task-workflow.js +74 -0
  60. package/dist/lib/sandbox/control/workflow-executor.js +99 -0
  61. package/dist/lib/sandbox/index.js +2 -2
  62. package/dist/lib/sandbox/recovery.js +28 -4
  63. package/dist/lib/sandbox/removal.js +1717 -0
  64. package/dist/lib/sandbox/task-cleanup.js +217 -0
  65. package/dist/lib/sandbox/workspace-view.js +13 -14
  66. package/dist/lib/task/activity-intent.js +2 -1
  67. package/dist/lib/task/activity-log.js +30 -24
  68. package/dist/lib/task/artifact-command.js +101 -0
  69. package/dist/lib/task/artifact-lifecycle.js +30 -28
  70. package/dist/lib/task/artifact-name.js +36 -0
  71. package/dist/lib/task/artifact-operations.js +23 -69
  72. package/dist/lib/task/artifact-receipts.js +3 -15
  73. package/dist/lib/task/artifact-repair-intent.js +87 -16
  74. package/dist/lib/task/artifact-schema.js +6 -14
  75. package/dist/lib/task/capabilities.js +12 -19
  76. package/dist/lib/task/codex-orchestration.js +71 -25
  77. package/dist/lib/task/commit-identity.js +28 -0
  78. package/dist/lib/task/commit-operation.js +2 -16
  79. package/dist/lib/task/control-authority.js +371 -5
  80. package/dist/lib/task/control-recovery.js +128 -0
  81. package/dist/lib/task/create.js +62 -9
  82. package/dist/lib/task/decision-details.js +2 -46
  83. package/dist/lib/task/events.js +120 -9
  84. package/dist/lib/task/finalization-state.js +9 -0
  85. package/dist/lib/task/human-override.js +3 -6
  86. package/dist/lib/task/implementation-inputs.js +3 -3
  87. package/dist/lib/task/intermediate-cleanup.js +383 -0
  88. package/dist/lib/task/invalidation.js +18 -65
  89. package/dist/lib/task/ledger-intents.js +7 -22
  90. package/dist/lib/task/ledger.js +1 -3
  91. package/dist/lib/task/lifecycle.js +19 -1
  92. package/dist/lib/task/local-artifact-finalization.js +132 -157
  93. package/dist/lib/task/manual-validation-completion.js +26 -0
  94. package/dist/lib/task/manual-validation-evidence.js +130 -0
  95. package/dist/lib/task/manual-validation-receipt.js +101 -0
  96. package/dist/lib/task/manual-validation-shared.js +40 -0
  97. package/dist/lib/task/manual-validation-transaction.js +243 -0
  98. package/dist/lib/task/markdown.js +47 -0
  99. package/dist/lib/task/orchestration.js +14 -17
  100. package/dist/lib/task/qualification-audit.js +20 -34
  101. package/dist/lib/task/review-artifacts.js +2 -18
  102. package/dist/lib/task/review-command.js +25 -0
  103. package/dist/lib/task/review-finalization.js +125 -193
  104. package/dist/lib/task/review-fingerprint.js +2 -2
  105. package/dist/lib/task/sections.js +34 -56
  106. package/dist/lib/task/verification-engine.js +116 -323
  107. package/dist/lib/task/verification.js +1 -1
  108. package/dist/lib/task/workflow-command.js +17 -0
  109. package/dist/lib/task/workflow-warning-intents.js +0 -16
  110. package/dist/lib/task/workflow-warnings.js +28 -2
  111. package/dist/lib/update.js +2 -0
  112. package/dist/lifecycle-build-manifest.json +3 -3
  113. package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +223 -33
  114. package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +3 -2
  115. package/lib/fs/durable-write.ts +29 -0
  116. package/lib/fs/file-lock.ts +113 -0
  117. package/lib/host-control/audit.ts +22 -0
  118. package/lib/host-control/client.ts +172 -0
  119. package/lib/host-control/command.ts +60 -0
  120. package/lib/host-control/path.ts +167 -0
  121. package/lib/host-control/secure-fs.ts +135 -0
  122. package/lib/host-control/server.ts +180 -0
  123. package/lib/host-control/service.ts +133 -0
  124. package/lib/init.ts +15 -2
  125. package/lib/internal/cli-route-inventory.ts +3 -1
  126. package/lib/internal/codex-lifecycle.ts +6 -6
  127. package/lib/internal/demo-promotion.ts +354 -0
  128. package/lib/internal/demo-transcript.ts +419 -0
  129. package/lib/internal/host-control.ts +28 -0
  130. package/lib/internal/manual-validation.ts +306 -0
  131. package/lib/internal/platform-pr.ts +15 -4
  132. package/lib/internal/release-workflow.ts +74 -82
  133. package/lib/internal/sandbox-control.ts +162 -1
  134. package/lib/internal/task-artifact.ts +13 -216
  135. package/lib/internal/task-create.ts +5 -5
  136. package/lib/internal/task-event.ts +20 -11
  137. package/lib/internal/task-operation-registry.ts +111 -3
  138. package/lib/internal/task-review.ts +6 -51
  139. package/lib/internal/task-validate.ts +26 -10
  140. package/lib/owned-path.ts +26 -0
  141. package/lib/platform/check-status.ts +9 -0
  142. package/lib/platform/comment-safety.ts +5 -0
  143. package/lib/platform/completion-backfill.ts +1 -1
  144. package/lib/platform/github-data.ts +322 -0
  145. package/lib/platform/github-provider.ts +15 -20
  146. package/lib/platform/issue-comments.ts +3 -4
  147. package/lib/platform/issues.ts +4 -245
  148. package/lib/platform/pr-checks.ts +13 -77
  149. package/lib/platform/pr-summary.ts +61 -5
  150. package/lib/platform/pull-requests.ts +3 -170
  151. package/lib/platform/snapshots.ts +53 -0
  152. package/lib/platform/verification-sync.ts +145 -155
  153. package/lib/release/workflow.ts +3 -3
  154. package/lib/sandbox/commands/create.ts +7 -6
  155. package/lib/sandbox/commands/rm.ts +3 -1909
  156. package/lib/sandbox/control/audit.ts +253 -0
  157. package/lib/sandbox/control/client.ts +109 -66
  158. package/lib/sandbox/control/executor.ts +134 -74
  159. package/lib/sandbox/control/finalization-response.ts +29 -0
  160. package/lib/sandbox/control/identity-sentinel.ts +137 -0
  161. package/lib/sandbox/control/lifecycle.ts +45 -3
  162. package/lib/sandbox/control/native-file-lock.ts +6 -107
  163. package/lib/sandbox/control/protocol.ts +75 -6
  164. package/lib/sandbox/control/server.ts +538 -123
  165. package/lib/sandbox/control/state.ts +78 -0
  166. package/lib/sandbox/control/task-workflow.ts +95 -0
  167. package/lib/sandbox/control/workflow-executor.ts +102 -0
  168. package/lib/sandbox/index.ts +2 -2
  169. package/lib/sandbox/recovery.ts +28 -5
  170. package/lib/sandbox/removal.ts +2144 -0
  171. package/lib/sandbox/task-cleanup.ts +209 -0
  172. package/lib/sandbox/workspace-view.ts +17 -15
  173. package/lib/task/activity-intent.ts +2 -2
  174. package/lib/task/activity-log.ts +27 -19
  175. package/lib/task/artifact-command.ts +104 -0
  176. package/lib/task/artifact-lifecycle.ts +31 -26
  177. package/lib/task/artifact-name.ts +55 -0
  178. package/lib/task/artifact-operations.ts +22 -68
  179. package/lib/task/artifact-receipts.ts +3 -14
  180. package/lib/task/artifact-repair-intent.ts +99 -18
  181. package/lib/task/artifact-schema.ts +5 -34
  182. package/lib/task/capabilities.ts +13 -22
  183. package/lib/task/codex-orchestration.ts +85 -24
  184. package/lib/task/commit-identity.ts +31 -0
  185. package/lib/task/commit-operation.ts +2 -16
  186. package/lib/task/control-authority.ts +502 -5
  187. package/lib/task/control-recovery.ts +174 -0
  188. package/lib/task/create.ts +63 -8
  189. package/lib/task/decision-details.ts +3 -71
  190. package/lib/task/events.ts +131 -10
  191. package/lib/task/finalization-state.ts +11 -0
  192. package/lib/task/human-override.ts +3 -8
  193. package/lib/task/implementation-inputs.ts +3 -3
  194. package/lib/task/intermediate-cleanup.ts +455 -0
  195. package/lib/task/invalidation.ts +18 -62
  196. package/lib/task/ledger-intents.ts +7 -24
  197. package/lib/task/ledger.ts +1 -3
  198. package/lib/task/lifecycle.ts +17 -1
  199. package/lib/task/local-artifact-finalization.ts +148 -182
  200. package/lib/task/manual-validation-completion.ts +40 -0
  201. package/lib/task/manual-validation-evidence.ts +209 -0
  202. package/lib/task/manual-validation-receipt.ts +152 -0
  203. package/lib/task/manual-validation-shared.ts +50 -0
  204. package/lib/task/manual-validation-transaction.ts +304 -0
  205. package/lib/task/markdown.ts +70 -0
  206. package/lib/task/orchestration.ts +19 -19
  207. package/lib/task/qualification-audit.ts +22 -33
  208. package/lib/task/review-artifacts.ts +1 -20
  209. package/lib/task/review-command.ts +23 -0
  210. package/lib/task/review-finalization.ts +131 -259
  211. package/lib/task/review-fingerprint.ts +2 -2
  212. package/lib/task/sections.ts +33 -54
  213. package/lib/task/verification-engine.ts +110 -366
  214. package/lib/task/verification-types.ts +17 -0
  215. package/lib/task/verification.ts +1 -1
  216. package/lib/task/workflow-command.ts +18 -0
  217. package/lib/task/workflow-warning-intents.ts +0 -13
  218. package/lib/task/workflow-warnings.ts +21 -2
  219. package/lib/update.ts +6 -0
  220. package/package.json +6 -6
  221. package/templates/.agents/hooks/lifecycle-delegation.js +6 -6
  222. package/templates/.agents/rules/testing-discipline.en.md +2 -0
  223. package/templates/.agents/rules/testing-discipline.zh-CN.md +2 -0
  224. package/templates/.agents/scripts/package.json +3 -0
  225. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +32 -20
  226. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +32 -20
  227. package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +1 -1
  228. package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +1 -1
  229. package/templates/.agents/skills/complete-manual-validation/reference/report-template.en.md +2 -0
  230. package/templates/.agents/skills/complete-manual-validation/reference/report-template.zh-CN.md +2 -0
  231. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +16 -7
  232. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +16 -7
  233. package/templates/.agents/skills/complete-task/SKILL.en.md +1 -1
  234. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +1 -1
  235. package/templates/.agents/skills/release/SKILL.en.md +7 -3
  236. package/templates/.agents/skills/release/SKILL.zh-CN.md +7 -3
  237. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +20 -3
  238. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +20 -3
  239. package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.en.md +4 -1
  240. package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.zh-CN.md +4 -1
  241. package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +2 -0
  242. package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +2 -0
  243. package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
  244. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
  245. package/templates/.agents/skills/test/SKILL.en.md +17 -6
  246. package/templates/.agents/skills/test/SKILL.zh-CN.md +17 -6
  247. package/templates/.claude/commands/run-manual-validation.en.md +1 -1
  248. package/templates/.claude/commands/run-manual-validation.zh-CN.md +1 -1
  249. package/dist/lib/platform/adapters.js +0 -56
  250. 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
- type NativeFsExt = Readonly<{
8
- flockSync: (fd: number, flags: string) => void;
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 primitive = process.platform === 'win32' ? 'LockFileEx' : 'flock';
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 noFollow = process.platform !== 'win32' && typeof fs.constants.O_NOFOLLOW === 'number'
160
- ? fs.constants.O_NOFOLLOW : 0;
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
- try {
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 SandboxControlRequest = SandboxTaskCommandRequest | SandboxTaskFinalizationRequest | SandboxTaskCreateRequest | SandboxCodexControllerRequest;
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
- const expected = ['args', 'controllerProcess', 'controllerProof', 'expiresAt', 'family', 'generation', 'id', 'issuedAt', 'token', 'version'];
290
- if (Object.keys(request).sort().join(',') !== expected.sort().join(',')
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');