Something went wrong. Try again.
My version of https://github.com/vercel-labs/fx
Something went wrong. Try again.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472const std = @import("std");const agent_runtime = @import("../agent/agent_runtime.zig");const agent_stream_provider = @import("../agent/stream_provider.zig");const runtime_context_compaction = @import("../agent/runtime/context_compaction.zig");const compaction_activity = @import("../output/compaction_activity.zig");const runtime_prompt_context = @import("../agent/runtime/prompt_context.zig");const command_admission = @import("../permissions/command_admission.zig");const permission_auto_classifier = @import("../permissions/auto_classifier.zig");const shared_theme = @import("../shared/theme.zig");const code_highlight = @import("../agent/presentation/code_highlight.zig");const code_highlight_languages = @import("../agent/presentation/code_highlight_languages.zig");const app_callbacks = @import("app_callbacks.zig");const runtime_profile = @import("../hosts/runtime_profile.zig");const host = @import("../hosts/host.zig");const app_permission_runtime = @import("app_permission_runtime.zig");const app_session_runtime = @import("app_session_runtime.zig");const provider_runtime = @import("provider_runtime.zig");const app_worker_runtime = @import("app_worker_runtime.zig");const auth_runtime = @import("../auth/auth_runtime.zig");const credentials = @import("../auth/credentials.zig");const change_tracker = @import("../workspace/change_tracker.zig");const file_mutation_contract = @import("../tooling/file_mutation_contract.zig");const hooks = @import("../hooks/hooks.zig");const io_mod = @import("../shared/io.zig");const mcp_elicitation_interaction = @import("../mcp/elicitation_interaction.zig");const mcp_model_catalog = @import("../mcp/model_catalog.zig");const permission_gate = @import("../permissions/permission_gate.zig");const permissions = @import("../permissions/permissions.zig");const prompt_policy_contract = @import("../config/prompt_policy.zig");const model_provider = @import("../config/model_provider.zig");const session_runtime = @import("../session/session.zig");const session_child_store = @import("../session/session_child_store.zig");const skill_runtime = @import("../skills/skill_runtime.zig");const subagent_agent_adapter = @import("../subagent/agent_adapter.zig");const subagent_domain = @import("../subagent/domain.zig");const subagent_execution = @import("../subagent/execution.zig");const debug_trace = @import("../shared/debug_trace.zig");const text_utils = @import("../shared/text_utils.zig");const tool_args = @import("../tooling/tool_args.zig");const tool_admission = @import("../tooling/tool_admission.zig");const tool_projection_mod = @import("../tooling/tool_projection.zig");const model_tool_schema = @import("../tooling/model_tool_schema.zig");const tool_dispatch = @import("../tooling/tool_dispatch.zig");const tool_mcp_runtime = @import("../tooling/tool_mcp_runtime.zig");const context_contract = @import("../workspace/context_contract.zig");const model_catalog = @import("../gateway/model_catalog.zig");const provider_set = @import("../gateway/provider_set.zig");const test_builtin_gateway = if (@import("builtin").is_test) @import("../../builtins/gateway.zig")else struct {};const test_builtin_tools = if (@import("builtin").is_test) @import("../../builtins/tools.zig")else struct {};const tool_presentation = @import("../tooling/tool_presentation.zig");const tool_runtime = @import("../tooling/tool_runtime.zig");const skill_invocation = @import("../skills/skill_invocation.zig");const web_fetch_runtime = @import("../tooling/web_fetch_runtime.zig");const web_search_runtime = @import("../tooling/web_search_runtime.zig");const types = @import("../shared/types.zig");const worker_runtime = @import("../agent/worker_runtime.zig");const workspace_access = @import("../workspace/workspace_access.zig");const js_host_workspace = @import("../hosts/js_host_workspace.zig");
const Allocator = std.mem.Allocator;const ChatMessage = types.ChatMessage;const PermissionGrant = types.PermissionGrant;const PermissionMode = types.PermissionMode;const ToolCall = types.ToolCall;const ToolPermissionDecision = types.ToolPermissionDecision;
pub fn Runtime(comptime App: type) type { return struct { const ToolAuthorityView = struct { mode: PermissionMode, grants: []const PermissionGrant, rules: types.PermissionRuleSet, };
fn appAccessScope(app: *const App) ?workspace_access.AccessScope { if (comptime @hasDecl(App, "workspaceAccessScope")) { return app.workspaceAccessScope(); } return null; }
fn appHostWorkspaceInfo(app: *const App) ?*const js_host_workspace.Info { if (comptime @hasDecl(App, "workspaceHostInfo")) { return app.workspaceHostInfo(); } return null; }
fn modelVisibleProjectContext(app: *App) []const u8 { if (comptime @hasField(App, "context_enabled")) { if (!app.context_enabled) return ""; } if (app.worker.active_context_snapshot) |snapshot| return snapshot.modelVisibleBytes(); return app.context_snapshot.modelVisibleBytes(); }
fn childToolContext(root_context: tool_runtime.Context) tool_runtime.Context { var child_context = root_context; child_context.tracker = null; return child_context; }
pub fn toolContext( app: *App, ignored_list_entries: []const []const u8, max_list_entries: usize, max_read_file_bytes: usize, max_read_file_lines: usize, max_read_file_line_len: usize, max_command_output_bytes: usize, gateway_retry_count: usize, gateway_chat_url: []const u8, ) tool_runtime.Context { return toolContextWithAuthority( app, ignored_list_entries, max_list_entries, max_read_file_bytes, max_read_file_lines, max_read_file_line_len, max_command_output_bytes, gateway_retry_count, gateway_chat_url, null, ); }
pub fn toolContextForSubagent( app: *App, ignored_list_entries: []const []const u8, max_list_entries: usize, max_read_file_bytes: usize, max_read_file_lines: usize, max_read_file_line_len: usize, max_command_output_bytes: usize, gateway_retry_count: usize, gateway_chat_url: []const u8, admission: subagent_domain.AdmissionSnapshot, ) tool_runtime.Context { return toolContextWithAuthority( app, ignored_list_entries, max_list_entries, max_read_file_bytes, max_read_file_lines, max_read_file_line_len, max_command_output_bytes, gateway_retry_count, gateway_chat_url, .{ .mode = admission.permission_mode, .grants = admission.grants, .rules = admission.rules, }, ); }
fn toolContextWithAuthority( app: *App, ignored_list_entries: []const []const u8, max_list_entries: usize, max_read_file_bytes: usize, max_read_file_lines: usize, max_read_file_line_len: usize, max_command_output_bytes: usize, gateway_retry_count: usize, gateway_chat_url: []const u8, authority: ?ToolAuthorityView, ) tool_runtime.Context { const host_workspace = appHostWorkspaceInfo(app); const workspace_root = if (host_workspace) |info| info.root() else app.workspace_root; const agent_settings = app.worker.effectiveAgentTurnSettings(); const permission_snapshot = if (authority) |snapshot| worker_runtime.PermissionSnapshot{ .mode = snapshot.mode, } else app_permission_runtime.Runtime(App).livePermissionSnapshot(app); const permission_grants = if (authority) |snapshot| snapshot.grants else app.permission_engine.grants.items; const permission_rules = if (authority) |snapshot| snapshot.rules else app.permission_engine.rules; const child_capability = if (comptime @hasField(App, "session_persistence")) app_session_runtime.Runtime(App).childCapability(app) else null; const selected_provider = provider_runtime.provider(app); const provider_capabilities = if (comptime @hasDecl(App, "providerSet")) app.providerSet().select(selected_provider).capabilities else if (selected_provider == .gateway) provider_set.Bundle.Capabilities{ .fx_search = true, .vision_fallback = true } else provider_set.Bundle.Capabilities{}; var ctx: tool_runtime.Context = .{ .workspace_root = workspace_root, .access_scope = if (host_workspace != null) workspace_access.AccessScope.primaryOnly(workspace_root) else appAccessScope(app), .ignored_list_entries = ignored_list_entries, .max_list_entries = max_list_entries, .max_read_file_bytes = max_read_file_bytes, .max_read_file_lines = max_read_file_lines, .max_read_file_line_len = max_read_file_line_len, .max_command_output_bytes = max_command_output_bytes, .max_tool_result_bytes = agent_settings.max_tool_result_bytes, .api_key = app.auth.apiKey() orelse "", .agent_stream_provider = if (comptime @hasDecl(App, "agentStreamProvider")) app.agentStreamProvider() else agent_stream_provider.unavailable_provider, .gateway_team = app.auth.gatewayTeam(), .credential_source = app.auth.credentialSource(), .account_id = app.auth.accountId(), .provider = selected_provider, .provider_capabilities = provider_capabilities, .oauth_transport = app.auth.oauthTransport(), .secret_store = if (comptime @hasDecl(@TypeOf(app.auth), "secretStore")) app.auth.secretStore() else host.unavailable_secret_store, .model = provider_runtime.model(app), .reviewer_model = if (comptime @hasField(App, "review_model")) app.review_model else "", .gateway_retry_count = gateway_retry_count, .gateway_chat_url = gateway_chat_url, .gateway_models_path = if (comptime @hasField(App, "web_search_models_path")) app.web_search_models_path else "/v1/models", .agent_step_limit = app.agent_step_limit, .fast_mode = agent_settings.fast_mode, .effort = agent_settings.effort, .first_call_tool_choice = agent_settings.first_call_tool_choice, .tool_registry = if (comptime @hasDecl(App, "toolRegistry")) app.toolRegistry() else .{}, .subagent_host = if (comptime @hasField(App, "session_persistence")) app_session_runtime.Runtime(App).subagentHost(app) else null, .subagent_caller_id = if (comptime @hasField(App, "session_persistence")) app_session_runtime.Runtime(App).activeSessionId(app) else null, .permission_mode = permission_snapshot.mode, .permission_grants = permission_grants, .permission_rules = permission_rules, .worker = &app.worker, .permission_prompter = tool_admission.workerPrompter(&app.worker), .cancel_flag = &app.worker.worker_cancel_requested, .session_child_capability = child_capability, .terminal_client = if (comptime @hasField(App, "terminal_client")) &app.terminal_client else null, .managed_executions = if (comptime @hasField(App, "managed_executions")) &app.managed_executions else null, .ephemeral_command_replay = if (comptime @hasField(App, "managed_executions")) app.managed_executions.replayStore() else null, .session = &app.session, .session_allocator = app.alloc, .skills_dir = app.skills.dir, .context_limits = if (comptime @hasField(App, "context_limits")) app.context_limits else .{}, .context_enabled = if (comptime @hasField(App, "context_enabled")) app.context_enabled else true, .context_registry = app.contextRegistry(), .output_chunk_ctx = @ptrCast(app), .on_output_chunk = app_callbacks.Bindings(App).onCommandOutputChunk, .workspace_executor = if (comptime @hasDecl(App, "workspaceExecutor")) app.workspaceExecutor() else null, .host_sandbox_default = if (host_workspace) |info| switch (info.permission) { .allow_sandboxed => .allow_sandboxed, .prompt => .prompt, } else .none, .permission_reviewer_provider = if (comptime @hasDecl(App, "permissionReviewerProvider")) app.permissionReviewerProvider() else null, .tracker = &app.change_tracker, .mcp_ctx = @ptrCast(app), .mcp_has_tool = if (comptime runtime_profile.allows(App, .mcp)) mcpHasTool else null, .mcp_validate_tool = if (comptime runtime_profile.allows(App, .mcp)) validateMcpTool else null, .mcp_call_tool = if (comptime runtime_profile.allows(App, .mcp)) callMcpTool else null, .mcp_search_tools = if (comptime runtime_profile.allows(App, .mcp)) searchMcpTools else null, .mcp_tool_schema = if (comptime runtime_profile.allows(App, .mcp)) mcpToolSchemaJson else null, .mcp_snapshot_tool = if (comptime runtime_profile.allows(App, .mcp)) mcpSnapshotTool else null, .mcp_call_feature = if (comptime runtime_profile.allows(App, .mcp)) callMcpFeature else null, .mcp_progress_ctx = @ptrCast(app), .on_mcp_progress = app_callbacks.Bindings(App).onMcpProgress, .tool_progress_ctx = @ptrCast(app), .on_tool_progress = app_callbacks.Bindings(App).onToolProgress, .subagent_status_renderer = app_callbacks.Bindings(App).subagentStatusRenderer(app), .lifecycle_view = app.lifecycle_view, .lifecycle_scope = lifecycleContext(app).scope, }; if (comptime @hasField(App, "web_fetch_runtime")) { ctx.web_fetch_runtime = &app.web_fetch_runtime; ctx.web_fetch_artifact_store = app.session.webFetchArtifactStore(); ctx.web_fetch_artifact_error = app.session.webFetchArtifactError(); ctx.web_fetch_progress_ctx = @ptrCast(app); ctx.on_web_fetch_progress = app_callbacks.Bindings(App).onWebFetchProgress; } if (comptime @hasField(App, "web_search_runtime")) { if (provider_capabilities.fx_search) { app.web_search_runtime.configure(.{ .api_key = app.auth.apiKey() orelse "", .credential_source = app.auth.credentialSource(), .gateway_team = app.auth.gatewayTeam(), .worker_model = provider_runtime.model(app), .gateway_retry_count = gateway_retry_count, .gateway_chat_url = gateway_chat_url, .usage = &app.session.usage, .usage_allocator = app.alloc, }); ctx.web_search_backend = app.web_search_runtime.dispatchBackend(); } ctx.web_search_runtime_ready = false; ctx.web_search_progress_ctx = @ptrCast(app); ctx.on_web_search_progress = app_callbacks.Bindings(App).onWebSearchProgress; } if (comptime runtime_profile.allows(App, .mcp) and @hasDecl(@TypeOf(app.worker), "requestMcpElicitationAnswerBlocking") and @hasDecl(App, "urlOpener")) { ctx.mcp_input_responder = .{ .context = @ptrCast(app), .capabilities = .{ .form = true, .url = true }, .legacy_url_manual_completion = true, .callback = respondToMcpInput, }; } ctx.model_capability_resolver = app_callbacks.Bindings(App).modelCapabilityResolver(app); ctx.model_override_resolver = app_callbacks.Bindings(App).modelOverrideResolver(app); return ctx; }
fn respondToMcpInput( raw_ctx: *anyopaque, alloc: Allocator, origin: tool_mcp_runtime.InputOrigin, required: tool_mcp_runtime.InputRequired, ) anyerror![]const u8 { return mcp_elicitation_interaction.respond(alloc, origin, required, .{ .questioner = .{ .context = raw_ctx, .ask_fn = askMcpQuestion }, .browser = .{ .context = raw_ctx, .open_fn = openMcpUrl }, .compact_forms = true, .capabilities = .{ .form = true, .url = true }, }); }
fn askMcpQuestion( raw_ctx: *anyopaque, alloc: Allocator, entries: []const types.QuestionBatchEntry, deadline_ms: i64, lifecycle_cancel_flag: ?*const std.atomic.Value(bool), ) anyerror!?[][]u8 { const app: *App = @ptrCast(@alignCast(raw_ctx)); var watch = DeadlineWatch{ .app = app, .deadline_ms = deadline_ms, .lifecycle_cancel_flag = lifecycle_cancel_flag, }; const watcher = try std.Thread.spawn(.{}, DeadlineWatch.run, .{&watch}); defer { watch.done.store(true, .release); watcher.join(); }
const c_alloc = std.heap.c_allocator; const answers = try app.worker.requestMcpElicitationAnswerBlocking(c_alloc, entries) orelse { if (watch.timed_out.load(.acquire)) return error.McpInputTimedOut; return null; }; defer freeMcpAnswers(c_alloc, answers); const copy = try alloc.alloc([]u8, answers.len); errdefer alloc.free(copy); var copied: usize = 0; errdefer for (copy[0..copied]) |answer| alloc.free(answer); while (copied < answers.len) : (copied += 1) { copy[copied] = try alloc.dupe(u8, answers[copied]); } return copy; }
fn openMcpUrl( raw_ctx: ?*anyopaque, alloc: Allocator, url: []const u8, ) anyerror!bool { const app: *App = @ptrCast(@alignCast(raw_ctx.?)); return app.urlOpener().open(alloc, url); }
fn freeMcpAnswers(alloc: Allocator, answers: [][]u8) void { for (answers) |answer| alloc.free(answer); alloc.free(answers); }
const DeadlineWatch = struct { app: *App, deadline_ms: i64, lifecycle_cancel_flag: ?*const std.atomic.Value(bool), done: std.atomic.Value(bool) = .init(false), timed_out: std.atomic.Value(bool) = .init(false),
fn run(self: *DeadlineWatch) void { while (!self.done.load(.acquire)) { const cancelled = self.app.worker.worker_cancel_requested.load(.acquire); const lifecycle_cancelled = if (self.lifecycle_cancel_flag) |flag| flag.load(.acquire) else false; const timed_out = currentAwakeMillis() >= self.deadline_ms; if (cancelled or lifecycle_cancelled or timed_out) { if (timed_out and !cancelled and !lifecycle_cancelled) { self.timed_out.store(true, .release); } if (self.app.worker.cancelPendingQuestionBatch()) return; } io_mod.sleep(20 * std.time.ns_per_ms); } } };
fn currentAwakeMillis() i64 { const now = std.Io.Clock.Timestamp.now(io_mod.getIo(), .awake); const milliseconds = @divFloor(now.raw.nanoseconds, std.time.ns_per_ms); return std.math.cast(i64, milliseconds) orelse if (milliseconds < 0) std.math.minInt(i64) else std.math.maxInt(i64); }
fn mcpHasTool(raw_ctx: *anyopaque, name: []const u8, access: tool_mcp_runtime.Access) bool { const app: *App = @ptrCast(@alignCast(raw_ctx)); return app.hasMcpTool(name, access); }
fn validateMcpTool(raw_ctx: *anyopaque, arena: Allocator, name: []const u8, arguments_json: []const u8, access: tool_mcp_runtime.Access) anyerror!tool_mcp_runtime.ValidationResult { const app: *App = @ptrCast(@alignCast(raw_ctx)); if (comptime @hasDecl(App, "validateMcpTool")) { return app.validateMcpTool(arena, name, arguments_json, access); } return .not_available; }
fn callMcpTool(raw_ctx: *anyopaque, arena: Allocator, name: []const u8, arguments_json: []const u8, max_tool_result_bytes: usize, options: tool_mcp_runtime.CallOptions) anyerror!?tool_mcp_runtime.CallResult { const app: *App = @ptrCast(@alignCast(raw_ctx)); return app.callMcpTool(arena, name, arguments_json, max_tool_result_bytes, options); }
fn searchMcpTools(raw_ctx: *anyopaque, arena: Allocator, request: tool_mcp_runtime.SearchRequest, permission_rules: types.PermissionRuleSet, _: @import("../config/context_limits.zig").Values, access: tool_mcp_runtime.Access, cancel_flag: ?*std.atomic.Value(bool)) anyerror!tool_mcp_runtime.SearchResult { const app: *App = @ptrCast(@alignCast(raw_ctx)); if (comptime @hasDecl(App, "searchMcpTools")) { return app.searchMcpTools(arena, request, permission_rules, access, cancel_flag); } return .{ .model_output = try arena.dupe(u8, "{\"tools\":[],\"count\":0}") }; }
fn mcpSnapshotTool(raw_ctx: *anyopaque, arena: Allocator, name: []const u8, known: tool_mcp_runtime.Binding, permission_rules: types.PermissionRuleSet, limits: @import("../config/context_limits.zig").Values, access: tool_mcp_runtime.Access) anyerror!tool_mcp_runtime.DefinitionSnapshot { const app: *App = @ptrCast(@alignCast(raw_ctx)); if (comptime @hasDecl(App, "acquireMcpRuntime")) { var lease = app.acquireMcpRuntime() orelse return .unavailable; defer lease.deinit(); return lease.runtime.snapshotToolDefinition(arena, name, known, permission_rules, limits, access); } return .unavailable; }
fn mcpToolSchemaJson(raw_ctx: *anyopaque, arena: Allocator, name: []const u8, permission_rules: types.PermissionRuleSet, _: @import("../config/context_limits.zig").Values, access: tool_mcp_runtime.Access, cancel_flag: ?*std.atomic.Value(bool)) anyerror!?tool_mcp_runtime.ToolSchemaResult { const app: *App = @ptrCast(@alignCast(raw_ctx)); if (comptime @hasDecl(App, "mcpToolSchemaJson")) { return app.mcpToolSchemaJson(arena, name, permission_rules, access, cancel_flag); } return null; }
fn callMcpFeature( raw_ctx: *anyopaque, arena: Allocator, request: tool_mcp_runtime.FeatureRequest, options: tool_mcp_runtime.FeatureCallOptions, ) anyerror!tool_mcp_runtime.FeatureResult { if (comptime !@hasDecl(App, "acquireMcpRuntime")) { return error.McpRuntimeUnavailable; } const app: *App = @ptrCast(@alignCast(raw_ctx)); var lease = app.acquireMcpRuntime() orelse return error.McpRuntimeUnavailable; defer lease.deinit(); return lease.runtime.callFeatureForModel(arena, request, options); }
pub fn resolveToolActionDisplayTarget( app: *App, arena: Allocator, call: ToolCall, ignored_list_entries: []const []const u8, max_list_entries: usize, max_read_file_bytes: usize, max_read_file_lines: usize, max_read_file_line_len: usize, max_command_output_bytes: usize, gateway_retry_count: usize, gateway_chat_url: []const u8, ) !?[]const u8 { const ctx = toolContext(app, ignored_list_entries, max_list_entries, max_read_file_bytes, max_read_file_lines, max_read_file_line_len, max_command_output_bytes, gateway_retry_count, gateway_chat_url); return tool_presentation.resolveTerminalDisplayTarget( arena, ctx.tool_registry, ctx.workspace_root, ctx.terminal_client, ctx.managed_executions, call, ); }
pub fn releaseAgentTerminalLease( app: *App, session_id: []const u8, ignored_list_entries: []const []const u8, max_list_entries: usize, max_read_file_bytes: usize, max_read_file_lines: usize, max_read_file_line_len: usize, max_command_output_bytes: usize, gateway_retry_count: usize, gateway_chat_url: []const u8, ) !void { return tool_runtime.release_agent_terminal_lease( toolContext( app, ignored_list_entries, max_list_entries, max_read_file_bytes, max_read_file_lines, max_read_file_line_len, max_command_output_bytes, gateway_retry_count, gateway_chat_url, ), session_id, ); }
pub fn describeToolAction( app: *App, arena: Allocator, call: ToolCall, display_target: ?[]const u8, advertised_dynamic_tool_names: []const []const u8, ignored_list_entries: []const []const u8, max_list_entries: usize, max_read_file_bytes: usize, max_read_file_lines: usize, max_read_file_line_len: usize, max_command_output_bytes: usize, gateway_retry_count: usize, gateway_chat_url: []const u8, ) ![]const u8 { const ctx = tool_runtime.withAdvertisedDynamicToolNames(toolContext(app, ignored_list_entries, max_list_entries, max_read_file_bytes, max_read_file_lines, max_read_file_line_len, max_command_output_bytes, gateway_retry_count, gateway_chat_url), advertised_dynamic_tool_names); return formatToolAction(ctx, arena, call, display_target, .active, null); }
pub fn describeToolActionCompleted( app: *App, arena: Allocator, call: ToolCall, display_target: ?[]const u8, advertised_dynamic_tool_names: []const []const u8, ignored_list_entries: []const []const u8, max_list_entries: usize, max_read_file_bytes: usize, max_read_file_lines: usize, max_read_file_line_len: usize, max_command_output_bytes: usize, gateway_retry_count: usize, gateway_chat_url: []const u8, ) ![]const u8 { const ctx = tool_runtime.withAdvertisedDynamicToolNames(toolContext(app, ignored_list_entries, max_list_entries, max_read_file_bytes, max_read_file_lines, max_read_file_line_len, max_command_output_bytes, gateway_retry_count, gateway_chat_url), advertised_dynamic_tool_names); return formatToolAction(ctx, arena, call, display_target, .completed, null); }
pub fn describeToolActionDenied( app: *App, arena: Allocator, call: ToolCall, display_target: ?[]const u8, label: []const u8, advertised_dynamic_tool_names: []const []const u8, ignored_list_entries: []const []const u8, max_list_entries: usize, max_read_file_bytes: usize, max_read_file_lines: usize, max_read_file_line_len: usize, max_command_output_bytes: usize, gateway_retry_count: usize, gateway_chat_url: []const u8, ) ![]const u8 { const ctx = tool_runtime.withAdvertisedDynamicToolNames(toolContext(app, ignored_list_entries, max_list_entries, max_read_file_bytes, max_read_file_lines, max_read_file_line_len, max_command_output_bytes, gateway_retry_count, gateway_chat_url), advertised_dynamic_tool_names); return formatToolAction(ctx, arena, call, display_target, .denied, label); }
pub fn requestToolPermissionSync( app: *App, arena: Allocator, call: ToolCall, review_turn: permission_auto_classifier.ReviewTurnContext, permission_mode: PermissionMode, local_grants: []const PermissionGrant, live_authority: ?agent_runtime.LiveToolAuthority, revalidation: ?agent_runtime.LivePermissionRevalidation, advertised_dynamic_tool_names: []const []const u8, mcp_review_schema_json: ?[]const u8, ignored_list_entries: []const []const u8, max_list_entries: usize, max_read_file_bytes: usize, max_read_file_lines: usize, max_read_file_line_len: usize, max_command_output_bytes: usize, gateway_retry_count: usize, gateway_chat_url: []const u8, ) !command_admission.PermissionOutcome { var ctx = tool_runtime.withAdvertisedDynamicToolNames(toolContext(app, ignored_list_entries, max_list_entries, max_read_file_bytes, max_read_file_lines, max_read_file_line_len, max_command_output_bytes, gateway_retry_count, gateway_chat_url), advertised_dynamic_tool_names); applyCredentialLease(app, &ctx, review_turn.credential, gateway_retry_count, gateway_chat_url); ctx.permission_review_turn = review_turn; ctx.mcp_review_schema_json = mcp_review_schema_json; const admission = ctx.admissionInputWithLiveAuthority(live_authority); return if (revalidation) |request| switch (request) { .action => |action| tool_admission.revalidateLiveActionPermissionOutcome( admission, arena, call, permission_mode, local_grants, action.authority, action.human_approval, ), } else tool_admission.requestPermissionOutcome( admission, arena, call, permission_mode, local_grants, ); }
pub fn requestPreparedFileMutationPermissionSync( app: *App, arena: Allocator, call: ToolCall, prepared: *tool_admission.PreparedFileMutationCall, review_turn: permission_auto_classifier.ReviewTurnContext, permission_mode: PermissionMode, local_grants: []const PermissionGrant, live_authority: ?agent_runtime.LiveToolAuthority, advertised_dynamic_tool_names: []const []const u8, ignored_list_entries: []const []const u8, max_list_entries: usize, max_read_file_bytes: usize, max_read_file_lines: usize, max_read_file_line_len: usize, max_command_output_bytes: usize, gateway_retry_count: usize, gateway_chat_url: []const u8, ) !command_admission.PermissionOutcome { var ctx = tool_runtime.withAdvertisedDynamicToolNames( toolContext( app, ignored_list_entries, max_list_entries, max_read_file_bytes, max_read_file_lines, max_read_file_line_len, max_command_output_bytes, gateway_retry_count, gateway_chat_url, ), advertised_dynamic_tool_names, ); applyCredentialLease(app, &ctx, review_turn.credential, gateway_retry_count, gateway_chat_url); ctx.permission_review_turn = review_turn; const admission = ctx.admissionInputWithLiveAuthority(live_authority); return tool_admission.requestPreparedFileMutationPermissionOutcome( admission, arena, call, prepared, permission_mode, local_grants, ); }
pub fn validateToolCall( app: *App, arena: Allocator, call: ToolCall, ignored_list_entries: []const []const u8, max_list_entries: usize, max_read_file_bytes: usize, max_read_file_lines: usize, max_read_file_line_len: usize, max_command_output_bytes: usize, gateway_retry_count: usize, gateway_chat_url: []const u8, ) !agent_runtime.ToolCallValidationResult { return tool_runtime.validateToolCall(toolContext(app, ignored_list_entries, max_list_entries, max_read_file_bytes, max_read_file_lines, max_read_file_line_len, max_command_output_bytes, gateway_retry_count, gateway_chat_url), arena, call); }
pub fn checkToolAvailability( app: *App, arena: Allocator, call: ToolCall, ignored_list_entries: []const []const u8, max_list_entries: usize, max_read_file_bytes: usize, max_read_file_lines: usize, max_read_file_line_len: usize, max_command_output_bytes: usize, gateway_retry_count: usize, gateway_chat_url: []const u8, ) !?[]const u8 { return tool_runtime.checkToolAvailability(toolContext(app, ignored_list_entries, max_list_entries, max_read_file_bytes, max_read_file_lines, max_read_file_line_len, max_command_output_bytes, gateway_retry_count, gateway_chat_url), arena, call); }
pub fn permissionTargetForCall( app: *App, arena: Allocator, call: ToolCall, advertised_dynamic_tool_names: []const []const u8, ignored_list_entries: []const []const u8, max_list_entries: usize, max_read_file_bytes: usize, max_read_file_lines: usize, max_read_file_line_len: usize, max_command_output_bytes: usize, gateway_retry_count: usize, gateway_chat_url: []const u8, ) ![]const u8 { const ctx = tool_runtime.withAdvertisedDynamicToolNames(toolContext(app, ignored_list_entries, max_list_entries, max_read_file_bytes, max_read_file_lines, max_read_file_line_len, max_command_output_bytes, gateway_retry_count, gateway_chat_url), advertised_dynamic_tool_names); return tool_admission.permissionTargetForCall(ctx.admissionInput(), arena, call); }
pub fn executeToolCall( app: *App, request: agent_runtime.ToolExecutionRequest, ignored_list_entries: []const []const u8, max_list_entries: usize, max_read_file_bytes: usize, max_read_file_lines: usize, max_read_file_line_len: usize, max_command_output_bytes: usize, gateway_retry_count: usize, gateway_chat_url: []const u8, ) !agent_runtime.ToolExecutionResult { var ctx = toolContext(app, ignored_list_entries, max_list_entries, max_read_file_bytes, max_read_file_lines, max_read_file_line_len, max_command_output_bytes, gateway_retry_count, gateway_chat_url); applyCredentialLease(app, &ctx, request.credential, gateway_retry_count, gateway_chat_url); ctx.root_user_intent_context = request.root_user_intent_context; ctx.root_user_messages = request.root_user_messages; ctx.root_user_evidence_complete = request.root_user_evidence_complete; ctx.session_grants = request.session_grants; ctx.advertised_dynamic_tool_names = request.advertised_dynamic_tool_names; ctx.max_tool_result_bytes = request.max_tool_result_bytes; return tool_runtime.executeToolCallAuthorized(ctx, request); }
fn applyCredentialLease( app: *App, ctx: *tool_runtime.Context, credential: types.CredentialLease, gateway_retry_count: usize, gateway_chat_url: []const u8, ) void { const credential_secret = credential.secret() orelse return; const credential_source = credential.credentialSource(); ctx.api_key = credential_secret; ctx.credential_source = credential_source; ctx.account_id = credential.accountId(); ctx.gateway_team = credential.tenant(); if (comptime @hasField(App, "web_search_runtime") and @hasField(App, "session")) { if (ctx.provider_capabilities.fx_search) { app.web_search_runtime.configure(.{ .api_key = credential_secret, .credential_source = credential_source, .gateway_team = credential.tenant(), .worker_model = provider_runtime.model(app), .gateway_retry_count = gateway_retry_count, .gateway_chat_url = gateway_chat_url, .usage = &app.session.usage, .usage_allocator = app.alloc, }); ctx.web_search_backend = app.web_search_runtime.dispatchBackend(); } } }
pub fn appendStaticContextMessage( app: *App, arena: Allocator, project_context: ?[]const u8, messages: *std.ArrayList(ChatMessage), ignored_list_entries: []const []const u8, max_list_entries: usize, max_read_file_bytes: usize, max_read_file_lines: usize, max_read_file_line_len: usize, max_command_output_bytes: usize, gateway_retry_count: usize, gateway_chat_url: []const u8, ) !void { _ = ignored_list_entries; _ = max_list_entries; _ = max_read_file_bytes; _ = max_read_file_lines; _ = max_read_file_line_len; _ = max_command_output_bytes; _ = gateway_retry_count; _ = gateway_chat_url; try app.contextRegistry().appendDefaultStatic(.{ .project_context = project_context orelse modelVisibleProjectContext(app), }, arena, messages); var snapshot = if (comptime @hasDecl(App, "snapshotMcpModelCatalog")) try app.snapshotMcpModelCatalog( arena, if (comptime @hasField(App, "permission_engine")) app.permission_engine.rules else .{}, false, ) else try mcp_model_catalog.Snapshot.empty(arena); defer snapshot.deinit(arena); const section = try mcp_model_catalog.render(arena, snapshot); if (section.text.len > 0) { try messages.append(arena, .{ .role = .system, .content = section.text }); } if (section.notice) |notice| try pushMcpModelCatalogNotice(app, notice); }
fn pushMcpModelCatalogNotice(app: *App, notice: []const u8) !void { if (comptime @hasDecl(@TypeOf(app.session), "claimContextNotice")) { if (!try app.session.claimContextNotice(std.heap.c_allocator, notice)) return; } const body = try types.renderContextNoticeBody(std.heap.c_allocator, notice); defer std.heap.c_allocator.free(body); try app_worker_runtime.Runtime(App).pushSemanticNotice(app, .{ .topic = "context", .tone = .warning, .body = body, .visibility = .full_only, }); }
pub fn appendTransientRuntimeContextMessage( app: *App, arena: Allocator, messages: *std.ArrayList(ChatMessage), ignored_list_entries: []const []const u8, max_list_entries: usize, max_read_file_bytes: usize, max_read_file_lines: usize, max_read_file_line_len: usize, max_command_output_bytes: usize, gateway_retry_count: usize, gateway_chat_url: []const u8, ) !void { _ = ignored_list_entries; _ = max_list_entries; _ = max_read_file_bytes; _ = max_read_file_lines; _ = max_read_file_line_len; _ = max_command_output_bytes; _ = gateway_retry_count; _ = gateway_chat_url; const permission_snapshot = app_permission_runtime.Runtime(App).livePermissionSnapshot(app); const host_workspace = appHostWorkspaceInfo(app); const workspace_root = if (host_workspace) |info| info.root() else app.workspace_root; try app.contextRegistry().appendDefaultTransient(.{ .workspace_root = workspace_root, .host_workspace = if (host_workspace) |info| .{ .root = info.root(), .cwd = info.cwd(), .home = info.home(), } else null, .access_scope = if (host_workspace != null) workspace_access.AccessScope.primaryOnly(workspace_root) else appAccessScope(app), .interactive = true, .permission_mode = permission_snapshot.mode, .stale_shell_handles = app.session.has_stale_shell_handles, }, arena, messages); }
pub fn refreshProjectContext(app: *App, targets: []const context_contract.ApplicableTarget) context_contract.ProviderError!void { app.context_snapshot.deinit(app.alloc); if (!app.context_enabled) return;
app.context_snapshot = app.contextRegistry().gatherDefaultSnapshot(app.alloc, .{ .workspace_root = app.workspace_root, .access_scope = appAccessScope(app), .targets = targets, .context_limits = if (comptime @hasField(App, "context_limits")) app.context_limits else .{}, }) catch |err| { debug_trace.logf("context", "gather failed err={s}", .{@errorName(err)}); return err; }; if (comptime @hasDecl(App, "writeDomainNotice")) { for (app.context_snapshot.notices) |notice| { const should_emit = if (comptime @hasDecl(@TypeOf(app.session), "claimContextNotice")) try app.session.claimContextNotice(app.alloc, notice) else true; if (should_emit) { const body = try types.renderContextNoticeBody(app.alloc, notice); defer app.alloc.free(body); app.writeDomainNotice(.{ .topic = "context", .tone = .warning, .body = body, .visibility = .full_only, }, true) catch return error.WriteFailed; } } } }
pub fn fetchModelIds( app: *App, provider: model_catalog.Provider, catalog_endpoint: []const u8, ) !std.ArrayList([]u8) { const result = model_catalog.fetchWithPublicFallback(provider, app.alloc, .{ .access = app.auth.modelCatalogAccess(), .endpoint = catalog_endpoint, }); var catalog = switch (result) { .loaded => |loaded| loaded.catalog, .failed => |failed| return failed.failure.asError(), }; defer model_catalog.freeModelCatalog(app.alloc, &catalog); return model_catalog.projectModelIds(app.alloc, catalog.items); }
pub fn processQueuedPrompt( app: *App, queued_job: worker_runtime.QueuedPrompt, gateway_retry_count: usize, gateway_chat_url: []const u8, failure_provenance: ?*?compaction_activity.ErrorProvenance, ) !void { if (failure_provenance) |out| out.* = null; var job = queued_job; var fresh_history: ?worker_runtime.FreshPromptHistory = null; defer if (fresh_history) |*snapshot| snapshot.deinit(std.heap.c_allocator); var snapshot_ownership = worker_runtime.ActivePromptSnapshotOwnership.init(job.images); app.worker.beginActivePromptSnapshots(&snapshot_ownership); if (job.recovery_checkpoint != null) { app.worker.preservePromptSnapshots(job.turn_id, job.images); } defer app.worker.endActivePromptSnapshots(&snapshot_ownership); { app.session_persistence.write_mutex.lockUncancelable(io_mod.getIo()); defer app.session_persistence.write_mutex.unlock(io_mod.getIo()); if (app.session_persistence.writable) |*loaded| try loaded.requireWritable(); } if (job.recovery_checkpoint == null) { if (try app_session_runtime.Runtime(App).snapshotFreshPromptBoundary(app, std.heap.c_allocator)) |value| { var checkpoint = value; defer checkpoint.deinit(std.heap.c_allocator); fresh_history = try app_callbacks.Bindings(App).prepareFreshPrompt(app, .{ .user = .{ .text = job.prompt, .images = job.images }, .prior_turn = checkpoint.interruptedTurn(), }); job.history = fresh_history.?.history; job.authorized_image_catalog = fresh_history.?.authorized_image_catalog; job.root_user_intent_context = fresh_history.?.root_user_intent_context; job.unversioned_history_count = fresh_history.?.unversioned_history_count; } } app.worker.active_context_snapshot = &job.context_snapshot; defer app.worker.active_context_snapshot = null; defer if (comptime !@import("builtin").single_threaded) { if (app.session_persistence.subagent_host) |child_host| child_host.cancelYielded(); }; app.worker.active_prompt_is_root_authority = if (app.session_persistence.writable) |writable| writable.external_prompt_origin == .persistent_child and job.recovery_checkpoint == null else false; defer app.worker.active_prompt_is_root_authority = false; app.worker.setActiveAgentTurnSettings(job.agent_settings); defer app.worker.clearActiveAgentTurnSettings(); var preflight_context_notices: std.Io.Writer.Allocating = .init(std.heap.c_allocator); defer preflight_context_notices.deinit(); for (job.context_snapshot.notices) |notice| { try appendClaimedContextNotice(app, &preflight_context_notices.writer, notice); }
var skill_catalog = app.skills.acquireCatalog(); defer skill_catalog.deinit(); const explicit_bindings = try std.heap.c_allocator.alloc(skill_invocation.ExplicitBinding, job.skill_bindings.len); defer std.heap.c_allocator.free(explicit_bindings); for (job.skill_bindings, 0..) |binding, index| { explicit_bindings[index] = .{ .name = binding.name, .path = binding.path }; } if (preflight_context_notices.written().len > 0) { try app_worker_runtime.Runtime(App).pushSemanticNotice(app, .{ .topic = "context", .tone = .warning, .body = preflight_context_notices.written(), .visibility = .full_only, }); } if (comptime @hasDecl(App, "waitForRequiredMcp")) { const failure = try app.waitForRequiredMcp( std.heap.c_allocator, &app.worker.worker_cancel_requested, ); if (failure) |message| { defer std.heap.c_allocator.free(message); try app_worker_runtime.Runtime(App).pushSemanticNotice(app, .{ .topic = "mcp", .tone = .warning, .body = message, }); return error.McpRequiredServerUnavailable; } } var tool_projection = try app.snapshotModelToolProjection( std.heap.c_allocator, job.permission_mode, ); defer tool_projection.deinit(std.heap.c_allocator); const session_child_capability = if (comptime @hasField(App, "session_persistence")) app_session_runtime.Runtime(App).childCapability(app) else null;
var deps = app_callbacks.Bindings(App).agentRuntimeDeps(app); if (comptime @hasDecl(App, "providerSet")) deps.agent_stream_provider = app.providerSet().select(job.provider).agent_stream_or_unavailable(); deps.compaction_failure = failure_provenance; const semantic_presentation = app_callbacks.Bindings(App).semanticPresentationSink(app); const config = buildQueuedPromptConfig( app, job, .{ .skills = skill_catalog.items, .diagnostics = skill_catalog.diagnostics }, explicit_bindings, gateway_retry_count, gateway_chat_url, &tool_projection, session_child_capability, ); const process_result = agent_runtime.processAgentPrompt(&app.session.agent, &deps, semantic_presentation, lifecycleContext(app), config, job); try process_result; }
pub fn processContextCompaction( app: *App, job: worker_runtime.ContextCompactionTask, gateway_retry_count: usize, failure_provenance: ?*?compaction_activity.ErrorProvenance, ) anyerror!void { if (failure_provenance) |out| out.* = null; const operation_id = job.operation_id orelse app.worker.beginCompactionActivity(.manual, job.turn_id); app.worker.runCompactionActivity(operation_id, .preparation); // Covers early cancellation before the transaction; acknowledged settlement wins. defer if (app.worker.isCancelRequested()) { app.worker.settleCompactionActivity(operation_id, .{ .outcome = .cancelled }); }; errdefer |err| { app.worker.settleCompactionActivity(operation_id, compaction_activity.failure(err, .preparation, app.worker.isCancelRequested())); if (failure_provenance) |out| out.* = .{ .operation_id = operation_id, .turn_id = job.turn_id, .err = err }; } var arena_state = std.heap.ArenaAllocator.init(std.heap.c_allocator); defer arena_state.deinit(); const arena = arena_state.allocator();
const result_storage: runtime_context_compaction.ResultStorage = if (app_session_runtime.Runtime(App).childCapability(app)) |capability| .{ .managed = capability } else .unavailable; var messages: std.ArrayList(ChatMessage) = .empty; defer messages.deinit(arena); const uncertain_history_count = @min( job.unversioned_history_count, job.history.len, ); _ = try session_runtime.appendCompactionHistoryChatMessages( arena, &messages, job.history, uncertain_history_count, ); const source_tokens = runtime_prompt_context.estimateCompactionSourceTokens( messages.items, ); var deps = app_callbacks.Bindings(App).agentRuntimeDeps(app); if (comptime @hasDecl(App, "providerSet")) deps.agent_stream_provider = app.providerSet().select(job.provider).agent_stream_or_unavailable(); const capabilities = deps.available_model_capabilities(deps.ctx, job.model); const permission_mode = app_permission_runtime.Runtime(App).livePermissionSnapshot(app).mode; var tool_projection = try app.snapshotModelToolProjection(arena, permission_mode); defer tool_projection.deinit(arena); var skill_catalog = app.skills.acquireCatalog(); defer skill_catalog.deinit(); const config = buildQueuedPromptConfig(app, .{ .prompt = @constCast(""), .images = &.{}, .model = job.model, .provider = job.provider, .api_key = job.api_key, .permission_mode = permission_mode, .history = &.{}, .grants = &.{}, .agent_settings = app.worker.effectiveAgentTurnSettings(), }, .{ .skills = skill_catalog.items, .diagnostics = skill_catalog.diagnostics }, &.{}, gateway_retry_count, "", &tool_projection, null); const base_continuation = try agent_runtime.prepareManualCompactionContinuation( arena, &deps, config, job.model, capabilities, ); var retention_target = runtime_prompt_context.recentContextTarget(capabilities, source_tokens); while (true) { if (app.worker.worker_cancel_requested.load(.seq_cst)) return; var continuation = base_continuation; const window = try agent_runtime.prepareRetainedCompactionWindow(arena, job.history, null, capabilities, source_tokens, deps.agent_stream_provider, .{ .provider = job.provider, .model = job.model }, .{ .target = retention_target }); const continuation_messages = try arena.alloc(ChatMessage, continuation.request.messages.len + window.retained_messages.len); @memcpy(continuation_messages[0..continuation.request.messages.len], continuation.request.messages); @memcpy(continuation_messages[continuation.request.messages.len..], window.retained_messages); continuation.request.messages = continuation_messages; const refine = window.refine_budget(arena, deps.agent_stream_provider, continuation, capabilities, source_tokens, &retention_target) catch |err| { if (err == error.Cancelled and app.worker.worker_cancel_requested.load(.seq_cst)) return; return @as(anyerror!void, err); }; if (refine) continue; var compaction_count: usize = 0; for (job.history) |turn| switch (turn) { .compacted_summary => |summary| { compaction_count = @max( compaction_count, summary.compaction_count, ); }, else => {}, }; const transaction = agent_runtime.compactContextTransaction(arena, &deps, .{ .trigger = .manual, .operation_id = operation_id, .activity_origin = .manual, .provider = job.provider, .working_capabilities = capabilities, .request_tokens = source_tokens, .source_tokens = runtime_prompt_context.estimateCompactionSourceTokens(window.source), .continuation = continuation, .retained_from = window.cut, .newest_exchange_tokens = window.newest_exchange_tokens, .source_messages = window.source, .uncertain_source_message_count = if (uncertain_history_count > 0) window.source.len else 0, .result_storage = result_storage, .api_key = job.api_key, .credential_source = job.credential_source, .account_id = job.account_id, .gateway_team = job.gateway_team, .session_id = app_session_runtime.Runtime(App).activeSessionId(app), .retry_count = gateway_retry_count, .cancel_flag = &app.worker.worker_cancel_requested, .trace_ctx = .{ .turn_id = job.turn_id }, .removed_turn_count = window.cut.turns, .compaction_count = compaction_count + 1, }) catch |err| { if (err == error.Cancelled and app.worker.worker_cancel_requested.load(.seq_cst)) { return; } return @as(anyerror!void, err); }; _ = transaction; return; } }
fn lifecycleContext(app: *App) agent_runtime.LifecycleContext { return .{ .view = app.lifecycle_view, .scope = .{ .kind = .interactive, .workspace_root = app.workspace_root, .session_id = if (comptime @hasField(App, "session_persistence")) app_session_runtime.Runtime(App).activeSessionId(app) else null, }, .outcome_allocator = app.alloc, }; }
pub fn runSubagentChild( raw: ?*anyopaque, turn: *subagent_execution.TurnContext, message: subagent_domain.QueuedMessage, admission: subagent_domain.AdmissionSnapshot, cancel: *std.atomic.Value(bool), ) subagent_execution.ServiceError!subagent_execution.RunOutcome { const app: *App = @ptrCast(@alignCast(raw.?)); const alloc = std.heap.c_allocator; var child_projection = app.snapshotSubagentModelToolProjection( alloc, admission.permission_mode, admission.rules, ) catch return error.OutOfMemory; defer child_projection.deinit(alloc); var skill_catalog = app.skills.acquireCatalog(); defer skill_catalog.deinit(); const prompt_policy = app.promptPolicy(); var tool_context = childToolContext(app.subagentToolContextForAdmission(admission)); tool_context.managed_executions = turn.managedExecutionRuntime(); tool_context.ephemeral_command_replay = turn.managedExecutionRuntime().replayStore(); const providers = if (comptime @hasDecl(App, "providerSet")) app.providerSet() else provider_set.Set{ .gateway = .{ .capabilities = tool_context.provider_capabilities, .agent_stream = tool_context.agent_stream_provider, .permission_reviewer = tool_context.permission_reviewer_provider, }, .codex = .{ .capabilities = tool_context.provider_capabilities, .agent_stream = tool_context.agent_stream_provider, .permission_reviewer = tool_context.permission_reviewer_provider, }, .grok = .{ .capabilities = tool_context.provider_capabilities, .agent_stream = tool_context.agent_stream_provider, .permission_reviewer = tool_context.permission_reviewer_provider, }, }; return subagent_agent_adapter.run(.{ .host = app_session_runtime.Runtime(App).subagentHost(app) orelse return error.ProviderFailed, .tool_context = tool_context, .provider_set = providers, .system_prompt = prompt_policy.system_prompt, .model_prompt_overlay = prompt_policy.modelPromptOverlay(admission.model), .skill_catalog = .{ .skills = skill_catalog.items, .diagnostics = skill_catalog.diagnostics }, .advertised_tool_names = child_projection.advertised_names, .advertised_functions = child_projection.advertised_functions, .custom_tool_guidance = child_projection.custom_guidance, .context_registry = app.contextRegistry(), .context_enabled = if (comptime @hasField(App, "context_enabled")) app.context_enabled else true, .project_context = modelVisibleProjectContext(app), .lifecycle_view = app.lifecycle_view, }, turn, message, admission, cancel); }
fn appendClaimedContextNotice(app: *App, writer: *std.Io.Writer, notice: []const u8) !void { const should_emit = if (comptime @hasDecl(@TypeOf(app.session), "claimContextNotice")) try app.session.claimContextNotice(std.heap.c_allocator, notice) else true; if (!should_emit) return; if (writer.buffered().len > 0 and !std.mem.endsWith(u8, writer.buffered(), "\n")) { try writer.writeByte('\n'); } const body = try types.renderContextNoticeBody(std.heap.c_allocator, notice); defer std.heap.c_allocator.free(body); try writer.writeAll(body); }
fn buildQueuedPromptConfig( app: *App, job: worker_runtime.QueuedPrompt, catalog: skill_invocation.Catalog, bindings: []const skill_invocation.ExplicitBinding, gateway_retry_count: usize, gateway_chat_url: []const u8, tool_projection: *const tool_projection_mod.EffectiveToolProjection, session_child_capability: ?*session_child_store.SessionChildCapability, ) agent_runtime.Config { const prompt_policy = app.promptPolicy(); return .{ .system_prompt = prompt_policy.system_prompt, .model_prompt_overlay = prompt_policy.modelPromptOverlay(job.model), .skill_catalog = catalog, .skill_bindings = bindings, .gateway_retry_count = gateway_retry_count, .recovery_pause_flag = if (comptime @hasField(@TypeOf(app.worker), "worker_recovery_pause_requested")) &app.worker.worker_recovery_pause_requested else null, .gateway_chat_url = gateway_chat_url, .advertised_tool_names = tool_projection.advertised_names, .advertised_functions = tool_projection.advertised_functions, .provider_capabilities = if (comptime @hasDecl(App, "providerSet")) app.providerSet().select(job.provider).capabilities else if (job.provider == .gateway) .{ .fx_search = true, .vision_fallback = true } else .{}, .custom_tool_guidance = tool_projection.custom_guidance, .agent_step_limit = app.agent_step_limit, .max_tool_result_bytes = job.agent_settings.max_tool_result_bytes, .cancel_flag = &app.worker.worker_cancel_requested, .review_enabled = false, .fast_mode = job.agent_settings.fast_mode, .effort = job.agent_settings.effort, .first_call_tool_choice = job.agent_settings.first_call_tool_choice, .workspace_root = app.workspace_root, .access_scope = appAccessScope(app), .origin = if (app.session_persistence.writable) |writable| if (writable.external_prompt_origin == .persistent_child) .subagent else .root else .root, .root_user_messages = if (app.session_persistence.writable) |writable| writable.external_root_user_messages else &.{}, .root_user_evidence_complete = if (app.session_persistence.writable) |writable| writable.external_root_user_evidence_complete else false, .current_prompt_is_root_authority = app.worker.active_prompt_is_root_authority, .session_child_capability = session_child_capability, .context_limits = if (comptime @hasField(App, "context_limits")) app.context_limits else .{}, }; } };}
const ToolActionState = enum { active, completed, denied };
fn formatToolAction( ctx: tool_runtime.Context, arena: Allocator, call: ToolCall, display_target: ?[]const u8, state: ToolActionState, denied_label: ?[]const u8,) ![]const u8 { if (std.mem.eql(u8, call.name, "web_search") or tool_presentation.isProviderSearchAlias(call.name)) { return formatWebSearchAction(arena, call, state, denied_label); } const spec = ctx.tool_registry.lookup(call.name) orelse { if (dynamicMcpActionLabel(state)) |label| { if (mcpToolAvailable(ctx, call.name)) { return formatToolActionValue(arena, label, call.name); } } return formatMissingSpecToolAction(arena, state, denied_label, call.name); }; if (try tool_presentation.formatRunCommandActivity(arena, ctx.tool_registry, ctx.workspace_root, call)) |activity| { defer arena.free(activity.detail); const label = if (activity.compatibility_tool) |compatibility_tool| specLabel(compatibility_tool, state, denied_label) else switch (state) { .active => "Running", .completed => "Ran", .denied => denied_label.?, }; return formatCommandActionValue( arena, label, activity.detail, ); } if (std.mem.eql(u8, call.name, "write_file") or std.mem.eql(u8, call.name, "edit_file")) { return formatToolActionValue( arena, specLabel(spec, state, denied_label), display_target orelse spec.label_arg_default, ); } if (try tool_presentation.subagentAction(arena, call, switch (state) { .active => .active, .completed => .completed, .denied => .{ .stopped = denied_label.? }, })) |action| { defer action.deinit(arena); return formatToolActionValue(arena, action.label, action.detail); } const args = tool_args.parseToolArgsObject(arena, call.arguments_json) catch { return formatInvalidArgsToolAction(arena, state, denied_label); };
const presentation = tool_dispatch.presentationForArgs(spec.*, args); const value = display_target orelse tool_presentation.resolvedSkillName(call, presentation) orelse tool_dispatch.presentationLabelValue(presentation, args) orelse presentation.label_arg_default; return formatToolActionValue(arena, presentationLabel(presentation, state, denied_label), value);}
fn formatWebSearchAction(arena: Allocator, call: ToolCall, state: ToolActionState, denied_label: ?[]const u8) ![]const u8 { const args = tool_args.parseToolArgsObject(arena, call.arguments_json) catch { return formatInvalidArgsToolAction(arena, state, denied_label); }; const label = switch (state) { .active => "Searching", .completed => "Searched", .denied => denied_label.?, }; return formatToolActionValue(arena, label, try tool_presentation.formatWebSearchActionDetail(arena, args));}
fn formatMissingSpecToolAction(arena: Allocator, state: ToolActionState, denied_label: ?[]const u8, name: []const u8) ![]const u8 { return switch (state) { .active => formatToolActionValue(arena, "Working", name), .completed => formatToolActionValue(arena, "Completed", name), .denied => formatToolActionValue(arena, denied_label.?, name), };}
fn formatInvalidArgsToolAction(arena: Allocator, state: ToolActionState, denied_label: ?[]const u8) ![]const u8 { return switch (state) { .active => std.fmt.allocPrint(arena, "● Working…\x1b[0m", .{}), .completed => formatToolActionValue(arena, "Completed", "tool call"), .denied => formatToolActionValue(arena, denied_label.?, "tool call"), };}
fn formatToolActionValue(arena: Allocator, label: []const u8, value: []const u8) ![]const u8 { return std.fmt.allocPrint(arena, "● {s}\x1b[0m {s}{s}\x1b[0m", .{ label, shared_theme.current().tool_stdout_style, value });}
/// Command rows keep the muted tool-text base and add shell syntax colors for/// quoted strings, numbers, comments, and keywords.fn formatCommandActionValue(arena: Allocator, label: []const u8, command: []const u8) ![]const u8 { const theme = shared_theme.current(); const profile = code_highlight_languages.resolve("sh") orelse return formatToolActionValue(arena, label, command); const highlighted = try code_highlight.highlight( arena, command, profile, if (theme.light) .light else .dark, theme.tool_stdout_style, ); return std.fmt.allocPrint(arena, "● {s}\x1b[0m {s}\x1b[0m", .{ label, highlighted });}
fn specLabel(spec: *const tool_dispatch.Tool, state: ToolActionState, denied_label: ?[]const u8) []const u8 { return switch (state) { .active => spec.action_label, .completed => spec.completed_action_label, .denied => denied_label.?, };}
fn presentationLabel(presentation: tool_dispatch.CallPresentation, state: ToolActionState, denied_label: ?[]const u8) []const u8 { return switch (state) { .active => presentation.action_label, .completed => presentation.completed_action_label, .denied => denied_label.?, };}
fn dynamicMcpActionLabel(state: ToolActionState) ?[]const u8 { return switch (state) { .active => "Running MCP", .completed => "Ran MCP", .denied => null, };}
fn mcpToolAvailable(ctx: tool_runtime.Context, name: []const u8) bool { var advertised = false; for (ctx.advertised_dynamic_tool_names) |advertised_name| { if (std.mem.eql(u8, advertised_name, name)) { advertised = true; break; } } if (!advertised) return false; const raw_ctx = ctx.mcp_ctx orelse return false; const has_tool = ctx.mcp_has_tool orelse return false; return has_tool(raw_ctx, name, ctx.mcp_access);}
const test_ignored_list_entries = [_][]const u8{ ".git", "zig-out" };const test_gateway_chat_url = "https://gateway.test/chat";const test_tools = [_]tool_dispatch.Tool{ test_builtin_tools.web_search, test_builtin_tools.shell, test_builtin_tools.grep_files, test_builtin_tools.skill, test_builtin_tools.install_skill, test_builtin_tools.subagent,};const test_tool_registry = tool_dispatch.Registry{ .tools = test_tools[0..] };const custom_label_tool = tool_dispatch.Tool{ .name = "custom_registered_tool", .description = "Custom registered test tool.", .model_schema = .{ .name = "custom_registered_tool", .description = "Custom registered test tool.", }, .action_label = "Custom running", .completed_action_label = "Custom ran", .label_arg_kind = .name, .label_arg_default = "custom fallback", .decode = test_builtin_tools.read_file.decode, .call = test_builtin_tools.read_file.call, .reads_only_fn = test_builtin_tools.read_file.reads_only_fn, .irreversible_fn = test_builtin_tools.read_file.irreversible_fn,};const custom_registry_tools = [_]tool_dispatch.Tool{custom_label_tool};const custom_tool_registry = tool_dispatch.Registry{ .tools = custom_registry_tools[0..] };
fn gatherTestProjectContext(_: Allocator, _: context_contract.InitialContextInput) context_contract.ProviderError!context_contract.ProviderContext { return .{};}
fn appendTestStaticContext(input: context_contract.StaticContextInput, alloc: Allocator, messages: *std.ArrayList(ChatMessage)) context_contract.ProviderError!void { const content = try std.fmt.allocPrint(alloc, "provider static:{s}", .{input.project_context}); try messages.append(alloc, .{ .role = .system, .content = content });}
fn appendTestTransientContext(input: context_contract.TransientContextInput, alloc: Allocator, messages: *std.ArrayList(ChatMessage)) context_contract.ProviderError!void { const content = try std.fmt.allocPrint( alloc, "provider transient:{s}:{s}", .{ input.workspace_root, @tagName(input.permission_mode) }, ); try messages.append(alloc, .{ .role = .system, .content = content });}
const test_context_provider = context_contract.Provider{ .id = "test.default_context", .gather_project_context_fn = gatherTestProjectContext, .select_applicable_project_context_fn = context_contract.selectNoApplicableProjectContext, .append_static_fn = appendTestStaticContext, .append_transient_fn = appendTestTransientContext,};const test_context_registry = context_contract.Registry{ .default_provider = test_context_provider };const test_prompt_policy = prompt_policy_contract.Policy{ .system_prompt = "test system prompt", .model_prompt_overlay_fn = struct { fn overlay(model: []const u8) ?[]const u8 { return if (std.mem.eql(u8, model, "test-model")) "test model overlay" else null; } }.overlay,};
var refresh_gather_calls: usize = 0;var refresh_targets_match: bool = false;var refresh_gather_error: context_contract.ProviderError = error.WriteFailed;
fn gatherFreshProjectContext(alloc: Allocator, input: context_contract.InitialContextInput) context_contract.ProviderError!context_contract.ProviderContext { refresh_gather_calls += 1; refresh_targets_match = input.targets.len == 1 and input.targets[0].kind == .file and std.mem.eql(u8, input.targets[0].path, "/tmp/workspace/images/example.png"); const content = try std.fmt.allocPrint(alloc, "fresh:{s}", .{input.workspace_root}); errdefer alloc.free(content); const notices = try alloc.alloc([]u8, 1); errdefer alloc.free(notices); notices[0] = try alloc.dupe(u8, "fresh context notice"); return .{ .content = content, .notices = notices };}
fn gatherFailingProjectContext(_: Allocator, _: context_contract.InitialContextInput) context_contract.ProviderError!context_contract.ProviderContext { refresh_gather_calls += 1; return refresh_gather_error;}
fn appendNoopStaticContext(_: context_contract.StaticContextInput, _: Allocator, _: *std.ArrayList(ChatMessage)) context_contract.ProviderError!void {}
fn appendNoopTransientContext(_: context_contract.TransientContextInput, _: Allocator, _: *std.ArrayList(ChatMessage)) context_contract.ProviderError!void {}
const fresh_context_registry = context_contract.Registry{ .default_provider = .{ .id = "test.fresh_context", .gather_project_context_fn = gatherFreshProjectContext, .select_applicable_project_context_fn = context_contract.selectNoApplicableProjectContext, .append_static_fn = appendNoopStaticContext, .append_transient_fn = appendNoopTransientContext,} };const failing_context_registry = context_contract.Registry{ .default_provider = .{ .id = "test.failing_context", .gather_project_context_fn = gatherFailingProjectContext, .select_applicable_project_context_fn = context_contract.selectNoApplicableProjectContext, .append_static_fn = appendNoopStaticContext, .append_transient_fn = appendNoopTransientContext,} };
const RefreshContextApp = struct { alloc: Allocator, workspace_root: []const u8 = "/tmp/workspace", context_enabled: bool, context_snapshot: context_contract.GatheredContextSnapshot, context_registry: context_contract.Registry, context_notices: std.ArrayList(u8) = .empty, context_notice_tone: ?types.NoticeTone = null, context_notice_visibility: ?types.NoticeVisibility = null, session: session_runtime.SessionRuntime = .{ .max_history_turns = 8 },
fn contextRegistry(self: *const RefreshContextApp) context_contract.Registry { return self.context_registry; }
fn deinit(self: *RefreshContextApp) void { self.context_snapshot.deinit(self.alloc); self.context_notices.deinit(self.alloc); self.session.deinit(self.alloc); }
fn writeDomainNotice(self: *RefreshContextApp, notice: types.SemanticNotice, _: bool) !void { self.context_notice_tone = notice.tone; self.context_notice_visibility = notice.visibility; try self.context_notices.appendSlice(self.alloc, notice.body); }};
fn makeTestContextSnapshot(alloc: Allocator, provider_id: []const u8, content: []const u8) !context_contract.GatheredContextSnapshot { const owned_provider_id = try alloc.dupe(u8, provider_id); errdefer alloc.free(owned_provider_id); return .{ .contribution = .{ .provider_id = owned_provider_id, .content = try alloc.dupe(u8, content), } };}
fn testToolContext(app: *FakeApp) tool_runtime.Context { return Runtime(FakeApp).toolContext(app, &test_ignored_list_entries, 100, 1024, 40, 120, 2048, 2, test_gateway_chat_url);}
const ProjectionBarrier = struct { entered: std.atomic.Value(bool) = .init(false), release: std.atomic.Value(bool) = .init(false),
fn wait(self: *ProjectionBarrier) void { self.entered.store(true, .release); while (!self.release.load(.acquire)) io_mod.sleep(std.time.ns_per_ms); }};
const FakeApp = struct { alloc: Allocator, agent_stream_provider: agent_stream_provider.Provider = agent_stream_provider.unavailable_provider, workspace_root: []const u8 = "/tmp/workspace", auth: auth_runtime.Runtime = .{}, selected_model: std.ArrayList(u8) = .empty, selected_provider: model_provider.ProviderId = .gateway, permission_engine: permissions.PermissionEngine = .{}, agent_step_limit: usize = 8, fast_mode: bool = true, effort: types.ReasoningEffort = types.ReasoningEffort.literal("high"), worker: worker_runtime.WorkerRuntime = .{}, session: session_runtime.SessionRuntime = .{ .max_history_turns = 4 }, session_persistence: app_session_runtime.Persistence = .{}, skills_dir: []const u8 = "/tmp/skills", context_snapshot: context_contract.GatheredContextSnapshot, permission_state: app_permission_runtime.State = .{}, change_tracker: change_tracker.ChangeTracker = .{}, skills: skill_runtime.Runtime = .{}, total_input_tokens: u64 = 0, total_output_tokens: u64 = 0, total_web_search_requests: u64 = 0, append_context_count: usize = 0, snapshot_tools_count: usize = 0, snapshot_permission_mode: ?PermissionMode = null, snapshot_permission_rule_pattern: ?[]const u8 = null, snapshot_barrier: ?*ProjectionBarrier = null, snapshot_tools_error: ?anyerror = null, snapshot_custom_guidance: []const u8 = "", mcp_name: []const u8 = "mcp_lookup", mcp_has_tool_calls: usize = 0, mcp_result: []const u8 = "{\"ok\":true}", diff_blocks: usize = 0, web_fetch_runtime: web_fetch_runtime.Runtime = web_fetch_runtime.Runtime.init(.{}), web_search_runtime: web_search_runtime.Runtime = web_search_runtime.Runtime.init(.{ .provider = test_builtin_gateway.default_web_search_provider, }), web_search_models_path: []const u8 = "/models", lifecycle_runtime: hooks.Runtime, lifecycle_view: hooks.RuntimeView, tool_registry: tool_dispatch.Registry = test_tool_registry, context_registry: context_contract.Registry = test_context_registry,
fn init(alloc: Allocator) !FakeApp { var lifecycle_runtime = hooks.Runtime.init(alloc); const lifecycle_view = lifecycle_runtime.freeze(); var app = FakeApp{ .alloc = alloc, .lifecycle_runtime = lifecycle_runtime, .lifecycle_view = lifecycle_view, .context_snapshot = try makeTestContextSnapshot(alloc, "test.default_context", "project context"), }; errdefer app.context_snapshot.deinit(alloc); var credential = credentials.Credential{ .token = try alloc.dupe(u8, "api-key"), .source = .ai_gateway_api_key, }; defer credential.deinit(alloc); _ = app.auth.adoptCredential(alloc, &credential); errdefer app.auth.deinit(alloc); try app.selected_model.appendSlice(alloc, "test-model"); return app; }
fn toolRegistry(self: *const FakeApp) tool_dispatch.Registry { return self.tool_registry; }
fn contextRegistry(self: *const FakeApp) context_contract.Registry { return self.context_registry; }
fn snapshotMcpModelCatalog( _: *FakeApp, alloc: Allocator, _: types.PermissionRuleSet, _: bool, ) !mcp_model_catalog.Snapshot { return mcp_model_catalog.Snapshot.empty(alloc); }
fn promptPolicy(_: *const FakeApp) prompt_policy_contract.Policy { return test_prompt_policy; }
pub fn agentStreamProvider(self: *const FakeApp) agent_stream_provider.Provider { return self.agent_stream_provider; }
fn deinit(self: *FakeApp) void { self.auth.deinit(self.alloc); self.selected_model.deinit(self.alloc); self.permission_engine.deinit(self.alloc); self.context_snapshot.deinit(self.alloc); self.worker.deinit(std.heap.c_allocator); self.web_fetch_runtime.deinit(self.alloc); self.web_search_runtime.deinit(); self.session.deinit(self.alloc); self.change_tracker.deinit(self.alloc); self.lifecycle_runtime.deinit(); }
fn hasMcpTool(self: *FakeApp, name: []const u8, _: tool_mcp_runtime.Access) bool { self.mcp_has_tool_calls += 1; return std.mem.eql(u8, name, self.mcp_name); }
fn callMcpTool(self: *FakeApp, arena: Allocator, name: []const u8, arguments_json: []const u8, _: usize, _: tool_mcp_runtime.CallOptions) !?tool_mcp_runtime.CallResult { _ = arguments_json; if (!self.hasMcpTool(name, .unrestricted)) return null; return .{ .model_output = try arena.dupe(u8, self.mcp_result) }; }
fn subagentToolContextForAdmission( self: *FakeApp, admission: subagent_domain.AdmissionSnapshot, ) tool_runtime.Context { return Runtime(FakeApp).toolContextForSubagent( self, &test_ignored_list_entries, 100, 1024, 40, 120, 2048, 2, test_gateway_chat_url, admission, ); }
fn snapshotModelToolProjection( self: *FakeApp, alloc: Allocator, permission_mode: PermissionMode, ) !tool_projection_mod.EffectiveToolProjection { self.snapshot_tools_count += 1; self.snapshot_permission_mode = permission_mode; if (self.snapshot_barrier) |barrier| barrier.wait(); self.snapshot_permission_rule_pattern = if (self.permission_engine.rules.rules.len > 0) self.permission_engine.rules.rules[0].pattern else null; if (self.snapshot_tools_error) |err| return err; const advertised_names = try alloc.alloc([]const u8, 0); errdefer alloc.free(advertised_names); const advertised_functions = try alloc.alloc(model_tool_schema.FunctionSchema, 0); errdefer alloc.free(advertised_functions); return .{ .advertised_names = advertised_names, .advertised_functions = advertised_functions, .custom_guidance = try alloc.dupe(u8, self.snapshot_custom_guidance), }; }
fn snapshotSubagentModelToolProjection( self: *FakeApp, alloc: Allocator, permission_mode: PermissionMode, permission_rules: types.PermissionRuleSet, ) !tool_projection_mod.EffectiveToolProjection { self.snapshot_tools_count += 1; self.snapshot_permission_mode = permission_mode; if (self.snapshot_barrier) |barrier| barrier.wait(); self.snapshot_permission_rule_pattern = if (permission_rules.rules.len > 0) permission_rules.rules[0].pattern else null; if (self.snapshot_tools_error) |err| return err; const advertised_names = try alloc.alloc([]const u8, 0); errdefer alloc.free(advertised_names); const advertised_functions = try alloc.alloc(model_tool_schema.FunctionSchema, 0); errdefer alloc.free(advertised_functions); return .{ .advertised_names = advertised_names, .advertised_functions = advertised_functions, .custom_guidance = try alloc.dupe(u8, self.snapshot_custom_guidance), }; }
pub fn appendRuntimeContextMessage(self: *FakeApp, arena: Allocator, messages: *std.ArrayList(ChatMessage)) !void { self.append_context_count += 1; try Runtime(FakeApp).appendTransientRuntimeContextMessage(self, arena, messages, &test_ignored_list_entries, 100, 1024, 40, 120, 2048, 2, test_gateway_chat_url); }
pub fn requestToolPermissionSync(self: *FakeApp, arena: Allocator, call: ToolCall, permission_mode: PermissionMode, local_grants: []const PermissionGrant) !command_admission.PermissionOutcome { return Runtime(FakeApp).requestToolPermissionSync(self, arena, call, "", permission_mode, local_grants, null, null, &.{}, null, &test_ignored_list_entries, 100, 1024, 40, 120, 2048, 2, test_gateway_chat_url); }
pub fn requestToolPermissionSyncWithAdvertised(self: *FakeApp, arena: Allocator, call: ToolCall, review_turn: permission_auto_classifier.ReviewTurnContext, permission_mode: PermissionMode, local_grants: []const PermissionGrant, live_authority: ?agent_runtime.LiveToolAuthority, revalidation: ?agent_runtime.LivePermissionRevalidation, advertised_dynamic_tool_names: []const []const u8, mcp_review_schema_json: ?[]const u8) !command_admission.PermissionOutcome { return Runtime(FakeApp).requestToolPermissionSync(self, arena, call, review_turn, permission_mode, local_grants, live_authority, revalidation, advertised_dynamic_tool_names, mcp_review_schema_json, &test_ignored_list_entries, 100, 1024, 40, 120, 2048, 2, test_gateway_chat_url); }
pub fn requestPreparedFileMutationPermissionSyncWithAdvertised(self: *FakeApp, arena: Allocator, call: ToolCall, prepared: *tool_admission.PreparedFileMutationCall, review_turn: permission_auto_classifier.ReviewTurnContext, permission_mode: PermissionMode, local_grants: []const PermissionGrant, live_authority: ?agent_runtime.LiveToolAuthority, advertised_dynamic_tool_names: []const []const u8) !command_admission.PermissionOutcome { return Runtime(FakeApp).requestPreparedFileMutationPermissionSync(self, arena, call, prepared, review_turn, permission_mode, local_grants, live_authority, advertised_dynamic_tool_names, &test_ignored_list_entries, 100, 1024, 40, 120, 2048, 2, test_gateway_chat_url); }
pub fn validateToolCall(self: *FakeApp, arena: Allocator, call: ToolCall) !agent_runtime.ToolCallValidationResult { return Runtime(FakeApp).validateToolCall(self, arena, call, &test_ignored_list_entries, 100, 1024, 40, 120, 2048, 2, test_gateway_chat_url); }
pub fn checkToolAvailability(self: *FakeApp, arena: Allocator, call: ToolCall) !?[]const u8 { return Runtime(FakeApp).checkToolAvailability(self, arena, call, &test_ignored_list_entries, 100, 1024, 40, 120, 2048, 2, test_gateway_chat_url); }
pub fn describeToolAction(self: *FakeApp, arena: Allocator, call: ToolCall) ![]const u8 { return Runtime(FakeApp).describeToolAction(self, arena, call, null, &.{}, &test_ignored_list_entries, 100, 1024, 40, 120, 2048, 2, test_gateway_chat_url); }
pub fn describeToolActionWithAdvertised(self: *FakeApp, arena: Allocator, call: ToolCall, display_target: ?[]const u8, advertised_dynamic_tool_names: []const []const u8) ![]const u8 { return Runtime(FakeApp).describeToolAction(self, arena, call, display_target, advertised_dynamic_tool_names, &test_ignored_list_entries, 100, 1024, 40, 120, 2048, 2, test_gateway_chat_url); }
pub fn describeToolActionCompleted(self: *FakeApp, arena: Allocator, call: ToolCall) ![]const u8 { return Runtime(FakeApp).describeToolActionCompleted(self, arena, call, null, &.{}, &test_ignored_list_entries, 100, 1024, 40, 120, 2048, 2, test_gateway_chat_url); }
pub fn describeToolActionCompletedWithAdvertised(self: *FakeApp, arena: Allocator, call: ToolCall, display_target: ?[]const u8, advertised_dynamic_tool_names: []const []const u8) ![]const u8 { return Runtime(FakeApp).describeToolActionCompleted(self, arena, call, display_target, advertised_dynamic_tool_names, &test_ignored_list_entries, 100, 1024, 40, 120, 2048, 2, test_gateway_chat_url); }
pub fn describeToolActionDenied(self: *FakeApp, arena: Allocator, call: ToolCall, label: []const u8) ![]const u8 { return Runtime(FakeApp).describeToolActionDenied(self, arena, call, null, label, &.{}, &test_ignored_list_entries, 100, 1024, 40, 120, 2048, 2, test_gateway_chat_url); }
pub fn describeToolActionDeniedWithAdvertised(self: *FakeApp, arena: Allocator, call: ToolCall, display_target: ?[]const u8, label: []const u8, advertised_dynamic_tool_names: []const []const u8) ![]const u8 { return Runtime(FakeApp).describeToolActionDenied(self, arena, call, display_target, label, advertised_dynamic_tool_names, &test_ignored_list_entries, 100, 1024, 40, 120, 2048, 2, test_gateway_chat_url); }
pub fn permissionTargetForCall(self: *FakeApp, arena: Allocator, call: ToolCall) ![]const u8 { return Runtime(FakeApp).permissionTargetForCall(self, arena, call, &.{}, &test_ignored_list_entries, 100, 1024, 40, 120, 2048, 2, test_gateway_chat_url); }
pub fn permissionTargetForCallWithAdvertised(self: *FakeApp, arena: Allocator, call: ToolCall, advertised_dynamic_tool_names: []const []const u8) ![]const u8 { return Runtime(FakeApp).permissionTargetForCall(self, arena, call, advertised_dynamic_tool_names, &test_ignored_list_entries, 100, 1024, 40, 120, 2048, 2, test_gateway_chat_url); }
pub fn executeToolCall(self: *FakeApp, request: agent_runtime.ToolExecutionRequest) !agent_runtime.ToolExecutionResult { return Runtime(FakeApp).executeToolCall(self, request, &test_ignored_list_entries, 100, 1024, 40, 120, 2048, 2, test_gateway_chat_url); }
pub fn executeToolCallWithAdvertised(self: *FakeApp, request: agent_runtime.ToolExecutionRequest) !agent_runtime.ToolExecutionResult { return self.executeToolCall(request); }
pub fn registerAndEmitDiffBlock(self: *FakeApp, payload: agent_runtime.DiffEntryPayload) !void { _ = payload; self.diff_blocks += 1; }
pub fn formatToolExecutionErrorForAgent(self: *FakeApp, arena: Allocator, tool_name: []const u8, err: anyerror) ![]const u8 { _ = self; return std.fmt.allocPrint(arena, "Tool {s} failed: {s}", .{ tool_name, @errorName(err) }); }};
fn testAgentStreamProvider(stream_fn: agent_stream_provider.StreamFn) agent_stream_provider.Provider { var provider = test_builtin_gateway.agent_stream_provider; provider.stream_fn = stream_fn; return provider;}
const TestCatalogProvider = struct { saw_expected_input: bool = false,
fn appendEntry( alloc: Allocator, catalog: *std.ArrayList(model_catalog.ModelCatalogEntry), id_text: []const u8, ) !void { const id = try alloc.dupe(u8, id_text); errdefer alloc.free(id); const model_type = try alloc.dupe(u8, "language"); errdefer alloc.free(model_type); try catalog.append(alloc, .{ .id = id, .model_type = model_type }); }
fn fetch( raw_context: ?*anyopaque, alloc: Allocator, input: model_catalog.FetchInput, ) Allocator.Error!model_catalog.ProviderResult { const self: *TestCatalogProvider = @ptrCast(@alignCast(raw_context.?)); self.saw_expected_input = std.mem.eql(u8, input.access.authorizationCredential() orelse "", "api-key") and input.access.teamContext() == null and input.access.credentialSource() == .ai_gateway_api_key and std.mem.eql(u8, input.endpoint, "/catalog") and input.cancel_flag == null and input.view == .full;
var catalog: std.ArrayList(model_catalog.ModelCatalogEntry) = .empty; errdefer model_catalog.freeModelCatalog(alloc, &catalog); try appendEntry(alloc, &catalog, "provider/first"); try appendEntry(alloc, &catalog, "provider/second"); return .{ .catalog = catalog }; }};
test "app model id loading uses the injected catalog provider" { const alloc = std.testing.allocator; var app = try FakeApp.init(alloc); defer app.deinit(); var test_provider = TestCatalogProvider{};
var ids = try Runtime(FakeApp).fetchModelIds( &app, .{ .context = @ptrCast(&test_provider), .fetch_fn = TestCatalogProvider.fetch, }, "/catalog", ); defer { for (ids.items) |id| alloc.free(id); ids.deinit(alloc); }
try std.testing.expect(test_provider.saw_expected_input); try std.testing.expectEqual(@as(usize, 2), ids.items.len); try std.testing.expectEqualStrings("provider/first", ids.items[0]); try std.testing.expectEqualStrings("provider/second", ids.items[1]);}
test "app agent runtime builds tool context from app state and MCP callbacks" { const alloc = std.testing.allocator; var app = try FakeApp.init(alloc); defer app.deinit();
app.permission_engine.mode = .auto; app.worker.agent_turn_settings = .{ .max_tool_result_bytes = 4096, .first_call_tool_choice = .none, .fast_mode = true, .effort = types.ReasoningEffort.literal("high"), }; try app.permission_engine.allow(alloc, "run_command", "/tmp/workspace::zig build");
const ctx = testToolContext(&app); try std.testing.expectEqualStrings("/tmp/workspace", ctx.workspace_root); try std.testing.expectEqualStrings("api-key", ctx.api_key); try std.testing.expectEqualStrings("test-model", ctx.model); try std.testing.expect(ctx.tool_registry.lookup("web_search") != null); try std.testing.expectEqual(PermissionMode.auto, ctx.permission_mode); try std.testing.expectEqual(@as(usize, 1), ctx.permission_grants.len); try std.testing.expect(ctx.fast_mode); try std.testing.expectEqual(types.ReasoningEffort.literal("high"), ctx.effort); try std.testing.expect(!ctx.web_search_runtime_ready); try std.testing.expect(ctx.web_search_backend != null); try std.testing.expect(ctx.web_fetch_runtime.? == &app.web_fetch_runtime); try std.testing.expect(ctx.web_fetch_progress_ctx != null); try std.testing.expect(ctx.on_web_fetch_progress != null); try std.testing.expectEqualStrings("test-model", app.web_search_runtime.worker_model); try std.testing.expectEqual(ctx.gateway_retry_count, app.web_search_runtime.gateway_retry_count); try std.testing.expectEqualStrings(ctx.gateway_chat_url, app.web_search_runtime.gateway_chat_url); try std.testing.expectEqualStrings("/models", ctx.gateway_models_path); try std.testing.expectEqual(@as(usize, 4096), ctx.max_tool_result_bytes); try std.testing.expectEqual(types.ToolChoice.none, ctx.first_call_tool_choice); try std.testing.expect(ctx.cancel_flag.? == &app.worker.worker_cancel_requested); try std.testing.expectEqual(&app.worker, ctx.worker); try std.testing.expect(!@hasField(tool_runtime.Context, "background")); try std.testing.expect(ctx.subagent_host == null); try std.testing.expect(ctx.subagent_caller_id == null); try std.testing.expectEqual(&app.session, ctx.session); try std.testing.expectEqual(&app.change_tracker, ctx.tracker.?); const child_ctx = Runtime(FakeApp).childToolContext(ctx); try std.testing.expect(child_ctx.tracker == null); try std.testing.expect(ctx.mcp_has_tool.?(ctx.mcp_ctx.?, "mcp_lookup", .unrestricted));
const result = try ctx.mcp_call_tool.?( ctx.mcp_ctx.?, alloc, "mcp_lookup", "{}", ctx.max_tool_result_bytes, .{}, ); defer alloc.free(result.?.model_output); try std.testing.expectEqualStrings("{\"ok\":true}", result.?.model_output);}
test "interactive app prepared file mutation callback applies app permission policy" { const alloc = std.testing.allocator; var tmp = std.testing.tmpDir(.{}); defer tmp.cleanup(); try tmp.dir.createDirPath(io_mod.getIo(), "workspace"); const workspace = try io_mod.dirRealpathAlloc(alloc, tmp.dir, "workspace"); defer alloc.free(workspace);
var app = try FakeApp.init(alloc); defer app.deinit(); app.workspace_root = workspace; const mutation_tools = [_]tool_dispatch.Tool{test_builtin_tools.write_file}; app.tool_registry = .{ .tools = &mutation_tools }; var rules = [_]types.PermissionRule{.{ .permission = @constCast("edit"), .pattern = @constCast("blocked.txt"), .action = .deny, }}; app.permission_engine.rules = .{ .rules = &rules }; defer app.permission_engine.rules = .{};
var arena_state = std.heap.ArenaAllocator.init(alloc); defer arena_state.deinit(); const arena = arena_state.allocator(); const call: ToolCall = .{ .id = "prepared-app-write", .name = "write_file", .arguments_json = "{\"path\":\"blocked.txt\",\"content\":\"blocked\"}", }; var prepared = switch (try tool_admission.prepareFileMutationCall(arena, call, .{ .tool_registry = app.toolRegistry(), .workspace_root = workspace, })) { .tool_failure => return error.TestExpectedPreparedFileMutation, .prepared => |value| value, }; defer prepared.deinit(arena);
const deps = app_callbacks.Bindings(FakeApp).agentRuntimeDeps(&app); const callback = deps.request_prepared_file_mutation_permission orelse return error.TestExpectedPreparedFileMutationCallback; const review_calls = [_]ToolCall{call}; const review_root_messages = [_][]const u8{"do not bypass policy"}; const review_turn: permission_auto_classifier.ReviewTurnContext = .{ .model = "openai/gpt-5", .pending_assistant = .{ .role = .assistant, .tool_calls = &review_calls }, .target_call_id = call.id, .origin = .root, .trusted_root_context = review_root_messages[0], }; const outcome = try callback( deps.ctx, arena, call, &prepared, review_turn, .ask, &.{}, null, &.{}, );
try std.testing.expectEqual(ToolPermissionDecision.policy_denied, outcome.decision); try std.testing.expect(outcome.execution_authority == null);}
test "app prompt projection configures web search then blocks native execution" { const alloc = std.testing.allocator; const web_search_contract = @import("../tooling/web_search_contract.zig"); const ProviderState = struct { calls: usize = 0, }; const FailingWebSearchProvider = struct { fn execute( raw_ctx: ?*anyopaque, _: Allocator, _: web_search_runtime.Inputs, _: web_search_contract.ProviderRequest, _: ?web_search_contract.ProgressFn, _: ?*anyopaque, ) anyerror!web_search_contract.ProviderResponse { const state: *ProviderState = @ptrCast(@alignCast(raw_ctx orelse return error.TestWebSearchProvider)); state.calls += 1; return error.TestWebSearchProvider; } }; var app = try FakeApp.init(alloc); defer app.deinit(); var provider_state = ProviderState{}; var provider = app.web_search_runtime.provider orelse return error.TestExpectedEqual; provider.context = @ptrCast(&provider_state); provider.execute_fn = FailingWebSearchProvider.execute; app.web_search_runtime = web_search_runtime.Runtime.init(.{ .provider = provider, });
app.web_search_runtime.configure(.{ .api_key = "stale-key", .worker_model = "stale-model", .gateway_retry_count = 99, .gateway_chat_url = "https://stale.invalid/chat", });
var arena_state = std.heap.ArenaAllocator.init(alloc); defer arena_state.deinit(); const arena = arena_state.allocator(); var messages: std.ArrayList(ChatMessage) = .empty; defer messages.deinit(arena); try Runtime(FakeApp).appendStaticContextMessage(&app, arena, null, &messages, &test_ignored_list_entries, 100, 1024, 40, 120, 2048, 2, test_gateway_chat_url); try app.appendRuntimeContextMessage(arena, &messages);
try std.testing.expectEqualStrings("stale-key", app.web_search_runtime.api_key);
const validation = try app.validateToolCall(arena, .{ .id = "search", .name = "web_search", .arguments_json = "{\"query\":\"x\"}", }); try std.testing.expectEqualStrings("web_search field \"query\" must contain at least two characters", validation.failure); try std.testing.expectEqualStrings(app.auth.apiKey().?, app.web_search_runtime.api_key); try std.testing.expectEqualStrings(app.selected_model.items, app.web_search_runtime.worker_model); try std.testing.expectEqual(@as(usize, 2), app.web_search_runtime.gateway_retry_count); try std.testing.expectEqualStrings(test_gateway_chat_url, app.web_search_runtime.gateway_chat_url);
const execution = try app.executeToolCall(.{ .call_allocator = arena, .result_allocator = arena, .call = .{ .id = "search-execute", .name = "web_search", .arguments_json = "{\"query\":\"current Zig release\"}", }, .authority = .ordinary, .session_grants = &.{}, .advertised_dynamic_tool_names = &.{}, .max_tool_result_bytes = 2048, }); try std.testing.expectEqualStrings(app.auth.apiKey().?, app.web_search_runtime.api_key); try std.testing.expectEqualStrings(app.selected_model.items, app.web_search_runtime.worker_model); try std.testing.expectEqual(@as(usize, 2), app.web_search_runtime.gateway_retry_count); try std.testing.expectEqualStrings(test_gateway_chat_url, app.web_search_runtime.gateway_chat_url); try std.testing.expectEqual(.failure, execution.status); try std.testing.expectEqual(@as(usize, 0), provider_state.calls);}
test "app ChatGPT route removes Gateway-backed auxiliary capabilities" { const alloc = std.testing.allocator; var app = try FakeApp.init(alloc); defer app.deinit(); var credential = credentials.Credential{ .token = try alloc.dupe(u8, "chatgpt-secret"), .source = .chatgpt_subscription, }; defer credential.deinit(alloc); _ = app.auth.adoptCredential(alloc, &credential); app.selected_provider = .codex;
const ctx = Runtime(FakeApp).toolContext( &app, &test_ignored_list_entries, 100, 1024, 40, 120, 2048, 2, test_gateway_chat_url, ); try std.testing.expect(ctx.web_search_backend == null); try std.testing.expect(ctx.permission_reviewer_provider == null);}
test "app agent runtime tool context combines active settings with live permission mode" { const alloc = std.testing.allocator; var app = try FakeApp.init(alloc); defer app.deinit();
app.fast_mode = false; app.effort = types.ReasoningEffort.literal("low"); app.worker.agent_turn_settings = .{ .max_tool_result_bytes = 1024, .first_call_tool_choice = .auto, .fast_mode = false, .effort = types.ReasoningEffort.literal("low"), }; app.worker.setActiveAgentTurnSettings(.{ .max_tool_result_bytes = 8192, .first_call_tool_choice = .none, .fast_mode = true, .effort = types.ReasoningEffort.literal("high"), }); defer app.worker.clearActiveAgentTurnSettings(); app.permission_engine.mode = .auto;
const ctx = testToolContext(&app);
try std.testing.expectEqual(@as(usize, 8192), ctx.max_tool_result_bytes); try std.testing.expectEqual(types.ToolChoice.none, ctx.first_call_tool_choice); try std.testing.expect(ctx.fast_mode); try std.testing.expectEqual(types.ReasoningEffort.literal("high"), ctx.effort); try std.testing.expectEqual(PermissionMode.auto, ctx.permission_mode);}
test "app agent runtime formats active completed denied and MCP tool actions" { const alloc = std.testing.allocator; var arena_state = std.heap.ArenaAllocator.init(alloc); defer arena_state.deinit(); const arena = arena_state.allocator();
var app = try FakeApp.init(alloc); defer app.deinit();
const run_call: ToolCall = .{ .id = "1", .name = "shell", .arguments_json = "{\"action\":\"run\",\"command\":\"zig build\"}", };
const active = try app.describeToolAction(arena, run_call); try std.testing.expect(std.mem.find(u8, active, "● ") != null); try std.testing.expect(std.mem.find(u8, active, "•") == null); try std.testing.expect(std.mem.find(u8, active, "⏺") == null); try std.testing.expect(std.mem.find(u8, active, "▸") == null); try std.testing.expect(std.mem.find(u8, active, "Running") != null); try std.testing.expect(std.mem.find(u8, active, "\x1b[38;5;252mzig\x1b[39m") != null); try std.testing.expect(std.mem.find(u8, active, " build") != null);
const completed = try app.describeToolActionCompleted(arena, run_call); try std.testing.expect(std.mem.find(u8, completed, "● ") != null); try std.testing.expect(std.mem.find(u8, completed, "•") == null); try std.testing.expect(std.mem.find(u8, completed, "⏺") == null); try std.testing.expect(std.mem.find(u8, completed, "▸") == null); try std.testing.expect(std.mem.find(u8, completed, "Ran") != null); try std.testing.expect(std.mem.find(u8, completed, "\x1b[38;5;252mzig\x1b[39m") != null); try std.testing.expect(std.mem.find(u8, completed, " build") != null);
const denied = try app.describeToolActionDenied(arena, run_call, "Denied"); try std.testing.expect(std.mem.find(u8, denied, "Denied") != null); try std.testing.expect(std.mem.find(u8, denied, "\x1b[38;5;252mzig\x1b[39m") != null); try std.testing.expect(std.mem.find(u8, denied, " build") != null);
const malformed_registered: ToolCall = .{ .id = "malformed_registered", .name = "grep_files", .arguments_json = "{", }; const malformed_completed = try app.describeToolActionCompleted(arena, malformed_registered); try std.testing.expectEqualStrings( "● Completed\x1b[0m \x1b[38;5;245mtool call\x1b[0m", malformed_completed, ); const malformed_denied = try app.describeToolActionDenied(arena, malformed_registered, "Denied"); try std.testing.expectEqualStrings( "● Denied\x1b[0m \x1b[38;5;245mtool call\x1b[0m", malformed_denied, );
const malformed_unknown: ToolCall = .{ .id = "malformed_unknown", .name = "mcp_unknown", .arguments_json = "{", }; const malformed_unknown_completed = try app.describeToolActionCompleted(arena, malformed_unknown); try std.testing.expect(std.mem.find(u8, malformed_unknown_completed, "mcp_unknown") != null);
const historical_memory: ToolCall = .{ .id = "historical_memory", .name = "memory", .arguments_json = "{\"action\":\"list\"}", }; const historical_memory_completed = try app.describeToolActionCompleted(arena, historical_memory); try std.testing.expect(std.mem.find(u8, historical_memory_completed, "memory") != null);
const mcp_call: ToolCall = .{ .id = "mcp", .name = "mcp_lookup", .arguments_json = "{}" }; const mcp_action = try app.describeToolActionCompleted(arena, mcp_call); try std.testing.expect(std.mem.find(u8, mcp_action, "Completed") != null); try std.testing.expect(std.mem.find(u8, mcp_action, "MCP") == null); try std.testing.expect(std.mem.find(u8, mcp_action, "mcp_lookup") != null);
const advertised = [_][]const u8{"mcp_lookup"}; const advertised_mcp_active = try Runtime(FakeApp).describeToolAction(&app, arena, mcp_call, null, &advertised, &test_ignored_list_entries, 100, 1024, 40, 120, 2048, 2, test_gateway_chat_url); try std.testing.expectEqualStrings( "● Running MCP\x1b[0m \x1b[38;5;245mmcp_lookup\x1b[0m", advertised_mcp_active, ); const advertised_mcp_action = try Runtime(FakeApp).describeToolActionCompleted(&app, arena, mcp_call, null, &advertised, &test_ignored_list_entries, 100, 1024, 40, 120, 2048, 2, test_gateway_chat_url); try std.testing.expectEqualStrings( "● Ran MCP\x1b[0m \x1b[38;5;245mmcp_lookup\x1b[0m", advertised_mcp_action, ); app.mcp_has_tool_calls = 0; const advertised_mcp_denied = try Runtime(FakeApp).describeToolActionDenied(&app, arena, mcp_call, null, "Denied", &advertised, &test_ignored_list_entries, 100, 1024, 40, 120, 2048, 2, test_gateway_chat_url); try std.testing.expectEqualStrings( "● Denied\x1b[0m \x1b[38;5;245mmcp_lookup\x1b[0m", advertised_mcp_denied, ); try std.testing.expectEqual(@as(usize, 0), app.mcp_has_tool_calls);
app.mcp_name = "mcp_other"; app.mcp_has_tool_calls = 0; const unavailable_mcp_action = try Runtime(FakeApp).describeToolActionCompleted(&app, arena, mcp_call, null, &advertised, &test_ignored_list_entries, 100, 1024, 40, 120, 2048, 2, test_gateway_chat_url); try std.testing.expect(std.mem.find(u8, unavailable_mcp_action, "MCP") == null); try std.testing.expectEqual(@as(usize, 1), app.mcp_has_tool_calls);
app.mcp_has_tool_calls = 0; const builtin_advertised = [_][]const u8{"shell"}; _ = try Runtime(FakeApp).describeToolActionCompleted(&app, arena, run_call, null, &builtin_advertised, &test_ignored_list_entries, 100, 1024, 40, 120, 2048, 2, test_gateway_chat_url); try std.testing.expectEqual(@as(usize, 0), app.mcp_has_tool_calls);}
test "app agent runtime formats registered tool labels from context registry" { const alloc = std.testing.allocator; var arena_state = std.heap.ArenaAllocator.init(alloc); defer arena_state.deinit(); const arena = arena_state.allocator();
var app = try FakeApp.init(alloc); defer app.deinit(); app.tool_registry = custom_tool_registry;
const call: ToolCall = .{ .id = "custom", .name = "custom_registered_tool", .arguments_json = "{\"name\":\"registry value\"}", }; const active = try app.describeToolAction(arena, call); try std.testing.expect(std.mem.find(u8, active, "Custom running") != null); try std.testing.expect(std.mem.find(u8, active, "registry value") != null);
const completed = try app.describeToolActionCompleted(arena, call); try std.testing.expect(std.mem.find(u8, completed, "Custom ran") != null); try std.testing.expect(std.mem.find(u8, completed, "registry value") != null);}
test "app agent runtime bounds a large multiline run command activity" { const alloc = std.testing.allocator; var arena_state = std.heap.ArenaAllocator.init(alloc); defer arena_state.deinit(); const arena = arena_state.allocator();
var app = try FakeApp.init(alloc); defer app.deinit();
const arguments_json = "{\"action\":\"run\",\"command\":\"" ++ ("x\\n" ** 20_000) ++ "\"}"; const label = try app.describeToolAction(arena, .{ .id = "large_command", .name = "shell", .arguments_json = arguments_json, });
try std.testing.expect(label.len <= 180); try std.testing.expect(std.mem.findScalar(u8, label, '\n') == null); try std.testing.expect(std.mem.find(u8, label, "...") != null);}
test "native web_search labels preserve bounded query and domain filters" { const alloc = std.testing.allocator; var arena_state = std.heap.ArenaAllocator.init(alloc); defer arena_state.deinit(); const arena = arena_state.allocator();
var app = try FakeApp.init(alloc); defer app.deinit();
const call: ToolCall = .{ .id = "web_search", .name = "web_search", .arguments_json = "{\"query\":\"current Zig release\",\"allowed_domains\":[\"ziglang.org\",\"github.com\"]}", }; const active = try app.describeToolAction(arena, call); try std.testing.expect(std.mem.find(u8, active, "Searching") != null); try std.testing.expect(std.mem.find(u8, active, "current Zig release") != null); try std.testing.expect(std.mem.find(u8, active, "allowed: ziglang.org, github.com") != null);
const completed = try app.describeToolActionCompleted(arena, call); try std.testing.expect(std.mem.find(u8, completed, "Searched") != null); try std.testing.expect(std.mem.find(u8, completed, "current Zig release") != null);}
test "provider search labels use search wording and generic fallback" { const alloc = std.testing.allocator; var arena_state = std.heap.ArenaAllocator.init(alloc); defer arena_state.deinit(); const arena = arena_state.allocator();
var app = try FakeApp.init(alloc); defer app.deinit();
const call: ToolCall = .{ .id = "provider_search", .name = "parallel_search", .arguments_json = "{}", .provenance = .provider_executed, }; const active = try app.describeToolAction(arena, call); try std.testing.expect(std.mem.find(u8, active, "Searching") != null); try std.testing.expect(std.mem.find(u8, active, "web") != null);
const completed = try app.describeToolActionCompleted(arena, call); try std.testing.expect(std.mem.find(u8, completed, "Searched") != null); try std.testing.expect(std.mem.find(u8, completed, "web") != null);}
test "tool labels preserve skill name value" { const alloc = std.testing.allocator; var arena_state = std.heap.ArenaAllocator.init(alloc); defer arena_state.deinit(); const arena = arena_state.allocator();
var app = try FakeApp.init(alloc); defer app.deinit();
const selected: @import("../skills/skill_contract.zig").PreparedSkill = .{ .skill = .{ .name = "selected-workflow", .description = "", .path = "/skills/different-directory", .source = .workspace_fx, } }; const selected_call: ToolCall = .{ .id = "selected", .name = "skill", .arguments_json = "{\"location\":\"skill:0000000000000001:0/different-directory\"}", .resolved_skill = &selected, }; const selected_active = try app.describeToolAction(arena, selected_call); try std.testing.expect(std.mem.find(u8, selected_active, "selected-workflow") != null); const selected_completed = try app.describeToolActionCompleted(arena, selected_call); try std.testing.expect(std.mem.find(u8, selected_completed, "selected-workflow") != null);
var selected_resource = selected_call; selected_resource.arguments_json = "{\"location\":\"/skills/different-directory\",\"resource\":\"references/rules.md\"}"; const selected_resource_completed = try app.describeToolActionCompleted(arena, selected_resource); try std.testing.expect(std.mem.find(u8, selected_resource_completed, "Read skill resource") != null); try std.testing.expect(std.mem.find(u8, selected_resource_completed, "references/rules.md") != null);
const skill_call: ToolCall = .{ .id = "skill", .name = "skill", .arguments_json = "{\"name\":\"workflow\"}", }; const active = try app.describeToolAction(arena, skill_call); try std.testing.expect(std.mem.find(u8, active, "Loading skill") != null); try std.testing.expect(std.mem.find(u8, active, "workflow") != null);
const completed = try app.describeToolActionCompleted(arena, skill_call); try std.testing.expect(std.mem.find(u8, completed, "Loaded skill") != null); try std.testing.expect(std.mem.find(u8, completed, "workflow") != null);
const resource_call: ToolCall = .{ .id = "skill_resource", .name = "skill", .arguments_json = "{\"name\":\"workflow\",\"resource\":\"references/contract-design.md\"}", }; const resource_active = try app.describeToolAction(arena, resource_call); try std.testing.expect(std.mem.find(u8, resource_active, "Reading skill resource") != null); try std.testing.expect(std.mem.find(u8, resource_active, "references/contract-design.md") != null); try std.testing.expect(std.mem.find(u8, resource_active, "Loading skill workflow") == null);
const resource_completed = try app.describeToolActionCompleted(arena, resource_call); try std.testing.expect(std.mem.find(u8, resource_completed, "Read skill resource") != null); try std.testing.expect(std.mem.find(u8, resource_completed, "references/contract-design.md") != null); try std.testing.expect(std.mem.find(u8, resource_completed, "Loaded skill workflow") == null);
const install_call: ToolCall = .{ .id = "install_skill", .name = "install_skill", .arguments_json = "{\"source\":\"vercel-labs/agent-skills\",\"skill\":\"workflow\"}", }; const install_active = try app.describeToolAction(arena, install_call); try std.testing.expect(std.mem.find(u8, install_active, "Installing skill") != null); try std.testing.expect(std.mem.find(u8, install_active, "vercel-labs/agent-skills") != null);
const install_completed = try app.describeToolActionCompleted(arena, install_call); try std.testing.expect(std.mem.find(u8, install_completed, "Installed skill") != null); try std.testing.expect(std.mem.find(u8, install_completed, "vercel-labs/agent-skills") != null);}
test "subagent labels show named request and reply" { const alloc = std.testing.allocator; var arena_state = std.heap.ArenaAllocator.init(alloc); defer arena_state.deinit(); const arena = arena_state.allocator();
var app = try FakeApp.init(alloc); defer app.deinit();
const subagent_call: ToolCall = .{ .id = "subagent", .name = "subagent", .arguments_json = "{\"request\":{\"action\":\"message\",\"agent\":\"reviewer\",\"message\":\"Check replay\"}}", };
const active = try app.describeToolAction(arena, subagent_call); try std.testing.expectEqualStrings("● reviewer working\x1b[0m \x1b[38;5;245m· Check replay\x1b[0m", active);
const completed = try app.describeToolActionCompleted(arena, subagent_call); try std.testing.expectEqualStrings("● reviewer replied\x1b[0m \x1b[38;5;245m· Check replay\x1b[0m", completed);}
test "app agent runtime refreshes enabled project context through registry" { const alloc = std.testing.allocator; refresh_gather_calls = 0; refresh_targets_match = false; var app = RefreshContextApp{ .alloc = alloc, .context_enabled = true, .context_snapshot = try makeTestContextSnapshot(alloc, "test.stale_context", "stale context"), .context_registry = fresh_context_registry, }; defer app.deinit();
const targets = [_]context_contract.ApplicableTarget{.{ .path = "/tmp/workspace/images/example.png", .kind = .file, }}; try Runtime(RefreshContextApp).refreshProjectContext(&app, &targets);
try std.testing.expectEqual(@as(usize, 1), refresh_gather_calls); try std.testing.expect(refresh_targets_match); const contribution = app.context_snapshot.contribution orelse return error.TestExpectedEqual; try std.testing.expectEqualStrings("test.fresh_context", contribution.provider_id); try std.testing.expectEqualStrings("fresh:/tmp/workspace", contribution.content); try std.testing.expectEqualStrings("fresh context notice", app.context_notices.items); try std.testing.expectEqual(types.NoticeTone.warning, app.context_notice_tone.?); try std.testing.expectEqual(types.NoticeVisibility.full_only, app.context_notice_visibility.?);}
test "app agent runtime clears disabled project context without gathering" { const alloc = std.testing.allocator; refresh_gather_calls = 0; var app = RefreshContextApp{ .alloc = alloc, .context_enabled = false, .context_snapshot = try makeTestContextSnapshot(alloc, "test.stale_context", "stale context"), .context_registry = fresh_context_registry, }; defer app.deinit();
try Runtime(RefreshContextApp).refreshProjectContext(&app, &.{});
try std.testing.expectEqual(@as(usize, 0), refresh_gather_calls); try std.testing.expect(app.context_snapshot.contribution == null);}
test "app agent runtime propagates project context gathering failures" { const alloc = std.testing.allocator; const errors = [_]context_contract.ProviderError{ error.OutOfMemory, error.NoSpaceLeft, error.WriteFailed, }; for (errors) |expected_error| { refresh_gather_calls = 0; refresh_gather_error = expected_error; var app = RefreshContextApp{ .alloc = alloc, .context_enabled = true, .context_snapshot = try makeTestContextSnapshot(alloc, "test.stale_context", "stale context"), .context_registry = failing_context_registry, }; defer app.deinit();
try std.testing.expectError( expected_error, Runtime(RefreshContextApp).refreshProjectContext(&app, &.{}), );
try std.testing.expectEqual(@as(usize, 1), refresh_gather_calls); try std.testing.expect(app.context_snapshot.contribution == null); }}
test "app agent runtime appends static and transient context through configured registry" { const alloc = std.testing.allocator; var app = try FakeApp.init(alloc); defer app.deinit();
var arena_state = std.heap.ArenaAllocator.init(alloc); defer arena_state.deinit(); const arena = arena_state.allocator(); var messages: std.ArrayList(ChatMessage) = .empty; defer messages.deinit(arena); app.permission_engine.mode = .auto;
try Runtime(FakeApp).appendStaticContextMessage(&app, arena, null, &messages, &test_ignored_list_entries, 100, 1024, 40, 120, 2048, 2, test_gateway_chat_url); try Runtime(FakeApp).appendTransientRuntimeContextMessage(&app, arena, &messages, &test_ignored_list_entries, 100, 1024, 40, 120, 2048, 2, test_gateway_chat_url);
try std.testing.expectEqual(@as(usize, 3), messages.items.len); try std.testing.expectEqual(types.ChatRole.system, messages.items[0].role); try std.testing.expectEqualStrings("provider static:project context", messages.items[0].content.?); try std.testing.expect(std.mem.find(u8, messages.items[1].content.?, "<mcp_servers>") != null); try std.testing.expectEqualStrings("provider transient:/tmp/workspace:auto", messages.items[2].content.?);}
test "app agent runtime prefers active queued project context snapshot" { const alloc = std.testing.allocator; var app = try FakeApp.init(alloc); defer app.deinit(); var queued_snapshot = try makeTestContextSnapshot(alloc, "test.queued_context", "queued project context"); defer queued_snapshot.deinit(alloc); app.worker.active_context_snapshot = &queued_snapshot; defer app.worker.active_context_snapshot = null;
var arena_state = std.heap.ArenaAllocator.init(alloc); defer arena_state.deinit(); const arena = arena_state.allocator(); var messages: std.ArrayList(ChatMessage) = .empty; defer messages.deinit(arena);
try Runtime(FakeApp).appendStaticContextMessage(&app, arena, null, &messages, &test_ignored_list_entries, 100, 1024, 40, 120, 2048, 2, test_gateway_chat_url);
try std.testing.expectEqual(@as(usize, 2), messages.items.len); try std.testing.expectEqualStrings("provider static:queued project context", messages.items[0].content.?); try std.testing.expect(std.mem.find(u8, messages.items[1].content.?, "<mcp_servers>") != null); const tool_context = testToolContext(&app); try std.testing.expectEqualStrings("test.default_context", tool_context.context_registry.defaultProvider().id);}
fn makeQueuedPrompt(alloc: Allocator) !worker_runtime.QueuedPrompt { return .{ .prompt = try alloc.dupe(u8, "draft an issue"), .images = &.{}, .model = try alloc.dupe(u8, "test-model"), .api_key = try alloc.dupe(u8, "api-key"), .permission_mode = .auto, .history = try alloc.alloc(types.HistoryTurn, 0), .grants = try alloc.alloc(types.PermissionGrant, 0), };}
test "queued fresh prompt closes only a still-paused turn before provider execution" { const session_codec = @import("../session/session_codec.zig"); const session_store = @import("../session/session_store.zig"); const Probe = struct { app: *FakeApp, returned: std.atomic.Value(bool) = .init(false), requests: std.atomic.Value(usize) = .init(0), failure: ?anyerror = null, cancel_after_prepare: bool = false,
fn run(self: *@This(), job: worker_runtime.QueuedPrompt) void { Runtime(FakeApp).processQueuedPrompt(self.app, job, 1, test_gateway_chat_url, null) catch |err| { self.failure = err; }; self.returned.store(true, .release); }
fn stream(raw: ?*anyopaque, _: Allocator, request: agent_stream_provider.ModelRequest) !agent_stream_provider.Result { const self: *@This() = @ptrCast(@alignCast(raw.?)); _ = self.requests.fetchAdd(1, .seq_cst); try std.testing.expect(!self.app.session_persistence.writable.?.conversation_writer.turn_open); try request.admission.admit(); request.delivery.markPossiblySent(); return .{ .completed = .{ .completion = .{ .content = "new answer", .finish_reason = .stop } } }; }
fn prepare(raw: *anyopaque, request: worker_runtime.FreshPromptPreparation) !worker_runtime.FreshPromptHistory { const self: *@This() = @ptrCast(@alignCast(raw)); const result = try app_session_runtime.Runtime(FakeApp).prepareFreshPrompt(self.app, request); if (self.cancel_after_prepare) self.app.worker.worker_cancel_requested.store(true, .seq_cst); return result; } }; const alloc = std.testing.allocator; const queue_alloc = std.heap.c_allocator; const Outcome = enum { success, preflight_oom, cancel_after_prepare, finished_before_prepare }; for ([_]Outcome{ .success, .preflight_oom, .cancel_after_prepare, .finished_before_prepare }) |outcome| { var tmp = std.testing.tmpDir(.{}); defer tmp.cleanup(); const root = try io_mod.dirRealpathAlloc(alloc, tmp.dir, "."); defer alloc.free(root); var app = try FakeApp.init(alloc); defer app.deinit(); app.workspace_root = root; app.session_persistence.store = try session_store.Store.initFromHome(alloc, root, root); defer app.session_persistence.deinit(alloc); app.session_persistence.writable = try app.session_persistence.store.?.startWritableSession(alloc, .{ .id = @constCast("queued-pause"), .origin_workspace_root = @constCast(root), .workspace_root = @constCast(root), .created_at_ms = 1, .updated_at_ms = 1, .conversation_language = .literal("en"), .history = &.{}, .total_input_tokens = 0, .total_output_tokens = 0, .preferences = .{ .model = @constCast("test-model"), .effort = .auto, .fast_mode = false }, }); try app_session_runtime.Runtime(FakeApp).commitContextCompaction(&app, .{ .summary = @constCast("<context_handoff>original request</context_handoff>"), .removed_turn_count = 0, .compaction_count = 1, }, .{ .user = .{ .text = @constCast("same prompt") }, .assistant = @constCast("") }, null); const checkpoint: session_codec.RecoveryCheckpoint = .{ .turn_id = 41, .user = .{ .text = @constCast("same prompt") }, .assistant_source = @constCast("old partial answer"), .cause = .response_interrupted, .action = .paused, .authority = .{ .provider = .gateway, .model = @constCast("test-model") }, .requested_fast_mode = false, .fast_mode = false, .max_provider_attempts = 1, .consumed_provider_attempts = 1, }; try app_session_runtime.Runtime(FakeApp).setRecoveryCheckpoint(&app, checkpoint); app.worker.worker_processing = true; app.worker.active_turn_id = 41; var queued = try makeQueuedPrompt(queue_alloc); queue_alloc.free(queued.prompt); queued.prompt = try queue_alloc.dupe(u8, "same prompt"); types.freeHistoryTurnSlice(queue_alloc, queued.history); queued.history = try app.session.snapshotHistory(queue_alloc); try app.worker.admitInteractivePrompt(queue_alloc, queued); try app.worker.enqueuePrompt(queue_alloc, try makeQueuedPrompt(queue_alloc)); const previous_finished = types.FinishedPrompt{ .turn = .{ .assistant = .{ .user = .{ .text = @constCast("same prompt") }, .assistant = @constCast("old completed answer"), } } }; if (outcome == .finished_before_prepare) { try app.worker.propagateHistoryTurn(queue_alloc, previous_finished.turn, 0); try app.worker.pushEvent(queue_alloc, .{ .finish_prompt = previous_finished }); } app.worker.finishProcessing(); const job = (try app.worker.tryTakeNextPrompt(queue_alloc)).?; defer worker_runtime.freeQueuedPrompt(queue_alloc, job); try std.testing.expect(job.delivery.isContinuation()); try std.testing.expect(job.recovery_checkpoint == null); var probe: Probe = .{ .app = &app, .cancel_after_prepare = outcome == .cancel_after_prepare }; var provider = testAgentStreamProvider(Probe.stream); provider.context = &probe; app.agent_stream_provider = provider; if (outcome == .preflight_oom) app.snapshot_tools_error = error.OutOfMemory; const thread = try std.Thread.spawn(.{}, Probe.run, .{ &probe, job }); var joined = false; defer if (!joined) { app.worker.requestShutdown(); thread.join(); }; const deadline = io_mod.milliTimestamp() + 5_000; var observed = false; while (true) { app.worker.worker_mutex.lockUncancelable(io_mod.getIo()); const waiting = app.worker.history_publication_response == .waiting; app.worker.worker_mutex.unlock(io_mod.getIo()); if (waiting) break; if (io_mod.milliTimestamp() >= deadline) return error.TestExpectedFreshPromptPreparation; io_mod.sleep(std.time.ns_per_ms); } while (!observed) { var events = app.worker.takeEvents(); defer events.deinit(queue_alloc); defer for (events.items) |event| worker_runtime.freeWorkerEvent(queue_alloc, event); for (events.items) |event| { if (event == .finish_prompt) { try app_session_runtime.Runtime(FakeApp).appendFinishedPrompt(&app, event.finish_prompt); continue; } if (event != .prepare_fresh_prompt) continue; try std.testing.expectEqual(@as(usize, 0), probe.requests.load(.seq_cst)); try std.testing.expect(!probe.returned.load(.acquire)); try std.testing.expectEqual(outcome != .finished_before_prepare, app.session_persistence.writable.?.conversation_writer.turn_open); const current = app_session_runtime.Runtime(FakeApp).normalizeFreshPromptPreparation(&app, event.prepare_fresh_prompt); app.worker.resolveFreshPrompt(queue_alloc, current, &probe, Probe.prepare); observed = true; } if (io_mod.milliTimestamp() >= deadline) return error.TestExpectedFreshPromptPreparation; if (!observed) io_mod.sleep(std.time.ns_per_ms); } thread.join(); joined = true; if (outcome == .preflight_oom) { try std.testing.expectEqual(error.OutOfMemory, probe.failure.?); try std.testing.expectEqual(@as(usize, 0), probe.requests.load(.seq_cst)); try std.testing.expect(!app.session_persistence.writable.?.conversation_writer.turn_open); continue; } if (probe.failure) |err| return err; if (outcome == .cancel_after_prepare) { try std.testing.expectEqual(@as(usize, 0), probe.requests.load(.seq_cst)); continue; } try std.testing.expectEqual(@as(usize, 1), probe.requests.load(.seq_cst)); if (outcome == .finished_before_prepare) { for (app.worker.queued_history) |turn| try std.testing.expect(turn != .interrupted); } else { try std.testing.expectEqualStrings("old partial answer", app.worker.queued_history[1].interrupted.assistant.?); } var events = app.worker.takeEvents(); defer events.deinit(queue_alloc); defer for (events.items) |event| worker_runtime.freeWorkerEvent(queue_alloc, event); for (events.items) |event| { if (event == .finish_prompt) try app_session_runtime.Runtime(FakeApp).appendFinishedPrompt(&app, event.finish_prompt); } var page = try app.session_persistence.store.?.loadHistoryPage(alloc, "queued-pause", null, 10); defer page.deinit(alloc); try std.testing.expectEqual(@as(usize, 2), page.turns.len); if (outcome == .finished_before_prepare) { try std.testing.expectEqualStrings("same prompt", page.turns[0].assistant.user.text); try std.testing.expectEqualStrings("old completed answer", page.turns[0].assistant.assistant); } else { try std.testing.expectEqualStrings("same prompt", page.turns[0].interrupted.user.text); try std.testing.expectEqualStrings("old partial answer", page.turns[0].interrupted.assistant.?); } try std.testing.expectEqualStrings("same prompt", page.turns[1].assistant.user.text); try std.testing.expectEqualStrings("new answer", page.turns[1].assistant.assistant); }}
test "manual compaction worker call commits a checkpoint without a continuation" { const Gateway = struct { request_count: usize = 0, saw_no_tools: bool = false, observed_model: ?[]const u8 = null,
fn stream( raw: ?*anyopaque, _: Allocator, request: agent_stream_provider.ModelRequest, ) !agent_stream_provider.Result { const self: *@This() = @ptrCast(@alignCast(raw.?)); self.request_count += 1; self.observed_model = request.model; self.saw_no_tools = request.tools.advertised_names.len == 0 and request.tools.advertised_functions.len == 0 and request.tools.additional_functions.len == 0 and request.tools.selected_dynamic.len == 0 and request.tool_choice == .none; try request.admission.admit(); request.delivery.markPossiblySent(); const response = "Continue after manual compaction with the user's constraints intact."; request.events.emit(.{ .content_delta = response }); return .{ .completed = .{ .completion = .{ .content = response, .finish_reason = .stop, } } }; } };
const alloc = std.testing.allocator; var app = try FakeApp.init(alloc); defer app.deinit(); var gateway = Gateway{}; var provider = testAgentStreamProvider(Gateway.stream); provider.context = &gateway; app.agent_stream_provider = provider;
var job = worker_runtime.ContextCompactionTask{ .model = try alloc.dupe(u8, "test-model"), .api_key = try alloc.dupe(u8, "api-key"), .credential_source = .ai_gateway_api_key, .history = try alloc.alloc(types.HistoryTurn, 2), }; defer worker_runtime.freeContextCompactionTask(alloc, job); job.history[0] = try types.dupeHistoryTurn(alloc, .{ .assistant = .{ .user = .{ .text = @constCast("exact user request") }, .assistant = @constCast("exact completed response\n" ++ ("evidence " ** 1_000)), } }); job.history[1] = try types.dupeHistoryTurn(alloc, .{ .assistant = .{ .user = .{ .text = @constCast("second user request") }, .assistant = @constCast("second response"), } }); for (job.history) |turn| try app.session.appendHistoryEntry(alloc, turn);
const Worker = struct { returned: std.atomic.Value(bool) = std.atomic.Value(bool).init(false), failure: ?anyerror = null,
fn run(self: *@This(), target: *FakeApp, task: worker_runtime.ContextCompactionTask) void { Runtime(FakeApp).processContextCompaction(target, task, 1, null) catch |err| { self.failure = err; }; self.returned.store(true, .release); }
fn commit(raw: *anyopaque, event: worker_runtime.ContextCompaction) !void { const target: *FakeApp = @ptrCast(@alignCast(raw)); try app_session_runtime.Runtime(FakeApp).commitContextCompaction(target, event.summary, event.active_prefix, event.retained_from); } }; var worker: Worker = .{}; const thread = try std.Thread.spawn(.{}, Worker.run, .{ &worker, &app, job }); var joined = false; defer if (!joined) { app.worker.requestShutdown(); thread.join(); }; var events: std.ArrayList(worker_runtime.WorkerEvent) = .empty; defer events.deinit(std.heap.c_allocator); defer for (events.items) |event| worker_runtime.freeWorkerEvent(std.heap.c_allocator, event); const deadline = io_mod.milliTimestamp() + 5_000; while (events.items.len == 0) { var batch = app.worker.takeEvents(); defer batch.deinit(std.heap.c_allocator); try events.appendSlice(std.heap.c_allocator, batch.items); if (io_mod.milliTimestamp() >= deadline) return error.TestExpectedCompactionEvent; if (events.items.len == 0) io_mod.sleep(std.time.ns_per_ms); } try std.testing.expect(events.items[0] == .context_compaction); try std.testing.expect(!worker.returned.load(.acquire)); const pending_activity = app.worker.compactionActivitySnapshot(); try std.testing.expectEqual(compaction_activity.Stage.publication, pending_activity.operation.?.phase.running); try std.testing.expectEqual(@as(usize, 2), app.session.historyLen()); app.worker.resolveContextCompaction(std.heap.c_allocator, events.items[0].context_compaction, &app, Worker.commit); thread.join(); joined = true; if (worker.failure) |err| return err; try std.testing.expectEqual(@as(usize, 2), app.session.historyLen()); var tail_events = app.worker.takeEvents(); defer tail_events.deinit(std.heap.c_allocator); try events.appendSlice(std.heap.c_allocator, tail_events.items);
try std.testing.expectEqual(@as(usize, 1), gateway.request_count); try std.testing.expect(gateway.saw_no_tools); try std.testing.expectEqualStrings("test-model", gateway.observed_model.?); try std.testing.expectEqual(@as(usize, 1), events.items.len); try std.testing.expect(events.items[0] == .context_compaction); try std.testing.expect(events.items[0].context_compaction.active_prefix == null);
const completed_activity = app.worker.compactionActivitySnapshot(); try std.testing.expectEqual(pending_activity.operation.?.id, completed_activity.operation.?.id); try std.testing.expectEqual(compaction_activity.Outcome.succeeded, completed_activity.operation.?.phase.terminal.outcome); try std.testing.expect(completed_activity.revision > pending_activity.revision); app.worker.finishProcessing(); try std.testing.expectEqualDeep(completed_activity, app.worker.compactionActivitySnapshot());
app.snapshot_custom_guidance = "fixed tool guidance " ** 20_000; var provenance: ?compaction_activity.ErrorProvenance = null; try std.testing.expectError(error.ContextCapacityExceeded, Runtime(FakeApp).processContextCompaction(&app, job, 1, &provenance)); const failed_activity = app.worker.compactionActivitySnapshot(); try std.testing.expectEqual(failed_activity.operation.?.id, provenance.?.operation_id); try std.testing.expectEqual(error.ContextCapacityExceeded, provenance.?.err); try std.testing.expectEqual(error.ContextCapacityExceeded, failed_activity.operation.?.phase.terminal.err.?); try std.testing.expect(failed_activity.operation.?.id != completed_activity.operation.?.id); try std.testing.expectEqual(@as(usize, 1), gateway.request_count); try std.testing.expectEqual(@as(usize, 0), app.worker.worker_events.items.len);}
test "app agent runtime processes a cancelled queued prompt" { const alloc = std.testing.allocator; var app = try FakeApp.init(alloc); defer app.deinit();
app.worker.worker_cancel_requested.store(true, .seq_cst);
const job = try makeQueuedPrompt(alloc); defer worker_runtime.freeQueuedPrompt(alloc, job);
try Runtime(FakeApp).processQueuedPrompt(&app, job, 1, test_gateway_chat_url, null);
try std.testing.expectEqual(@as(usize, 0), app.append_context_count); try std.testing.expectEqual(@as(usize, 1), app.snapshot_tools_count);
var events = app.worker.takeEvents(); defer events.deinit(std.heap.c_allocator); defer for (events.items) |event| worker_runtime.freeWorkerEvent(std.heap.c_allocator, event);
try std.testing.expectEqual(@as(usize, 2), events.items.len); try std.testing.expect(events.items[0] == .tool_lifecycle); try std.testing.expect(events.items[0].tool_lifecycle.turn_finished.turn_id != 0); try std.testing.expectEqual( types.TurnPresentationOutcome.interrupted, events.items[0].tool_lifecycle.turn_finished.outcome, ); try std.testing.expect(events.items[1] == .finish_prompt); try std.testing.expect(events.items[1].finish_prompt.turn == .interrupted);}
test "app direct ask delivers semantic presentation through the runtime sink" { const Gateway = struct { fn stream( _: ?*anyopaque, _: Allocator, request: agent_stream_provider.ModelRequest, ) !agent_stream_provider.Result { try request.admission.admit(); request.events.emit(.{ .content_delta = "Before table.\n" ++ "| Name | Count |\n" ++ "|------|------:|\n" ++ "| api | 7 |\n" ++ "After table.\n\n" ++ "```zig\n" ++ "const ready = true;\n" ++ "```\n\n" ++ "---\n" }); return .{ .completed = .{ .completion = .{ .content = "", .finish_reason = .stop } } }; } };
const alloc = std.testing.allocator; var app = try FakeApp.init(alloc); defer app.deinit(); const job = try makeQueuedPrompt(alloc); defer worker_runtime.freeQueuedPrompt(alloc, job);
app.agent_stream_provider = testAgentStreamProvider(Gateway.stream);
try Runtime(FakeApp).processQueuedPrompt(&app, job, 1, test_gateway_chat_url, null);
var events = app.worker.takeEvents(); defer events.deinit(std.heap.c_allocator); defer for (events.items) |event| worker_runtime.freeWorkerEvent(std.heap.c_allocator, event);
var table_count: usize = 0; var code_count: usize = 0; var rule_count: usize = 0; const SemanticEvent = enum { table, code_block, thematic_rule }; var semantic_events: [3]SemanticEvent = undefined; var semantic_event_count: usize = 0; for (events.items) |event| switch (event) { .assistant_presentation => |presentation| switch (presentation) { .table => |table| { table_count += 1; semantic_events[semantic_event_count] = .table; semantic_event_count += 1; try std.testing.expectEqualStrings("api", table.rows[1].cells[0]); }, .code_block => |block| { code_count += 1; semantic_events[semantic_event_count] = .code_block; semantic_event_count += 1; try std.testing.expectEqualStrings("zig", block.language); try std.testing.expectEqualStrings("const ready = true;\n", block.code); }, .thematic_rule => { rule_count += 1; semantic_events[semantic_event_count] = .thematic_rule; semantic_event_count += 1; }, .text => {}, }, else => {}, };
try std.testing.expectEqual(@as(usize, 1), table_count); try std.testing.expectEqual(@as(usize, 1), code_count); try std.testing.expectEqual(@as(usize, 1), rule_count); try std.testing.expectEqualSlices( SemanticEvent, &.{ .table, .code_block, .thematic_rule }, semantic_events[0..semantic_event_count], );}
test "app agent runtime clears active turn settings when queued prompt setup fails" { const alloc = std.testing.allocator; var app = try FakeApp.init(alloc); defer app.deinit();
app.fast_mode = false; app.effort = types.ReasoningEffort.literal("low"); app.worker.agent_turn_settings = .{ .fast_mode = false, .effort = types.ReasoningEffort.literal("low"), }; app.snapshot_tools_error = error.TestExpectedEqual;
var job = try makeQueuedPrompt(alloc); defer worker_runtime.freeQueuedPrompt(alloc, job); job.agent_settings = .{ .fast_mode = true, .effort = types.ReasoningEffort.literal("high"), }; job.permission_mode = .yolo;
try std.testing.expectError(error.TestExpectedEqual, Runtime(FakeApp).processQueuedPrompt(&app, job, 1, test_gateway_chat_url, null)); try std.testing.expectEqual( @as(?PermissionMode, .yolo), app.snapshot_permission_mode, );
const effective = app.worker.effectiveAgentTurnSettings(); try std.testing.expect(!effective.fast_mode); try std.testing.expectEqual(types.ReasoningEffort.literal("low"), effective.effort);}
test "subagent tool projection uses immutable admission permission rules" { const alloc = std.testing.allocator; var app = try FakeApp.init(alloc); defer app.deinit();
var live_rules = [_]types.PermissionRule{.{ .permission = @constCast("bash"), .pattern = @constCast("live-rule"), .action = .deny, }}; app.permission_engine.rules = .{ .rules = &live_rules }; defer app.permission_engine.rules = .{};
var admission_rules = [_]types.PermissionRule{.{ .permission = @constCast("bash"), .pattern = @constCast("admitted-rule"), .action = .allow, }}; var admission = try subagent_domain.captureAdmission(alloc, .{ .parent_id = "parent", .source_id = "parent", .model = "test-model", .effort = .auto, .permission_mode = .auto, .rules = .{ .rules = &admission_rules }, }); defer admission.deinit(alloc);
var replacement_rules = [_]types.PermissionRule{.{ .permission = @constCast("bash"), .pattern = @constCast("replacement-rule"), .action = .ask, }}; var barrier = ProjectionBarrier{}; app.snapshot_barrier = &barrier; app.snapshot_tools_error = error.TestExpectedEqual; var turn: subagent_execution.TurnContext = undefined; var cancel = std.atomic.Value(bool).init(false); const message = subagent_domain.QueuedMessage{ .id = @constCast("message"), .source_id = @constCast("parent"), .content = @constCast("run"), .created_at_ms = 1, };
const Project = struct { app: *FakeApp, turn: *subagent_execution.TurnContext, message: subagent_domain.QueuedMessage, admission: subagent_domain.AdmissionSnapshot, cancel: *std.atomic.Value(bool), done: std.atomic.Value(bool) = std.atomic.Value(bool).init(false), err: ?anyerror = null,
fn run(self: *@This()) void { defer self.done.store(true, .release); _ = Runtime(FakeApp).runSubagentChild( self.app, self.turn, self.message, self.admission, self.cancel, ) catch |err| { self.err = err; return; }; } }; var project = Project{ .app = &app, .turn = &turn, .message = message, .admission = admission, .cancel = &cancel, }; const projection_thread = try std.Thread.spawn(.{}, Project.run, .{&project}); var joined = false; defer if (!joined) { barrier.release.store(true, .release); projection_thread.join(); }; const observation_deadline = io_mod.milliTimestamp() + 5_000; while (!barrier.entered.load(.acquire) and !project.done.load(.acquire) and io_mod.milliTimestamp() < observation_deadline) { io_mod.sleep(std.time.ns_per_ms); } if (!barrier.entered.load(.acquire)) { cancel.store(true, .release); barrier.release.store(true, .release); projection_thread.join(); joined = true; return error.TestProjectionNotEntered; } app.permission_engine.rules = .{ .rules = &replacement_rules }; barrier.release.store(true, .release); projection_thread.join(); joined = true;
try std.testing.expectEqual(error.OutOfMemory, project.err.?); try std.testing.expectEqualStrings( "admitted-rule", app.snapshot_permission_rule_pattern orelse return error.TestExpectedEqual, );}
test "subagent tool context uses immutable admission authority" { const alloc = std.testing.allocator; var app = try FakeApp.init(alloc); defer app.deinit();
var live_rules = [_]types.PermissionRule{.{ .permission = @constCast("bash"), .pattern = @constCast("live-rule"), .action = .deny, }}; app.permission_engine.rules = .{ .rules = &live_rules }; defer app.permission_engine.rules = .{};
var admission_rules = [_]types.PermissionRule{.{ .permission = @constCast("bash"), .pattern = @constCast("admitted-rule"), .action = .allow, }}; const admission_grants = [_]types.PermissionGrant{.{ .tool_name = @constCast("run_command"), .target_path = @constCast("/tmp/workspace::zig build"), }}; var admission = try subagent_domain.captureAdmission(alloc, .{ .parent_id = "parent", .source_id = "parent", .model = "test-model", .effort = .auto, .permission_mode = .auto, .rules = .{ .rules = &admission_rules }, .grants = &admission_grants, }); defer admission.deinit(alloc);
const ctx = app.subagentToolContextForAdmission(admission); try std.testing.expectEqual(PermissionMode.auto, ctx.permission_mode); try std.testing.expectEqual(@as(usize, 1), ctx.permission_rules.rules.len); try std.testing.expectEqualStrings( "admitted-rule", ctx.permission_rules.rules[0].pattern, ); try std.testing.expectEqual(@as(usize, 1), ctx.permission_grants.len); try std.testing.expectEqualStrings( "run_command", ctx.permission_grants[0].tool_name, );}
test "app agent runtime settles queued snapshot ownership when prompt admission fails" { const alloc = std.testing.allocator; const Admission = enum { projection_failure, invalid_writer, uncertain_checkpoint }; for ([_]Admission{ .projection_failure, .invalid_writer, .uncertain_checkpoint }) |admission| { const invalid_writer = admission != .projection_failure; var tmp = std.testing.tmpDir(.{}); defer tmp.cleanup(); { var file = try tmp.dir.createFile(std.testing.io, "queued-snapshot.bin", .{}); defer file.close(std.testing.io); try file.writeStreamingAll(std.testing.io, "\x89PNG\r\n\x1a\nqueued"); } const snapshot_path = try io_mod.dirRealpathAlloc(alloc, tmp.dir, "queued-snapshot.bin"); defer alloc.free(snapshot_path);
var app = try FakeApp.init(alloc); defer app.deinit(); app.snapshot_tools_error = error.TestExpectedEqual; const root = try io_mod.dirRealpathAlloc(alloc, tmp.dir, "."); defer alloc.free(root); defer app.session_persistence.deinit(alloc); if (invalid_writer) { app.session_persistence.store = try @import("../session/session_store.zig").Store.initFromHome(alloc, root, root); app.session_persistence.writable = try app.session_persistence.store.?.startWritableSession(alloc, .{ .id = @constCast("rejected-images"), .origin_workspace_root = @constCast(root), .workspace_root = @constCast(root), .created_at_ms = 1, .updated_at_ms = 1, .conversation_language = .literal("en"), .history = &.{}, .total_input_tokens = 0, .total_output_tokens = 0, .preferences = .{ .model = @constCast("test-model"), .effort = .auto, .fast_mode = false }, }); app.session_persistence.writable.?.conversation_writer.failure = error.SessionCommitFailed; }
var job = try makeQueuedPrompt(alloc); defer worker_runtime.freeQueuedPrompt(alloc, job); job.images = try types.dupeImageAttachmentSlice(alloc, &.{.{ .id = 1, .path = @constCast("/tmp/source.png"), .media_type = @constCast("image/png"), .snapshot_path = snapshot_path, .snapshot_sha256 = @constCast("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), }});
if (admission == .uncertain_checkpoint) { app.worker.active_turn_id = 41; app.worker.preservePromptSnapshots(41, job.images); app.session_persistence.writable.?.conversation_writer.failure = error.SessionPersistenceUncertain; } try std.testing.expectError( switch (admission) { .projection_failure => error.TestExpectedEqual, .invalid_writer => error.SessionCommitFailed, .uncertain_checkpoint => error.SessionPersistenceUncertain, }, Runtime(FakeApp).processQueuedPrompt(&app, job, 1, test_gateway_chat_url, null), ); if (admission == .uncertain_checkpoint) { try std.Io.Dir.accessAbsolute(std.testing.io, snapshot_path, .{}); } else { try std.testing.expectError( error.FileNotFound, std.Io.Dir.accessAbsolute(std.testing.io, snapshot_path, .{}), ); } }}
test "app agent runtime discards every snapshot in a failed multi-image preflight" { const alloc = std.testing.allocator; var tmp = std.testing.tmpDir(.{}); defer tmp.cleanup(); { var first = try tmp.dir.createFile(std.testing.io, "first.bin", .{}); defer first.close(std.testing.io); try first.writeStreamingAll(std.testing.io, "first"); } { var second = try tmp.dir.createFile(std.testing.io, "second.bin", .{}); defer second.close(std.testing.io); try second.writeStreamingAll(std.testing.io, "second"); } const first_path = try io_mod.dirRealpathAlloc(alloc, tmp.dir, "first.bin"); defer alloc.free(first_path); const second_path = try io_mod.dirRealpathAlloc(alloc, tmp.dir, "second.bin"); defer alloc.free(second_path);
var app = try FakeApp.init(alloc); defer app.deinit(); app.snapshot_tools_error = error.TestExpectedEqual; var job = try makeQueuedPrompt(alloc); defer worker_runtime.freeQueuedPrompt(alloc, job); job.images = try types.dupeImageAttachmentSlice(alloc, &.{ .{ .id = 1, .path = @constCast("/tmp/first.png"), .media_type = @constCast("image/png"), .snapshot_path = first_path, .snapshot_sha256 = @constCast("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), }, .{ .id = 2, .path = @constCast("/tmp/second.png"), .media_type = @constCast("image/png"), .snapshot_path = second_path, .snapshot_sha256 = @constCast("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"), }, });
try std.testing.expectError( error.TestExpectedEqual, Runtime(FakeApp).processQueuedPrompt(&app, job, 1, test_gateway_chat_url, null), ); try std.testing.expectError( error.FileNotFound, std.Io.Dir.accessAbsolute(std.testing.io, first_path, .{}), ); try std.testing.expectError( error.FileNotFound, std.Io.Dir.accessAbsolute(std.testing.io, second_path, .{}), );}
test "app agent runtime queued prompt config uses captured job settings over stale live app state" { const alloc = std.testing.allocator; var app = try FakeApp.init(alloc); defer app.deinit();
app.fast_mode = false; app.effort = types.ReasoningEffort.literal("low"); app.worker.agent_turn_settings = .{ .max_tool_result_bytes = 1024, .first_call_tool_choice = .auto, .fast_mode = false, .effort = types.ReasoningEffort.literal("low"), };
var job = try makeQueuedPrompt(alloc); defer worker_runtime.freeQueuedPrompt(alloc, job); job.agent_settings = .{ .max_tool_result_bytes = 8192, .first_call_tool_choice = .none, .fast_mode = true, .effort = types.ReasoningEffort.literal("high"), }; const custom_guidance = try alloc.dupe(u8, "app custom tool guidance"); var tool_projection = tool_projection_mod.EffectiveToolProjection{ .advertised_names = try alloc.alloc([]const u8, 0), .advertised_functions = try alloc.alloc(model_tool_schema.FunctionSchema, 0), .custom_guidance = custom_guidance, }; defer tool_projection.deinit(alloc); const config = Runtime(FakeApp).buildQueuedPromptConfig(&app, job, .{ .skills = &.{} }, &.{}, 1, test_gateway_chat_url, &tool_projection, null);
try std.testing.expect(config.fast_mode); try std.testing.expectEqual(types.ReasoningEffort.literal("high"), config.effort); try std.testing.expectEqual(@as(usize, 8192), config.max_tool_result_bytes); try std.testing.expectEqual(types.ToolChoice.none, config.first_call_tool_choice); try std.testing.expectEqualSlices([]const u8, tool_projection.advertised_names, config.advertised_tool_names); try std.testing.expectEqualSlices(model_tool_schema.FunctionSchema, tool_projection.advertised_functions, config.advertised_functions); try std.testing.expectEqualStrings(tool_projection.custom_guidance, config.custom_tool_guidance); try std.testing.expectEqualStrings(test_prompt_policy.system_prompt, config.system_prompt); try std.testing.expectEqualStrings("test model overlay", config.model_prompt_overlay.?); try std.testing.expect(!app.fast_mode); try std.testing.expectEqual(types.ReasoningEffort.literal("low"), app.effort);}
test "app agent runtime highlights shell command rows over the tool text base" { const alloc = std.testing.allocator; var arena_state = std.heap.ArenaAllocator.init(alloc); defer arena_state.deinit(); const arena = arena_state.allocator();
var app = try FakeApp.init(alloc); defer app.deinit();
const run_call: ToolCall = .{ .id = "1", .name = "shell", .arguments_json = "{\"action\":\"run\",\"command\":\"printf 'hello world' | wc -c\"}", }; const completed = try app.describeToolActionCompleted(arena, run_call);
// The label and untokenized command text keep the muted tool-text base, // while the command verb and quoted string pick up syntax palette colors // and return to the base after their closes. try std.testing.expect(std.mem.startsWith(u8, completed, "● Ran\x1b[0m \x1b[38;5;245m")); try std.testing.expect(std.mem.indexOf( u8, completed, "\x1b[38;5;252mprintf\x1b[39m\x1b[38;5;245m", ) != null); try std.testing.expect(std.mem.indexOf( u8, completed, "\x1b[38;5;250m'hello world'\x1b[39m\x1b[38;5;245m", ) != null); try std.testing.expect(std.mem.endsWith(u8, completed, "\x1b[0m")); // The pipe and the command after it take the keyword color, the flag the // number color; plain bytes are intact beneath the styling. try std.testing.expect(std.mem.indexOf(u8, completed, "\x1b[38;5;252m|\x1b[39m") != null); try std.testing.expect(std.mem.indexOf(u8, completed, "\x1b[38;5;252mwc\x1b[39m") != null); try std.testing.expect(std.mem.indexOf(u8, completed, "\x1b[38;5;250m-c\x1b[39m") != null);}