Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@syncagent/vue

Package Overview
Dependencies
Maintainers
1
Versions
5
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@syncagent/vue

SyncAgent Vue 3 SDK — composables and chat component

latest
Source
npmnpm
Version
0.5.1
Version published
Maintainers
1
Created
Source

@syncagent/vue

Vue 3 SDK for SyncAgent — composables for AI database chat in Vue apps.

Works with MongoDB, PostgreSQL, MySQL, SQLite, SQL Server, and Supabase.

npm version License: MIT

Get Your API Key

  • Sign up for a free account
  • Go to your DashboardNew Project → choose your database type
  • Copy your API key (starts with sa_)

Every new project gets a 14-day trial with 500 free requests — no credit card required. After the trial, you get 100 free requests/month on the Free plan.

Install

npm install @syncagent/vue @syncagent/js

Quick Start

<script setup lang="ts">
import { ref } from "vue";
import { SyncAgentClient } from "@syncagent/js";
import { useSyncAgent } from "@syncagent/vue";

const client = new SyncAgentClient({
  apiKey: import.meta.env.VITE_SYNCAGENT_KEY,
  connectionString: import.meta.env.VITE_DATABASE_URL,
});

const { messages, isLoading, error, status, sendMessage, stop, reset } = useSyncAgent({ client });
const input = ref("");

function send() {
  if (!input.value.trim()) return;
  sendMessage(input.value);
  input.value = "";
}
</script>

<template>
  <div class="chat">
    <div v-if="status" class="status">⏳ {{ status.label }}</div>

    <div class="messages">
      <div v-for="(msg, i) in messages" :key="i" :class="['message', msg.role]">
        {{ msg.content }}
      </div>
    </div>

    <div v-if="error" class="error">⚠️ {{ error.message }}</div>

    <div class="input-row">
      <input v-model="input" @keydown.enter="send" placeholder="Ask about your data..." :disabled="isLoading" />
      <button @click="send" :disabled="isLoading || !input.trim()">Send</button>
      <button v-if="isLoading" @click="stop">Stop</button>
      <button @click="reset">Clear</button>
    </div>
  </div>
</template>

Environment Variables

# .env (Vite)
VITE_SYNCAGENT_KEY=sa_your_api_key
VITE_DATABASE_URL=mongodb+srv://user:pass@cluster/db

⚠️ Security note: In production, pass the connection string from your server/API route instead of exposing it in client-side env vars.

Multi-tenant SaaS

<script setup lang="ts">
import { computed } from "vue";
import { SyncAgentClient } from "@syncagent/js";
import { useSyncAgent } from "@syncagent/vue";
import { useAuth } from "@/composables/auth";

const { user } = useAuth();

const client = computed(() => new SyncAgentClient({
  apiKey: import.meta.env.VITE_SYNCAGENT_KEY,
  connectionString: import.meta.env.VITE_DATABASE_URL,
  filter: { organizationId: user.value.orgId },
  operations: user.value.isAdmin
    ? ["read", "create", "update", "delete"]
    : ["read"],
}));

const { messages, isLoading, sendMessage } = useSyncAgent({
  client: client.value,
  context: { userId: user.value.id, page: "dashboard" },
});
</script>

Custom Tools

<script setup lang="ts">
import { SyncAgentClient } from "@syncagent/js";
import { useSyncAgent } from "@syncagent/vue";

const client = new SyncAgentClient({
  apiKey: import.meta.env.VITE_SYNCAGENT_KEY,
  connectionString: import.meta.env.VITE_DATABASE_URL,
  tools: {
    sendNotification: {
      description: "Send a push notification to a user",
      inputSchema: {
        userId:  { type: "string", description: "User ID" },
        message: { type: "string", description: "Notification message" },
      },
      execute: async ({ userId, message }) => {
        await pushService.send(userId, message);
        return { sent: true };
      },
    },
  },
});

const { messages, sendMessage } = useSyncAgent({ client });
</script>

Tools-only Mode

