Skip to documentation
SLOP

tiny.acp.Client

Reference tiny.acp Client

Defined in client.

A caller owns a client for the life of one agent program, to send it prompts and get replies back.

API (27)

Actions

Public operations.

Fields and members

Public fields and members.

No direct callersNo direct callsclientClient
Static calls · unresolved targets: unknown · external targets: unknown.

Source

Source: lib/acp/src/client.zig:357

zig
/// A caller owns a client for the life of one agent program, to send it prompts and get replies/// back. The instance holds one agent process, the pipes to it, the reader for its output, and the/// state of one session. The order of use is `init`, `start`, any number of prompt calls, then/// `deinit`. The client writes each message to the agent as one line of JSON and reads the agent's/// messages one line at a time. Each call writes one request and then reads the agent's messages/// until the matching response arrives. While it waits, the call hands updates to the observer,/// answers permission requests, answers any other request from the agent with a "method not found"/// error, and drops other notifications. A line of invalid JSON, any other message, or the end of/// the agent's output before the response fails the call with `error.AgentProtocolError`. The/// fields hold the client's working state, and the package tests read `initialize`, `modes` and/// `child` directly.pub const Client = struct {    /// The allocator for everything the client owns: the argument list, the directory copy, the    /// reader's buffer, parsed messages, reply text and the session id.    allocator: std.mem.Allocator,    /// The argument list the agent started with: the command, then its arguments. The client owns    /// the list, and the strings in it are borrowed from `Options`.    argv: []const []const u8,    /// The client's own copy of `Options.cwd`, or null. The method `start` reads it for    /// `session/new`, and `deinit` frees it.    cwd: ?[]u8,    /// The name, title and version from `Options.client`, sent by `start`.    info: ClientInfo,    /// The MCP servers from `Options.mcp_servers`, sent by `start`.    mcp_servers: []const McpServer,    /// The permission lists from `Options.permission_policy`.    permission_policy: PermissionPolicy,    /// The callbacks from `Options.observer`, or null.    observer: ?Observer,    /// The traffic caps from `Options.transfer_limits`.    transfer_limits: TransferLimits,    /// The caller's number for this client from `Options.transport_epoch`, stamped on every update.    transport_epoch: u64,    /// The number the next update gets. The value starts at 1 and rises by one for each update, and    /// nothing resets it.    next_update_sequence: u64 = 1,    /// The Zig standard library's thread-based I/O (`std.Io.Threaded`) the client created for the    /// agent's process and pipes. The state carries the environment the agent started with. The    /// method `deinit` frees it.    io_state: sys.thread.ThreadedIo,    /// The agent process, with the pipes to its standard input and output. The agent runs in a    /// process group of its own, and its standard error is discarded.    child: sys.process.Child,    /// The line reader over the agent's standard output, which holds the reader's buffer.    reader: LineReader,    /// The id the next request gets. The value starts at 0 and rises by one per request, so    /// `initialize` gets 0, `session/new` gets 1, and the first prompt gets 2.    next_id: i64 = 0,    /// The agent's id for the current session, taken from the `session/new` answer. The field is    /// null before `start`, and every prompt call fails with `error.AgentProtocolError` while it is    /// null. The client owns it.    session_id: ?[]u8 = null,    /// The agent's answer to `initialize`, set by `start`. The field holds the empty defaults    /// before `start`. Callers read the protocol version, capability flags and authentication    /// methods from it.    initialize: protocol.Initialize = .{},    /// The session's modes from the `session/new` answer, set by `start`. The field is empty when    /// the agent reports no modes.    modes: protocol.Modes = .{},    /// The client's own copy of the message in the latest JSON-RPC error answer from the agent, or    /// null. Each later error answer replaces it, and a later success leaves it in place. Callers    /// read it through `agentError`.    agent_error: ?[]u8 = null,    /// A caller calls `init` to start the agent and prepare everything the conversation needs, so    /// later calls only exchange messages. The function checks the limits, allocates the reader's    /// buffer, copies the argument list and the working directory, then starts the agent. The    /// function returns `error.InvalidLimits` before it allocates anything when a traffic cap or    /// the transport epoch is zero. The call allocates the reader's buffer, the line limit plus one    /// byte, before the agent starts, so a limit too large to add one to fails with    /// `error.CapacityOverflow` and no process starts. The agent starts in a new process group of    /// its own, with pipes on standard input and output and standard error discarded. The call    /// returns allocation errors, errors from starting the process, and    /// `error.AgentTransportFailed` when the process has no output pipe. On an error after the    /// agent starts, the function signals the agent's process group to terminate and then to die,    /// and waits for the agent to exit. The call sends the agent no message: the conversation    /// begins with `start`. The caller owns the result and ends it with `deinit`.    pub fn init(allocator: std.mem.Allocator, options: Options) !Client {        if (!options.transfer_limits.valid() or options.transport_epoch == 0) {            return error.InvalidLimits;        }        var reader_storage = try reader.Storage.init(allocator, options.reader_limits);        errdefer reader_storage.deinit(allocator);        const argv = try allocator.alloc([]const u8, options.args.len + 1);        errdefer allocator.free(argv);        argv[0] = options.command;        @memcpy(argv[1..], options.args);        const cwd = if (options.cwd) |value| try allocator.dupe(u8, value) else null;        errdefer if (cwd) |value| allocator.free(value);        var io_state = sys.thread.initThreadedIo(allocator, .{ .environ = options.environ orelse sys.env.current() });        errdefer io_state.deinit();        const io = io_state.io();        var child = try sys.process.spawn(io, .{            .argv = argv,            .stdin = .pipe,            .stdout = .pipe,            .stderr = .ignore,            .cwd = if (cwd) |value| .{ .path = value } else .inherit,            .pgid = 0,        });        errdefer stopChildAndReap(&child, io);        const stdout = child.stdout orelse return error.AgentTransportFailed;        reader_storage.activate();        return .{            .allocator = allocator,            .argv = argv,            .cwd = cwd,            .info = options.client,            .mcp_servers = options.mcp_servers,            .permission_policy = options.permission_policy,            .observer = options.observer,            .transfer_limits = options.transfer_limits,            .transport_epoch = options.transport_epoch,            .io_state = io_state,            .child = child,            .reader = LineReader.init(io, stdout, reader_storage),        };    }    /// A caller calls `deinit` once to end the agent and free what the client owns when done with    /// the client. The function frees the reader's buffer, the stored `initialize` answer, the    /// modes, the error message, the session id and the directory copy. The client signals the    /// agent's whole process group to terminate and then to die, and waits for the agent to exit.    /// The client sends the agent no message first, `session/cancel` included. The call frees the    /// I/O instance and the argument list.    pub fn deinit(self: *Client) void {        self.reader.deinit(self.allocator);        self.initialize.deinit(self.allocator);        self.modes.deinit(self.allocator);        if (self.agent_error) |value| self.allocator.free(value);        if (self.session_id) |value| self.allocator.free(value);        if (self.cwd) |value| self.allocator.free(value);        stopChildAndReap(&self.child, self.io_state.io());        self.io_state.deinit();        self.allocator.free(self.argv);    }    /// A caller reads the status to learn why reading stopped after    /// `error.ReaderMessageCapacityExceeded`, or to watch how close the agent's lines come to the    /// limit. The call returns a snapshot of the reader's limit, buffer size, bytes waiting,    /// longest line seen, rejected lines, and whether reading has stopped. The package test reads    /// it after an overlong line.    pub fn readerStatus(self: *const Client) reader.Status {        return self.reader.storage.status();    }    /// A caller uses this call to stop the agent and everything it started at once. The client    /// signals the agent's whole process group to terminate and then to die, so tools the agent    /// started stop too. The call falls back to signalling the agent alone when signalling the    /// group fails. The function sends no `session/cancel` message: the client sends that message    /// only when a reply reaches a limit. The function returns without waiting for the agent to    /// exit, and `deinit` waits for it later. The call returns at once when the process has already    /// been reaped. A later call that reads from the agent finds its output closed and fails with    /// `error.AgentProtocolError`.    pub fn requestCancel(self: *Client) void {        stopChild(&self.child, self.io_state.io());    }    /// A caller calls `start` once after `init` and before the first prompt to open the    /// conversation. The call sends `initialize` with protocol version 1, an empty set of client    /// capabilities and the client's name, title and version, then waits for the answer. The    /// function fails with `error.AgentProtocolError` when the agent answers with an error, a    /// malformed result, or a protocol version other than 1. The client keeps the message of an    /// error answer for `agentError`. The call stores the answer in the `initialize` field. The    /// function then sends `session/new` with the working directory and the MCP servers, and stores    /// the session id and the modes from the answer. Updates and permission requests that arrive    /// meanwhile go through the observer as they do during a prompt. Calling it again runs both    /// steps again and replaces the stored answer, session id and modes. The call can also fail    /// with `error.RequestCapacityExceeded`, `error.ReaderMessageCapacityExceeded` and the errors    /// listed at `promptDetailedContent` for messages that arrive meanwhile.    pub fn start(self: *Client) !void {        const initialize_id = self.nextRequestId();        try self.writeInitialize(initialize_id);        var initialize_capture = PromptCapture{};        defer initialize_capture.deinit(self.allocator);        var initialized = try self.readResponse(initialize_id, &initialize_capture);        defer initialized.deinit();        _ = try self.responseResult(initialized.value);        var initialize = try protocol.Initialize.fromResponse(self.allocator, initialized.value);        errdefer initialize.deinit(self.allocator);        if (initialize.protocol_version != 1) return error.AgentProtocolError;        self.initialize.deinit(self.allocator);        self.initialize = initialize;        const session_id_request = self.nextRequestId();        if (self.cwd) |cwd| {            try self.writeSessionNew(session_id_request, cwd);        } else {            const cwd = try sys.fs.cwdAlloc(self.allocator);            defer self.allocator.free(cwd);            try self.writeSessionNew(session_id_request, cwd);        }        var session_capture = PromptCapture{};        defer session_capture.deinit(self.allocator);        var session = try self.readResponse(session_id_request, &session_capture);        defer session.deinit();        _ = try self.responseResult(session.value);        var modes = try protocol.Modes.fromResponse(self.allocator, session.value) orelse protocol.Modes{};        errdefer modes.deinit(self.allocator);        if (self.session_id) |value| {            self.allocator.free(value);            self.session_id = null;        }        self.session_id = try self.extractSessionId(session.value);        self.modes.deinit(self.allocator);        self.modes = modes;    }    /// A caller uses this call for the common case of sending text and getting the reply text back.    /// The call sends the text as one prompt and returns the reply: the text of the agent's message    /// chunks, joined in arrival order. The caller owns the returned bytes and frees them with the    /// client's allocator. The call drops the stop reason and any overflow record, which    /// `promptDetailed` returns. The function fails as `promptDetailedContent` does.    pub fn prompt(self: *Client, text: []const u8) ![]u8 {        var result = try self.promptDetailed(text);        const response = result.response;        result.response = &.{};        result.deinit(self.allocator);        return response;    }    /// A caller uses this call to send text and also learn why the agent stopped and whether the    /// reply was cut short. The call sends the text as one text block and returns the reply, the    /// stop reason and any overflow record. The caller frees the result with `PromptResult.deinit`    /// and the client's allocator. The function fails as `promptDetailedContent` does.    pub fn promptDetailed(self: *Client, text: []const u8) !protocol.PromptResult {        const content = [_]protocol.PromptContent{.{ .text = text }};        return try self.promptDetailedContent(content[0..]);    }    /// A caller uses this call to send a prompt that carries files, links or media alongside text.    /// The call sends one `session/prompt` request for the current session with the given blocks in    /// order. The function fails with `error.AgentProtocolError` before `start` has set a session.    /// The call reads until the prompt's response: each update goes to the observer, and agent    /// message chunks join the reply within the reply limits. When one more chunk would pass a    /// reply limit, the reply keeps what came before it, the result records the overflow, and the    /// client sends one `session/cancel` and keeps waiting for the agent's final response. The call    /// returns the reply, the agent's stop reason and the overflow record, and the caller frees the    /// result with `PromptResult.deinit`. The function fails with `error.RequestCapacityExceeded`    /// when the request is too large to send, and nothing is sent. The call fails with    /// `error.UpdateCapacityExceeded` or `error.UpdateCountCapacityExceeded` for an update past a    /// limit, `error.ReaderMessageCapacityExceeded` for a line past the line limit,    /// `error.MissingDurableObserver` when a callback it needs is missing, and any error an    /// observer callback returns. The call fails with `error.AgentProtocolError` for an error    /// answer, a malformed message, or a response without a `stopReason`. The call reads the blocks    /// and their strings only during the call.    pub fn promptDetailedContent(self: *Client, content: []const protocol.PromptContent) !protocol.PromptResult {        const session_id = self.session_id orelse return error.AgentProtocolError;        const prompt_id = self.nextRequestId();        try self.writePrompt(prompt_id, session_id, content);        var capture = PromptCapture{};        defer capture.deinit(self.allocator);        var response = try self.readResponse(prompt_id, &capture);        defer response.deinit();        const response_text = try capture.chunks.toOwnedSlice(self.allocator);        errdefer self.allocator.free(response_text);        const stop_reason = try self.extractPromptStop(response.value);        errdefer self.allocator.free(stop_reason);        return .{            .response = response_text,            .stop_reason = stop_reason,            .overflow = capture.overflow,        };    }    fn nextRequestId(self: *Client) i64 {        const id = self.next_id;        self.next_id += 1;        return id;    }    /// A caller uses this call to show what the agent said after a call fails with    /// `error.AgentProtocolError`. The call returns the message of the latest JSON-RPC error answer    /// from the agent, or null when there has been none. The message stays until a later error    /// answer replaces it, so a later success leaves it in place. When copying a new message fails    /// for lack of memory, the client keeps the older message. The slice belongs to the client and    /// stays valid until the next error answer or `deinit`.    pub fn agentError(self: *const Client) ?[]const u8 {        return self.agent_error;    }    fn recordAgentError(self: *Client, error_value: std.json.Value) void {        const object = json.getObject(error_value) orelse return;        const message = json.objectString(object, "message") orelse return;        const owned = self.allocator.dupe(u8, message) catch return;        if (self.agent_error) |previous| self.allocator.free(previous);        self.agent_error = owned;    }    fn responseResult(self: *Client, value: std.json.Value) !std.json.ObjectMap {        const object = json.getObject(value) orelse return error.AgentProtocolError;        if (object.get("error")) |error_value| {            self.recordAgentError(error_value);            return error.AgentProtocolError;        }        const result = object.get("result") orelse return error.AgentProtocolError;        return json.getObject(result) orelse return error.AgentProtocolError;    }    fn extractSessionId(self: *Client, value: std.json.Value) ![]u8 {        const result = try self.responseResult(value);        const session_id = json.objectString(result, "sessionId") orelse return error.AgentProtocolError;        return try self.allocator.dupe(u8, session_id);    }    fn extractPromptStop(self: *Client, value: std.json.Value) ![]u8 {        const result = try self.responseResult(value);        const reason = json.objectString(result, "stopReason") orelse return error.AgentProtocolError;        return try self.allocator.dupe(u8, reason);    }    fn writeInitialize(self: *Client, id: i64) !void {        var out: std.Io.Writer.Allocating = .init(self.allocator);        defer out.deinit();        var stream = pretty_json.Writer.init(&out.writer, .minified);        const root = try stream.object();        try root.field("jsonrpc", "2.0");        try root.field("id", id);        try root.field("method", "initialize");        const params = try root.object("params");        try params.field("protocolVersion", 1);        try params.field("clientCapabilities", .{});        const client_info = try params.object("clientInfo");        try client_info.field("name", self.info.name);        try client_info.field("title", self.info.title);        try client_info.field("version", self.info.version);        try client_info.end();        try params.end();        try root.endLine();        try self.send(out.written());    }    fn writeSessionNew(self: *Client, id: i64, cwd: []const u8) !void {        var out: std.Io.Writer.Allocating = .init(self.allocator);        defer out.deinit();        var stream = pretty_json.Writer.init(&out.writer, .minified);        const root = try stream.object();        try root.field("jsonrpc", "2.0");        try root.field("id", id);        try root.field("method", "session/new");        const params = try root.object("params");        try params.field("cwd", cwd);        try writeMcpServers(try params.array("mcpServers"), self.mcp_servers);        try params.end();        try root.endLine();        try self.send(out.written());    }    fn writePrompt(        self: *Client,        id: i64,        session_id: []const u8,        content: []const protocol.PromptContent,    ) !void {        var out: std.Io.Writer.Allocating = .init(self.allocator);        defer out.deinit();        var stream = pretty_json.Writer.init(&out.writer, .minified);        const root = try stream.object();        try root.field("jsonrpc", "2.0");        try root.field("id", id);        try root.field("method", "session/prompt");        const params = try root.object("params");        try params.field("sessionId", session_id);        try writePromptContent(try params.array("prompt"), content);        try params.end();        try root.endLine();        try self.send(out.written());    }    fn writePromptContent(array: pretty_json.Array, content: []const protocol.PromptContent) !void {        for (content) |item| try writePromptContentItem(array, item);        try array.end();    }    fn writePromptContentItem(array: pretty_json.Array, item: protocol.PromptContent) !void {        switch (item) {            .text => |text| {                const object = try array.object();                try object.field("type", "text");                try object.field("text", text);                try object.end();            },            .image => |media| try writePromptMedia(array, "image", media),            .audio => |media| try writePromptMedia(array, "audio", media),            .resource_text => |resource| try writePromptTextResource(array, resource),            .resource_blob => |resource| try writePromptBlobResource(array, resource),            .resource_link => |link| try writePromptResourceLink(array, link),        }    }    fn writePromptMedia(        array: pretty_json.Array,        content_type: []const u8,        media: protocol.PromptMedia,    ) !void {        const object = try array.object();        try object.field("type", content_type);        try object.field("mimeType", media.mime_type);        try object.field("data", media.data);        try writeOptionalStringField(object, "uri", media.uri);        try object.end();    }    fn writePromptTextResource(        array: pretty_json.Array,        resource: protocol.PromptTextResource,    ) !void {        const object = try array.object();        try object.field("type", "resource");        const value = try object.object("resource");        try value.field("uri", resource.uri);        try value.field("text", resource.text);        try writeOptionalStringField(value, "mimeType", resource.mime_type);        try value.end();        try object.end();    }    fn writePromptBlobResource(        array: pretty_json.Array,        resource: protocol.PromptBlobResource,    ) !void {        const object = try array.object();        try object.field("type", "resource");        const value = try object.object("resource");        try value.field("uri", resource.uri);        try value.field("blob", resource.blob);        try writeOptionalStringField(value, "mimeType", resource.mime_type);        try value.end();        try object.end();    }    fn writePromptResourceLink(array: pretty_json.Array, link: protocol.PromptResourceLink) !void {        const object = try array.object();        try object.field("type", "resource_link");        try object.field("uri", link.uri);        try object.field("name", link.name);        try writeOptionalStringField(object, "mimeType", link.mime_type);        try writeOptionalStringField(object, "title", link.title);        try writeOptionalStringField(object, "description", link.description);        if (link.size) |size| try object.field("size", size);        try object.end();    }    fn writeOptionalStringField(        object: pretty_json.Object,        name: []const u8,        value: []const u8,    ) !void {        if (value.len == 0) return;        try object.field(name, value);    }    fn writeErrorResponse(self: *Client, id_value: std.json.Value) !void {        var out: std.Io.Writer.Allocating = .init(self.allocator);        defer out.deinit();        var stream = pretty_json.Writer.init(&out.writer, .minified);        const root = try stream.object();        try root.field("jsonrpc", "2.0");        try root.field("id", id_value);        const error_value = try root.object("error");        try error_value.field("code", -32601);        try error_value.field("message", "method not found");        try error_value.end();        try root.endLine();        try self.send(out.written());    }    fn writePermissionResponse(        self: *Client,        id_value: std.json.Value,        selection: PermissionSelection,    ) !void {        var out: std.Io.Writer.Allocating = .init(self.allocator);        defer out.deinit();        var stream = pretty_json.Writer.init(&out.writer, .minified);        const root = try stream.object();        try root.field("jsonrpc", "2.0");        try root.field("id", id_value);        const result = try root.object("result");        const outcome = try result.object("outcome");        try outcome.field("outcome", selection.outcome);        try writeOptionalStringField(outcome, "optionId", selection.option_id);        try outcome.end();        try result.end();        try root.endLine();        try self.send(out.written());    }    fn writeCancel(self: *Client) !void {        const session_id = self.session_id orelse return error.AgentProtocolError;        var out: std.Io.Writer.Allocating = .init(self.allocator);        defer out.deinit();        var stream = pretty_json.Writer.init(&out.writer, .minified);        const root = try stream.object();        try root.field("jsonrpc", "2.0");        try root.field("method", "session/cancel");        const params = try root.object("params");        try params.field("sessionId", session_id);        try params.end();        try root.endLine();        try self.send(out.written());    }    fn send(self: *Client, bytes: []const u8) !void {        if (bytes.len > self.transfer_limits.request_bytes) {            return error.RequestCapacityExceeded;        }        const stdin = self.child.stdin orelse return error.AgentTransportFailed;        try fileWriteAll(self.io_state.io(), stdin, bytes);    }    fn selectPermission(self: *Client, object: std.json.ObjectMap) !OwnedSelection {        if (self.observer) |observer| {            if (observer.permissionFn != null) {                var preview = try protocol.PermissionRequest.fromClientRequest(                    self.allocator,                    object,                    "",                    "pending",                    "",                    "",                );                defer preview.deinit(self.allocator);                if (observer.permission(preview)) |reply| {                    var owned = OwnedSelection{ .allocator = self.allocator, .selection = .{ .outcome = "cancelled" } };                    errdefer owned.deinit();                    owned.outcome = try self.allocator.dupe(u8, reply.outcome);                    owned.option_id = try self.allocator.dupe(u8, reply.option_id);                    owned.option_kind = try self.allocator.dupe(u8, reply.option_kind);                    owned.selection = .{                        .outcome = owned.outcome.?,                        .option_id = owned.option_id.?,                        .option_kind = owned.option_kind.?,                    };                    return owned;                }            }        }        return .{ .allocator = self.allocator, .selection = permissionSelection(object, self.permission_policy) };    }    fn readResponse(self: *Client, id: i64, capture: *PromptCapture) !std.json.Parsed(std.json.Value) {        while (try self.reader.nextLine()) |line| {            const trimmed = std.mem.trim(u8, line, " \t\r\n");            if (trimmed.len == 0) continue;            var parsed = std.json.parseFromSlice(std.json.Value, self.allocator, trimmed, .{}) catch return error.AgentProtocolError;            if (isResponseFor(parsed.value, id)) return parsed;            if (try self.handleIncoming(parsed.value, trimmed, capture)) {                parsed.deinit();                continue;            }            parsed.deinit();            return error.AgentProtocolError;        }        return error.AgentProtocolError;    }    fn handleIncoming(        self: *Client,        value: std.json.Value,        envelope: []const u8,        capture: *PromptCapture,    ) !bool {        const object = json.getObject(value) orelse return false;        if (json.objectString(object, "method")) |method| {            if (std.mem.eql(u8, method, "session/update")) {                const update_sequence = self.next_update_sequence;                if (update_sequence > self.transfer_limits.update_count) {                    return error.UpdateCountCapacityExceeded;                }                if (envelope.len > self.transfer_limits.update_bytes) {                    return error.UpdateCapacityExceeded;                }                self.next_update_sequence = std.math.add(                    u64,                    update_sequence,                    1,                ) catch return error.UpdateCountCapacityExceeded;                var update = (try protocol.Update.fromSessionNotification(                    self.allocator,                    object,                    self.transport_epoch,                    update_sequence,                    envelope,                )) orelse return error.AgentProtocolError;                defer update.deinit(self.allocator);                const observer = self.observer orelse                    return error.MissingDurableObserver;                try observer.update(update);                if (try capture.appendResponse(                    self.allocator,                    update,                    self.transfer_limits,                )) try self.writeCancel();                return true;            }            if (std.mem.eql(u8, method, "session/request_permission")) {                const id_value = object.get("id") orelse return false;                var selection = try self.selectPermission(object);                defer selection.deinit();                var request = try protocol.PermissionRequest.fromClientRequest(                    self.allocator,                    object,                    envelope,                    selection.selection.outcome,                    selection.selection.option_id,                    selection.selection.option_kind,                );                defer request.deinit(self.allocator);                const observer = self.observer orelse                    return error.MissingDurableObserver;                try observer.commitPermission(request);                try self.writePermissionResponse(id_value, selection.reply());                return true;            }            if (object.get("id")) |id_value| {                try self.writeErrorResponse(id_value);                return true;            }            return true;        }        return false;    }};

