Something went wrong. Try again.
fork of Kludex/zigai, ported to zig 0.17
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594//! Typed contracts for the `io.modelcontextprotocol/tasks` extension.//!//! Request values borrow application memory. Parsed task results own one arena;//! every string and JSON value in `Owned` remains valid until `deinit`.
const std = @import("std");const json_limits = @import("../json.zig");const primitives = @import("primitives.zig");
pub const extension_identifier = "io.modelcontextprotocol/tasks";
pub const methods = struct { pub const get = "tasks/get"; pub const update = "tasks/update"; pub const cancel = "tasks/cancel"; pub const status_notification = "notifications/tasks";};
/// Extension-specific JSON-RPC errors defined by SEP-2663.pub const error_codes = struct { pub const missing_required_client_capability = -32003;};
pub const Error = error{ InvalidTask, InvalidTaskRequest,};
pub const Status = enum { working, input_required, completed, cancelled, failed,
pub fn terminal(self: Status) bool { return switch (self) { .working, .input_required => false, .completed, .cancelled, .failed => true, }; }};
/// Metadata common to task-creation, polling, and notification payloads.pub const Metadata = struct { task_id: []const u8, status_message: ?[]const u8, created_at: []const u8, last_updated_at: []const u8, /// Milliseconds from creation, or `null` when the task has no TTL. ttl_ms: ?u64, /// Server-suggested polling delay in milliseconds. poll_interval_ms: ?u64,};
pub const JsonRpcError = struct { code: i64, message: []const u8, data: ?std.json.Value,};
/// A status-specific task payload. JSON objects borrow the enclosing `Owned`/// arena and must not outlive it.pub const State = union(Status) { working: void, input_required: std.json.ObjectMap, completed: std.json.ObjectMap, cancelled: void, failed: JsonRpcError,};
/// The seed returned instead of a synchronous tool result.pub const CreatedTask = struct { metadata: Metadata, status: Status,};
/// The complete state returned by `tasks/get` or `notifications/tasks`.pub const DetailedTask = struct { metadata: Metadata, state: State,
pub fn status(self: DetailedTask) Status { return std.meta.activeTag(self.state); }};
/// Bounded client polling policy. The server's current suggestion is honored;/// `minimum_interval_ms` prevents an absent or overly aggressive suggestion/// from creating a hot loop.pub const PollPolicy = struct { default_interval_ms: u64 = 1_000, minimum_interval_ms: u64 = 10, max_polls: usize = 256,
pub fn interval(self: PollPolicy, suggested_ms: ?u64) u64 { return @max(suggested_ms orelse self.default_interval_ms, self.minimum_interval_ms); }};
/// Observes one validated task state. The value borrows the current `Owned`/// task and is valid only for the callback duration.pub const StatusSink = struct { context: *anyopaque, emitFn: *const fn (context: *anyopaque, task: DetailedTask) anyerror!void,
pub fn emit(self: StatusSink, task: DetailedTask) !void { return self.emitFn(self.context, task); }};
pub const Value = union(enum) { created: CreatedTask, detailed: DetailedTask,};
/// Arena-owned task result. Call `deinit` exactly once.pub const Owned = struct { arena: std.heap.ArenaAllocator, value: Value,
pub fn deinit(self: *Owned) void { self.arena.deinit(); self.* = undefined; }};
/// Caller-owned terminal tasks returned by a durable resume pass.pub const OwnedList = struct { allocator: std.mem.Allocator, items: []Owned,
pub fn deinit(self: *OwnedList) void { for (self.items) |*item| item.deinit(); self.allocator.free(self.items); self.* = undefined; }};
/// Parameters shared by `tasks/get` and `tasks/cancel`.pub const Request = struct { task_id: []const u8,
pub fn stringifyAlloc(self: Request, allocator: std.mem.Allocator) ![]u8 { if (self.task_id.len == 0) return error.InvalidTaskRequest; return std.json.Stringify.valueAlloc(allocator, .{ .taskId = self.task_id }, .{}); }};
/// Parameters for `tasks/update`.pub const UpdateRequest = struct { task_id: []const u8, /// MRTR response map encoded as one bounded JSON object. input_responses_json: []const u8,
pub fn stringifyAlloc(self: UpdateRequest, allocator: std.mem.Allocator) ![]u8 { if (self.task_id.len == 0) return error.InvalidTaskRequest; var arena = std.heap.ArenaAllocator.init(allocator); defer arena.deinit(); const responses = try parseObject(arena.allocator(), self.input_responses_json, error.InvalidTaskRequest); var iterator = responses.iterator(); while (iterator.next()) |entry| if (entry.value_ptr.* != .object) return error.InvalidTaskRequest; return std.json.Stringify.valueAlloc(allocator, .{ .taskId = self.task_id, .inputResponses = std.json.Value{ .object = responses }, }, .{}); }};
/// Parses the `resultType: "task"` result returned when a task is created.pub fn parseCreated(allocator: std.mem.Allocator, source: []const u8) !Owned { var owned = Owned{ .arena = .init(allocator), .value = undefined }; errdefer owned.arena.deinit(); const object = try parseObject(owned.arena.allocator(), source, error.InvalidTask); try expectResultType(object, "task"); const common = try parseCommon(object); owned.value = .{ .created = .{ .metadata = common.metadata, .status = common.status, } }; return owned;}
/// Validates a borrowed task-creation result without retaining it.pub fn validateCreated(value: std.json.Value) !void { const object = valueObject(value) catch return error.InvalidTask; try expectResultType(object, "task"); _ = try parseCommon(object);}
/// Parses the detailed state returned by `tasks/get`.pub fn parseResult(allocator: std.mem.Allocator, source: []const u8) !Owned { return parseDetailed(allocator, source, true);}
/// Parses one complete `notifications/tasks` JSON-RPC notification.pub fn parseNotification(allocator: std.mem.Allocator, source: []const u8) !Owned { var owned = Owned{ .arena = .init(allocator), .value = undefined }; errdefer owned.arena.deinit(); const root = try parseObject(owned.arena.allocator(), source, error.InvalidTask); if (!std.mem.eql(u8, try requiredString(root, "jsonrpc"), "2.0") or root.get("id") != null) { return error.InvalidTask; } if (!std.mem.eql(u8, try requiredString(root, "method"), methods.status_notification)) { return error.InvalidTask; } const params = try requiredObject(root, "params"); owned.value = .{ .detailed = try detailedFromObject(params, false) }; return owned;}
fn parseDetailed( allocator: std.mem.Allocator, source: []const u8, has_result_type: bool,) !Owned { var owned = Owned{ .arena = .init(allocator), .value = undefined }; errdefer owned.arena.deinit(); const object = try parseObject(owned.arena.allocator(), source, error.InvalidTask); owned.value = .{ .detailed = try detailedFromObject(object, has_result_type) }; return owned;}
fn detailedFromObject(object: std.json.ObjectMap, has_result_type: bool) !DetailedTask { if (has_result_type) try expectResultType(object, "complete"); const common = try parseCommon(object); return .{ .metadata = common.metadata, .state = try parseState(object, common.status), };}
/// Validates a borrowed `tasks/get` result without retaining it.pub fn validateResult(value: std.json.Value) !void { return validateDetailed(value, true);}
/// Validates borrowed `notifications/tasks` parameters without retaining them.pub fn validateNotification(value: std.json.Value) !void { return validateDetailed(value, false);}
fn validateDetailed(value: std.json.Value, has_result_type: bool) !void { const object = valueObject(value) catch return error.InvalidTask; if (has_result_type) try expectResultType(object, "complete"); const common = try parseCommon(object); _ = try parseState(object, common.status);}
const Common = struct { metadata: Metadata, status: Status,};
fn parseCommon(object: std.json.ObjectMap) !Common { const task_id = try requiredString(object, "taskId"); if (task_id.len == 0) return error.InvalidTask; const status = std.meta.stringToEnum(Status, try requiredString(object, "status")) orelse return error.InvalidTask; return .{ .metadata = .{ .task_id = task_id, .status_message = try optionalString(object, "statusMessage"), .created_at = try requiredString(object, "createdAt"), .last_updated_at = try requiredString(object, "lastUpdatedAt"), .ttl_ms = try nullableUnsigned(object, "ttlMs"), .poll_interval_ms = try optionalUnsigned(object, "pollIntervalMs"), }, .status = status, };}
fn parseState(object: std.json.ObjectMap, status: Status) !State { return switch (status) { .working => blk: { try rejectPayload(object); break :blk .{ .working = {} }; }, .input_required => blk: { if (object.get("result") != null or object.get("error") != null) return error.InvalidTask; const requests = try requiredObject(object, "inputRequests"); var iterator = requests.iterator(); while (iterator.next()) |entry| { const request = switch (entry.value_ptr.*) { .object => |value| value, else => return error.InvalidTask, }; _ = primitives.InputKind.fromMethod(try requiredString(request, "method")) catch return error.InvalidTask; if (request.get("params")) |params| if (params != .object) return error.InvalidTask; } break :blk .{ .input_required = requests }; }, .completed => blk: { if (object.get("inputRequests") != null or object.get("error") != null) return error.InvalidTask; break :blk .{ .completed = try requiredObject(object, "result") }; }, .cancelled => blk: { try rejectPayload(object); break :blk .{ .cancelled = {} }; }, .failed => blk: { if (object.get("inputRequests") != null or object.get("result") != null) return error.InvalidTask; const rpc_error = try requiredObject(object, "error"); break :blk .{ .failed = .{ .code = try requiredInteger(rpc_error, "code"), .message = try requiredString(rpc_error, "message"), .data = rpc_error.get("data"), } }; }, };}
fn rejectPayload(object: std.json.ObjectMap) !void { if (object.get("inputRequests") != null or object.get("result") != null or object.get("error") != null) return error.InvalidTask;}
fn expectResultType(object: std.json.ObjectMap, expected: []const u8) !void { if (!std.mem.eql(u8, try requiredString(object, "resultType"), expected)) return error.InvalidTask;}
fn parseObject( allocator: std.mem.Allocator, source: []const u8, comptime invalid_error: anytype,) !std.json.ObjectMap { const value = try json_limits.parseLeaky( std.json.Value, allocator, source, json_limits.defaults.mcp_message, .{ .allocate = .alloc_always }, invalid_error, ); return switch (value) { .object => |object| object, else => invalid_error, };}
fn requiredObject(object: std.json.ObjectMap, name: []const u8) !std.json.ObjectMap { return switch (object.get(name) orelse return error.InvalidTask) { .object => |value| value, else => error.InvalidTask, };}
fn valueObject(value: std.json.Value) !std.json.ObjectMap { return switch (value) { .object => |object| object, else => error.InvalidTask, };}
fn requiredString(object: std.json.ObjectMap, name: []const u8) ![]const u8 { return switch (object.get(name) orelse return error.InvalidTask) { .string => |value| value, else => error.InvalidTask, };}
fn optionalString(object: std.json.ObjectMap, name: []const u8) !?[]const u8 { return switch (object.get(name) orelse return null) { .string => |value| value, else => error.InvalidTask, };}
fn requiredInteger(object: std.json.ObjectMap, name: []const u8) !i64 { return switch (object.get(name) orelse return error.InvalidTask) { .integer => |value| value, else => error.InvalidTask, };}
fn nullableUnsigned(object: std.json.ObjectMap, name: []const u8) !?u64 { return switch (object.get(name) orelse return error.InvalidTask) { .null => null, .integer => |value| if (value >= 0) @intCast(value) else error.InvalidTask, else => error.InvalidTask, };}
fn optionalUnsigned(object: std.json.ObjectMap, name: []const u8) !?u64 { return switch (object.get(name) orelse return null) { .integer => |value| if (value >= 0) @intCast(value) else error.InvalidTask, else => error.InvalidTask, };}
test "task requests serialize bounded parameters" { const get = try (Request{ .task_id = "task-1" }).stringifyAlloc(std.testing.allocator); defer std.testing.allocator.free(get); try std.testing.expectEqualStrings("{\"taskId\":\"task-1\"}", get);
const update = try (UpdateRequest{ .task_id = "task-1", .input_responses_json = "{\"approval\":{\"action\":\"accept\"}}", }).stringifyAlloc(std.testing.allocator); defer std.testing.allocator.free(update); try std.testing.expectEqualStrings( "{\"taskId\":\"task-1\",\"inputResponses\":{\"approval\":{\"action\":\"accept\"}}}", update, );}
test "task polling policy bounds server intervals and status observers" { const policy = PollPolicy{ .default_interval_ms = 50, .minimum_interval_ms = 10 }; try std.testing.expectEqual(@as(u64, 50), policy.interval(null)); try std.testing.expectEqual(@as(u64, 10), policy.interval(5)); try std.testing.expectEqual(@as(u64, 1_000), policy.interval(1_000));
const Observer = struct { status: ?Status = null, fn emit(context: *anyopaque, task: DetailedTask) !void { const self: *@This() = @ptrCast(@alignCast(context)); self.status = task.status(); } }; var observer: Observer = .{}; try (StatusSink{ .context = &observer, .emitFn = Observer.emit }).emit(.{ .metadata = .{ .task_id = "task-1", .status_message = null, .created_at = "then", .last_updated_at = "now", .ttl_ms = null, .poll_interval_ms = null, }, .state = .{ .working = {} }, }); try std.testing.expectEqual(Status.working, observer.status.?);}
test "task requests reject empty identifiers and malformed responses" { try std.testing.expectError( error.InvalidTaskRequest, (Request{ .task_id = "" }).stringifyAlloc(std.testing.allocator), ); const invalid = [_][]const u8{ "[]", "{", "{\"key\":true}" }; for (invalid) |source| try std.testing.expectError( error.InvalidTaskRequest, (UpdateRequest{ .task_id = "task", .input_responses_json = source }).stringifyAlloc(std.testing.allocator), ); try std.testing.expectError( error.InvalidTaskRequest, (UpdateRequest{ .task_id = "", .input_responses_json = "{}" }).stringifyAlloc(std.testing.allocator), );}
test "created task parsing owns metadata and exposes every status" { const statuses = [_]Status{ .working, .input_required, .completed, .cancelled, .failed }; for (statuses) |status| { const source = try std.fmt.allocPrint( std.testing.allocator, "{{\"resultType\":\"task\",\"taskId\":\"task-{s}\",\"status\":\"{s}\"," ++ "\"statusMessage\":\"ready\",\"createdAt\":\"2026-08-15T10:00:00Z\"," ++ "\"lastUpdatedAt\":\"2026-08-15T10:01:00Z\",\"ttlMs\":null,\"pollIntervalMs\":250}}", .{ @tagName(status), @tagName(status) }, ); defer std.testing.allocator.free(source); var owned = try parseCreated(std.testing.allocator, source); defer owned.deinit(); const task = owned.value.created; try std.testing.expectEqual(status, task.status); try std.testing.expectEqualStrings("ready", task.metadata.status_message.?); try std.testing.expectEqual(@as(?u64, null), task.metadata.ttl_ms); try std.testing.expectEqual(@as(?u64, 250), task.metadata.poll_interval_ms); }}
test "detailed task parsing enforces status-specific payloads" { const cases = [_]struct { source: []const u8, status: Status, }{ .{ .status = .working, .source = "{\"resultType\":\"complete\",\"taskId\":\"a\",\"status\":\"working\",\"createdAt\":\"now\",\"lastUpdatedAt\":\"now\",\"ttlMs\":100}" }, .{ .status = .input_required, .source = "{\"resultType\":\"complete\",\"taskId\":\"b\",\"status\":\"input_required\",\"createdAt\":\"now\",\"lastUpdatedAt\":\"now\",\"ttlMs\":100,\"inputRequests\":{\"approval\":{\"method\":\"elicitation/create\",\"params\":{}}}}" }, .{ .status = .completed, .source = "{\"resultType\":\"complete\",\"taskId\":\"c\",\"status\":\"completed\",\"createdAt\":\"now\",\"lastUpdatedAt\":\"now\",\"ttlMs\":100,\"result\":{\"content\":[]}}" }, .{ .status = .cancelled, .source = "{\"resultType\":\"complete\",\"taskId\":\"d\",\"status\":\"cancelled\",\"createdAt\":\"now\",\"lastUpdatedAt\":\"now\",\"ttlMs\":null}" }, .{ .status = .failed, .source = "{\"resultType\":\"complete\",\"taskId\":\"e\",\"status\":\"failed\",\"createdAt\":\"now\",\"lastUpdatedAt\":\"now\",\"ttlMs\":100,\"error\":{\"code\":-32603,\"message\":\"failed\",\"data\":{\"retry\":false}}}" }, }; for (cases) |case| { var owned = try parseResult(std.testing.allocator, case.source); defer owned.deinit(); const task = owned.value.detailed; try std.testing.expectEqual(case.status, task.status()); try std.testing.expectEqual(case.status.terminal(), task.status().terminal()); }
var notification = try parseNotification( std.testing.allocator, "{\"jsonrpc\":\"2.0\",\"method\":\"notifications/tasks\",\"params\":{" ++ "\"taskId\":\"n\",\"status\":\"failed\",\"createdAt\":\"now\"," ++ "\"lastUpdatedAt\":\"now\",\"ttlMs\":0,\"error\":{\"code\":-1,\"message\":\"no\"}}}", ); defer notification.deinit(); const failure = notification.value.detailed.state.failed; try std.testing.expectEqual(@as(i64, -1), failure.code); try std.testing.expect(failure.data == null);
const invalid_notifications = [_][]const u8{ "{}", "{\"jsonrpc\":\"1.0\",\"method\":\"notifications/tasks\",\"params\":{}}", "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"notifications/tasks\",\"params\":{}}", "{\"jsonrpc\":\"2.0\",\"method\":\"notifications/progress\",\"params\":{}}", "{\"jsonrpc\":\"2.0\",\"method\":\"notifications/tasks\"}", }; for (invalid_notifications) |source| try std.testing.expectError( error.InvalidTask, parseNotification(std.testing.allocator, source), );
const borrowed = try json_limits.parseLeaky( std.json.Value, notification.arena.allocator(), "{\"taskId\":\"v\",\"status\":\"working\",\"createdAt\":\"now\",\"lastUpdatedAt\":\"now\",\"ttlMs\":0}", json_limits.defaults.mcp_message, .{}, error.InvalidTask, ); try validateNotification(borrowed);}
test "borrowed task creation validation rejects non-objects" { var arena = std.heap.ArenaAllocator.init(std.testing.allocator); defer arena.deinit(); const valid = try json_limits.parseLeaky( std.json.Value, arena.allocator(), "{\"resultType\":\"task\",\"taskId\":\"a\",\"status\":\"working\",\"createdAt\":\"now\",\"lastUpdatedAt\":\"now\",\"ttlMs\":0}", json_limits.defaults.mcp_message, .{}, error.InvalidTask, ); try validateCreated(valid); try std.testing.expectError(error.InvalidTask, validateCreated(.{ .array = .init(arena.allocator()) })); try std.testing.expectError(error.InvalidTask, validateNotification(.null));}
test "task parsing rejects malformed metadata and payload combinations" { const invalid = [_][]const u8{ "[]", "{\"resultType\":\"complete\"}", "{\"resultType\":\"task\",\"taskId\":\"\",\"status\":\"working\",\"createdAt\":\"now\",\"lastUpdatedAt\":\"now\",\"ttlMs\":0}", "{\"resultType\":\"task\",\"taskId\":\"a\",\"status\":\"unknown\",\"createdAt\":\"now\",\"lastUpdatedAt\":\"now\",\"ttlMs\":0}", "{\"resultType\":\"task\",\"taskId\":\"a\",\"status\":\"working\",\"statusMessage\":1,\"createdAt\":\"now\",\"lastUpdatedAt\":\"now\",\"ttlMs\":0}", "{\"resultType\":\"task\",\"taskId\":\"a\",\"status\":\"working\",\"createdAt\":1,\"lastUpdatedAt\":\"now\",\"ttlMs\":0}", "{\"resultType\":\"task\",\"taskId\":\"a\",\"status\":\"working\",\"createdAt\":\"now\",\"lastUpdatedAt\":1,\"ttlMs\":0}", "{\"resultType\":\"task\",\"taskId\":\"a\",\"status\":\"working\",\"createdAt\":\"now\",\"lastUpdatedAt\":\"now\",\"ttlMs\":-1}", "{\"resultType\":\"task\",\"taskId\":\"a\",\"status\":\"working\",\"createdAt\":\"now\",\"lastUpdatedAt\":\"now\",\"ttlMs\":0,\"pollIntervalMs\":-1}", }; for (invalid) |source| try std.testing.expectError( error.InvalidTask, parseCreated(std.testing.allocator, source), );
const invalid_details = [_][]const u8{ "{\"resultType\":\"task\",\"taskId\":\"a\",\"status\":\"working\",\"createdAt\":\"now\",\"lastUpdatedAt\":\"now\",\"ttlMs\":0}", "{\"resultType\":\"complete\",\"taskId\":\"a\",\"status\":\"working\",\"createdAt\":\"now\",\"lastUpdatedAt\":\"now\",\"ttlMs\":0,\"result\":{}}", "{\"resultType\":\"complete\",\"taskId\":\"a\",\"status\":\"input_required\",\"createdAt\":\"now\",\"lastUpdatedAt\":\"now\",\"ttlMs\":0,\"inputRequests\":[]}", "{\"resultType\":\"complete\",\"taskId\":\"a\",\"status\":\"input_required\",\"createdAt\":\"now\",\"lastUpdatedAt\":\"now\",\"ttlMs\":0,\"inputRequests\":{\"x\":true}}", "{\"resultType\":\"complete\",\"taskId\":\"a\",\"status\":\"input_required\",\"createdAt\":\"now\",\"lastUpdatedAt\":\"now\",\"ttlMs\":0,\"inputRequests\":{\"x\":{\"method\":\"unknown\"}}}", "{\"resultType\":\"complete\",\"taskId\":\"a\",\"status\":\"input_required\",\"createdAt\":\"now\",\"lastUpdatedAt\":\"now\",\"ttlMs\":0,\"inputRequests\":{\"x\":{\"method\":\"roots/list\",\"params\":[]}}}", "{\"resultType\":\"complete\",\"taskId\":\"a\",\"status\":\"completed\",\"createdAt\":\"now\",\"lastUpdatedAt\":\"now\",\"ttlMs\":0,\"result\":[],\"error\":{}}", "{\"resultType\":\"complete\",\"taskId\":\"a\",\"status\":\"cancelled\",\"createdAt\":\"now\",\"lastUpdatedAt\":\"now\",\"ttlMs\":0,\"error\":{}}", "{\"resultType\":\"complete\",\"taskId\":\"a\",\"status\":\"failed\",\"createdAt\":\"now\",\"lastUpdatedAt\":\"now\",\"ttlMs\":0,\"error\":{\"code\":\"x\",\"message\":1}}", }; for (invalid_details) |source| try std.testing.expectError( error.InvalidTask, parseResult(std.testing.allocator, source), );}
test "task parsing and serialization release partial allocations" { const Check = struct { fn run(allocator: std.mem.Allocator) !void { const update = try (UpdateRequest{ .task_id = "task", .input_responses_json = "{\"a\":{\"action\":\"accept\",\"content\":{\"ok\":true}}}", }).stringifyAlloc(allocator); defer allocator.free(update); var task = try parseResult( allocator, "{\"resultType\":\"complete\",\"taskId\":\"task\",\"status\":\"input_required\",\"statusMessage\":\"waiting\",\"createdAt\":\"now\",\"lastUpdatedAt\":\"now\",\"ttlMs\":1000,\"pollIntervalMs\":10,\"inputRequests\":{\"a\":{\"method\":\"elicitation/create\",\"params\":{}}}}", ); defer task.deinit(); } }; try std.testing.checkAllAllocationFailures(@import("../testing.zig").non_resizing_allocator, Check.run, .{});}