@q5m-ai/sdk
v0.5.1
Published
Auth + data + sessions SDK for q5m-backed apps.
Readme
@q5m-ai/sdk
Auth + data client for q5m-backed apps. Stable surface over the platform's internals.
pnpm add @q5m-ai/sdk
# or: npm install @q5m-ai/sdkQuickstart
import { createQ5MClient } from "@q5m-ai/sdk";
const q5m = createQ5MClient({ appSlug: "wealth-desk" });
await q5m.auth.signInWithMagicLink({ email: "[email protected]" });
// (user clicks the link, returns to your app)
await q5m.data.set({
scope: "user",
agentId: "wealth-desk",
key: "profile",
content: { name: "Erik", currency: "CAD" },
});
const row = await q5m.data.get({
scope: "user",
agentId: "wealth-desk",
key: "profile",
});That's it. Auth, persistence, done.
React
import { createQ5MClient } from "@q5m-ai/sdk";
import { Q5MProvider, useSession, useData } from "@q5m-ai/sdk/react";
const q5m = createQ5MClient({ appSlug: "wealth-desk" });
function App() {
return (
<Q5MProvider client={q5m}>
<Dashboard />
</Q5MProvider>
);
}
function Dashboard() {
const { session, loading } = useSession();
const { row } = useData({ scope: "user", agentId: "wealth-desk", key: "profile" });
if (loading) return <div>Loading…</div>;
if (!session) return <SignIn />;
return <pre>{JSON.stringify(row?.content, null, 2)}</pre>;
}Reference
createQ5MClient(opts)
| Option | Required | Default |
|---|---|---|
| appSlug | yes | — |
| apiUrl | no | https://api.q5m.ai |
q5m.auth
signUp({ email, password? })
signInWithPassword({ email, password })
signInWithMagicLink({ email, redirectTo? })
signInWithGoogle({ redirectTo? })
signOut()
resetPassword({ email, redirectTo? })
getSession() / getUser()
onAuthStateChange(cb)Passwordless signUp (no password) sends a magic link. signInWithGoogle
uses the app's own Google OAuth client if one is registered on
apps/{slug}/app.yaml, otherwise the shared q5m client.
q5m.data
Polymorphic document store backed by platform.data. Read
docs/data-guide.md for the operational manual
(shape choice, scopes, joins, indexing).
ADR-0007
covers the architectural decision.
q5m.data.get({ scope, key });
q5m.data.set({ scope, key, content }); // replace
q5m.data.patch({ scope, key, patch }); // RFC 7396 merge
q5m.data.delete({ scope, key });
q5m.data.list({ scope, keyPrefix?, where?, orderBy?, limit?, offset?, count? });
// → { rows, total: number | null } (total is null unless count: "exact")Scopes — scope plus required fields:
user→agentId. Per (user × agent × key). Most common.group→agentId. Per (group × agent × key).groupIddefaults to the active group.group_user→agentId. Per (group × user × agent × key).general→ none. Cross-cutting per-user.agent→agentId. Agent-global; reads by any authenticated user, writes service-only.session→sessionId. Per-session ephemeral.
where operators: eq (default), neq, gt, gte, lt, lte, in, like.
where: {
"content.confidence": "high",
"content.match_date": { gte: "2026-06-01" },
}Dot-notation paths target JSONB columns; plain names pass through to columns.
q5m.skills
Skills layer over platform.data: authored instructional content. The
default skill is instructions (the agent's full playbook); other skills
are use-case workflows. See
ADR-0014.
q5m.skills.load({ agentId, name, groupId? }); // scope cascade → string | null
q5m.skills.list({ agentId }); // → string[] (default first)
q5m.skills.save({ agentId, name, content, scope?, groupId? }); // write override
q5m.skills.strReplace({ agentId, name, oldStr, newStr, scope?, groupId? }); // unique-substring edit
q5m.skills.insert({ agentId, name, insertLine, insertText, scope?, groupId? }); // line insert
q5m.skills.delete({ agentId, name, scope?, groupId? }); // revert to built-in
q5m.skills.rename({ agentId, name, newName, scope?, groupId? }); // rename an overrideload resolves with a scope cascade: user, then group, then agent, first
hit wins, so a user/group skill layers over the agent-scope built-in.
list returns the agent's built-in skill names with the default
(instructions) first.
The mutations (save, strReplace, insert, delete, rename) operate
on the writable override scope: user by default, or group. They never
touch the agent-scope built-in. save upserts. strReplace requires
oldStr to occur exactly once. insert adds text after insertLine
(0-indexed; 0 = beginning). delete removes the override so load falls
back to the built-in. rename moves an override and refuses to overwrite
an existing destination.
Scopes: scope is user (default) or group. A group mutation
requires an active group or an explicit groupId.
q5m.memory
Persistent memory over platform.data. Anthropic memory-tool-shaped:
path-addressed CRUD (view / create / strReplace / insert /
delete / rename) over markdown content that survives across
conversations. The reserved path /profile is the user's living
markdown profile, auto-rendered into the agent's system prompt. See
ADR-0012.
q5m.memory.view({ agentId, path });
q5m.memory.create({ agentId, path, fileText });
q5m.memory.strReplace({ agentId, path, oldStr, newStr });
q5m.memory.insert({ agentId, path, insertLine, insertText });
q5m.memory.delete({ agentId, path });
q5m.memory.rename({ agentId, path, newPath });Scopes — every call takes an optional scope (matches the canonical
platform.data scope shapes):
user(default) — user × this agent. The agent's own memory of this user.group— group × this agent. Household-shared memory; install-admin gated. Requires an active group or an explicitgroupId.
There is no cross-cutting shortcut: each agent's memory tool writes only its own namespace. Cross-cutting facts about the user (name, timezone, units) go on Q's profile and are updated by talking to Q directly.
q5m.groups
list() // groups the user belongs to (with role)
setActive(groupId) / getActive()
onActiveChange(cb)
members({ groupId?, kind? }) // kind: 'human' | 'agent' | 'all'
restoreActive() // call once at app bootq5m.installs
list() // every (agentId, groupId|null) the user has installed
share({ agentSlug, groupId }) // move solo data → group context atomicallyq5m.users
me() // signed-in user's profile (incl. email)
updateMe({ displayName?, avatarUrl? })
byIds(ids[]) // Map<id, PublicProfile> — only co-members resolvableq5m.sessions
list({ groupId?, agentId? })
get(sessionId)
messages(sessionId, { limit?, before? })
create({ agentId, groupId? })
send(sessionId, content) // returns full assistant text (non-streaming, v1)@q5m-ai/sdk/react
<Q5MProvider client={q5m}>
useQ5M()
useSession() / useUser() / useMe()
useActiveGroup()
useData(input) / useDataList(input)
useUsers(ids[])Misc
mergePatch(target, patch)— RFC 7396 reference implementation, exported for client-side optimistic updates.
Versioning
@q5m-ai/sdk follows semver. Pre-1.0:
0.1.x— bug fixes.0.x.0— additions and breaking changes (allowed).1.0.0— strict semver with deprecation cycles thereafter.
Source
Lives in q5m-platform/sdk/. Schema-coupled changes ship in the same PR
as the migration. License: MIT.
