Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions apps/web/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ a compact `/uploads/...` reference after each successful save. `/` is exclusivel
persistent project-file browser;
`@` is exclusively the user-skill picker. The file browser reads durable project-file metadata and
does not create or wake Daytona merely because the user opens it.
Computer preview wakeups rotate the preview session and reload the visible iframe
once after an actual sandbox/process recovery. Silent capability rotation keeps the
live iframe mounted so application state is preserved during ordinary use.

## Public exports

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,12 @@ async function refreshPreviewSession(
const controller = new AbortController();
runtime.wakeAbort = controller;
runtime.waking = true;
if (showBooting) setPhase("booting");
if (showBooting) {
// Unmount a stale cross-origin document before a real wake. The fresh handoff URL then performs
// its own cookie exchange on remount; silent capability rotation still preserves live SPA state.
clearPreviewUrls(deps);
setPhase("booting");
}
try {
const outcome = await requestPreviewRefresh(
{ ...deps, threadId: deps.threadId },
Expand Down
5 changes: 4 additions & 1 deletion packages/agent-core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ Tools execute autonomously inside the active request context. Sandbox operations
remain project-root confined, browser actions remain origin-bound, connected-app
actions remain scoped to the user's active account, and secret-bearing input is
validated before execution. Deterministic prepare/execute boundaries keep dynamic
ports and Git destinations stable between resolution and execution.
ports and Git destinations stable between resolution and execution. The managed
preview tool owns Computer-visible dev servers, remaps a requested port to the
project's allocated port when necessary, and is distinct from generic background
process tools so idle recovery always has a canonical process record.
Browser-only runs use the account sandbox without materializing a persistent project;
workspace-backed file, shell, document, chart, or artifact work resolves the thread's
project lazily when durable project storage is actually needed.
Expand Down
4 changes: 2 additions & 2 deletions packages/agent-core/src/mastra/system-prompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ A Linux sandbox is available when the task genuinely needs it. Ordinary conversa
- Node.js 24 (node, npm, pnpm) and Python 3 (python3, pip3) — install anything else you need from the shell.
- LibreOffice (headless) plus preinstalled Node libraries for deliverables: pptxgenjs (slides), docx, exceljs, @react-pdf/renderer, recharts, arquero.
- A headed Chromium browser you drive to test what you build and to browse the web.
- A dev server you expose as a live preview on port 5173.
- A dev server you expose with code_start_dev_server. Request port 5173 normally; the tool owns the user-visible Computer preview and safely remaps it when another project already uses that port. Never launch a user-facing app with shell_exec, shell_terminal, or shell_start_process because those processes are not registered as the project preview and cannot recover after sandbox idle stops.
Use the computer when the requested outcome needs it. Do the work there instead of describing work you could just do, and never claim you did something you didn't run.`,

`## How you work
Expand Down Expand Up @@ -202,7 +202,7 @@ Beyond these you also have browser, document-generation, data-analysis, web-rese

const WEB_MODULE = `## Building web apps

Make the app real and complete: working features, real data flow, considered design. Default to a clean modern stack — React / Next.js. Ship something polished: sensible colour and type, responsive, mobile-first, no lorem ipsum, no dead buttons, no placeholder images. Write the files, install deps, and start the dev server early (code_start_dev_server on port 5173, --hostname 0.0.0.0) so you're always working against the running app.
Make the app real and complete: working features, real data flow, considered design. Default to a clean modern stack — React / Next.js. Ship something polished: sensible colour and type, responsive, mobile-first, no lorem ipsum, no dead buttons, no placeholder images. Write the files, install deps, and start the dev server early with code_start_dev_server (request port 5173 and bind to 0.0.0.0) so you're always working against the running app. Never substitute a shell process for the managed preview, even for a static server or when restarting an existing project.
Verify it in the browser: open the app's INTERNAL address in the sandbox's headed Chromium — http://localhost:<port> (e.g. http://localhost:5173), NOT the external preview link (your sandbox browser can't reach that) — then screenshot and read it (browser_open / browser_screenshot / browser_act / browser_observe / browser_extract, Stagehand LOCAL). Fix layout and console errors, re-check. If the browser can't load it at all, note you couldn't visually verify and go straight to your closing summary. The running app is shown to the user automatically in the Computer panel's Browser tab — never paste the preview URL.`;

const MOBILE_MODULE = `## Building the mobile app
Expand Down
8 changes: 4 additions & 4 deletions packages/agent-core/src/mastra/tool-defs/code-tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export const mastraRunCode = createTool({
export const mastraShellExec = createTool({
id: "shell_exec",
description:
"Run a deterministic sandbox command in argv form. Omit cwd for projectless browser, skill-runtime, or environment-inspection commands. For any command that reads, creates, or changes persistent project files, set cwd to /workspace; that explicitly attaches the project and maps /workspace to its persistent folder.",
"Run a bounded deterministic sandbox command in argv form; never use it to launch a user-facing dev server or background service. Use code_start_dev_server for Computer previews. Omit cwd for projectless browser, skill-runtime, or environment-inspection commands. For any command that reads, creates, or changes persistent project files, set cwd to /workspace; that explicitly attaches the project and maps /workspace to its persistent folder.",
inputSchema: ShellExecInputSchema,
outputSchema: ShellExecOutputSchema,
execute: async (input, context) => {
Expand All @@ -79,7 +79,7 @@ export const mastraShellExec = createTool({
export const mastraShellStartProcess = createTool({
id: "shell_start_process",
description:
"Start a long-running process under /workspace in the project sandbox with optional port readiness and restart policy.",
"Start a non-preview background process under /workspace with optional port readiness and restart policy. Never use this for a web or mobile app the user should see in Computer; code_start_dev_server is the only tool that registers and restores that preview.",
inputSchema: ShellStartProcessInputSchema,
outputSchema: ShellProcessOutputSchema,
execute: async (input, context) => {
Expand All @@ -104,7 +104,7 @@ export const mastraShellKillProcess = createTool({
export const mastraShellTerminal = createTool({
id: "shell_terminal",
description:
"Run a short terminal-style command in /workspace. Prefer shell_exec for deterministic argv automation.",
"Run a short foreground terminal-style command in /workspace. Never start a dev server or background process here; use code_start_dev_server for Computer previews. Prefer shell_exec for deterministic argv automation.",
inputSchema: ShellTerminalInputSchema,
outputSchema: ShellExecOutputSchema,
execute: async (input, context) => {
Expand Down Expand Up @@ -215,7 +215,7 @@ export const mastraGitPush = createTool({
export const mastraStartDevServer = createTool({
id: "code_start_dev_server",
description:
"Start a managed long-running dev server under /workspace. Returns only process readiness and the internal port; the user opens the authenticated preview from the Computer panel.",
"Start the managed web or mobile dev server shown in Computer. This is the only preview-registering server tool: it assigns the project's stable internal port, remaps the requested port in the command when needed, and restores the process after sandbox idle stops. Returns readiness and the actual internal port.",
inputSchema: StartDevServerInputSchema,
outputSchema: StartDevServerOutputSchema,
execute: async (input, context) => {
Expand Down
19 changes: 18 additions & 1 deletion packages/agent-core/src/tools/code/preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,12 @@ export async function prepareStartDevServer(
const isExpo = isExpoStartCommand(parsedInput.command);
const isMobile = isExpo || parsedInput.isMobile;
const port = await allocateDevServerPort(runtimeContext, slug, isMobile);
const command = remapRequestedDevServerPort(parsedInput.command, parsedInput.port, port);
return {
mayUseNetwork: isExpo,
port,
process: {
command: isExpo ? expoWebCommand(port) : parsedInput.command,
command: isExpo ? expoWebCommand(port) : command,
cwd,
env: { ...parsedInput.env, PORT: String(port) },
isMobile,
Expand All @@ -94,6 +95,22 @@ export async function prepareStartDevServer(
};
}

/**
* The model supplies a familiar port such as 5173, while the shared user sandbox may allocate a
* different stable port for this project. Remap standalone occurrences in argv and shell payloads
* so explicit framework flags and positional ports agree with the readiness probe and preview URL.
*/
function remapRequestedDevServerPort(
command: readonly string[],
requestedPort: number,
allocatedPort: number,
): string[] {
if (requestedPort === allocatedPort) return [...command];
const requestedPortToken = new RegExp(`(?<!\\d)${requestedPort}(?!\\d)`, "gu");
const allocatedPortToken = String(allocatedPort);
return command.map((argument) => argument.replace(requestedPortToken, allocatedPortToken));
}

/** Executes only the fully resolved process plan. */
export async function executePreparedStartDevServer(
prepared: PreparedStartDevServer,
Expand Down