Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
2 changes: 1 addition & 1 deletion SandboxDockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# FROM docker.io/cloudflare/sandbox:0.1.3
FROM docker.io/cloudflare/sandbox:0.4.14
FROM docker.io/cloudflare/sandbox:0.5.2

ARG TARGETARCH
RUN apt-get update && \
Expand Down
4 changes: 2 additions & 2 deletions bun.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"dependencies": {
"@ashishkumar472/cf-git": "1.0.5",
"@cloudflare/containers": "^0.0.28",
"@cloudflare/sandbox": "0.4.14",
"@cloudflare/sandbox": "^0.5.2",
"@noble/ciphers": "^1.3.0",
"@octokit/rest": "^22.0.1",
"@radix-ui/react-accordion": "^1.2.12",
Expand Down Expand Up @@ -230,7 +230,7 @@

"@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.4.0", "", { "dependencies": { "mime": "^3.0.0" } }, "sha512-+tv3z+SPp+gqTIcImN9o0hqE9xyfQjI1XD9pL6NuKjua9B1y7mNYv0S9cP+QEbA4ppVgGZEmKOvHX5G5Ei1CVA=="],

"@cloudflare/sandbox": ["@cloudflare/sandbox@0.4.14", "", { "dependencies": { "@cloudflare/containers": "^0.0.30" } }, "sha512-sI8yEqUd/vukhwSHGi93xau7xEtbP+OD/8xnjGbXr2iIGnAV+05VhLVIfJmwuU3VE8R96dnBJtEucEE5ZtXhcQ=="],
"@cloudflare/sandbox": ["@cloudflare/sandbox@0.5.2", "", { "dependencies": { "@cloudflare/containers": "^0.0.30" }, "peerDependencies": { "@openai/agents": "^0.3.2" }, "optionalPeers": ["@openai/agents"] }, "sha512-YXKeFr+FkPQTdKzmQv0khRV9JcK+W9ZpfxqZ4xu9TRzt0BHXLbjACSoIWkVtkuUPK1BTQdwqI5Ku1fYpM1pqGw=="],

"@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.7.8", "", { "peerDependencies": { "unenv": "2.0.0-rc.21", "workerd": "^1.20250927.0" }, "optionalPeers": ["workerd"] }, "sha512-Ky929MfHh+qPhwCapYrRPwPVHtA2Ioex/DbGZyskGyNRDe9Ru3WThYZivyNVaPy5ergQSgMs9OKrM9Ajtz9F6w=="],

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"dependencies": {
"@ashishkumar472/cf-git": "1.0.5",
"@cloudflare/containers": "^0.0.28",
"@cloudflare/sandbox": "0.4.14",
"@cloudflare/sandbox": "0.5.2",
"@noble/ciphers": "^1.3.0",
"@octokit/rest": "^22.0.1",
"@radix-ui/react-accordion": "^1.2.12",
Expand Down
2 changes: 1 addition & 1 deletion worker/agents/assistants/codeDebugger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,7 @@ The goal is working code, verified through evidence. Think internally, act decis
<appendix>
The most important class of errors is the "Maximum update depth exceeded" error which you definitely need to identify and fix.
Here are some important guidelines for identifying such issues and preventing them:
${PROMPT_UTILS.REACT_RENDER_LOOP_PREVENTION}
${PROMPT_UTILS.REACT_RENDER_LOOP_PREVENTION_LITE}
${PROMPT_UTILS.COMMON_DEP_DOCUMENTATION}
Expand Down
8 changes: 8 additions & 0 deletions worker/agents/assistants/projectsetup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ Output:
- Focus on blueprint requirements only
- cloudflare:workers is not needed, it's already installed

**Do not recommend installing \`cloudflare:workers\` or \`cloudflare:durable-objects\` as dependencies, these are already installed in the project always.**

${PROMPT_UTILS.COMMANDS}

<INPUT DATA>
Expand Down Expand Up @@ -128,6 +130,12 @@ ${error}`);
context: this.inferenceContext,
modelName: error? AIModels.GEMINI_2_5_FLASH : undefined,
});

