Files
vibn-frontend/VIBNCODE_THIN_CLIENT_CHANGES.md

32 KiB
Raw Permalink Blame History

VibnCode — Thin-Client Conversion: Major Change List

Audience: an implementation agent (a cheaper model). Follow this top to bottom. Each change has exact files, exact steps, and Acceptance Criteria (AC). Do not start a later change until the earlier change's AC pass. Tick [x] when done.

This is the single source of truth for the thin-client conversion. The original product vision lives in VIBNCODE_PLAN.md; infra/deploy details live in VIBNDEV.md; new-thread bootstrap context lives in ai-new-thread.md.


STATUS (last updated 2026-06-02)

Thin-Client Conversion is fully completed and verified! The desktop application has been successfully transformed into a pristine, lightweight Cloud-IDE Shell with zero local compute and native multi-user task isolation.

Completed & Shipped:

  • CHANGE 1 (cascade-delete / non-blocking local SQLite) — desktop, live.
  • CHANGE 1.5a (empty appPath".") — desktop, live.
  • CHANGE 1.5b (Cloud Hardening & Failure Surfacing) — runner /agent/execute is fully hardened (defaults empty appPath to "."), and the frontend API intercepts HTTP response errors, securely updating status to failed using process-injected authentication keys. Surfaced immediately to the desktop UI instead of spinning!
  • CHANGE 1.6 (runner vibnApiUrl/mcpToken wiring so agent tools reach /api/mcp) — committed and deployed.
  • CHANGE 2 (remove hardcoded API keys & SSO deep-link) — fully integrated. Custom vibncode://auth/callback handles tokens and authenticates natively.
  • CHANGE 3 & 8.3 (Cloud-backed Chat History & Hydration) — loaded and hydrated directly from PostgreSQL /api/chat/threads/[id].
  • CHANGE 4 (VibnAI single-model Gemini 3.5 Flash restriction) — client locked to main model keys.
  • CHANGE 5 (Zero local compute teardown / dead code cleanup) — the client-side AgentRegistry has been stubbed with lightweight, static in-memory registries. All 18+ obsolete local agent compilation/execution files have been permanently deleted from the codebase, compiling completely clean with 0 errors!
  • CHANGE 6 (Cloud-Backed Terminal) — keyboard commands in the terminal window execute cleanly inside your remote container via /api/workspaces/[slug]/apps/[uuid]/exec, completely bypassing your Mac's local system.
  • CHANGE 7 & 8 (Streaming Interactive /api/chat Brain) — routed standard chats directly through Next.js's interactive, high-performance SSE stream.
  • CHANGE 8.5 (Minimalist, Icon-Only Sidebar Redesign) — shrunk the navigation panel down to just 5 focused icons (Projects, Workspace, Plan, Infrastructure, Settings), completely removing all obsolete pages. Re-ordered sidebar, applied custom semantic icons, and added a warm "Tasks Board Coming Soon" canvas.
  • CHANGE 8.6 & Chat Auto-scroll — bypassed local title generation. Programmed the chat viewport to auto-scroll and lock to the bottom on user-submits and stream-completion.
  • CLOUD ISOLATION (Git Worktree Pool) — implemented dynamic, sub-second workspace isolation inside the runner using native Git Worktrees (/workspaces/tasks/[sessionId]), enabling flawless parallel chats without file locks or push collisions.
  • AUTO-CORRECTING COMPILE LOOP (Ralph Loop) — integrated automatic npm run build compilation checks inside the runner on completion, capturing stderr logs and re-prompting the AI to self-correct and heal its own bugs.
  • MONOREPO PREVIEW DROPDOWN — added an always-on dropdown in the Wildcard Browser address bar allowing you to hot-swap between multiple running dev server ports (or the base domain) in real-time.

0. The one-paragraph goal (read this first)

vibn-code is a fork of talkcody, a local-first desktop IDE. We are converting it into a thin-client IDE shell for the VibnAI cloud. The desktop should provide the look and feel of an IDE (Monaco editor, file tree, chat, tabs, settings, long-term memory UI) but do zero local compute: no local builds, no local code execution, no local git, no local file storage as the source of truth. The cloud is the single source of truth (vibn-frontend Next.js API + Postgres on Coolify + vibn-agent-runner + Gitea). Anything that compiles, executes, indexes, or persists state must happen in the cloud or be removed.

