@agent-native/creative-context 0.5.3 → 0.5.5

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.
Files changed (49) hide show
  1. package/dist/client/CreativeContextChip.js +1 -1
  2. package/dist/client/CreativeContextChip.js.map +1 -1
  3. package/dist/client/CreativeContextPanel.d.ts +1 -1
  4. package/dist/client/CreativeContextPanel.d.ts.map +1 -1
  5. package/dist/client/CreativeContextPanel.js +2 -1
  6. package/dist/client/CreativeContextPanel.js.map +1 -1
  7. package/dist/client/CreativeContextSettingsLink.js +1 -1
  8. package/dist/client/CreativeContextSettingsLink.js.map +1 -1
  9. package/dist/client/actions.d.ts.map +1 -1
  10. package/dist/client/actions.js +1 -1
  11. package/dist/client/actions.js.map +1 -1
  12. package/dist/client/agent-tab.d.ts +2 -1
  13. package/dist/client/agent-tab.d.ts.map +1 -1
  14. package/dist/client/agent-tab.js.map +1 -1
  15. package/dist/client/application-state.js +1 -1
  16. package/dist/client/application-state.js.map +1 -1
  17. package/dist/embeddings/providers.d.ts +1 -6
  18. package/dist/embeddings/providers.d.ts.map +1 -1
  19. package/dist/embeddings/providers.js +1 -175
  20. package/dist/embeddings/providers.js.map +1 -1
  21. package/dist/embeddings/types.d.ts +1 -18
  22. package/dist/embeddings/types.d.ts.map +1 -1
  23. package/dist/search/fusion.d.ts +1 -14
  24. package/dist/search/fusion.d.ts.map +1 -1
  25. package/dist/search/fusion.js +1 -24
  26. package/dist/search/fusion.js.map +1 -1
  27. package/dist/search/postgres-fts.d.ts +1 -21
  28. package/dist/search/postgres-fts.d.ts.map +1 -1
  29. package/dist/search/postgres-fts.js +1 -82
  30. package/dist/search/postgres-fts.js.map +1 -1
  31. package/dist/vector/pgvector.d.ts +1 -27
  32. package/dist/vector/pgvector.d.ts.map +1 -1
  33. package/dist/vector/pgvector.js +1 -116
  34. package/dist/vector/pgvector.js.map +1 -1
  35. package/package.json +3 -3
  36. package/src/client/CreativeContextChip.tsx +1 -1
  37. package/src/client/CreativeContextPanel.tsx +3 -6
  38. package/src/client/CreativeContextSettingsLink.tsx +1 -1
  39. package/src/client/actions.ts +4 -1
  40. package/src/client/agent-tab.tsx +2 -4
  41. package/src/client/application-state.ts +1 -1
  42. package/src/embeddings/providers.ts +1 -228
  43. package/src/embeddings/types.ts +6 -24
  44. package/src/eval/library-flow.acceptance.spec.tsx +4 -1
  45. package/src/search/fusion.ts +1 -39
  46. package/src/search/postgres-fts.spec.ts +10 -18
  47. package/src/search/postgres-fts.ts +1 -112
  48. package/src/vector/pgvector.spec.ts +19 -2
  49. package/src/vector/pgvector.ts +1 -164
