char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
521a64cd0
char/aliceai-t5-moe
1/** 2* SandboxService - Runs untrusted code in a sandboxed worker 3* 4* Executes model-generated code inside a CSP-restricted, opaque-origin 5* iframe worker with output and timeout limits. No reactive state; consumed 6* by toolsStore for code-execution tools. 7*/ 8 9import { buildSandboxHarness } from './sandbox-harness' ; 10import { 11NEWLINE , 12SANDBOX_EMPTY_OUTPUT , 13SANDBOX_OUTPUT_MAX_CHARS , 14SANDBOX_TIMEOUT_MS_DEFAULT , 15SANDBOX_TIMEOUT_MS_MAX , 16SANDBOX_TOOL_NAME , 17SANDBOX_TRUNCATION_NOTICE 18} from '$lib/constants' ; 19import { settingsStore } from '$lib/stores/settings/index.svelte' ; 20import type { ToolExecutionResult } from '$lib/types' ; 21 22/** Cached harnesses keyed by whether nerdamer is included. */ 23const harnessCache :Record < string , string > = {}; 24 25/** 26* Build the sandbox harness. When symbolic math is enabled, loads the 27* nerdamer prelude lazily; otherwise builds a plain harness with an empty 28* prelude. Cached per variant so toggling the setting is instant. 29*/ 30async function getHarness () :Promise < string > { 31const enabled = !! settingsStore . config . symbolicMathEnabled ; 32const key = enabled ?'nerdamer' :'plain' ; 33 34if ( ! harnessCache [ key ]) { 35if ( enabled ) { 36const { default :nerdamerJs } = await import ( 'virtual:nerdamer' ); 37 38harnessCache [ key ] = buildSandboxHarness ( nerdamerJs ); 39} else { 40harnessCache [ key ] = buildSandboxHarness ( '' ); 41} 42} 43 44return harnessCache [ key ]; 45} 46 47interface SandboxReply { 48logs ?:unknown ; 49result ?:unknown ; 50error ?:unknown ; 51} 52 53function formatReply ( reply :SandboxReply ) :ToolExecutionResult { 54const lines :string [] = []; 55 56if ( Array . isArray ( reply . logs )) { 57for ( const line of reply . logs ) lines . push ( String ( line )); 58} 59 60if ( reply . error != null ) { 61lines . push ( `Error: ${ String ( reply . error )} ` ); 62} else if ( reply . result != null ) { 63lines . push ( `=> ${ String ( reply . result )} ` ); 64} 65 66let content = lines . join ( NEWLINE ); 67 68if ( ! content ) content = SANDBOX_EMPTY_OUTPUT ; 69 70if ( content . length > SANDBOX_OUTPUT_MAX_CHARS ) { 71content = ` ${ content . slice ( 0 , SANDBOX_OUTPUT_MAX_CHARS )} ${ NEWLINE } ${ SANDBOX_TRUNCATION_NOTICE } ` ; 72} 73 74return { content, isError :reply . error != null }; 75} 76 77export class SandboxService { 78/** 79* Execute a browser sandbox tool call and return its output. 80* One disposable iframe per execution, removed on completion, 81* timeout or abort. Removing the iframe terminates the worker 82* at the browser level, so runaway code cannot outlive it. 83*/ 84static async executeTool ( 85toolName :string , 86params :Record < string , unknown >, 87signal ?:AbortSignal 88) :Promise < ToolExecutionResult > { 89if ( toolName !== SANDBOX_TOOL_NAME ) { 90return { content :`Unknown browser tool: ${ toolName } ` , isError :true }; 91} 92 93const code = typeof params . code === 'string' ?params . code :'' ; 94 95if ( ! code ) { 96return { content :'Missing required parameter: code' , isError :true }; 97} 98 99const harness = await getHarness (); 100const requested = Number ( params . timeout_ms ); 101const timeoutMs = 102Number . isFinite ( requested ) && requested > 0 103 ?Math . min ( requested , SANDBOX_TIMEOUT_MS_MAX ) 104 :SANDBOX_TIMEOUT_MS_DEFAULT ; 105 106return new Promise < ToolExecutionResult >(( resolve , reject ) => { 107const iframe = document . createElement ( 'iframe' ); 108 109iframe . setAttribute ( 'sandbox' , 'allow-scripts' ); 110iframe . style . display = 'none' ; 111iframe . srcdoc = harness ; 112 113let settled = false ; 114 115const cleanup = () => { 116settled = true ; 117clearTimeout ( timer ); 118window . removeEventListener ( 'message' , onMessage ); 119signal ?. removeEventListener ( 'abort' , onAbort ); 120iframe . remove (); 121}; 122const finish = ( result :ToolExecutionResult ) => { 123if ( settled ) return ; 124 125cleanup (); 126resolve ( result ); 127}; 128const onAbort = () => { 129if ( settled ) return ; 130 131cleanup (); 132reject ( new DOMException ( 'Sandbox execution aborted' , 'AbortError' )); 133}; 134const onMessage = ( event :MessageEvent ) => { 135if ( event . source !== iframe . contentWindow ) return ; 136 137finish ( formatReply (( event . data ?? {}) as SandboxReply )); 138}; 139const timer = setTimeout ( 140() => finish ({ content :`Execution timed out after ${ timeoutMs } ms` , isError :true }), 141timeoutMs 142); 143 144window . addEventListener ( 'message' , onMessage ); 145signal ?. addEventListener ( 'abort' , onAbort ); 146iframe . onload = () => iframe . contentWindow ?. postMessage ({ code}, '*' ); 147document . body . appendChild ( iframe ); 148}); 149} 150}