fix: Robust patch generation and safety timeouts
- Condensed Canvas Isolation Architecture to streamline AI focus - Added robust JSON extraction (brace-finding) to handle conversational AI output - Implemented 90s safety timeout for patch generation to prevent infinite 'Generating' state - Throttled status updates to improve UI performance during streaming
This commit is contained in:
@@ -24,34 +24,11 @@ export const setActiveModel = (model: string): void => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const CANVAS_ISOLATION_ARCHITECTURE = `
|
export const CANVAS_ISOLATION_ARCHITECTURE = `
|
||||||
# CANVAS ISOLATION ARCHITECTURE
|
### CANVAS ISOLATION ARCHITECTURE
|
||||||
|
1. **Base Canvas**: Existing code is immutable. Avoid touching unrelated blocks.
|
||||||
## 1. LAYERED CANVAS SYSTEM
|
2. **Isolation Chamber**: Extract ONLY specific sections mentioned.
|
||||||
- **Base Canvas**: The complete, immutable original codebase
|
3. **Precision Merge**: Merge back using surgical anchors. 100% preservation.
|
||||||
- **Change Canvas**: Isolated layer containing ONLY the requested modifications
|
4. **Boundary Mapping**: Identify exact coordinates. No cascading changes.
|
||||||
- **Merge Canvas**: The precise intersection where changes are applied
|
|
||||||
- **Validation Canvas**: Verification layer ensuring no unintended modifications
|
|
||||||
|
|
||||||
## 2. SURGICAL CHANGE EXECUTION ENGINE
|
|
||||||
|
|
||||||
**Phase 1: Boundary Mapping**
|
|
||||||
- Parse user request with extreme precision. Create coordinates of exact change locations.
|
|
||||||
- Establish quarantine zones around unrelated code.
|
|
||||||
|
|
||||||
**Phase 2: Isolation Chamber**
|
|
||||||
- Extract ONLY the specific code sections that need modification.
|
|
||||||
- Maintain isolation. Apply atomic operations that cannot affect surrounding code.
|
|
||||||
|
|
||||||
**Phase 3: Contextual Merge**
|
|
||||||
- Merge changes back using surgical precision (Surgical Extraction -> Controlled Merge).
|
|
||||||
- Verify zero impact on unrelated code.
|
|
||||||
|
|
||||||
## 3. TECHNICAL ENFORCEMENT
|
|
||||||
- **Code Fencing**: Mark sections as Isolation Chambers.
|
|
||||||
- **Atomic Constraints**: No cascading modifications beyond immediate scope.
|
|
||||||
- **Preservation Rate**: 100% of unrelated code must remain unchanged.
|
|
||||||
|
|
||||||
Your expertise is measured by your ability to make surgical changes while preserving 100% of the user's existing work.
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
// --- GEMINI 3 PRO / VIBE CODING TEMPLATE ---
|
// --- GEMINI 3 PRO / VIBE CODING TEMPLATE ---
|
||||||
@@ -60,58 +37,58 @@ You are an expert Frontend Engineer.
|
|||||||
Your task is to implement the User's Plan into a SINGLE, HIGH-FIDELITY HTML FILE using the REQUESTED FRAMEWORK.
|
Your task is to implement the User's Plan into a SINGLE, HIGH-FIDELITY HTML FILE using the REQUESTED FRAMEWORK.
|
||||||
|
|
||||||
### TECHNICAL REQUIREMENTS:
|
### TECHNICAL REQUIREMENTS:
|
||||||
1. **Single File**: Everything (HTML, CSS, JS) must be in one file.
|
1. ** Single File **: Everything(HTML, CSS, JS) must be in one file.
|
||||||
2. **CDNs Only**: Use reputable CDNs (cdnjs, unpkg, esm.sh) for libraries.
|
2. ** CDNs Only **: Use reputable CDNs(cdnjs, unpkg, esm.sh) for libraries.
|
||||||
3. **React**: If React is requested, use React 18 + ReactDOM 18 + Babel Standalone (for JSX).
|
3. ** React **: If React is requested, use React 18 + ReactDOM 18 + Babel Standalone(for JSX).
|
||||||
- <script src="https://unpkg.com/react@18/umd/react.development.js"></script>
|
- <script src="https://unpkg.com/react@18/umd/react.development.js" > </script>
|
||||||
- <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
|
- <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" > </script>
|
||||||
- <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
|
- <script src="https://unpkg.com/@babel/standalone/babel.min.js" > </script>
|
||||||
- <script type="text/babel"> ... your code ... </script>
|
- <script type="text/babel" > ... your code ... </script>
|
||||||
4. **Vue**: If Vue is requested, use Vue 3 global build.
|
4. ** Vue **: If Vue is requested, use Vue 3 global build.
|
||||||
- <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
|
- <script src="https://unpkg.com/vue@3/dist/vue.global.js" > </script>
|
||||||
5. **Aesthetics**: Unless the user specified a specific CSS framework (like Bootstrap), DEFAULT to **TailwindCSS** for styling to maintain "Vibe" quality.
|
5. ** Aesthetics **: Unless the user specified a specific CSS framework(like Bootstrap), DEFAULT to ** TailwindCSS ** for styling to maintain "Vibe" quality.
|
||||||
- <script src="https://cdn.tailwindcss.com"></script>
|
- <script src="https://cdn.tailwindcss.com" > </script>
|
||||||
|
|
||||||
### CRITICAL: CLIENT-SIDE ONLY
|
### CRITICAL: CLIENT - SIDE ONLY
|
||||||
- **NO SERVER-SIDE TEMPLATES**: DO NOT use Jinja2, Liquid, PHP, etc.
|
- ** NO SERVER - SIDE TEMPLATES **: DO NOT use Jinja2, Liquid, PHP, etc.
|
||||||
- **NO NODE.JS SPECIFIC**: No 'require', no 'process.env', no 'npm install'.
|
- ** NO NODE.JS SPECIFIC **: No 'require', no 'process.env', no 'npm install'.
|
||||||
- **Mock Data**: Create dummy data arrays inside the script.
|
- ** Mock Data **: Create dummy data arrays inside the script.
|
||||||
|
|
||||||
### OUTPUT FORMAT:
|
### OUTPUT FORMAT:
|
||||||
- RETURN **ONLY** THE RAW HTML CODE.
|
- RETURN ** ONLY ** THE RAW HTML CODE.
|
||||||
- DO NOT USE MARKDOWN BLOCK.
|
- DO NOT USE MARKDOWN BLOCK.
|
||||||
- DO NOT include or display the plan/instructions in the UI. The plan is input-only.
|
- DO NOT include or display the plan / instructions in the UI.The plan is input - only.
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const MODERN_TEMPLATE_PROMPT = `
|
export const MODERN_TEMPLATE_PROMPT = `
|
||||||
You are an elite Frontend Architect simulating the reasoning and coding quality of Google Gemini 1.5 Pro.
|
You are an elite Frontend Architect simulating the reasoning and coding quality of Google Gemini 1.5 Pro.
|
||||||
|
|
||||||
### DESIGN & TECH STACK (NON-NEGOTIABLE)
|
### DESIGN & TECH STACK(NON - NEGOTIABLE)
|
||||||
1. **Framework**: Vanilla HTML5 + JavaScript (ES6+). NO React/Vue/Angular unless explicitly requested.
|
1. ** Framework **: Vanilla HTML5 + JavaScript(ES6 +).NO React / Vue / Angular unless explicitly requested.
|
||||||
2. **Styling**: Tailwind CSS (via CDN). Use modern utility classes (flex, grid, glassmorphism, gradients). Use slate/zinc/neutral for structural greys and vibrant indigo/violet/blue for accents.
|
2. ** Styling **: Tailwind CSS(via CDN).Use modern utility classes(flex, grid, glassmorphism, gradients).Use slate / zinc / neutral for structural greys and vibrant indigo / violet / blue for accents.
|
||||||
3. **Icons**: FontAwesome (via CDN).
|
3. ** Icons **: FontAwesome(via CDN).
|
||||||
4. **Font**: 'Inter' or 'JetBrains Mono' via Google Fonts.
|
4. ** Font **: 'Inter' or 'JetBrains Mono' via Google Fonts.
|
||||||
|
|
||||||
### ATOMIC OUTPUT PROTOCOL
|
### ATOMIC OUTPUT PROTOCOL
|
||||||
1. You are strictly forbidden from 'chatting' your code.
|
1. You are strictly forbidden from 'chatting' your code.
|
||||||
2. You must output the code inside a standard XML block: <artifact_payload>.
|
2. You must output the code inside a standard XML block: <artifact_payload>.
|
||||||
3. Do not output internal tool logs (like <<goose) inside this block.
|
3. Do not output internal tool logs(like << goose) inside this block.
|
||||||
|
|
||||||
Example Output:
|
Example Output:
|
||||||
<artifact_payload file="index.html">
|
<artifact_payload file="index.html" >
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html >
|
||||||
<html lang="en">
|
<html lang="en" >
|
||||||
...
|
...
|
||||||
</html>
|
</html>
|
||||||
</artifact_payload>
|
</artifact_payload>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export const MockComputerDriver: GooseUltraComputerDriver = {
|
export const MockComputerDriver: GooseUltraComputerDriver = {
|
||||||
checkArmed: () => true, // In real app, check env var or system flag
|
checkArmed: () => true, // In real app, check env var or system flag
|
||||||
runAction: async (action, params) => {
|
runAction: async (action, params) => {
|
||||||
console.log(`[Desktop] ${action}`, params);
|
console.log(`[Desktop] ${action} `, params);
|
||||||
await sleep(800);
|
await sleep(800);
|
||||||
return { status: 'success', screenshot: 'https://picsum.photos/800/600' };
|
return { status: 'success', screenshot: 'https://picsum.photos/800/600' };
|
||||||
}
|
}
|
||||||
@@ -119,11 +96,11 @@ export const MockComputerDriver: GooseUltraComputerDriver = {
|
|||||||
|
|
||||||
export const MockBrowserDriver: GooseUltraBrowserDriver = {
|
export const MockBrowserDriver: GooseUltraBrowserDriver = {
|
||||||
navigate: async (url) => {
|
navigate: async (url) => {
|
||||||
console.log(`[Browser] Navigate: ${url}`);
|
console.log(`[Browser] Navigate: ${url} `);
|
||||||
await sleep(1000);
|
await sleep(1000);
|
||||||
},
|
},
|
||||||
assert: async (selector) => {
|
assert: async (selector) => {
|
||||||
console.log(`[Browser] Assert: ${selector}`);
|
console.log(`[Browser] Assert: ${selector} `);
|
||||||
await sleep(500);
|
await sleep(500);
|
||||||
return true; // Always pass in mock
|
return true; // Always pass in mock
|
||||||
}
|
}
|
||||||
@@ -136,8 +113,8 @@ export const MockServerDriver: GooseUltraServerDriver = {
|
|||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
runCommand: async (cmd, dryRun) => {
|
runCommand: async (cmd, dryRun) => {
|
||||||
if (dryRun) return `[DryRun] Would execute: ${cmd}`;
|
if (dryRun) return `[DryRun] Would execute: ${cmd} `;
|
||||||
console.log(`[Server] Executing: ${cmd}`);
|
console.log(`[Server] Executing: ${cmd} `);
|
||||||
await sleep(1000);
|
await sleep(1000);
|
||||||
return `Success: ${cmd} executed.\nLogs: [System OK]`;
|
return `Success: ${cmd} executed.\nLogs: [System OK]`;
|
||||||
}
|
}
|
||||||
@@ -165,7 +142,7 @@ Proposed Plan: "${plan.substring(0, 500)}..."
|
|||||||
Analyze if the Plan matches the User Request category.
|
Analyze if the Plan matches the User Request category.
|
||||||
- If User asked for a Game and Plan is a Dashboard -> FAIL.
|
- If User asked for a Game and Plan is a Dashboard -> FAIL.
|
||||||
- If User asked for a Portfolio and Plan is a Chatbot -> FAIL.
|
- If User asked for a Portfolio and Plan is a Chatbot -> FAIL.
|
||||||
- If they vaguely match (e.g., "App" and "Dashboard"), PASS.
|
- If they vaguely match(e.g., "App" and "Dashboard"), PASS.
|
||||||
|
|
||||||
OUTPUT ONLY JSON:
|
OUTPUT ONLY JSON:
|
||||||
{
|
{
|
||||||
@@ -173,14 +150,14 @@ OUTPUT ONLY JSON:
|
|||||||
"why": "string reason",
|
"why": "string reason",
|
||||||
"detectedAppType": "string",
|
"detectedAppType": "string",
|
||||||
"requestType": "string"
|
"requestType": "string"
|
||||||
}`;
|
} `;
|
||||||
|
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
let buf = '';
|
let buf = '';
|
||||||
const onChunk = (c: string) => buf += c;
|
const onChunk = (c: string) => buf += c;
|
||||||
const onComplete = (c: string) => {
|
const onComplete = (c: string) => {
|
||||||
electron.removeChatListeners();
|
electron.removeChatListeners();
|
||||||
const final = (c && c.length > buf.length ? c : buf).replace(/```json/g, '').replace(/```/g, '').trim();
|
const final = (c && c.length > buf.length ? c : buf).replace(/```json/gi, '').replace(/```/g, '').trim();
|
||||||
try {
|
try {
|
||||||
const res = JSON.parse(final);
|
const res = JSON.parse(final);
|
||||||
resolve(res);
|
resolve(res);
|
||||||
@@ -725,15 +702,30 @@ If the request requires a FULL REDESIGN (changing >50% of layout or colors), set
|
|||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let fullResponse = '';
|
let fullResponse = '';
|
||||||
|
const timeout = setTimeout(() => {
|
||||||
|
cleanup();
|
||||||
|
reject(new Error("Patch Generation Timeout (90s). The model took too long to respond."));
|
||||||
|
}, 90000);
|
||||||
|
|
||||||
const onChunkHandler = (c: string) => {
|
const onChunkHandler = (c: string) => {
|
||||||
fullResponse += c;
|
fullResponse += c;
|
||||||
if (onChunk) onChunk(JSON.stringify({ status: "Generating Patch...", size: fullResponse.length }));
|
// Throttled status updates
|
||||||
|
if (onChunk && fullResponse.length % 50 === 0) {
|
||||||
|
onChunk(JSON.stringify({ status: "Generating Patch...", size: fullResponse.length }));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onCompleteHandler = async (c: string) => {
|
const onCompleteHandler = async (c: string) => {
|
||||||
|
clearTimeout(timeout);
|
||||||
cleanup();
|
cleanup();
|
||||||
const rawJson = (c.length > fullResponse.length ? c : fullResponse).trim();
|
let rawJson = (c.length > fullResponse.length ? c : fullResponse).trim();
|
||||||
|
|
||||||
|
// Robust extraction: find first { and last }
|
||||||
|
const firstBrace = rawJson.indexOf('{');
|
||||||
|
const lastBrace = rawJson.lastIndexOf('}');
|
||||||
|
if (firstBrace !== -1 && lastBrace > firstBrace) {
|
||||||
|
rawJson = rawJson.substring(firstBrace, lastBrace + 1);
|
||||||
|
}
|
||||||
|
|
||||||
// Parse JSON
|
// Parse JSON
|
||||||
let patchPlan: PatchPlan | null = null;
|
let patchPlan: PatchPlan | null = null;
|
||||||
@@ -741,13 +733,11 @@ If the request requires a FULL REDESIGN (changing >50% of layout or colors), set
|
|||||||
const jsonStr = rawJson.replace(/```json/gi, '').replace(/```/g, '').trim();
|
const jsonStr = rawJson.replace(/```json/gi, '').replace(/```/g, '').trim();
|
||||||
patchPlan = JSON.parse(jsonStr);
|
patchPlan = JSON.parse(jsonStr);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Failed to parse patch JSON", e);
|
console.error("Failed to parse patch JSON", e, rawJson.substring(0, 300));
|
||||||
// Retry or Fail?
|
|
||||||
if (retryCount < 1) {
|
if (retryCount < 1) {
|
||||||
console.log("Retrying patch generation...");
|
|
||||||
return resolve(applyPlanToExistingHtml(plan, currentHtml, onChunk, retryCount + 1, projectId));
|
return resolve(applyPlanToExistingHtml(plan, currentHtml, onChunk, retryCount + 1, projectId));
|
||||||
}
|
}
|
||||||
reject(new Error("Failed to parse patch plan"));
|
reject(new Error("Failed to parse patch plan: " + (e as Error).message));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user