feat(refactor): premium zed-style chat UI, collapsible reasoning, and comprehensive strict type sweeps

This commit is contained in:
2026-05-21 17:05:42 -07:00
parent 180aa9b311
commit 8049a7f1ab
35 changed files with 5144 additions and 5789 deletions

View File

@@ -5,6 +5,8 @@ exports.streamGeminiChat = streamGeminiChat;
const genai_1 = require("@google/genai");
const GEMINI_API_KEY = process.env.GOOGLE_API_KEY || "";
const GEMINI_MODEL = process.env.VIBN_CHAT_MODEL || "gemini-3.1-pro-preview";
// Add a clear visual log so we always know exactly which model is active in the terminal
console.log(`[GeminiChat Runner] Initialized — Model: ${GEMINI_MODEL}`);
if (!GEMINI_API_KEY) {
console.warn(`[GeminiChat] WARNING: GOOGLE_API_KEY is not set. Chat stream will fail with 403 Forbidden.`);
}
@@ -73,16 +75,13 @@ async function callGeminiChat(opts) {
if (opts.systemPrompt) {
config.systemInstruction = opts.systemPrompt;
}
if (opts.includeThoughts) {
config.thinkingConfig = { thinkingBudgetTokens: 1024 };
}
const fns = toGeminiFunctions(opts.tools ?? []);
if (fns)
config.tools = fns;
const response = await ai.models.generateContent({
model: GEMINI_MODEL,
contents: toGeminiContents(opts.messages),
config
config,
});
let text = "";
let thoughts = "";
@@ -108,7 +107,7 @@ async function callGeminiChat(opts) {
text,
thoughts,
toolCalls,
finishReason: response.candidates?.[0]?.finishReason
finishReason: response.candidates?.[0]?.finishReason,
};
}
catch (error) {
@@ -125,7 +124,6 @@ async function* streamGeminiChat(opts) {
const config = {
temperature: opts.temperature ?? 0.7,
maxOutputTokens: 8192,
thinkingConfig: { thinkingBudgetTokens: 1024 },
};
if (opts.systemPrompt) {
config.systemInstruction = opts.systemPrompt;
@@ -136,7 +134,7 @@ async function* streamGeminiChat(opts) {
const streamResult = await ai.models.generateContentStream({
model: GEMINI_MODEL,
contents: toGeminiContents(opts.messages),
config
config,
});
for await (const chunk of streamResult) {
const parts = chunk.candidates?.[0]?.content?.parts ?? [];

View File

@@ -18,6 +18,7 @@ Since you are running autonomously, you must take action immediately.
A turn ends when you have fully completed the task AND shipped the code.
- **For build/edit tasks:** The natural stopping point is starting the dev server via \`dev_server_start\`, verifying it works via \`browser_console\`, and calling the \`ship\` tool to deploy to production.
- **CRITICAL:** When you successfully finish a task from the Execution Plan, you MUST call \`plan_task_complete { taskId }\` to check it off the list before moving to the next task.
- If you run into a fatal error that you cannot fix after two attempts, write a brief summary of the blocker and stop.
# Hard rules — non-negotiable

View File

@@ -91,6 +91,34 @@ exports.VIBN_TOOL_DEFINITIONS = [
required: ["projectId"],
},
},
// ── Email ───────────────────────────────────────────────────────────────
{
name: "email_send",
description: "Send an email to a user. Use this to notify users of important events, send reports, or distribute content. You can optionally design the email using MJML syntax for beautifully responsive emails.",
parameters: {
type: "OBJECT",
properties: {
projectId: {
type: "STRING",
description: "The Vibn project ID sending the email.",
},
to: { type: "STRING", description: "The recipient's email address." },
subject: {
type: "STRING",
description: "The subject line of the email.",
},
text: {
type: "STRING",
description: "The plain-text fallback body of the email.",
},
mjml: {
type: "STRING",
description: "Optional MJML markup (<mjml><mj-body>...</mj-body></mjml>). If provided, it will be automatically compiled to HTML. Do NOT provide raw HTML, only use valid MJML syntax.",
},
},
required: ["projectId", "to", "subject", "text"],
},
},
// ── Market Research & GTM ───────────────────────────────────────────────
{
name: "market_categories_suggest",
@@ -1485,26 +1513,33 @@ After this returns, ALWAYS call apps_deploy { uuid } to regenerate the live Trae
},
},
{
name: "plan_decision_log",
description: "Log a decision the user has made. Call this PROACTIVELY whenever a non-trivial choice gets settled in conversation (database engine, auth approach, framework, pricing model, copy, branding…) — so it shows up in the Plan tab and you stop re-asking it next session. Don't ask permission; log it and move on.",
name: "plan_document_update",
description: "Overwrite the content of one of the Blueprint documents in the Plan tab. These documents define the specifications for the product. ALWAYS use this instead of `fs_write` when a user asks you to update the PRD, Spec, or Architecture plan.",
parameters: {
type: "OBJECT",
properties: {
projectId: { type: "STRING", description: "The Vibn project ID." },
title: {
projectId: { type: "STRING" },
docId: {
type: "STRING",
description: 'Short topic of the decision (e.g. "Database engine", "Auth provider").',
description: "The specific document to update.",
enum: [
"stories",
"acceptance",
"success",
"ui_design",
"tech_context",
"data_model",
"file_structure",
"tasks",
"checklist",
],
},
choice: {
content: {
type: "STRING",
description: 'What was chosen (e.g. "Postgres", "Stripe Checkout").',
},
why: {
type: "STRING",
description: "Optional 1-2 sentence reasoning. Strongly recommended.",
description: "The full markdown content for the document. This will completely overwrite the existing document.",
},
},
required: ["projectId", "title", "choice"],
required: ["projectId", "docId", "content"],
},
},
{

View File

@@ -0,0 +1,16 @@
const fs = require('fs');
const file = 'src/prompts/coder.ts';
let code = fs.readFileSync(file, 'utf8');
const oldStopping = `A turn ends when you have fully completed the task AND shipped the code.
- **For build/edit tasks:** The natural stopping point is starting the dev server via \\\`dev_server_start\\\`, verifying it works via \\\`browser_console\\\`, and calling the \\\`ship\\\` tool to deploy to production.
- If you run into a fatal error that you cannot fix after two attempts, write a brief summary of the blocker and stop.`;
const newStopping = `A turn ends when you have fully completed the task AND shipped the code.
- **For build/edit tasks:** The natural stopping point is starting the dev server via \\\`dev_server_start\\\`, verifying it works via \\\`browser_console\\\`, and calling the \\\`ship\\\` tool to deploy to production.
- **CRITICAL:** When you successfully finish a task from the Execution Plan, you MUST call \\\`plan_task_complete { taskId }\\\` to check it off the list before moving to the next task.
- If you run into a fatal error that you cannot fix after two attempts, write a brief summary of the blocker and stop.`;
code = code.replace(oldStopping, newStopping);
fs.writeFileSync(file, code);
console.log("Patched Delegate Prompt to enforce plan_task_complete");

View File

@@ -1,10 +1,15 @@
import { GoogleGenAI } from '@google/genai';
import { GoogleGenAI } from "@google/genai";
const GEMINI_API_KEY = process.env.GOOGLE_API_KEY || "";
const GEMINI_MODEL = process.env.VIBN_CHAT_MODEL || "gemini-3.1-pro-preview";
// Add a clear visual log so we always know exactly which model is active in the terminal
console.log(`[GeminiChat Runner] Initialized — Model: ${GEMINI_MODEL}`);
if (!GEMINI_API_KEY) {
console.warn(`[GeminiChat] WARNING: GOOGLE_API_KEY is not set. Chat stream will fail with 403 Forbidden.`);
console.warn(
`[GeminiChat] WARNING: GOOGLE_API_KEY is not set. Chat stream will fail with 403 Forbidden.`,
);
}
const ai = new GoogleGenAI({ apiKey: GEMINI_API_KEY });
@@ -108,12 +113,10 @@ export async function callGeminiChat(opts: {
temperature: opts.temperature ?? 0.7,
maxOutputTokens: 8192,
};
if (opts.systemPrompt) {
config.systemInstruction = opts.systemPrompt;
}
if (opts.systemPrompt) {
config.systemInstruction = opts.systemPrompt;
}
const fns = toGeminiFunctions(opts.tools ?? []);
if (fns) config.tools = fns;
@@ -121,13 +124,13 @@ export async function callGeminiChat(opts: {
const response = await ai.models.generateContent({
model: GEMINI_MODEL,
contents: toGeminiContents(opts.messages),
config
config,
});
let text = "";
let thoughts = "";
const toolCalls: ToolCall[] = [];
const parts = response.candidates?.[0]?.content?.parts ?? [];
for (const part of parts) {
@@ -139,19 +142,18 @@ export async function callGeminiChat(opts: {
toolCalls.push({
id: `tc-${Date.now()}-${Math.random().toString(36).slice(2)}`,
name: part.functionCall.name || "",
args: part.functionCall.args as Record<string, unknown> ?? {},
args: (part.functionCall.args as Record<string, unknown>) ?? {},
thoughtSignature: (part as any).thoughtSignature,
});
}
}
return {
text,
thoughts,
toolCalls,
finishReason: response.candidates?.[0]?.finishReason
return {
text,
thoughts,
toolCalls,
finishReason: response.candidates?.[0]?.finishReason,
};
} catch (error) {
return {
text: "",
@@ -172,11 +174,10 @@ export async function* streamGeminiChat(opts: {
const config: any = {
temperature: opts.temperature ?? 0.7,
maxOutputTokens: 8192,
};
if (opts.systemPrompt) {
config.systemInstruction = opts.systemPrompt;
config.systemInstruction = opts.systemPrompt;
}
const fns = toGeminiFunctions(opts.tools ?? []);
@@ -185,7 +186,7 @@ export async function* streamGeminiChat(opts: {
const streamResult = await ai.models.generateContentStream({
model: GEMINI_MODEL,
contents: toGeminiContents(opts.messages),
config
config,
});
for await (const chunk of streamResult) {
@@ -200,7 +201,6 @@ export async function* streamGeminiChat(opts: {
}
yield { type: "done" };
} catch (error) {
yield {
type: "error",

View File

@@ -18,6 +18,7 @@ Since you are running autonomously, you must take action immediately.
A turn ends when you have fully completed the task AND shipped the code.
- **For build/edit tasks:** The natural stopping point is starting the dev server via \`dev_server_start\`, verifying it works via \`browser_console\`, and calling the \`ship\` tool to deploy to production.
- **CRITICAL:** When you successfully finish a task from the Execution Plan, you MUST call \`plan_task_complete { taskId }\` to check it off the list before moving to the next task.
- If you run into a fatal error that you cannot fix after two attempts, write a brief summary of the blocker and stop.
# Hard rules — non-negotiable

View File

@@ -113,6 +113,37 @@ export const VIBN_TOOL_DEFINITIONS: ToolDefinition[] = [
},
},
// ── Email ───────────────────────────────────────────────────────────────
{
name: "email_send",
description:
"Send an email to a user. Use this to notify users of important events, send reports, or distribute content. You can optionally design the email using MJML syntax for beautifully responsive emails.",
parameters: {
type: "OBJECT",
properties: {
projectId: {
type: "STRING",
description: "The Vibn project ID sending the email.",
},
to: { type: "STRING", description: "The recipient's email address." },
subject: {
type: "STRING",
description: "The subject line of the email.",
},
text: {
type: "STRING",
description: "The plain-text fallback body of the email.",
},
mjml: {
type: "STRING",
description:
"Optional MJML markup (<mjml><mj-body>...</mj-body></mjml>). If provided, it will be automatically compiled to HTML. Do NOT provide raw HTML, only use valid MJML syntax.",
},
},
required: ["projectId", "to", "subject", "text"],
},
},
// ── Market Research & GTM ───────────────────────────────────────────────
{
@@ -1675,28 +1706,35 @@ After this returns, ALWAYS call apps_deploy { uuid } to regenerate the live Trae
},
},
{
name: "plan_decision_log",
name: "plan_document_update",
description:
"Log a decision the user has made. Call this PROACTIVELY whenever a non-trivial choice gets settled in conversation (database engine, auth approach, framework, pricing model, copy, branding…) — so it shows up in the Plan tab and you stop re-asking it next session. Don't ask permission; log it and move on.",
"Overwrite the content of one of the Blueprint documents in the Plan tab. These documents define the specifications for the product. ALWAYS use this instead of `fs_write` when a user asks you to update the PRD, Spec, or Architecture plan.",
parameters: {
type: "OBJECT",
properties: {
projectId: { type: "STRING", description: "The Vibn project ID." },
title: {
projectId: { type: "STRING" },
docId: {
type: "STRING",
description: "The specific document to update.",
enum: [
"stories",
"acceptance",
"success",
"ui_design",
"tech_context",
"data_model",
"file_structure",
"tasks",
"checklist",
],
},
content: {
type: "STRING",
description:
'Short topic of the decision (e.g. "Database engine", "Auth provider").',
},
choice: {
type: "STRING",
description: 'What was chosen (e.g. "Postgres", "Stripe Checkout").',
},
why: {
type: "STRING",
description: "Optional 1-2 sentence reasoning. Strongly recommended.",
"The full markdown content for the document. This will completely overwrite the existing document.",
},
},
required: ["projectId", "title", "choice"],
required: ["projectId", "docId", "content"],
},
},
{