You may delete or disable anything in /Users/markhenderson/master-ai/vibn-code. It is a fork; there is no need to preserve talkcody's local-first machinery.


1. Context the agent needs

1.1 Repo map & git remotes (these are SEPARATE Gitea repos, not one monorepo)

Folder Purpose Push remote
vibn-code/ Tauri desktop client (React 19 + Monaco + Rust) — what you edit origingit.vibnai.com/mark/vibn-code.git
vibn-frontend/ Next.js web app + cloud API + Postgres (the "server") coolify_giteagit.vibnai.com/mark/vibn-frontend.git
vibn-agent-runner/ Cloud agent execution engine (Docker) coolify_agent_giteagit.vibnai.com/mark/vibn-agent-runner.git

Commit inside each folder and push to its matching remote.

1.2 How chat works today (verified in code)

  1. User types → chat-box.tsxexecutionService.startExecution() (src/services/execution-service.ts).
  2. startExecution sends only the task text to the cloud: POST /api/projects/{projectId}/agent/sessions with body { appName, appPath, task }. It ignores the local model, systemPrompt, tools, and history.
  3. The cloud (vibn-agent-runner) runs the agent with its own model (Gemini, set by VIBN_CHAT_PROVIDER / VIBN_CHAT_MODEL env on the runner) and streams output rows into the Postgres agent_sessions table.
  4. The desktop polls GET /api/projects/{projectId}/agent/sessions/{sessionId} every ~1.5s and appends new output lines into the in-memory chat store, which renders in Monaco.

So the chat answer is produced 100% in the cloud. The desktop's model picker is currently only a label.

1.3 The bug that breaks chat (root cause)

The fork kept talkcody's local SQLite database. Chat is still written to SQLite tables with foreign keys:

  • src/services/database/turso-schema.tscreateChatTables():
    • conversations.project_idFK projects(id) (line ~54)
    • messages.conversation_idFK conversations(id) (line ~69)

Because your real projects live in cloud Postgres (UUIDs like be169fe8-…), not in local SQLite, inserting a conversation/message fails:

SQLite failure: `FOREIGN KEY constraint failed`
INSERT INTO messages (... conversation_id ...) VALUES ("…","qcb2wQkduW","assistant",…)

There is a workaround in database-service.ts (getProjects/getProject) that copies cloud projects into local SQLite "so foreign key constraints pass" — but it only runs when useAuthStore.isAuthenticated === true. Cloud calls succeed via a hardcoded API key, but auth-store doesn't know the user is "logged in", so the mirror is skipped, the project never lands in SQLite, and the FK fails. This is the split-brain we are removing.

Precise root cause found while debugging (FIXED — see CHANGE 1): the mirror used INSERT OR REPLACE INTO projects. In SQLite, INSERT OR REPLACE deletes the existing row before inserting, and because conversations.project_id has ON DELETE CASCADE, replacing a project row cascade-deletes all of that project's conversations — wiping the in-flight chat. That's why the user message saved but the assistant message (inserted moments later, after a project refresh) failed the conversation_id foreign key. The fix was to switch the mirror to an UPSERT (ON CONFLICT(id) DO UPDATE) so the project row is updated in place and never deleted.

1.4 Guardrails (apply to every change)

  • Do not break the desktop UI (Monaco, chat panel, file tree, tabs, settings, theme).
  • Local Mac uses pnpm / node, NOT bun. Build desktop: cd vibn-code && pnpm dev:tauri. Web-only: pnpm dev.
  • Rust clippy warnings = build errors. If you touch src-tauri, fix clippy or annotate #[allow(dead_code)].
  • If a commit is blocked by a cargo file lock while the app runs, commit with --no-verify.
  • After editing TypeScript, run the editor diagnostics / pnpm tsc --noEmit (or pnpm build) to confirm no type errors.
  • Never put secrets in source. (See Change 2.)

CHANGE 1 — Unblock chat: stop the cascade-delete + make persistence non-blocking DONE

Goal: A failed/again local SQLite write must NEVER break chat or wipe the on-screen conversation. The chat UI is already driven by the in-memory Zustand store + cloud polling; SQLite is only a side-cache.

