# Configure LLM Reference Configure is the identity layer for AI agents. Profiles can be read and updated through two production paths: hosted phone auth for federated user-owned profiles, or server-side API-only `X-User-Id` access for unlinked developer-scoped profiles that can be linked later. Profiles contain identity, preferences, memories, connected tool data, and per-agent context. This is a reference map, not a tutorial. For the executable quickstart, read: https://configure.dev/skill.md **Canonical short index** (llmstxt.org format): https://docs.configure.dev/llms.txt **Canonical long-form (this file, mirrored):** https://docs.configure.dev/llms-full.txt --- ## Start Here - **Quickstart (coding agent playbook):** https://configure.dev/skill.md - **Hosted iframe script:** https://configure.dev/js/configure.js - **Direct HTTP API guide:** https://docs.configure.dev/guides/http-api - **Canonical HTTP API reference:** https://docs.configure.dev/reference/http-api - **OpenAPI:** https://docs.configure.dev/openapi.yaml - **Server-side users / unlinked profiles:** https://docs.configure.dev/guides/server-side-users - **Setup (developer account + keys):** `npm install configure` then `npx configure setup` - **Canonical starter:** `node_modules/configure/template/` after install — the packaged Atlas-derived shell — mirrored in the repo at https://github.com/christiansancheta/memory-link/tree/main/examples/quickstart - **Production reference:** Atlas source at https://github.com/christiansancheta/memory-link/tree/main/apps/agents/atlas or live at https://atlas.configure.dev - **Documentation:** https://docs.configure.dev --- ## Integration Decision Use one of these paths: | Path | Use when | Identity | Main limitation | |------|----------|----------|-----------------| | Hosted federated auth | You need user-owned identity, connected tools, or cross-agent sharing | Phone-linked Configure user; app receives agent-scoped token | Requires hosted Configure UI at auth/approval time | | API-only unlinked profile | You have stable user IDs and want profile reads/writes without Configure in the hot path | Developer-scoped profile from server-side `sk_...` + `X-User-Id` | Other developers' agents cannot read it; connected tools require linking later | | Batch seed | You are importing existing users | Developer-scoped unlinked profiles | Link later with `externalId` for federation | Unlinked profiles are valid production profiles. They support `profile.get()`, `profile.remember()`, `profile.ingest()`, and CFS reads/writes through the SDK or raw HTTP API. They become federated when the user completes `Configure.auth({ externalId: '' })`. --- ## API-Only Profile Path Use this when the app should not render Configure UI yet. This path is server-side and requires `sk_...`. ```typescript import { ConfigureClient } from 'configure'; const client = new ConfigureClient({ apiKey: process.env.CONFIGURE_API_KEY, agent: process.env.CONFIGURE_AGENT, userId: 'your-internal-user-id', }); const profile = await client.profile.get(); await client.profile.remember(undefined, undefined, 'Prefers concise answers'); await client.profile.ingest(undefined, undefined, { text: 'Known CRM or onboarding profile text', sync: true, }); ``` Raw HTTP equivalent: ```bash curl "https://api.configure.dev/v1/memory/profile?user_id=your-internal-user-id" \ -H "X-API-Key: sk_..." \ -H "X-User-Id: your-internal-user-id" ``` The first request creates `(developer_account, external_id)` in `external_user_mappings` and resolves it to an internal UUID. The external ID string is never used directly as a storage path. Unlinked profile tradeoffs: - visible only to agents under the same developer account - invisible to other developers' agents until phone linking - no Gmail, Calendar, Drive, or Notion connections until hosted auth/OAuth - reads are metered at the configured unlinked-user premium multiplier Link later: ```js Configure.auth({ el, publishableKey: 'pk_...', agent: 'your-agent', externalId: 'your-internal-user-id', theme: 'light' }); ``` After phone verification, Configure merges the unlinked profile into the federated profile. --- ## Hosted Federated Path The production browser integration uses `https://configure.dev/js/configure.js`. This script mounts Configure-owned iframes inline into the developer's page. Sensitive surfaces (auth, profile, connections) render on Configure's origin. The parent app cannot edit their contents. ```html ``` ### Available surfaces ```js // All hosted surfaces also accept optional theme: 'light' | 'dark' // Auth + approval — returns agent-scoped token Configure.auth({ el, publishableKey, agent, theme }) // Sensitive surfaces — require token + userId from auth Configure.profileEditor({ el, publishableKey, token, userId, agent }) Configure.connections({ el, publishableKey, token, userId, agent, tools }) Configure.singleConnector({ el, publishableKey, token, userId, agent, tool, message, name }) Configure.accessRequest({ el, publishableKey, token, userId, agent, tool, message, description }) Configure.memoryImport({ el, publishableKey, token, userId, agent }) // Display/interactive surfaces Configure.memoryCard({ el, publishableKey, agent, memories }) Configure.confirmation({ el, publishableKey, agent, message, confirmLabel, cancelLabel }) Configure.toolApproval({ el, publishableKey, agent, tool, params, actionId, timeoutSeconds }) ``` Every method returns a `ConfigureFrame` with `{ id, iframe, destroy(), close(), refresh() }`. **Always pass `theme: 'light' | 'dark'`** to every hosted method (`Configure.auth()`, `Configure.connections()`, `Configure.memoryImport()`, etc.). There are not separate light-mode and dark-mode component libraries — the same hosted surfaces accept a `theme` option that controls iframe appearance. If you omit it, the SDK falls back to the user's OS-level `prefers-color-scheme` and emits a console warning. When the resolved theme mismatches your host shell, the iframe content renders with broken contrast (cards look empty, text washes out, buttons appear disabled). The starter shell supports a page light/dark toggle but defaults hosted Configure surfaces to `theme: 'light'` in both modes because auth currently renders more reliably there. Only opt into hosted `theme: 'dark'` after verifying auth and tool surfaces in a real browser. ### Auto-mount (paste-only path) ```html
``` The script auto-mounts auth into elements with the `data-configure-auth` attribute. Also supports `data-agent-name` and `data-agent-logo` for display branding. ### Optional display branding All hosted surface methods accept optional `agentName` and `agentLogo` fields: ```js Configure.auth({ el, publishableKey, agent: 'atlas', theme: 'light', agentName: 'Atlas', // Display name shown in linking header and copy agentLogo: 'https://...' // Logo URL — default globe icon if omitted }); ``` These fields are display-only. They do not affect token scope, backend agent identity, or auth semantics. If omitted, the agent slug is humanized for display (e.g. `"atlas-travel"` → `"Atlas Travel"`). Raw `` Lit components are internal to Configure-owned iframe surfaces. They are not the production integration path. See "Internal Components (Reference)" below. When a coding model is wiring Configure into an app, it should start from the canonical template if the project does not already have a strong shell. It should preserve the chat-first shell: one main conversation column, one composer, hosted auth inline as the first assistant step, and hosted Configure surfaces inline in the conversation flow or nearby chat controls. It should change only `.env`, `public/brand.css`, `public/brand.js`, and backend prompt/tool behavior unless the app already has a strong shell. It should not invent a new shell, build a feature-grid landing page, move hosted Configure surfaces into detached side rails, or treat hosted components as the whole product. --- ## Environment Variables | Variable | Value | Where | |----------|-------|-------| | `CONFIGURE_API_KEY` | `sk_...` | Server only. Never in browser code. | | `CONFIGURE_PUBLISHABLE_KEY` | `pk_...` | Browser/embed safe. Used in `Configure.auth()` and other hosted methods. | | `CONFIGURE_AGENT` | Agent name string | Both server and browser. Determines write namespace. | | `MODEL_PROVIDER` | `anthropic`, `openai`, or `openrouter` | Server only. Used by the quickstart provider adapter. | | `MODEL_NAME` | Provider model name | Server only. Defaults depend on provider. | | Provider key | `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, or `OPENROUTER_API_KEY` | Server only. Configure does not provide the LLM. | --- ## Browser Events Events dispatched on the container element and `document` after hosted iframe interactions. ### Auth | Event | Payload | When | |-------|---------|------| | `configure:authenticated` | `{ token: string, userId: string }` | User completed phone OTP + hosted approval. Token is agent-scoped. | ### Lifecycle (all surfaces) | Event | Payload | When | |-------|---------|------| | `configure:error` | `{ code?: string, message: string }` | Hosted surface encountered an error. Codes: `session_required`, `agent_token_unavailable`, `token_wrong_type`, `approval_required`, `token_invalid`. | | `configure:closed` | `{ surface: string }` | Surface closed (modal dismiss or explicit close). | ### Profile | Event | Payload | When | |-------|---------|------| | `configure:profile-approved` | `{ userId: string, agent: string }` | User approved profile access for this agent. | ### Connections | Event | Payload | When | |-------|---------|------| | `configure:tool-connect` | `{ tool: string, connected: true }` | User connected a tool (Gmail, Calendar, etc.). | | `configure:tool-disconnect` | `{ tool: string }` | User disconnected a tool. | | `configure:tool-error` | `{ tool: string, error: string }` | Tool connection/disconnection failed. | ### Memory import | Event | Payload | When | |-------|---------|------| | `configure:import-start` | `{ provider: string, stageId: string }` | Chat history import started. | | `configure:import-complete` | `{ provider: string, source: string }` | Chat history import finished. | | `configure:import-error` | `{ message: string, phase: string }` | Chat history import failed. | ### Confirmation | Event | Payload | When | |-------|---------|------| | `configure:confirmed` | `{ confirmed: true }` | User confirmed a confirmation dialog. | | `configure:cancelled` | `{ confirmed: false }` | User cancelled a confirmation dialog. | ### Tool approval | Event | Payload | When | |-------|---------|------| | `configure:tool-approve` | `{ actionId: string }` | User approved a tool action. | | `configure:tool-deny` | `{ actionId: string }` | User denied a tool action. | | `configure:tool-always-allow` | `{ actionId: string }` | User chose to always allow this tool action. | ### Memory card | Event | Payload | When | |-------|---------|------| | `configure:memory-edit` | `{ key: string }` | User requested to edit a memory. | | `configure:memory-delete` | `{ key: string }` | User requested to delete a memory. | ### Access request | Event | Payload | When | |-------|---------|------| | `configure:access-granted` | `{ tool: string, agent: string }` | User granted access to the requested resource. | | `configure:access-denied` | `{ tool: string, agent: string }` | User denied access to the requested resource. | --- ## Token And Permission Model - **User-scoped JWT** stays inside Configure-owned iframes. It is never exposed to the integrator's app. - **Agent-scoped JWT** is the only token posted to the parent app via `configure:authenticated`. - The parent app sends the agent-scoped token to its backend. The backend uses `CONFIGURE_API_KEY` (`sk_...`) for server-side SDK calls. - **API-only unlinked users** use no user JWT. The backend sends `sk_...` plus `X-User-Id`; Configure resolves it inside the developer account and marks the request as developer-scoped. - **Approval gate:** the hosted approval flow must complete before an agent-scoped token is issued. `agent_subjects.approved_at` must be set. - **`/v1/me/*` routes** accept only user-scoped JWTs (inside Configure-owned surfaces). - **Agent runtime routes** accept only agent-scoped JWTs with matching agent. - **Profile API routes** accept either matching agent-scoped JWTs for federated users or server-side `sk_...` + `X-User-Id` for unlinked users. - **Tool connection/search/action routes** require federated users with agent-scoped tokens; unlinked users cannot use connected tools until linked. Recovery: - `403 token_wrong_type` — using the wrong token type. Browser should send agent token from `configure:authenticated`. - `403 approval_required` — user has not completed hosted approval. Reopen `Configure.auth()`. --- ## Server-Side Usage For federated users, the backend receives the agent-scoped token from the browser and uses `CONFIGURE_API_KEY` for SDK calls. ```typescript import { ConfigureClient, CONFIGURE_TOOLS } from 'configure'; const client = new ConfigureClient(process.env.CONFIGURE_API_KEY, { agent: process.env.CONFIGURE_AGENT, }); // Read user profile with agent-scoped token const profile = await client.profile.get(token, userId, { sections: ['identity', 'summary', 'integrations'], }); const context = profile.format({ guidelines: true }); ``` For API-only unlinked users, configure the server-side client with your stable external user ID and omit the token: ```typescript const client = new ConfigureClient({ apiKey: process.env.CONFIGURE_API_KEY, agent: process.env.CONFIGURE_AGENT, userId: 'your-internal-user-id', }); const profile = await client.profile.get(); await client.profile.remember(undefined, undefined, 'Prefers dark mode'); ``` For the full agentic loop (CONFIGURE_TOOLS, tool execution, memory extraction), see the reference implementation and the Tool Definitions section below. `CONFIGURE_API_KEY` (`sk_...`) is server-only. `CONFIGURE_PUBLISHABLE_KEY` (`pk_...`) is browser-only. Never swap them. --- ## Setup And CLI - **Install:** `npm init -y` if needed, then `npm install configure` - **CLI:** `npx configure setup` — opens browser setup, polls for completion, writes `.env` - **Browser fallback:** https://configure.dev/login — use only when the CLI cannot run interactively - **Coding agent rule:** if Configure keys are missing, install the package and run `npx configure setup`. Do not implement raw phone OTP provisioning. ### Setup recovery - If hosted setup says `agent already exists`, choose the existing agent if it already belongs to this project or rerun setup with a unique agent slug. - If hosted setup says `Display name not defined`, fill in a non-empty display name before submitting again. - If agent ownership does not line up, sign in with the same Google or GitHub account that owns the existing agent. - If the browser session is stale or the hosted form gets wedged, rerun `npx configure setup` to generate a fresh login URL. - If local behavior looks older than the current docs, update `configure` first and rerun setup. --- ## Reference Implementation The canonical starter is the packaged Configure template at `node_modules/configure/template/`. The repo mirror lives at `examples/quickstart/`. This starter is the Atlas-derived reference path for new integrations: hosted auth, streaming, profile context, the agentic tool loop, hosted UI surfaces, and background profile ingestion inside a fixed shell with inline Configure placement. Do not substitute a smoke-test page or raw component playground when the goal is to build a real agent experience. Start from the packaged template and customize it. Canonical template files: - `template/server.mjs` in the installed package, mirrored by https://github.com/christiansancheta/memory-link/blob/main/examples/quickstart/server.mjs - `template/public/index.html` in the installed package, mirrored by https://github.com/christiansancheta/memory-link/blob/main/examples/quickstart/public/index.html Atlas is the fuller production reference: - https://github.com/christiansancheta/memory-link/blob/main/apps/agents/atlas/server.ts — Full agent server with SSE streaming, conversation persistence, tool status events - https://github.com/christiansancheta/memory-link/blob/main/apps/agents/atlas/public/index.html — Full agent frontend with richer product shell behavior - Live: https://atlas.configure.dev --- ## Package Migration - Use `configure` (the npm package) for all new integrations. - `@configure-ai/sdk` is deprecated. Migrate to `configure`. - `@configure-ai/components` is deprecated. Browser integrations should use `https://configure.dev/js/configure.js` and the hosted iframe API (`Configure.auth()`, etc.). - The `configure` npm package ships SDK + CLI artifacts plus the canonical agent template under `template/`. Raw Lit components are internal to Configure-owned hosted iframe surfaces. --- ## Package ```bash npm install configure ``` Import paths: | Path | Contents | |------|----------| | `'configure'` | ConfigureClient, CONFIGURE_TOOLS, CONFIGURE_GUIDELINES, errors, types | | `'configure/tool-definitions'` | Tool definitions only (CONFIGURE_TOOLS, OPT_IN_TOOLS, UI_TOOLS, utilities) | | `'configure/types'` | TypeScript type exports | | `'configure/cfs-types'` | CFS operation types (CfsLsResult, CfsReadResult, CfsWriteResult) | --- ## ConfigureClient ```typescript import { ConfigureClient } from 'configure'; // Auto-detect from env vars (CONFIGURE_API_KEY, CONFIGURE_AGENT) const client = new ConfigureClient(); // Explicit key const client = new ConfigureClient('sk_...'); // Explicit key + options const client = new ConfigureClient('sk_...', { agent: 'my-agent' }); ``` ### ConfigureClientOptions ```typescript interface ConfigureClientOptions { apiKey?: string; // sk_ key (alternative to constructor arg) baseUrl?: string; // default: 'https://api.configure.dev' agent?: string; // agent name — determines write namespace userId?: string; // external user ID (sends X-User-Id header; requires sk_) timeout?: number; // request timeout in ms fetch?: typeof fetch; // custom fetch implementation } ``` ### Module accessors ```typescript client.auth // AuthModule client.profile // ProfileModule client.tools // ToolsModule client.self // SelfModule client.peer(name) // PeerModule (for cross-agent reads) ``` --- ## AuthModule (Server / Headless Reference) These methods are **not** for production browser auth. Production browser auth uses `Configure.auth()` from the hosted iframe script, which owns the entire OTP flow inside a Configure-origin iframe. The SDK auth methods below exist for trusted server-side or headless contexts only. ```typescript // Send OTP to phone (server/headless only) await client.auth.sendOtp(phone: string): Promise<{ ok: true }> // Verify OTP — returns auth credentials (server/headless only) await client.auth.verifyOtp( phone: string, code: string, options?: { agent?: string } ): Promise<{ token: string; userId: string }> ``` Do not build browser OTP forms using these methods. Do not proxy these through your backend for browser auth. The hosted iframe handles phone input, OTP delivery, verification, profile approval, and agent-scoped token minting — all on Configure's origin. --- ## ProfileModule ### get() ```typescript await client.profile.get( authToken?: string, userId?: string, options?: { path?: string; sections?: Array<'identity' | 'preferences' | 'summary' | 'agents' | 'integrations'>; } ): Promise ``` Sections control what data is fetched: | Section | Returns | |---------|---------| | `identity` | Name, email, phone_last4, occupation, location, bio, interests | | `preferences` | Array of preference strings | | `summary` | User summary markdown (from /user.md) | | `agents` | Per-agent memories: `{ [agentName]: { memories: [{ date, content }] } }` | | `integrations` | Tool connection state + data: gmail, calendar, drive, notion | Default: all sections. Federated example: ```typescript const profile = await client.profile.get(token, userId); ``` Unlinked API-only example: ```typescript const client = new ConfigureClient({ apiKey: 'sk_...', agent: 'my-agent', userId: 'external-123' }); const profile = await client.profile.get(); ``` ### format() ```typescript profile.format(options?: { includeTools?: boolean; // include tool data (Gmail emails, calendar events, etc.) — default: false guidelines?: boolean; // append CONFIGURE_GUIDELINES — default: true }): string ``` Returns a prompt-ready string. Inject into your system prompt. ### remember() ```typescript await client.profile.remember( authToken?: string, userId?: string, fact: string ): Promise ``` Save a specific fact about the user. ### ingest() — single user ```typescript await client.profile.ingest( authToken: string, userId: string, messages: ConversationMessage[], options?: { memoryCriteria?: string; sync?: boolean } ): Promise ``` Extract memories from a conversation. `sync: false` (default) returns immediately. `sync: true` waits for extraction to complete. ### ingest() — text mode ```typescript await client.profile.ingest( authToken: string, userId: string, options: { text: string; memoryCriteria?: string; sync?: boolean } ): Promise ``` ### ingest() — batch ```typescript await client.profile.ingest( users: IngestUserEntry[], options?: { memoryCriteria?: string } ): Promise ``` Bulk ingest for multiple users. Each entry: `{ userId: string; messages?: ConversationMessage[]; conversations?: { messages: ConversationMessage[] }[]; text?: string }`. ### getMemories() ```typescript await client.profile.getMemories( authToken?: string, userId?: string, options?: Record ): Promise<{ memories: any }> ``` ### isLinked() ```typescript await client.profile.isLinked( authToken?: string, userId?: string ): Promise ``` Check if a user is linked to a federated phone identity. ### generateDocuments() ```typescript await client.profile.generateDocuments( authToken?: string, userId?: string ): Promise ``` ### ls() ```typescript // List profile manifest (no path) await client.profile.ls(authToken?, userId?): Promise // List CFS directory contents await client.profile.ls(authToken?, userId?, path: string, opts?: CfsLsOptions): Promise ``` ### read() ```typescript await client.profile.read( authToken?: string, userId?: string, path?: string ): Promise ``` ### write() ```typescript await client.profile.write( authToken?: string, userId?: string, path?: string, content?: string, options?: CfsWriteOptions ): Promise ``` Write modes: `overwrite` (default), `append`, `merge` (shallow JSON merge). Federated users can write only under `/agents/{resolved-agent}/`. Unlinked users resolved through server-side `sk_...` + `X-User-Id` may also be managed at root profile paths such as `/identity.json` because the profile is developer-scoped until linked. ### search() ```typescript await client.profile.search( authToken?: string, userId?: string, query?: string, options?: CfsSearchOptions ): Promise ``` ### rm() ```typescript await client.profile.rm( authToken?: string, userId?: string, path?: string ): Promise ``` --- ## Profile Seeding Profiles start empty after first authentication or first `X-User-Id` access. Seeding fills them with data before the first conversation. **Tool connect (richest, ~10s):** `client.tools.connect(token, 'gmail')` → user completes OAuth → profile auto-populates with identity, email insights, contacts. Hosted: `Configure.connections({ el, publishableKey, agent, token, userId, tools: "gmail,calendar,drive,notion" })` **Memory import (user-driven, ~30s):** Hosted: `Configure.memoryImport({ el, publishableKey, agent, token, userId })` Events: `configure:import-start`, `configure:import-complete` **Conversation ingest (~5s):** `client.profile.ingest(token, userId, messages)` — fire-and-forget. With criteria: `client.profile.ingest(token, userId, messages, { memoryCriteria: 'dietary preferences, travel style' })` **Batch seed (migration):** `client.profile.ingest([{ userId, messages }], options)` — up to 50 users. Text mode: `client.profile.ingest(token, userId, { text: 'User bio from CRM...' })` Batch mode creates or updates unlinked developer-scoped profiles and does not need per-user tokens. **Manual remember (instant):** `client.profile.remember(token, userId, 'Prefers dark mode')` --- ## ToolsModule Tool APIs require a federated approved user and an agent-scoped Bearer token from `Configure.auth()`. Unlinked `X-User-Id` profiles can use profile APIs, but they cannot connect Gmail, Calendar, Drive, or Notion, or call tool search/action endpoints until linked. ### Connection management ```typescript await client.tools.list(authToken?: string): Promise await client.tools.connect(authToken: string, tool: ToolType, options?: { redirectUrl?: string }): Promise await client.tools.confirm(authToken: string, tool: ToolType, code: string): Promise await client.tools.sync(authToken: string, tool: ToolType): Promise await client.tools.syncAll(authToken?: string): Promise await client.tools.disconnect(authToken: string, tool: ToolType): Promise await client.tools.disconnectAll(authToken?: string): Promise ``` `ToolType = 'gmail' | 'calendar' | 'drive' | 'notion'` ### Live search ```typescript await client.tools.searchEmails(authToken: string, userId: string, query: string, options?: SearchOptions): Promise await client.tools.getCalendar(authToken: string, userId: string, range: 'today' | 'tomorrow' | 'week' | 'month'): Promise await client.tools.searchFiles(authToken: string, userId: string, query: string, options?: SearchOptions): Promise await client.tools.searchNotes(authToken: string, userId: string, query: string, options?: SearchOptions): Promise await client.tools.searchWeb(authToken: string, userId: string, query: string, options?: SearchOptions): Promise await client.tools.fetchUrl(authToken: string, userId: string, url: string): Promise ``` `SearchOptions = { maxResults?: number }` ### Actions ```typescript await client.tools.createCalendarEvent(authToken: string, userId: string, event: { title: string; startTime: string; endTime: string; description?: string; location?: string; }): Promise await client.tools.sendEmail(authToken: string, userId: string, email: { to: string; subject: string; body: string; }): Promise ``` --- ## SelfModule (Agent Storage) Agent's own CFS namespace. No auth token required — uses API key identity. ```typescript await client.self.ls(path?: string, opts?: CfsLsOptions): Promise await client.self.read(path: string): Promise await client.self.write(path: string, content: string, opts?: CfsWriteOptions): Promise await client.self.search(query: string, opts?: CfsSearchOptions): Promise await client.self.rm(path: string): Promise await client.self.getProfile(opts?: { sections?: string[] }): Promise await client.self.getMemories(opts?: { agent?: string }): Promise await client.self.remember(fact: string): Promise ``` --- ## PeerModule (Agent CFS Reads) Read another agent entity's own CFS, subject to the target agent's `/permissions` node. This does not read a user's profile. User profile cross-agent visibility is handled by `profile.get()` / `profile.getMemories()` and requires either a federated approved user or the same developer-scoped unlinked user. ```typescript const peer = client.peer('other-agent'); await peer.read(path: string): Promise await peer.ls(path?: string, opts?: CfsLsOptions): Promise await peer.search(query: string, opts?: CfsSearchOptions): Promise ``` --- ## Tool Definitions ```typescript import { CONFIGURE_TOOLS, OPT_IN_TOOLS, UI_TOOLS, toOpenAIFunctions } from 'configure'; ``` ### CONFIGURE_TOOLS (16 tools — Anthropic format) **Profile tools:** - `get_profile` — Get the user's full profile - `profile_read` — Read a specific CFS path - `profile_ls` — List CFS directory contents - `get_memories` — Get saved memories - `remember` — Save a fact about the user - `ingest` — Extract memories from conversation messages **Self tools:** - `self_get_profile` — Get the agent's own profile - `self_get_memories` — Get the agent's own memories **Live data tools:** - `search_emails` — Search user's Gmail (input: `query`, `max_results?`) - `get_calendar` — Get calendar events (input: `range`: today/tomorrow/week/month) - `search_files` — Search Google Drive (input: `query`, `max_results?`) - `search_notes` — Search Notion (input: `query`, `max_results?`) - `search_web` — Search the web (input: `query`, `max_results?`) - `fetch_url` — Fetch a URL (input: `url`) **Action tools:** - `create_calendar_event` — Create a calendar event (input: `title`, `start_time`, `end_time`, `description?`, `location?`) - `send_email` — Send an email (input: `to`, `subject`, `body`) ### OPT_IN_TOOLS (6 tools) Not included by default. Append to tools array if needed: - `profile_write` — Write to user's CFS - `profile_search` — Search user's CFS - `profile_rm` — Delete from user's CFS - `self_read` — Read from agent's storage - `self_ls` — List agent's storage - `self_search` — Search agent's storage ### SELF_WRITE_TOOLS (3 tools) - `self_write` — Write to agent's storage - `self_rm` — Delete from agent's storage - `self_remember` — Save a fact to agent's storage ### UI_TOOLS (1 tool) - `show_ui_component` — Render a UI component (connection_list, single_connector, memory_card, etc.) ### Utilities ```typescript toOpenAIFunctions(tools) // Convert Anthropic tool format to OpenAI function format isUITool(toolName) // Check if a tool is a UI tool parseUIToolCall(name, input) // Parse a UI tool call into component + props getProfileTools() // Get profile tool subset getSelfTools() // Get self tool subset getSearchTools() // Get search tool subset getActionTools() // Get action tool subset getOptInTools() // Get opt-in tool subset ``` --- ## CONFIGURE_GUIDELINES Appended to `profile.format()` output by default (`guidelines: true`). ``` CONFIGURE GUIDELINES — handling personal data responsibly GROUNDING: - Only state specific facts (dates, names, amounts, locations) if they appear in the user's profile context or tool results. - Never fabricate or assume personal information. If you lack data, say so. - If the profile context seems incomplete, use search tools to get real data — do not guess. TRANSPARENCY: - When referencing personal data, briefly cite your source: "from your Gmail", "from your calendar", etc. - If the user asks how you know something, explain clearly — you found it in their connected data. - When using web search results, include source URLs so users can verify. TOOL CONNECTIONS: - When a tool returns a connection error or "not connected" status, do not echo the error. Let the user know naturally — e.g. "I'd need access to your calendar for that — would you like to connect it?" - Do not repeatedly prompt the user to connect tools they have already declined or been asked about. CONVERSATION EFFICIENCY: - Check conversation history before re-searching — you may have already retrieved the data. - If a search didn't find what the user needs, try different queries with synonyms or alternative phrasing. ``` Also available as an export: `import { CONFIGURE_GUIDELINES } from 'configure';` --- ## Internal Components (Reference) Production browser integrations use the hosted iframe API via `configure.js` (see Hosted Federated Path above). The raw `` Lit components listed below are internal to Configure-owned iframe surfaces. They are documented here for reference only. ### Internal loading Configure-owned iframe surfaces load the internal component bundle from `/components/configure-components.global.js`. Integrator code should not import or ship raw `` components directly. ### Shared attributes All components accept: | Attribute | Type | Description | |-----------|------|-------------| | `api-key` | string | Publishable key (`pk_...`) | | `base-url` | string | API base URL (default: `https://api.configure.dev`) | | `auth-token` | string | User auth token | | `user-id` | string | User ID | | `agent` | string | Agent name | | `theme` | string | `'light'` or `'dark'` | ### configure-auth Inline 3-step auth flow: phone → OTP → profile review. ### configure-auth-modal Modal 4-step auth flow: phone → OTP → tool connections → profile editor. ### configure-connection-list Tool connection UI. Shows connect/disconnect buttons for each tool. ### configure-single-connector Wrapper for connecting a single tool. ### configure-memory-import Chat history import. Supports ChatGPT, Claude, and Gemini. ### configure-profile-editor Data access review with per-field permission toggles. ### configure-tool-approval Action approval UI for email sending and calendar event creation. ### configure-memory-card Display a saved memory. ### configure-confirmation Generic confirmation dialog. ### configure-access-request Access request flow for cross-agent data. ### configure-phone-input Phone number input with E.164 formatting. Used internally by auth components. ### configure-otp-input 6-digit OTP code input. Used internally by auth components. ### configure-export-button / configure-export-prompt Export flow sub-components. Used internally by `configure-memory-import`. --- ## API Endpoints All endpoints use `https://api.configure.dev` as the base URL. **Important:** This table is the SDK-backed runtime surface only. Browser auth should still use `Configure.auth()` via `https://configure.dev/js/configure.js`, and developer setup should use `npm install configure` then `npx configure setup`, with `https://configure.dev/login` as the fallback browser path when the CLI cannot run. ### Auth headers Every request needs an agent identity (`X-API-Key`). Profile requests also need a user identity, supplied one of two ways: | Header | When | Resolves to | |---|---|---| | `X-API-Key: sk_...` | Server-side SDK/HTTP writes and most examples | Developer account + agent | | `X-API-Key: pk_...` | Browser-safe hosted surfaces and read-only allowed endpoints | Developer account + agent with restricted access | | `X-Agent: ` | Optional; required when the developer has 2+ agents | The named agent | | `Authorization: Bearer ` | Federated user (phone-verified via `Configure.auth()`) | Linked user, cross-agent profile | | `X-User-Id: ` | Unlinked user (server-side with `sk_...`, no OTP) | Developer-scoped profile, auto-approved | `X-User-Id` lets developers read/write profiles from the server for users that have never been through phone auth. It requires `sk_...`; do not put this path in browser code. The string is your own stable identifier (email, DB ID, UUID). Configure auto-creates an `external_user_mappings` row scoped to your developer account on first contact; the profile is invisible to other developers until the user federates by completing phone verification through `Configure.auth({ externalId: '' })`. Unlinked-user reads are metered at a premium rate. Tool connection/search/action endpoints require a federated agent-scoped Bearer token. They do not accept unlinked `X-User-Id` as a substitute for user OAuth consent. For agents calling Configure from a language without an official SDK (Go, Rust, Elixir, edge runtimes, shell), see https://docs.configure.dev/reference/http-api. The OpenAPI spec is https://docs.configure.dev/openapi.yaml. | Method | Path | Auth | Description | |--------|------|------|-------------| | POST | `/v1/auth/otp/start` | `sk_` or `pk_` | Send OTP | | POST | `/v1/auth/otp/verify` | `sk_` or `pk_` | Verify OTP → token + userId | | GET | `/v1/memory/profile` | `sk_`/`pk_` + token, or `sk_` + `X-User-Id` | Get user profile | | POST | `/v1/memory/remember` | `sk_` + (token or `X-User-Id`) | Save a fact | | GET | `/v1/memory/memories/query` | `sk_`/`pk_` + token, or `sk_` + `X-User-Id` | Query memories | | POST | `/v1/memory/documents/generate` | `sk_`/`pk_` + token | Regenerate documents | | POST | `/v1/memory/ingest` | `sk_` (+ token or `X-User-Id` for single-user; `sk_` alone for batch) | Ingest conversation; batch-seed unlinked users with `users[]` | | POST | `/v1/memory/search/emails` | `sk_` + token | Search Gmail | | POST | `/v1/memory/search/calendar` | `sk_` + token | Get calendar events | | POST | `/v1/memory/search/files` | `sk_` + token | Search Drive | | POST | `/v1/memory/search/notes` | `sk_` + token | Search Notion | | POST | `/v1/memory/search/web` | `sk_` + token | Search the web | | POST | `/v1/memory/actions/create-event` | `sk_` + token | Create calendar event | | POST | `/v1/memory/actions/send-email` | `sk_` + token | Send email | | POST | `/v1/tools/:tool/connect` | `sk_`/`pk_` + token | Start tool OAuth | | GET | `/v1/profile/:userId` | `sk_`/`pk_` + token, or `sk_` + `X-User-Id` | List user CFS subtree | | GET | `/v1/profile/:userId/read` | `sk_`/`pk_` + token, or `sk_` + `X-User-Id` | Read user CFS node | | GET | `/v1/profile/:userId/search` | `sk_`/`pk_` + token, or `sk_` + `X-User-Id` | Search user CFS | | PUT | `/v1/profile/:userId/write` | `sk_` + (token or `X-User-Id`) | Write user CFS node | | DELETE | `/v1/profile/:userId` | `sk_` + (token or `X-User-Id`) | Delete user CFS node | | GET | `/v1/self` | `sk_` | List agent storage | | GET | `/v1/self/read` | `sk_` | Read agent storage | | PUT | `/v1/self/write` | `sk_` | Write agent storage | | GET | `/v1/self/search` | `sk_` | Search agent storage | | DELETE | `/v1/self` | `sk_` | Delete agent storage | | GET | `/v1/agent/:name` | `sk_` | List peer agent data | | GET | `/v1/agent/:name/read` | `sk_` | Read peer agent data | | GET | `/v1/agent/:name/search` | `sk_` | Search peer agent data | --- ## Types ```typescript interface UserProfile { identity: UserIdentity; preferences: string[]; summary?: string; agents: Record>; integrations: Record; linked?: boolean; filtered?: boolean; hiddenSections?: string[]; format(options?: { includeTools?: boolean; guidelines?: boolean }): string; path?: string; data?: unknown; } interface UserIdentity { name?: string; email?: string; phone_last4?: string; occupation?: string; location?: string; bio?: string; interests?: string[]; } interface ProfileManifest { identity: { fields: string[] }; preferences: { count: number }; integrations: Record; agents: Record; } interface ConversationMessage { role: 'user' | 'assistant'; content: string; } interface IngestUserEntry { userId: string; conversations?: { messages: ConversationMessage[] }[]; messages?: ConversationMessage[]; text?: string; } type ToolType = 'gmail' | 'calendar' | 'drive' | 'notion'; interface CfsLsResult { path: string; children: { name: string; type: string }[]; } interface CfsReadResult { path: string; content: string; content_type: string; } interface CfsWriteResult { path: string; ok: boolean; } ``` --- ## Error Codes ```typescript import { ConfigureError, ErrorCode, classifyError } from 'configure'; ``` ### ConfigureError ```typescript class ConfigureError extends Error { code: ErrorCodeValue; // machine-readable (e.g., 'AUTH_REQUIRED') statusCode?: number; // HTTP status type?: string; // backend error type param?: string | null; // field that caused the error retryable?: boolean; // safe to retry? suggestedAction?: string; // e.g., 'reauthenticate', 'connect_tool' docUrl?: string; // link to docs retryAfter?: number | null; // seconds to wait (rate limits) requestId?: string; // backend request ID } ``` ### Error code table | Code | HTTP | Retryable | Description | |------|------|-----------|-------------| | `API_KEY_MISSING` | — | No | No API key provided to ConfigureClient | | `AUTH_REQUIRED` | 401/403 | No | Token missing, invalid, or expired | | `INVALID_INPUT` | 400 | No | Input failed validation | | `TOOL_NOT_CONNECTED` | 400 | No | Tool not connected for this user | | `NETWORK_ERROR` | — | Yes | DNS, connection refused, etc. | | `RATE_LIMITED` | 429 | Yes | Too many requests | | `NOT_FOUND` | 404 | No | Resource not found | | `SERVER_ERROR` | 5xx | Yes | Backend error | | `TIMEOUT` | — | Yes | Request timed out | | `ACCESS_DENIED` | 403 | No | Not authorized for this resource | | `TOOL_ERROR` | — | Yes | Tool operation failed | | `PAYMENT_REQUIRED` | 402 | No | Billing/quota limit reached | | `token_wrong_type` | 403 | No | Wrong token authority — e.g., agent token used on `/v1/me/*` or user token on agent runtime route | | `approval_required` | 403 | No | User has not completed hosted approval for this agent. `agent_subjects.approved_at` not set. | | `token_invalid` | 401 | No | Token is malformed, expired, or unrecognized | | `session_required` | — | No | No Configure-origin user session exists. Reopen `Configure.auth()`. | | `agent_token_unavailable` | — | No | Backend approval endpoint not yet available. Track 2 dependency. | ### classifyError() Universal error classifier. Works with ConfigureError, network errors, or plain strings. Returns a ConfigureError with a user-friendly message. Intended for Configure-originated errors; when used with third-party errors (e.g., Anthropic SDK), it maps to a generic category — preserve the original error source context when logging. ```typescript try { // Configure SDK calls } catch (err) { const classified = classifyError(err); console.error(classified.code, classified.message); } ``` ### Retry pattern ```typescript async function withRetry(fn: () => Promise, maxRetries = 3): Promise { for (let i = 0; i <= maxRetries; i++) { try { return await fn(); } catch (err) { if (err instanceof ConfigureError) { if (!err.retryable || i === maxRetries) throw err; const delay = err.retryAfter ? err.retryAfter * 1000 : Math.pow(2, i) * 1000; await new Promise(r => setTimeout(r, delay)); } else { throw err; } } } throw new Error('unreachable'); } ``` --- ## Advanced Topics - **Customizing surfaces:** `Configure.*()` methods accept `mode: 'modal'` for overlay presentation. Container elements can be styled for positioning. - **Tool connections:** OAuth flows for Gmail, Calendar, Drive, Notion are managed through `Configure.connections()` or `Configure.singleConnector()`. - **Memory import:** Supports ChatGPT, Claude, and Gemini history via `Configure.memoryImport()`. - **Cross-agent reads:** Use `client.peer('other-agent')` for reading another agent's data (requires federation + user permissions). - **Deployment:** Backend uses `CONFIGURE_API_KEY` as env secret. Frontend loads `configure.js` from CDN. No CORS configuration needed for hosted iframes. - **Local development:** Set `CONFIGURE_BASE_URL` to point at a local or staging backend if needed. --- △⇆📜 ⚷⛰⚵⇆∞ 👁