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

@syncagent/js

Package Overview
Dependencies
Maintainers
1
Versions
25
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@syncagent/js

SyncAgent JavaScript SDK — AI database agent for any app

latest
Source
npmnpm
Version
0.4.0
Version published
Maintainers
1
Created
Source

@syncagent/js

Core JavaScript/TypeScript SDK for SyncAgent — add an AI database agent to any app.

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/js

Quick Start

import { SyncAgentClient } from "@syncagent/js";

const agent = new SyncAgentClient({
  apiKey: "sa_your_api_key",
  connectionString: process.env.DATABASE_URL,
});

// Non-streaming
const result = await agent.chat([
  { role: "user", content: "How many users signed up this month?" }
]);
console.log(result.text);

// Streaming
await agent.chat(
  [{ role: "user", content: "Show me the top 10 customers by revenue" }],
  {
    onToken: (token) => process.stdout.write(token),
    onComplete: (text) => console.log("\nDone"),
    onError: (err) => console.error(err),
  }
);

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

Configuration

new SyncAgentClient(config: SyncAgentConfig)
OptionTypeRequiredDescription
apiKeystringYour SyncAgent API key (sa_...)
connectionStringstring✅*Your database URL — sent at runtime, never stored. *Optional when toolsOnly: true.
toolsRecord<string, ToolDefinition>Custom tools the agent can call client-side
baseUrlstringOverride API URL (dev only)
filterRecord<string, any>Mandatory query filter for multi-tenancy
operations("read"|"create"|"update"|"delete")[]Restrict operations for this session
toolsOnlybooleanDisables all DB tools — agent only uses your custom tools
autoDetectPagebooleantrueAuto-detect current page from window.location
systemInstructionstringCustom agent instructions — personality, tone, rules
confirmWritesbooleanfalseAsk for confirmation before create/update/delete
languagestringLanguage the agent responds in (e.g. "French")
maxResultsnumber50Default max records per query
sensitiveFieldsstring[]["password","token","secret"]Fields masked in responses
onBeforeToolCall(name, args) => booleanCalled before each client tool. Return false to block.
onAfterToolCall(name, args, result) => voidCalled after each client tool executes

client.chat(messages, options?)

const result = await agent.chat(messages, options);

messagesMessage[]

{ role: "user" | "assistant"; content: string }

optionsChatOptions

OptionTypeDescription
onToken(token: string) => voidCalled for each streamed text chunk
onComplete(text: string) => voidCalled with the full response text
onError(error: Error) => voidCalled on error
onStatus(step: string, label: string) => voidLive status updates
onData(data: ToolData) => voidCalled when a DB tool returns structured data
onToolCall(name: string, args: any, result: any) => voidCalled when a custom tool executes
signalAbortSignalCancel the request
contextRecord<string, any>Extra context injected into every message

Returns Promise<ChatResult>{ text: string }

Status steps

onStatus fires with these step values:

  • "connecting" — connecting to the database
  • "schema" — discovering schema
  • "thinking" — AI is reasoning
  • "querying" — executing a DB tool
  • "done" — complete

client.getSchema()

const schema = await agent.getSchema();
// CollectionSchema[]

Multi-turn Conversations

const history: Message[] = [];

history.push({ role: "user", content: "Show top 5 customers" });
const r1 = await agent.chat(history);
history.push({ role: "assistant", content: r1.text });

history.push({ role: "user", content: "Now show their total orders" });
const r2 = await agent.chat(history);

Context & Auto Page Detection

The SDK automatically detects the current page from window.location on every message — zero config needed.

URL: /dashboard/orders/ord_123?tab=details

Auto-detected context:
  currentPage: "orders"
  currentPath: "/dashboard/orders/ord_123"
  currentRecordId: "ord_123"
  param_tab: "details"

Pass additional context:

await agent.chat(messages, {
  context: { userId: "user_123", userRole: "admin", orgName: "Acme Corp" }
});

onData — React to Query Results

await agent.chat(messages, {
  onData: (data) => {
    console.log(data.collection); // "orders"
    console.log(data.data);       // array of result rows
    console.log(data.count);      // number of results
  }
});

Custom Tools

Give the agent capabilities beyond your database. Tools run entirely in your app — SyncAgent only sees the schema and result.

const agent = new SyncAgentClient({
  apiKey: "sa_your_key",
  connectionString: process.env.DATABASE_URL,
  tools: {
    sendEmail: {
      description: "Send an email to a user",
      inputSchema: {
        to:      { type: "string", description: "Recipient email" },
        subject: { type: "string", description: "Subject line" },
        body:    { type: "string", description: "Email body" },
      },
      execute: async ({ to, subject, body }) => {
        await mailer.send({ to, subject, text: body });
        return { sent: true };
      },
    },
  },
});