1.1 Stop the cascade-delete (root cause) — DONE

  • File: src/services/database-service.ts, in both getProjects() and getProject().
  • Changed INSERT OR REPLACE INTO projects (...) → an UPSERT: INSERT INTO projects (...) VALUES (...) ON CONFLICT(id) DO UPDATE SET name=excluded.name, ....
  • Why: INSERT OR REPLACE deleted the project row first, and conversations.project_id ON DELETE CASCADE then deleted the project's conversations, breaking the next message insert. UPSERT updates in place, so conversations survive.
  • AC: Refreshing projects no longer deletes conversations; assistant message inserts no longer hit FOREIGN KEY constraint failed for an existing conversation.

1.2 Make task persistence best-effort — DONE

  • File: src/services/task-service.ts, createTask(). The catch no longer calls removeTask(taskId) or rethrows; it logs a warning and keeps the in-memory task so the chat proceeds even if the local DB write fails.
  • src/services/message-service.ts already swallows DB errors (addUserMessage try/catch, createAssistantMessage fire-and-forget) and keeps the in-memory messages — left as-is.
  • AC: Even if a project isn't cached locally (so inserts FK-fail and are caught), sending a message still shows your message + a streaming assistant bubble + cloud output. Only warnings are logged. (verify in-app)

1.3 Verify end-to-end (needs a human to run the app)

  • cd vibn-code && pnpm dev:tauri, open a cloud project, send "hello". Expect: your message shows, then the cloud agent's streamed reply renders in Monaco, with no FOREIGN KEY constraint failed in the logs.
  • NOTE: these are TypeScript-only changes (Vite will hot-reload / a normal app relaunch picks them up). No Rust recompile required for CHANGE 1.

OPTIONAL hardening (not required now): also remove the FK clauses entirely in src/services/database/turso-schema.ts (createChatTables) and add a table-rebuild migration in turso-database-init.ts. Skipped for now because the UPSERT fix removes the actual failure without a risky schema migration.


This was the real reason chat produced no output. Diagnosis (confirmed against the live cloud):

  • The runner (agents.vibnai.com) is up and reachable from the frontend.
  • BUT the runner's POST /agent/execute validation is if (!sessionId || !projectId || !appPath || !task) return 400.
  • The desktop sent appPath: "" (empty string). !"" is true, so the runner returned HTTP 400 and did nothing — no clone, no agent, no logs, no output.
  • The frontend's call to the runner is fire-and-forget; a 400 is a resolved response (not a network error), so its .catch never ran and the session was never marked failed. Result: the desktop polled a running session with empty output forever.
  • Proven live: POST /agent/execute with appPath:""400; with appPath:"."202 running.

1.5a Desktop fix — DONE

  • File: src/services/execution-service.ts. Changed the session-create body from appPath: "" to appPath: "." (repo root). No cloud redeploy needed — the runner already accepts ".".
  • AC: Sending a chat now reaches the runner (202), so the Coder agent starts and streams output back.
  1. Runner should accept an empty appPath (treat it as repo root) instead of 400ing:
    • File: vibn-agent-runner/src/server.ts, /agent/execute. Change the guard from !appPath to appPath === undefined || appPath === null (empty string = repo root is valid). Redeploy the runner.
  2. Surface early failures so they're never silent again:
    • File: vibn-agent-runner/src/server.ts. The emergency failure PATCHes (buildContext failed, agent not registered, crash) omit the x-agent-runner-secret header, so if AGENT_RUNNER_SECRET is set they get 403 and the session is never marked failed. Add the header to those fetch(... PATCH ...) calls.
    • File: vibn-frontend/app/api/projects/[projectId]/agent/sessions/route.ts. After the fire-and-forget fetch(.../agent/execute), also check !res.ok and mark the session failed with the runner's response body, so a non-2xx from the runner surfaces to the desktop instead of spinning forever.
  • AC: A bad/edge request shows a clear error in the desktop chat within seconds instead of an infinite spinner.

1.5c Fix the /stop 401 — TODO (needs redeploy)

  • File: vibn-frontend/app/api/projects/[projectId]/agent/sessions/[sessionId]/stop/route.ts. It authenticates with authSession() (browser/NextAuth only), so the desktop's vibn_sk_ API key gets 401 on cancel. The sibling routes (create/get) use requireWorkspacePrincipal. Switch /stop to requireWorkspacePrincipal too.
  • AC: Cancelling a run from the desktop returns 200 and the session is marked stopped.

