@fitlab-ai/agent-infra 0.11.12 → 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/lib/agent-clients/adapters/codex-lifecycle/store.js +11 -25
- 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/platform/github-client.js +33 -3
- package/dist/lib/sandbox/commands/create.js +6 -2
- package/dist/lib/sandbox/commands/ls.js +4 -5
- package/dist/lib/sandbox/commands/rm.js +1 -1
- package/dist/lib/sandbox/control/client.js +4 -64
- package/dist/lib/sandbox/control/executor.js +4 -50
- package/dist/lib/sandbox/control/lifecycle.js +66 -4
- package/dist/lib/sandbox/control/protocol.js +5 -16
- package/dist/lib/sandbox/control/server.js +203 -234
- package/dist/lib/sandbox/control/task-view.js +0 -13
- package/dist/lib/sandbox/mounts.js +7 -0
- package/dist/lib/sandbox/recovery.js +28 -9
- package/dist/lib/sandbox/removal.js +19 -0
- package/dist/lib/sandbox/workspace-identity.js +18 -6
- package/dist/lib/sandbox/workspace-view.js +48 -2
- package/dist/lib/task/commands/ls.js +3 -3
- package/dist/lib/task/control-authority.js +2 -0
- package/dist/lib/task/control-recovery.js +1 -2
- package/dist/lib/task/finalization.js +14 -0
- package/dist/lib/task/lifecycle.js +203 -31
- package/dist/lib/task/orchestration.js +26 -72
- package/dist/lib/task/short-id.js +37 -27
- package/dist/lib/task/verification-engine.js +4 -11
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-clients/adapters/codex-lifecycle/store.ts +20 -23
- 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/platform/github-client.ts +34 -3
- package/lib/sandbox/commands/create.ts +6 -1
- package/lib/sandbox/commands/ls.ts +4 -5
- package/lib/sandbox/commands/rm.ts +1 -1
- package/lib/sandbox/control/client.ts +5 -78
- package/lib/sandbox/control/executor.ts +2 -53
- package/lib/sandbox/control/lifecycle.ts +67 -4
- package/lib/sandbox/control/protocol.ts +6 -20
- package/lib/sandbox/control/server.ts +9 -44
- package/lib/sandbox/control/task-view.ts +0 -19
- package/lib/sandbox/mounts.ts +8 -0
- package/lib/sandbox/recovery.ts +35 -16
- package/lib/sandbox/removal.ts +21 -2
- package/lib/sandbox/workspace-identity.ts +16 -6
- package/lib/sandbox/workspace-view.ts +41 -2
- package/lib/task/commands/ls.ts +3 -3
- package/lib/task/control-authority.ts +1 -0
- package/lib/task/control-recovery.ts +2 -3
- package/lib/task/finalization.ts +14 -0
- package/lib/task/lifecycle.ts +180 -27
- package/lib/task/orchestration.ts +30 -68
- package/lib/task/short-id.ts +33 -40
- package/lib/task/verification-engine.ts +3 -9
- package/package.json +1 -1
- package/templates/.agents/rules/README.en.md +1 -0
- package/templates/.agents/rules/README.zh-CN.md +1 -0
- 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/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-task/SKILL.en.md +5 -26
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +5 -23
- 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/run-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
import { validateTaskCreateCandidate } from '../../task/create.js';
|
|
3
|
-
import { normalizeAgentToken } from '../../agent-clients/tokens.js';
|
|
4
3
|
import { isAgentClientId } from '../../agent-clients/types.js';
|
|
5
4
|
export const SANDBOX_CONTROL_MAX_BYTES = 64 * 1024;
|
|
6
5
|
export const SANDBOX_CONTROL_MAX_LOGICAL_RECORDS = 1024;
|
|
@@ -11,7 +10,7 @@ export const SANDBOX_CONTROL_ADMISSION_WINDOW_MS = 2_000;
|
|
|
11
10
|
export const SANDBOX_CONTROL_STATUS_INTERVAL_MS = 250;
|
|
12
11
|
export const SANDBOX_CONTROL_STATUS_STALE_MS = 1_500;
|
|
13
12
|
export const SANDBOX_CONTROL_FUTURE_SKEW_MS = 1_000;
|
|
14
|
-
export const SANDBOX_CONTROL_FAMILIES = ['task-lifecycle', 'task-
|
|
13
|
+
export const SANDBOX_CONTROL_FAMILIES = ['task-lifecycle', 'task-create', 'agent-client'];
|
|
15
14
|
export const DEFAULT_SANDBOX_CONTROL_TIMING = Object.freeze({
|
|
16
15
|
controlTickMs: 250,
|
|
17
16
|
parkedBindingInitialMs: 1_000,
|
|
@@ -151,31 +150,21 @@ export function validateSandboxControlRequest(value, manifest, options = {}) {
|
|
|
151
150
|
}
|
|
152
151
|
return request;
|
|
153
152
|
}
|
|
154
|
-
if (request.family === 'task-finalization') {
|
|
155
|
-
const expected = ['agent', 'args', 'expiresAt', 'family', 'generation', 'id', 'issuedAt', 'operation', 'token', 'version'];
|
|
156
|
-
if (Object.keys(request).sort().join(',') !== expected.sort().join(',')
|
|
157
|
-
|| request.operation !== 'complete'
|
|
158
|
-
|| !Array.isArray(request.args) || request.args.length !== 0
|
|
159
|
-
|| typeof request.agent !== 'string' || normalizeAgentToken(request.agent) !== request.agent) {
|
|
160
|
-
fail('SANDBOX_CONTROL_REQUEST_INVALID', 'task-finalization request schema or authorization is invalid');
|
|
161
|
-
}
|
|
162
|
-
if (manifest.mode !== 'task-bound' || !manifest.taskId) {
|
|
163
|
-
fail('SANDBOX_CONTROL_BRANCH_ONLY', 'branch-only sandboxes cannot finalize tasks');
|
|
164
|
-
}
|
|
165
|
-
return request;
|
|
166
|
-
}
|
|
167
153
|
const expected = ['args', 'expiresAt', 'family', 'generation', 'id', 'issuedAt', 'token', 'version'];
|
|
168
154
|
if (Object.keys(request).sort().join(',') !== expected.sort().join(',')
|
|
169
155
|
|| !Array.isArray(request.args) || !request.args.every((arg) => typeof arg === 'string')) {
|
|
170
156
|
fail('SANDBOX_CONTROL_REQUEST_INVALID', 'request schema or authorization is invalid');
|
|
171
157
|
}
|
|
158
|
+
if (request.args[1] === 'complete') {
|
|
159
|
+
fail('SANDBOX_CONTROL_COMMAND_DENIED', 'complete must use task-finalization');
|
|
160
|
+
}
|
|
172
161
|
if (manifest.mode !== 'task-bound' || !manifest.taskId) {
|
|
173
162
|
fail('SANDBOX_CONTROL_BRANCH_ONLY', "branch-only sandboxes cannot coordinate tasks; return to the host and run 'ai sandbox start --recreate <task-ref-or-correct-branch>'");
|
|
174
163
|
}
|
|
175
164
|
return request;
|
|
176
165
|
}
|
|
177
166
|
export function bindSandboxControlTask(request, taskId) {
|
|
178
|
-
if (request.family === 'task-create' || request.family === 'agent-client'
|
|
167
|
+
if (request.family === 'task-create' || request.family === 'agent-client') {
|
|
179
168
|
fail('SANDBOX_CONTROL_REQUEST_INVALID', `${request.family} requests do not bind a current task`);
|
|
180
169
|
}
|
|
181
170
|
if (request.args.length === 0)
|
|
@@ -20,41 +20,12 @@ import { nextSandboxControlBackoff } from './timing.js';
|
|
|
20
20
|
import { readTaskFinalizationReceipt } from '../../task/finalization.js';
|
|
21
21
|
import { recoverAgentClientOperation, validateAgentClientOperation } from './agent-operation.js';
|
|
22
22
|
import { validateSandboxControlIdentity } from './identity-sentinel.js';
|
|
23
|
-
import { mergeSandboxTaskView,
|
|
23
|
+
import { mergeSandboxTaskView, taskViewForManifest } from './task-view.js';
|
|
24
24
|
import { taskCreateOutputUnavailableResult } from '../../task/create-service.js';
|
|
25
|
-
async function taskViewForFinalizationResult(manifest, request, result) {
|
|
26
|
-
if (request.family !== 'task-finalization' || result?.exitCode !== 0 || !manifest.taskId)
|
|
27
|
-
return null;
|
|
28
|
-
let receipt = null;
|
|
29
|
-
for (let attempt = 0; attempt < 100 && !receipt; attempt += 1) {
|
|
30
|
-
try {
|
|
31
|
-
receipt = readTaskFinalizationReceipt(manifest.repoRoot, manifest.taskId);
|
|
32
|
-
}
|
|
33
|
-
catch {
|
|
34
|
-
// A missing or temporarily moving task directory is retried before publishing completion.
|
|
35
|
-
}
|
|
36
|
-
if (!receipt)
|
|
37
|
-
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
38
|
-
}
|
|
39
|
-
const finalized = taskViewAfterFinalization({
|
|
40
|
-
taskId: manifest.taskId,
|
|
41
|
-
generation: manifest.generation,
|
|
42
|
-
requestId: request.id,
|
|
43
|
-
receipt
|
|
44
|
-
});
|
|
45
|
-
if (finalized.state === 'unknown')
|
|
46
|
-
return finalized;
|
|
47
|
-
const canonical = taskViewForManifest({ ...manifest, receipt });
|
|
48
|
-
return canonical.state === 'current' && canonical.observedSource === 'completed'
|
|
49
|
-
? canonical
|
|
50
|
-
: finalized;
|
|
51
|
-
}
|
|
52
25
|
function appendBrokerAudit(manifest, event, fields = {}) {
|
|
53
26
|
appendDiagnosticAudit(manifest, event, { source: 'broker', ...fields });
|
|
54
27
|
}
|
|
55
28
|
function operationKey(request) {
|
|
56
|
-
if (request.family === 'task-finalization')
|
|
57
|
-
return request.operation;
|
|
58
29
|
if (request.family === 'task-create')
|
|
59
30
|
return 'create';
|
|
60
31
|
if (request.family === 'agent-client')
|
|
@@ -220,9 +191,7 @@ function taskCreateDomainEvidence(manifest, output) {
|
|
|
220
191
|
async function readRecoveryDomain(manifest, manifestPath, request, operation, terminalResult, payloadOutput) {
|
|
221
192
|
if (!operation)
|
|
222
193
|
return { domain: null };
|
|
223
|
-
const taskRef = request.
|
|
224
|
-
? manifest.taskId
|
|
225
|
-
: 'args' in request ? request.args[0] ?? null : manifest.taskId;
|
|
194
|
+
const taskRef = 'args' in request ? request.args[0] ?? null : manifest.taskId;
|
|
226
195
|
const output = parseControlOutput(payloadOutput);
|
|
227
196
|
if (!taskRef && operation.family !== 'task-create' && operation.family !== 'agent-client') {
|
|
228
197
|
return { domain: null };
|
|
@@ -730,6 +699,8 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
|
|
|
730
699
|
try {
|
|
731
700
|
if (!brokerOwns())
|
|
732
701
|
return;
|
|
702
|
+
if (isSandboxControlRootQuiescing(root))
|
|
703
|
+
return;
|
|
733
704
|
if (taskView.state === 'current' && taskView.observedSource === 'completed') {
|
|
734
705
|
// Recheck durable re-entry evidence before publishing a trusted startup view.
|
|
735
706
|
const unverified = { ...taskView, state: 'unknown',
|
|
@@ -836,11 +807,6 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
|
|
|
836
807
|
settledExecution = active;
|
|
837
808
|
active = null;
|
|
838
809
|
}
|
|
839
|
-
const finalizationView = settledExecution
|
|
840
|
-
? await taskViewForFinalizationResult(manifest, settledExecution.request, settledExecution.result)
|
|
841
|
-
: null;
|
|
842
|
-
if (finalizationView)
|
|
843
|
-
taskView = finalizationView;
|
|
844
810
|
let reasonCode = null;
|
|
845
811
|
try {
|
|
846
812
|
if (!brokerOwns())
|
|
@@ -918,224 +884,232 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
|
|
|
918
884
|
fs.rmSync(path.join(manifest.processingDir, settledExecution.request.id), { recursive: true, force: true });
|
|
919
885
|
}
|
|
920
886
|
let retiring = false;
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
const
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
...requestAuditFields(manifest, request),
|
|
978
|
-
requestPath: claimed,
|
|
979
|
-
executionPath: executionPath(manifest, request.id)
|
|
980
|
-
});
|
|
981
|
-
appendBrokerAudit(manifest, 'executor-prepare-start', {
|
|
982
|
-
...requestAuditFields(manifest, request),
|
|
983
|
-
requestPath: claimed,
|
|
984
|
-
executorCwd: manifest.repoRoot,
|
|
985
|
-
executorEntry: options.internalCliPath ?? process.argv[1] ?? null
|
|
986
|
-
});
|
|
987
|
-
prepared = await prepareExecution({
|
|
988
|
-
manifest, manifestPath, request, requestPath: claimed,
|
|
989
|
-
internalCliPath: options.internalCliPath ?? process.argv[1]
|
|
990
|
-
});
|
|
991
|
-
const preparedExecution = prepared;
|
|
992
|
-
if (!preparedExecution)
|
|
993
|
-
throw new Error('SANDBOX_CONTROL_EXECUTION_PREPARE_INVALID');
|
|
994
|
-
criticalRequestPhase(manifest, request, 'prepared', 'in-progress', `${preparedExecution.execution.child.pid}`);
|
|
995
|
-
appendBrokerAudit(manifest, 'executor-prepared', {
|
|
996
|
-
...requestAuditFields(manifest, request),
|
|
997
|
-
requestPath: claimed,
|
|
998
|
-
executorCwd: manifest.repoRoot,
|
|
999
|
-
childPid: preparedExecution.execution.child.pid,
|
|
1000
|
-
childStartTime: preparedExecution.execution.child.startTime,
|
|
1001
|
-
childProcessGroupId: preparedExecution.execution.child.processGroupId,
|
|
1002
|
-
executionPath: executionPath(manifest, request.id)
|
|
1003
|
-
});
|
|
1004
|
-
const execution = {
|
|
1005
|
-
request, prepared: preparedExecution, result: null,
|
|
1006
|
-
resultEvidenceWritten: false, failure: null, settled: false
|
|
1007
|
-
};
|
|
1008
|
-
active = execution;
|
|
1009
|
-
preparedExecution.completion.then((result) => {
|
|
1010
|
-
execution.result = sanitizeSandboxControlResult(manifest, result);
|
|
1011
|
-
appendBrokerAudit(manifest, 'executor-completed', {
|
|
887
|
+
const quiescing = isSandboxControlRootQuiescing(root);
|
|
888
|
+
if (quiescing && !active)
|
|
889
|
+
break;
|
|
890
|
+
if (!quiescing)
|
|
891
|
+
for (const name of fs.readdirSync(requestsDir).sort()) {
|
|
892
|
+
if (!/^[a-f0-9-]{16,64}\.json$/.test(name))
|
|
893
|
+
continue;
|
|
894
|
+
const id = name.slice(0, -5);
|
|
895
|
+
const source = path.join(requestsDir, name);
|
|
896
|
+
let claimed = null;
|
|
897
|
+
let validatedRequest = null;
|
|
898
|
+
let prepared = null;
|
|
899
|
+
try {
|
|
900
|
+
if (isSandboxControlRootQuiescing(root)) {
|
|
901
|
+
retiring = true;
|
|
902
|
+
break;
|
|
903
|
+
}
|
|
904
|
+
if (!brokerOwns()) {
|
|
905
|
+
retiring = true;
|
|
906
|
+
break;
|
|
907
|
+
}
|
|
908
|
+
claimed = claimRequest(manifest, source, id);
|
|
909
|
+
appendBrokerAudit(manifest, 'request-claimed', {
|
|
910
|
+
requestId: id,
|
|
911
|
+
requestSourcePath: source,
|
|
912
|
+
requestClaimedPath: claimed,
|
|
913
|
+
requestProcessingDir: path.dirname(claimed)
|
|
914
|
+
});
|
|
915
|
+
if (!brokerOwns()) {
|
|
916
|
+
retiring = true;
|
|
917
|
+
break;
|
|
918
|
+
}
|
|
919
|
+
consumeRequest(consumedDir, id);
|
|
920
|
+
if (!brokerOwns()) {
|
|
921
|
+
retiring = true;
|
|
922
|
+
break;
|
|
923
|
+
}
|
|
924
|
+
if (reasonCode)
|
|
925
|
+
throw new Error(reasonCode);
|
|
926
|
+
if (active)
|
|
927
|
+
throw new Error('SANDBOX_CONTROL_BUSY');
|
|
928
|
+
const request = validateSandboxControlRequest(JSON.parse(fs.readFileSync(claimed, 'utf8')), manifest);
|
|
929
|
+
if (request.family === 'agent-client')
|
|
930
|
+
validateAgentClientOperation(request, manifest);
|
|
931
|
+
validatedRequest = request;
|
|
932
|
+
criticalRequestPhase(manifest, request, 'validated', 'in-progress');
|
|
933
|
+
appendBrokerAudit(manifest, 'request-validated', {
|
|
934
|
+
...requestAuditFields(manifest, request),
|
|
935
|
+
requestPath: claimed
|
|
936
|
+
});
|
|
937
|
+
if (bindingCheck(manifest))
|
|
938
|
+
throw new Error('SANDBOX_WORKTREE_BINDING_LOST');
|
|
939
|
+
if (readActiveLease(manifest))
|
|
940
|
+
throw new Error('SANDBOX_CONTROL_HANDOFF_ACTIVE');
|
|
941
|
+
criticalRequestPhase(manifest, request, 'gated', 'in-progress');
|
|
942
|
+
appendBrokerAudit(manifest, 'request-gates-passed', {
|
|
1012
943
|
...requestAuditFields(manifest, request),
|
|
1013
|
-
|
|
944
|
+
requestPath: claimed,
|
|
945
|
+
bindingChecked: true,
|
|
946
|
+
handoffLeaseChecked: true
|
|
947
|
+
});
|
|
948
|
+
criticalRequestPhase(manifest, request, 'reserved', 'in-progress');
|
|
949
|
+
writeSandboxControlReservation(manifest, request.id, sandboxControlGenerationUsage(manifest));
|
|
950
|
+
appendBrokerAudit(manifest, 'executor-reservation-written', {
|
|
951
|
+
...requestAuditFields(manifest, request),
|
|
952
|
+
requestPath: claimed,
|
|
953
|
+
executionPath: executionPath(manifest, request.id)
|
|
954
|
+
});
|
|
955
|
+
appendBrokerAudit(manifest, 'executor-prepare-start', {
|
|
956
|
+
...requestAuditFields(manifest, request),
|
|
957
|
+
requestPath: claimed,
|
|
958
|
+
executorCwd: manifest.repoRoot,
|
|
959
|
+
executorEntry: options.internalCliPath ?? process.argv[1] ?? null
|
|
960
|
+
});
|
|
961
|
+
prepared = await prepareExecution({
|
|
962
|
+
manifest, manifestPath, request, requestPath: claimed,
|
|
963
|
+
internalCliPath: options.internalCliPath ?? process.argv[1]
|
|
964
|
+
});
|
|
965
|
+
const preparedExecution = prepared;
|
|
966
|
+
if (!preparedExecution)
|
|
967
|
+
throw new Error('SANDBOX_CONTROL_EXECUTION_PREPARE_INVALID');
|
|
968
|
+
criticalRequestPhase(manifest, request, 'prepared', 'in-progress', `${preparedExecution.execution.child.pid}`);
|
|
969
|
+
appendBrokerAudit(manifest, 'executor-prepared', {
|
|
970
|
+
...requestAuditFields(manifest, request),
|
|
971
|
+
requestPath: claimed,
|
|
972
|
+
executorCwd: manifest.repoRoot,
|
|
1014
973
|
childPid: preparedExecution.execution.child.pid,
|
|
1015
|
-
childStartTime: preparedExecution.execution.child.startTime
|
|
974
|
+
childStartTime: preparedExecution.execution.child.startTime,
|
|
975
|
+
childProcessGroupId: preparedExecution.execution.child.processGroupId,
|
|
976
|
+
executionPath: executionPath(manifest, request.id)
|
|
1016
977
|
});
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
978
|
+
const execution = {
|
|
979
|
+
request, prepared: preparedExecution, result: null,
|
|
980
|
+
resultEvidenceWritten: false, failure: null, settled: false
|
|
981
|
+
};
|
|
982
|
+
active = execution;
|
|
983
|
+
preparedExecution.completion.then((result) => {
|
|
984
|
+
execution.result = sanitizeSandboxControlResult(manifest, result);
|
|
985
|
+
appendBrokerAudit(manifest, 'executor-completed', {
|
|
1022
986
|
...requestAuditFields(manifest, request),
|
|
1023
|
-
|
|
1024
|
-
|
|
987
|
+
...resultAuditFields(execution.result),
|
|
988
|
+
childPid: preparedExecution.execution.child.pid,
|
|
989
|
+
childStartTime: preparedExecution.execution.child.startTime
|
|
1025
990
|
});
|
|
1026
|
-
|
|
1027
|
-
|
|
991
|
+
try {
|
|
992
|
+
writeSandboxControlResultEvidence(manifest, request.id, execution.result);
|
|
993
|
+
execution.resultEvidenceWritten = true;
|
|
994
|
+
execution.settled = true;
|
|
995
|
+
appendBrokerAudit(manifest, 'executor-result-evidence-written', {
|
|
996
|
+
...requestAuditFields(manifest, request),
|
|
997
|
+
resultPath: path.join(manifest.processingDir, request.id, 'result.json'),
|
|
998
|
+
...resultAuditFields(execution.result)
|
|
999
|
+
});
|
|
1000
|
+
}
|
|
1001
|
+
catch (error) {
|
|
1002
|
+
execution.failure = error;
|
|
1003
|
+
appendBrokerAudit(manifest, 'executor-result-evidence-failed', {
|
|
1004
|
+
...requestAuditFields(manifest, request),
|
|
1005
|
+
errorType: error instanceof Error ? error.name : typeof error,
|
|
1006
|
+
});
|
|
1007
|
+
}
|
|
1008
|
+
}, (error) => {
|
|
1028
1009
|
execution.failure = error;
|
|
1029
|
-
|
|
1010
|
+
execution.settled = true;
|
|
1011
|
+
appendBrokerAudit(manifest, 'executor-failed', {
|
|
1030
1012
|
...requestAuditFields(manifest, request),
|
|
1013
|
+
childPid: preparedExecution.execution.child.pid,
|
|
1014
|
+
childStartTime: preparedExecution.execution.child.startTime,
|
|
1031
1015
|
errorType: error instanceof Error ? error.name : typeof error,
|
|
1032
1016
|
});
|
|
1033
|
-
}
|
|
1034
|
-
}, (error) => {
|
|
1035
|
-
execution.failure = error;
|
|
1036
|
-
execution.settled = true;
|
|
1037
|
-
appendBrokerAudit(manifest, 'executor-failed', {
|
|
1038
|
-
...requestAuditFields(manifest, request),
|
|
1039
|
-
childPid: preparedExecution.execution.child.pid,
|
|
1040
|
-
childStartTime: preparedExecution.execution.child.startTime,
|
|
1041
|
-
errorType: error instanceof Error ? error.name : typeof error,
|
|
1042
1017
|
});
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
version: 2, id, phase: 'accepted', exitCode: null, stdout: '', stderr: '', error: null
|
|
1053
|
-
});
|
|
1054
|
-
writeSandboxControlTransition(manifest, { requestId: request.id, phase: 'accepted-committed' });
|
|
1055
|
-
appendBrokerAudit(manifest, 'request-accepted', {
|
|
1056
|
-
...requestAuditFields(manifest, request),
|
|
1057
|
-
acceptedPath: acceptedResponsePath(manifest, id),
|
|
1058
|
-
executionPhase: 'prepared'
|
|
1059
|
-
});
|
|
1060
|
-
if (!brokerOwns()) {
|
|
1061
|
-
prepared.terminate(false);
|
|
1062
|
-
active = null;
|
|
1063
|
-
retiring = true;
|
|
1064
|
-
break;
|
|
1065
|
-
}
|
|
1066
|
-
try {
|
|
1067
|
-
criticalRequestPhase(manifest, request, 'start-authorized', 'in-progress', `${prepared.execution.child.pid}`);
|
|
1068
|
-
appendBrokerAudit(manifest, 'executor-start', {
|
|
1069
|
-
...requestAuditFields(manifest, request),
|
|
1070
|
-
childPid: prepared.execution.child.pid,
|
|
1071
|
-
childStartTime: prepared.execution.child.startTime,
|
|
1072
|
-
executionPath: executionPath(manifest, request.id)
|
|
1018
|
+
if (!brokerOwns()) {
|
|
1019
|
+
prepared.terminate(false);
|
|
1020
|
+
active = null;
|
|
1021
|
+
retiring = true;
|
|
1022
|
+
continue;
|
|
1023
|
+
}
|
|
1024
|
+
criticalRequestPhase(manifest, request, 'accepted-authorized', 'in-progress');
|
|
1025
|
+
writeAcceptedResponse(manifest, {
|
|
1026
|
+
version: 2, id, phase: 'accepted', exitCode: null, stdout: '', stderr: '', error: null
|
|
1073
1027
|
});
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
catch (error) {
|
|
1077
|
-
appendBrokerAudit(manifest, 'executor-start-failed', {
|
|
1028
|
+
writeSandboxControlTransition(manifest, { requestId: request.id, phase: 'accepted-committed' });
|
|
1029
|
+
appendBrokerAudit(manifest, 'request-accepted', {
|
|
1078
1030
|
...requestAuditFields(manifest, request),
|
|
1079
|
-
|
|
1031
|
+
acceptedPath: acceptedResponsePath(manifest, id),
|
|
1032
|
+
executionPhase: 'prepared'
|
|
1080
1033
|
});
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
if (!owned || !brokerOwns()) {
|
|
1034
|
+
if (!brokerOwns()) {
|
|
1035
|
+
prepared.terminate(false);
|
|
1084
1036
|
active = null;
|
|
1085
1037
|
retiring = true;
|
|
1086
1038
|
break;
|
|
1087
1039
|
}
|
|
1088
|
-
|
|
1089
|
-
|
|
1040
|
+
try {
|
|
1041
|
+
criticalRequestPhase(manifest, request, 'start-authorized', 'in-progress', `${prepared.execution.child.pid}`);
|
|
1042
|
+
appendBrokerAudit(manifest, 'executor-start', {
|
|
1043
|
+
...requestAuditFields(manifest, request),
|
|
1044
|
+
childPid: prepared.execution.child.pid,
|
|
1045
|
+
childStartTime: prepared.execution.child.startTime,
|
|
1046
|
+
executionPath: executionPath(manifest, request.id)
|
|
1047
|
+
});
|
|
1048
|
+
prepared.start(brokerOwns);
|
|
1049
|
+
}
|
|
1050
|
+
catch (error) {
|
|
1051
|
+
appendBrokerAudit(manifest, 'executor-start-failed', {
|
|
1052
|
+
...requestAuditFields(manifest, request),
|
|
1053
|
+
errorType: error instanceof Error ? error.name : typeof error,
|
|
1054
|
+
});
|
|
1055
|
+
const owned = brokerOwns();
|
|
1056
|
+
prepared.terminate(owned);
|
|
1057
|
+
if (!owned || !brokerOwns()) {
|
|
1058
|
+
active = null;
|
|
1059
|
+
retiring = true;
|
|
1060
|
+
break;
|
|
1061
|
+
}
|
|
1062
|
+
writeSandboxControlResponse(manifest, unknown(id));
|
|
1063
|
+
if (!brokerOwns()) {
|
|
1064
|
+
active = null;
|
|
1065
|
+
retiring = true;
|
|
1066
|
+
break;
|
|
1067
|
+
}
|
|
1068
|
+
removeAcceptedResponse(manifest, id);
|
|
1069
|
+
fs.rmSync(path.join(manifest.processingDir, id), { recursive: true, force: true });
|
|
1070
|
+
active = null;
|
|
1071
|
+
claimed = null;
|
|
1072
|
+
if (!brokerOwns()) {
|
|
1073
|
+
retiring = true;
|
|
1074
|
+
break;
|
|
1075
|
+
}
|
|
1076
|
+
appendBrokerAudit(manifest, 'executor-gate-failed', { requestId: id });
|
|
1077
|
+
continue;
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
catch (error) {
|
|
1081
|
+
if (prepared) {
|
|
1082
|
+
try {
|
|
1083
|
+
prepared.terminate(brokerOwns());
|
|
1084
|
+
}
|
|
1085
|
+
catch { /* cleanup is best effort after a failed admission */ }
|
|
1086
|
+
prepared = null;
|
|
1090
1087
|
active = null;
|
|
1088
|
+
}
|
|
1089
|
+
if (!brokerOwns()) {
|
|
1091
1090
|
retiring = true;
|
|
1092
1091
|
break;
|
|
1093
1092
|
}
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1093
|
+
const detail = controlError(error);
|
|
1094
|
+
appendBrokerAudit(manifest, 'request-rejected', {
|
|
1095
|
+
requestId: id,
|
|
1096
|
+
requestFamily: validatedRequest?.family ?? null,
|
|
1097
|
+
sandboxTaskId: manifest.taskId,
|
|
1098
|
+
requestPath: claimed,
|
|
1099
|
+
errorCode: detail.code,
|
|
1100
|
+
errorRetryable: detail.retryable,
|
|
1101
|
+
errorType: error instanceof Error ? error.name : typeof error,
|
|
1102
|
+
});
|
|
1103
|
+
writeSandboxControlResponse(manifest, rejected(id, error));
|
|
1098
1104
|
if (!brokerOwns()) {
|
|
1099
1105
|
retiring = true;
|
|
1100
1106
|
break;
|
|
1101
1107
|
}
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
}
|
|
1106
|
-
catch (error) {
|
|
1107
|
-
if (prepared) {
|
|
1108
|
-
try {
|
|
1109
|
-
prepared.terminate(brokerOwns());
|
|
1110
|
-
}
|
|
1111
|
-
catch { /* cleanup is best effort after a failed admission */ }
|
|
1112
|
-
prepared = null;
|
|
1113
|
-
active = null;
|
|
1114
|
-
}
|
|
1115
|
-
if (!brokerOwns()) {
|
|
1116
|
-
retiring = true;
|
|
1117
|
-
break;
|
|
1118
|
-
}
|
|
1119
|
-
const detail = controlError(error);
|
|
1120
|
-
appendBrokerAudit(manifest, 'request-rejected', {
|
|
1121
|
-
requestId: id,
|
|
1122
|
-
requestFamily: validatedRequest?.family ?? null,
|
|
1123
|
-
sandboxTaskId: manifest.taskId,
|
|
1124
|
-
requestPath: claimed,
|
|
1125
|
-
errorCode: detail.code,
|
|
1126
|
-
errorRetryable: detail.retryable,
|
|
1127
|
-
errorType: error instanceof Error ? error.name : typeof error,
|
|
1128
|
-
});
|
|
1129
|
-
writeSandboxControlResponse(manifest, rejected(id, error));
|
|
1130
|
-
if (!brokerOwns()) {
|
|
1131
|
-
retiring = true;
|
|
1132
|
-
break;
|
|
1108
|
+
removeAcceptedResponse(manifest, id);
|
|
1109
|
+
if (claimed)
|
|
1110
|
+
fs.rmSync(path.dirname(claimed), { recursive: true, force: true });
|
|
1133
1111
|
}
|
|
1134
|
-
removeAcceptedResponse(manifest, id);
|
|
1135
|
-
if (claimed)
|
|
1136
|
-
fs.rmSync(path.dirname(claimed), { recursive: true, force: true });
|
|
1137
1112
|
}
|
|
1138
|
-
}
|
|
1139
1113
|
if (retiring || !brokerOwns())
|
|
1140
1114
|
break;
|
|
1141
1115
|
await delay(timing.controlTickMs);
|
|
@@ -1157,11 +1131,6 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
|
|
|
1157
1131
|
}
|
|
1158
1132
|
}
|
|
1159
1133
|
if (owned && active.result && active.resultEvidenceWritten) {
|
|
1160
|
-
const finalizationView = await taskViewForFinalizationResult(manifest, active.request, active.result);
|
|
1161
|
-
if (finalizationView) {
|
|
1162
|
-
taskView = finalizationView;
|
|
1163
|
-
writeSandboxControlStatus(manifest, broker, 'healthy', null, null, Date.now(), taskView);
|
|
1164
|
-
}
|
|
1165
1134
|
if (publishExecutionResult(manifest, active.request, active.result, brokerOwns)) {
|
|
1166
1135
|
if (brokerOwns()) {
|
|
1167
1136
|
removeAcceptedResponse(manifest, active.request.id);
|
|
@@ -130,19 +130,6 @@ export function projectSandboxTaskView(input) {
|
|
|
130
130
|
reasonCode: receipt ? 'SANDBOX_TASK_VIEW_SOURCE_UNCONFIRMED' : 'SANDBOX_TASK_VIEW_EVIDENCE_UNAVAILABLE'
|
|
131
131
|
};
|
|
132
132
|
}
|
|
133
|
-
export function taskViewAfterFinalization(params) {
|
|
134
|
-
const receipt = receiptIdentity(params.receipt, params.generation, params.requestId);
|
|
135
|
-
if (!receipt) {
|
|
136
|
-
return {
|
|
137
|
-
state: 'unknown', taskId: params.taskId, observedSource: 'unknown', receipt: null,
|
|
138
|
-
reasonCode: 'SANDBOX_TASK_VIEW_RECEIPT_INVALID'
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
return {
|
|
142
|
-
state: 'finalized-stale', taskId: params.taskId, observedSource: 'active', receipt,
|
|
143
|
-
reasonCode: 'SANDBOX_TASK_VIEW_FINALIZED'
|
|
144
|
-
};
|
|
145
|
-
}
|
|
146
133
|
export function mergeSandboxTaskView(canonical, previous) {
|
|
147
134
|
if (previous.state !== 'finalized-stale' && previous.state !== 'unknown')
|
|
148
135
|
return canonical;
|
|
@@ -44,6 +44,13 @@ export function sandboxCoreBindMounts(config, branch, overrides) {
|
|
|
44
44
|
containerPath: taskMountPath,
|
|
45
45
|
readOnly: false
|
|
46
46
|
});
|
|
47
|
+
if (overrides.completedTaskSource) {
|
|
48
|
+
mounts.push({
|
|
49
|
+
hostPaths: [overrides.completedTaskSource],
|
|
50
|
+
containerPath: path.posix.join('/workspace/.agents/workspace/completed', overrides.taskId),
|
|
51
|
+
readOnly: false
|
|
52
|
+
});
|
|
53
|
+
}
|
|
47
54
|
}
|
|
48
55
|
mounts.push({
|
|
49
56
|
hostPaths: [shareCommonDir(config)],
|