Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
3c7639b
feat(deepcitation): add renderVerifiedHtml shared pipeline
Apr 14, 2026
9203661
feat(deepcitation): export renderVerifiedHtml from public API
Apr 14, 2026
72e85d4
chore: delete publish.ts (replaced by server-side createReport)
Apr 14, 2026
8b89881
feat(sdk): replace publishVerificationReport with createReport; add r…
Apr 14, 2026
db7c94c
feat(example): replace client-side pipeline with dc.createReport
Apr 14, 2026
cee69b8
chore: delete publish tests, exclude bun render tests from Jest, upda…
Apr 14, 2026
e589af0
fix(viewTransition): defer CDN repositioning until animation complete…
Apr 14, 2026
a1ef2c8
fix(useViewportBoundaryGuard): prevent flag clobber on flushSync doub…
Apr 14, 2026
96953a6
feat(markdownToHtml): add claim card + MODEL meta item; remove audien…
Apr 14, 2026
1ba64c7
feat(example): add qmd-local-search — local-first RAG with on-device …
Apr 14, 2026
91bb88d
chore: delete teardown-v14.png; downgrade tsconfig.jest ignoreDepreca…
Apr 14, 2026
4eccf17
fix: make onDone required in runPageCollapseGhostAnimation; add sched…
Apr 14, 2026
66eeeb5
refactor: fix scheduleReposition event-handler bug; clean up minor qu…
Apr 14, 2026
3a667bd
fix(ci): resolve lint-and-validate and test failures
Apr 14, 2026
f759353
fix(ci): restore ignoreDeprecations "6.0" for TS6 moduleResolution co…
Apr 14, 2026
71f4e43
fix: address claude-review blocking findings
Apr 14, 2026
ef73e3e
fix(animation): anchor page-expand ghost to annotation position via d…
Apr 14, 2026
0590996
fix(animation): rename locate pulse stage grow→flash; align impl with…
Apr 14, 2026
c9ebc02
fix: address claude-review round 2 findings
Apr 14, 2026
5291eb7
docs: promote agent skill to top of README; demote dev quick start
Apr 14, 2026
5af40f5
fix(security): replace polynomial regexes in reportUtils with string …
Apr 14, 2026
f453084
refactor(reportUtils): replace useless changed sentinel with while(true)
Apr 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 10 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,21 @@ We believe AI citations should follow **show, don't tell**; they should prove th

DeepCitation turns model citations into deterministic, inspectable proof.

## Install
## Agent Skills

The fastest way to use DeepCitation — verify citations directly from your AI coding agent with the `/verify` skill. No app code, no API integration. Works with Claude Code, Cursor, Windsurf, and other agents that support skills.