Use toolsOnly: true when you want the agent to only call your custom tools — no database access:

<script setup lang="ts">
import { SyncAgentClient } from "@syncagent/js";
import { useSyncAgent } from "@syncagent/vue";

const client = new SyncAgentClient({
  apiKey: import.meta.env.VITE_SYNCAGENT_KEY,
  toolsOnly: true,
  tools: {
    searchProducts: {
      description: "Search products by name",
      inputSchema: { query: { type: "string" } },
      execute: async ({ query }) => {
        const res = await fetch(`/api/products?q=${query}`);
        return res.json();
      },
    },
  },
});

const { messages, sendMessage } = useSyncAgent({ client });
</script>

Customer Agent Mode

Use useCustomerChat to build customer support interfaces with Vue reactive state. This composable wraps the JS SDK's customerChat method, managing conversation state, escalation, and resolution as Vue Ref values.

<script setup lang="ts">
import { ref } from "vue";
import { SyncAgentClient } from "@syncagent/js";
import { useCustomerChat } from "@syncagent/vue";

const client = new SyncAgentClient({
  apiKey: import.meta.env.VITE_SYNCAGENT_KEY,
  connectionString: import.meta.env.VITE_DATABASE_URL,
  externalUserId: "user_abc123",
});

const {
  messages,
  conversationId,
  isLoading,
  isEscalated,
  isResolved,
  error,
  welcomeMessage,
  sendMessage,
  rateConversation,
  reset,
} = useCustomerChat({
  client,
  onEscalated: () => {
    console.log("Conversation escalated to a human agent");
  },
  onResolved: (id) => {
    console.log(`Conversation ${id} resolved`);
  },
});

const input = ref("");

function send() {
  if (!input.value.trim()) return;
  sendMessage(input.value);
  input.value = "";
}
</script>

<template>
  <div class="customer-chat">
    <div v-if="welcomeMessage" class="welcome">{{ welcomeMessage }}</div>

    <div class="messages">
      <div v-for="(msg, i) in messages" :key="i" :class="['message', msg.role]">
        {{ msg.content }}
      </div>
    </div>

    <div v-if="isEscalated" class="notice">🙋 You've been connected to a human agent.</div>
    <div v-if="isResolved" class="notice">
      ✅ Conversation resolved.
      <button @click="rateConversation(5)">Rate ⭐⭐⭐⭐⭐</button>
    </div>

    <div v-if="error" class="error">⚠️ {{ error.message }}</div>

    <div class="input-row">
      <input v-model="input" @keydown.enter="send" placeholder="Ask a question..." :disabled="isLoading" />
      <button @click="send" :disabled="isLoading || !input.trim()">Send</button>
      <button @click="reset">Clear</button>
    </div>
  </div>
</template>

Return Values

NameTypeDescription
messagesRef<Message[]>Conversation message history
conversationIdRef<string | null>Current conversation ID
isLoadingRef<boolean>True while awaiting a response
isEscalatedRef<boolean>True when escalated to a human agent
isResolvedRef<boolean>True when the conversation is resolved
errorRef<Error | null>Last error, if any
welcomeMessageRef<string | null>Welcome message (first interaction only)
sendMessage(content: string, metadata?: Record<string, any>) => Promise<void>Send a customer message
rateConversation(rating: number) => Promise<void>Rate the conversation (1-5)
reset() => voidClear all state and start fresh

UseCustomerChatOptions

OptionTypeDescription
clientSyncAgentClientRequired. Must be configured with externalUserId
onEscalated() => voidCalled when the conversation is escalated to a human agent
onResolved(conversationId: string) => voidCalled when the conversation is resolved

Guest Identification

When no externalUserId is provided, the composable activates a guest identification flow. Guests must identify themselves before sending messages. The composable exposes reactive refs and a method for managing this flow.

Guest Fields from useCustomerChat

NameTypeDescription
isIdentifiedRef<boolean>true when the guest has been identified (via form or stored identity)
guestIdentityRef<GuestIdentity | null>The current guest identity object, or null if not yet identified
identifyGuest(data: { name: string; email: string; phone?: string }) => voidSubmit guest identification data — validates, generates ID, persists, and transitions state

