I said one thing to a chat box: save the article I'm reading into my notes, and put a reminder on my calendar to review it in three days.
It did it. The notes went into my notes app, the calendar entry into a different calendar app, and the article came from a tab open in my browser. Three programs that don't know each other, each with its own process, its own database; the thing that pulled them together to finish the job was the chat box in the middle — sanqian.
I never clicked "enable AI" in any of those apps. They just happened to be on the same line.
That line is where most of my attention has gone lately. I'm increasingly convinced of one thing: nearly every app on the desktop will want AI. Notes want summarizing and rewriting; the calendar wants to understand "clear my Wednesday afternoon"; the browser wants to read the current page out to you. But no app should build a whole AI stack just for that — wiring up a model, writing the agent loop, managing memory, scheduling tools, pausing before every risky action to wait for the user's nod. It's heavy, and it's the same work everywhere; every app rebuilding it is wasted effort. Worse, built separately, they can never work together.
sanqian is that line. This isn't on a roadmap — it's what it does today: an app connects and immediately gains a full set of AI capabilities; at the same time it puts its own capabilities on the line, for other apps, and for sanqian itself, to call.
Three things on one line
Connecting takes an SDK, and it does three things for an app.
The first is consuming. The app hands a sentence to sanqian and gets back a full agent's answer; the model, the memory, the tool scheduling, the user confirmations — all that drudgery is handled at the other end.
const sdk = new SanqianSDK({ appName: "my-notes" })
const reply = await sdk.chat("default", [
{ role: "user", content: "Summarize these few notes into one paragraph" },
])A notes app or a browser extension can grow AI in a few hours, without standing up a backend of its own.
The second is contributing. The app registers what it can do as a tool: a name, a description for the model to read, a parameter schema, and a function that does the actual work.
new SanqianSDK({
appName: "my-notes",
tools: [{
name: "search_notes",
description: "Search the user's notes by keyword",
handler: async ({ query }) => db.search(query),
}],
})The SDK connects the app to sanqian over a single WebSocket and registers these tools; when some agent needs one, sanqian calls back along that same line. A tool's name automatically takes the app's name as a prefix — search_notes is my-notes:search_notes on the line — so it no longer belongs only to notes; it becomes a capability everyone on the line can see. Beyond tools, an app can also offer "context" — handing over, say, "the note being edited right now," which sanqian fetches fresh when it needs it.
The third is where this line differs most from "every app with its own AI": apps can call each other. An app can define its own agent, and that agent's tool list can name another app's capabilities directly.
await sdk.createAgent({
agent_id: "planner",
system_prompt: "You help me plan things.",
tools: ["search_notes", "calendar:create_event"],
})Don't know what's on the line? Search it: a single searchCapabilities("scheduling") surfaces the tools the calendar app registered. A task can be handed from one app to another; and an app that's needed but not running can be woken from the line — they find each other through a local connection file, and the one that isn't running gets launched.
That opening example happened exactly this way. My one sentence was split into three parts: the browser pulled the text, notes filed it away, the calendar scheduled it — each landing on a capability some app had registered. I didn't need to know who they were. sanqian did.
One more thing, easy to miss: sanqian is on this line too. Its own chat box can use every capability on the line directly. When I talk to it, it draws on the abilities of every connected app on the machine. The more apps connect, the more useful it is; the more useful it is, the more apps want to connect.
Why the desktop, and not yet another cloud API? Because this line never leaves the machine. Apps find each other through a local file; sanqian starts and everyone connects automatically; the data stays on this machine; an app no one is using sleeps, and wakes when it's called. It resembles MCP — handing tools to a model — but goes further: it's bidirectional, an app both offers capabilities and consumes them, both injects context and steps into the flow; and it lets apps discover and orchestrate each other, rather than handing tools one way to a single model.
How it's built today
None of this is hypothetical — it already runs. But the way it's built today doesn't sit right with me.
sanqian's hub today is a heavy Python process: a web framework, bound to a full agent-orchestration stack, holding a database to itself. Apps connect and register tools, and the central agent calls them back. That opening example is what it produced. It works — there are just three things I keep coming back to. It's bound to Python, while the machines sanqian installs on mostly ship with Node. It's heavy: for that bit of AI, you first haul in an entire orchestration stack. And the one that matters most: every app's AI runs inside this one central process, so the moment you close it, every app loses its AI at once. For each connected app, whether it gets AI at all hangs on this single hub — which is the opposite of "every app can easily have AI."
Go one level down and it's an old problem. In a centralized system, the center is both what makes it simple and what makes it fragile. The internet of things settled this two ways long ago: one kind of network must have a coordinator — simple, but a single point; the other is distributed and self-healing — sturdy, but every node is more complex. Neither transfers cleanly here.
Computation can scatter; state can't
What I eventually understood is this: the thing to split isn't "centralized versus decentralized" — it's computation and state.
Computation — the loop where the agent thinks — can scatter completely: make it a library, drop it into each app's own process, and let each run on its own. The hub no longer has to stay up; close it, and every app keeps thinking on its own.
State — memory, settings, the things that genuinely need to be shared — can't scatter. It needs one writer and only one; otherwise several processes writing to one store at once will, sooner or later, break something. So this part still flows back to a hub — only the hub is cut down to something light that any app can bring up when it needs it.
Let me be honest: only computation scatters; that central piece of state can't be removed. It's still a single point — just changed from a heavy Python elephant into a light process that can regrow at any moment. What I can do isn't to kill it, but to make it light enough, and easy enough to rebuild, that it's no longer worth worrying about.
I'm leaving the longer-term questions for later. The memory on this machine — should it one day sync to another of your devices, or to the cloud? I'm not rushing to build sync, but every piece of data is written from day one in a shape that can be synced; the day it's actually needed, you swap the backend without going back to change the data itself.
This rewrite isn't for any new feature. That opening — three apps strung together — already works today. What I'm after is something that sounds unambitious: that after you close sanqian, your notes can still think. A good line shouldn't be the one switch no one can do without.
And it's all being rebuilt now — slowly.