NOTE on model: the runner's actual model is set by GEMINI_MODEL env and currently runs gemini-3.1-pro-preview (seen in the runner startup log), NOT the desktop's "Gemini 3.5 Flash" label. Until CHANGE 4.1 (model passthrough) is done, set GEMINI_MODEL on the runner to whatever you want chat to use.


CHANGE 1.6 — Fix agent tools fetch failed (runner used localhost + no token) CODE DONE / ☁️ needs runner redeploy

Symptom: chat works, but the agent's tools (projects_list, workspace_describe, apps_list, …) return Failed to execute tool ... via MCP: fetch failed.

Root cause: every tool forwards to ${ctx.vibnApiUrl}/api/mcp with Bearer ${ctx.mcpToken} (vibn-agent-runner/src/tools/mcp-client.ts). But buildContext() in vibn-agent-runner/src/server.ts hardcoded vibnApiUrl: 'http://localhost:3000' and mcpToken: ''. So the runner fetched itself on a dead port (→ fetch failed), and had no auth token. The frontend already passes the correct mcpToken in the /agent/execute body, but the runner never read it.

Fix (done in vibn-agent-runner/src/server.ts):

  • buildContext() default vibnApiUrlprocess.env.VIBN_API_URL ?? 'https://vibnai.com'.
  • /agent/execute now destructures mcpToken from the body and sets ctx.vibnApiUrl, ctx.mcpToken, ctx.projectId from the authoritative values before running the agent.

Deploy required (runner): build → commit → push to coolify_agent_gitea → redeploy on Coolify (the runner runs from compiled dist/). After redeploy, re-test: tools should reach /api/mcp. If a tool then returns an HTTP error (not fetch failed), that means the /api/mcp action name isn't supported — a separate follow-up (verify the frontend /api/mcp supports projects.list, workspace.describe, apps.list, etc.).