Install from [**DeepCitation/skills**](https://github.com/DeepCitation/skills).

## Building your own integration?

Install the package:

```sh
npm install deepcitation # or bun add / yarn add / pnpm add
```

## Quick Start
### Quick Start

```typescript
import { DeepCitation, extractVisibleText, wrapCitationPrompt } from "deepcitation";
Expand Down Expand Up @@ -119,13 +127,6 @@ npm install && npm run dev
- [AG-UI Chat](./examples/agui-chat) — [Live Demo](https://agui-chat-deepcitation.vercel.app/)
- [URL Citations](./examples/url-example)


## Agent Skills

Verify citations directly from your AI coding agent with the `/verify` skill — no app code needed. Works with Claude Code, Cursor, Windsurf, and other agents that support skills.

Install from [**DeepCitation/skills**](https://github.com/DeepCitation/skills).

## Development

### Running Tests
Expand Down
8 changes: 8 additions & 0 deletions docs/agents/engineering-rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@ Do **not** flag these shared fields as "semantically document-only" in code revi

> All citations are potentially verifiable against a page-indexed document. The `type` discriminator indicates the *source* of the citation, not whether page/line fields will be populated.

## Test Failure Policy

When tests fail, fix them. Do not investigate attribution.

- **Never use `git stash` to check whether a failure is "pre-existing"** — stashing is unsafe in shared workspaces where multiple developers and agents operate on the same working tree.
- **"I think those failures were pre-existing" is not acceptable** without an actual fix. It signals time spent on blame instead of solutions.
- If a failing test is genuinely out of scope for your current PR, note it explicitly in the PR description and open a separate tracking issue — but do not leave the suite red.

## Testing Rules

- Tests must validate implemented behavior, not aspirational behavior.
Expand Down
37 changes: 37 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Complete, runnable examples demonstrating DeepCitation integration patterns.
| [**basic-verification**](./basic-verification) | Core 3-step workflow with OpenAI/Anthropic | Learning the basics, quick integration | — |
| [**langchain-rag-chat**](./langchain-rag-chat) | Next.js + LangChain.js RAG app with DeepCitation verification | RAG pipelines, retrieval + proof UI | [Live Demo](https://langchain-rag-chat-deepcitation.vercel.app/) |
| [**mastra-rag-chat**](./mastra-rag-chat) | Next.js + Mastra RAG app with DeepCitation verification | Mastra framework, TypeScript-native RAG | [Live Demo](https://mastra-rag-deepcitation.vercel.app/) |
| [**qmd-local-search**](./qmd-local-search) | CLI example using [qmd](https://github.com/tobi/qmd) as an on-device markdown index with DeepCitation verification | Local-first RAG, privacy-sensitive corpora, offline retrieval | — |
| [**nextjs-ai-sdk**](./nextjs-ai-sdk) | Next.js chat app with Vercel AI SDK | Full-stack apps, streaming UI | [Live Demo](https://nextjs-ai-sdk-deepcitation.vercel.app/) |
| [**agui-chat**](./agui-chat) | AG-UI protocol chat with SSE streaming | AG-UI integration, protocol-level control | [Live Demo](https://agui-chat-deepcitation.vercel.app/) |
| [**static-html**](./static-html) | CDN popover in plain HTML, no build step | Static sites, CDN integration | — |
Expand Down Expand Up @@ -137,6 +138,42 @@ npm run dev
# Open http://localhost:3000
```

### qmd Local Search

CLI example using [qmd](https://github.com/tobi/qmd) as an on-device markdown
index. Retrieval is local (BM25 + vector + LLM rerank); DeepCitation verifies
the resulting citations against a parallel PDF corpus keyed by filename stem.

```typescript
import { createStore } from "@tobilu/qmd";

const store = await createStore({
dbPath: "./.qmd-index.sqlite",
config: { collections: { corpus: { path: "./corpus/md", pattern: "**/*.md" } } },
});

await store.update();
await store.embed(); // first run downloads a GGUF embedding model

const hits = await store.search({ query: question, collection: "corpus", limit: 6 });

// Bridge: each hit.file → corpus/pdf/<stem>.pdf
const pdfUploads = [...new Set(hits.map(h => h.file))].map(mdFile => {
const pdfPath = mdFileToPdfPath(mdFile);
return { file: readFileSync(pdfPath), filename: basename(pdfPath) };
});

const { fileDataParts, deepTextPagesByAttachmentId } = await dc.prepareAttachments(pdfUploads);
```

```bash
# Run the qmd example
cd qmd-local-search
bun install # auto-builds corpus/pdf from corpus/md
cp .env.example .env # add DEEPCITATION_API_KEY + OPENAI_API_KEY
bun run start # interactive picker
```

## More Resources

- [Full Documentation](https://docs.deepcitation.com/)
Expand Down
10 changes: 5 additions & 5 deletions examples/basic-verification/src/curl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ async function prepareUrlAttachment(url: string): Promise<{
*/
async function verifyCitations(
attachmentId: string,
citations: Record<string, { fullPhrase?: string; pageNumber?: number }>,
citations: Record<string, { sourceContext?: string; pageNumber?: number }>,
): Promise<{
verifications: Record<
string,
Expand Down Expand Up @@ -275,7 +275,7 @@ provided documents accurately and cite your sources.`;

console.log(`📋 Parsed ${citationCount} citation(s) from LLM output`);
for (const [key, citation] of Object.entries(parsedCitations)) {
console.log(` [${key}]: "${citation.fullPhrase?.slice(0, 50)}..."`);
console.log(` [${key}]: "${citation.sourceContext?.slice(0, 50)}..."`);
}
console.log();

Expand Down Expand Up @@ -321,9 +321,9 @@ provided documents accurately and cite your sources.`;
console.log(`${"─".repeat(60)}`);

const originalCitation = parsedCitations[key];
if (originalCitation?.fullPhrase) {
if (originalCitation?.sourceContext) {
console.log(
` 📝 Claimed: "${originalCitation.fullPhrase.slice(0, 100)}${originalCitation.fullPhrase.length > 100 ? "..." : ""}"`,
` 📝 Claimed: "${originalCitation.sourceContext.slice(0, 100)}${originalCitation.sourceContext.length > 100 ? "..." : ""}"`,
);
}

Expand Down Expand Up @@ -423,7 +423,7 @@ async function main() {
console.log(" -d '{");
console.log(' "data": {');
console.log(' "attachmentId": "<ATTACHMENT_ID>",');
console.log(' "citations": { "1": { "fullPhrase": "...", "pageNumber": 1 } },');
console.log(' "citations": { "1": { "sourceContext": "...", "pageNumber": 1 } },');
console.log(' "outputImageFormat": "avif"');
console.log(" }");
console.log(" }'");
Expand Down
14 changes: 7 additions & 7 deletions examples/basic-verification/src/fixture-to-html.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ function convertFixture(provider: string) {
// Debug: show what we got
for (const [hash, citation] of Object.entries(parsedCitations)) {
console.log(
` [${citation.citationNumber}] hash=${hash.slice(0, 8)}… anchor="${citation.anchorText?.slice(0, 30)}"`,
` [${citation.citationNumber}] hash=${hash.slice(0, 8)}… match="${citation.sourceMatch?.slice(0, 30)}"`,
);
}

Expand All @@ -56,16 +56,16 @@ function convertFixture(provider: string) {
for (const [hash, citation] of Object.entries(parsedCitations)) {
stubVerifications[hash] = {
status: "found",
label: citation.anchorText || `Citation ${citation.citationNumber}`,
label: citation.sourceMatch || `Citation ${citation.citationNumber}`,
attachmentId: citation.attachmentId || "fixture",
verifiedFullPhrase: citation.fullPhrase,
verifiedAnchorText: citation.anchorText,
verifiedMatchSnippet: citation.fullPhrase?.slice(0, 80),
verifiedSourceContext: citation.sourceContext,
verifiedSourceMatch: citation.sourceMatch,
verifiedMatchSnippet: citation.sourceContext?.slice(0, 80),
citation: {
pageNumber: citation.pageNumber,
lineIds: citation.lineIds,
fullPhrase: citation.fullPhrase,
anchorText: citation.anchorText,
sourceContext: citation.sourceContext,
sourceMatch: citation.sourceMatch,
},
document: {
verifiedPageNumber: citation.pageNumber,
Expand Down
Loading
Loading