feat(refactor): premium zed-style chat UI, collapsible reasoning, and comprehensive strict type sweeps
This commit is contained in:
12
vibn-agent-runner/dist/llm/gemini-chat.js
vendored
12
vibn-agent-runner/dist/llm/gemini-chat.js
vendored
@@ -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 ?? [];
|
||||
|
||||
1
vibn-agent-runner/dist/prompts/coder.js
vendored
1
vibn-agent-runner/dist/prompts/coder.js
vendored
@@ -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
|
||||
|
||||
59
vibn-agent-runner/dist/tools/vibn-tools.js
vendored
59
vibn-agent-runner/dist/tools/vibn-tools.js
vendored
@@ -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"],
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
16
vibn-agent-runner/patch_delegate_prompt.js
Normal file
16
vibn-agent-runner/patch_delegate_prompt.js
Normal 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");
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"],
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user