Using the GuestIdentificationForm Component

The GuestIdentificationForm SFC provides a ready-made form with built-in validation. It emits a submit event with the complete GuestIdentity when the guest provides valid data.

<script setup lang="ts">
import { SyncAgentClient } from "@syncagent/js";
import { useCustomerChat, GuestIdentificationForm } from "@syncagent/vue";
import type { GuestIdentity, GuestFormConfig } from "@syncagent/vue";

const client = new SyncAgentClient({
  apiKey: import.meta.env.VITE_SYNCAGENT_KEY,
});

const { messages, isIdentified, sendMessage, identifyGuest } = useCustomerChat({
  client,
  onGuestIdentified: (identity) => {
    console.log("Guest identified:", identity.name, identity.email);
  },
});

const formConfig: GuestFormConfig = {
  title: "Welcome!",
  subtitle: "Tell us a bit about yourself to get started.",
  submitButtonText: "Start Chat",
  namePlaceholder: "Your name",
  emailPlaceholder: "you@example.com",
};

function handleGuestSubmit(identity: GuestIdentity) {
  identifyGuest(identity);
}
</script>

<template>
  <div class="chat">
    <GuestIdentificationForm
      v-if="!isIdentified"
      :config="formConfig"
      class-name="guest-form"
      @submit="handleGuestSubmit"
    />

    <div v-else>
      <div v-for="(msg, i) in messages" :key="i" :class="msg.role">
        {{ msg.content }}
      </div>
      <!-- chat input here -->
    </div>
  </div>
</template>

Manual Guest Identification via Composable

You can build your own form and call identifyGuest directly:

<script setup lang="ts">
import { ref } from "vue";
import { SyncAgentClient } from "@syncagent/js";
import { useCustomerChat } from "@syncagent/vue";

const client = new SyncAgentClient({
  apiKey: import.meta.env.VITE_SYNCAGENT_KEY,
});

const { isIdentified, guestIdentity, identifyGuest, error, messages, sendMessage } = useCustomerChat({
  client,
  onGuestIdentified: (identity) => {
    console.log("Guest identified:", identity.guestId);
  },
});

const name = ref("");
const email = ref("");
const phone = ref("");

function submitGuestForm() {
  identifyGuest({
    name: name.value,
    email: email.value,
    phone: phone.value || undefined,
  });
}
</script>

<template>
  <div>
    <form v-if="!isIdentified" @submit.prevent="submitGuestForm">
      <input v-model="name" placeholder="Name" required />
      <input v-model="email" type="email" placeholder="Email" required />
      <input v-model="phone" type="tel" placeholder="Phone (optional)" />
      <button type="submit">Start Chat</button>
      <p v-if="error" class="error">{{ error.message }}</p>
    </form>

    <div v-else>
      <p>Welcome, {{ guestIdentity?.name }}!</p>
      <div v-for="(msg, i) in messages" :key="i" :class="msg.role">
        {{ msg.content }}
      </div>
      <!-- chat input here -->
    </div>
  </div>
</template>

Note: If identifyGuest is called with invalid data (empty name or invalid email), the error ref is set with a validation message and isIdentified remains false. Messages cannot be sent until the guest is identified.

Dual Mode (Database + Customer Agent)

⚠️ Deprecated: createDual() will be removed in a future major version. Use useDualChat() instead — pass externalUserId directly to enable both modes on a single instance.

Legacy pattern (deprecated):

<script setup lang="ts">
import { SyncAgentClient } from "@syncagent/js";
import { useSyncAgent, useCustomerChat } from "@syncagent/vue";

const { db, support } = SyncAgentClient.createDual({
  apiKey: import.meta.env.VITE_SYNCAGENT_KEY,
  connectionString: import.meta.env.VITE_DATABASE_URL,
  externalUserId: "customer_123",
});