if (!results || !results.string) {
this.logger.error('Project setup returned no result after all retries');
throw new Error('Failed to generate setup commands: inference returned null');
}

this.logger.info(`Generated setup commands: ${results.string}`);

this.save([createAssistantMessage(results.string)]);
Expand Down
18 changes: 7 additions & 11 deletions worker/agents/core/simpleGeneratorAgent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import { ScreenshotAnalysisOperation } from '../operations/ScreenshotAnalysis';
// Database schema imports removed - using zero-storage OAuth flow
import { BaseSandboxService } from '../../services/sandbox/BaseSandboxService';
import { WebSocketMessageData, WebSocketMessageType } from '../../api/websocketTypes';
import { InferenceContext, ModelConfig } from '../inferutils/config.types';
import { InferenceContext } from '../inferutils/config.types';
import { ModelConfigService } from '../../database/services/ModelConfigService';
import { fixProjectIssues } from '../../services/code-fixer';
import { GitVersionControl } from '../git';
Expand Down Expand Up @@ -355,22 +355,14 @@ export class SimpleCodeGeneratorAgent extends Agent<Env, CodeGenState> {
// Load the latest user configs
const modelConfigService = new ModelConfigService(this.env);
const userConfigsRecord = await modelConfigService.getUserModelConfigs(this.state.inferenceContext.userId);

const userModelConfigs: Record<string, ModelConfig> = {};
for (const [actionKey, mergedConfig] of Object.entries(userConfigsRecord)) {
if (mergedConfig.isUserOverride) {
const { isUserOverride, userConfigId, ...modelConfig } = mergedConfig;
userModelConfigs[actionKey] = modelConfig;
}
}
this.setState({
...this.state,
inferenceContext: {
...this.state.inferenceContext,
userModelConfigs,
userModelConfigs: userConfigsRecord,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Type Mismatch: UserModelConfigWithMetadata vs ModelConfig

The getUserModelConfigs() method returns Record<AgentActionKey, UserModelConfigWithMetadata>, which includes extra metadata fields (isUserOverride, userConfigId).

However, InferenceContext.userModelConfigs is typed as Record<AgentActionKey, ModelConfig> (see worker/agents/inferutils/config.types.ts:359).

The previous code (commit 7b90c82) explicitly filtered out these metadata fields:

const userModelConfigs: Record<string, ModelConfig> = {};
for (const [actionKey, mergedConfig] of Object.entries(userConfigsRecord)) {
    if (mergedConfig.isUserOverride) {
        const { isUserOverride, userConfigId, ...modelConfig } = mergedConfig;
        userModelConfigs[actionKey] = modelConfig;
    }
}

Issue: While TypeScript will allow this assignment (since UserModelConfigWithMetadata extends ModelConfig), downstream code may not expect these extra fields. The old code also only included configs where isUserOverride === true, but the new code includes all configs.

Recommendation: Either:

  1. Keep the old filtering logic if the isUserOverride check was intentional, or
  2. Update the InferenceContext type to accept UserModelConfigWithMetadata if the extra fields are acceptable

Clarification needed: Was the isUserOverride filter intentional, or was it meant to filter all configs regardless of override status?

},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium Priority - Type Safety: The removed filtering logic appears to have served a purpose in extracting clean ModelConfig objects by removing metadata fields (isUserOverride, userConfigId).

The new code directly assigns userConfigsRecord (which contains these extra fields) to userModelConfigs. This could cause issues if:

  1. The InferenceContext.userModelConfigs type expects Record<string, ModelConfig> without metadata fields
  2. Downstream code doesn't expect these additional fields

Recommendation: Verify that the type definition for InferenceContext.userModelConfigs accepts the full record structure returned by getUserModelConfigs(), or restore the filtering logic to maintain type safety.

});
this.logger().info(`Agent ${this.getAgentId()} session: ${this.state.sessionId} onStart: User configs loaded successfully`, {userModelConfigs});
this.logger().info(`Agent ${this.getAgentId()} session: ${this.state.sessionId} onStart: User configs loaded successfully`, {userConfigsRecord});
}

