NAME

Claude::Agent::Query - Query iterator for Claude Agent SDK

SYNOPSIS

use Claude::Agent::Query;
use Claude::Agent::Options;

my $query = Claude::Agent::Query->new(
    prompt  => "Find all TODO comments",
    options => Claude::Agent::Options->new(
        allowed_tools => ['Read', 'Glob', 'Grep'],
    ),
);

# Blocking iteration
while (my $msg = $query->next) {
    if ($msg->isa('Claude::Agent::Message::Result')) {
        print $msg->result, "\n";
        last;
    }
}

DESCRIPTION

This module handles communication with the Claude CLI process and provides both blocking and async iteration over response messages.

CONSTRUCTOR

my $query = Claude::Agent::Query->new(
    prompt  => "Find all TODO comments",
    options => $options,
    loop    => $loop,    # optional, for async integration
);

Arguments

  • prompt - Required. The prompt to send to Claude.

  • options - Optional. A Claude::Agent::Options object.

  • loop - Optional. An IO::Async::Loop for async integration. If not provided, a new loop is created internally.

For proper async behavior, pass your application's event loop. This allows next_async to be truly event-driven instead of polling.

cleanup

$query->cleanup;

Explicitly clean up SDK server sockets. Call this when you're done iterating through messages but before the Query goes out of scope. This is especially important in loops that create multiple queries.

next

my $msg = $query->next;

Blocking call to get the next message. Returns undef when no more messages.

Performance Note: This method uses a polling loop with 0.1 second intervals, which may cause unnecessary CPU wake-ups and latency for long-running queries. For better efficiency in async applications, use next_async() with Future->wait() or integrate with your event loop directly.

TIMEOUT BOUNDARY WARNING: The actual timeout may exceed the configured query_timeout value by up to 0.1 seconds (the polling interval), as the timeout check occurs after each loop_once() call completes. This is inherent to the polling design. For applications requiring precise timeout behavior:

  • Use next_async() with Future->wait_until($deadline) for precise control

  • Set query_timeout slightly lower than your hard deadline to account for skew

  • Integrate with your own event loop for microsecond-precision timing

next_async

my $msg = await $query->next_async;

Async call to get the next message. Returns a Future that resolves when a message is available. This is truly event-driven - no polling.

session_id

my $id = $query->session_id;

Returns the session ID once available (after init message).

is_finished

if ($query->is_finished) { ... }

Returns true if the query has finished (process exited).

error

if (my $err = $query->error) { ... }

Returns error message if the process failed.

interrupt

$query->interrupt;

Send interrupt signal to abort current operation.

send_user_message

$query->send_user_message("Continue with the next step");

Send a follow-up user message during streaming.

set_permission_mode

$query->set_permission_mode('acceptEdits');

Change permission mode during streaming.

respond_to_permission

$query->respond_to_permission($tool_use_id, {
    behavior      => 'allow',
    updated_input => $input,
});

Respond to a permission request.

rewind_files

$query->rewind_files;

Revert file changes to the checkpoint state.

AUTHOR

LNATION, <email at lnation.org>

LICENSE

This software is Copyright (c) 2026 by LNATION.

This is free software, licensed under The Artistic License 2.0 (GPL Compatible).