// Database agent for admin queries
const { messages: adminMessages, sendMessage: adminSend } = useSyncAgent({ client: db });

// Customer agent for support
const { messages: supportMessages, sendMessage: supportSend } = useCustomerChat({ client: support });
</script>

Before (deprecated):

<script setup lang="ts">
import { SyncAgentClient } from "@syncagent/js";
import { useSyncAgent, useCustomerChat } from "@syncagent/vue";

const { db, support } = SyncAgentClient.createDual({
  apiKey: import.meta.env.VITE_SYNCAGENT_KEY,
  connectionString: import.meta.env.VITE_DATABASE_URL,
  externalUserId: "customer_123",
});

const { messages: adminMessages, sendMessage: adminSend } = useSyncAgent({ client: db });
const { messages: supportMessages, sendMessage: supportSend } = useCustomerChat({ client: support });
</script>

After (recommended):

<script setup lang="ts">
import { SyncAgentClient } from "@syncagent/js";
import { useDualChat } from "@syncagent/vue";

const dualClient = SyncAgentClient.createDual({
  apiKey: import.meta.env.VITE_SYNCAGENT_KEY,
  connectionString: import.meta.env.VITE_DATABASE_URL,
  externalUserId: "customer_123",
});

const { db, support } = useDualChat({ client: dualClient });
</script>

Unified Dual Mode

The useDualChat() composable provides a single reactive interface for both database and customer chat on the same client instance. Pass externalUserId to a DualClient to enable both db (database agent) and support (customer agent) namespaces from one setup.

<script setup lang="ts">
import { ref } from "vue";
import { SyncAgentClient } from "@syncagent/js";
import { useDualChat } from "@syncagent/vue";

const dualClient = SyncAgentClient.createDual({
  apiKey: import.meta.env.VITE_SYNCAGENT_KEY,
  connectionString: import.meta.env.VITE_DATABASE_URL,
  externalUserId: "user_abc123",
});

const { db, support } = useDualChat({ client: dualClient });

const dbInput = ref("");
const supportInput = ref("");

function sendDbMessage() {
  if (!dbInput.value.trim()) return;
  db.sendMessage(dbInput.value);
  dbInput.value = "";
}

function sendSupportMessage() {
  if (!supportInput.value.trim()) return;
  support.sendMessage(supportInput.value);
  supportInput.value = "";
}
</script>

<template>
  <div class="dual-chat">
    <!-- Database Agent -->
    <div class="panel">
      <h3>Database Agent</h3>
      <div v-for="(msg, i) in db.messages" :key="'db-' + i" :class="msg.role">
        {{ msg.content }}
      </div>
      <input v-model="dbInput" @keydown.enter="sendDbMessage" placeholder="Query your data..." />
      <button @click="sendDbMessage" :disabled="db.isLoading">Send</button>
    </div>

    <!-- Customer Support -->
    <div class="panel">
      <h3>Customer Support</h3>
      <div v-for="(msg, i) in support.messages" :key="'support-' + i" :class="msg.role">
        {{ msg.content }}
      </div>
      <input v-model="supportInput" @keydown.enter="sendSupportMessage" placeholder="Ask support..." />
      <button @click="sendSupportMessage" :disabled="support.isLoading">Send</button>
    </div>
  </div>
</template>

DualChatReturn

All properties are Vue reactive refs.

db namespace

NameTypeDescription
db.messagesRef<Message[]>Database agent conversation history
db.isLoadingRef<boolean>True while the database agent is streaming
db.errorRef<Error | null>Last error from the database agent
db.statusRef<{ step: string; label: string } | null>Live status (connecting, querying, thinking, done)
db.lastDataRef<ToolData | null>Last DB query result
db.sendMessage(content: string) => Promise<void>Send a message to the database agent
db.stop() => voidAbort the current database agent stream
db.reset() => voidClear database agent messages

support namespace

