Each Vibn project now gets its OWN Coolify project named
vibn-{workspace-slug}-{project-slug}. All apps/databases/services
deployed for the project land inside that Coolify project, giving
us clean grouping, cascading delete, and per-project domain
namespaces.
Changes:
- New lib/projects.ts: ensureProjectCoolifyProject (idempotent
create/lookup), getProjectCoolifyUuid, getOwnedCoolifyProjectUuids
- /api/projects/create: pre-insert row, mint per-project Coolify
project, then complete the row with productData (preserves the
coolifyProjectUuid that was just set)
- apps.list (MCP): without projectId, aggregates across ALL
workspace-owned Coolify projects; with projectId, scopes to
that project's Coolify project. Returns coolifyProjectUuid
on each result so the AI knows where things live.
- apps.create (MCP): accepts projectId; auto-mints the Vibn
project's Coolify project on first deploy if missing
- apps_list/apps_create tool defs: projectId param surfaced
- System prompt: Project as first-class — planning + live as
facets of ONE thing, never as separate worlds. AI told to
always pass projectId on apps_create.
Stage 2 (next): set-aware ensureResourceInProject across all
single-resource MCP tools (apps.get/delete/exec/etc.) and
cascading delete via projects.delete.
Made-with: Cursor
Gemini's function_declarations validator requires:
- ARRAY types must declare items schema
- Free-form OBJECT (without properties) is rejected
Renamed free-object params to *Json string fields (envsJson, patchJson,
headersJson) and added server-side JSON.parse before forwarding to MCP.
Any param ending in "Json" is automatically unpacked into its real key
(e.g. envsJson string is parsed into envs object).
Made-with: Cursor
The Gemini REST API returns thoughtSignature as a sibling part field:
{ "functionCall": {...}, "thoughtSignature": "..." }
not inside functionCall. We were reading part.functionCall.thought_signature
(always undefined) and writing fc.thought_signature inside the functionCall
object (also wrong). Now correctly reads part.thoughtSignature and writes
part.thoughtSignature when building history.
Made-with: Cursor
Gemini 3.1 Pro thinking model requires thought_signature to be echoed
in functionResponse. SSE stream doesn't reliably include it in individual
chunks. Switch tool-calling rounds to non-streaming generateContent which
always returns the complete response with thought_signature present.
Made-with: Cursor
Thinking models attach a thought_signature to functionCall parts.
Must be echoed back in functionResponse or API returns 400.
Carry it through ToolCall -> ChatMessage -> toGeminiContents().
Made-with: Cursor
Gemini can now:
- Search GitHub for MIT-licensed OSS repos matching any description
- Read specific files from any public repo (READMEs, design systems,
package.json, docker-compose.yml, component libraries, etc.)
- Fetch any public URL for docs, APIs, or reference material
No hardcoded pipelines — Gemini decides how to use these tools
based on what the user asks for.
Made-with: Cursor
- Right-docked chat panel on all workspace pages ([workspace]/layout.tsx)
- Streaming SSE responses with Gemini 3.1 Pro preview via generativelanguage API
- Full tool-calling loop (up to 6 rounds): deploys apps, lists projects, registers
domains, fetches logs — all via existing MCP dispatcher
- Persistent conversation history: fs_chat_threads + fs_chat_messages tables (Postgres)
- Thread management: create, list, rename (auto-title from first message), delete
- Panel collapses to a tab; open state persisted to localStorage
- Read-only mode hint when no MCP token is present
- Graceful content margin shift when panel is open
Made-with: Cursor
- gemini-client.ts: replaces Vertex AI REST + service account auth with
direct generativelanguage.googleapis.com calls using GOOGLE_API_KEY.
Removes all Firebase credential setup code.
- summarize/route.ts: same migration, simplified to a single fetch call.
- No longer depends on gen-lang-client-0980079410 GCP project for AI calls.
Co-authored-by: Cursor <cursoragent@cursor.com>