Stays up to date.
Keep your tools connected. Let Automations maintain what matters.
Give your agents shared context so they can coordinate, build on each other’s work, and pick up where others left off.
export default async (_ctx, client) => {
return client.connections.list({ connector_key: "google.gmail" });
};Open this and approve Gmail. One connection, scoped to Acme Co. I'll use it for customer mail, including renewal threads.
import {
ConnectorRuntime, type RuntimeConnectorDefinition,
type SyncCredentials,
} from "@lobu/connector-sdk";
type Checkpoint = { after?: string };
type Options = Record<string, never>;
type Usage = {
id: string; customer: string; week: string;
activeSeats: number; updatedAt: string;
};
// Application-specific Postgres adapter; implementation and auth omitted.
// It returns a stable cursor and only rows after the last checkpoint.
declare const usageDb: {
changes(input: { after?: string; credentials: SyncCredentials | null }):
Promise<{ rows: Usage[]; checkpoint: Checkpoint; more: boolean }>;
};
export default class AcmeUsage extends ConnectorRuntime<Checkpoint, Options> {
readonly definition: RuntimeConnectorDefinition<Checkpoint, Options> = {
key: "acme.usage", name: "Customer usage", version: "1.0.0",
feeds: {
usage: {
key: "usage", name: "Weekly active seats",
sync: async (ctx) => {
const page = await usageDb.changes({
after: ctx.checkpoint?.after, credentials: ctx.credentials,
});
await ctx.commit(page.rows.map((row) => ({
origin_id: "usage:" + ctx.feedId + ":" + row.id,
origin_type: "customer_usage",
occurred_at: new Date(row.updatedAt),
title: row.customer + " usage",
payload_text: row.activeSeats + " active seats · " + row.week,
})), page.checkpoint);
return { status: page.more ? "more" : "complete" };
},
},
},
};
}Collect selected usage changes alongside customer mail. Agents can compare weekly active seats with renewal dates and open issues.
Weekly active seats from Postgres. Each sync resumes from its saved checkpoint.
Illustrative feed excerpt. Implement the database adapter and configure its credentials before deployment.
import { defineEntityType, defineRelationshipType } from "@lobu/cli/config";
export const customer = defineEntityType({
key: "customer", name: "Customer",
properties: {
domain: { type: "string" },
accountOwner: { type: "string" },
renewalDate: { type: "string" },
openIssue: { type: "string" },
},
});
export const contact = defineEntityType({ key: "contact", name: "Contact" });
export const contactFor = defineRelationshipType({
key: "contact-for", name: "Contact For",
description: "A person is a contact for a customer.",
rules: [{ source: contact, target: customer }],
});Your agents can now work with the same customer records and relationships—not separate copies.
Domain, account owner, renewal date, and open issue. Linked to their contacts.
The people connected to each customer record.
Contacts → Customers. Shared relationships your agents can query.
Pick up where another person or agent left off.
Save findings and decisions to the workspace, then continue from another agent without copying the conversation across.
Illustrative shared-workspace handoff. No live customer data or actions.
export default async (_ctx, client) => {
return client.query(`
SELECT title, payload_text
FROM events
WHERE title = 'Meridian renewal'
ORDER BY occurred_at DESC
LIMIT 10
`);
};Ask in the agent you already have. The answer uses the same workspace. Creating a managed agent is optional.
Meridian needs attention: usage fell 40%, SEC-142 is open, and renewal is September 30. Ask Maya to review the account; churn is not confirmed.
export default async (_ctx, client) => {
const workspace = await client.org("acme-co");
const saved = await workspace.knowledge.save({
title: "Meridian renewal decision",
author: "Maya Chen",
semantic_type: "decision",
content: "I’ll review Meridian. Alex should check SEC-142 before I contact the customer.",
idempotency_key: "meridian-renewal-decision:v1",
});
return workspace.knowledge.read({ content_ids: [saved.id] });
};Saved Maya’s decision to Acme Co. Nothing has been sent to the customer.
export default async (_ctx, client) => {
const workspace = await client.org("acme-co");
return workspace.query(`
SELECT title, payload_text
FROM events
WHERE title = 'Meridian renewal decision'
ORDER BY occurred_at DESC
LIMIT 1
`);
};Maya will review Meridian. Check SEC-142 and share your findings with her before any customer follow-up.
Saved decision · Maya Chen · Slack. The decision was explicitly saved—not copied from a private chat.
export default async (_ctx, client) => {
return client.automations.create({
slug: "renewal-follow-up",
name: "Renewal follow-up",
managed_agent_id: "customer-success",
prompt: "Draft a follow-up for the account owner. Nothing is sent until someone approves it.",
triggers: [
{ kind: "schedule", cron: "0 9 * * 1-5" },
{ kind: "event", source: "workspace", event_types: ["observation"] },
],
});
};Creating the Automation. It runs on schedule — and fires immediately when a matching workspace event lands. Nothing is sent until someone approves it.
Weekday morning, plus matching workspace events. Checks the renewal date, usage, and open issues, then drafts a note for the account owner.
First run already in — an overnight event triggered it ahead of the schedule.
Meridian renewal for Maya Chen. Names the owner, cites the open issue. Waiting for approval.
Refine the workflow. Keep the history.
Review a result, update the instructions, and use the revised version on the next run.
export default async (_ctx, client) => {
const workspace = await client.org("acme-co");
const { automations } = await workspace.automations.list({});
const followUp = automations.find(item => item.slug === "renewal-follow-up");
if (!followUp) throw new Error("Create the renewal follow-up first.");
return workspace.automations.createVersion({
automation_id: followUp.id,
prompt: "Draft a follow-up for the account owner. Include the account owner and open issue. Do not predict churn. Nothing is sent until someone approves it.",
});
};Saved as version 2. The next run will use the revised instructions.
v2 · Include the account owner and open issue. Do not predict churn.
The earlier version remains in the history. This updates the renewal follow-up—not every agent in the workspace.
Lobu is an open-source workspace for people and AI agents. Connect Claude, ChatGPT, Codex or OpenClaw over MCP, and self-host it or use Lobu Cloud.
Keep your tools connected. Let Automations maintain what matters.
See the sources behind an answer and what changed along the way.
Use your existing agents and devices with the same shared context.
Connect it to the same workspace, with the access you choose, so it can build on the knowledge, decisions, and work your team has already saved.
Know an industry inside out? Partner with Lobu