NameTypeDescription
support.messagesRef<Message[]>Customer support conversation history
support.conversationIdRef<string | null>Current support conversation ID
support.isLoadingRef<boolean>True while awaiting a support response
support.isEscalatedRef<boolean>True when escalated to a human agent
support.isResolvedRef<boolean>True when the conversation is resolved
support.errorRef<Error | null>Last error from the support agent
support.welcomeMessageRef<string | null>Welcome message (first interaction only)
support.sendMessage(content: string, metadata?: Record<string, any>) => Promise<void>Send a message to customer support
support.rateConversation(rating: number) => Promise<void>Rate the support conversation (1-5)
support.reset() => voidClear support state and start fresh

UseDualChatOptions

OptionTypeRequiredDescription
clientDualClientYesA client created via SyncAgentClient.createDual() with externalUserId
contextRecord<string, any>NoExtra context injected into every message for both agents
onData(data: ToolData) => voidNoCalled when the database agent tool returns data
onEscalated() => voidNoCalled when the support conversation is escalated to a human agent
onResolved(conversationId: string) => voidNoCalled when the support conversation is resolved

Error Handling

If useDualChat() is called without a valid DualClient instance, it throws:

useDualChat: a valid DualClient instance is required

Ensure you pass a client created via SyncAgentClient.createDual() with a valid externalUserId.

Customer Chat Component

The <SyncAgentCustomerChat> component is a pre-built, drop-in customer support chat widget. It handles the full conversation lifecycle: guest identification, messaging, escalation to human agents, real-time Pusher messages, satisfaction rating, and theming — all with zero custom UI required.

<script setup lang="ts">
import { SyncAgentCustomerChat } from "@syncagent/vue";
</script>

<template>
  <SyncAgentCustomerChat
    api-key="sa_your_api_key"
    connection-string="your_connection_string"
  />
</template>

A floating chat button appears in the bottom-right corner. Anonymous visitors see a guest identification form before chatting.

Props

PropTypeDefaultDescription
configSyncAgentConfigPre-built configuration object. Takes priority over individual props.
apiKeystringAPI key (required if no config).
connectionStringstringDatabase connection string (required if no config).
externalUserIdstringAuthenticated user ID. Skips guest form when provided.
mode"floating" | "inline""floating"Floating toggle button or embedded inline panel.
position"bottom-right" | "bottom-left""bottom-right"Position of the floating widget (floating mode only).
defaultOpenbooleanfalseWhether the floating panel starts open (floating mode only).
titlestring"Customer Support"Header title text (max 100 chars).
subtitlestring"How can we help you?"Header subtitle text (max 200 chars).
placeholderstring"Type your message..."Input placeholder text (max 150 chars).
welcomeMessagestringInitial welcome message displayed before any interaction.
accentColorstring"#6366f1"Primary accent color (hex, rgb, or hsl).
darkModebooleanfalseEnable dark mode color scheme.
classNamestringAdditional CSS class on the root container.
guestFormGuestFormConfigCustom guest form configuration (title, subtitle, placeholders, button text).
pusherKeystringPusher app key for real-time human agent messages.
pusherClusterstring"us2"Pusher cluster.
metadataRecord<string, any>Extra metadata sent with every message.

Emits

EventPayloadDescription
escalatedFired when the conversation is escalated to a human agent.
resolvedconversationId: stringFired when the conversation is resolved.
guest-identifiedidentity: GuestIdentityFired when a guest completes identification.

Code Examples

Config Object

<script setup lang="ts">
import { SyncAgentCustomerChat } from "@syncagent/vue";

const config = {
  apiKey: "sa_your_api_key",
  connectionString: import.meta.env.VITE_DATABASE_URL,
  customerMode: true,
  externalUserId: "user_abc123",
};
</script>

<template>
  <SyncAgentCustomerChat :config="config" />
</template>

Floating Mode (default)

<template>
  <SyncAgentCustomerChat
    api-key="sa_your_api_key"
    connection-string="your_connection_string"
    position="bottom-left"
    :default-open="true"
    title="Need Help?"
    subtitle="We're here for you"
  />
</template>

Inline Mode

Embed the chat inside your layout instead of a floating button:

