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.
============================================================================
CRITICAL: THIS SDK IS NOT THREAD-SAFE
============================================================================
The Query object and its internal state must only be accessed from a single thread at a time. The internal re-entrancy guard (_processing_message) uses a non-atomic check-then-set pattern that provides protection against recursive calls in single-threaded event loops only - it does NOT provide thread safety.
For multi-threaded applications, you MUST:
Use external synchronization (mutexes, locks) to protect ALL Query method calls, OR
Use separate Query instances per thread (no shared state)
Failure to synchronize access from multiple threads may result in message loss, ordering issues, or undefined behavior.
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.
Important: For proper async behavior, pass your application's event loop. This allows next_async to be truly event-driven instead of polling.
Thread Safety: This class is NOT thread-safe. All method calls must be protected by external synchronization when accessed from multiple threads.
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).