const std = @import("std");const agent_runtime = @import("../agent/agent_runtime.zig");const agent_stream_provider = @import("../agent/stream_provider.zig");const command_admission = @import("../permissions/command_admission.zig");const permission_auto_classifier = @import("../permissions/auto_classifier.zig");const auth_runtime = @import("../auth/auth_runtime.zig");const credentials = @import("../auth/credentials.zig");const model_capabilities = @import("../config/model_capabilities.zig");const input_completion_runtime = @import("input_completion_runtime.zig");const core_input_runtime = @import("../input/runtime.zig");const app_worker_runtime = @import("app_worker_runtime.zig");const app_session_runtime = @import("app_session_runtime.zig");const change_tracker_mod = @import("../workspace/change_tracker.zig");const debug_trace = @import("../shared/debug_trace.zig");const diagnostics = @import("../workspace/diagnostics.zig");const diff_mod = @import("../output/diff.zig");const file_mutation = @import("../tooling/file_mutation.zig");const file_mutation_contract = @import("../tooling/file_mutation_contract.zig");const command_output_content = @import("../tooling/command_output_content.zig");const tool_admission = @import("../tooling/tool_admission.zig");const gateway_error_format = @import("../shared/gateway_error_format.zig");const io_mod = @import("../shared/io.zig");const session_runtime = @import("../session/session.zig");const session_codec = @import("../session/session_codec.zig");const session_usage = @import("../session/session_usage.zig");const parent_delivery_projector = @import("../subagent/parent_delivery_projector.zig");const task_helpers = @import("../tasks/task_helpers.zig");const types = @import("../shared/types.zig");const worker_runtime = @import("../agent/worker_runtime.zig");const assistant_presentation = @import("../agent/assistant_presentation.zig");const activity_runtime = @import("../output/activity_runtime.zig");const assistant_pacer = @import("../../ui/assistant/pacer.zig");const ui_render = @import("../../ui/render.zig");const render_request = @import("../../ui/render_request.zig");const transcript_runtime = @import("../../ui/transcript/runtime.zig"); const Allocator = std.mem.Allocator;const ChatMessage = types.ChatMessage;const HistoryTurn = types.HistoryTurn;const PermissionGrant = types.PermissionGrant;const PermissionMode = types.PermissionMode;const ToolCall = types.ToolCall;const ToolExecutionResult = agent_runtime.ToolExecutionResult;const WorkerEvent = worker_runtime.WorkerEvent; const reset_style = ui_render.reset_style; fn preparedDiffPayload( alloc: Allocator, handoff: file_mutation.CommittedFileHandoff,) !diff_mod.DiffEntryPayload { return preparedDiffPayloadWithFullAllocator(alloc, alloc, handoff);} fn preparedDiffPayloadWithFullAllocator( alloc: Allocator, full_alloc: Allocator, handoff: file_mutation.CommittedFileHandoff,) !diff_mod.DiffEntryPayload { return diff_mod.formatFileChangePayload( alloc, full_alloc, handoff.preview, handoff.tracker.previous_content, if (handoff.full_view) |snapshot| .{ .after_content = snapshot.after_content, .lifecycle_id = snapshot.lifecycle_id, } else null, .{ .added_fg = ui_render.diff_added_style, .removed_fg = ui_render.diff_removed_style, .context_fg = ui_render.dim_style, .added_marker_fg = ui_render.diff_added_marker_style, .removed_marker_fg = ui_render.diff_removed_marker_style, .reset = ui_render.reset_style, }, );} test "prepared preview formatter preserves context and informative elision" { const preview: diff_mod.FileChangePreview = .{ .path = "note.txt", .lines = &.{ .{ .op = .context, .old_line = 1, .new_line = 1, .text = "alpha", }, .{ .op = .deletion, .old_line = 2, .text = "before", }, .{ .op = .addition, .new_line = 2, .text = "after", }, .{ .op = .elision, .text = "⋯ +2 -1 omitted", }, }, .additions = 3, .deletions = 2, .truncated = true, }; const formatted = try diff_mod.formatFileChangePreview( std.testing.allocator, preview, .{ .added_fg = "", .removed_fg = "", .context_fg = "", .reset = "", }, ); defer std.testing.allocator.free(formatted); try std.testing.expect(std.mem.find(u8, formatted, "alpha") != null); try std.testing.expect( std.mem.find(u8, formatted, "⋯ +2 -1 omitted") != null, );} test "prepared diff payload keeps compact elision and retains the complete approved review" { const alloc = std.testing.allocator; var after: std.ArrayList(u8) = .empty; defer after.deinit(alloc); for (1..121) |line_number| { var line: [32]u8 = undefined; try after.appendSlice( alloc, try std.fmt.bufPrint(&line, "full-review-{d:0>3}\n", .{line_number}), ); } const preview_lines = [_]diff_mod.PreviewLine{ .{ .op = .addition, .new_line = 1, .text = "full-review-001" }, .{ .op = .elision, .text = "⋯ +118 omitted" }, .{ .op = .addition, .new_line = 120, .text = "full-review-120" }, }; const handoff = file_mutation.CommittedFileHandoff.init( .{ .path = "full-review.txt", .lines = &preview_lines, .additions = 120, .deletions = 0, .truncated = true, }, .{ .kind = .write, .raw_path = "/tmp/full-review.txt", .previous_content = null, .committed_at_ms = 0, }, ); var enriched_handoff = handoff; enriched_handoff.full_view = .{ .after_content = after.items, .lifecycle_id = .{ .turn_id = 39, .call_id = "full-review" }, }; const payload = try preparedDiffPayload(alloc, enriched_handoff); defer diff_mod.freeDiffEntryPayload(alloc, payload); try std.testing.expect(std.mem.indexOf(u8, payload.preview, "⋯ +118 omitted") != null); const full = payload.full orelse return error.TestExpectedFullDiff; try std.testing.expect(std.mem.indexOf(u8, full.content, "full-review-001") != null); try std.testing.expect(std.mem.indexOf(u8, full.content, "full-review-120") != null); try std.testing.expect(std.mem.indexOf(u8, full.content, "⋯ +118 omitted") == null);} test "prepared diff payload keeps compact output when full formatting is unavailable" { const handoff = testCommittedFileHandoff(); var enriched_handoff = handoff; enriched_handoff.full_view = .{ .after_content = "after\n", .lifecycle_id = .{ .turn_id = 40, .call_id = "full-format-fallback" }, }; var failing = std.testing.FailingAllocator.init( std.testing.allocator, .{ .fail_index = 0 }, ); const payload = try preparedDiffPayloadWithFullAllocator( std.testing.allocator, failing.allocator(), enriched_handoff, ); defer diff_mod.freeDiffEntryPayload(std.testing.allocator, payload); try std.testing.expect(payload.full == null); try std.testing.expect(std.mem.indexOf(u8, payload.preview, "after") != null);} test "full diff formatter renders unchanged review elisions" { const before = "same-01\n" ++ "same-02\n" ++ "same-03\n" ++ "same-04\n" ++ "same-05\n" ++ "same-06\n" ++ "same-07\n" ++ "same-08\n" ++ "same-09\n" ++ "same-10\n" ++ "old\n"; const after = "same-01\n" ++ "same-02\n" ++ "same-03\n" ++ "same-04\n" ++ "same-05\n" ++ "same-06\n" ++ "same-07\n" ++ "same-08\n" ++ "same-09\n" ++ "same-10\n" ++ "new\n"; const preview_lines = [_]diff_mod.PreviewLine{ .{ .op = .deletion, .old_line = 11, .text = "old" }, .{ .op = .addition, .new_line = 11, .text = "new" }, }; var handoff = file_mutation.CommittedFileHandoff.init( .{ .path = "elision.txt", .lines = &preview_lines, .additions = 1, .deletions = 1, .truncated = false, }, .{ .kind = .edit, .raw_path = "/tmp/elision.txt", .previous_content = before, .committed_at_ms = 0, }, ); handoff.full_view = .{ .after_content = after, .lifecycle_id = .{ .turn_id = 41, .call_id = "context-elision" }, }; const snapshot = handoff.full_view orelse return error.TestExpectedFullDiff; const full = (try diff_mod.formatFileChangeFullDiff( std.testing.allocator, handoff.tracker.previous_content, .{ .after_content = snapshot.after_content, .lifecycle_id = snapshot.lifecycle_id, }, .{}, )) orelse return error.TestExpectedFullDiff; defer full.deinit(std.testing.allocator); try std.testing.expect(std.mem.indexOf(u8, full.content, "5 unchanged lines ⋯") != null); try std.testing.expect(std.mem.indexOf(u8, full.content, "old") != null); try std.testing.expect(std.mem.indexOf(u8, full.content, "new") != null);} pub fn Bindings(comptime App: type) type { return struct { pub fn agentRuntimeDeps(app: *App) agent_runtime.AgentRuntimeDeps { var deps: agent_runtime.AgentRuntimeDeps = .{ .ctx = @ptrCast(app), .agent_stream_provider = if (comptime @hasDecl(App, "agentStreamProvider")) app.agentStreamProvider() else agent_stream_provider.unavailable_provider, .cooperative_transport_pulse = if (comptime @hasDecl(App, "cooperativeTransportPulse")) .{ .ctx = @ptrCast(app), .run = cooperativeTransportPulse, } else null, .tool_registry = if (comptime @hasDecl(App, "toolRegistry")) app.toolRegistry() else .{}, .context_registry = if (comptime @hasDecl(App, "contextRegistry")) app.contextRegistry() else null, .context_enabled = if (comptime @hasField(App, "context_enabled")) app.context_enabled else false, .finalize_turn = agentFinalizeTurn, .prepare_parent_turn_context = agentPrepareParentTurnContext, .acknowledge_parent_turn_context = agentAcknowledgeParentTurnContext, .append_runtime_context = agentAppendRuntimeContext, .append_static_context = agentAppendStaticContext, .validate_tool_call = agentValidateToolCall, .check_tool_availability = agentCheckToolAvailability, .request_tool_permission = agentRequestToolPermission, .request_prepared_file_mutation_permission = agentRequestPreparedFileMutationPermission, .describe_tool_action = agentDescribeToolAction, .describe_tool_action_completed = agentDescribeToolActionCompleted, .describe_tool_action_denied = agentDescribeToolActionDenied, .permission_target_for_call = agentPermissionTargetForCall, .execute_tool_call = agentExecuteToolCall, .publish_committed_file_handoff = agentPublishCommittedFileHandoff, .propagate_history_turn = agentPropagateHistoryTurn, .recovery_checkpoint = if (comptime @hasField(App, "session_persistence")) if (app.session_persistence.writable != null) .{ .set = agentSetRecoveryCheckpoint, } else null else null, .propagate_grant = agentPropagateGrant, .push_event = agentPushEvent, .push_text = agentPushText, .push_tool_lifecycle = agentPushToolLifecycle, .push_diff_block = agentPushDiffBlock, .push_system_notice = agentPushSystemNotice, .push_interactive_notice = agentPushInteractiveNotice, .push_context_notice = agentPushContextNotice, .push_route_recovery_status = agentPushRouteRecoveryStatus, .push_command_output_complete = agentPushCommandOutputComplete, .push_http_error = agentPushHttpError, .refresh_gateway_credential = if (comptime @hasField(App, "auth")) refreshGatewayCredential else null, .request_route_recovery = if (comptime @hasDecl(@TypeOf(app.worker), "requestRouteRecoveryAnswerBlocking")) agentRequestRouteRecovery else null, .available_model_capabilities = agentAvailableModelCapabilities, .resolve_model_capabilities = agentResolveModelCapabilities, .format_tool_execution_error = agentFormatToolExecutionError, .record_tool_call_rejected = agentRecordToolCallRejected, .report_usage = agentReportUsage, .report_inner_tool_usage = agentReportInnerToolUsage, .usage_allocator = app.alloc, .diff_marker_styles = .{ .added = ui_render.diff_added_marker_style, .removed = ui_render.diff_removed_marker_style, }, }; if (comptime @hasField(@TypeOf(app.session), "usage")) { deps.usage = &app.session.usage; if (comptime @hasField(App, "session_persistence") and @hasField(@TypeOf(app.session_persistence), "writable")) { app.session.usage.configureCheckpointSink( if (app.session_persistence.writable != null) .{ .context = @ptrCast(app), .allocator = app.alloc, .persist = agentPersistUsageCheckpoint, } else null, ); } } if (comptime @hasDecl(App, "requestSandboxWideningSyncWithAdvertised")) { deps.request_sandbox_widening = agentRequestSandboxWidening; } return deps; } fn refreshGatewayCredential( raw_ctx: *anyopaque, alloc: std.mem.Allocator, source: credentials.Source, mode: auth_runtime.CredentialRefreshMode, ) !?[]u8 { const app: *App = @ptrCast(@alignCast(raw_ctx)); return auth_runtime.refreshFxLoginToken( app.auth.oauthTransport(), alloc, source, mode, ); } pub fn modelCapabilityResolver(app: *App) model_capabilities.Resolver { return .{ .ctx = @ptrCast(app), .resolve_fn = agentResolveModelCapabilities, }; } pub fn semanticPresentationSink(app: *App) agent_runtime.SemanticPresentationSink { return .{ .ctx = @ptrCast(app), .table = agentPushTable, .code_block = agentPushCodeBlock, .thematic_rule = agentPushThematicRule, }; } pub fn workerEventHandlers(app: *App) app_worker_runtime.WorkerEventHandlers { return .{ .ctx = @ptrCast(app), .tool_lifecycle = worker_tool_lifecycle_presenter(app), .write_user_prompt = workerBridgeWriteUserPrompt, .write_user_prompt_with_skill_bindings = workerBridgeWriteUserPromptWithSkillBindings, .append_text = workerBridgeAppendText, .append_table = workerBridgeAppendTable, .append_code_block = workerBridgeAppendCodeBlock, .append_thematic_rule = workerBridgeAppendThematicRule, .drain_assistant_text = workerBridgeDrainAssistantText, .open_model_picker = workerBridgeOpenModelPicker, .semantic_notice = workerBridgeSemanticNotice, .command_output = workerBridgeCommandOutput, .command_output_complete = workerBridgeCommandOutputComplete, .diff_block = workerBridgeDiffBlock, .append_history_turn = workerBridgeAppendHistoryTurn, .session_grant = workerBridgeSessionGrant, .error_text = workerBridgeErrorText, }; } pub fn worker_tool_lifecycle_presenter( app: *App, ) activity_runtime.LifecyclePresenter { return .{ .ctx = @ptrCast(app), .snapshot_fn = worker_tool_lifecycle_snapshot, .record_fn = worker_tool_lifecycle_record, .apply_fn = worker_apply_tool_lifecycle, .finish_batch_fn = worker_finish_tool_lifecycle_batch, }; } fn worker_tool_lifecycle_snapshot( raw_ctx: *anyopaque, ) activity_runtime.LifecycleSnapshot { const app: *App = @ptrCast(@alignCast(raw_ctx)); return .{ .finalized_turn_watermark = app.shell.finalizedToolTurnWatermark(), .active_tool_count = app.shell.activeToolActivityCount(), .focused_entry_id = app.shell.focusedToolEntryId(), .focused_activity_kind = app.shell.focusedToolActivityKind(), .activity = app.shell.activityProjection(), }; } fn worker_tool_lifecycle_record( raw_ctx: *anyopaque, id: types.ToolLifecycleId, ) ?activity_runtime.ToolPresentationRecord { const app: *App = @ptrCast(@alignCast(raw_ctx)); return app.shell.toolActivityRecord(id); } fn worker_apply_tool_lifecycle( raw_ctx: *anyopaque, alloc: std.mem.Allocator, event: types.ToolLifecycleEvent, ) !activity_runtime.LifecycleTransition { const app: *App = @ptrCast(@alignCast(raw_ctx)); const previous_focused_entry_id = app.shell.focusedToolEntryId(); const preserve_normal_buffer_anchor = if (comptime @hasField(App, "terminal")) app.terminal.alternate_screen_owner != .none else false; const applied_activity_kind = if (preserve_normal_buffer_anchor) try app.shell.applyToolLifecyclePreservingNormalBufferAnchor(alloc, event) else try app.shell.applyToolLifecycle(alloc, event); return .{ .previous_focused_entry_id = previous_focused_entry_id, .snapshot = worker_tool_lifecycle_snapshot(raw_ctx), .applied_activity_kind = applied_activity_kind, .terminal_record = switch (event) { .terminal => |terminal| app.shell.toolActivityRecord(terminal.id), .provisional, .authoritative_started, .progress, .turn_finished => null, }, }; } fn worker_finish_tool_lifecycle_batch( raw_ctx: *anyopaque, alloc: std.mem.Allocator, ) !void { const app: *App = @ptrCast(@alignCast(raw_ctx)); try app.shell.finishLifecycleBatch(alloc); } pub fn onCommandOutputChunk(ctx: *anyopaque, lifecycle_id: ?types.ToolLifecycleId, stream: command_output_content.Stream, chunk: []const u8) !void { const app: *App = @ptrCast(@alignCast(ctx)); if (app.worker.worker_cancel_requested.load(.seq_cst)) return; try app_worker_runtime.Runtime(App).pushCommandOutput(app, lifecycle_id, stream, chunk); } pub fn onMcpProgress(ctx: *anyopaque, lifecycle_id: types.ToolLifecycleId, text: []const u8) void { const app: *App = @ptrCast(@alignCast(ctx)); var label_buf: [512]u8 = undefined; const label = std.fmt.bufPrint( &label_buf, "{s}● {s}{s}", .{ ui_render.bold_style, text, reset_style }, ) catch ui_render.bold_style ++ "● MCP progress" ++ reset_style; app_worker_runtime.Runtime(App).pushToolLifecycle(app, .{ .progress = .{ .id = lifecycle_id, .text = label, } }) catch |err| { debug_trace.logf("mcp", "failed to publish MCP tool progress err={s}", .{@errorName(err)}); return; }; debug_trace.logf("mcp", "queued MCP tool progress turn_id={d}", .{lifecycle_id.turn_id});Lines 1–500 of 2323. Load more
const std = @import("std"); const agent_runtime = @import("../agent/agent_runtime.zig"); const agent_stream_provider = @import("../agent/stream_provider.zig"); const command_admission = @import("../permissions/command_admission.zig"); const permission_auto_classifier = @import("../permissions/auto_classifier.zig"); const auth_runtime = @import("../auth/auth_runtime.zig"); const credentials = @import("../auth/credentials.zig"); const model_capabilities = @import("../config/model_capabilities.zig"); const input_completion_runtime = @import("input_completion_runtime.zig"); const core_input_runtime = @import("../input/runtime.zig"); const app_worker_runtime = @import("app_worker_runtime.zig"); const app_session_runtime = @import("app_session_runtime.zig"); const change_tracker_mod = @import("../workspace/change_tracker.zig"); const debug_trace = @import("../shared/debug_trace.zig"); const diagnostics = @import("../workspace/diagnostics.zig"); const diff_mod = @import("../output/diff.zig"); const file_mutation = @import("../tooling/file_mutation.zig"); const file_mutation_contract = @import("../tooling/file_mutation_contract.zig"); const command_output_content = @import("../tooling/command_output_content.zig"); const tool_admission = @import("../tooling/tool_admission.zig"); const gateway_error_format = @import("../shared/gateway_error_format.zig"); const io_mod = @import("../shared/io.zig"); const session_runtime = @import("../session/session.zig"); const session_codec = @import("../session/session_codec.zig"); const session_usage = @import("../session/session_usage.zig"); const parent_delivery_projector = @import("../subagent/parent_delivery_projector.zig"); const task_helpers = @import("../tasks/task_helpers.zig"); const types = @import("../shared/types.zig"); const worker_runtime = @import("../agent/worker_runtime.zig"); const assistant_presentation = @import("../agent/assistant_presentation.zig"); const activity_runtime = @import("../output/activity_runtime.zig"); const assistant_pacer = @import("../../ui/assistant/pacer.zig"); const ui_render = @import("../../ui/render.zig"); const render_request = @import("../../ui/render_request.zig"); const transcript_runtime = @import("../../ui/transcript/runtime.zig"); const Allocator = std.mem.Allocator; const ChatMessage = types.ChatMessage; const HistoryTurn = types.HistoryTurn; const PermissionGrant = types.PermissionGrant; const PermissionMode = types.PermissionMode; const ToolCall = types.ToolCall; const ToolExecutionResult = agent_runtime.ToolExecutionResult; const WorkerEvent = worker_runtime.WorkerEvent; const reset_style = ui_render.reset_style; fn preparedDiffPayload( alloc: Allocator, handoff: file_mutation.CommittedFileHandoff, ) !diff_mod.DiffEntryPayload { return preparedDiffPayloadWithFullAllocator(alloc, alloc, handoff); } fn preparedDiffPayloadWithFullAllocator( alloc: Allocator, full_alloc: Allocator, handoff: file_mutation.CommittedFileHandoff, ) !diff_mod.DiffEntryPayload { return diff_mod.formatFileChangePayload( alloc, full_alloc, handoff.preview, handoff.tracker.previous_content, if (handoff.full_view) |snapshot| .{ .after_content = snapshot.after_content, .lifecycle_id = snapshot.lifecycle_id, } else null, .{ .added_fg = ui_render.diff_added_style, .removed_fg = ui_render.diff_removed_style, .context_fg = ui_render.dim_style, .added_marker_fg = ui_render.diff_added_marker_style, .removed_marker_fg = ui_render.diff_removed_marker_style, .reset = ui_render.reset_style, }, ); } test "prepared preview formatter preserves context and informative elision" { const preview: diff_mod.FileChangePreview = .{ .path = "note.txt", .lines = &.{ .{ .op = .context, .old_line = 1, .new_line = 1, .text = "alpha", }, .{ .op = .deletion, .old_line = 2, .text = "before", }, .{ .op = .addition, .new_line = 2, .text = "after", }, .{ .op = .elision, .text = "⋯ +2 -1 omitted", }, }, .additions = 3, .deletions = 2, .truncated = true, }; const formatted = try diff_mod.formatFileChangePreview( std.testing.allocator, preview, .{ .added_fg = "", .removed_fg = "", .context_fg = "", .reset = "", }, ); defer std.testing.allocator.free(formatted); try std.testing.expect(std.mem.find(u8, formatted, "alpha") != null); try std.testing.expect( std.mem.find(u8, formatted, "⋯ +2 -1 omitted") != null, ); } test "prepared diff payload keeps compact elision and retains the complete approved review" { const alloc = std.testing.allocator; var after: std.ArrayList(u8) = .empty; defer after.deinit(alloc); for (1..121) |line_number| { var line: [32]u8 = undefined; try after.appendSlice( alloc, try std.fmt.bufPrint(&line, "full-review-{d:0>3}\n", .{line_number}), ); } const preview_lines = [_]diff_mod.PreviewLine{ .{ .op = .addition, .new_line = 1, .text = "full-review-001" }, .{ .op = .elision, .text = "⋯ +118 omitted" }, .{ .op = .addition, .new_line = 120, .text = "full-review-120" }, }; const handoff = file_mutation.CommittedFileHandoff.init( .{ .path = "full-review.txt", .lines = &preview_lines, .additions = 120, .deletions = 0, .truncated = true, }, .{ .kind = .write, .raw_path = "/tmp/full-review.txt", .previous_content = null, .committed_at_ms = 0, }, ); var enriched_handoff = handoff; enriched_handoff.full_view = .{ .after_content = after.items, .lifecycle_id = .{ .turn_id = 39, .call_id = "full-review" }, }; const payload = try preparedDiffPayload(alloc, enriched_handoff); defer diff_mod.freeDiffEntryPayload(alloc, payload); try std.testing.expect(std.mem.indexOf(u8, payload.preview, "⋯ +118 omitted") != null); const full = payload.full orelse return error.TestExpectedFullDiff; try std.testing.expect(std.mem.indexOf(u8, full.content, "full-review-001") != null); try std.testing.expect(std.mem.indexOf(u8, full.content, "full-review-120") != null); try std.testing.expect(std.mem.indexOf(u8, full.content, "⋯ +118 omitted") == null); } test "prepared diff payload keeps compact output when full formatting is unavailable" { const handoff = testCommittedFileHandoff(); var enriched_handoff = handoff; enriched_handoff.full_view = .{ .after_content = "after\n", .lifecycle_id = .{ .turn_id = 40, .call_id = "full-format-fallback" }, }; var failing = std.testing.FailingAllocator.init( std.testing.allocator, .{ .fail_index = 0 }, ); const payload = try preparedDiffPayloadWithFullAllocator( std.testing.allocator, failing.allocator(), enriched_handoff, ); defer diff_mod.freeDiffEntryPayload(std.testing.allocator, payload); try std.testing.expect(payload.full == null); try std.testing.expect(std.mem.indexOf(u8, payload.preview, "after") != null); } test "full diff formatter renders unchanged review elisions" { const before = "same-01\n" ++ "same-02\n" ++ "same-03\n" ++ "same-04\n" ++ "same-05\n" ++ "same-06\n" ++ "same-07\n" ++ "same-08\n" ++ "same-09\n" ++ "same-10\n" ++ "old\n"; const after = "same-01\n" ++ "same-02\n" ++ "same-03\n" ++ "same-04\n" ++ "same-05\n" ++ "same-06\n" ++ "same-07\n" ++ "same-08\n" ++ "same-09\n" ++ "same-10\n" ++ "new\n"; const preview_lines = [_]diff_mod.PreviewLine{ .{ .op = .deletion, .old_line = 11, .text = "old" }, .{ .op = .addition, .new_line = 11, .text = "new" }, }; var handoff = file_mutation.CommittedFileHandoff.init( .{ .path = "elision.txt", .lines = &preview_lines, .additions = 1, .deletions = 1, .truncated = false, }, .{ .kind = .edit, .raw_path = "/tmp/elision.txt", .previous_content = before, .committed_at_ms = 0, }, ); handoff.full_view = .{ .after_content = after, .lifecycle_id = .{ .turn_id = 41, .call_id = "context-elision" }, }; const snapshot = handoff.full_view orelse return error.TestExpectedFullDiff; const full = (try diff_mod.formatFileChangeFullDiff( std.testing.allocator, handoff.tracker.previous_content, .{ .after_content = snapshot.after_content, .lifecycle_id = snapshot.lifecycle_id, }, .{}, )) orelse return error.TestExpectedFullDiff; defer full.deinit(std.testing.allocator); try std.testing.expect(std.mem.indexOf(u8, full.content, "5 unchanged lines ⋯") != null); try std.testing.expect(std.mem.indexOf(u8, full.content, "old") != null); try std.testing.expect(std.mem.indexOf(u8, full.content, "new") != null); } pub fn Bindings(comptime App: type) type { return struct { pub fn agentRuntimeDeps(app: *App) agent_runtime.AgentRuntimeDeps { var deps: agent_runtime.AgentRuntimeDeps = .{ .ctx = @ptrCast(app), .agent_stream_provider = if (comptime @hasDecl(App, "agentStreamProvider")) app.agentStreamProvider() else agent_stream_provider.unavailable_provider, .cooperative_transport_pulse = if (comptime @hasDecl(App, "cooperativeTransportPulse")) .{ .ctx = @ptrCast(app), .run = cooperativeTransportPulse, } else null, .tool_registry = if (comptime @hasDecl(App, "toolRegistry")) app.toolRegistry() else .{}, .context_registry = if (comptime @hasDecl(App, "contextRegistry")) app.contextRegistry() else null, .context_enabled = if (comptime @hasField(App, "context_enabled")) app.context_enabled else false, .finalize_turn = agentFinalizeTurn, .prepare_parent_turn_context = agentPrepareParentTurnContext, .acknowledge_parent_turn_context = agentAcknowledgeParentTurnContext, .append_runtime_context = agentAppendRuntimeContext, .append_static_context = agentAppendStaticContext, .validate_tool_call = agentValidateToolCall, .check_tool_availability = agentCheckToolAvailability, .request_tool_permission = agentRequestToolPermission, .request_prepared_file_mutation_permission = agentRequestPreparedFileMutationPermission, .describe_tool_action = agentDescribeToolAction, .describe_tool_action_completed = agentDescribeToolActionCompleted, .describe_tool_action_denied = agentDescribeToolActionDenied, .permission_target_for_call = agentPermissionTargetForCall, .execute_tool_call = agentExecuteToolCall, .publish_committed_file_handoff = agentPublishCommittedFileHandoff, .propagate_history_turn = agentPropagateHistoryTurn, .recovery_checkpoint = if (comptime @hasField(App, "session_persistence")) if (app.session_persistence.writable != null) .{ .set = agentSetRecoveryCheckpoint, } else null else null, .propagate_grant = agentPropagateGrant, .push_event = agentPushEvent, .push_text = agentPushText, .push_tool_lifecycle = agentPushToolLifecycle, .push_diff_block = agentPushDiffBlock, .push_system_notice = agentPushSystemNotice, .push_interactive_notice = agentPushInteractiveNotice, .push_context_notice = agentPushContextNotice, .push_route_recovery_status = agentPushRouteRecoveryStatus, .push_command_output_complete = agentPushCommandOutputComplete, .push_http_error = agentPushHttpError, .refresh_gateway_credential = if (comptime @hasField(App, "auth")) refreshGatewayCredential else null, .request_route_recovery = if (comptime @hasDecl(@TypeOf(app.worker), "requestRouteRecoveryAnswerBlocking")) agentRequestRouteRecovery else null, .available_model_capabilities = agentAvailableModelCapabilities, .resolve_model_capabilities = agentResolveModelCapabilities, .format_tool_execution_error = agentFormatToolExecutionError, .record_tool_call_rejected = agentRecordToolCallRejected, .report_usage = agentReportUsage, .report_inner_tool_usage = agentReportInnerToolUsage, .usage_allocator = app.alloc, .diff_marker_styles = .{ .added = ui_render.diff_added_marker_style, .removed = ui_render.diff_removed_marker_style, }, }; if (comptime @hasField(@TypeOf(app.session), "usage")) { deps.usage = &app.session.usage; if (comptime @hasField(App, "session_persistence") and @hasField(@TypeOf(app.session_persistence), "writable")) { app.session.usage.configureCheckpointSink( if (app.session_persistence.writable != null) .{ .context = @ptrCast(app), .allocator = app.alloc, .persist = agentPersistUsageCheckpoint, } else null, ); } } if (comptime @hasDecl(App, "requestSandboxWideningSyncWithAdvertised")) { deps.request_sandbox_widening = agentRequestSandboxWidening; } return deps; } fn refreshGatewayCredential( raw_ctx: *anyopaque, alloc: std.mem.Allocator, source: credentials.Source, mode: auth_runtime.CredentialRefreshMode, ) !?[]u8 { const app: *App = @ptrCast(@alignCast(raw_ctx)); return auth_runtime.refreshFxLoginToken( app.auth.oauthTransport(), alloc, source, mode, ); } pub fn modelCapabilityResolver(app: *App) model_capabilities.Resolver { return .{ .ctx = @ptrCast(app), .resolve_fn = agentResolveModelCapabilities, }; } pub fn semanticPresentationSink(app: *App) agent_runtime.SemanticPresentationSink { return .{ .ctx = @ptrCast(app), .table = agentPushTable, .code_block = agentPushCodeBlock, .thematic_rule = agentPushThematicRule, }; } pub fn workerEventHandlers(app: *App) app_worker_runtime.WorkerEventHandlers { return .{ .ctx = @ptrCast(app), .tool_lifecycle = worker_tool_lifecycle_presenter(app), .write_user_prompt = workerBridgeWriteUserPrompt, .write_user_prompt_with_skill_bindings = workerBridgeWriteUserPromptWithSkillBindings, .append_text = workerBridgeAppendText, .append_table = workerBridgeAppendTable, .append_code_block = workerBridgeAppendCodeBlock, .append_thematic_rule = workerBridgeAppendThematicRule, .drain_assistant_text = workerBridgeDrainAssistantText, .open_model_picker = workerBridgeOpenModelPicker, .semantic_notice = workerBridgeSemanticNotice, .command_output = workerBridgeCommandOutput, .command_output_complete = workerBridgeCommandOutputComplete, .diff_block = workerBridgeDiffBlock, .append_history_turn = workerBridgeAppendHistoryTurn, .session_grant = workerBridgeSessionGrant, .error_text = workerBridgeErrorText, }; } pub fn worker_tool_lifecycle_presenter( app: *App, ) activity_runtime.LifecyclePresenter { return .{ .ctx = @ptrCast(app), .snapshot_fn = worker_tool_lifecycle_snapshot, .record_fn = worker_tool_lifecycle_record, .apply_fn = worker_apply_tool_lifecycle, .finish_batch_fn = worker_finish_tool_lifecycle_batch, }; } fn worker_tool_lifecycle_snapshot( raw_ctx: *anyopaque, ) activity_runtime.LifecycleSnapshot { const app: *App = @ptrCast(@alignCast(raw_ctx)); return .{ .finalized_turn_watermark = app.shell.finalizedToolTurnWatermark(), .active_tool_count = app.shell.activeToolActivityCount(), .focused_entry_id = app.shell.focusedToolEntryId(), .focused_activity_kind = app.shell.focusedToolActivityKind(), .activity = app.shell.activityProjection(), }; } fn worker_tool_lifecycle_record( raw_ctx: *anyopaque, id: types.ToolLifecycleId, ) ?activity_runtime.ToolPresentationRecord { const app: *App = @ptrCast(@alignCast(raw_ctx)); return app.shell.toolActivityRecord(id); } fn worker_apply_tool_lifecycle( raw_ctx: *anyopaque, alloc: std.mem.Allocator, event: types.ToolLifecycleEvent, ) !activity_runtime.LifecycleTransition { const app: *App = @ptrCast(@alignCast(raw_ctx)); const previous_focused_entry_id = app.shell.focusedToolEntryId(); const preserve_normal_buffer_anchor = if (comptime @hasField(App, "terminal")) app.terminal.alternate_screen_owner != .none else false; const applied_activity_kind = if (preserve_normal_buffer_anchor) try app.shell.applyToolLifecyclePreservingNormalBufferAnchor(alloc, event) else try app.shell.applyToolLifecycle(alloc, event); return .{ .previous_focused_entry_id = previous_focused_entry_id, .snapshot = worker_tool_lifecycle_snapshot(raw_ctx), .applied_activity_kind = applied_activity_kind, .terminal_record = switch (event) { .terminal => |terminal| app.shell.toolActivityRecord(terminal.id), .provisional, .authoritative_started, .progress, .turn_finished => null, }, }; } fn worker_finish_tool_lifecycle_batch( raw_ctx: *anyopaque, alloc: std.mem.Allocator, ) !void { const app: *App = @ptrCast(@alignCast(raw_ctx)); try app.shell.finishLifecycleBatch(alloc); } pub fn onCommandOutputChunk(ctx: *anyopaque, lifecycle_id: ?types.ToolLifecycleId, stream: command_output_content.Stream, chunk: []const u8) !void { const app: *App = @ptrCast(@alignCast(ctx)); if (app.worker.worker_cancel_requested.load(.seq_cst)) return; try app_worker_runtime.Runtime(App).pushCommandOutput(app, lifecycle_id, stream, chunk); } pub fn onMcpProgress(ctx: *anyopaque, lifecycle_id: types.ToolLifecycleId, text: []const u8) void { const app: *App = @ptrCast(@alignCast(ctx)); var label_buf: [512]u8 = undefined; const label = std.fmt.bufPrint( &label_buf, "{s}● {s}{s}", .{ ui_render.bold_style, text, reset_style }, ) catch ui_render.bold_style ++ "● MCP progress" ++ reset_style; app_worker_runtime.Runtime(App).pushToolLifecycle(app, .{ .progress = .{ .id = lifecycle_id, .text = label, } }) catch |err| { debug_trace.logf("mcp", "failed to publish MCP tool progress err={s}", .{@errorName(err)}); return; }; debug_trace.logf("mcp", "queued MCP tool progress turn_id={d}", .{lifecycle_id.turn_id});