<template>
  <div style="height: 600px">
    <SyncAgentCustomerChat
      api-key="sa_your_api_key"
      connection-string="your_connection_string"
      mode="inline"
    />
  </div>
</template>

Dark Mode with Custom Accent Color

<template>
  <SyncAgentCustomerChat
    api-key="sa_your_api_key"
    connection-string="your_connection_string"
    :dark-mode="true"
    accent-color="#8b5cf6"
  />
</template>

Handling Emitted Events

<script setup lang="ts">
import { SyncAgentCustomerChat } from "@syncagent/vue";
import type { GuestIdentity } from "@syncagent/vue";

function onEscalated() {
  console.log("Conversation escalated to a human agent");
}

function onResolved(conversationId: string) {
  console.log("Conversation resolved:", conversationId);
}

function onGuestIdentified(identity: GuestIdentity) {
  console.log("Guest identified:", identity.name, identity.email);
}
</script>

<template>
  <SyncAgentCustomerChat
    api-key="sa_your_api_key"
    connection-string="your_connection_string"
    @escalated="onEscalated"
    @resolved="onResolved"
    @guest-identified="onGuestIdentified"
  />
</template>

useCustomerChat Composable

For developers who want to build a fully custom chat UI, the useCustomerChat composable provides all the reactive state and methods without any pre-built UI.

Options

OptionTypeRequiredDescription
clientSyncAgentClientYesClient instance (configure with customerMode: true).
externalUserIdstringNoAuthenticated user ID. Bypasses guest flow when provided.
onEscalated() => voidNoCalled when the conversation is escalated to a human agent.
onResolved(conversationId: string) => voidNoCalled when the conversation is resolved.
onGuestIdentified(identity: GuestIdentity) => voidNoCalled when a guest completes identification.

Return Values

NameTypeDescription
messagesRef<Message[]>Conversation message history.
conversationIdRef<string | null>Current conversation ID (set after first message).
isLoadingRef<boolean>true while awaiting a response.
isEscalatedRef<boolean>true when escalated to a human agent.
isResolvedRef<boolean>true when the conversation is resolved.
errorRef<Error | null>Last error, or null.
welcomeMessageRef<string | null>Welcome message (first interaction only).
isIdentifiedRef<boolean>true when the user is identified (has externalUserId or completed guest form).
guestIdentityRef<GuestIdentity | null>The stored guest identity, or null.
sendMessage(content: string, metadata?: Record<string, any>) => Promise<void>Send a message to the customer agent.
rateConversation(rating: number) => Promise<void>Rate the conversation (1–5).
identifyGuest(data: { name: string; email: string; phone?: string }) => voidSubmit guest identification data.
reset() => voidClear all state and start fresh.

Custom UI Example

<script setup lang="ts">
import { ref } from "vue";
import { SyncAgentClient } from "@syncagent/js";
import { useCustomerChat } from "@syncagent/vue";

const client = new SyncAgentClient({
  apiKey: import.meta.env.VITE_SYNCAGENT_KEY,
  connectionString: import.meta.env.VITE_DATABASE_URL,
  customerMode: true,
  externalUserId: "user_abc123",
});

const {
  messages,
  isLoading,
  isEscalated,
  isResolved,
  error,
  sendMessage,
  rateConversation,
} = useCustomerChat({ client });

const input = ref("");

function send() {
  if (!input.value.trim()) return;
  sendMessage(input.value);
  input.value = "";
}
</script>

<template>
  <div class="my-custom-chat">
    <div v-for="(msg, i) in messages" :key="i" :class="msg.role">
      {{ msg.content }}
    </div>

    <p v-if="isEscalated">You're connected to a human agent.</p>

    <div v-if="isResolved">
      <p>Conversation resolved!</p>
      <button @click="rateConversation(5)">⭐ Rate 5/5</button>
    </div>

    <p v-if="error" class="error">{{ error.message }}</p>

    <form @submit.prevent="send">
      <input v-model="input" placeholder="Ask a question..." :disabled="isLoading" />
      <button type="submit" :disabled="isLoading || !input.trim()">Send</button>
    </form>
  </div>
