Personalization for agents.
Configure gives agents a hosted sign-in flow and a server-side profile runtime. This repo keeps the two main integration paths separate:
- Web apps use the core
configureSDK. Theweb/example usespersonalize()as a small web-server convenience. - Spectrum message agents keep Spectrum as the messaging runtime and add Configure at the message boundary with
withConfigure.
Two complete examples show the main integration paths:
| Example | What it shows |
|---|---|
web/ |
A "Continue with Configure" button that redirects, exchanges a code, and reads the profile. |
message-agent/ |
A Spectrum/iMessage agent that adds Configure identity and profile context with withConfigure. |
For web apps:
npm install configureFor Spectrum message agents, use configure-spectrum with Photon's Spectrum SDK.
Web apps: personalize()
personalize() is a web convenience helper. It runs a minimal sign-in server for the hosted Configure flow, exchanges the returned code server-side, and reads the profile.
import { personalize } from "configure";
personalize({
apiKey: process.env.CONFIGURE_API_KEY!, // sk_, server-side
publishableKey: process.env.CONFIGURE_PUBLISHABLE_KEY!, // pk_, browser-safe
agent: process.env.CONFIGURE_AGENT!,
baseUrl: "http://localhost:4000",
onSignedIn: ({ profile }) => profile, // return HTML or JSON
}).listen(4000);Spectrum message agents: withConfigure()
withConfigure() is the Spectrum adapter. It wraps an existing Spectrum message handler so Spectrum continues to own messaging, providers, webhooks, and delivery. Configure resolves identity, consent, profile runtime, and memory access before the handler replies.
import { withConfigure } from "configure-spectrum";
const configureSpectrum = withConfigure({
apiKey: process.env.CONFIGURE_API_KEY!,
publishableKey: process.env.CONFIGURE_PUBLISHABLE_KEY!,
agent: process.env.CONFIGURE_AGENT!,
store: withConfigure.localStore(),
signIn: {
linkMode: "managed",
connectors: ["gmail", "calendar"],
},
});
for await (const [space, message] of app.messages) {
await configureSpectrum.handle(space, message, async (ctx) => {
const tools = ctx.profile.tools({
connectors: ["gmail", "calendar"],
actions: ["email.send", "calendar.create_event"],
});
// Give your model Configure tools and route configure_* calls to ctx.profile.executeTool.
// Tool visibility means hosted/app capability; executeTool enforces user state and recovery.
// If the turn used configure_profile_read/search, call ctx.profile.commit() with bounded evidence.
});
}Under the hood, both paths rely on the same Configure primitives: open a hosted Configure handoff, keep tokens server-side, expose Configure tools in the model loop, route Configure tool calls, and commit after read-backed turns. Web apps usually build the hosted URL directly. Spectrum message agents let the adapter own link delivery before the model handler runs.
For Spectrum iMessage, let the adapter use the current turn's routed space.phone for hosted return metadata. Do not hardcode a Photon line in the default sample path; only pass signIn.agentPhone when your app has an explicit, deterministic line binding.
const configure = new Configure({ apiKey, agent });
const url = configure.auth.signInUrl({ publishableKey, returnTo }); // hosted web/account-link handoff
const { token } = await configure.auth.exchangeSignInCode(code); // exchange (sk_)
const profile = configure.profile({ token });
const tools = profile.tools({
connectors: ["gmail", "calendar"],
actions: ["email.send", "calendar.create_event"],
});Expose Configure tools as the normal model-loop path. Keep configure_profile_read and configure_profile_search available for overview, concrete memories, imported-source questions, or details that need exact source attribution. Expose connector and action tools when the hosted/product surface requested those capabilities and the app supports them. profile.executeTool() still enforces linked state, connector state, permissions, scopes, approval, and reconnect recovery; if a connector/action is unavailable, send the hosted connect, reconnect, permissions, or approval link. After a read-backed turn, call profile.commit() or ctx.profile.commit() with bounded user/assistant turn evidence. Host-side profile.read({ sections }) plus profile.format() is available for app-owned UI, inspection, or explicit context slots; it is not required for the normal model loop.
Configure resolves the user server-side before profile access. In Spectrum message agents, withConfigure() owns the message-auth handoff so the model does not generate Configure sign-in links.
git clone https://github.com/configure-dev/configure-quickstart
cd configure-quickstart/web
cp .env.example .env # add your keys
npm install
npm run dev # → http://localhost:4000Open the page, click Continue with Configure, and you land on a page showing the profile your server just read. Get your keys with npx configure setup or from the dashboard.
For the Spectrum example:
cd ../message-agent
cp .env.example .env # add Configure + Photon keys
npm install
npm run devConfigure uses two keys with different reach. Getting this split right is the entire security model.
| Variable | Prefix | Where it lives |
|---|---|---|
CONFIGURE_API_KEY |
sk_ |
Server only. Exchanges the code and reads profiles. |
CONFIGURE_PUBLISHABLE_KEY |
pk_ |
Browser-safe. Builds the sign-in link. |
CONFIGURE_AGENT |
— | Your agent handle in Configure. |
The secret key never leaves your server. The browser only ever holds the publishable key and a one-time code; the token and every profile read stay on the backend.
Browser Your server (sk_) Configure (hosted)
─────── ───────────────── ──────────────────
Continue with Configure ───────────▶ /login ──── signInUrl() ──────▶ phone + consent
│
show profile ◀──── profile.read({ sections }) ◀── exchangeSignInCode(code) ◀────┘ redirect ?code=
In a Spectrum message agent, withConfigure() wraps the existing message handler. Spectrum owns messaging, providers, webhooks, and delivery; Configure resolves identity, consent, and profile access before the agent replies.
This repo is agent-readable. Point a coding agent at llms.txt for the whole integration in one file, or drop SKILL.md into its skills.