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()withFuture->wait_until($deadline)for precise controlSet 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).