5224 lines · 211.4 KB · Zig
RawDownload
const std = @import("std");const secret = @import("../auth/secret.zig");const io_mod = @import("../shared/io.zig");const debug_trace = @import("../shared/debug_trace.zig");const diff_mod = @import("../output/diff.zig");const file_mutation_contract = @import("../tooling/file_mutation_contract.zig");const image_attachments = @import("../images/image_attachments.zig");const context_contract = @import("../workspace/context_contract.zig");const sandbox = @import("../permissions/sandbox.zig");const auto_classifier_context = @import("../permissions/auto_classifier_context.zig");const permission_request = @import("../permissions/permission_request.zig");const notification_contract = @import("../notifications/notification_contract.zig");const session_runtime = @import("../session/session.zig");const session_codec = @import("../session/session_codec.zig");const skill_contract = @import("../skills/skill_contract.zig");const tool_result_limits = @import("../tooling/tool_result_limits.zig");const command_output_content = @import("../tooling/command_output_content.zig");const paste_blocks = @import("../input/pasted_blocks.zig");const entity_spans = @import("../shared/entity_spans.zig");const types = @import("../shared/types.zig");const assistant_presentation = @import("assistant_presentation.zig"); pub const AgentTurnSettings = struct {    max_tool_result_bytes: usize = tool_result_limits.default_max_tool_result_bytes,    first_call_tool_choice: types.ToolChoice = .auto,    fast_mode: bool = false,    effort: types.ReasoningEffort = .auto,}; pub const SkillBinding = struct {    name: []u8,    path: []u8,}; pub const SkillDisplaySpan = struct {    raw_start: usize,    raw_end: usize,    name: []u8,    path: []u8,    display_source: ?skill_contract.SkillSource = null,    owns_trailing_separator: bool = false,}; pub const BeginPromptWithSkillBindings = struct {    prompt: types.UserTurn,    skill_bindings: []SkillBinding = &.{},    skill_display_spans: []SkillDisplaySpan = &.{},}; pub const QueueReviewDraft = struct {    input: []u8,    pasted_blocks: []paste_blocks.PastedBlock = &.{},    image_tokens: []entity_spans.ImageTokenSpan = &.{},    skill_display_spans: []SkillDisplaySpan = &.{},}; pub const QueuedPrompt = struct {    turn_id: u64 = 0,    prompt: []u8,    images: []types.ImageAttachment,    authorized_image_catalog: []types.ImageAttachment = &.{},    model: []u8,    api_key: []u8,    gateway_team: ?[]u8 = null,    credential_source: ?types.CredentialSource = null,    permission_mode: types.PermissionMode,    sandbox_backend: sandbox.BackendKind = .none,    history: []types.HistoryTurn,    root_user_intent_context: []u8 = &.{},    grants: []types.PermissionGrant,    skill_bindings: []SkillBinding = &.{},    skill_display_spans: []SkillDisplaySpan = &.{},    review_draft: ?QueueReviewDraft = null,    context_snapshot: context_contract.GatheredContextSnapshot = .{},    agent_settings: AgentTurnSettings = .{},    snapshot_file_ownerships: []types.SnapshotFileOwnership = &.{},    /// Present only when an explicit host action resumes a durable paused    /// model turn. The same ownership rules as the other queued fields apply.    recovery_checkpoint: ?session_codec.RecoveryCheckpoint = null,    /// True when this output surface already contains the checkpoint's    /// assistant source. Recovery still restores the source for overlap and    /// persistence, but publishes only novel continuation text.    recovery_source_already_presented: bool = false,}; pub const ActivePromptSnapshotOwnership = struct {    images: []const types.ImageAttachment,    state: enum {        active,        handed_off,        discarded,    } = .active,    shared_ownership: ?types.SnapshotFileOwnership = null,     pub fn init(images: []const types.ImageAttachment) ActivePromptSnapshotOwnership {        return .{ .images = images };    }     fn ensureSharedOwnership(        self: *ActivePromptSnapshotOwnership,        alloc: std.mem.Allocator,    ) !?types.SnapshotFileOwnership {        if (self.images.len == 0) return null;        if (self.shared_ownership) |ownership| return ownership;        const ownership = (try SnapshotFileOwnershipState.create(alloc, self.images)).handle();        self.shared_ownership = ownership;        return ownership;    }     fn handoff(        self: *ActivePromptSnapshotOwnership,        alloc: std.mem.Allocator,    ) !?types.SnapshotFileOwnership {        if (self.state != .active or self.images.len == 0) return null;        const ownership = (try self.ensureSharedOwnership(alloc)).?;        self.state = .handed_off;        return ownership;    }     fn deinit(self: *ActivePromptSnapshotOwnership) void {        if (self.state == .active) {            if (self.shared_ownership) |ownership| {                ownership.release();            } else {                image_attachments.deleteUnreferencedImageSnapshots(self.images, &.{});            }            self.state = .discarded;        }    }}; const SnapshotFileOwnershipState = struct {    alloc: std.mem.Allocator,    images: []types.ImageAttachment,    references: std.atomic.Value(usize) = std.atomic.Value(usize).init(1),    transferred: std.atomic.Value(bool) = std.atomic.Value(bool).init(false),     fn create(        alloc: std.mem.Allocator,        images: []const types.ImageAttachment,    ) !*SnapshotFileOwnershipState {        const owned_images = try types.dupeImageAttachmentSlice(alloc, images);        errdefer types.freeImageAttachmentSlice(alloc, owned_images);        const state = try alloc.create(SnapshotFileOwnershipState);        state.* = .{            .alloc = alloc,            .images = owned_images,        };        return state;    }     fn handle(self: *SnapshotFileOwnershipState) types.SnapshotFileOwnership {        return .{            .ctx = self,            .retain_fn = retain,            .release_fn = release,            .transfer_fn = transfer,        };    }     fn retain(raw: *anyopaque) void {        const self: *SnapshotFileOwnershipState = @ptrCast(@alignCast(raw));        _ = self.references.fetchAdd(1, .seq_cst);    }     fn release(raw: *anyopaque) void {        const self: *SnapshotFileOwnershipState = @ptrCast(@alignCast(raw));        if (self.references.fetchSub(1, .seq_cst) != 1) return;        if (!self.transferred.load(.seq_cst)) {            image_attachments.deleteUnreferencedImageSnapshots(self.images, &.{});        }        const alloc = self.alloc;        types.freeImageAttachmentSlice(alloc, self.images);        alloc.destroy(self);    }     fn transfer(raw: *anyopaque) void {        const self: *SnapshotFileOwnershipState = @ptrCast(@alignCast(raw));        self.transferred.store(true, .seq_cst);    }}; pub const PermissionSnapshot = struct {    mode: types.PermissionMode,    sandbox_backend: sandbox.BackendKind,}; pub const CommandOutputChunk = struct {    lifecycle_id: ?types.ToolLifecycleId = null,    stream: command_output_content.Stream,    text: []u8,}; pub const QueuePreview = struct {    count: usize = 0,    paused: bool = false,}; pub const QueueReviewReason = enum {    manual,    post_cancel,}; pub const QueuedPromptDraft = struct {    turn_id: u64,    prompt: []u8,    images: []types.ImageAttachment,    skill_display_spans: []SkillDisplaySpan,    review_draft: ?QueueReviewDraft = null,     pub fn reviewInput(self: QueuedPromptDraft) []const u8 {        return if (self.review_draft) |review| review.input else self.prompt;    }     pub fn reviewSkillDisplaySpans(        self: QueuedPromptDraft,    ) []const SkillDisplaySpan {        return if (self.review_draft) |review|            review.skill_display_spans        else            self.skill_display_spans;    }}; const PreparedQueuedPromptDraft = struct {    turn_id: u64,    prompt: []u8,    images: []types.ImageAttachment,    authorized_image_catalog: []types.ImageAttachment,    skill_bindings: []SkillBinding,    skill_display_spans: []SkillDisplaySpan,    review_draft: ?QueueReviewDraft,}; const PreparedHistoryPropagation = struct {    history: []types.HistoryTurn,    root_user_intent_context: []u8,    authorized_image_catalog: []types.ImageAttachment,    snapshot_file_ownerships: ?[]types.SnapshotFileOwnership,    added_snapshot_ownership: ?types.SnapshotFileOwnership,     fn deinit(self: PreparedHistoryPropagation, alloc: std.mem.Allocator) void {        types.freeHistoryTurnSlice(alloc, self.history);        alloc.free(self.root_user_intent_context);        types.freeImageAttachmentSlice(alloc, self.authorized_image_catalog);        if (self.added_snapshot_ownership) |ownership| ownership.release();        if (self.snapshot_file_ownerships) |ownerships| alloc.free(ownerships);    }}; pub fn freeQueuedPromptDraft(alloc: std.mem.Allocator, draft: QueuedPromptDraft) void {    alloc.free(draft.prompt);    types.freeImageAttachmentSlice(alloc, draft.images);    freeSkillDisplaySpans(alloc, draft.skill_display_spans);    freeQueueReviewDraftOpt(alloc, draft.review_draft);} pub fn freeQueuedPromptDrafts(alloc: std.mem.Allocator, drafts: []QueuedPromptDraft) void {    for (drafts) |draft| freeQueuedPromptDraft(alloc, draft);    if (drafts.len > 0) alloc.free(drafts);} pub fn dupeQueueReviewDraft(    alloc: std.mem.Allocator,    draft: QueueReviewDraft,) !QueueReviewDraft {    const input = try alloc.dupe(u8, draft.input);    errdefer alloc.free(input);     const pasted_blocks: []paste_blocks.PastedBlock = if (draft.pasted_blocks.len > 0)        try alloc.alloc(paste_blocks.PastedBlock, draft.pasted_blocks.len)    else        @constCast(&.{});    var pasted_count: usize = 0;    errdefer {        for (pasted_blocks[0..pasted_count]) |block| alloc.free(block.text);        if (pasted_blocks.len > 0) alloc.free(pasted_blocks);    }    while (pasted_count < draft.pasted_blocks.len) : (pasted_count += 1) {        pasted_blocks[pasted_count] = .{            .id = draft.pasted_blocks[pasted_count].id,            .text = try alloc.dupe(u8, draft.pasted_blocks[pasted_count].text),            .line_count = draft.pasted_blocks[pasted_count].line_count,            .span = draft.pasted_blocks[pasted_count].span,        };    }     const image_tokens: []entity_spans.ImageTokenSpan = if (draft.image_tokens.len > 0)        try alloc.dupe(entity_spans.ImageTokenSpan, draft.image_tokens)    else        @constCast(&.{});    errdefer if (image_tokens.len > 0) alloc.free(image_tokens);     const skill_display_spans = try dupeSkillDisplaySpans(        alloc,        draft.skill_display_spans,    );    return .{        .input = input,        .pasted_blocks = pasted_blocks,        .image_tokens = image_tokens,        .skill_display_spans = skill_display_spans,    };} pub fn freeQueueReviewDraft(    alloc: std.mem.Allocator,    draft: QueueReviewDraft,) void {    alloc.free(draft.input);    for (draft.pasted_blocks) |block| alloc.free(block.text);    if (draft.pasted_blocks.len > 0) alloc.free(draft.pasted_blocks);    if (draft.image_tokens.len > 0) alloc.free(draft.image_tokens);    freeSkillDisplaySpans(alloc, draft.skill_display_spans);} fn freeQueueReviewDraftOpt(    alloc: std.mem.Allocator,    draft: ?QueueReviewDraft,) void {    if (draft) |owned| freeQueueReviewDraft(alloc, owned);} fn freePreparedQueuedPromptDraft(alloc: std.mem.Allocator, draft: PreparedQueuedPromptDraft) void {    alloc.free(draft.prompt);    types.freeImageAttachmentSlice(alloc, draft.images);    types.freeImageAttachmentSlice(alloc, draft.authorized_image_catalog);    freeSkillBindings(alloc, draft.skill_bindings);    freeSkillDisplaySpans(alloc, draft.skill_display_spans);    freeQueueReviewDraftOpt(alloc, draft.review_draft);} fn skillBindingsFromDisplaySpans(    alloc: std.mem.Allocator,    spans: []const SkillDisplaySpan,) ![]SkillBinding {    var bindings: std.ArrayList(SkillBinding) = .empty;    errdefer {        for (bindings.items) |binding| {            alloc.free(binding.name);            alloc.free(binding.path);        }        bindings.deinit(alloc);    }    for (spans) |span| {        var already_present = false;        for (bindings.items) |binding| {            if (std.mem.eql(u8, binding.path, span.path)) {                already_present = true;                break;            }        }        if (already_present) continue;         const name = try alloc.dupe(u8, span.name);        errdefer alloc.free(name);        const path = try alloc.dupe(u8, span.path);        errdefer alloc.free(path);        try bindings.append(alloc, .{ .name = name, .path = path });    }    if (bindings.items.len == 0) {        bindings.deinit(alloc);        return &.{};    }    return bindings.toOwnedSlice(alloc);} pub const StateSnapshot = struct {    processing: bool = false,    active_turn_id: u64 = 0,    queued_count: usize = 0,    pending_event_count: usize = 0,    queue_review_reason: ?QueueReviewReason = null,    cancel_requested: bool = false,    pending_permission_request: ?permission_request.OwnedPermissionRequest = null,    pending_permission_review: ?PendingPermissionReview = null,     pub fn deinit(self: StateSnapshot, alloc: std.mem.Allocator) void {        if (self.pending_permission_request) |request| {            var owned = request;            owned.deinit(alloc);        }    }}; pub const PendingPermissionReview = struct {    request_id: u64,    review: *const diff_mod.FileReview,}; pub const FinalizationFailure = struct {    turn_id: u64,    outcome: types.TurnPresentationOutcome,}; pub const InteractiveAdmissionSnapshot = union(enum) {    open,    stopped,    finalization_failed: FinalizationFailure,}; pub const PermissionSubmissionResult = enum {    accepted,    stale,    no_pending,}; const OwnedQuestionOption = struct {    label: []u8,    description: ?[]u8 = null,}; const OwnedQuestionEntry = struct {    question: []u8,    options: []OwnedQuestionOption,}; pub const QuestionPromptSource = enum {    agent_question,    route_recovery,    mcp_elicitation,}; const OwnedQuestionBatch = struct {    entries: []OwnedQuestionEntry,    source: QuestionPromptSource = .agent_question,}; const QuestionResponse = union(enum) {    pending,    cancelled,    answered: [][]u8,}; pub const PendingQuestionBatchSnapshot = struct {    entries: []types.QuestionBatchEntry,    source: QuestionPromptSource = .agent_question,     pub fn deinit(self: PendingQuestionBatchSnapshot, alloc: std.mem.Allocator) void {        for (self.entries) |entry| {            alloc.free(@constCast(entry.question));            for (entry.options) |opt| {                alloc.free(@constCast(opt.label));                if (opt.description) |desc| alloc.free(@constCast(desc));            }            alloc.free(@constCast(entry.options));        }        alloc.free(self.entries);    }}; pub const WorkerEvent = union(enum) {    begin_prompt: types.UserTurn,    begin_prompt_with_skill_bindings: BeginPromptWithSkillBindings,    append_user_feedback: []u8,    assistant_presentation: assistant_presentation.Event,    notification: notification_contract.Notification,    question_requested,    open_model_picker,    semantic_notice: types.SemanticNotice,    route_recovery_status: types.RouteRecoveryStatus,    clear_route_recovery_status,    api_status_text: []u8,    command_output: CommandOutputChunk,    command_output_complete: ?types.ToolLifecycleId,    tool_lifecycle: types.ToolLifecycleEvent,    turn_token_update: types.TurnTokenProgress,    /// A tool call that publishes no status row of its own began streaming its    /// arguments. The turn is working, with nothing to print until it lands.    tool_payload_started,    diff_block: diff_mod.DiffEntryPayload,    finish_prompt: types.FinishedPrompt,    session_grant: types.PermissionGrant,    error_text: types.SemanticNotice,}; pub const WorkerEventBatch = struct {    events: std.ArrayList(WorkerEvent),    cancel_requested: bool,}; pub const WorkerRuntime = struct {    worker_mutex: std.Io.Mutex = .init,    worker_cond: std.Io.Condition = .init,    queued_prompts: std.ArrayList(QueuedPrompt) = .empty,    worker_events: std.ArrayList(WorkerEvent) = .empty,    worker_processing: bool = false,    active_turn_id: u64 = 0,    worker_stop_requested: bool = false,    finalization_failure: ?FinalizationFailure = null,    worker_cancel_requested: std.atomic.Value(bool) = std.atomic.Value(bool).init(false),    worker_recovery_pause_requested: std.atomic.Value(bool) = std.atomic.Value(bool).init(false),    worker_connectivity_wait_active: std.atomic.Value(bool) = std.atomic.Value(bool).init(false),    /// Set under `worker_mutex` once the active turn publishes its terminal    /// recovery pause. The turn may still be finalizing, but cannot perform    /// more model work, so one continuation may queue before processing clears.    recovery_continuation_ready: bool = false,    queued_prompt_count: usize = 0,    /// `null` means admission is open; otherwise queue take is paused for review.    queue_admission: ?QueueReviewReason = null,

Lines 1–500 of 5224.