</template>

Theming

The <SyncAgentCustomerChat> component uses the shared theme engine (computeTheme from @syncagent/js) to generate a complete WCAG-compliant color palette from two inputs:

  • accentColor — Any valid CSS color string (hex, rgb, hsl). Used for buttons, user message bubbles, focus outlines, and the header background. Defaults to #6366f1.
  • darkMode — When true, generates a dark color scheme. All text-on-background pairs maintain a minimum 4.5:1 contrast ratio.

All styles are applied inline, so no external CSS is required and no styles leak to or from your application. You can pass a className prop to add your own class for additional positioning or layout styles, and use the :style binding on a wrapper element for further customization.

<template>
  <!-- Custom accent + dark mode -->
  <SyncAgentCustomerChat
    api-key="sa_your_api_key"
    connection-string="your_connection_string"
    accent-color="#ec4899"
    :dark-mode="true"
    class-name="my-chat-widget"
  />
</template>

<style>
.my-chat-widget {
  /* Additional layout styles — component colors are handled internally */
  margin: 16px;
}
</style>

Accessibility

The <SyncAgentCustomerChat> component is built to meet WCAG 2.1 AA standards:

  • ARIA roles: The container uses role="region" with an aria-label. The message list uses role="log" to convey its purpose to assistive technologies.
  • Live regions: New messages are announced via aria-live="polite" with aria-relevant="additions", so screen readers announce incoming messages without interrupting the user.
  • Keyboard navigation: All interactive elements are reachable via Tab. Buttons activate with Enter or Space. Rating stars use a roving tabindex pattern with arrow key navigation.
  • Focus management: When the view transitions from the guest form to the chat panel, focus moves to the message input within 100ms. This ensures keyboard users aren't stranded.
  • Contrast requirements: The theme engine guarantees a minimum 4.5:1 contrast ratio for all text-on-background pairs and a minimum 3:1 contrast ratio for focus indicators against adjacent colors.
  • Focus indicators: All interactive elements display a visible focus outline (minimum 2px width) with at least 3:1 contrast against adjacent colors.

API Reference

useSyncAgent(options)

OptionTypeDescription
clientSyncAgentClientRequired. Create with new SyncAgentClient(config)
contextRecord<string,any>Extra context injected into every message
onData(data: ToolData) => voidCalled when a DB tool returns data

Returns

NameTypeDescription
messagesReadonly<Ref<Message[]>>Conversation history
isLoadingReadonly<Ref<boolean>>True while streaming
errorReadonly<Ref<Error|null>>Last error
statusReadonly<Ref<{step,label}|null>>Live status (connecting, querying, thinking, done)
lastDataReadonly<Ref<ToolData|null>>Last DB query result
sendMessage(content: string) => Promise<void>Send a message
stop() => voidAbort current stream
reset() => voidClear all messages

Supported Databases

DatabaseConnection String Format
MongoDBmongodb+srv://user:pass@cluster.mongodb.net/mydb
PostgreSQLpostgresql://user:pass@host:5432/mydb
MySQLmysql://user:pass@host:3306/mydb
SQLite/absolute/path/to/database.sqlite
SQL ServerServer=host,1433;Database=mydb;User Id=user;Password=pass;Encrypt=true;
Supabasehttps://xxx.supabase.co|your-anon-key

Security

  • Your database connection string is never stored on SyncAgent servers
  • Passed at runtime, used once, immediately discarded
  • API keys are hashed with bcrypt — raw keys are never stored

Plans & Pricing

PlanRequests/moCollectionsPrice
Free (+ 14-day trial)100 (500 during trial)5GH₵0
Starter5,00020GH₵150/mo
Pro50,000UnlimitedGH₵500/mo
EnterpriseUnlimitedUnlimitedCustom

View full pricing →

Resources

License

MIT

Keywords

syncagent

FAQs

Package last updated on 22 May 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts