/**
 * Agent-as-tool adapter.
 *
 * This module provides the AgentAsTool class that wraps an Agent as a tool
 * so it can be used by another agent. Agents passed directly in the tools
 * array are automatically wrapped via {@link Agent.asTool}.
 */
import { JsonBlock, TextBlock, ToolResultBlock } from '../types/messages.js';
import { createErrorResult, Tool, ToolStreamEvent } from '../tools/tool.js';
/**
 * @internal Not for external use. Use {@link Agent.asTool} to create instances.
 *
 * Adapter that exposes an Agent as a tool for use by other agents.
 *
 * The tool accepts a single `input` string parameter, invokes the wrapped
 * agent, and returns the text response.
 *
 * @example
 * ```typescript
 * import { Agent } from '@strands-agents/sdk'
 *
 * const researcher = new Agent({
 *   name: 'researcher',
 *   description: 'Finds information on a topic',
 *   printer: false,
 * })
 *
 * // Use via convenience method (default: fresh conversation each call)
 * const tool = researcher.asTool()
 *
 * // Preserve context across invocations
 * const tool = researcher.asTool({ preserveContext: true })
 *
 * const writer = new Agent({ tools: [tool] })
 * const result = await writer.invoke('Write about AI agents')
 * ```
 */
export class AgentAsTool extends Tool {
    name;
    description;
    toolSpec;
    _agent;
    _preserveContext;
    _initialSnapshot;
    _busy = false;
    constructor(config) {
        super();
        this._agent = config.agent;
        this._preserveContext = config.preserveContext ?? false;
        if (!this._preserveContext && this._agent.sessionManager != null) {
            throw new Error(`Agent '${this._agent.name}' has a SessionManager, which conflicts with preserveContext=false. ` +
                'The SessionManager persists conversation history externally, but preserveContext=false resets ' +
                'state between invocations. Use preserveContext=true or remove the SessionManager.');
        }
        if (!this._preserveContext) {
            this._initialSnapshot = this._agent.takeSnapshot({ preset: 'session' });
        }
        this.name = config.name ?? config.agent.name;
        this.description =
            config.description ??
                config.agent.description ??
                `Use the ${this.name} agent by providing a natural language input`;
        this.toolSpec = {
            name: this.name,
            description: this.description,
            inputSchema: {
                type: 'object',
                properties: {
                    input: {
                        type: 'string',
                        description: 'The natural language input to send to the agent.',
                    },
                },
                required: ['input'],
            },
        };
    }
    /**
     * The wrapped agent instance.
     */
    get agent() {
        return this._agent;
    }
    async *stream(toolContext) {
        const { toolUse } = toolContext;
        const toolUseId = toolUse.toolUseId;
        // Concurrency guard: loadSnapshot + agent.stream() must not overlap.
        if (this._busy) {
            return createErrorResult(`Agent '${this.name}' is already processing a request`, toolUseId);
        }
        this._busy = true;
        try {
            const { input } = toolUse.input;
            // Reset agent state if not preserving context
            if (this._initialSnapshot) {
                this._agent.loadSnapshot(this._initialSnapshot);
            }
            // Stream the sub-agent, forwarding the outer invocation's state so
            // mutations in the inner agent's hooks/tools are visible to the outer
            // agent's downstream callbacks and final AgentResult.
            const gen = this._agent.stream(input, { invocationState: toolContext.invocationState });
            let next = await gen.next();
            while (!next.done) {
                const event = next.value;
                if (event.type == 'toolStreamUpdateEvent') {
                    yield event.event;
                }
                else {
                    yield new ToolStreamEvent({ data: next.value });
                }
                next = await gen.next();
            }
            const result = next.value;
            // Build the tool result
            if (result.structuredOutput !== undefined) {
                return new ToolResultBlock({
                    toolUseId,
                    status: 'success',
                    content: [new JsonBlock({ json: result.structuredOutput })],
                });
            }
            return new ToolResultBlock({
                toolUseId,
                status: 'success',
                content: [new TextBlock(result.toString())], // toString defined by AgentResult
            });
        }
        catch (error) {
            return createErrorResult(error, toolUseId);
        }
        finally {
            this._busy = false;
        }
    }
}
//# sourceMappingURL=agent-as-tool.js.map