@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,3 +1,5 @@
|
|
|
1
|
+
import { isCompletionEvidence } from '../../task/orchestration.js';
|
|
2
|
+
import { parseControlOutput } from '../../task/control-recovery.js';
|
|
1
3
|
import { execFileSync } from 'node:child_process';
|
|
2
4
|
import fs from 'node:fs';
|
|
3
5
|
import path from 'node:path';
|
|
@@ -70,6 +72,62 @@ export function reservationPath(manifest, requestId) {
|
|
|
70
72
|
throw new Error('SANDBOX_CONTROL_RESERVATION_INVALID');
|
|
71
73
|
return path.join(manifest.processingDir, requestId, 'reservation.json');
|
|
72
74
|
}
|
|
75
|
+
export function terminalResultPath(manifest, requestId) {
|
|
76
|
+
if (!/^[a-f0-9-]{16,64}$/u.test(requestId))
|
|
77
|
+
throw new Error('SANDBOX_CONTROL_TERMINAL_RESULT_INVALID');
|
|
78
|
+
return path.join(manifest.processingDir, requestId, 'terminal-result.json');
|
|
79
|
+
}
|
|
80
|
+
export function createSandboxControlTerminalResult(manifest, request, output) {
|
|
81
|
+
const nested = parseControlOutput(output) ?? {};
|
|
82
|
+
const run = nested.run && typeof nested.run === 'object' && !Array.isArray(nested.run)
|
|
83
|
+
? nested.run : null;
|
|
84
|
+
const rawCompletion = nested.completionEvidence ?? run?.completionEvidence;
|
|
85
|
+
const operation = request.operation ?? (typeof nested.intent === 'string' ? nested.intent : null) ?? '';
|
|
86
|
+
return {
|
|
87
|
+
version: 1,
|
|
88
|
+
requestId: request.id,
|
|
89
|
+
generation: manifest.generation,
|
|
90
|
+
taskId: manifest.taskId,
|
|
91
|
+
intentDigest: createHash('sha256').update(`${request.family}\0${operation}`, 'utf8').digest('hex'),
|
|
92
|
+
targetState: typeof nested.targetState === 'string' ? nested.targetState : null,
|
|
93
|
+
status: typeof nested.status === 'string' ? nested.status : 'completed',
|
|
94
|
+
changed: typeof nested.changed === 'boolean' ? nested.changed : null,
|
|
95
|
+
completionEvidence: isCompletionEvidence(rawCompletion) ? rawCompletion : null
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
export function writeSandboxControlTerminalResult(manifest, request, output) {
|
|
99
|
+
const result = createSandboxControlTerminalResult(manifest, request, output);
|
|
100
|
+
const filePath = terminalResultPath(manifest, request.id);
|
|
101
|
+
try {
|
|
102
|
+
atomicWriteJsonNoReplace(filePath, result);
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
if (!(error instanceof Error) || error.message !== 'SANDBOX_CONTROL_TERMINAL_ALREADY_EXISTS')
|
|
106
|
+
throw error;
|
|
107
|
+
const existing = readSandboxControlTerminalResult(filePath);
|
|
108
|
+
if (JSON.stringify(existing) !== JSON.stringify(result))
|
|
109
|
+
throw new Error('SANDBOX_CONTROL_TERMINAL_RESULT_CONFLICT');
|
|
110
|
+
}
|
|
111
|
+
return readSandboxControlTerminalResult(filePath);
|
|
112
|
+
}
|
|
113
|
+
export function readSandboxControlTerminalResult(filePath) {
|
|
114
|
+
const value = readJsonFile(filePath);
|
|
115
|
+
if (!value || value.version !== 1 || typeof value.requestId !== 'string'
|
|
116
|
+
|| typeof value.generation !== 'string' || (value.taskId !== null && typeof value.taskId !== 'string')
|
|
117
|
+
|| typeof value.intentDigest !== 'string' || !/^[a-f0-9]{64}$/u.test(value.intentDigest)
|
|
118
|
+
|| (value.targetState !== null && typeof value.targetState !== 'string')
|
|
119
|
+
|| typeof value.status !== 'string'
|
|
120
|
+
|| (value.changed !== null && typeof value.changed !== 'boolean')) {
|
|
121
|
+
throw new Error('SANDBOX_CONTROL_TERMINAL_RESULT_INVALID');
|
|
122
|
+
}
|
|
123
|
+
if (value.completionEvidence !== null && !isCompletionEvidence(value.completionEvidence)) {
|
|
124
|
+
throw new Error('SANDBOX_CONTROL_TERMINAL_RESULT_INVALID');
|
|
125
|
+
}
|
|
126
|
+
if (value.changed === undefined) {
|
|
127
|
+
throw new Error('SANDBOX_CONTROL_TERMINAL_RESULT_INVALID');
|
|
128
|
+
}
|
|
129
|
+
return value;
|
|
130
|
+
}
|
|
73
131
|
export function readSandboxControlResultEvidence(filePath) {
|
|
74
132
|
assertRegularFile(filePath);
|
|
75
133
|
const raw = fs.readFileSync(filePath, 'utf8');
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { randomUUID } from 'node:crypto';
|
|
4
|
+
import { readStableFile, SecureFileError, writeAtomicFile } from '../../host-control/secure-fs.js';
|
|
5
|
+
import { TASK_WORKFLOW_COMMANDS, TASK_WORKFLOW_OPERATIONS } from '../../task/workflow-command.js';
|
|
6
|
+
export function validateTaskWorkflowRequest(value) {
|
|
7
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
8
|
+
throw new Error('TASK_WORKFLOW_REQUEST_INVALID');
|
|
9
|
+
const request = value;
|
|
10
|
+
if (Object.keys(request).sort().join(',') !== 'args,generation,id,operation,taskId,version'
|
|
11
|
+
|| request.version !== 1 || typeof request.id !== 'string' || !/^[a-f0-9-]{16,64}$/u.test(request.id)
|
|
12
|
+
|| typeof request.taskId !== 'string' || !/^TASK-\d{8}-\d{6}$/u.test(request.taskId)
|
|
13
|
+
|| typeof request.generation !== 'string' || !request.generation
|
|
14
|
+
|| !Object.hasOwn(TASK_WORKFLOW_COMMANDS, request.operation)
|
|
15
|
+
|| !Array.isArray(request.args) || request.args.length < 2 || request.args.length > 128
|
|
16
|
+
|| !request.args.every((arg) => typeof arg === 'string' && !/[\r\n]/u.test(arg))
|
|
17
|
+
|| request.args[0] !== request.taskId)
|
|
18
|
+
throw new Error('TASK_WORKFLOW_REQUEST_INVALID');
|
|
19
|
+
const [, selector] = TASK_WORKFLOW_COMMANDS[request.operation];
|
|
20
|
+
if (selector !== null && request.args[1] !== selector)
|
|
21
|
+
throw new Error('TASK_WORKFLOW_REQUEST_INVALID');
|
|
22
|
+
return request;
|
|
23
|
+
}
|
|
24
|
+
/** Preserve argv verbatim; the domain command owns option parsing and validation. */
|
|
25
|
+
export function createTaskWorkflowRequest(command, args, taskId, generation) {
|
|
26
|
+
const operation = TASK_WORKFLOW_OPERATIONS.find((key) => {
|
|
27
|
+
const [name, selector] = TASK_WORKFLOW_COMMANDS[key];
|
|
28
|
+
return name === command && (selector === null || selector === args[1]);
|
|
29
|
+
});
|
|
30
|
+
if (!operation)
|
|
31
|
+
throw new Error('TASK_WORKFLOW_OPERATION_UNSUPPORTED');
|
|
32
|
+
return validateTaskWorkflowRequest({ version: 1, id: randomUUID(), taskId, generation, operation, args: [...args] });
|
|
33
|
+
}
|
|
34
|
+
export function canonicalArtifactName(value) {
|
|
35
|
+
return /^(?:analysis|plan|code|review-analysis|review-plan|review-code)(?:-r[1-9]\d*)?\.md$/u.test(value)
|
|
36
|
+
&& path.basename(value) === value;
|
|
37
|
+
}
|
|
38
|
+
function assertTaskArtifactDirectory(directory) {
|
|
39
|
+
const resolved = path.resolve(directory);
|
|
40
|
+
let stat;
|
|
41
|
+
try {
|
|
42
|
+
stat = fs.lstatSync(resolved);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
throw new SecureFileError('TASK_ARTIFACT_WRITE_DENIED', 'task directory is unavailable');
|
|
46
|
+
}
|
|
47
|
+
if (!stat.isDirectory() || stat.isSymbolicLink()) {
|
|
48
|
+
throw new SecureFileError('TASK_ARTIFACT_WRITE_DENIED', 'task directory must be a real directory');
|
|
49
|
+
}
|
|
50
|
+
return resolved;
|
|
51
|
+
}
|
|
52
|
+
export async function readTaskArtifact(taskDir, input) {
|
|
53
|
+
if (!canonicalArtifactName(input.artifact))
|
|
54
|
+
throw new SecureFileError('TASK_ARTIFACT_WRITE_DENIED', 'artifact must be a canonical top-level basename');
|
|
55
|
+
const root = assertTaskArtifactDirectory(taskDir);
|
|
56
|
+
const candidate = path.join(root, input.artifact);
|
|
57
|
+
if (path.dirname(candidate) !== root)
|
|
58
|
+
throw new SecureFileError('TASK_ARTIFACT_WRITE_DENIED', 'artifact escapes task directory');
|
|
59
|
+
const stable = await readStableFile(candidate, {
|
|
60
|
+
maxBytes: 1024 * 1024,
|
|
61
|
+
...(input.expectedSha256 ? { expectedSha256: input.expectedSha256 } : {})
|
|
62
|
+
});
|
|
63
|
+
return { artifact: input.artifact, bytes: stable.bytes, sha256: stable.sha256 };
|
|
64
|
+
}
|
|
65
|
+
export async function writeTaskArtifact(taskDir, artifact) {
|
|
66
|
+
if (!canonicalArtifactName(artifact.artifact))
|
|
67
|
+
throw new SecureFileError('TASK_ARTIFACT_WRITE_DENIED', 'artifact must be a canonical top-level basename');
|
|
68
|
+
const root = assertTaskArtifactDirectory(taskDir);
|
|
69
|
+
const target = path.join(root, artifact.artifact);
|
|
70
|
+
if (path.dirname(target) !== root)
|
|
71
|
+
throw new SecureFileError('TASK_ARTIFACT_WRITE_DENIED', 'artifact escapes task directory');
|
|
72
|
+
await writeAtomicFile(target, artifact.bytes, 0o600, artifact.expectedSha256);
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=task-workflow.js.map
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { TASK_WORKFLOW_COMMANDS } from '../../task/workflow-command.js';
|
|
2
|
+
import { parseArtifactCommand, executeArtifactCommand } from '../../task/artifact-command.js';
|
|
3
|
+
import { parseReviewCommand } from '../../task/review-command.js';
|
|
4
|
+
import { prepareLocalArtifact, commitLocalArtifactProvenance } from '../../task/local-artifact-finalization.js';
|
|
5
|
+
import { prepareReviewSummaryCandidate, commitReviewSummaryProvenance } from '../../task/review-finalization.js';
|
|
6
|
+
import { withTaskExecutionLock } from '../../task/task-execution-lock.js';
|
|
7
|
+
import { dispatchHostControlCommand } from '../../host-control/command.js';
|
|
8
|
+
import { assertSandboxTaskSource } from '../workspace-view.js';
|
|
9
|
+
import { appendDiagnosticAudit } from './audit.js';
|
|
10
|
+
import { readTaskArtifact, writeTaskArtifact } from './task-workflow.js';
|
|
11
|
+
import { applyTaskEvent } from '../../task/events.js';
|
|
12
|
+
import { parseTaskEventRequest } from '../../internal/task-event.js';
|
|
13
|
+
function executionResult(result) {
|
|
14
|
+
return { exitCode: result.status === 'failed' || result.status === 'refused' ? 1 : 0, stdout: `${JSON.stringify(result)}\n`, stderr: '' };
|
|
15
|
+
}
|
|
16
|
+
/** The broker already authorized this host executor; domain operations own validation. */
|
|
17
|
+
export async function executeTaskWorkflow(manifest, request, lifecycleRecoveryAttestation = null) {
|
|
18
|
+
let publicationStarted = false;
|
|
19
|
+
try {
|
|
20
|
+
const taskDir = assertSandboxTaskSource(manifest.repoRoot, request.taskId);
|
|
21
|
+
const [command] = TASK_WORKFLOW_COMMANDS[request.operation];
|
|
22
|
+
if (request.operation === 'event') {
|
|
23
|
+
const eventRequest = parseTaskEventRequest(request.args);
|
|
24
|
+
const boundEventRequest = lifecycleRecoveryAttestation && eventRequest.requestId === undefined
|
|
25
|
+
? { ...eventRequest, requestId: lifecycleRecoveryAttestation.lifecycleRequestId }
|
|
26
|
+
: eventRequest;
|
|
27
|
+
const result = applyTaskEvent(boundEventRequest, {
|
|
28
|
+
repoRoot: manifest.repoRoot,
|
|
29
|
+
lifecycleRecoveryAttestation,
|
|
30
|
+
deferLifecycleRecoveryConsumption: true
|
|
31
|
+
});
|
|
32
|
+
return executionResult(result);
|
|
33
|
+
}
|
|
34
|
+
if (command !== 'task-artifact' && command !== 'task-review') {
|
|
35
|
+
publicationStarted = true;
|
|
36
|
+
// Inherit this executor's process group so its existing recovery owns the worker too.
|
|
37
|
+
const result = await dispatchHostControlCommand({
|
|
38
|
+
operation: command, payload: { workingDirectory: manifest.repoRoot, args: request.args }
|
|
39
|
+
});
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
const input = command === 'task-artifact' ? parseArtifactCommand(request.args) : parseReviewCommand(request.args);
|
|
43
|
+
if (command === 'task-artifact' && 'operation' in input && input.operation === 'inspect') {
|
|
44
|
+
return executionResult(executeArtifactCommand(input, { repoRoot: manifest.repoRoot }));
|
|
45
|
+
}
|
|
46
|
+
if ('operation' in input && (input.operation === 'init' || input.operation === 'repair')) {
|
|
47
|
+
return executionResult(executeArtifactCommand(input, { repoRoot: manifest.repoRoot, artifactDir: taskDir }));
|
|
48
|
+
}
|
|
49
|
+
return await withTaskExecutionLock(manifest.repoRoot, request.taskId, `sandbox-control.${request.operation}`, async () => {
|
|
50
|
+
const artifact = await readTaskArtifact(taskDir, { artifact: input.artifact });
|
|
51
|
+
const content = artifact.bytes.toString('utf8');
|
|
52
|
+
let result;
|
|
53
|
+
if ('operation' in input) {
|
|
54
|
+
const { family } = input;
|
|
55
|
+
if (family !== 'analysis' && family !== 'plan' && family !== 'code')
|
|
56
|
+
throw new Error('ARTIFACT_IDENTITY_INVALID');
|
|
57
|
+
const local = { taskRef: request.taskId, family, artifact: input.artifact, repoRoot: manifest.repoRoot };
|
|
58
|
+
const prepared = prepareLocalArtifact(local, content, lifecycleRecoveryAttestation ?? undefined);
|
|
59
|
+
if (prepared.result.status === 'failed')
|
|
60
|
+
return executionResult(commitLocalArtifactProvenance(prepared));
|
|
61
|
+
publicationStarted = true;
|
|
62
|
+
await writeTaskArtifact(taskDir, {
|
|
63
|
+
artifact: artifact.artifact,
|
|
64
|
+
bytes: artifact.bytes,
|
|
65
|
+
expectedSha256: artifact.sha256
|
|
66
|
+
});
|
|
67
|
+
result = commitLocalArtifactProvenance(prepared);
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
if (input.overrideTicket)
|
|
71
|
+
throw new Error('TASK_WORKFLOW_OVERRIDE_UNSUPPORTED');
|
|
72
|
+
const prepared = prepareReviewSummaryCandidate(input, content, { repoRoot: manifest.repoRoot });
|
|
73
|
+
if (input.dryRun || prepared.result.status === 'planned')
|
|
74
|
+
return executionResult(prepared.result);
|
|
75
|
+
if (prepared.result.status === 'failed')
|
|
76
|
+
return executionResult(commitReviewSummaryProvenance(prepared, manifest.repoRoot));
|
|
77
|
+
publicationStarted = true;
|
|
78
|
+
await writeTaskArtifact(taskDir, {
|
|
79
|
+
artifact: input.artifact,
|
|
80
|
+
bytes: Buffer.from(prepared.content, 'utf8'),
|
|
81
|
+
expectedSha256: artifact.sha256
|
|
82
|
+
});
|
|
83
|
+
result = commitReviewSummaryProvenance(prepared, manifest.repoRoot);
|
|
84
|
+
}
|
|
85
|
+
if (result.status !== 'failed')
|
|
86
|
+
appendDiagnosticAudit(manifest, 'task-workflow-artifact-finalized', {
|
|
87
|
+
requestId: request.id, sandboxTaskId: request.taskId, workflowOperation: request.operation,
|
|
88
|
+
artifact: input.artifact, sha256: result.artifactSha256, semanticDigest: result.semanticDigest
|
|
89
|
+
});
|
|
90
|
+
return executionResult({ ...result, changed: true });
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
95
|
+
const code = error?.code ?? /^([A-Z][A-Z0-9_]+)/u.exec(message)?.[1] ?? 'TASK_WORKFLOW_REQUEST_INVALID';
|
|
96
|
+
return executionResult({ status: 'failed', changed: publicationStarted ? null : false, error: { code, message } });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=workflow-executor.js.map
|
|
@@ -19,8 +19,8 @@ Commands:
|
|
|
19
19
|
rm <branch | TASK-id> | --unbound | --purge
|
|
20
20
|
Remove one sandbox; use a full TASK-id for task-bound
|
|
21
21
|
cleanup and a branch for branch-only cleanup. --unbound
|
|
22
|
-
removes completed task-bound and branch-only sandboxes
|
|
23
|
-
active, blocked, and archive tasks are protected.
|
|
22
|
+
removes completed task-bound and branch-only sandboxes
|
|
23
|
+
plus verified auxiliary state; active, blocked, and archive tasks are protected.
|
|
24
24
|
start [--recreate] <branch | TASK-id | N>
|
|
25
25
|
Verify or recover an existing sandbox container;
|
|
26
26
|
optionally replace only the container on failure
|
|
@@ -18,7 +18,9 @@ import { declaredTmpfsSeedEntries, resolveTools, toolConfigDirCandidates } from
|
|
|
18
18
|
import { fetchSandboxRows, selectSandboxContainer, startSandboxContainer } from './commands/list-running.js';
|
|
19
19
|
import { getProcessIdentityState, getProcessStartTime, removePidFileIfMatches } from '../server/process-state.js';
|
|
20
20
|
import { acquireSandboxControlBrokerStartup, garbageCollectSandboxControlRoot, isSandboxControlRootQuiescing, readSandboxControlManifest } from './control/lifecycle.js';
|
|
21
|
-
import {
|
|
21
|
+
import { readSandboxControlStatus } from './control/state.js';
|
|
22
|
+
import { validateSandboxControlIdentity } from './control/identity-sentinel.js';
|
|
23
|
+
import { appendDiagnosticAudit as appendControlDiagnosticAudit } from './control/audit.js';
|
|
22
24
|
import { inspectSandboxControlContainer } from './control/container-identity.js';
|
|
23
25
|
import { SANDBOX_CONTROL_FUTURE_SKEW_MS, SANDBOX_CONTROL_STATUS_STALE_MS } from './control/protocol.js';
|
|
24
26
|
export function worktreeProbeForEngine(engine, probe = runProbe) {
|
|
@@ -61,6 +63,17 @@ export async function startSandboxControlBroker(repoRoot, manifestPath) {
|
|
|
61
63
|
const internalCli = path.resolve(directory, '..', '..', 'bin', `internal-cli${extension}`);
|
|
62
64
|
const manifest = readSandboxControlManifest(manifestPath);
|
|
63
65
|
const root = path.dirname(manifestPath);
|
|
66
|
+
const identity = validateSandboxControlIdentity({
|
|
67
|
+
publicStatusDir: manifest.publicStatusDir,
|
|
68
|
+
root,
|
|
69
|
+
mode: manifest.mode,
|
|
70
|
+
taskId: manifest.taskId,
|
|
71
|
+
generation: manifest.generation,
|
|
72
|
+
controlRootId: manifest.controlRootId
|
|
73
|
+
});
|
|
74
|
+
if (identity.state !== 'valid') {
|
|
75
|
+
throw new Error(`SANDBOX_CONTROL_IDENTITY_${identity.state.replaceAll('-', '_').toUpperCase()}`);
|
|
76
|
+
}
|
|
64
77
|
if (isSandboxControlRootQuiescing(root))
|
|
65
78
|
throw new Error('SANDBOX_CONTROL_QUIESCING');
|
|
66
79
|
const brokerPath = path.join(root, 'broker.json');
|
|
@@ -130,8 +143,8 @@ export async function startSandboxControlBroker(repoRoot, manifestPath) {
|
|
|
130
143
|
}
|
|
131
144
|
}
|
|
132
145
|
if (replacedBrokerRecord) {
|
|
133
|
-
|
|
134
|
-
|
|
146
|
+
appendControlDiagnosticAudit(manifest, 'broker-observed-crash');
|
|
147
|
+
appendControlDiagnosticAudit(manifest, 'broker-restart');
|
|
135
148
|
}
|
|
136
149
|
child = spawn(process.execPath, nodeEntryArgs(internalCli, ['sandbox-control', 'serve', '--manifest', manifestPath]), { cwd: repoRoot, detached: true, stdio: 'ignore' });
|
|
137
150
|
}
|
|
@@ -168,6 +181,17 @@ async function ensureSandboxControlBroker(params) {
|
|
|
168
181
|
if (!fs.existsSync(control.manifestPath))
|
|
169
182
|
return;
|
|
170
183
|
const validatedManifest = readSandboxControlManifest(control.manifestPath);
|
|
184
|
+
const identity = validateSandboxControlIdentity({
|
|
185
|
+
publicStatusDir: validatedManifest.publicStatusDir,
|
|
186
|
+
root: control.root,
|
|
187
|
+
mode: validatedManifest.mode,
|
|
188
|
+
taskId: validatedManifest.taskId,
|
|
189
|
+
generation: validatedManifest.generation,
|
|
190
|
+
controlRootId: validatedManifest.controlRootId
|
|
191
|
+
});
|
|
192
|
+
if (identity.state !== 'valid') {
|
|
193
|
+
throw new Error(`SANDBOX_CONTROL_IDENTITY_${identity.state.replaceAll('-', '_').toUpperCase()}`);
|
|
194
|
+
}
|
|
171
195
|
const containerObservation = await inspectSandboxControlContainer(validatedManifest);
|
|
172
196
|
if (containerObservation.state === 'unknown') {
|
|
173
197
|
throw new Error(`SANDBOX_CONTROL_CONTAINER_UNKNOWN: ${containerObservation.reason}`);
|
|
@@ -514,7 +538,7 @@ function expectedMounts(params) {
|
|
|
514
538
|
? {}
|
|
515
539
|
: {
|
|
516
540
|
runtimeDir: control.runtimeDir,
|
|
517
|
-
taskSources: taskSources.
|
|
541
|
+
taskSources: taskSources.accessiblePaths,
|
|
518
542
|
taskId: taskId
|
|
519
543
|
})
|
|
520
544
|
}).map((mount) => ({
|