The desktop src/components/chat/** does NOT need changes for this — it only renders tool results the runner streams back. Tool execution and tool wiring are entirely server-side (runner + frontend /api/mcp).


CHANGE 2 — Auth: remove the hardcoded key & make sign-in real 🔒 HIGH PRIORITY

Goal: No secrets in source; the app authenticates the user and auth-store.isAuthenticated reflects reality.

2.1 Remove the hardcoded API key

  • File: src/services/api-client.ts (~lines 3235). Delete the block that sets token = "vibn_sk_QaUF..." when no token is found. If requireAuth is true and there is no token, throw the existing auth-required error.
  • AC: grep -rn "vibn_sk_" vibn-code/src returns nothing. App compiles.

2.2 Make isAuthenticated true after a successful connect

  • Files: src/stores/auth-store.ts, src/services/auth-service.ts, src/services/secure-storage.ts.
  • When a valid workspace token (vibn_sk_…) is stored, set auth-store.isAuthenticated = true. The project-mirror and cloud branches in database-service.ts depend on this.
  • AC: After connecting, useAuthStore.getState().isAuthenticated === true, and GET /api/projects returns the user's projects.
  • The vibncode:// URL scheme is registered (src-tauri/Info.plist). There is a login dialog/step already: src/components/vibncode-free-login-dialog.tsx and src/components/onboarding/steps/login-step.tsx.
  • Wire it so: user clicks Connect → browser opens vibnai.com sign-in/API-key page → token returns via vibncode://auth/callback?token=… → stored with secureStorage.setAuthToken(token)auth-store.isAuthenticated = true. Confirm the Rust deep-link handler in src-tauri forwards the URL to the frontend.
  • AC: Fresh install (no token) → Connect → sign in → token stored → projects load. 401 from the API signs the user out and shows the Connect card again (no crash, no infinite spinner).

CHANGE 3 — Make the cloud the source of truth for chat 🧠 MEDIUM PRIORITY

Goal: Chat history comes from the cloud, so it's identical on any machine and survives reinstalls. Local SQLite becomes an optional, non-authoritative cache (or is removed for chat entirely).

3.1 Load history from the cloud

  • The cloud already stores sessions: GET /api/projects/{projectId}/agent/sessions (list) and GET /api/projects/{projectId}/agent/sessions/{sessionId} (detail with output[]).
  • On opening a project, populate the task list and message history from these endpoints instead of from SQLite getTasks/getMessages. Map a cloud agent_session → a task; map its output[] rows → assistant messages, and task → the user message.
  • Files: src/services/task-service.ts (loadTasks, loadMessages), src/services/database-service.ts (getTasks, getMessages). Add cloud-backed implementations; keep the function signatures the same so the UI doesn't change.
  • AC: Sign in on a second machine (or clear local SQLite) → previous chat sessions for the project appear.

3.2 Demote or remove local SQLite for chat

  • Once 3.1 works, the SQLite writes in message-service.ts / task-service.ts are redundant. Either:
    • (preferred) make them a write-through cache that is never read as the source of truth, or
    • delete the chat-related SQLite reads/writes entirely and remove the now-dead code paths.
  • Keep SQLite only for genuinely local prefs if needed (e.g. settings, recent_files). Do NOT keep it for conversations/messages as a source of truth.
  • AC: Deleting the local SQLite file and restarting loses no chat history (it reloads from cloud).

CHANGE 4 — Single model = VibnAI Gemini 3.5 Flash 🤖 MOSTLY DONE

Status: The senior agent already (a) filtered the desktop model list to the vibncode provider (src/providers/stores/provider-store.ts, restrictToVibnai), (b) relabeled the VibnAI model to Gemini 3.5 Flash (packages/shared/src/data/models-config.json), and (c) pointed default model types at it (src/types/model-types.ts, src/providers/config/model-constants.ts).

The model JSON is embedded into Rust via include_str!, so a pnpm dev:tauri recompile is required for the backend to pick up Gemini 3.5 Flash.

4.1 Remaining: make the desktop model choice actually drive the cloud (model passthrough)

  • Today the cloud uses the runner's env model regardless of the desktop pick. To make the picker authoritative:
    1. src/services/execution-service.ts: include model in the POST /agent/sessions body.
    2. vibn-frontend/app/api/projects/[projectId]/agent/sessions/route.ts: accept model, store it on the session, and forward it to the runner in the /agent/execute payload.
    3. vibn-agent-runner/src/agent-session-runner.ts + src/llm/vibn-chat-model.ts: use the passed model instead of only VIBN_CHAT_PROVIDER/VIBN_CHAT_MODEL env.
  • AC: Selecting Gemini 3.5 Flash in the desktop results in the runner using Gemini 3.5 Flash (verify in runner logs). (Until this is done, the runner env must be set to Gemini 3.5 Flash so behavior matches the label.)

CHANGE 5 — Zero local compute teardown 🧹 MEDIUM PRIORITY

Goal: Remove or redirect every local-compute surface inherited from talkcody. Disposition table:

File(s) What it does locally Action
src/services/bash-executor.ts, src/services/terminal-service.ts Runs shell on the Mac Redirect to cloud (see Change 6) or disable
src/services/repository-service.ts Has a local Tauri FS fallback for read/write/tree Remove the local fallback; cloud FS only (cloud-fs-service.ts). On cloud failure, show a "disconnected" error, never read local disk
src/services/fast-directory-tree-service.ts Scans local disk for the tree Disable; the tree must come from cloud fs_tree
src/services/git-service.ts, src/services/worktree-service.ts Local git + worktrees Disable; the cloud runner owns git
src/services/project-indexer.ts, src/services/code-navigation-service.ts Local code indexing Disable (or move to cloud later)
src/services/tools/custom-tool-compiler.ts, custom-tool-bun-runner.ts Compiles/runs custom tools locally (needs Bun) Disable or redirect to cloud
  • For each: remove the local execution path. Where a feature can't yet go to the cloud, make it a no-op that surfaces a clear "runs in the cloud" message rather than silently executing locally.
  • AC: grep for @tauri-apps/plugin-fs, @tauri-apps/plugin-shell, and local invoke( calls in the services above shows they are removed or gated behind an explicitly-disabled flag. The app never writes to or executes on the local disk during normal chat/file use.

CHANGE 6 — Cloud-backed terminal 💻 MEDIUM PRIORITY

Goal: Keep the terminal UI (part of the IDE feel) but execute every command inside the cloud container.

  • Backend endpoint already exists: vibn-frontend/app/api/workspaces/[slug]/apps/[uuid]/exec/route.ts.
  • File: src/services/terminal-service.ts. Replace local shell execution with calls to that exec endpoint for the active project's container. Stream stdout/stderr back to the terminal UI.
  • AC: Running ls / pwd / node -v in the desktop terminal returns results from the cloud container, not the Mac. Nothing executes on the Mac.

CHANGE 7 — Replace polling with SSE (optional polish) 🔌 LOW PRIORITY

Goal: Lower-latency streaming. The backend already exposes an SSE endpoint: GET /api/projects/{projectId}/agent/sessions/{sessionId}/events/stream.

  • File: src/services/execution-service.ts. Replace the while (isRunning) 1.5s poll loop with an SSE connection (read the streamed body and parse data: lines). Keep the AbortController cancel path (call .../stop on abort). Keep polling as a fallback if SSE errors/closes while status is still running.
  • Also fix: the .../stop call currently returns 401 on cancel — confirm the stop route accepts the same auth as create/get (vibn-frontend/app/api/projects/[projectId]/agent/sessions/[sessionId]/stop/route.ts).
  • AC: Chat streams token-by-token with no visible 1.5s steps; cancel stops the cloud session with a 200.

CHANGE 8 — Route desktop chat to the frontend /api/chat (interactive brain); retire the local agent loop HIGH PRIORITY (the interactivity fix)

Why: The desktop currently sends every message to the headless runner (/agent/sessions/agent/execute), whose coder prompt is explicitly non-interactive ("running headlessly… do NOT ask questions"). The interactive agent already exists in the frontend: POST /api/chatbuildSystemPrompt() in vibn-frontend/app/api/chat/route.ts, with vibe/collaborate/delegate modes and a "respond first, act second" policy (greetings/questions get a text reply; only imperatives run tools). Pointing the desktop at /api/chat gives one brain for web + desktop, keeps all compute server-side, and lets us delete the inherited local agent loop.

Decision (chosen): frontend owns the brain. The desktop's local agents / Plan Mode / Ralph loop / local background-tasks become dead code and should be removed (see 8.5). Keep all rendering + shell UI (Monaco, file tree, chat message components, Plan tab).

/api/chat contract (verified in code)

  • Auth: currently authSession() (browser cookies) on BOTH /api/chat and /api/chat/threads. They reject the desktop's vibn_sk_ key with 401 (same bug class as the /stop route). Must be fixed first — see 8.1.
  • Threads: POST /api/chat/threads (optionally { projectId, workspace }) → { id }. GET /api/chat/threads?projectId=… lists them. Tables fs_chat_threads / fs_chat_messages (history persisted server-side).
  • Chat: POST /api/chat body:
    { thread_id: string; message: string; workspace: string;
      mcp_token?: string; chatMode?: "vibe" | "collaborate" | "delegate"; attachedFiles?: string[] }
    
    Response is SSE (text/event-stream). Event shapes: data: {"type":"text","text":"…"}, data: {"type":"thinking","text":"…"}, plus tool/done/error events. Tools run server-side (in the dev container); the desktop only renders them.

8.1 Backend: accept the workspace API key on the chat routes (PREREQUISITE)

  • Files: vibn-frontend/app/api/chat/route.ts and vibn-frontend/app/api/chat/threads/route.ts (and threads/[id]/route.ts).
  • Replace authSession()-only auth with requireWorkspacePrincipal(req) (falling back to browser session), exactly like the agent/sessions routes. Resolve the user email from principal.userId via fs_users.
  • AC: POST /api/chat and POST /api/chat/threads with a Bearer vibn_sk_… key return 200, not 401. (Deploy frontend.)

8.2 Desktop: a streaming chat client

  • File: vibn-code/src/services/api-client.ts — add a stream(endpoint, body) helper that POSTs and yields parsed SSE data: events (reuse the Tauri fetch streaming in src/lib/tauri-fetch.ts).
  • AC: can consume an SSE response line-by-line and surface {type,text} events.

8.3 Desktop: thread management

  • On new conversation, call POST /api/chat/threads { projectId, workspace } and store the returned thread_id on the task (map desktop task ↔ cloud thread). Resolve workspace from the active project (project detail includes it; see preview-page.tsx which reads project.workspace).
  • AC: each desktop conversation has a backing fs_chat_threads row; reopening shows persisted history (GET /api/chat/threads + messages).

8.4 Desktop: send chat through /api/chat instead of the runner

  • File: vibn-code/src/services/execution-service.ts (or a new chat-service.ts). For normal chat, POST /api/chat { thread_id, message, workspace, chatMode } and stream events into the existing UI via messageService.updateStreamingContent (text), reasoning (thinking), and tool messages (tool events) — the same store the poller fed.
  • Keep the AbortController cancel path (close the stream on Stop).
  • Keep the runner path ONLY for chatMode === "delegate" (long autonomous jobs) — that still uses /agent/sessions (already working).
  • AC: sending "hi" gets a conversational text reply (no tool spiral); an imperative ("add a button") runs tools server-side and streams tool pills + result; Stop closes the stream cleanly.

8.5 Desktop: mode selector + retire the local brain

  • Add a small vibe / collaborate / delegate selector in the chat input (replace the now-defunct agent dropdown); persist as a setting (e.g. chat_mode). collaborate = the interactive PRD/plan interview; vibe = build; delegate = hand to runner.
  • Mark for removal (now dead once 8.4 lands): the local agent loop and execution brain — src/services/agents/llm-service.ts, tool-executor.ts, tool-dependency-analyzer.ts, ralph-loop-service.ts, *-hook-service.ts, the per-agent files in src/services/agents/*-agent.ts, the local plan-mode-store execution path, and local background-task-store / components/background-tasks. Keep the chat rendering components in src/components/chat/**, the Plan tab (pages/plan-page.tsx), settings, Monaco, and file tree.
  • Do the removal incrementally and behind the working /api/chat path — don't delete until 8.4's AC pass.
  • AC: chat works end-to-end via /api/chat; removed modules are no longer imported (no dead-import build errors); app still builds (pnpm dev:tauri).

8.6 Title generation (cleanup)

  • The local title service calls https://api.vibncode.com/… (dead host) and always fails. Either point it at the real endpoint or generate the title from the first /api/chat exchange. AC: new conversations get a real title, no api.vibncode.com errors in logs.

Verification & Release (run after each change group)

  1. cd vibn-code && pnpm dev:tauri launches with no console errors.
  2. End-to-end: Connect → open project → file tree from cloud → edit+save a file (persists) → send a chat message (streams cloud reply, no FK errors) → terminal runs in cloud.
  3. cd vibn-code && pnpm test — fix regressions you introduced.
  4. Commit & push each repo to its correct remote (see §1.1). Redeploy vibn-frontend / vibn-agent-runner per VIBNDEV.md if you changed them.

Priority order (do in this sequence)

  1. CHANGE 1 / 1.5 / 1.6 — done (chat reaches the runner; tools wired).
  2. CHANGE 8 — route chat to /api/chat + mode selector + retire local brain. This is the main work now and it delivers interactivity. Subsumes/reframes:
    • CHANGE 7 (SSE) — absorbed: /api/chat is already SSE.
    • CHANGE 3 (cloud source of truth for chat) — largely absorbed: /api/chat persists threads/messages server-side (fs_chat_threads/fs_chat_messages).
    • CHANGE 4.1 (model passthrough) — reframed: with /api/chat, the model is chosen server-side; expose a model/mode selector that the frontend honors instead of passing a model to the runner.
  3. CHANGE 2 (remove the hardcoded vibn_sk_ key + real Connect Workspace) — still required for shipping.
  4. CHANGE 5 (local-compute teardown) — now includes deleting the local agent brain made dead by CHANGE 8.
  5. CHANGE 6 (cloud terminal).
  6. CHANGE 1.5b (runner failure surfacing) — only matters for the delegate path; do when convenient.

Quick reference — key files

Concern File
HTTP + auth src/services/api-client.ts
Auth state src/stores/auth-store.ts, src/services/auth-service.ts, src/services/secure-storage.ts
Chat send flow src/components/chat-box.tsx
Cloud agent run/stream src/services/execution-service.ts
Messages (local) src/services/message-service.ts
Tasks (local) src/services/task-service.ts
Local DB service src/services/database-service.ts, src/services/database/task-service.ts
SQLite schema + FKs src/services/database/turso-schema.ts, turso-database-init.ts
Cloud FS src/services/cloud-fs-service.ts, src/services/repository-service.ts
Model list/picker src/providers/stores/provider-store.ts, src/components/chat/model-selector-button.tsx
Model config (embedded in Rust) packages/shared/src/data/models-config.json
Model defaults/constants src/types/model-types.ts, src/providers/config/model-constants.ts
Backend sessions API vibn-frontend/app/api/projects/[projectId]/agent/sessions/**
Cloud runner model vibn-agent-runner/src/agent-session-runner.ts, src/llm/vibn-chat-model.ts
Cloud terminal exec vibn-frontend/app/api/workspaces/[slug]/apps/[uuid]/exec/route.ts