@@ -1,117 +1,2 @@
1
- import { isPostgres } from "@agent-native/core/db";
2
- export const PGVECTOR_REQUIRED_MESSAGE = "Visual search requires Postgres with the pgvector extension in the configured DATABASE_URL database.";
3
- function checkedDimensions(value) {
4
- if (!Number.isInteger(value) || value < 1 || value > 16_000) {
5
- throw new Error("Embedding dimensions must be an integer from 1 to 16000.");
6
- }
7
- return value;
8
- }
9
- function vectorTable(dimensions) {
10
- return `creative_context_vectors_${checkedDimensions(dimensions)}`;
11
- }
12
- function vectorIndex(dimensions) {
13
- return `creative_context_vectors_${checkedDimensions(dimensions)}_hnsw`;
14
- }
15
- function vectorLiteral(vector, dimensions) {
16
- if (vector.length !== dimensions) {
17
- throw new Error(`Embedding has ${vector.length} values; expected ${dimensions}.`);
18
- }
19
- for (const value of vector) {
20
- if (!Number.isFinite(value)) {
21
- throw new Error("Embedding vectors may contain only finite numbers.");
22
- }
23
- }
24
- return `[${vector.join(",")}]`;
25
- }
26
- export function assertPgVectorAvailable(postgres = isPostgres()) {
27
- if (!postgres)
28
- throw new Error(PGVECTOR_REQUIRED_MESSAGE);
29
- }
30
- export async function ensurePgVectorIndex(db, dimensions, postgres = isPostgres()) {
31
- assertPgVectorAvailable(postgres);
32
- const table = vectorTable(dimensions);
33
- const index = vectorIndex(dimensions);
34
- try {
35
- await db.execute("CREATE EXTENSION IF NOT EXISTS vector");
36
- await db.execute(`
37
- CREATE TABLE IF NOT EXISTS ${table} (
38
- vector_key TEXT PRIMARY KEY,
39
- embedding_set_id TEXT NOT NULL,
40
- embedding vector(${checkedDimensions(dimensions)}) NOT NULL,
41
- updated_at TEXT NOT NULL
42
- )
43
- `);
44
- await db.execute(`CREATE INDEX IF NOT EXISTS ${index} ON ${table} USING hnsw (embedding vector_cosine_ops)`);
45
- }
46
- catch (error) {
47
- throw new Error(PGVECTOR_REQUIRED_MESSAGE, { cause: error });
48
- }
49
- }
50
- export async function upsertPgVector(db, input, postgres = isPostgres()) {
51
- assertPgVectorAvailable(postgres);
52
- await ensurePgVectorIndex(db, input.dimensions, postgres);
53
- const table = vectorTable(input.dimensions);
54
- await db.execute({
55
- sql: `
56
- INSERT INTO ${table} (vector_key, embedding_set_id, embedding, updated_at)
57
- VALUES (?, ?, ?::vector, ?)
58
- ON CONFLICT (vector_key) DO UPDATE SET
59
- embedding_set_id = EXCLUDED.embedding_set_id,
60
- embedding = EXCLUDED.embedding,
61
- updated_at = EXCLUDED.updated_at
62
- `,
63
- args: [
64
- input.vectorKey,
65
- input.embeddingSetId,
66
- vectorLiteral(input.vector, input.dimensions),
67
- input.updatedAt ?? new Date().toISOString(),
68
- ],
69
- });
70
- }
71
- export async function deletePgVectors(db, input, postgres = isPostgres()) {
72
- assertPgVectorAvailable(postgres);
73
- if (input.vectorKeys.length === 0)
74
- return 0;
75
- const table = vectorTable(input.dimensions);
76
- const placeholders = input.vectorKeys.map(() => "?").join(", ");
77
- const result = await db.execute({
78
- sql: `DELETE FROM ${table} WHERE vector_key IN (${placeholders})`,
79
- args: [...input.vectorKeys],
80
- });
81
- return result.rowsAffected;
82
- }
83
- export async function queryPgVectorIndex(db, input, postgres = isPostgres()) {
84
- assertPgVectorAvailable(postgres);
85
- const limit = Math.max(1, Math.min(200, Math.floor(input.limit ?? 40)));
86
- const allowed = input.allowedVectorKeys;
87
- if (allowed.length === 0)
88
- return [];
89
- const table = vectorTable(input.dimensions);
90
- const literal = vectorLiteral(input.vector, input.dimensions);
91
- const allowedSql = allowed.length
92
- ? ` AND vector_key IN (${allowed.map(() => "?").join(", ")})`
93
- : "";
94
- try {
95
- await db.execute("SET hnsw.iterative_scan = strict_order");
96
- }
97
- catch {
98
- // pgvector before iterative scans still supports the filtered exact query.
99
- }
100
- const result = await db.execute({
101
- sql: `
102
- SELECT vector_key, embedding_set_id,
103
- 1 - (embedding <=> ?::vector) AS score
104
- FROM ${table}
105
- WHERE embedding_set_id = ?${allowedSql}
106
- ORDER BY embedding <=> ?::vector
107
- LIMIT ?
108
- `,
109
- args: [literal, input.embeddingSetId, ...allowed, literal, limit],
110
- });
111
- return result.rows.map((row) => ({
112
- vectorKey: String(row.vector_key),
113
- embeddingSetId: String(row.embedding_set_id),
114
- score: Number(row.score),
115
- }));
116
- }
1
+ export * from "@agent-native/core/search";
117
2
  //# sourceMappingURL=pgvector.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"pgvector.js","sourceRoot":"","sources":["../../src/vector/pgvector.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pgvector.js","sourceRoot":"","sources":["../../src/vector/pgvector.ts"],"names":[],"mappings":"AAAA,cAAc,2BAA2B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/creative-context",
