6283 lines · 252.6 KB · Zig
RawDownload
const std = @import("std");const core_types = @import("../shared/types.zig");const debug_trace = @import("../shared/debug_trace.zig");const io_mod = @import("../shared/io.zig");const message = @import("../shared/message.zig");const text_utils = @import("../shared/text_utils.zig");const tool_result_errors = @import("../tooling/tool_result_errors.zig");const session_permission_state = @import("../permissions/session_permission_state.zig");const image_attachments = @import("../images/image_attachments.zig");const generation_usage_provider = @import("generation_usage_provider.zig");const web_fetch_artifacts = @import("web_fetch_artifacts.zig");pub const session_usage = @import("session_usage.zig");pub const profile_usage_runtime = @import("profile_usage_runtime.zig");const command_contract = @import("../execution/command_contract.zig");const sort_utils = @import("../shared/sort_utils.zig");const Allocator = std.mem.Allocator; test {    _ = session_usage;    _ = profile_usage_runtime;} const compact_continuation_preamble = "This session is being continued from earlier compacted context. The summary below covers the earlier portion of the conversation.\n\n";const compact_recent_messages_note = "Recent conversation turns are preserved verbatim.";const compact_direct_resume_instruction = "Continue the conversation from where it left off without asking the user to repeat context. Resume directly.";const compact_summary_max_chars: usize = 1200;const compact_summary_max_lines: usize = 24;const compact_summary_max_line_chars: usize = 160; pub const interrupted_turn_notice: core_types.SemanticNotice = .{    .topic = "system",    .tone = .cancelled,    .body = "cancelled",}; pub const failed_turn_notice: core_types.SemanticNotice = .{    .topic = "system",    .tone = .@"error",    .body = "failed",}; pub fn interruptedTurnNotice(entry: InterruptedHistoryTurn) core_types.SemanticNotice {    return switch (entry.terminal_reason) {        .cancelled => interrupted_turn_notice,        .failed => failed_turn_notice,    };} pub const interrupted_turn_context =    "<turn_aborted>\n" ++    "The previous turn ended before completion. Any tools or commands may have partially executed. " ++    "Do not continue this request unless the user explicitly asks to continue.\n" ++    "</turn_aborted>"; pub fn projectSnapshotLocator(buffer: []u8, path: []const u8) ![]const u8 {    if (!std.fs.path.isAbsolute(path)) return path;    return std.fmt.bufPrint(buffer, "images/{s}", .{std.fs.path.basename(path)});} pub const interrupted_before_completion_output = "The previous response ended before completion.";pub const aborted_tool_output = "aborted by user"; /// Conversation language tag stored in session records.pub const ConversationLanguage = core_types.ConversationLanguage;/// Image attachment metadata stored on user turns.pub const ImageAttachment = core_types.ImageAttachment;/// User-side text and image attachments for a stored history turn.pub const UserTurn = core_types.UserTurn;/// Gateway tool call metadata stored when an interrupted turn is cancelled during a tool call.pub const ToolCall = core_types.ToolCall;/// Stored assistant response paired with the user turn that produced it.pub const AssistantHistoryTurn = core_types.AssistantHistoryTurn;/// Stored background command metadata paired with the user turn that produced it.pub const BackgroundCommandHistoryTurn = core_types.BackgroundCommandHistoryTurn;pub const StableBackgroundRecordId = core_types.StableBackgroundRecordId;pub const CancelledCommandPresentation = core_types.CancelledCommandPresentation;/// Stored interrupted turn marker, optionally paired with the active tool call.pub const InterruptedHistoryTurn = core_types.InterruptedHistoryTurn;pub const InterruptedTerminalReason = core_types.InterruptedTerminalReason;/// Stored compacted context summary produced by core history compaction.pub const CompactedSummaryHistoryTurn = core_types.CompactedSummaryHistoryTurn;/// One persisted session history record.pub const HistoryTurn = core_types.HistoryTurn;pub const freeUserTurn = core_types.freeUserTurn;pub const dupeUserTurn = core_types.dupeUserTurn;pub const max_work_id_bytes: usize = 128; pub const WorkIdAssociation = enum {    none,    copy_event,    already_associated,}; pub const WorkIdError = error{    InvalidWorkId,    ConflictingWorkId,}; pub fn validateWorkId(work_id: []const u8) WorkIdError!void {    if (work_id.len == 0 or work_id.len > max_work_id_bytes or        !std.unicode.utf8ValidateSlice(work_id) or        std.mem.indexOfScalar(u8, work_id, 0) != null)    {        return error.InvalidWorkId;    }} pub fn historyTurnWorkId(turn: HistoryTurn) ?[]const u8 {    return switch (turn) {        .assistant => |entry| entry.user.work_id,        .background_command => |entry| entry.user.work_id,        .interrupted => |entry| entry.user.work_id,        .compacted_summary => null,    };} /// Purely decides how authoritative event provenance relates to one turn.pub fn decideWorkIdAssociation(    turn: HistoryTurn,    event_work_id: ?[]const u8,) WorkIdError!WorkIdAssociation {    const turn_work_id = historyTurnWorkId(turn);    if (turn_work_id) |work_id| try validateWorkId(work_id);    if (event_work_id) |work_id| try validateWorkId(work_id);     if (event_work_id == null) {        return if (turn_work_id == null) .none else error.ConflictingWorkId;    }    if (turn == .compacted_summary) return error.ConflictingWorkId;    if (turn_work_id) |work_id| {        return if (std.mem.eql(u8, work_id, event_work_id.?))            .already_associated        else            error.ConflictingWorkId;    }    return .copy_event;} /// Installs a separately owned work ID after pure association succeeds.pub fn copyWorkIdToTurn(    alloc: Allocator,    turn: *HistoryTurn,    work_id: []const u8,) (Allocator.Error || WorkIdError)!void {    if (try decideWorkIdAssociation(turn.*, work_id) != .copy_event) return;    const owned = try alloc.dupe(u8, work_id);    switch (turn.*) {        .assistant => |*entry| entry.user.work_id = owned,        .background_command => |*entry| entry.user.work_id = owned,        .interrupted => |*entry| entry.user.work_id = owned,        .compacted_summary => unreachable,    }} pub const freeHistoryTurn = core_types.freeHistoryTurn;pub const dupeToolCall = core_types.dupeToolCall;pub const freeToolCall = core_types.freeToolCall;pub const dupeCompletedToolNames = core_types.dupeCompletedToolNames;pub const freeCompletedToolNames = core_types.freeCompletedToolNames;pub const ExecutionMemory = core_types.ExecutionMemory;pub const ToolExecutionStep = core_types.ToolExecutionStep;pub const PersistedToolResult = core_types.PersistedToolResult;pub const PersistedToolStatus = core_types.PersistedToolStatus;pub const FileEvidence = core_types.FileEvidence;pub const FileEvidenceAction = core_types.FileEvidenceAction;pub const freeExecutionMemory = core_types.freeExecutionMemory;pub const freeToolCallSlice = core_types.freeToolCallSlice;pub const freePersistedToolResults = core_types.freePersistedToolResults; /// Builds a sorted, independently owned attachment catalog from complete/// canonical history plus current-turn images. The caller frees the returned/// slice with `core_types.freeImageAttachmentSlice` using the same allocator.pub fn collect_image_catalog(    alloc: Allocator,    canonical_history: []const HistoryTurn,    current_images: []const ImageAttachment,) (Allocator.Error || error{    ImageCatalogTooLarge,    InvalidImageId,    DuplicateImageId,})![]ImageAttachment {    var attachment_count = current_images.len;    for (canonical_history) |turn| {        attachment_count = std.math.add(            usize,            attachment_count,            images_for_history_turn(turn).len,        ) catch return error.ImageCatalogTooLarge;    }    if (attachment_count == 0) return &.{};     const catalog = try alloc.alloc(ImageAttachment, attachment_count);    errdefer alloc.free(catalog);    var copied: usize = 0;    errdefer for (catalog[0..copied]) |attachment| {        core_types.freeImageAttachment(alloc, attachment);    };     for (canonical_history) |turn| {        for (images_for_history_turn(turn)) |attachment| {            catalog[copied] = try dupeImageAttachment(alloc, attachment);            copied += 1;        }    }    for (current_images) |attachment| {        catalog[copied] = try dupeImageAttachment(alloc, attachment);        copied += 1;    }     sort_utils.sort(ImageAttachment, catalog, {}, image_id_less_than);    var previous_id: ?usize = null;    for (catalog) |attachment| {        if (attachment.id == 0) return error.InvalidImageId;        if (previous_id == attachment.id) return error.DuplicateImageId;        previous_id = attachment.id;    }    std.debug.assert(image_attachments.imageAttachmentsSortedById(catalog));    return catalog;} /// Rebuilds an owned catalog after queue review replaces the current-turn/// attachments. Historical authority is retained; the old current slice must/// still match the catalog exactly or the replacement is rejected as stale.pub fn replace_image_catalog_current_images(    alloc: Allocator,    catalog: []const ImageAttachment,    old_current_images: []const ImageAttachment,    new_current_images: []const ImageAttachment,) (Allocator.Error || error{    ImageCatalogTooLarge,    InvalidImageId,    DuplicateImageId,    StaleImageCatalog,})![]ImageAttachment {    try validate_image_catalog(catalog);     for (old_current_images, 0..) |old_current, index| {        if (old_current.id == 0) return error.InvalidImageId;        for (old_current_images[0..index]) |prior| {            if (prior.id == old_current.id) return error.DuplicateImageId;        }        const authorized = image_attachment_for_id(catalog, old_current.id) orelse            return error.StaleImageCatalog;        if (!image_attachments_equal(authorized, old_current)) return error.StaleImageCatalog;    }     const retained_count = std.math.sub(usize, catalog.len, old_current_images.len) catch        return error.StaleImageCatalog;    const attachment_count = std.math.add(        usize,        retained_count,        new_current_images.len,    ) catch return error.ImageCatalogTooLarge;    if (attachment_count == 0) return &.{};     const replacement = try alloc.alloc(ImageAttachment, attachment_count);    errdefer alloc.free(replacement);    var copied: usize = 0;    errdefer for (replacement[0..copied]) |attachment| {        core_types.freeImageAttachment(alloc, attachment);    };     for (catalog) |attachment| {        if (image_attachment_for_id(old_current_images, attachment.id) != null) continue;        replacement[copied] = try dupeImageAttachment(alloc, attachment);        copied += 1;    }    std.debug.assert(copied == retained_count);    for (new_current_images) |attachment| {        replacement[copied] = try dupeImageAttachment(alloc, attachment);        copied += 1;    }     sort_utils.sort(ImageAttachment, replacement, {}, image_id_less_than);    try validate_image_catalog(replacement);    return replacement;} /// Returns a new owned catalog containing any image authority introduced by a/// completed history turn. Existing IDs must either match exactly or the/// queued catalog is rejected as stale.pub fn merge_image_catalog_history_turn(    alloc: Allocator,    catalog: []const ImageAttachment,    turn: HistoryTurn,) (Allocator.Error || error{    ImageCatalogTooLarge,    InvalidImageId,    DuplicateImageId,    StaleImageCatalog,})![]ImageAttachment {    try validate_image_catalog(catalog);    const additions = images_for_history_turn(turn);    var missing_count: usize = 0;    for (additions, 0..) |addition, index| {        if (addition.id == 0) return error.InvalidImageId;        for (additions[0..index]) |prior| {            if (prior.id == addition.id) return error.DuplicateImageId;        }        if (image_attachment_for_id(catalog, addition.id)) |existing| {            if (!image_attachments_equal(existing, addition)) return error.StaleImageCatalog;        } else {            missing_count += 1;        }    }     const attachment_count = std.math.add(usize, catalog.len, missing_count) catch        return error.ImageCatalogTooLarge;    if (attachment_count == 0) return &.{};    const merged = try alloc.alloc(ImageAttachment, attachment_count);    errdefer alloc.free(merged);    var copied: usize = 0;    errdefer for (merged[0..copied]) |attachment| {        core_types.freeImageAttachment(alloc, attachment);    };    for (catalog) |attachment| {        merged[copied] = try dupeImageAttachment(alloc, attachment);        copied += 1;    }    for (additions) |attachment| {        if (image_attachment_for_id(catalog, attachment.id) != null) continue;        merged[copied] = try dupeImageAttachment(alloc, attachment);        copied += 1;    }    sort_utils.sort(ImageAttachment, merged, {}, image_id_less_than);    try validate_image_catalog(merged);    return merged;} fn validate_image_catalog(catalog: []const ImageAttachment) error{    InvalidImageId,    DuplicateImageId,}!void {    var previous_id: ?usize = null;    for (catalog) |attachment| {        if (attachment.id == 0) return error.InvalidImageId;        if (previous_id != null and previous_id.? >= attachment.id) {            if (previous_id.? == attachment.id) return error.DuplicateImageId;            return error.InvalidImageId;        }        previous_id = attachment.id;    }} fn image_attachment_for_id(    attachments: []const ImageAttachment,    id: usize,) ?ImageAttachment {    for (attachments) |attachment| {        if (attachment.id == id) return attachment;    }    return null;} fn image_attachments_equal(lhs: ImageAttachment, rhs: ImageAttachment) bool {    return lhs.id == rhs.id and        std.mem.eql(u8, lhs.path, rhs.path) and        std.mem.eql(u8, lhs.media_type, rhs.media_type) and        optionalBytesEqual(lhs.snapshot_path, rhs.snapshot_path) and        optionalBytesEqual(lhs.snapshot_sha256, rhs.snapshot_sha256);} fn optionalBytesEqual(lhs: ?[]const u8, rhs: ?[]const u8) bool {    if (lhs == null or rhs == null) return lhs == null and rhs == null;    return std.mem.eql(u8, lhs.?, rhs.?);} fn images_for_history_turn(turn: HistoryTurn) []const ImageAttachment {    return switch (turn) {        .compacted_summary => &.{},        .assistant => |entry| entry.user.images,        .background_command => |entry| entry.user.images,        .interrupted => |entry| entry.user.images,    };} fn mutable_images_for_history_turn(turn: *HistoryTurn) []ImageAttachment {    return switch (turn.*) {        .compacted_summary => &.{},        .assistant => |*entry| entry.user.images,        .background_command => |*entry| entry.user.images,        .interrupted => |*entry| entry.user.images,    };} fn mutable_images_slice_for_history_turn(turn: *HistoryTurn) ?*[]ImageAttachment {    return switch (turn.*) {        .compacted_summary => null,        .assistant => |*entry| &entry.user.images,        .background_command => |*entry| &entry.user.images,        .interrupted => |*entry| &entry.user.images,    };} fn user_text_for_history_turn(turn: HistoryTurn) []const u8 {    return switch (turn) {        .compacted_summary => "",        .assistant => |entry| entry.user.text,        .background_command => |entry| entry.user.text,        .interrupted => |entry| entry.user.text,    };} const LegacyImagePlaceholderIterator = struct {    text: []const u8,    offset: usize = 0,     const prefix = "[Image #";    const Slot = struct {        id: ?usize,        start: usize,        length: usize,    };     fn next(self: *@This()) ?Slot {        const relative_start = std.mem.indexOf(u8, self.text[self.offset..], prefix) orelse            return null;        const start = self.offset + relative_start;        if (image_attachments.matchImagePlaceholder(self.text, start)) |match| {            self.offset = start + match.length;            return .{ .id = match.id, .start = start, .length = match.length };        }         const body_start = start + prefix.len;        self.offset = if (std.mem.indexOfScalar(u8, self.text[body_start..], ']')) |relative_end|            body_start + relative_end + 1        else            body_start;        return .{ .id = null, .start = start, .length = self.offset - start };    }}; const LegacyImageRepair = struct {    turn_index: usize,    image_index: usize,    placeholder: ?LegacyImagePlaceholderIterator.Slot,    assigned_id: usize = 0,}; const LegacyTurnTextRepair = struct {    turn_index: usize,    text: []u8,}; fn history_placeholder_id_count(history: []const HistoryTurn, id: usize) usize {    var count: usize = 0;    for (history) |turn| {        var placeholders = LegacyImagePlaceholderIterator{            .text = user_text_for_history_turn(turn),        };        while (placeholders.next()) |placeholder| {            if (placeholder.id != null and placeholder.id.? == id) count += 1;        }    }    return count;} fn legacy_placeholder_claim(    history: []const HistoryTurn,    placeholder: ?LegacyImagePlaceholderIterator.Slot,) ?usize {    const slot = placeholder orelse return null;    const placeholder_id = slot.id orelse return null;    if (placeholder_id == 0) return null;    if (history_placeholder_id_count(history, placeholder_id) != 1) return null;    if (history_contains_image_id(history, placeholder_id)) return null;    return placeholder_id;} fn legacy_repairs_contain_id(repairs: []const LegacyImageRepair, id: usize) bool {    for (repairs) |repair| {        if (repair.assigned_id == id) return true;    }    return false;} fn assign_legacy_image_ids(    history: []const HistoryTurn,    repairs: []LegacyImageRepair,) error{ImageIdOverflow}!void {    for (repairs) |*repair| {        repair.assigned_id = legacy_placeholder_claim(history, repair.placeholder) orelse 0;    }     var candidate: usize = 1;    for (repairs) |*repair| {        if (repair.assigned_id != 0) continue;        while (history_contains_image_id(history, candidate) or            legacy_repairs_contain_id(repairs, candidate))        {            candidate = std.math.add(usize, candidate, 1) catch                return error.ImageIdOverflow;        }        repair.assigned_id = candidate;    }} fn rebuilt_legacy_turn_text(    alloc: Allocator,    history: []const HistoryTurn,

Lines 1–500 of 6283.