Multi-tenant SaaS

Pass filter to scope every agent operation to the current user's organization. Enforced server-side.

const agent = new SyncAgentClient({
  apiKey: "sa_your_key",
  connectionString: process.env.DATABASE_URL,
  filter: { organizationId: currentUser.orgId },
  operations: currentUser.isAdmin
    ? ["read", "create", "update", "delete"]
    : ["read"],
});

Tools-only Mode

Build an AI assistant powered by your own APIs — no database access needed.

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

Customer Agent Mode

Route messages through the customer support pipeline — with persona, knowledge base, conversation flows, escalation, and AI fallback — instead of the direct database agent.

import { SyncAgentClient } from "@syncagent/js";

const client = new SyncAgentClient({
  apiKey: "sa_your_api_key",
  externalUserId: "customer_123",
});

Note: customerMode is automatically enabled when externalUserId is provided. You do not need to set customerMode: true explicitly.

Configuration

OptionTypeRequiredDescription
customerModebooleanEnable customer agent mode. Routes messages through the customer support pipeline.
externalUserIdstringCustomer identifier for multi-tenant scoping. When provided, customerMode is automatically enabled and both chat() and customerChat() become available on the same instance.

client.customerChat(message, options?)

Send a message through the customer support pipeline.

Error: Throws "SyncAgent: customerChat() requires externalUserId in config (or customerMode: true)" if called without externalUserId in the client config.

const result = await client.customerChat("How do I reset my password?", {
  conversationId: "conv_abc123",
  metadata: { page: "/settings" },
  onEscalated: () => console.log("Escalated to human agent"),
  onResolved: (id) => console.log(`Conversation ${id} resolved`),
});

Parameters

ParameterTypeRequiredDescription
messagestringThe customer's message
optionsCustomerChatOptionsOptional configuration (see below)

Returns Promise<CustomerChatResult>

CustomerChatOptions

FieldTypeDescription
conversationIdstringExisting conversation ID to continue
metadataRecord<string, any>Additional metadata to store on the conversation
onEscalated() => voidCalled when the conversation is escalated to a human agent
onResolved(conversationId: string) => voidCalled when the conversation is resolved

CustomerChatResult

FieldTypeDescription
conversationIdstringThe conversation ID (new or existing)
responsestringThe agent's reply text
escalatedbooleanWhether the conversation was escalated to a human agent
autoReplybooleanWhether this was an automatic reply (e.g. welcome message)
flowActivebooleanWhether a conversation flow is currently active
resolvedbooleanWhether the conversation has been resolved
welcomeMessagestring | undefinedWelcome message if this is a new conversation
sourcesany[] | undefinedKnowledge base sources used to generate the response
flowSession{ flowId: string; currentNodeId: string } | undefinedActive flow session state

client.rateConversation(conversationId, rating)

Submit a satisfaction rating for a completed conversation.

Error: Throws "SyncAgent: rateConversation() requires externalUserId in config" if called without externalUserId in the client config.

await client.rateConversation("conv_abc123", 5);

Parameters

ParameterTypeRequiredDescription
conversationIdstringThe conversation ID to rate
ratingnumberInteger between 1 and 5

Validation rules:

  • conversationId must be a non-empty string
  • rating must be an integer between 1 and 5 (inclusive)
  • Throws an error if validation fails

Returns Promise<void>

Conversation Flows

When a customer's message matches a configured flow's trigger phrases, the agent enters a conversation flow — a scripted branching path that guides the customer through a series of decision points, responses, and terminal actions (resolve or escalate).

You can detect an active flow by checking the flowActive and flowSession fields in the CustomerChatResult response:

FieldTypeDescription
flowActivebooleantrue when the conversation is currently inside a flow
flowSession{ flowId: string; currentNodeId: string } | undefinedIdentifies which flow and node the conversation is on

Detecting and Handling Active Flows

const result = await client.customerChat("I want to check my order status");

if (result.flowActive && result.flowSession) {
  console.log(`Flow active: ${result.flowSession.flowId}`);
  console.log(`Current node: ${result.flowSession.currentNodeId}`);

  // The response contains the current node's content (e.g. decision options)
  console.log(result.response);
  // Example: "How would you like to check your order?\n1. By order number\n2. By email address"

  // Present the options to the user in your UI, then send their choice
  // as the next message to continue the flow:
  const followUp = await client.customerChat("By order number", {
    conversationId: result.conversationId,
  });

  // Check if the flow is still active or has reached a terminal node
  if (!followUp.flowActive) {
    console.log("Flow completed");
  }
}