3
- "version": "0.5.3",
3
+ "version": "0.5.5",
4
4
  "description": "Shared creative context ingestion, versioning, search, brand DNA, and context-pack primitives for agent-native apps.",
5
5
  "homepage": "https://github.com/BuilderIO/agent-native#readme",
6
6
  "bugs": {
@@ -54,8 +54,8 @@
54
54
  "react-dom": "^19.2.7",
55
55
  "typescript-7": "npm:typescript@^7.0.2",
56
56
  "vitest": "^4.1.5",
57
- "@agent-native/core": "0.109.4",
58
- "@agent-native/toolkit": "0.5.0"
57
+ "@agent-native/core": "0.112.0",
58
+ "@agent-native/toolkit": "0.6.0"
59
59
  },
60
60
  "peerDependencies": {
61
61
  "@agent-native/core": ">=0.8.0",
@@ -1,4 +1,4 @@
1
- import { useT } from "@agent-native/core/client";
1
+ import { useT } from "@agent-native/core/client/i18n";
2
2
  import {
3
3
  Badge,
4
4
  DropdownMenu,
@@ -1,10 +1,7 @@
1
- import {
2
- useFormatters,
3
- useUploadResource,
4
- useT,
5
- type AgentPageScope,
6
- } from "@agent-native/core/client";
1
+ import { type AgentPageScope } from "@agent-native/core/client/agent-chat";
2
+ import { useFormatters, useT } from "@agent-native/core/client/i18n";
7
3
  import { useOrg } from "@agent-native/core/client/org";
4
+ import { useUploadResource } from "@agent-native/core/client/uploads";
8
5
  import {
9
6
  AlertDialog,
10
7
  AlertDialogAction,
@@ -1,4 +1,4 @@
1
- import { useT } from "@agent-native/core/client";
1
+ import { useT } from "@agent-native/core/client/i18n";
2
2
  import { IconArrowUpRight, IconBooks } from "@tabler/icons-react";
3
3
 
4
4
  export function CreativeContextSettingsLink({
@@ -1,4 +1,7 @@
1
- import { useActionMutation, useActionQuery } from "@agent-native/core/client";
1
+ import {
2
+ useActionMutation,
3
+ useActionQuery,
4
+ } from "@agent-native/core/client/hooks";
2
5
 
3
6
  import type {
4
7
  BrandDnaPayload,
@@ -1,7 +1,5 @@
1
- import type {
2
- AgentPageScope,
3
- SettingsTabItem,
4
- } from "@agent-native/core/client";
1
+ import { type AgentPageScope } from "@agent-native/core/client/agent-chat";
2
+ import { type SettingsTabItem } from "@agent-native/core/client/settings";
5
3
  import { IconLibrary } from "@tabler/icons-react";
6
4
  import type { ReactNode } from "react";
7
5
 
@@ -3,7 +3,7 @@ import {
3
3
  readClientAppState,
4
4
  setClientAppState,
5
5
  useChangeVersion,
6
- } from "@agent-native/core/client";
6
+ } from "@agent-native/core/client/hooks";
7
7
  import { useCallback, useEffect, useState } from "react";
8
8
 
9
9
  export const CREATIVE_CONTEXT_STATE_KEY = "creative-context";
@@ -1,228 +1 @@
1
- import { readBoundedResponseBytes } from "@agent-native/core/ingestion";
2
- import { resolveSecret } from "@agent-native/core/server";
3
-
4
- import type {
5
- EmbeddingFamily,
6
- EmbeddingImageInput,
7
- EmbeddingInputPurpose,
8
- MultimodalEmbeddingInput,
9
- } from "./types.js";
10
-
11
- const DEFAULT_DIMENSIONS = 1024;
12
-
13
- function dataUrl(image: EmbeddingImageInput): string {
14
- return `data:${image.mimeType};base64,${image.base64}`;
15
- }
16
-
17
- function normalizedInput(input: MultimodalEmbeddingInput) {
18
- const text = input.text?.trim();
19
- const images = input.images ?? [];
20
- if (!text && images.length === 0) {
21
- throw new Error("Embedding input needs text, an image, or both.");
22
- }
23
- return { text, images };
24
- }
25
-
26
- async function postJson(
27
- url: string,
28
- headers: Record<string, string>,
29
- body: unknown,
30
- providerModel: string,
31
- ): Promise<Record<string, unknown>> {
32
- const controller = new AbortController();
33
- const timeout = setTimeout(() => controller.abort(), 30_000);
34
- try {
35
- const response = await fetch(url, {
36
- method: "POST",
37
- headers: { "Content-Type": "application/json", ...headers },
38
- body: JSON.stringify(body),
39
- signal: controller.signal,
40
- });
41
- if (!response.ok) {
42
- throw new Error(
43
- `Embedding provider ${providerModel} failed with status ${response.status}.`,
44
- );
45
- }
46
- const bytes = await readBoundedResponseBytes(response, 1_000_000);
47
- return JSON.parse(new TextDecoder().decode(bytes)) as Record<
48
- string,
49
- unknown
50
- >;
51
- } catch (error) {
52
- if (controller.signal.aborted) {
53
- throw new Error(`Embedding provider ${providerModel} timed out.`);
54
- }
55
- throw error;
56
- } finally {
57
- clearTimeout(timeout);
58
- }
59
- }
60
-
61
- function numberVectors(value: unknown): number[][] {
62
- if (!Array.isArray(value))
63
- throw new Error("Embedding response was malformed.");
64
- return value.map((vector) => {
65
- if (
66
- !Array.isArray(vector) ||
67
- vector.some((entry) => !Number.isFinite(entry))
68
- ) {
69
- throw new Error("Embedding response contained an invalid vector.");
70
- }
71
- return vector.map(Number);
72
- });
73
- }
74
-
75
- export function createGeminiEmbeddingFamily(
76
- apiKey: string,
77
- dimensions = DEFAULT_DIMENSIONS,
78
- ): EmbeddingFamily {
79
- const model = "gemini-embedding-2";
80
- return {
81
- id: `gemini:${model}:${dimensions}`,
82
- provider: "gemini",
83
- model,
84
- version: "stable-2026-04",
85
- dimensions,
86
- supportedImageMimeTypes: ["image/png", "image/jpeg"],
87
- async embed(inputs, purpose) {
88
- const vectors: number[][] = [];
89
- for (const raw of inputs) {
90
- const input = normalizedInput(raw);
91
- const instruction =
92
- purpose === "query"
93
- ? "task: search result | query:"
94
- : "title: none | text:";
95
- const parts: Record<string, unknown>[] = [
96
- { text: `${instruction} ${input.text ?? ""}`.trim() },
97
- ...input.images.map((image) => ({
98
- inlineData: { mimeType: image.mimeType, data: image.base64 },
99
- })),
100
- ];
101
- const result = await postJson(
102
- `https://generativelanguage.googleapis.com/v1beta/models/${model}:embedContent`,
103
- { "x-goog-api-key": apiKey },
104
- {
105
- content: { parts },
106
- output_dimensionality: dimensions,
107
- },
108
- `gemini/${model}`,
109
- );
110
- const embedding = result.embedding as { values?: unknown } | undefined;
111
- vectors.push(...numberVectors([embedding?.values]));
112
- }
113
- return vectors;
114
- },
115
- };
116
- }
117
-
118
- export function createCohereEmbeddingFamily(
119
- apiKey: string,
120
- dimensions = DEFAULT_DIMENSIONS,
121
- ): EmbeddingFamily {
122
- const model = "embed-v4.0";
123
- return {
124
- id: `cohere:${model}:${dimensions}`,
125
- provider: "cohere",
126
- model,
127
- version: "v4.0",
128
- dimensions,
129
- supportedImageMimeTypes: [
130
- "image/png",
131
- "image/jpeg",
132
- "image/webp",
133
- "image/gif",
134
- ],
135
- async embed(inputs, purpose) {
136
- const contentInputs = inputs.map((raw) => {
137
- const input = normalizedInput(raw);
138
- return {
139
- content: [
140
- ...(input.text ? [{ type: "text", text: input.text }] : []),
141
- ...input.images.map((image) => ({
142
- type: "image_url",
143
- image_url: { url: dataUrl(image) },
144
- })),
145
- ],
146
- };
147
- });
148
- const result = await postJson(
149
- "https://api.cohere.com/v2/embed",
150
- { Authorization: `Bearer ${apiKey}` },
151
- {
152
- model,
153
- inputs: contentInputs,
154
- input_type: purpose === "query" ? "search_query" : "search_document",
155
- embedding_types: ["float"],
156
- output_dimension: dimensions,
157
- },
158
- `cohere/${model}`,
159
- );
160
- const embeddings = result.embeddings as
161
- | { float?: unknown; float_?: unknown }
162
- | undefined;
163
- return numberVectors(embeddings?.float ?? embeddings?.float_);
164
- },
165
- };
166
- }
167
-
168
- export function createVoyageEmbeddingFamily(apiKey: string): EmbeddingFamily {
169
- const model = "voyage-multimodal-3.5";
170
- return {
171
- id: `voyage:${model}:1024`,
172
- provider: "voyage",
173
- model,
174
- version: "3.5",
175
- dimensions: 1024,
176
- supportedImageMimeTypes: [
177
- "image/png",
178
- "image/jpeg",
179
- "image/webp",
180
- "image/gif",
181
- ],
182
- async embed(inputs, purpose) {
183
- const voyageInputs = inputs.map((raw) => {
184
- const input = normalizedInput(raw);
185
- return {
186
- content: [
187
- ...(input.text ? [{ type: "text", text: input.text }] : []),
188
- ...input.images.map((image) => ({
189
- type: "image_base64",
190
- image_base64: dataUrl(image),
191
- })),
192
- ],
193
- };
194
- });
195
- const result = await postJson(
196
- "https://api.voyageai.com/v1/multimodalembeddings",
197
- { Authorization: `Bearer ${apiKey}` },
198
- {
199
- model,
200
- inputs: voyageInputs,
201
- input_type: purpose,
202
- truncation: true,
203
- },
204
- `voyage/${model}`,
205
- );
206
- return numberVectors(result.embeddings);
207
- },
208
- };
209
- }
210
-
211
- export async function availableEmbeddingFamilies(): Promise<EmbeddingFamily[]> {
212
- const [gemini, cohere, voyage] = await Promise.all([
213
- resolveSecret("GEMINI_API_KEY").catch(() => null),
214
- resolveSecret("COHERE_API_KEY").catch(() => null),
215
- resolveSecret("VOYAGE_API_KEY").catch(() => null),
216
- ]);
217
- return [
218
- ...(gemini ? [createGeminiEmbeddingFamily(gemini)] : []),
219
- ...(cohere ? [createCohereEmbeddingFamily(cohere)] : []),
220
- ...(voyage ? [createVoyageEmbeddingFamily(voyage)] : []),
221
- ];
222
- }
223
-
224
- export function defaultEmbeddingFamily(
225
- families: readonly EmbeddingFamily[],
226
- ): EmbeddingFamily | null {
227
- return families.length === 1 ? (families[0] ?? null) : null;
228
- }
1
+ export * from "@agent-native/core/embeddings";
@@ -1,24 +1,6 @@
1
- export type EmbeddingInputPurpose = "query" | "document";
2
-
3
- export interface EmbeddingImageInput {
4
- mimeType: "image/png" | "image/jpeg" | "image/webp" | "image/gif";
5
- base64: string;
6
- }
7
-
8
- export interface MultimodalEmbeddingInput {
9
- text?: string;
10
- images?: EmbeddingImageInput[];
11
- }
12
-
13
- export interface EmbeddingFamily {
14
- id: string;
15
- provider: "gemini" | "cohere" | "voyage" | (string & {});
16
- model: string;
17
- version: string;
18
- dimensions: number;
19
- supportedImageMimeTypes?: readonly EmbeddingImageInput["mimeType"][];
20
- embed(
21
- inputs: readonly MultimodalEmbeddingInput[],
22
- purpose: EmbeddingInputPurpose,
23
- ): Promise<number[][]>;
24
- }
1
+ export type {
2
+ EmbeddingFamily,
3
+ EmbeddingImageInput,
4
+ EmbeddingInputPurpose,
5
+ MultimodalEmbeddingInput,
6
+ } from "@agent-native/core/embeddings";
@@ -4,13 +4,16 @@ import { readFile } from "node:fs/promises";
4
4
  import { renderToStaticMarkup } from "react-dom/server";
5
5
  import { describe, expect, it, vi } from "vitest";
6
6
 
7
- vi.mock("@agent-native/core/client", () => ({
7
+ vi.mock("@agent-native/core/client/hooks", () => ({
8
8
  getBrowserTabId: () => "acceptance-tab",
9
9
  readClientAppState: vi.fn(),
10
10
  setClientAppState: vi.fn(),
11
11
  useActionMutation: vi.fn(),
12
12
  useActionQuery: vi.fn(),
13
13
  useChangeVersion: () => 0,
14
+ }));
15
+
16
+ vi.mock("@agent-native/core/client/i18n", () => ({
14
17
  useT: () => (key: string) =>
15
18
  ({
16
19
  "creativeContext.off": "Off",
@@ -1,39 +1 @@
1
- export interface RankedCandidate<T> {
2
- key: string;
3
- value: T;
4
- score: number;
5
- reason?: string;
6
- }
7
-
8
- export interface FusedCandidate<T> extends RankedCandidate<T> {
9
- laneRanks: Record<string, number>;
10
- reasons: string[];
11
- }
12
-
13
- export function reciprocalRankFusion<T>(
14
- lanes: Readonly<Record<string, readonly RankedCandidate<T>[]>>,
15
- options: { rankConstant?: number; limit?: number } = {},
16
- ): FusedCandidate<T>[] {
17
- const rankConstant = Math.max(1, options.rankConstant ?? 60);
18
- const fused = new Map<string, FusedCandidate<T>>();
19
- for (const [lane, candidates] of Object.entries(lanes)) {
20
- candidates.forEach((candidate, index) => {
21
- const rank = index + 1;
22
- const current = fused.get(candidate.key) ?? {
23
- ...candidate,
24
- score: 0,
25
- laneRanks: {},
26
- reasons: [],
27
- };
28
- current.score += 1 / (rankConstant + rank);
29
- current.laneRanks[lane] = rank;
30
- if (candidate.reason && !current.reasons.includes(candidate.reason)) {
31
- current.reasons.push(candidate.reason);
32
- }
33
- fused.set(candidate.key, current);
34
- });
35
- }
36
- return [...fused.values()]
37
- .sort((a, b) => b.score - a.score || a.key.localeCompare(b.key))
38
- .slice(0, Math.max(1, options.limit ?? 40));
39
- }
1
+ export * from "@agent-native/core/search";
@@ -21,20 +21,16 @@ describe("Postgres creative-context FTS", () => {
21
21
  it("filters every Postgres candidate through accessible chunk ids", async () => {
22
22
  isPostgres.mockReturnValue(true);
23
23
  const db = {
24
- execute: vi
25
- .fn()
26
- .mockResolvedValueOnce({ rows: [], rowsAffected: 0 })
27
- .mockResolvedValueOnce({ rows: [], rowsAffected: 0 })
28
- .mockResolvedValueOnce({
29
- rows: [{ chunk_id: "allowed", item_version_id: "v1", score: 0.8 }],
30
- rowsAffected: 0,
31
- }),
24
+ execute: vi.fn().mockResolvedValueOnce({
25
+ rows: [{ chunk_id: "allowed", item_version_id: "v1", score: 0.8 }],
26
+ rowsAffected: 0,
27
+ }),
32
28
  };
33
29
  const hits = await queryPostgresFts(db, {
34
30
  query: "pricing slide",
35
31
  allowedChunkIds: ["allowed"],
36
32
  });
37
- const query = db.execute.mock.calls[2]?.[0] as {
33
+ const query = db.execute.mock.calls[0]?.[0] as {
38
34
  sql: string;
39
35
  args: unknown[];
40
36
  };
@@ -46,19 +42,15 @@ describe("Postgres creative-context FTS", () => {
46
42
  it("can search the global FTS index before access-scoped hydration", async () => {
47
43
  isPostgres.mockReturnValue(true);
48
44
  const db = {
49
- execute: vi
50
- .fn()
51
- .mockResolvedValueOnce({ rows: [], rowsAffected: 0 })
52
- .mockResolvedValueOnce({ rows: [], rowsAffected: 0 })
53
- .mockResolvedValueOnce({
54
- rows: [{ chunk_id: "late", item_version_id: "v9", score: 0.9 }],
55
- rowsAffected: 0,
56
- }),
45
+ execute: vi.fn().mockResolvedValueOnce({
46
+ rows: [{ chunk_id: "late", item_version_id: "v9", score: 0.9 }],
47
+ rowsAffected: 0,
48
+ }),
57
49
  };
58
50
  const hits = await queryPostgresFts(db, {
59
51
  query: "pricing slide",
60
52
  });
61
- const query = db.execute.mock.calls[2]?.[0] as {
53
+ const query = db.execute.mock.calls[0]?.[0] as {
62
54
  sql: string;
63
55
  args: unknown[];
64
56
  };