@deepcitation/deepcitation-js 1.1.16 → 1.1.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,7 +10,7 @@ import type { CitationInput, ConvertFileInput, ConvertFileResponse, DeepCitation
10
10
  * const dc = new DeepCitation({ apiKey: process.env.DEEPCITATION_API_KEY });
11
11
  *
12
12
  * // Upload a file
13
- * const { fileId, promptContent } = await dc.uploadFile(file);
13
+ * const { attachmentId, promptContent } = await dc.uploadFile(file);
14
14
  *
15
15
  * // Include promptContent in your LLM messages
16
16
  * const response = await llm.chat({
@@ -22,7 +22,7 @@ import type { CitationInput, ConvertFileInput, ConvertFileResponse, DeepCitation
22
22
  *
23
23
  * // Verify citations in the LLM output
24
24
  * const citations = getAllCitationsFromLlmOutput(response);
25
- * const verified = await dc.verifyCitations(fileId, citations);
25
+ * const verified = await dc.verifyCitations(attachmentId, citations);
26
26
  * ```
27
27
  */
28
28
  export declare class DeepCitation {
@@ -45,7 +45,7 @@ export declare class DeepCitation {
45
45
  *
46
46
  * @param file - The file to upload (File, Blob, or Buffer)
47
47
  * @param options - Optional upload options
48
- * @returns Upload response with fileId and extracted text
48
+ * @returns Upload response with attachmentId and extracted text
49
49
  *
50
50
  * @example
51
51
  * ```typescript
@@ -86,8 +86,8 @@ export declare class DeepCitation {
86
86
  * });
87
87
  *
88
88
  * // Then prepare the file for verification
89
- * const { deepTextPromptPortion, fileId } = await dc.prepareConvertedFile({
90
- * fileId: result.fileId
89
+ * const { deepTextPromptPortion, attachmentId } = await dc.prepareConvertedFile({
90
+ * attachmentId: result.attachmentId
91
91
  * });
92
92
  * ```
93
93
  */
@@ -96,8 +96,8 @@ export declare class DeepCitation {
96
96
  * Prepare a previously converted file for citation verification.
97
97
  * Use this after calling convertToPdf() to extract text and get deepTextPromptPortion.
98
98
  *
99
- * @param options - Options with fileId from convertFile
100
- * @returns Upload response with fileId and extracted text
99
+ * @param options - Options with attachmentId from convertFile
100
+ * @returns Upload response with attachmentId and extracted text
101
101
  *
102
102
  * @example
103
103
  * ```typescript
@@ -105,8 +105,8 @@ export declare class DeepCitation {
105
105
  * const converted = await dc.convertToPdf({ url: "https://example.com/article" });
106
106
  *
107
107
  * // Then prepare it for verification
108
- * const { deepTextPromptPortion, fileId } = await dc.prepareConvertedFile({
109
- * fileId: converted.fileId
108
+ * const { deepTextPromptPortion, attachmentId } = await dc.prepareConvertedFile({
109
+ * attachmentId: converted.attachmentId
110
110
  * });
111
111
  *
112
112
  * // Use deepTextPromptPortion in your LLM prompt...
@@ -117,7 +117,7 @@ export declare class DeepCitation {
117
117
  * Upload multiple files for citation verification and get structured content.
118
118
  * This is the recommended way to prepare files for LLM prompts.
119
119
  *
120
- * @param files - Array of files to upload with optional filenames and fileIds
120
+ * @param files - Array of files to upload with optional filenames and attachmentIds
121
121
  * @returns Object containing fileDataParts for verification and deepTextPromptPortion for LLM
122
122
  *
123
123
  * @example
@@ -142,7 +142,7 @@ export declare class DeepCitation {
142
142
  /**
143
143
  * Verify citations against a previously uploaded file.
144
144
  *
145
- * @param fileId - The file ID returned from uploadFile
145
+ * @param attachmentId - The attachment ID returned from uploadFile
146
146
  * @param citations - Citations to verify (from getAllCitationsFromLlmOutput)
147
147
  * @param options - Optional verification options
148
148
  * @returns Verification results with status and proof images
@@ -152,7 +152,7 @@ export declare class DeepCitation {
152
152
  * import { getAllCitationsFromLlmOutput } from '@deepcitation/deepcitation-js';
153
153
  *
154
154
  * const citations = getAllCitationsFromLlmOutput(llmResponse);
155
- * const verified = await dc.verifyCitations(fileId, citations);
155
+ * const verified = await dc.verifyCitations(attachmentId, citations);
156
156
  *
157
157
  * for (const [key, result] of Object.entries(verified.verifications)) {
158
158
  * console.log(key, result.searchState?.status);
@@ -160,7 +160,7 @@ export declare class DeepCitation {
160
160
  * }
161
161
  * ```
162
162
  */
163
- verifyCitations(fileId: string, citations: CitationInput, options?: VerifyCitationsOptions): Promise<VerifyCitationsResponse>;
163
+ verifyCitations(attachmentId: string, citations: CitationInput, options?: VerifyCitationsOptions): Promise<VerifyCitationsResponse>;
164
164
  /**
165
165
  * Verify citations from LLM output with automatic parsing.
166
166
  * This is the recommended way to verify citations for new integrations.
@@ -31,7 +31,7 @@ async function extractErrorMessage(response, fallbackAction) {
31
31
  * const dc = new DeepCitation({ apiKey: process.env.DEEPCITATION_API_KEY });
32
32
  *
33
33
  * // Upload a file
34
- * const { fileId, promptContent } = await dc.uploadFile(file);
34
+ * const { attachmentId, promptContent } = await dc.uploadFile(file);
35
35
  *
36
36
  * // Include promptContent in your LLM messages
37
37
  * const response = await llm.chat({
@@ -43,7 +43,7 @@ async function extractErrorMessage(response, fallbackAction) {
43
43
  *
44
44
  * // Verify citations in the LLM output
45
45
  * const citations = getAllCitationsFromLlmOutput(response);
46
- * const verified = await dc.verifyCitations(fileId, citations);
46
+ * const verified = await dc.verifyCitations(attachmentId, citations);
47
47
  * ```
48
48
  */
49
49
  export class DeepCitation {
@@ -72,7 +72,7 @@ export class DeepCitation {
72
72
  *
73
73
  * @param file - The file to upload (File, Blob, or Buffer)
74
74
  * @param options - Optional upload options
75
- * @returns Upload response with fileId and extracted text
75
+ * @returns Upload response with attachmentId and extracted text
76
76
  *
77
77
  * @example
78
78
  * ```typescript
@@ -89,8 +89,8 @@ export class DeepCitation {
89
89
  const { blob, name } = toBlob(file, options?.filename);
90
90
  const formData = new FormData();
91
91
  formData.append("file", blob, name);
92
- if (options?.fileId)
93
- formData.append("fileId", options.fileId);
92
+ if (options?.attachmentId)
93
+ formData.append("attachmentId", options.attachmentId);
94
94
  if (options?.filename)
95
95
  formData.append("filename", options.filename);
96
96
  const response = await fetch(`${this.apiUrl}/prepareFile`, {
@@ -130,14 +130,14 @@ export class DeepCitation {
130
130
  * });
131
131
  *
132
132
  * // Then prepare the file for verification
133
- * const { deepTextPromptPortion, fileId } = await dc.prepareConvertedFile({
134
- * fileId: result.fileId
133
+ * const { deepTextPromptPortion, attachmentId } = await dc.prepareConvertedFile({
134
+ * attachmentId: result.attachmentId
135
135
  * });
136
136
  * ```
137
137
  */
138
138
  async convertToPdf(input) {
139
139
  const inputObj = typeof input === "string" ? { url: input } : input;
140
- const { url, file, filename, fileId } = inputObj;
140
+ const { url, file, filename, attachmentId } = inputObj;
141
141
  if (!url && !file) {
142
142
  throw new Error("Either url or file must be provided");
143
143
  }
@@ -149,15 +149,15 @@ export class DeepCitation {
149
149
  Authorization: `Bearer ${this.apiKey}`,
150
150
  "Content-Type": "application/json",
151
151
  },
152
- body: JSON.stringify({ url, filename, fileId }),
152
+ body: JSON.stringify({ url, filename, attachmentId }),
153
153
  });
154
154
  }
155
155
  else {
156
156
  const { blob, name } = toBlob(file, filename);
157
157
  const formData = new FormData();
158
158
  formData.append("file", blob, name);
159
- if (fileId)
160
- formData.append("fileId", fileId);
159
+ if (attachmentId)
160
+ formData.append("attachmentId", attachmentId);
161
161
  if (filename)
162
162
  formData.append("filename", filename);
163
163
  response = await fetch(`${this.apiUrl}/convertFile`, {
@@ -175,8 +175,8 @@ export class DeepCitation {
175
175
  * Prepare a previously converted file for citation verification.
176
176
  * Use this after calling convertToPdf() to extract text and get deepTextPromptPortion.
177
177
  *
178
- * @param options - Options with fileId from convertFile
179
- * @returns Upload response with fileId and extracted text
178
+ * @param options - Options with attachmentId from convertFile
179
+ * @returns Upload response with attachmentId and extracted text
180
180
  *
181
181
  * @example
182
182
  * ```typescript
@@ -184,8 +184,8 @@ export class DeepCitation {
184
184
  * const converted = await dc.convertToPdf({ url: "https://example.com/article" });
185
185
  *
186
186
  * // Then prepare it for verification
187
- * const { deepTextPromptPortion, fileId } = await dc.prepareConvertedFile({
188
- * fileId: converted.fileId
187
+ * const { deepTextPromptPortion, attachmentId } = await dc.prepareConvertedFile({
188
+ * attachmentId: converted.attachmentId
189
189
  * });
190
190
  *
191
191
  * // Use deepTextPromptPortion in your LLM prompt...
@@ -199,7 +199,7 @@ export class DeepCitation {
199
199
  "Content-Type": "application/json",
200
200
  },
201
201
  body: JSON.stringify({
202
- fileId: options.fileId,
202
+ attachmentId: options.attachmentId,
203
203
  }),
204
204
  });
205
205
  if (!response.ok) {
@@ -211,7 +211,7 @@ export class DeepCitation {
211
211
  * Upload multiple files for citation verification and get structured content.
212
212
  * This is the recommended way to prepare files for LLM prompts.
213
213
  *
214
- * @param files - Array of files to upload with optional filenames and fileIds
214
+ * @param files - Array of files to upload with optional filenames and attachmentIds
215
215
  * @returns Object containing fileDataParts for verification and deepTextPromptPortion for LLM
216
216
  *
217
217
  * @example
@@ -237,14 +237,14 @@ export class DeepCitation {
237
237
  return { fileDataParts: [], deepTextPromptPortion: [] };
238
238
  }
239
239
  // Upload all files in parallel
240
- const uploadPromises = files.map(({ file, filename, fileId }) => this.uploadFile(file, { filename, fileId }).then((result) => ({
240
+ const uploadPromises = files.map(({ file, filename, attachmentId }) => this.uploadFile(file, { filename, attachmentId }).then((result) => ({
241
241
  result,
242
242
  filename,
243
243
  })));
244
244
  const uploadResults = await Promise.all(uploadPromises);
245
245
  // Extract file data parts with deepTextPromptPortion included (single source of truth)
246
246
  const fileDataParts = uploadResults.map(({ result, filename }) => ({
247
- fileId: result.fileId,
247
+ attachmentId: result.attachmentId,
248
248
  deepTextPromptPortion: result.deepTextPromptPortion,
249
249
  filename: filename || result.metadata?.filename,
250
250
  }));
@@ -255,7 +255,7 @@ export class DeepCitation {
255
255
  /**
256
256
  * Verify citations against a previously uploaded file.
257
257
  *
258
- * @param fileId - The file ID returned from uploadFile
258
+ * @param attachmentId - The attachment ID returned from uploadFile
259
259
  * @param citations - Citations to verify (from getAllCitationsFromLlmOutput)
260
260
  * @param options - Optional verification options
261
261
  * @returns Verification results with status and proof images
@@ -265,7 +265,7 @@ export class DeepCitation {
265
265
  * import { getAllCitationsFromLlmOutput } from '@deepcitation/deepcitation-js';
266
266
  *
267
267
  * const citations = getAllCitationsFromLlmOutput(llmResponse);
268
- * const verified = await dc.verifyCitations(fileId, citations);
268
+ * const verified = await dc.verifyCitations(attachmentId, citations);
269
269
  *
270
270
  * for (const [key, result] of Object.entries(verified.verifications)) {
271
271
  * console.log(key, result.searchState?.status);
@@ -273,7 +273,7 @@ export class DeepCitation {
273
273
  * }
274
274
  * ```
275
275
  */
276
- async verifyCitations(fileId, citations, options) {
276
+ async verifyCitations(attachmentId, citations, options) {
277
277
  // Normalize citations to a map with citation keys
278
278
  const citationMap = {};
279
279
  if (Array.isArray(citations)) {
@@ -301,7 +301,7 @@ export class DeepCitation {
301
301
  const requestUrl = `${this.apiUrl}/verifyCitations`;
302
302
  const requestBody = {
303
303
  data: {
304
- fileId,
304
+ attachmentId,
305
305
  citations: citationMap,
306
306
  outputImageFormat: options?.outputImageFormat || "avif",
307
307
  },
@@ -348,20 +348,20 @@ export class DeepCitation {
348
348
  if (Object.keys(citations).length === 0) {
349
349
  return { verifications: {} };
350
350
  }
351
- // Group citations by fileId
352
- const citationsByFile = new Map();
351
+ // Group citations by attachmentId
352
+ const citationsByAttachment = new Map();
353
353
  for (const [key, citation] of Object.entries(citations)) {
354
- const fileId = citation.fileId || "";
355
- if (!citationsByFile.has(fileId)) {
356
- citationsByFile.set(fileId, {});
354
+ const attachmentId = citation.attachmentId || "";
355
+ if (!citationsByAttachment.has(attachmentId)) {
356
+ citationsByAttachment.set(attachmentId, {});
357
357
  }
358
- citationsByFile.get(fileId)[key] = citation;
358
+ citationsByAttachment.get(attachmentId)[key] = citation;
359
359
  }
360
360
  // Verify all files in parallel
361
361
  const verificationPromises = [];
362
- for (const [fileId, fileCitations] of citationsByFile) {
363
- if (fileId) {
364
- verificationPromises.push(this.verifyCitations(fileId, fileCitations, { outputImageFormat }));
362
+ for (const [attachmentId, fileCitations] of citationsByAttachment) {
363
+ if (attachmentId) {
364
+ verificationPromises.push(this.verifyCitations(attachmentId, fileCitations, { outputImageFormat }));
365
365
  }
366
366
  }
367
367
  const results = await Promise.all(verificationPromises);
@@ -12,8 +12,8 @@ export interface DeepCitationConfig {
12
12
  * Response from uploading a file for citation verification
13
13
  */
14
14
  export interface UploadFileResponse {
15
- /** The file ID assigned by DeepCitation (custom or auto-generated) */
16
- fileId: string;
15
+ /** The attachment ID assigned by DeepCitation (custom or auto-generated) */
16
+ attachmentId: string;
17
17
  /** The full text content formatted for LLM prompts with page markers and line IDs. Use this in your user prompts. */
18
18
  deepTextPromptPortion: string;
19
19
  /** Form fields extracted from PDF forms */
@@ -41,8 +41,8 @@ export interface UploadFileResponse {
41
41
  * Options for file upload
42
42
  */
43
43
  export interface UploadFileOptions {
44
- /** Optional custom file ID to use instead of auto-generated one */
45
- fileId?: string;
44
+ /** Optional custom attachment ID to use instead of auto-generated one */
45
+ attachmentId?: string;
46
46
  /** Optional custom filename (uses File.name if not provided) */
47
47
  filename?: string;
48
48
  }
@@ -72,15 +72,15 @@ export interface FileInput {
72
72
  file: File | Blob | Buffer;
73
73
  /** Optional filename */
74
74
  filename?: string;
75
- /** Optional custom file ID */
76
- fileId?: string;
75
+ /** Optional custom attachment ID */
76
+ attachmentId?: string;
77
77
  }
78
78
  /**
79
79
  * File reference returned from prepareFiles
80
80
  */
81
81
  export interface FileDataPart {
82
- /** The file ID assigned by DeepCitation */
83
- fileId: string;
82
+ /** The attachment ID assigned by DeepCitation */
83
+ attachmentId: string;
84
84
  /** The formatted text content for LLM prompts (with page markers and line IDs) */
85
85
  deepTextPromptPortion: string;
86
86
  /** Optional filename for display purposes */
@@ -120,15 +120,15 @@ export interface ConvertFileInput {
120
120
  file?: File | Blob | Buffer;
121
121
  /** Optional custom filename for the converted PDF */
122
122
  filename?: string;
123
- /** Optional custom file ID */
124
- fileId?: string;
123
+ /** Optional custom attachment ID */
124
+ attachmentId?: string;
125
125
  }
126
126
  /**
127
127
  * Response from convertFile
128
128
  */
129
129
  export interface ConvertFileResponse {
130
- /** The file ID assigned by DeepCitation. Pass this to prepareConvertedFile(). */
131
- fileId: string;
130
+ /** The attachment ID assigned by DeepCitation. Pass this to prepareConvertedFile(). */
131
+ attachmentId: string;
132
132
  /** Metadata about the conversion */
133
133
  metadata: {
134
134
  /** Original filename before conversion */
@@ -149,6 +149,6 @@ export interface ConvertFileResponse {
149
149
  * Options for processing a converted file
150
150
  */
151
151
  export interface PrepareConvertedFileOptions {
152
- /** The file ID from a previous convertFile call */
153
- fileId: string;
152
+ /** The attachment ID from a previous convertFile call */
153
+ attachmentId: string;
154
154
  }
package/lib/index.d.ts CHANGED
@@ -4,7 +4,7 @@
4
4
  */
5
5
  export { DeepCitation } from "./client/index.js";
6
6
  export type { DeepCitationConfig, UploadFileResponse, UploadFileOptions, VerifyCitationsResponse, VerifyCitationsOptions, CitationInput, FileInput, FileDataPart, PrepareFilesResult, VerifyCitationsFromLlmOutput, } from "./client/index.js";
7
- export { parseCitation, getCitationStatus, getAllCitationsFromLlmOutput, groupCitationsByFileId, groupCitationsByFileIdObject, } from "./parsing/parseCitation.js";
7
+ export { parseCitation, getCitationStatus, getAllCitationsFromLlmOutput, groupCitationsByAttachmentId, groupCitationsByAttachmentIdObject, } from "./parsing/parseCitation.js";
8
8
  export { normalizeCitations, getCitationPageNumber, } from "./parsing/normalizeCitation.js";
9
9
  export { isGeminiGarbage, cleanRepeatingLastSentence, } from "./parsing/parseWorkAround.js";
10
10
  export type { Citation, CitationStatus, VerifyCitationRequest, VerifyCitationResponse, OutputImageFormat, } from "./types/citation.js";
package/lib/index.js CHANGED
@@ -5,7 +5,7 @@
5
5
  // Client
6
6
  export { DeepCitation } from "./client/index.js";
7
7
  // Parsing
8
- export { parseCitation, getCitationStatus, getAllCitationsFromLlmOutput, groupCitationsByFileId, groupCitationsByFileIdObject, } from "./parsing/parseCitation.js";
8
+ export { parseCitation, getCitationStatus, getAllCitationsFromLlmOutput, groupCitationsByAttachmentId, groupCitationsByAttachmentIdObject, } from "./parsing/parseCitation.js";
9
9
  export { normalizeCitations, getCitationPageNumber, } from "./parsing/normalizeCitation.js";
10
10
  export { isGeminiGarbage, cleanRepeatingLastSentence, } from "./parsing/parseWorkAround.js";
11
11
  export { DEFAULT_OUTPUT_IMAGE_FORMAT } from "./types/citation.js";
@@ -1,6 +1,6 @@
1
1
  export const removeCitations = (pageText, leaveValueBehind) => {
2
- const citationRegex = /<cite\s+fileId='(\w{0,25})'\s+start_page[\_a-zA-Z]*='page[\_a-zA-Z]*(\d+)_index_(\d+)'\s+full_phrase='((?:[^'\\]|\\.)*)'\s+line(?:_ids|Ids)='([^']+)'(?:\s+(value|reasoning)='((?:[^'\\]|\\.)*)')?\s*\/>/g;
3
- return pageText.replace(citationRegex, (match, fileId, pageNumber, index, fullPhrase, lineIds, value) => {
2
+ const citationRegex = /<cite\s+(?:fileId|attachmentId)='(\w{0,25})'\s+start_page[\_a-zA-Z]*='page[\_a-zA-Z]*(\d+)_index_(\d+)'\s+full_phrase='((?:[^'\\]|\\.)*)'\s+line(?:_ids|Ids)='([^']+)'(?:\s+(value|reasoning)='((?:[^'\\]|\\.)*)')?\s*\/>/g;
3
+ return pageText.replace(citationRegex, (match, attachmentId, pageNumber, index, fullPhrase, lineIds, value) => {
4
4
  //it is still value= so we need to remove the value=
5
5
  if (leaveValueBehind) {
6
6
  return value?.replace(/value=['"]|['"]/g, "") || "";
@@ -53,7 +53,7 @@ const normalizeCitationContent = (input) => {
53
53
  key === "start_pageKey" ||
54
54
  key === "start_page_key")
55
55
  return "start_page_key";
56
- if (key === "fileID" || key === "fileId" || key === "file_id")
56
+ if (key === "fileID" || key === "fileId" || key === "file_id" || key === "attachmentId" || key === "attachment_id")
57
57
  return "file_id";
58
58
  if (key === "keySpan" || key === "key_span")
59
59
  return "key_span";
@@ -71,7 +71,7 @@ const normalizeCitationContent = (input) => {
71
71
  // 2. ROBUST TEXT ATTRIBUTE PARSING (reasoning, value, full_phrase)
72
72
  // This regex matches: Key = Quote -> Content (lazy) -> Lookahead for (Next Attribute OR End of Tag)
73
73
  // It effectively ignores quotes inside the content during the initial capture.
74
- const textAttributeRegex = /(fullPhrase|full_phrase|keySpan|key_span|reasoning|value)\s*=\s*(['"])([\s\S]*?)(?=\s+(?:line_ids|lineIds|timestamps|fileId|file_id|start_page_key|start_pageKey|startPageKey|keySpan|key_span|reasoning|value|full_phrase)|\s*\/?>)/gm;
74
+ const textAttributeRegex = /(fullPhrase|full_phrase|keySpan|key_span|reasoning|value)\s*=\s*(['"])([\s\S]*?)(?=\s+(?:line_ids|lineIds|timestamps|fileId|file_id|attachmentId|attachment_id|start_page_key|start_pageKey|startPageKey|keySpan|key_span|reasoning|value|full_phrase)|\s*\/?>)/gm;
75
75
  normalized = normalized.replace(textAttributeRegex, (_match, key, openQuote, rawContent) => {
76
76
  let content = rawContent;
77
77
  // The lazy match usually captures the closing quote because the lookahead
@@ -27,53 +27,53 @@ export declare const getAllCitationsFromLlmOutput: (llmOutput: any) => {
27
27
  [key: string]: Citation;
28
28
  };
29
29
  /**
30
- * Groups citations by their fileId for multi-file verification scenarios.
30
+ * Groups citations by their attachmentId for multi-file verification scenarios.
31
31
  * This is useful when you have citations from multiple files and need to
32
32
  * verify them against their respective source documents.
33
33
  *
34
34
  * @param citations - Array of Citation objects or a dictionary of citations
35
- * @returns Map of fileId to dictionary of citations from that file
35
+ * @returns Map of attachmentId to dictionary of citations from that file
36
36
  *
37
37
  * @example
38
38
  * ```typescript
39
39
  * const citations = getAllCitationsFromLlmOutput(response.content);
40
- * const citationsByFile = groupCitationsByFileId(citations);
40
+ * const citationsByAttachment = groupCitationsByAttachmentId(citations);
41
41
  *
42
42
  * // Verify citations for each file
43
- * for (const [fileId, fileCitations] of citationsByFile) {
44
- * const verified = await dc.verifyCitations(fileId, fileCitations);
43
+ * for (const [attachmentId, fileCitations] of citationsByAttachment) {
44
+ * const verified = await dc.verifyCitations(attachmentId, fileCitations);
45
45
  * // Process verification results...
46
46
  * }
47
47
  * ```
48
48
  */
49
- export declare function groupCitationsByFileId(citations: Citation[] | {
49
+ export declare function groupCitationsByAttachmentId(citations: Citation[] | {
50
50
  [key: string]: Citation;
51
51
  }): Map<string, {
52
52
  [key: string]: Citation;
53
53
  }>;
54
54
  /**
55
- * Groups citations by their fileId and returns as a plain object.
56
- * Alternative to groupCitationsByFileId that returns a plain object instead of a Map.
55
+ * Groups citations by their attachmentId and returns as a plain object.
56
+ * Alternative to groupCitationsByAttachmentId that returns a plain object instead of a Map.
57
57
  *
58
58
  * @param citations - Array of Citation objects or a dictionary of citations
59
- * @returns Object with fileId keys mapping to citation dictionaries
59
+ * @returns Object with attachmentId keys mapping to citation dictionaries
60
60
  *
61
61
  * @example
62
62
  * ```typescript
63
63
  * const citations = getAllCitationsFromLlmOutput(response.content);
64
- * const citationsByFile = groupCitationsByFileIdObject(citations);
64
+ * const citationsByAttachment = groupCitationsByAttachmentIdObject(citations);
65
65
  *
66
66
  * // Verify citations for each file using Promise.all
67
- * const verificationPromises = Object.entries(citationsByFile).map(
68
- * ([fileId, fileCitations]) => dc.verifyCitations(fileId, fileCitations)
67
+ * const verificationPromises = Object.entries(citationsByAttachment).map(
68
+ * ([attachmentId, fileCitations]) => dc.verifyCitations(attachmentId, fileCitations)
69
69
  * );
70
70
  * const results = await Promise.all(verificationPromises);
71
71
  * ```
72
72
  */
73
- export declare function groupCitationsByFileIdObject(citations: Citation[] | {
73
+ export declare function groupCitationsByAttachmentIdObject(citations: Citation[] | {
74
74
  [key: string]: Citation;
75
75
  }): {
76
- [fileId: string]: {
76
+ [attachmentId: string]: {
77
77
  [key: string]: Citation;
78
78
  };
79
79
  };
@@ -88,7 +88,7 @@ export const parseCitation = (fragment, mdAttachmentId, citationCounterRef, isVe
88
88
  : "";
89
89
  const middleCite = fragment.substring(fragment.indexOf("<cite"), fragment.indexOf("/>") + 2);
90
90
  // GROUPS:
91
- // 1: fileId
91
+ // 1: attachmentId
92
92
  // 2: start_page number
93
93
  // 3: index number
94
94
  // 4: full_phrase content (escaped)
@@ -101,8 +101,8 @@ export const parseCitation = (fragment, mdAttachmentId, citationCounterRef, isVe
101
101
  const match = citationMatches?.[0];
102
102
  const pageNumber = match?.[2] ? parseInt(match?.[2]) : undefined;
103
103
  const pageIndex = match?.[3] ? parseInt(match?.[3]) : undefined;
104
- let fileId = match?.[1];
105
- let attachmentId = fileId?.length === 20 ? fileId : mdAttachmentId || fileId;
104
+ let rawAttachmentId = match?.[1];
105
+ let attachmentId = rawAttachmentId?.length === 20 ? rawAttachmentId : mdAttachmentId || rawAttachmentId;
106
106
  // Use helper to handle escaped quotes inside the phrase
107
107
  let fullPhrase = cleanAndUnescape(match?.[4]);
108
108
  let keySpan = cleanAndUnescape(match?.[5]);
@@ -128,7 +128,7 @@ export const parseCitation = (fragment, mdAttachmentId, citationCounterRef, isVe
128
128
  console.error("Error parsing lineIds", e);
129
129
  }
130
130
  // GROUPS for AV:
131
- // 1: fileId
131
+ // 1: attachmentId
132
132
  // 2: full_phrase content (escaped)
133
133
  // 3: timestamps content
134
134
  // 4: Optional Key (value|reasoning)
@@ -138,8 +138,8 @@ export const parseCitation = (fragment, mdAttachmentId, citationCounterRef, isVe
138
138
  const avMatch = avCitationMatches?.[0];
139
139
  let timestamps;
140
140
  if (avMatch) {
141
- fileId = avMatch?.[1];
142
- attachmentId = fileId?.length === 20 ? fileId : mdAttachmentId || fileId;
141
+ rawAttachmentId = avMatch?.[1];
142
+ attachmentId = rawAttachmentId?.length === 20 ? rawAttachmentId : mdAttachmentId || rawAttachmentId;
143
143
  fullPhrase = cleanAndUnescape(avMatch?.[2]);
144
144
  const timestampsString = avMatch?.[3]?.replace(/timestamps=['"]|['"]/g, "");
145
145
  const [startTime, endTime] = timestampsString?.split("-") || [];
@@ -154,7 +154,7 @@ export const parseCitation = (fragment, mdAttachmentId, citationCounterRef, isVe
154
154
  timestamps = { startTime, endTime };
155
155
  }
156
156
  const citation = {
157
- fileId: attachmentId,
157
+ attachmentId: attachmentId,
158
158
  pageNumber,
159
159
  startPageKey: `page_number_${pageNumber || 1}_index_${pageIndex || 0}`,
160
160
  fullPhrase,
@@ -188,7 +188,7 @@ const parseJsonCitation = (jsonCitation, citationNumber) => {
188
188
  const startPageKey = jsonCitation.startPageKey ?? jsonCitation.start_page_key;
189
189
  const keySpan = jsonCitation.keySpan ?? jsonCitation.key_span;
190
190
  const rawLineIds = jsonCitation.lineIds ?? jsonCitation.line_ids;
191
- const fileId = jsonCitation.fileId ?? jsonCitation.file_id;
191
+ const attachmentId = jsonCitation.attachmentId ?? jsonCitation.attachment_id ?? jsonCitation.fileId ?? jsonCitation.file_id;
192
192
  const reasoning = jsonCitation.reasoning;
193
193
  const value = jsonCitation.value;
194
194
  if (!fullPhrase) {
@@ -215,7 +215,7 @@ const parseJsonCitation = (jsonCitation, citationNumber) => {
215
215
  ? [...rawLineIds].sort((a, b) => a - b)
216
216
  : undefined;
217
217
  const citation = {
218
- fileId,
218
+ attachmentId,
219
219
  pageNumber,
220
220
  fullPhrase,
221
221
  citationNumber,
@@ -363,71 +363,71 @@ export const getAllCitationsFromLlmOutput = (llmOutput) => {
363
363
  return citations;
364
364
  };
365
365
  /**
366
- * Groups citations by their fileId for multi-file verification scenarios.
366
+ * Groups citations by their attachmentId for multi-file verification scenarios.
367
367
  * This is useful when you have citations from multiple files and need to
368
368
  * verify them against their respective source documents.
369
369
  *
370
370
  * @param citations - Array of Citation objects or a dictionary of citations
371
- * @returns Map of fileId to dictionary of citations from that file
371
+ * @returns Map of attachmentId to dictionary of citations from that file
372
372
  *
373
373
  * @example
374
374
  * ```typescript
375
375
  * const citations = getAllCitationsFromLlmOutput(response.content);
376
- * const citationsByFile = groupCitationsByFileId(citations);
376
+ * const citationsByAttachment = groupCitationsByAttachmentId(citations);
377
377
  *
378
378
  * // Verify citations for each file
379
- * for (const [fileId, fileCitations] of citationsByFile) {
380
- * const verified = await dc.verifyCitations(fileId, fileCitations);
379
+ * for (const [attachmentId, fileCitations] of citationsByAttachment) {
380
+ * const verified = await dc.verifyCitations(attachmentId, fileCitations);
381
381
  * // Process verification results...
382
382
  * }
383
383
  * ```
384
384
  */
385
- export function groupCitationsByFileId(citations) {
385
+ export function groupCitationsByAttachmentId(citations) {
386
386
  const grouped = new Map();
387
387
  // Normalize input to entries
388
388
  const entries = Array.isArray(citations)
389
389
  ? citations.map((c, idx) => [generateCitationKey(c) || String(idx + 1), c])
390
390
  : Object.entries(citations);
391
391
  for (const [key, citation] of entries) {
392
- const fileId = citation.fileId || "";
393
- if (!grouped.has(fileId)) {
394
- grouped.set(fileId, {});
392
+ const attachmentId = citation.attachmentId || "";
393
+ if (!grouped.has(attachmentId)) {
394
+ grouped.set(attachmentId, {});
395
395
  }
396
- grouped.get(fileId)[key] = citation;
396
+ grouped.get(attachmentId)[key] = citation;
397
397
  }
398
398
  return grouped;
399
399
  }
400
400
  /**
401
- * Groups citations by their fileId and returns as a plain object.
402
- * Alternative to groupCitationsByFileId that returns a plain object instead of a Map.
401
+ * Groups citations by their attachmentId and returns as a plain object.
402
+ * Alternative to groupCitationsByAttachmentId that returns a plain object instead of a Map.
403
403
  *
404
404
  * @param citations - Array of Citation objects or a dictionary of citations
405
- * @returns Object with fileId keys mapping to citation dictionaries
405
+ * @returns Object with attachmentId keys mapping to citation dictionaries
406
406
  *
407
407
  * @example
408
408
  * ```typescript
409
409
  * const citations = getAllCitationsFromLlmOutput(response.content);
410
- * const citationsByFile = groupCitationsByFileIdObject(citations);
410
+ * const citationsByAttachment = groupCitationsByAttachmentIdObject(citations);
411
411
  *
412
412
  * // Verify citations for each file using Promise.all
413
- * const verificationPromises = Object.entries(citationsByFile).map(
414
- * ([fileId, fileCitations]) => dc.verifyCitations(fileId, fileCitations)
413
+ * const verificationPromises = Object.entries(citationsByAttachment).map(
414
+ * ([attachmentId, fileCitations]) => dc.verifyCitations(attachmentId, fileCitations)
415
415
  * );
416
416
  * const results = await Promise.all(verificationPromises);
417
417
  * ```
418
418
  */
419
- export function groupCitationsByFileIdObject(citations) {
419
+ export function groupCitationsByAttachmentIdObject(citations) {
420
420
  const grouped = {};
421
421
  // Normalize input to entries
422
422
  const entries = Array.isArray(citations)
423
423
  ? citations.map((c, idx) => [generateCitationKey(c) || String(idx + 1), c])
424
424
  : Object.entries(citations);
425
425
  for (const [key, citation] of entries) {
426
- const fileId = citation.fileId || "";
427
- if (!grouped[fileId]) {
428
- grouped[fileId] = {};
426
+ const attachmentId = citation.attachmentId || "";
427
+ if (!grouped[attachmentId]) {
428
+ grouped[attachmentId] = {};
429
429
  }
430
- grouped[fileId][key] = citation;
430
+ grouped[attachmentId][key] = citation;
431
431
  }
432
432
  return grouped;
433
433
  }
@@ -1,5 +1,5 @@
1
- export declare const CITATION_MARKDOWN_SYNTAX_PROMPT = "\nCitation syntax to use within Markdown:\n\u2022 To support any ideas or information that requires a citation from the provided content, use the following citation syntax:\n<cite file_id='file_id' start_page_key='page_number_PAGE_index_INDEX' full_phrase='the verbatim text of the terse phrase inside <file_text />; remember to escape quotes and newlines inside the full_phrase to remain as valid JSON' key_span='the verbatim 1-3 words within full_phrase that best support the citation' line_ids='2-6' reasoning='the terse logic used to conclude the citation' />\n\n\u2022 Very important: for page numbers, only use the page number and page index info from the page_number_PAGE_index_INDEX format (e.g. <page_number_1_index_0>) and never from the contents inside the page.\n\u2022 start_page_key, full_phrase, and line_ids are required for each citation.\n\u2022 Infer line_ids, as we only provide the first, last, and every 5th line. When copying a previous <cite />, use the full info from the previous citation without changing the start_page_key, line_ids, or any other <cite /> attributes.\n\u2022 Use refer to line_ids inclusively, and use a range (or single) for each citation, split multiple sequential line_ids into multiple citations.\n\u2022 These citations will be replaced and displayed in-line as a numeric element (e.g. [1]), the markdown preceding <cite /> should read naturally with only one <cite /> per sentence with rare exceptions for two <cite /> in a sentence. <cite /> often present best at the end of the sentence, and are not grouped at the end of the document.\n\u2022 The full_phrase should be the exact verbatim text of the phrase or paragraph from the source document to support the insight or idea.\n\u2022 We do NOT put the full_phrase inside <cite ...></cite>; we only use full_phrase inside the full_phrase attribute.\n";
2
- export declare const AV_CITATION_MARKDOWN_SYNTAX_PROMPT = "\n\u2022 To support any ideas or information that requires a citation from the provided content, use the following citation syntax:\n<cite file_id='file_id' full_phrase='the verbatim text of the phrase; remember to escape quotes and newlines inside the full_phrase to remain as valid JSON' timestamps='HH:MM:SS.SSS-HH:MM:SS.SSS' reasoning='the logic connecting the form section requirements to the supporting source citation' />\n\u2022 These citations are displayed in-line or in the relevant list item, and are not grouped at the end of the document.\n";
1
+ export declare const CITATION_MARKDOWN_SYNTAX_PROMPT = "\nCitation syntax to use within Markdown:\n\u2022 To support any ideas or information that requires a citation from the provided content, use the following citation syntax:\n<cite file_id='attachment_id' start_page_key='page_number_PAGE_index_INDEX' full_phrase='the verbatim text of the terse phrase inside <file_text />; remember to escape quotes and newlines inside the full_phrase to remain as valid JSON' key_span='the verbatim 1-3 words within full_phrase that best support the citation' line_ids='2-6' reasoning='the terse logic used to conclude the citation' />\n\n\u2022 Very important: for page numbers, only use the page number and page index info from the page_number_PAGE_index_INDEX format (e.g. <page_number_1_index_0>) and never from the contents inside the page.\n\u2022 start_page_key, full_phrase, and line_ids are required for each citation.\n\u2022 Infer line_ids, as we only provide the first, last, and every 5th line. When copying a previous <cite />, use the full info from the previous citation without changing the start_page_key, line_ids, or any other <cite /> attributes.\n\u2022 Use refer to line_ids inclusively, and use a range (or single) for each citation, split multiple sequential line_ids into multiple citations.\n\u2022 These citations will be replaced and displayed in-line as a numeric element (e.g. [1]), the markdown preceding <cite /> should read naturally with only one <cite /> per sentence with rare exceptions for two <cite /> in a sentence. <cite /> often present best at the end of the sentence, and are not grouped at the end of the document.\n\u2022 The full_phrase should be the exact verbatim text of the phrase or paragraph from the source document to support the insight or idea.\n\u2022 We do NOT put the full_phrase inside <cite ...></cite>; we only use full_phrase inside the full_phrase attribute.\n";
2
+ export declare const AV_CITATION_MARKDOWN_SYNTAX_PROMPT = "\n\u2022 To support any ideas or information that requires a citation from the provided content, use the following citation syntax:\n<cite file_id='attachment_id' full_phrase='the verbatim text of the phrase; remember to escape quotes and newlines inside the full_phrase to remain as valid JSON' timestamps='HH:MM:SS.SSS-HH:MM:SS.SSS' reasoning='the logic connecting the form section requirements to the supporting source citation' />\n\u2022 These citations are displayed in-line or in the relevant list item, and are not grouped at the end of the document.\n";
3
3
  export interface WrapSystemPromptOptions {
4
4
  /** The original system prompt to wrap with citation instructions */
5
5
  systemPrompt: string;
@@ -77,7 +77,7 @@ export declare function wrapCitationPrompt(options: WrapCitationPromptOptions):
77
77
  export declare const CITATION_JSON_OUTPUT_FORMAT: {
78
78
  type: string;
79
79
  properties: {
80
- fileId: {
80
+ attachmentId: {
81
81
  type: string;
82
82
  };
83
83
  startPageKey: {
@@ -109,7 +109,7 @@ export declare const CITATION_JSON_OUTPUT_FORMAT: {
109
109
  export declare const CITATION_AV_BASED_JSON_OUTPUT_FORMAT: {
110
110
  type: string;
111
111
  properties: {
112
- fileId: {
112
+ attachmentId: {
113
113
  type: string;
114
114
  };
115
115
  startPageKey: {
@@ -1,7 +1,7 @@
1
1
  export const CITATION_MARKDOWN_SYNTAX_PROMPT = `
2
2
  Citation syntax to use within Markdown:
3
3
  • To support any ideas or information that requires a citation from the provided content, use the following citation syntax:
4
- <cite file_id='file_id' start_page_key='page_number_PAGE_index_INDEX' full_phrase='the verbatim text of the terse phrase inside <file_text />; remember to escape quotes and newlines inside the full_phrase to remain as valid JSON' key_span='the verbatim 1-3 words within full_phrase that best support the citation' line_ids='2-6' reasoning='the terse logic used to conclude the citation' />
4
+ <cite file_id='attachment_id' start_page_key='page_number_PAGE_index_INDEX' full_phrase='the verbatim text of the terse phrase inside <file_text />; remember to escape quotes and newlines inside the full_phrase to remain as valid JSON' key_span='the verbatim 1-3 words within full_phrase that best support the citation' line_ids='2-6' reasoning='the terse logic used to conclude the citation' />
5
5
 
6
6
  • Very important: for page numbers, only use the page number and page index info from the page_number_PAGE_index_INDEX format (e.g. <page_number_1_index_0>) and never from the contents inside the page.
7
7
  • start_page_key, full_phrase, and line_ids are required for each citation.
@@ -13,7 +13,7 @@ Citation syntax to use within Markdown:
13
13
  `;
14
14
  export const AV_CITATION_MARKDOWN_SYNTAX_PROMPT = `
15
15
  • To support any ideas or information that requires a citation from the provided content, use the following citation syntax:
16
- <cite file_id='file_id' full_phrase='the verbatim text of the phrase; remember to escape quotes and newlines inside the full_phrase to remain as valid JSON' timestamps='HH:MM:SS.SSS-HH:MM:SS.SSS' reasoning='the logic connecting the form section requirements to the supporting source citation' />
16
+ <cite file_id='attachment_id' full_phrase='the verbatim text of the phrase; remember to escape quotes and newlines inside the full_phrase to remain as valid JSON' timestamps='HH:MM:SS.SSS-HH:MM:SS.SSS' reasoning='the logic connecting the form section requirements to the supporting source citation' />
17
17
  • These citations are displayed in-line or in the relevant list item, and are not grouped at the end of the document.
18
18
  `;
19
19
  /**
@@ -110,7 +110,7 @@ export function wrapCitationPrompt(options) {
110
110
  export const CITATION_JSON_OUTPUT_FORMAT = {
111
111
  type: "object",
112
112
  properties: {
113
- fileId: { type: "string" },
113
+ attachmentId: { type: "string" },
114
114
  startPageKey: {
115
115
  type: "string",
116
116
  description: 'Only return a result like "page_number_PAGE_index_INDEX" from the provided page keys (e.g. <page_number_1_index_0>) and never from the contents inside the page.',
@@ -134,7 +134,7 @@ export const CITATION_JSON_OUTPUT_FORMAT = {
134
134
  },
135
135
  },
136
136
  required: [
137
- "fileId",
137
+ "attachmentId",
138
138
  "startPageKey",
139
139
  "reasoning",
140
140
  "fullPhrase",
@@ -145,7 +145,7 @@ export const CITATION_JSON_OUTPUT_FORMAT = {
145
145
  export const CITATION_AV_BASED_JSON_OUTPUT_FORMAT = {
146
146
  type: "object",
147
147
  properties: {
148
- fileId: { type: "string" },
148
+ attachmentId: { type: "string" },
149
149
  startPageKey: {
150
150
  type: "string",
151
151
  description: 'Only return a result like "page_number_PAGE_index_INDEX" from the provided page keys (e.g. <page_number_1_index_0>) and never from the contents inside the page.',
@@ -91,7 +91,7 @@ export function decompressPromptIds(compressed, prefixMap) {
91
91
  const escPrefix = prefix.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&");
92
92
  text = text.replace(new RegExp(escPrefix, "g"), full);
93
93
  }
94
- //this is for citation fileId or file_id
94
+ //this is for citation attachmentId, file_id, or fileId
95
95
  if (entries.length === 1 && (text.includes("file_id='") || text.includes('file_id="'))) {
96
96
  const fullId = entries[0][1];
97
97
  text = text.replace(/file_id='[^']*'|file_id="[^"]*"/g, `file_id='${fullId}'`);
@@ -100,6 +100,10 @@ export function decompressPromptIds(compressed, prefixMap) {
100
100
  const fullId = entries[0][1];
101
101
  text = text.replace(/fileId='[^']*'|fileId="[^"]*"/g, `fileId='${fullId}'`);
102
102
  }
103
+ else if (entries.length === 1 && (text.includes("attachmentId='") || text.includes('attachmentId="'))) {
104
+ const fullId = entries[0][1];
105
+ text = text.replace(/attachmentId='[^']*'|attachmentId="[^"]*"/g, `attachmentId='${fullId}'`);
106
+ }
103
107
  const newLength = text?.length;
104
108
  const diff = originalLength - newLength;
105
109
  if (diff > 0) {
@@ -79,7 +79,8 @@
79
79
  }
80
80
 
81
81
  /* Status-specific styles */
82
- .dc-citation--verified .dc-citation-text {
82
+ /* Only brackets variant gets verified blue text color */
83
+ .dc-citation--brackets.dc-citation--verified .dc-citation-text {
83
84
  color: var(--dc-color-verified, #2563eb);
84
85
  }
85
86
 
@@ -87,7 +88,8 @@
87
88
  background-color: var(--dc-verified-hover-bg, rgba(37, 99, 235, 0.08));
88
89
  }
89
90
 
90
- .dc-citation--verified:hover .dc-citation-text {
91
+ /* Only brackets variant gets verified blue text on hover */
92
+ .dc-citation--brackets.dc-citation--verified:hover .dc-citation-text {
91
93
  text-decoration: underline;
92
94
  color: var(--dc-color-verified-hover, #1d4ed8);
93
95
  }
@@ -7,7 +7,7 @@ import { getCitationPageNumber } from "../parsing/normalizeCitation.js";
7
7
  export function generateCitationKey(citation) {
8
8
  const pageNumber = citation.pageNumber || getCitationPageNumber(citation.startPageKey);
9
9
  const keyParts = [
10
- citation.fileId || "",
10
+ citation.attachmentId || "",
11
11
  pageNumber?.toString() || "",
12
12
  citation.fullPhrase || "",
13
13
  citation.keySpan?.toString() || "",
@@ -8,7 +8,7 @@ export interface VerifyCitationResponse {
8
8
  };
9
9
  }
10
10
  export interface VerifyCitationRequest {
11
- fileId: string;
11
+ attachmentId: string;
12
12
  citations: {
13
13
  [key: string]: Citation;
14
14
  };
@@ -16,7 +16,7 @@ export interface VerifyCitationRequest {
16
16
  apiKey?: string;
17
17
  }
18
18
  export interface Citation {
19
- fileId?: string;
19
+ attachmentId?: string;
20
20
  fullPhrase?: string | null;
21
21
  keySpan?: string | null;
22
22
  startPageKey?: string | null;
@@ -6,7 +6,7 @@ export declare const PENDING_VERIFICATION_INDEX = -2;
6
6
  export declare const BLANK_VERIFICATION: Verification;
7
7
  export declare function deterministicIdFromVerification(verification: Verification): string;
8
8
  export interface Verification {
9
- fileId?: string | null;
9
+ attachmentId?: string | null;
10
10
  label?: string | null;
11
11
  pageNumber?: number | null;
12
12
  timestamp?: number | null;
@@ -2,12 +2,12 @@ import { sha1Hash } from "../utils/sha.js";
2
2
  export const NOT_FOUND_VERIFICATION_INDEX = -1;
3
3
  export const PENDING_VERIFICATION_INDEX = -2;
4
4
  export const BLANK_VERIFICATION = {
5
- fileId: null,
5
+ attachmentId: null,
6
6
  pageNumber: NOT_FOUND_VERIFICATION_INDEX,
7
7
  matchSnippet: null,
8
8
  source: null,
9
9
  citation: {
10
- fileId: undefined,
10
+ attachmentId: undefined,
11
11
  startPageKey: null,
12
12
  fullPhrase: null,
13
13
  keySpan: null,
@@ -17,5 +17,5 @@ export const BLANK_VERIFICATION = {
17
17
  },
18
18
  };
19
19
  export function deterministicIdFromVerification(verification) {
20
- return sha1Hash(`${verification.label}-${verification.fileId}-${verification.pageNumber}-${verification.hitIndexWithinPage}-${verification.matchSnippet}-${verification?.hitIndexWithinPage}`);
20
+ return sha1Hash(`${verification.label}-${verification.attachmentId}-${verification.pageNumber}-${verification.hitIndexWithinPage}-${verification.matchSnippet}-${verification?.hitIndexWithinPage}`);
21
21
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deepcitation/deepcitation-js",
3
- "version": "1.1.16",
3
+ "version": "1.1.18",
4
4
  "description": "DeepCitation JavaScript SDK for deterministic AI citation verification",
5
5
  "type": "module",
6
6
  "private": false,