When flowActive is false, the conversation is handled by the standard AI agent (persona, knowledge base, or escalation). Flows take priority over the AI agent when a trigger phrase matches.

SyncAgentClient.createDual(config)

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

Create both a database agent and a customer agent from a single config. Useful for apps that serve both internal users (admins) and end-customers from the same codebase.

Legacy pattern (deprecated):

import { SyncAgentClient } from "@syncagent/js";

const { db, support } = SyncAgentClient.createDual({
  apiKey: "sa_your_api_key",
  connectionString: "postgresql://user:pass@host:5432/mydb",
  externalUserId: currentUser.id,
});

// Admin: direct database queries
const result = await db.chat([{ role: "user", content: "Show all overdue invoices" }]);

// Customer: support pipeline (persona, flows, KB, escalation)
const reply = await support.customerChat("I need help with my order");
await support.rateConversation(reply.conversationId, 5);

Migration

Before — using createDual():

import { SyncAgentClient } from "@syncagent/js";

const { db, support } = SyncAgentClient.createDual({
  apiKey: "sa_your_api_key",
  connectionString: "postgresql://user:pass@host:5432/mydb",
  externalUserId: "customer_123",
});

const dbResult = await db.chat([{ role: "user", content: "Show all overdue invoices" }]);
const supportResult = await support.customerChat("I need help with my order");

After — unified constructor:

import { SyncAgentClient } from "@syncagent/js";

const client = new SyncAgentClient({
  apiKey: "sa_your_api_key",
  connectionString: "postgresql://user:pass@host:5432/mydb",
  externalUserId: "customer_123",
});

const dbResult = await client.chat([{ role: "user", content: "Show all overdue invoices" }]);
const supportResult = await client.customerChat("I need help with my order");

Parameters

ParameterTypeRequiredDescription
configDualClientConfigShared configuration for both clients

DualClientConfig

FieldTypeRequiredDescription
apiKeystringYour SyncAgent API key
connectionStringstringDatabase connection string
externalUserIdstringCustomer identifier — scopes customer agent conversations
baseUrlstringOverride API base URL
filterRecord<string, any>Mandatory query filter for multi-tenant scoping
operationsstring[]Restrict allowed operations (read, create, update, delete)
toolsRecord<string, ToolDefinition>Custom tools for the database agent
systemInstructionstringCustom system prompt
languagestringResponse language

Returns { db: SyncAgentClient, support: SyncAgentClient }

  • db — Database agent instance (customerMode: false). Use db.chat() for direct DB queries.
  • support — Customer agent instance (customerMode: true). Use support.customerChat() for the support pipeline.

Guest Identification

When using Customer Agent Mode without an externalUserId, the SDK supports a guest identification flow. Guests provide their name and email before chatting, and their identity is persisted to localStorage for returning visits.

Client Methods

client.getGuestIdentity()

Returns the stored guest identity, or null if no identity has been saved.

const identity = client.getGuestIdentity();
// GuestIdentity | null

client.setGuestIdentity(identity)

Persists a guest identity to localStorage and sets the externalUserId for subsequent API calls.

import { generateGuestIdentifier } from "@syncagent/js";

client.setGuestIdentity({
  name: "Jane Doe",
  email: "jane@example.com",
  phone: null,
  guestId: generateGuestIdentifier("jane@example.com"),
});

client.getGuestFormConfig()

Returns the GuestFormConfig passed at construction (via the guestForm option), or undefined if none was provided.

const formConfig = client.getGuestFormConfig();
// GuestFormConfig | undefined

onGuestIdentified Callback

Use the onGuestIdentified callback in CustomerChatOptions to react when a guest completes identification:

import { SyncAgentClient } from "@syncagent/js";

const client = new SyncAgentClient({
  apiKey: "sa_your_api_key",
  customerMode: true,
  guestForm: {
    title: "Hi there!",
    subtitle: "Tell us who you are to get started",
    submitButtonText: "Begin Chat",
  },
});

const result = await client.customerChat("Hello", {
  onGuestIdentified: (identity) => {
    console.log(`Guest identified: ${identity.name} (${identity.guestId})`);
  },
});

Utility Functions

FunctionSignatureDescription
validateName(name: string) => FieldValidationResultValidates that name contains at least one non-whitespace character
validateEmail(email: string) => FieldValidationResultValidates email matches local@domain.tld pattern
validateGuestForm(data: { name, email, phone? }) => { valid: boolean; errors: Record<string, string> }Validates the complete guest form submission
generateGuestIdentifier(email: string) => stringGenerates a deterministic guest_-prefixed ID from the email (FNV-1a hash)
import {
  validateName,
  validateEmail,
  validateGuestForm,
  generateGuestIdentifier,
} from "@syncagent/js";