private async gitInit() {
Expand Down Expand Up @@ -1702,6 +1694,8 @@ export class SimpleCodeGeneratorAgent extends Agent<Env, CodeGenState> {
}

const regenerated = await this.regenerateFile({ filePath: path, fileContents, filePurpose }, issues, 0);
// Invalidate cache
this.staticAnalysisCache = null;
// Persist to sandbox instance
await this.getSandboxServiceClient().writeFiles(sandboxInstanceId, [{ filePath: regenerated.filePath, fileContents: regenerated.fileContents }], `Deep debugger fix: ${path}`);
return { path, diff: regenerated.lastDiff };
Expand Down Expand Up @@ -1766,6 +1760,8 @@ export class SimpleCodeGeneratorAgent extends Agent<Env, CodeGenState> {
fileCount: result.files.length
});

await this.deployToSandbox(savedFiles, false);

return { files: savedFiles.map(f => {
return {
path: f.filePath,
Expand Down
11 changes: 9 additions & 2 deletions worker/agents/operations/PhaseGeneration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const SYSTEM_PROMPT = `<ROLE>
<TASK>
You are given the blueprint (PRD) and the client query. You will be provided with all previously implemented project phases, the current latest snapshot of the codebase, and any current runtime issues or static analysis reports.

**Your primary task:** Design the next phase of the project as a deployable milestone leading to project completion or to address any user feedbacks or reported bugs.
**Your primary task:** Design the next phase of the project as a deployable milestone leading to project completion or to address any user feedbacks or reported bugs (runtime error fixing is the highest priority).

**Phase Planning Process:**
1. **ANALYZE** current codebase state and identify what's implemented vs. what remains
Expand All @@ -36,6 +36,8 @@ const SYSTEM_PROMPT = `<ROLE>
- **Accessibility**: Proper semantic HTML, ARIA labels, keyboard navigation
- **Supreme software development practices**: Follow the best coding principles and practices, and lay out the codebase in a way that is easy to maintain, extend and debug.
4. **VALIDATE** that the phase will be deployable with all views/pages working beautifully across devices

Plan the phase name and description appropriately. They don't have to strictly adhere to the blueprint's roadmap as unforeseen issues may occur.

The project needs to be fully ready to ship in a reasonable amount of time. Plan accordingly.
If no more phases are needed, conclude by putting blank fields in the response.
Expand Down Expand Up @@ -230,7 +232,7 @@ const issuesPromptFormatterWithGuidelines = (issues: IssueReport): string => {
serialized = `
${PROMPT_UTILS.COMMON_PITFALLS}

${issues.runtimeErrors.some((error) => error.message.includes('infinite loop') || error.message.includes('re-renders')) ? PROMPT_UTILS.REACT_RENDER_LOOP_PREVENTION: ''}
${issues.runtimeErrors.some((error) => error.message.includes('infinite loop') || error.message.includes('re-renders')) ? PROMPT_UTILS.REACT_RENDER_LOOP_PREVENTION_LITE: ''}

${serialized}`;
}
Expand Down Expand Up @@ -293,6 +295,11 @@ export class PhaseGenerationOperation extends AgentOperation<PhaseGenerationInpu
format: 'markdown',
});

if (!results) {
logger.error('Phase generation returned no result after all retries');
throw new Error('Failed to generate next phase: inference returned null');
}

logger.info(`Generated next phase: ${results.name}, ${results.description}`);

return results;
Expand Down
5 changes: 5 additions & 0 deletions worker/agents/operations/PostPhaseCodeFixer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@ export class FastCodeFixerOperation extends AgentOperation<FastCodeFixerInputs,
context: options.inferenceContext,
});

