
Research
/Security News
Laravel Lang Compromised with RCE Backdoor Across 700+ Versions
Laravel Lang packages were compromised with an RCE backdoor across hundreds of versions, exposing cloud, CI/CD, and developer secrets.
@syncagent/js
Advanced tools
Core JavaScript/TypeScript SDK for SyncAgent — add an AI database agent to any app.
npm install @syncagent/js
import { SyncAgentClient } from "@syncagent/js";
const agent = new SyncAgentClient({
apiKey: "sa_your_api_key",
connectionString: process.env.DATABASE_URL,
// baseUrl: "http://localhost:3100", // dev only — defaults to https://syncagent.dev
});
const result = await agent.chat([
{ role: "user", content: "How many users do we have?" }
]);
console.log(result.text);
new SyncAgentClient(config: SyncAgentConfig)
| Option | Type | Required | Description |
|---|---|---|---|
apiKey | string | ✅ | Your SyncAgent API key (sa_...) |
connectionString | string | ✅ | Your database URL — sent at runtime, never stored |
tools | Record<string, ToolDefinition> | — | Custom tools the agent can call client-side |
baseUrl | string | — | Override API URL. Defaults to https://syncagent.dev |
filter | Record<string, any> | — | Mandatory query filter — scopes ALL operations to matching records. Use for multi-tenant SaaS. |
operations | ("read"|"create"|"update"|"delete")[] | — | Restrict which operations are allowed for this session. Must be a subset of the project's configured operations. |
toolsOnly | boolean | — | When true, disables all built-in DB tools — the agent only uses your custom tools. connectionString becomes optional. |
autoDetectPage | boolean | true | Auto-detect current page from window.location and include in context. Set false to disable. |
systemInstruction | string | — | Custom instructions for the agent — personality, tone, rules. Prepended to system prompt. |
confirmWrites | boolean | false | When true, agent asks for explicit user confirmation before any create/update/delete. |
language | string | — | Language the agent responds in. E.g. "French", "Spanish", "Japanese". |
maxResults | number | 50 | Default max records per query. Set lower for mobile, higher for dashboards. |
sensitiveFields | string[] | ["password","token","secret"] | Fields the agent masks as "••••••••" in responses. |
onBeforeToolCall | (name, args) => boolean | — | Called before each client tool executes. Return false to block. |
onAfterToolCall | (name, args, result) => void | — | Called after each client tool executes. Use for logging/analytics. |
client.chat(messages, options?)const result = await agent.chat(messages, options);
messages — Message[]
{ role: "user" | "assistant"; content: string }
options — ChatOptions
| Option | Type | Description |
|---|---|---|
onToken | (token: string) => void | Called for each streamed text chunk |
onComplete | (text: string) => void | Called with the full response text |
onError | (error: Error) => void | Called on error |
onStatus | (step: string, label: string) => void | Called with live status updates while working |
onData | (data: ToolData) => void | Called when a DB tool returns structured data |
onToolCall | (name: string, args: any, result: any) => void | Called when a custom tool executes |
signal | AbortSignal | Cancel the request |
context | Record<string, any> | Extra context injected into every message |
Returns Promise<ChatResult> → { text: string }
onStatus fires with these step values:
"connecting" — connecting to the database"schema" — discovering schema"thinking" — AI is reasoning"querying" — executing a DB tool"done" — completeclient.getSchema()const schema = await agent.getSchema();
// CollectionSchema[]
The SDK automatically detects the current page from window.location on every message. The agent knows what page the user is on, what record they're viewing, and any relevant query params — with zero developer code.
URL: /dashboard/orders/ord_123?tab=details
Auto-detected context:
currentPage: "orders"
currentPath: "/dashboard/orders/ord_123"
currentRecordId: "ord_123"
param_tab: "details"
The user can now say "show me this order" and the agent knows to query ord_123.
You can also pass additional context that merges on top of the auto-detected values:
await agent.chat(messages, {
context: {
userId: "user_123",
userRole: "admin",
orgName: "Acme Corp",
}
});
To disable auto-detection:
const agent = new SyncAgentClient({
apiKey: "sa_your_key",
connectionString: process.env.DATABASE_URL,
autoDetectPage: false, // only use manually passed context
});
Context is sent as a dedicated field in the request body and injected into the AI system prompt — not appended to the user's message text.
onData callbackReact to structured query results in your own UI:
const agent = new SyncAgentClient({
apiKey: "sa_your_key",
connectionString: process.env.DATABASE_URL,
});
await agent.chat(messages, {
onData: (data) => {
// data.tool — "query_documents" | "aggregate_documents"
// data.collection — e.g. "orders"
// data.data — array of result rows
// data.count — number of results
console.log(`Got ${data.count} rows from ${data.collection}`);
setTableData(data.data); // update your own table component
}
});
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 };
},
},
},
});
When building a SaaS app, multiple organizations share the same database. Pass filter to scope every agent operation to the current user's organization. The agent cannot query, insert, update, or delete outside this scope — it's enforced server-side.
import { SyncAgentClient } from "@syncagent/js";
// Scope to the current user's organization
const agent = new SyncAgentClient({
apiKey: "sa_your_key",
connectionString: process.env.DATABASE_URL,
filter: { organizationId: currentUser.orgId },
});
// Every query is now automatically scoped:
// "Show all orders" → db.orders.find({ organizationId: "org_123" })
// "Count users" → db.users.countDocuments({ organizationId: "org_123" })
// "Add a product" → db.products.insertOne({ ...doc, organizationId: "org_123" })
Without filter — agent queries everything (suitable for single-tenant apps or admin tools).
With filter — every read, write, update, and delete is strictly scoped. The agent is told about the scope in its system prompt and cannot override it.
Common patterns:
// By organization ID
filter: { organizationId: org.id }
// By tenant slug
filter: { tenant: "acme-corp" }
// By user ID (personal data)
filter: { userId: currentUser.id }
// Multiple fields
filter: { orgId: org.id, deleted: false }
// SQL databases (same syntax)
filter: { tenant_id: tenant.id }
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 email all of them" });
const r2 = await agent.chat(history);
const controller = new AbortController();
agent.chat(messages, { signal: controller.signal });
controller.abort(); // cancel at any time
import type {
SyncAgentConfig, Message, ChatOptions, ChatResult,
CollectionSchema, SchemaField, ToolDefinition, ToolParameter, ToolData,
} from "@syncagent/js";
MIT
Use operations to give different users different levels of access within the same project. The client can only restrict further — it can never grant more than what the project dashboard allows.
// Read-only for regular users
const agent = new SyncAgentClient({
apiKey: "sa_your_key",
connectionString: process.env.DATABASE_URL,
filter: { organizationId: currentUser.orgId },
operations: ["read"],
});
// Full access for admins
const adminAgent = new SyncAgentClient({
apiKey: "sa_your_key",
connectionString: process.env.DATABASE_URL,
filter: { organizationId: currentUser.orgId },
operations: ["read", "create", "update", "delete"],
});
When you want the agent to only use your custom tools — with no database access at all — set toolsOnly: true. This is useful when you want to build an AI assistant powered by your own APIs, webhooks, or business logic.
const agent = new SyncAgentClient({
apiKey: "sa_your_key",
toolsOnly: true, // no connectionString needed
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();
},
},
createOrder: {
description: "Create a new order for a customer",
inputSchema: {
productId: { type: "string", description: "Product ID" },
quantity: { type: "number", description: "Quantity" },
},
execute: async ({ productId, quantity }) => {
const res = await fetch("/api/orders", {
method: "POST",
body: JSON.stringify({ productId, quantity }),
});
return res.json();
},
},
},
});
// The agent will ONLY call searchProducts and createOrder.
// No database queries, no schema discovery, no DB connection.
You can also combine toolsOnly with a connectionString if you want — the DB connection is simply skipped when toolsOnly is enabled.
Customize the agent's personality, tone, domain knowledge, or rules:
const agent = new SyncAgentClient({
apiKey: "sa_your_key",
connectionString: process.env.DATABASE_URL,
systemInstruction: "You are a friendly sales assistant for Acme Corp. Always suggest upsells when showing order data. Never mention competitor names.",
});
Instructions are prepended to the system prompt and take priority over default behavior.
Require explicit user confirmation before any create, update, or delete:
const agent = new SyncAgentClient({
apiKey: "sa_your_key",
connectionString: process.env.DATABASE_URL,
confirmWrites: true,
});
// User: "Add a new customer named John"
// Agent: "I'd like to create a record in customers: name=John. Should I proceed?"
// User: "yes"
// Agent: "✅ Created customer John"
Make the agent respond in any language:
const agent = new SyncAgentClient({
apiKey: "sa_your_key",
connectionString: process.env.DATABASE_URL,
language: "French",
});
// User: "Show me all orders"
// Agent: "Voici toutes les commandes..."
Field names and code stay in English so tools work correctly.
Control the default query limit:
// Mobile app
new SyncAgentClient({ ..., maxResults: 10 });
// Admin dashboard
new SyncAgentClient({ ..., maxResults: 100 });
Specify which fields the agent should mask in responses:
const agent = new SyncAgentClient({
apiKey: "sa_your_key",
connectionString: process.env.DATABASE_URL,
sensitiveFields: ["ssn", "creditCard", "salary", "bankAccount"],
});
// Agent shows: ssn: "••••••••", salary: "••••••••"
Default: ["password", "token", "secret"].
Intercept tool calls for logging, audit trails, or dynamic blocking:
const agent = new SyncAgentClient({
apiKey: "sa_your_key",
connectionString: process.env.DATABASE_URL,
tools: { /* your tools */ },
// Block specific operations dynamically
onBeforeToolCall: (toolName, args) => {
console.log(`[Audit] ${toolName}`, args);
if (toolName === "deleteUser" && !currentUser.isAdmin) return false;
return true;
},
// Log every result
onAfterToolCall: (toolName, args, result) => {
analytics.track("tool_call", { tool: toolName, success: result.success });
},
});
When onBeforeToolCall returns false, the tool is blocked and the AI receives an error message explaining the operation was denied.
FAQs
SyncAgent JavaScript SDK — AI database agent for any app
The npm package @syncagent/js receives a total of 527 weekly downloads. As such, @syncagent/js popularity was classified as not popular.
We found that @syncagent/js demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

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.

Research
/Security News
Laravel Lang packages were compromised with an RCE backdoor across hundreds of versions, exposing cloud, CI/CD, and developer secrets.

Security News
Socket found a malicious postinstall hook across 700+ GitHub repos, including PHP packages on Packagist and Node.js project repositories.

Security News
Vibe coding at scale is reshaping how packages are created, contributed, and selected across the software supply chain