Recipe, OpenAI Agents SDK + Shelvia
Shelvia is the memory layer the Agents SDK calls, Shelvia does not run your agents. The Agents SDK runs them; Shelvia gives each run reviewed context and accepts new memory only through the review queue.
Principle
Treat Shelvia as a typed tool the Agents SDK can call. Reads use the memory.read or memory.context scope. Writes go through the memory.candidates scope and produce a pending review candidate, never a trusted memory row.
Setup
- Create a Workspace API token in Settings > API with scopes: memory.read + memory.context (read-only run) OR + memory.candidates (run that proposes new memory).
- Store the token in SHELVIA_API_KEY. Never log it.
- Install the Shelvia SDK: npm install @shelvia/sdk
Pattern, Shelvia as a read tool
Register a Shelvia retrieval as one of the Agents SDK's typed tools. The agent calls it before generating; the response is a ranked context pack with structured sections.
import { Agent, tool } from "openai-agents-sdk";
import { ShelviaClient } from "@shelvia/sdk";
const shelvia = new ShelviaClient({ apiKey: process.env.SHELVIA_API_KEY! });
const fetchShelviaContext = tool({
name: "fetch_shelvia_context",
description:
"Retrieve reviewed Shelvia project memory before acting. Returns decisions, sources, prompts, next steps, and constraints.",
parameters: {
project_id: { type: "string", description: "Shelvia project id" },
purpose: { type: "string", description: "Why this context is needed" },
},
execute: async ({ project_id, purpose }) => {
const pack = await shelvia.context.generatePack(project_id, {
purpose,
max_chars: 12000,
semantic_ranking: true,
});
return pack;
},
});
const agent = new Agent({
name: "release-coordinator",
instructions:
"Before answering, call fetch_shelvia_context to retrieve reviewed Shelvia memory. Cite decisions and sources by id when you use them.",
tools: [fetchShelviaContext],
});Pattern, agent proposes a memory candidate
When the agent run produces a new claim worth keeping, propose it as a candidate. It enters the review queue with status pending. A workspace member approves before it becomes trusted memory.
const proposeShelviaCandidate = tool({
name: "propose_shelvia_candidate",
description:
"Propose a Shelvia memory candidate (decision / next_step / open_question / ...). The candidate enters the review queue. A human approves before it becomes trusted memory.",
parameters: {
project_id: { type: "string" },
type: {
type: "string",
enum: ["decision","next_step","open_question","prompt","source","note"],
},
title: { type: "string" },
content: { type: "string" },
source_url: { type: "string", nullable: true },
},
execute: async ({ project_id, type, title, content, source_url }) => {
return await shelvia.memory.createCandidate(
project_id,
{ type, title, content, source_url },
{ idempotencyKey: `agent-openai-${runId}-${type}-${hash(content)}` },
);
},
});Propose memory candidates safely
Agents propose. Humans approve. Shelvia keeps the trusted memory. The candidate write is not a trusted-memory write; the row enters the review queue and only a workspace member can promote it.
- Identity: create a narrow-scope Workspace API token per agent run (label it, e.g., "OpenAI support-agent recipe"). Reuse shv_live_<hex> with memory.candidates + memory.context, no special token type.
- Idempotency: every candidate write should carry an Idempotency-Key shaped agent-<runtime>-<run-id>-<candidate-hash>. Agent reruns are common; idempotency keeps the review queue clean.
- Runtime tag: record the runtime via the logContinuation emit (suggested_tool: "openai-agents-sdk"). The candidate body does not carry a runtime field, observability does.
- Never write to trusted memory directly. Shelvia has no service-token path that bypasses review. There is no shelvia.projects.writeDecision().
- Never log raw chain-of-thought. Keep the candidate body to the final claim, not the reasoning trace. Use the optional `reasoning` field for a short justification only.
- Never include secrets or tokens in the candidate body. The validator rejects forbidden keys (cookie, authorization, session_token, api_key, ...) with a structured error code.
- Agents must not approve their own memory. Approval is a UI action in the Shelvia review surface, performed by a workspace member with the right role.
Observability (optional)
After a run, the agent can log a continuation event that records which reviewed context was used and what the next step should be. This is an optional audit / observability emit, it does NOT become trusted memory and does NOT bypass review-before-save. Scope required: continuations.log.
// After the agent run completes:
await shelvia.projects.logContinuation(projectId, {
next_best_action: "Draft customer reply for the v2 launch",
why: "Agent used the Shelvia context pack to summarize prior decisions.",
suggested_tool: "openai-agents-sdk",
priority: "normal",
});Safety notes
- Shelvia is not running the agent. The Agents SDK is. Shelvia is the reviewed memory the agent retrieves and proposes back into.
- The candidate write is always pending. A workspace member must approve before it appears in any future context pack.
- The continuation log is audit-only. It does not write to trusted memory tables (project_decisions, source_links, saved_prompts).
- Pass an Idempotency-Key header (or @shelvia/sdk idempotencyKey) on the write tool if you retry runs.
- Don't put the API key in the system prompt. Keep it server-side.
- memory.read excludes pending candidates by default. Pass include_pending: true only when the agent should see them, and label every pending row in your UI.
Where to go next
- REST API reference, /docs/developers/api
- TypeScript SDK reference, /docs/developers/sdk
- Review-before-save concept, /docs/concepts/review-before-save
- Context Packs concept, /docs/concepts/context-packs
For runnable code samples and the developer reference, see /developers. For the trust model in depth, see /security.