if (!result || !result.string) {
logger.error('Fast code fixer returned no result after all retries');
throw new Error('Failed to fix code issues: inference returned null');
}

const files = codeGenerationFormat.deserialize(result.string);
return files;
}
Expand Down
10 changes: 10 additions & 0 deletions worker/agents/operations/UserConversationProcessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,11 @@ export class UserConversationProcessor extends AgentOperation<UserConversationIn
}
}

if (!result || !result.string) {
logger.error('User message processing returned no result');
throw new Error('Failed to process user message: inference returned null');
}

logger.info("Successfully processed user message", {
streamingSuccess: !!extractedUserResponse,
});
Expand Down Expand Up @@ -663,6 +668,11 @@ Provide the summary now:`
context: options.inferenceContext,
});

if (!summaryResult || !summaryResult.string) {
logger.error('Conversation summarization returned no result');
throw new Error('Failed to generate conversation summary: inference returned null');
}

const summary = summaryResult.string.trim();

logger.info('Generated conversation summary', {
Expand Down
16 changes: 10 additions & 6 deletions worker/agents/planning/blueprint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const logger = createLogger('Blueprint');

const SYSTEM_PROMPT = `<ROLE>
You are a meticulous and forward-thinking Senior Software Architect and Product Manager at Cloudflare with extensive expertise in modern UI/UX design and visual excellence.
Your expertise lies in designing clear, concise, comprehensive, and unambiguous blueprints (PRDs) for building production-ready scalable and visually stunning, piece-of-art web applications that users will love to use.
Your expertise lies in designing clear, concise, comprehensive, and unambiguous blueprints (PRDs) for building production-ready scalable and visually stunning, piece-of-art web applications that users will love to use, using Cloudflare workers and durable objects.
</ROLE>

<TASK>
Expand All @@ -24,7 +24,8 @@ const SYSTEM_PROMPT = `<ROLE>
Focus on a clear and comprehensive design that prioritizes STUNNING VISUAL DESIGN, polish and depth, be to the point, explicit and detailed in your response, and adhere to our development process.
Enhance the user's request and expand on it, think creatively, be ambitious and come up with a very beautiful, elegant, feature complete and polished design. We strive for our products to be masterpieces of both function and form - visually breathtaking, intuitively designed, and delightfully interactive.

**REMEMBER: This is not a toy or demo project. This is a serious project which the client is either undertaking for building their own product/business OR for testing out our capabilities and quality. We do not just expect an MVP, We expect a production-ready, polished, and exceptional solution**
**REMEMBER: This is not a toy or educational project. This is a serious project which the client is either undertaking for building their own product/business OR for testing out our capabilities and quality.**
**Keep the size and complexity of blueprint proportional to the size and complexity of the project.** eg, No need to overengineer a 'todo' app.
</TASK>

<GOAL>
Expand All @@ -35,7 +36,6 @@ const SYSTEM_PROMPT = `<ROLE>
**VISUAL DESIGN EXCELLENCE**: Design the application frontend with exceptional attention to visual details - specify exact components, navigation patterns, headers, footers, color schemes, typography scales, spacing systems, micro-interactions, animations, hover states, loading states, and responsive behaviors.
**USER EXPERIENCE FOCUS**: Plan intuitive user flows, clear information hierarchy, accessible design patterns, and delightful interactions that make users want to use the application.
Build upon the provided template. Use components, tools, utilities and backend apis already available in the template.
Think and **BREAKDOWN** The project into multiple incremental phases that build upon each other to create a complete, polished product following our <PHASES GENERATION STRATEGY>.
</GOAL>

