summaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
authortanaka takashi <isazaka@gmail.com>2026-08-13 19:15:01 +0900
committertanaka takashi <isazaka@gmail.com>2026-08-13 19:15:01 +0900
commit36ae1a4cba791ec633a81b38b1d8345af8487276 (patch)
tree91cef96efac7eb8c2754658ce5af5181b1a15898 /scripts
parent76fdcb43585a4c4d288c1026d318a0ddbe3e26ab (diff)
feat!: unify sandbox configuration and permission application
Replace the split executable approval and sandbox resolution workflows with schema_version 2 and a YAML-driven execution.entries model. Resolve workspace, mount, executable, runtime, network, and resource settings into one config-scoped Applied Configuration snapshot protected by a user-only DACL and written with atomic replacement. Reduce the public CLI to implicit server startup, show, and apply. Remove serve/check, executable and sandbox subcommands, and configuration override flags including --config, --lang, --transport, --path, and --runtime-root. Add migration diagnostics for every removed schema and command surface, and keep server preflight mandatory for both stdio and HTTP. Update effective-policy compilation to validate the applied fingerprint, resolved paths, and executable hashes before startup. Preserve fail-closed behavior while allowing execution entries and runtime roots inside a development workspace to inherit that workspace capability without creating an impossible narrower ACL boundary. Rewrite the README around the consolidated permission matrix and document configuration, application, store location, startup, transport-specific elevation, Python 3.14, PowerShell, and cmd usage. Align specifications, design, implementation plan, traceability, audit report, localization, publish scripts, and operational verification with the breaking schema. Verification: Release build completed with zero warnings and errors; Agent.Core.Tests 104/104 and Agent.Planner.Tests 10/10 passed; administrator Windows Security/Recovery 56/56 and README operational 19/19 passed with no skips; published HTTP MCP completed a cmd/curl/venv-Python task with network deny and zero ACL, WFP, journal, backup, or process residue.
Diffstat (limited to 'scripts')
-rw-r--r--scripts/E2EClient/Program.cs2
-rw-r--r--scripts/Operational-E2E.ps134
-rw-r--r--scripts/Publish-Release.ps14
-rw-r--r--scripts/Restart-PublishedServer.ps12
-rw-r--r--scripts/Run-Final-ElevatedVerification.ps14
-rw-r--r--scripts/Verify-PublishedHttp.ps115
-rw-r--r--scripts/Verify-Readme.ps1261
7 files changed, 98 insertions, 224 deletions
diff --git a/scripts/E2EClient/Program.cs b/scripts/E2EClient/Program.cs
index ee7bb75..8733cdf 100644
--- a/scripts/E2EClient/Program.cs
+++ b/scripts/E2EClient/Program.cs
@@ -13,7 +13,7 @@ IClientTransport transport = args[0] == "stdio"
{
Name = "agent-mcp-operational-e2e",
Command = Path.GetFullPath(args[1]),
- Arguments = ["serve", "--config", Path.GetFullPath(args[2]), "--lang", "ja", "--transport", "stdio"],
+ Arguments = [Path.GetFullPath(args[2])],
WorkingDirectory = Path.GetDirectoryName(Path.GetFullPath(args[1]))!
})
: new HttpClientTransport(new HttpClientTransportOptions
diff --git a/scripts/Operational-E2E.ps1 b/scripts/Operational-E2E.ps1
index b253198..cea1c75 100644
--- a/scripts/Operational-E2E.ps1
+++ b/scripts/Operational-E2E.ps1
@@ -17,8 +17,7 @@ $bundleRoot = Join-Path $root 'bundle'
$fixture = Join-Path $root 'fixture'
$alias = 'python-e2e-' + [Guid]::NewGuid().ToString('N').Substring(0, 12)
$passed = $false
-$approved = $false
-$resolutionStorePath = $null
+$appliedStorePath = $null
$server = $null
New-Item -ItemType Directory -Path $bundleRoot,$fixture | Out-Null
$fixtureAclBefore = (Get-Acl -LiteralPath $fixture).Sddl
@@ -40,6 +39,8 @@ try {
$pythonVersion = (& $python --version 2>&1 | Out-String).Trim()
if ($pythonVersion -notmatch '^Python 3\.14\.') { throw "Operational E2E requires Python 3.14; resolved '$pythonVersion'." }
$runtimeRoot = Split-Path $python
+ $pythonYaml = $pythonLogical.Replace('\','/')
+ $runtimeYaml = $runtimeRoot.Replace('\','/')
Set-Content -LiteralPath (Join-Path $fixture 'README.md') -Encoding utf8NoBOM -Value @'
# Graph metrics fixture
@@ -50,7 +51,7 @@ produce a deterministic topological order, detect cycles, and calculate the weig
$fixtureYaml = $fixture.Replace('\','/')
$config = @"
-schema_version: 1
+schema_version: 2
application:
language: ja
mcp:
@@ -83,9 +84,10 @@ sandbox:
- $fixtureYaml
mounts: []
execution:
- executables:
- - $alias
- shells: []
+ entries:
+ - alias: $alias
+ path: $pythonYaml
+ runtime_root: $runtimeYaml
network:
access: none
local_endpoints: []
@@ -112,15 +114,12 @@ logging:
"@
Set-Content -LiteralPath $configPath -Value $config -Encoding utf8NoBOM
- & $agent executable approve $alias --path $pythonLogical --runtime-root $runtimeRoot --yes --config $configPath --lang en
- if ($LASTEXITCODE -ne 0) { throw 'Executable approval failed.' }
- $approved = $true
- & $agent sandbox confirm --yes --config $configPath --lang en
- if ($LASTEXITCODE -ne 0) { throw 'Sandbox Environment Resolution confirmation failed.' }
+ & $agent apply $configPath --yes
+ if ($LASTEXITCODE -ne 0) { throw 'Applied Configuration failed.' }
$normalizedConfig = [IO.Path]::GetFullPath($configPath).ToUpperInvariant()
$hash = [Convert]::ToHexString([Security.Cryptography.SHA256]::HashData([Text.Encoding]::UTF8.GetBytes($normalizedConfig)))
- $resolutionStorePath = Join-Path $env:LOCALAPPDATA "AgentSandboxMcp\environment-resolutions\$hash.json"
- & $agent sandbox show --format json --config $configPath --lang en | Out-Null
+ $appliedStorePath = Join-Path $env:LOCALAPPDATA "AgentSandboxMcp\applied-configurations\$hash.json"
+ & $agent show $configPath | Out-Null
if ($LASTEXITCODE -ne 0) { throw 'Effective Sandbox Policy show failed.' }
$request = @"
@@ -138,7 +137,7 @@ README.md の読取後、2ファイルの作成、最初のテスト実行、失
if ($LASTEXITCODE -ne 0) { throw 'E2E stdio client failed.' }
}
else {
- $server = Start-Process -FilePath $agent -ArgumentList @('serve','--config',$configPath,'--lang','ja','--transport','http') -WorkingDirectory $bundle -WindowStyle Hidden -PassThru
+ $server = Start-Process -FilePath $agent -ArgumentList @($configPath) -WorkingDirectory $bundle -WindowStyle Hidden -PassThru
$httpAuthority = if ($HttpHost -eq '::1') { '[::1]' } else { $HttpHost }
$endpoint = "http://${httpAuthority}:18080/mcp"
$ready = $false
@@ -183,11 +182,8 @@ catch {
}
finally {
if ($server -and -not $server.HasExited) { $server.Kill($true); $server.WaitForExit() }
- if ($approved -and (Test-Path -LiteralPath $agent)) {
- & $agent executable revoke $alias --yes --config $configPath --lang en | Out-Null
- }
- if ($resolutionStorePath -and (Test-Path -LiteralPath $resolutionStorePath)) {
- Remove-Item -LiteralPath $resolutionStorePath -Force
+ if ($appliedStorePath -and (Test-Path -LiteralPath $appliedStorePath)) {
+ Remove-Item -LiteralPath $appliedStorePath -Force
}
if ($passed -and (Test-Path -LiteralPath $root)) { Remove-Item -LiteralPath $root -Recurse -Force }
}
diff --git a/scripts/Publish-Release.ps1 b/scripts/Publish-Release.ps1
index d794bb5..41bdfa3 100644
--- a/scripts/Publish-Release.ps1
+++ b/scripts/Publish-Release.ps1
@@ -66,8 +66,8 @@ try {
if (-not $SkipRuntimeCheck) {
Push-Location $freshBundle
try {
- & .\agent-mcp.exe sandbox resolve --config .\agent.yaml --lang en
- if ($LASTEXITCODE -ne 0) { throw "Bundle resolution check failed with exit code $LASTEXITCODE." }
+ & .\agent-mcp.exe show .\agent.yaml
+ if ($LASTEXITCODE -ne 0) { throw "Bundle configuration check failed with exit code $LASTEXITCODE." }
}
finally { Pop-Location }
}
diff --git a/scripts/Restart-PublishedServer.ps1 b/scripts/Restart-PublishedServer.ps1
index 58dd928..d4386f4 100644
--- a/scripts/Restart-PublishedServer.ps1
+++ b/scripts/Restart-PublishedServer.ps1
@@ -26,5 +26,5 @@ if ($StopOnly) { exit 0 }
if (-not (Test-Path -LiteralPath $agent) -or -not (Test-Path -LiteralPath $configuration)) {
throw 'Published agent-mcp executable or configuration is missing.'
}
-$started = Start-Process -FilePath $agent -ArgumentList @('serve', '--config', $configuration) -WorkingDirectory $bundle -WindowStyle Hidden -PassThru
+$started = Start-Process -FilePath $agent -ArgumentList @($configuration) -WorkingDirectory $bundle -WindowStyle Hidden -PassThru
Write-Output "Started published agent-mcp PID $($started.Id)."
diff --git a/scripts/Run-Final-ElevatedVerification.ps1 b/scripts/Run-Final-ElevatedVerification.ps1
index 627bd65..71135c2 100644
--- a/scripts/Run-Final-ElevatedVerification.ps1
+++ b/scripts/Run-Final-ElevatedVerification.ps1
@@ -47,8 +47,8 @@ $windowsTests = Join-Path $repository 'build\Release\Agent.Windows.Tests\net10.0
$coreTests = Join-Path $repository 'build\Release\Agent.Core.Tests\net10.0-windows10.0.26100.0\Agent.Core.Tests.exe'
$plannerTests = Join-Path $repository 'build\Release\Agent.Planner.Tests\net10.0\Agent.Planner.Tests.exe'
-Invoke-VerificationStep 'bundle-check' {
- & (Join-Path $bundle 'agent-mcp.exe') check --config (Join-Path $bundle 'agent.yaml') --lang en
+Invoke-VerificationStep 'bundle-show' {
+ & (Join-Path $bundle 'agent-mcp.exe') show (Join-Path $bundle 'agent.yaml')
}
Invoke-VerificationStep 'windows-security-tests' { & $windowsTests }
Invoke-VerificationStep 'core-unit-integration-tests' { & $coreTests }
diff --git a/scripts/Verify-PublishedHttp.ps1 b/scripts/Verify-PublishedHttp.ps1
index a02a28b..7dd5fe2 100644
--- a/scripts/Verify-PublishedHttp.ps1
+++ b/scripts/Verify-PublishedHttp.ps1
@@ -29,6 +29,8 @@ $runRoot = Join-Path $testRootPath ('agent-mcp-http-cmd-' + [Guid]::NewGuid().To
$runRootForward = $runRoot.Replace('\', '/')
$endpoint = 'http://127.0.0.1:8080/mcp'
$log = Join-Path $evidence 'verification.log'
+$serverOut = Join-Path $evidence 'server.stdout.log'
+$serverErr = Join-Path $evidence 'server.stderr.log'
$server = $null
New-Item -ItemType Directory -Force -Path $evidence | Out-Null
New-Item -ItemType Directory -Path $runRoot | Out-Null
@@ -47,14 +49,17 @@ run-probe.cmdは次を順番に実行し、どれかが失敗したら非0で終
"@
try {
- & $agent check --config $config --lang ja 2>&1 | Tee-Object -FilePath $log
- if ($LASTEXITCODE -ne 0) { throw "Published check failed with exit code $LASTEXITCODE." }
+ & $agent show $config 2>&1 | Tee-Object -FilePath $log
+ if ($LASTEXITCODE -ne 0) { throw "Published configuration show failed with exit code $LASTEXITCODE." }
- $server = Start-Process -FilePath $agent -ArgumentList @('serve','--config',$config,'--lang','ja','--transport','http') `
- -WorkingDirectory $bundle -WindowStyle Hidden -PassThru
+ $server = Start-Process -FilePath $agent -ArgumentList @($config) -WorkingDirectory $bundle `
+ -WindowStyle Hidden -RedirectStandardOutput $serverOut -RedirectStandardError $serverErr -PassThru
$ready = $false
for ($attempt = 0; $attempt -lt 100; $attempt++) {
- if ($server.HasExited) { throw "Published server exited before readiness: $($server.ExitCode)" }
+ if ($server.HasExited) {
+ $diagnostic = if (Test-Path -LiteralPath $serverErr) { Get-Content -LiteralPath $serverErr -Raw } else { '' }
+ throw "Published server exited before readiness: $($server.ExitCode) $diagnostic"
+ }
try {
$response = Invoke-WebRequest -Uri $endpoint -Method Get -SkipHttpErrorCheck
if ($response.StatusCode -eq 405) { $ready = $true; break }
diff --git a/scripts/Verify-Readme.ps1 b/scripts/Verify-Readme.ps1
index f67f640..42c8b8f 100644
--- a/scripts/Verify-Readme.ps1
+++ b/scripts/Verify-Readme.ps1
@@ -1,255 +1,128 @@
param(
- [Parameter(Mandatory)]
- [string]$TestRoot,
- [Parameter(Mandatory)]
- [string]$VenvPython,
+ [Parameter(Mandatory)][string]$TestRoot,
+ [Parameter(Mandatory)][string]$VenvPython,
[string]$EvidenceRoot = 'artifacts\evidence\readme-verification',
[switch]$CliOnly
)
$ErrorActionPreference = 'Stop'
-$OutputEncoding = [Text.UTF8Encoding]::new($false)
-[Console]::OutputEncoding = $OutputEncoding
$repository = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path
$testRootPath = (Resolve-Path -LiteralPath $TestRoot).Path
$venvPythonPath = (Resolve-Path -LiteralPath $VenvPython).Path
$evidence = [IO.Path]::GetFullPath((Join-Path $repository $EvidenceRoot))
-if (-not $evidence.StartsWith($repository + [IO.Path]::DirectorySeparatorChar, [StringComparison]::OrdinalIgnoreCase)) {
- throw 'Evidence output must remain inside the repository.'
-}
-$administrator = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole(
- [Security.Principal.WindowsBuiltInRole]::Administrator)
-if (-not $administrator -and -not $CliOnly) { throw 'README verification must run as Windows Administrator unless -CliOnly is specified.' }
-if (-not (Test-Path -LiteralPath $venvPythonPath -PathType Leaf)) { throw 'VenvPython must be an existing file.' }
+if (-not $evidence.StartsWith($repository + [IO.Path]::DirectorySeparatorChar, [StringComparison]::OrdinalIgnoreCase)) { throw 'Evidence output must remain inside the repository.' }
+$administrator = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
+if (-not $administrator -and -not $CliOnly) { throw 'README operational verification must run as Windows Administrator.' }
$pwshPath = (Get-Command pwsh.exe -ErrorAction Stop).Source
-$pwshRuntime = Split-Path $pwshPath
$cmdPath = $env:ComSpec
$venvRoot = Split-Path (Split-Path $venvPythonPath)
-$pyvenv = Get-Content -LiteralPath (Join-Path $venvRoot 'pyvenv.cfg')
-$pythonRuntime = (($pyvenv | Where-Object { $_ -match '^home\s*=' }) -split '=', 2)[1].Trim()
-if (-not (Test-Path -LiteralPath $pythonRuntime -PathType Container)) { throw "Python Runtime Root does not exist: $pythonRuntime" }
+$pythonRuntime = (((Get-Content -LiteralPath (Join-Path $venvRoot 'pyvenv.cfg')) | Where-Object { $_ -match '^home\s*=' }) -split '=', 2)[1].Trim()
$basePythonPath = Join-Path $pythonRuntime 'python.exe'
if (-not (Test-Path -LiteralPath $basePythonPath -PathType Leaf)) { throw "Base Python does not exist: $basePythonPath" }
New-Item -ItemType Directory -Force -Path $evidence | Out-Null
-$runRoot = Join-Path $testRootPath ('agent-mcp-readme-cli-' + [Guid]::NewGuid().ToString('N'))
+$runRoot = Join-Path $testRootPath ('agent-mcp-readme-' + [Guid]::NewGuid().ToString('N'))
New-Item -ItemType Directory -Path $runRoot | Out-Null
$agent = Join-Path $repository 'build\Release\Agent.App\net10.0-windows10.0.26100.0\agent-mcp.exe'
$windowsTests = Join-Path $repository 'build\Release\Agent.Windows.Tests\net10.0-windows10.0.26100.0\Agent.Windows.Tests.exe'
$configPath = Join-Path $runRoot 'agent.yaml'
$logPath = Join-Path $evidence 'verification.log'
-$failed = $false
-$aliases = @(
- ('readme-python-' + [Guid]::NewGuid().ToString('N').Substring(0, 8))
- ('readme-powershell-' + [Guid]::NewGuid().ToString('N').Substring(0, 8))
- ('readme-cmd-' + [Guid]::NewGuid().ToString('N').Substring(0, 8))
-)
-$pythonAlias, $powershellAlias, $cmdAlias = $aliases
-$listener = [Net.Sockets.TcpListener]::new([Net.IPAddress]::Loopback, 0)
-$listener.Start()
-$httpPort = ([Net.IPEndPoint]$listener.LocalEndpoint).Port
-$listener.Stop()
-$resolutionStorePath = $null
$server = $null
+$failed = $false
-function Invoke-Step {
- param([string]$Name, [scriptblock]$Action)
+function Invoke-Step([string]$Name, [scriptblock]$Action) {
"START $Name $(Get-Date -Format o)" | Tee-Object -FilePath $logPath -Append
try {
$global:LASTEXITCODE = 0
& $Action *>&1 | Tee-Object -FilePath $logPath -Append
if ($LASTEXITCODE -ne 0) { throw "$Name exited with $LASTEXITCODE" }
- "PASS $Name $(Get-Date -Format o)" | Tee-Object -FilePath $logPath -Append
- }
- catch {
- $_ | Out-String | Tee-Object -FilePath $logPath -Append
- $script:failed = $true
- "FAIL $Name $(Get-Date -Format o)" | Tee-Object -FilePath $logPath -Append
- }
+ "PASS $Name" | Tee-Object -FilePath $logPath -Append
+ } catch { $script:failed = $true; "FAIL $Name $($_.Exception.Message)" | Tee-Object -FilePath $logPath -Append }
}
-function Invoke-ExpectedFailure {
- param([string]$Name, [string[]]$Arguments)
- "START $Name $(Get-Date -Format o)" | Tee-Object -FilePath $logPath -Append
+function Invoke-ExpectedFailure([string]$Name, [string[]]$Arguments) {
& $agent @Arguments *>&1 | Tee-Object -FilePath $logPath -Append
- if ($LASTEXITCODE -ne 2) {
- $script:failed = $true
- "FAIL $Name expected_exit=2 actual_exit=$LASTEXITCODE" | Tee-Object -FilePath $logPath -Append
- }
- else { "PASS $Name expected_exit=2" | Tee-Object -FilePath $logPath -Append }
-}
-
-function Start-And-ProbeServer {
- param([bool]$ExplicitServe)
- $arguments = if ($ExplicitServe) {
- @('serve', '--config', $configPath, '--lang', 'ja', '--transport', 'http')
- } else {
- @('--config', $configPath, '--lang', 'ja', '--transport', 'http')
- }
- $script:server = Start-Process -FilePath $agent -ArgumentList $arguments -WorkingDirectory (Split-Path $agent) -WindowStyle Hidden -PassThru
- $endpoint = "http://127.0.0.1:$httpPort/mcp"
- for ($attempt = 0; $attempt -lt 100; $attempt++) {
- if ($script:server.HasExited) { throw "Server exited before becoming ready: $($script:server.ExitCode)" }
- try {
- $response = Invoke-WebRequest -Uri $endpoint -Method Get -SkipHttpErrorCheck
- if ($response.StatusCode -eq 405) { return }
- } catch { }
- Start-Sleep -Milliseconds 100
- }
- throw 'Server did not become ready.'
+ if ($LASTEXITCODE -ne 2) { $script:failed = $true; "FAIL $Name expected=2 actual=$LASTEXITCODE" | Tee-Object -FilePath $logPath -Append }
+ else { "PASS $Name" | Tee-Object -FilePath $logPath -Append }
}
try {
- $forwardRoot = $testRootPath.Replace('\', '/')
- $forwardPrompt = (Join-Path $repository 'prompts\user-system.md').Replace('\', '/')
- $forwardSkills = (Join-Path $repository 'skills').Replace('\', '/')
- $forwardLocales = (Join-Path $repository 'config\locales').Replace('\', '/')
- $forwardLogs = (Join-Path $runRoot 'logs').Replace('\', '/')
+ $listener = [Net.Sockets.TcpListener]::new([Net.IPAddress]::Loopback, 0); $listener.Start()
+ $httpPort = ([Net.IPEndPoint]$listener.LocalEndpoint).Port; $listener.Stop()
+ $forwardRoot = $testRootPath.Replace('\','/')
+ $forwardPrompt = (Join-Path $repository 'prompts\user-system.md').Replace('\','/')
+ $forwardSkills = (Join-Path $repository 'skills').Replace('\','/')
+ $forwardLocales = (Join-Path $repository 'config\locales').Replace('\','/')
+ $forwardLogs = (Join-Path $runRoot 'logs').Replace('\','/')
+ $pythonYaml = $basePythonPath.Replace('\','/'); $runtimeYaml = $pythonRuntime.Replace('\','/')
+ $pwshYaml = $pwshPath.Replace('\','/'); $cmdYaml = $cmdPath.Replace('\','/')
@"
-schema_version: 1
-application:
- language: en
+schema_version: 2
+application: { language: en }
mcp:
transport: http
- http:
- host: 127.0.0.1
- port: $httpPort
- images:
- max_count: 4
- max_bytes_each: 10485760
- max_total_bytes: 20971520
- tasks:
- time_to_live_hours: 24
- poll_interval_ms: 1000
-agent:
- user_system_prompt: $forwardPrompt
- max_iterations: 8
- same_failure_limit: 2
-ollama:
- endpoint: http://127.0.0.1:11434
- model: deepseek-v4-flash:cloud
- think: true
- keep_alive: 5m
- options:
- num_ctx: 32768
- temperature: 0
- extra_options: {}
-planner:
- max_tasks: 8
+ http: { host: 127.0.0.1, port: $httpPort }
+agent: { user_system_prompt: $forwardPrompt, max_iterations: 8, same_failure_limit: 2 }
+ollama: { endpoint: 'http://127.0.0.1:11434', model: 'deepseek-v4-flash:cloud', think: true }
+planner: { max_tasks: 8 }
sandbox:
profile: development
- workspaces:
- - $forwardRoot
+ workspaces: [$forwardRoot]
mounts: []
execution:
- executables:
- - $pythonAlias
- shells:
- - $powershellAlias
- - $cmdAlias
- network:
- access: none
- local_endpoints: []
- restricted_endpoints: []
- resources:
- profile: constrained
-file_edit:
- memory_backup_limit_mb: 16
-limits:
- whole_read_bytes: 1048576
- search_max_results: 1000
- tail_max_lines: 10000
- context_summary_ratio: 0.8
-localization:
- directory: $forwardLocales
- fallback: en
-skills:
- directory: $forwardSkills
-logging:
- directory: $forwardLogs
- level: information
- http_requests: true
- max_file_bytes: 10485760
- retained_files: 5
+ entries:
+ - { alias: python, path: $pythonYaml, runtime_root: $runtimeYaml }
+ - { alias: powershell, path: '$pwshYaml' }
+ - { alias: cmd, path: $cmdYaml }
+ network: { access: none, local_endpoints: [], restricted_endpoints: [] }
+ resources: { profile: constrained }
+localization: { directory: $forwardLocales, fallback: en }
+skills: { directory: $forwardSkills }
+logging: { directory: $forwardLogs, level: information, max_file_bytes: 10485760, retained_files: 5 }
"@ | Set-Content -LiteralPath $configPath -Encoding utf8NoBOM
- @(
- "timestamp=$(Get-Date -Format o)",
- "commit=$(git -C $repository rev-parse HEAD)",
- "administrator=$administrator",
- "os=$([Environment]::OSVersion.VersionString)",
- "venv_python=$venvPythonPath",
- "venv_version=$(& $venvPythonPath --version)",
- "powershell=$pwshPath",
- "cmd=$cmdPath"
- ) | Set-Content -LiteralPath (Join-Path $evidence 'metadata.txt') -Encoding utf8NoBOM
-
- Invoke-Step 'cli-sandbox-resolve' { & $agent sandbox resolve --config $configPath --lang ja }
- Invoke-Step 'cli-sandbox-confirm-yes' { & $agent sandbox confirm --yes --config $configPath --lang ja }
$normalizedConfig = [IO.Path]::GetFullPath($configPath).ToUpperInvariant()
$hash = [Convert]::ToHexString([Security.Cryptography.SHA256]::HashData([Text.Encoding]::UTF8.GetBytes($normalizedConfig)))
- $resolutionStorePath = Join-Path $env:LOCALAPPDATA "AgentSandboxMcp\environment-resolutions\$hash.json"
- Invoke-Step 'cli-executable-resolve-path' { & $agent executable resolve $pythonAlias --path $basePythonPath --config $configPath --lang ja }
- Invoke-Step 'cli-executable-approve-runtime-root-yes' { & $agent executable approve $pythonAlias --path $basePythonPath --runtime-root $pythonRuntime --yes --config $configPath --lang ja }
- Invoke-Step 'cli-shell-resolve-powershell' { & $agent executable resolve $powershellAlias --path $pwshPath --config $configPath --lang en }
- Invoke-Step 'cli-shell-approve-runtime-root-yes' { & $agent executable approve $powershellAlias --path $pwshPath --runtime-root $pwshRuntime --yes --config $configPath --lang en }
- Invoke-Step 'cli-shell-resolve-cmd' { & $agent executable resolve $cmdAlias --path $cmdPath --config $configPath --lang en }
- Invoke-Step 'cli-shell-approve-cmd-no-runtime' { & $agent executable approve $cmdAlias --path $cmdPath --no-runtime --yes --config $configPath --lang en }
- Invoke-Step 'cli-executable-list' { & $agent executable list --config $configPath --lang ja }
- Invoke-Step 'cli-sandbox-show-text-default-format' { & $agent sandbox show --config $configPath --lang en }
- Invoke-Step 'cli-sandbox-show-json' { & $agent sandbox show --format json --config $configPath --lang ja }
- if ($CliOnly) {
- Invoke-ExpectedFailure 'cli-check-detects-non-admin' @('check', '--config', $configPath, '--lang', 'ja')
- }
- else {
- Invoke-Step 'cli-check' { & $agent check --config $configPath --lang ja }
- }
+ $appliedStorePath = Join-Path $env:LOCALAPPDATA "AgentSandboxMcp\applied-configurations\$hash.json"
- Invoke-ExpectedFailure 'cli-invalid-transport' @('serve', '--config', $configPath, '--transport', 'invalid')
- Invoke-ExpectedFailure 'cli-invalid-format' @('sandbox', 'show', '--format', 'xml', '--config', $configPath)
- Invoke-ExpectedFailure 'cli-management-missing-config' @('sandbox', 'resolve')
- Invoke-ExpectedFailure 'cli-mutually-exclusive-runtime-options' @('executable', 'approve', $pythonAlias, '--path', $basePythonPath, '--runtime-root', $pythonRuntime, '--no-runtime', '--config', $configPath)
+ Invoke-Step 'cli-show-before-apply' { & $agent show $configPath }
+ Invoke-Step 'cli-apply-yes' { & $agent apply $configPath --yes }
+ Invoke-Step 'cli-show-applied' { & $agent show $configPath }
+ Invoke-ExpectedFailure 'removed-serve' @('serve')
+ Invoke-ExpectedFailure 'removed-check' @('check')
+ Invoke-ExpectedFailure 'removed-config-option' @('--config', $configPath)
+ Invoke-ExpectedFailure 'removed-language-option' @('--lang', 'ja')
+ Invoke-ExpectedFailure 'removed-transport-option' @('--transport', 'http')
+ Invoke-ExpectedFailure 'removed-executable-command' @('executable', 'list')
+ Invoke-ExpectedFailure 'removed-sandbox-command' @('sandbox', 'show')
if (-not $CliOnly) {
- Invoke-Step 'cli-serve-explicit-http' {
- Start-And-ProbeServer $true
- $script:server.Kill($true)
- $script:server.WaitForExit()
- $script:server = $null
- }
- Invoke-Step 'cli-serve-implicit-option-first' {
- Start-And-ProbeServer $false
- $script:server.Kill($true)
- $script:server.WaitForExit()
- $script:server = $null
+ Invoke-Step 'implicit-http-start' {
+ $script:server = Start-Process -FilePath $agent -ArgumentList @($configPath) -WorkingDirectory (Split-Path $agent) -WindowStyle Hidden -PassThru
+ $endpoint = "http://127.0.0.1:$httpPort/mcp"
+ for ($attempt=0; $attempt -lt 100; $attempt++) {
+ if ($script:server.HasExited) { throw "Server exited: $($script:server.ExitCode)" }
+ try { $response = Invoke-WebRequest -Uri $endpoint -Method Get -SkipHttpErrorCheck; if ($response.StatusCode -eq 405) { break } } catch { }
+ Start-Sleep -Milliseconds 100
+ }
+ if ($attempt -eq 100) { throw 'Server did not become ready.' }
+ $script:server.Kill($true); $script:server.WaitForExit(); $script:server = $null
}
-
$env:AGENT_MCP_MONKEY_ROOT = $testRootPath
$env:AGENT_MCP_TEST_VENV_PYTHON = $venvPythonPath
$env:AGENT_MCP_TEST_POWERSHELL = $pwshPath
- $env:AGENT_MCP_TEST_POWERSHELL_RUNTIME = $pwshRuntime
+ $env:AGENT_MCP_TEST_POWERSHELL_RUNTIME = Split-Path $pwshPath
$env:AGENT_MCP_TEST_CMD = $cmdPath
- Invoke-Step 'readme-operational-windows-tests' {
- & $windowsTests -class Agent.Windows.Tests.ReadmeOperationalTests -parallel none -failSkips
- }
+ Invoke-Step 'readme-operational-windows-tests' { & $windowsTests -class Agent.Windows.Tests.ReadmeOperationalTests -parallel none -failSkips }
}
- Invoke-Step 'cli-executable-revoke-python' { & $agent executable revoke $pythonAlias --yes --config $configPath --lang ja }
- Invoke-Step 'cli-executable-revoke-powershell' { & $agent executable revoke $powershellAlias --yes --config $configPath --lang en }
- Invoke-Step 'cli-executable-revoke-cmd' { & $agent executable revoke $cmdAlias --yes --config $configPath --lang en }
-}
-finally {
+} finally {
if ($server -and -not $server.HasExited) { $server.Kill($true); $server.WaitForExit() }
- foreach ($alias in $aliases) {
- & $agent executable revoke $alias --yes --config $configPath --lang en 2>$null | Out-Null
- }
- if ($resolutionStorePath -and (Test-Path -LiteralPath $resolutionStorePath)) { Remove-Item -LiteralPath $resolutionStorePath -Force }
+ if ($appliedStorePath -and (Test-Path -LiteralPath $appliedStorePath)) { Remove-Item -LiteralPath $appliedStorePath -Force }
if (Test-Path -LiteralPath $runRoot) {
- $resolvedRunRoot = (Resolve-Path -LiteralPath $runRoot).Path
- if (-not $resolvedRunRoot.StartsWith($testRootPath.TrimEnd('\') + '\', [StringComparison]::OrdinalIgnoreCase)) {
- throw 'Refusing to clean a test directory outside TestRoot.'
- }
- Remove-Item -LiteralPath $resolvedRunRoot -Recurse -Force
+ $resolved = (Resolve-Path -LiteralPath $runRoot).Path
+ if (-not $resolved.StartsWith($testRootPath.TrimEnd('\') + '\', [StringComparison]::OrdinalIgnoreCase)) { throw 'Refusing to clean outside TestRoot.' }
+ Remove-Item -LiteralPath $resolved -Recurse -Force
}
Remove-Item Env:AGENT_MCP_MONKEY_ROOT,Env:AGENT_MCP_TEST_VENV_PYTHON,Env:AGENT_MCP_TEST_POWERSHELL,Env:AGENT_MCP_TEST_POWERSHELL_RUNTIME,Env:AGENT_MCP_TEST_CMD -ErrorAction SilentlyContinue
}