// Individual field validation
const nameResult = validateName("Jane");
// { valid: true }

const emailResult = validateEmail("not-an-email");
// { valid: false, error: "Please enter a valid email address" }

// Full form validation
const formResult = validateGuestForm({
  name: "Jane Doe",
  email: "jane@example.com",
  phone: "+1234567890",
});
// { valid: true, errors: {} }

// Generate a deterministic guest ID
const guestId = generateGuestIdentifier("jane@example.com");
// "guest_a1b2c3d4"

Types

import type {
  GuestIdentity,
  GuestFormConfig,
  FieldValidationResult,
} from "@syncagent/js";

GuestIdentity

FieldTypeDescription
namestringGuest's display name
emailstringGuest's email address
phonestring | nullOptional phone number
guestIdstringDeterministic identifier (guest_ + FNV-1a hex hash of email)

GuestFormConfig

FieldTypeDefaultDescription
titlestring"Welcome"Form heading text
subtitlestring"Please introduce yourself to get started"Form description text
submitButtonTextstring"Start Chat"Submit button label
namePlaceholderstringPlaceholder for the name input
emailPlaceholderstringPlaceholder for the email input
phonePlaceholderstringPlaceholder for the phone input
classNamestringCustom CSS class for the form container
onSubmit(identity: GuestIdentity) => voidCalled after successful form submission

FieldValidationResult

FieldTypeDescription
validbooleanWhether the field passed validation
errorstring | undefinedError message if validation failed

Unified Dual Mode

By passing externalUserId to the SyncAgentClient constructor alongside your connectionString, both chat() and customerChat() become available on the same instance — no need to call createDual() or manage separate clients.

This is the simplest way to build apps that serve both internal (admin/database) and customer-facing use cases from a single client.

import { SyncAgentClient } from "@syncagent/js";

const client = new SyncAgentClient({
  apiKey: "sa_your_api_key",
  connectionString: process.env.DATABASE_URL,
  externalUserId: "customer_123",
});

// Database agent — direct queries
const dbResult = await client.chat([
  { role: "user", content: "Show all overdue invoices" }
]);
console.log(dbResult.text);

// Customer agent — support pipeline (persona, flows, KB, escalation)
const supportResult = await client.customerChat("I need help with my order", {
  onEscalated: () => console.log("Escalated to human agent"),
  onResolved: (id) => console.log(`Conversation ${id} resolved`),
});
console.log(supportResult.response);

Abort / Cancel

const controller = new AbortController();
agent.chat(messages, { signal: controller.signal });
controller.abort();

Express.js Integration

import express from "express";
import { SyncAgentClient } from "@syncagent/js";

const app = express();
app.use(express.json());

const agent = new SyncAgentClient({
  apiKey: process.env.SYNCAGENT_KEY,
  connectionString: process.env.DATABASE_URL,
});

app.post("/chat", async (req, res) => {
  res.setHeader("Content-Type", "text/plain");
  res.setHeader("Transfer-Encoding", "chunked");

  await agent.chat(req.body.messages, {
    onToken: (token) => res.write(token),
    onComplete: () => res.end(),
    onError: (err) => { res.status(500).end(err.message); },
  });
});

app.listen(3000);

TypeScript Types

import type {
  SyncAgentConfig, Message, ChatOptions, ChatResult,
  CollectionSchema, SchemaField, ToolDefinition, ToolParameter, ToolData,
  CustomerChatResult, CustomerChatOptions, DualClientConfig, DualClient,
  DualChatReturn, DualChatOptions, UnifiedConfig,
} from "@syncagent/js";

New Types Reference

TypeDescriptionImport
DualChatReturnReturn type for the useDualChat() hook and framework equivalents — contains db and support namespaces with messages, state, and actionsimport type { DualChatReturn } from "@syncagent/js"
DualChatOptionsOptions for the useDualChat() hook/composable — includes context, onData, onEscalated, and onResolved callbacksimport type { DualChatOptions } from "@syncagent/js"
UnifiedConfigConfig type alias for the unified client — SyncAgentConfig with externalUserId requiredimport type { UnifiedConfig } from "@syncagent/js"

Security

  • Your database connection string is never stored on SyncAgent servers
  • It's passed at runtime, used to process the request, and immediately discarded
  • API keys are hashed with bcrypt — raw keys are never stored
  • Rate limiting: max 5 concurrent requests per API key per 10 seconds

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 21 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