<INSTRUCTIONS>
Expand Down Expand Up @@ -100,6 +100,7 @@ const SYSTEM_PROMPT = `<ROLE>
✅ Icon libraries: lucide-react, heroicons (specify in frameworks)
❌ Never: .png, .jpg, .svg, .gif files in phase files list
Binary files cannot be generated. Always use the approaches above for visual content.
Do not recommend installing \`cloudflare:workers\` or \`cloudflare:durable-objects\` as dependencies, these are already installed in the project always.
</INSTRUCTIONS>

<KEY GUIDELINES>
Expand Down Expand Up @@ -239,11 +240,14 @@ export async function generateBlueprint({ env, inferenceContext, query, language
stream: stream,
});

if (results) {
// Filter and remove any pdf files
results.initialPhase.files = results.initialPhase.files.filter(f => !f.path.endsWith('.pdf'));
if (!results) {
logger.error('Blueprint generation returned no result after all retries');
throw new Error('Failed to generate blueprint: inference returned null');
}

// Filter and remove any pdf files
results.initialPhase.files = results.initialPhase.files.filter(f => !f.path.endsWith('.pdf'));

// // A hack
// if (results?.initialPhase) {
// results.initialPhase.lastPhase = false;
Expand Down
7 changes: 6 additions & 1 deletion worker/agents/planning/templateSelector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export async function selectTemplate({ env, query, availableTemplates, inference
templateCount: availableTemplates.length
});

availableTemplates = availableTemplates.filter(t => t.projectType !== 'presentation');
availableTemplates = availableTemplates.filter(t => t.projectType !== 'presentation' && !t.disabled && !t.name.includes('minimal'));
const validTemplateNames = availableTemplates.map(t => t.name);

const templateDescriptions = availableTemplates.map((t, index) =>
Expand Down Expand Up @@ -128,6 +128,11 @@ ENTROPY SEED: ${generateSecureToken(64)} - for unique results`;
maxTokens: 2000,
});

if (!selection) {
logger.error('Template selection returned no result after all retries');
throw new Error('Failed to select template: inference returned null');
}

logger.info(`AI template selection result: ${selection.selectedTemplateName || 'None'}, Reasoning: ${selection.reasoning}`);
return selection;

Expand Down
6 changes: 4 additions & 2 deletions worker/agents/prompts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,9 @@ Template Usage Instructions:
${template.description.usage}

<DO NOT TOUCH FILES>
These files are forbidden to be modified. Do not touch them under any circumstances.
These files are forbidden to be modified. Do not touch them under any circumstances. Doing so will break the application.
${(template.dontTouchFiles ?? []).join('\n')}
worker/core-utils.ts
</DO NOT TOUCH FILES>

