char-fork/llama.cpp

git clone https://git.t4t.associates/char-fork/llama.cpp

Aleksander Grygierui: Stores split refactor (#27240)521a64cd0

char/aliceai-t5-moe
4.2 KiB150 linesraw
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 {
11	NEWLINE,
12	SANDBOX_EMPTY_OUTPUT,
13	SANDBOX_OUTPUT_MAX_CHARS,
14	SANDBOX_TIMEOUT_MS_DEFAULT,
15	SANDBOX_TIMEOUT_MS_MAX,
16	SANDBOX_TOOL_NAME,
17	SANDBOX_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> {
31	const enabled = !!settingsStore.config.symbolicMathEnabled;
32	const key = enabled ? 'nerdamer' : 'plain';
33
34	if (!harnessCache[key]) {
35		if (enabled) {
36			const { default: nerdamerJs } = await import('virtual:nerdamer');
37
38			harnessCache[key] = buildSandboxHarness(nerdamerJs);
39		} else {
40			harnessCache[key] = buildSandboxHarness('');
41		}
42	}
43
44	return harnessCache[key];
45}
46
47interface SandboxReply {
48	logs?: unknown;
49	result?: unknown;
50	error?: unknown;
51}
52
53function formatReply(reply: SandboxReply): ToolExecutionResult {
54	const lines: string[] = [];
55
56	if (Array.isArray(reply.logs)) {
57		for (const line of reply.logs) lines.push(String(line));
58	}
59
60	if (reply.error != null) {
61		lines.push(`Error: ${String(reply.error)}`);
62	} else if (reply.result != null) {
63		lines.push(`=> ${String(reply.result)}`);
64	}
65
66	let content = lines.join(NEWLINE);
67
68	if (!content) content = SANDBOX_EMPTY_OUTPUT;
69
70	if (content.length > SANDBOX_OUTPUT_MAX_CHARS) {
71		content = `${content.slice(0, SANDBOX_OUTPUT_MAX_CHARS)}${NEWLINE}${SANDBOX_TRUNCATION_NOTICE}`;
72	}
73
74	return { 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	 */
84	static async executeTool(
85		toolName: string,
86		params: Record<string, unknown>,
87		signal?: AbortSignal
88	): Promise<ToolExecutionResult> {
89		if (toolName !== SANDBOX_TOOL_NAME) {
90			return { content: `Unknown browser tool: ${toolName}`, isError: true };
91		}
92
93		const code = typeof params.code === 'string' ? params.code : '';
94
95		if (!code) {
96			return { content: 'Missing required parameter: code', isError: true };
97		}
98
99		const harness = await getHarness();
100		const requested = Number(params.timeout_ms);
101		const timeoutMs =
102			Number.isFinite(requested) && requested > 0
103				? Math.min(requested, SANDBOX_TIMEOUT_MS_MAX)
104				: SANDBOX_TIMEOUT_MS_DEFAULT;
105
106		return new Promise<ToolExecutionResult>((resolve, reject) => {
107			const iframe = document.createElement('iframe');
108
109			iframe.setAttribute('sandbox', 'allow-scripts');
110			iframe.style.display = 'none';
111			iframe.srcdoc = harness;
112
113			let settled = false;
114
115			const cleanup = () => {
116				settled = true;
117				clearTimeout(timer);
118				window.removeEventListener('message', onMessage);
119				signal?.removeEventListener('abort', onAbort);
120				iframe.remove();
121			};
122			const finish = (result: ToolExecutionResult) => {
123				if (settled) return;
124
125				cleanup();
126				resolve(result);
127			};
128			const onAbort = () => {
129				if (settled) return;
130
131				cleanup();
132				reject(new DOMException('Sandbox execution aborted', 'AbortError'));
133			};
134			const onMessage = (event: MessageEvent) => {
135				if (event.source !== iframe.contentWindow) return;
136
137				finish(formatReply((event.data ?? {}) as SandboxReply));
138			};
139			const timer = setTimeout(
140				() => finish({ content: `Execution timed out after ${timeoutMs} ms`, isError: true }),
141				timeoutMs
142			);
143
144			window.addEventListener('message', onMessage);
145			signal?.addEventListener('abort', onAbort);
146			iframe.onload = () => iframe.contentWindow?.postMessage({ code }, '*');
147			document.body.appendChild(iframe);
148		});
149	}
150}