agent-v2.ps1

# agent-v2.ps1 - Agent with structured tools
# agent-v2.ps1 "Create a new file called 'test.txt' with the content 'Hello, World!', then read it back and display the contents."
param(
    [string]$Task
)

$apiKey = $env:OPENAI_API_KEY
if (-not $apiKey) { Write-Host "Set OPENAI_API_KEY"; exit }

$tools = @(
    @{
        type     = "function"
        function = @{
            name        = "Read-File"
            description = "Read the contents of a file"
            parameters  = @{
                type       = "object"
                properties = @{
                    path = @{ type = "string"; description = "Path to the file" }
                }
                required   = @("path")
            }
        }
    }
    @{
        type     = "function"
        function = @{
            name        = "Write-File"
            description = "Write content to a file"
            parameters  = @{
                type       = "object"
                properties = @{
                    path    = @{ type = "string"; description = "Path to the file" }
                    content = @{ type = "string"; description = "Content to write" }
                }
                required   = @("path", "content")
            }
        }
    }
    @{
        type     = "function"
        function = @{
            name        = "Run-Command"
            description = "Run a PowerShell command"
            parameters  = @{
                type       = "object"
                properties = @{
                    command = @{ type = "string"; description = "The command to run" }
                }
                required   = @("command")
            }
        }
    }
)

function Execute-Tool {
    param([string]$Name, $ToolInput)
    
    switch ($Name) {
        "Read-File" {
            try {
                $content = Get-Content $ToolInput.path -Raw
                return "Contents of $($ToolInput.path):`n$content"
            }
            catch {
                return "Error: $_"
            }
        }
        "Write-File" {
            try {
                Set-Content $ToolInput.path $ToolInput.content
                return "Successfully wrote to $($ToolInput.path)"
            }
            catch {
                return "Error: $_"
            }
        }
        "Run-Command" {
            try {
                $output = Invoke-Expression $ToolInput.command 2>&1 | Out-String
                return "`$ $($ToolInput.command)`n$output"
            }
            catch {
                return "Error: $_"
            }
        }
        default { return "Unknown tool: $Name" }
    }
}

function Check-Permission {
    param([string]$ToolName, $ToolInput)
    
    if ($ToolName -eq "Run-Command") {
        $cmd = $ToolInput.command
        if ($cmd -match "rm|del|Remove-Item|rmdir|rd|Set-Content.*>.*|.*\|.*iex") {
            Write-Host "⚠️ Potentially dangerous command: $cmd"
            $response = Read-Host "Allow? (y/n)"
            return $response -eq "y"
        }
    }
    elseif ($ToolName -eq "Write-File") {
        Write-Host "📝 Will write to: $($ToolInput.path)"
        $response = Read-Host "Allow? (y/n)"
        return $response -eq "y"
    }
    return $true
}

$messages = @(@{ role = "user"; content = $Task })

while ($true) {
    $body = @{
        model       = "gpt-4.1"
        messages    = $messages
        max_tokens  = 4096
        tools       = $tools
        tool_choice = "auto"
    } | ConvertTo-Json -Depth 10

    $response = Invoke-RestMethod -Uri "https://api.openai.com/v1/chat/completions" -Method Post -Headers @{
        "Authorization" = "Bearer $apiKey"
        "Content-Type"  = "application/json"
    } -Body $body

    $message = $response.choices[0].message
    $messages += $message

    if ($message.tool_calls) {
        $toolResults = @()
        foreach ($toolCall in $message.tool_calls) {
            $toolName = $toolCall.function.name
            $toolInput = $toolCall.function.arguments | ConvertFrom-Json
            
            Write-Host "🔧 $toolName`: $($toolInput | ConvertTo-Json -Compress)"
            
            if (Check-Permission $toolName $toolInput) {
                $result = Execute-Tool $toolName $toolInput
                Write-Host " → $($result.Substring(0, [Math]::Min(200, $result.Length)))..."
            }
            else {
                $result = "Permission denied by user"
                Write-Host " 🚫 $result"
            }
            
            $toolResults += @{
                tool_call_id = $toolCall.id
                role         = "tool"
                content      = $result
            }
        }
        $messages += $toolResults
    }
    else {
        Write-Host "✅ $($message.content)"
        break
    }
}