<REDACTED FILES>
Expand Down Expand Up @@ -888,6 +889,7 @@ COMMON_PITFALLS: `<AVOID COMMON PITFALLS>
Applying this rule to your situation will fix both the type-check errors and the browser's runtime error.

# Never write image files! Never write jpeg, png, svg, etc files yourself! Always use some image url from the web.
**Do not recommend installing \`cloudflare:workers\` or \`cloudflare:durable-objects\` as dependencies, these are already installed in the project always.**

</AVOID COMMON PITFALLS>`,
COMMON_DEP_DOCUMENTATION: `<COMMON DEPENDENCY DOCUMENTATION>
Expand Down Expand Up @@ -1368,7 +1370,7 @@ export function issuesPromptFormatter(issues: IssueReport): string {

### 1. CRITICAL RUNTIME ERRORS (Fix First - Deployment Blockers)
**Error Count:** ${issues.runtimeErrors?.length || 0} runtime errors detected
**Contains Render Loops:** ${runtimeErrorsText.includes('Maximum update depth') || runtimeErrorsText.includes('Too many re-renders') ? 'YES - HIGHEST PRIORITY' : 'No'}
**Contains Render Loops:** ${runtimeErrorsText.includes('Maximum update depth') || runtimeErrorsText.includes('Too many re-renders') || runtimeErrorsText.includes('infinite loop') ? 'YES - HIGHEST PRIORITY' : 'No'}

${runtimeErrorsText || 'No runtime errors detected'}

Expand Down
2 changes: 2 additions & 0 deletions worker/services/sandbox/BaseSandboxService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ export abstract class BaseSandboxService {
language: t.language,
frameworks: t.frameworks || [],
description: t.description,
disabled: t.disabled ?? false,
projectType: t.projectType || 'app',
renderMode: t.renderMode,
slideDirectory: t.slideDirectory,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Critical Bug: Missing disabled field in template mapping

The TemplateInfoSchema in sandboxTypes.ts (line 116) requires a disabled field:

disabled: z.boolean(),

However, this mapping on lines 90-98 does not include the disabled field. This will cause Zod validation failures when the response is validated against TemplateInfoSchema.

Fix:

Suggested change
slideDirectory: t.slideDirectory,
name: t.name,
language: t.language,
frameworks: t.frameworks || [],
description: t.description,
disabled: t.disabled ?? false,
projectType: t.projectType || 'app',
renderMode: t.renderMode,
slideDirectory: t.slideDirectory,

Note: The PR correctly adds disabled to line 95 in the diff (in the template details section), but this section at line 90-98 was missed.

Expand Down Expand Up @@ -177,6 +178,7 @@ export abstract class BaseSandboxService {
selection: catalogInfo?.description.selection || '',
usage: catalogInfo?.description.usage || ''
},
disabled: catalogInfo?.disabled ?? false,
fileTree,
allFiles: filesMap,
language: catalogInfo?.language,
Expand Down
45 changes: 24 additions & 21 deletions worker/services/sandbox/sandboxTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,27 +21,6 @@ export const TemplateFileSchema = z.object({
})
export type TemplateFile = z.infer<typeof TemplateFileSchema>

// --- Template Details ---

export const TemplateDetailsSchema = z.object({
name: z.string(),
description: z.object({
selection: z.string(),
usage: z.string(),
}),
fileTree: FileTreeNodeSchema,
allFiles: z.record(z.string(), z.string()),
language: z.string().optional(),
deps: z.record(z.string(), z.string()),
frameworks: z.array(z.string()).optional(),
importantFiles: z.array(z.string()),
dontTouchFiles: z.array(z.string()),
redactedFiles: z.array(z.string()),
renderMode: z.enum(['sandbox', 'browser']).optional(),
slideDirectory: z.string().optional(),
})
export type TemplateDetails = z.infer<typeof TemplateDetailsSchema>

// ==========================================
// RUNTIME ERROR SCHEMAS
// ==========================================
Expand Down Expand Up @@ -98,6 +77,29 @@ export type CommandExecutionResult = z.infer<typeof CommandExecutionResultSchema

// --- API Request/Response Schemas ---


// --- Template Details ---

export const TemplateDetailsSchema = z.object({
name: z.string(),
description: z.object({
selection: z.string(),
usage: z.string(),
}),
fileTree: FileTreeNodeSchema,
allFiles: z.record(z.string(), z.string()),
language: z.string().optional(),
deps: z.record(z.string(), z.string()),
frameworks: z.array(z.string()).optional(),
importantFiles: z.array(z.string()),
dontTouchFiles: z.array(z.string()),
redactedFiles: z.array(z.string()),
renderMode: z.enum(['sandbox', 'browser']).optional(),
slideDirectory: z.string().optional(),
disabled: z.boolean(),
})
export type TemplateDetails = z.infer<typeof TemplateDetailsSchema>

// /templates (GET)

export const TemplateInfoSchema = z.object({
Expand All @@ -111,6 +113,7 @@ export const TemplateInfoSchema = z.object({
}),
renderMode: z.enum(['sandbox', 'browser']).optional(),
slideDirectory: z.string().optional(),
disabled: z.boolean(),
})
export type TemplateInfo = z.infer<typeof TemplateInfoSchema>

Expand Down
Loading