Source: lib/acp/src/root.zig:66

zig
pub const Client = client.Client;
Called byCallstest sourcelib.acp.src.clienttest: client allows granted ACP permi...test sourcelib.acp.src.clienttest: client allows granted MCP tool ...test sourcelib.acp.src.clienttest: client cancellation kills proce...test sourcelib.acp.src.clienttest: client collects ACP agent messa...test sourcelib.acp.src.clienttest: client commits admitted respons...+6 moreprivate sourcelib.acp.src.client.LineReaderdeinitprivate sourcelib.acp.src.clientstopChildAndReapInitializedeinitModesdeinitClientdeinit
Static calls · unresolved targets: 0 · external targets: 3.
Called byCallstest sourcelib.acp.src.clienttest: client admits reader capacity b...test sourcelib.acp.src.clienttest: client allows granted ACP permi...test sourcelib.acp.src.clienttest: client allows granted MCP tool ...test sourcelib.acp.src.clienttest: client cancellation kills proce...test sourcelib.acp.src.clienttest: client collects ACP agent messa...+7 moreprivate sourcelib.acp.src.client.LineReaderinitprivate sourcelib.acp.src.clientstopChildAndReapClientinit
Static calls · unresolved targets: 0 · external targets: 12.
Called byCallstest sourcelib.acp.src.clienttest: client collects ACP agent messa...test sourcelib.acp.src.clienttest: client refuses an oversized req...ClientpromptDetailedClientprompt
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallsClientprompttest sourcelib.acp.src.clienttest: client allows granted ACP permi...test sourcelib.acp.src.clienttest: client allows granted MCP tool ...test sourcelib.acp.src.clienttest: client commits admitted respons...test sourcelib.acp.src.clienttest: client frames every official AC...+2 moreClientpromptDetailedContentClientpromptDetailed
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsClientpromptDetailedprivate sourcelib.acp.src.client.ClientextractPromptStopprivate sourcelib.acp.src.client.ClientnextRequestIdprivate sourcelib.acp.src.client.ClientreadResponseprivate sourcelib.acp.src.client.ClientwritePromptClientpromptDetailedContent
Static calls · unresolved targets: 0 · external targets: 4.
Called byCallsNo direct callstest sourcelib.acp.src.clienttest: client rejects oversized ACP ou...ClientreaderStatus
Static calls · unresolved targets: 1 · external targets: 0.
Called byCallstest sourcelib.acp.src.clienttest: client cancellation kills proce...private sourcelib.acp.src.clientstopChildClientrequestCancel
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallstest sourcelib.acp.src.clienttest: client allows granted ACP permi...test sourcelib.acp.src.clienttest: client allows granted MCP tool ...test sourcelib.acp.src.clienttest: client collects ACP agent messa...test sourcelib.acp.src.clienttest: client commits admitted respons...test sourcelib.acp.src.clienttest: client frames every official AC...+5 moreprivate sourcelib.acp.src.client.ClientextractSessionIdprivate sourcelib.acp.src.client.ClientnextRequestIdprivate sourcelib.acp.src.client.ClientreadResponseprivate sourcelib.acp.src.client.ClientresponseResultprivate sourcelib.acp.src.client.ClientwriteInitialize+5 moreClientstart
Static calls · unresolved targets: 0 · external targets: 8.

Complete caller list for Client.deinit

11 direct callers.

Complete caller list for Client.init

12 direct callers.

Complete caller list for Client.promptDetailed

7 direct callers.

Complete caller list for Client.start

10 direct callers.

Complete call list for Client.start

10 direct calls.

Audit

Definitions10
Public names20
Members18
Version26.7.0
Revisiondaab053ee433