Getting Started with AgentSDK for Swift

July 17, 2026 · View on GitHub

For macOS applications that need Autohand CLI commands, persistent /goal state, /deep-research, or the replayable /autoresearch ledger, use AutohandCLIClient and continue with the autoresearch guide. The provider-backed Agent and Runner below remain available on both macOS and iOS.

This guide walks through installing the Swift AgentSDK, configuring a provider, and running your first agent.

Prerequisites

  • macOS 14+ or iOS 17+
  • Swift 6.0+
  • An API key from your AI provider (OpenAI, OpenRouter, etc.)

1. Add the Package

Add AgentSDK to your Package.swift:

// swift-tools-version: 6.0
import PackageDescription

let package = Package(
    name: "MyAgentApp",
    platforms: [.macOS(.v14)],
    dependencies: [
        .package(path: "../AgentSDK"),
    ],
    targets: [
        .executableTarget(
            name: "MyAgentApp",
            dependencies: ["AgentSDK"]
        ),
    ]
)

Or add via Xcode: File → Add Package Dependencies → local path to AgentSDK.

2. Your First Agent

Create main.swift:

import AgentSDK
import Foundation

let provider = OpenAIProvider(apiKey: "sk-...")

let agent = Agent(
    name: "Assistant",
    instructions: "You are a helpful coding assistant.",
    tools: [.readFile, .writeFile, .bash],
    maxTurns: 10,
    model: ModelID("gpt-4o"),
    provider: provider
)

let result = try await Runner.runSync(
    agent: agent,
    prompt: "Write a hello world program in Swift"
)

print(result)

Run it:

swift run

3. Streaming Events

For real-time output, use runStream():

import AgentSDK
import Foundation

let provider = OpenAIProvider(apiKey: "sk-...")

let agent = Agent(
    name: "Assistant",
    instructions: "You are a helpful coding assistant.",
    tools: [.readFile, .bash],
    model: ModelID("gpt-4o"),
    provider: provider
)

let stream = Runner.runStream(
    agent: agent,
    prompt: "What files are in the current directory?"
)

for try await event in stream {
    switch event.type {
    case .content:
        if let data = event.data {
            print(data, terminator: "")
        }
    case .toolCall:
        print("\n[Tool: \(event.tool?.rawValue ?? "unknown")]")
    case .toolResult:
        print("[Tool result: \(event.data?.prefix(100) ?? "")]")
    case .toolError:
        print("[Tool error: \(event.data ?? "")]")
    case .done:
        print("\n[Done]")
    }
}

4. Working with Files

let agent = Agent(
    name: "Reviewer",
    instructions: "You review code for correctness and style.",
    tools: [.readFile, .editFile],
    model: ModelID("gpt-4o"),
    provider: provider,
    cwd: "/path/to/project"
)

let result = try await Runner.runSync(
    agent: agent,
    prompt: "Review Sources/Types.swift for issues"
)

5. Handling Permissions

let hookManager = HookManager()
let permManager = PermissionManager(
    hookManager: hookManager,
    mode: .ask
)

Runner.setPermissionManager(permManager)

let agent = Agent(
    name: "SafeAgent",
    instructions: "Be careful with destructive operations.",
    tools: [.readFile, .writeFile, .bash],
    model: ModelID("gpt-4o"),
    provider: provider
)

let stream = Runner.runStream(
    agent: agent,
    prompt: "Create a new Swift file"
)

for try await event in stream {
    switch event.type {
    case .toolCall:
        if let tool = event.tool {
            let request = PermissionRequest(tool: tool)
            let decision = await permManager.requestPermission(request)
            if decision.decision == .deny {
                print("\n[Blocked: \(tool.rawValue)]")
            }
        }
    case .content:
        if let data = event.data {
            print(data, terminator: "")
        }
    default:
        break
    }
}

6. Using Different Loop Strategies

// Plan-and-Execute for complex multi-step tasks
let planner = Agent(
    name: "Planner",
    instructions: "Plan first, then execute step by step.",
    tools: [.readFile, .writeFile, .editFile, .bash],
    loopType: .planAndExecute,
    model: ModelID("gpt-4o"),
    provider: provider
)

// Parallel for independent operations
let parallel = Agent(
    name: "Parallel",
    instructions: "Execute independent tasks in parallel.",
    tools: [.readFile, .webSearch],
    loopType: .parallel,
    model: ModelID("gpt-4o"),
    provider: provider
)

7. Custom Tools

struct CustomTool: ToolDefinition {
    let name = "uppercase"
    let description = "Convert text to uppercase."
    let annotations = ToolAnnotations(readOnlyHint: true, idempotentHint: true)
    let parameters: [String: AnyCodable] = [
        "type": AnyCodable("object"),
        "properties": AnyCodable([
            "text": ["type": "string", "description": "Text to convert"],
        ] as [String: Any]),
        "required": AnyCodable(["text"]),
    ]

    func execute(params: [String: AnyCodable]) async throws -> ToolResult {
        guard let text = params["text"]?.value as? String else {
            return .failure(error: "Missing text parameter")
        }
        return .success(data: text.uppercased())
    }
}

let registry = ToolRegistry()
registry.register(CustomTool())
registry.register(ReadFileTool())
registry.register(BashTool())

8. Using Hooks

let hookManager = HookManager()

hookManager.addHook(HookDefinition(
    event: .beforeExecution,
    command: "echo 'Agent starting...'"
))

hookManager.addHook(HookDefinition(
    event: .afterExecution,
    command: "echo 'Agent finished.'"
))

Runner.setHookManager(hookManager)

Next Steps

  • Read API_REFERENCE.md for complete method documentation
  • See MIGRATION.md if migrating from the TypeScript SDK
  • Check the Examples/ directory for more patterns