@ai-matrx/chat 0.3.11 → 0.3.15

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,41 @@
1
1
  # @ai-matrx/chat
2
2
 
3
+ ## 0.3.15 — 2026-10-07
4
+ ### The Export menu's .csv download is formula-guarded (AP-2)
5
+
6
+ - `csvExportItem` (the Export menu's `.csv` DOWNLOAD) is always formula-guarded: a person opens it in a spreadsheet, so `=HYPERLINK(...)` arrives as text.
7
+ - `rowsToCsv(rows, columns?, { spreadsheetSafe? })` is guarded by default; pass `{ spreadsheetSafe: false }` only where an AI or a parser reads the text.
8
+
9
+ **Consumer action:** none. A caller that feeds `rowsToCsv` text to an AI passes `{ spreadsheetSafe: false }`.
10
+
11
+ ## 0.3.14 — 2026-10-07
12
+ ### Re-sync before matrx-frontend consumes the published package (P27)
13
+
14
+ - Carries every matrx-frontend `packages/chat` edit after `f0b5af6a6a`: the `CopySplit` host slot (split
15
+ Copy on the person's own message), JSON extraction reading the bytes behind a split wrapper
16
+ (`stream-block-accumulator`, active-request selectors, `process-stream`), agent-copy export, and
17
+ `host/db-types.ts` regenerated from the frontend's current schema (SCOPES-W3 moved the scope tables to
18
+ `deprecated`; `ContextItemRow` reads them there).
19
+ - Consumer action: none.
20
+
21
+ ## 0.3.13 — 2026-10-07
22
+ ### No signed-in-only reads for a signed-out visitor (Applets L4)
23
+
24
+ - `refetchSingleMessage` makes no `chat.message` read for a signed-out visitor (anon holds no grant; a
25
+ guest run on `/p/<slug>` answered eight 42501s per run) and never retries a refusal — the bounded
26
+ durable read waits on lag, not on an error.
27
+ - The desktop-presence warm waits for the chat host (`onChatHostConfigured`): it loaded before
28
+ `<ChatProvider>` rendered and announced "No chat host is configured" on every `/p` load.
29
+ - Consumer action: none.
30
+
31
+ ## 0.3.12 — 2026-10-07
32
+ ### One copy, one toast (AP-2)
33
+
34
+ - Sandbox file viewer, transcript integrity report, prompt preview and voice debug copies passed their
35
+ success words to the notifying `useClipboard` hook instead of toasting a second time after the kit's
36
+ own "Text copied to clipboard!".
37
+ - Consumer action: none.
38
+
3
39
  ## 0.3.11 — 2026-10-07
4
40
  ### Re-sync before matrx-frontend consumes the published package (P27)
5
41
 
@@ -25,11 +25,19 @@ export interface ExportItem {
25
25
  export declare function exportFilename(base: string, extension: string): string;
26
26
  export declare function jsonExportItem(data: unknown | (() => unknown), label?: string): ExportItem;
27
27
  export declare function textExportItem(content: string | (() => string), label: string, extension?: string): ExportItem;
28
- /** Rows → CSV (Alchemy's one writer). Columns: explicit list, or the union of keys across rows. */
28
+ /**
29
+ * Rows → CSV (Alchemy's one writer). Columns: explicit list, or the union of keys across rows.
30
+ * `spreadsheetSafe` defaults ON (kit `spreadsheetSafeByDefault`: a .csv is opened in a
31
+ * spreadsheet, so formula-looking text gets a leading `'`). Pass `{ spreadsheetSafe: false }` ONLY
32
+ * where an AI or a parser reads the text — never for a file a person downloads.
33
+ */
29
34
  export declare function rowsToCsv(rows: Array<Record<string, unknown>>, columns?: Array<{
30
35
  key: string;
31
36
  header: string;
32
- }>): string;
37
+ }>, options?: {
38
+ spreadsheetSafe?: boolean;
39
+ }): string;
40
+ /** The Export menu's .csv DOWNLOAD — a person opens it in a spreadsheet, so it is always formula-guarded. */
33
41
  export declare function csvExportItem(rows: Array<Record<string, unknown>> | (() => Array<Record<string, unknown>>), label?: string, columns?: Array<{
34
42
  key: string;
35
43
  header: string;
@@ -35,14 +35,15 @@ function textExportItem(content, label, extension = "txt") {
35
35
  })
36
36
  };
37
37
  }
38
- function rowsToCsv(rows, columns) {
38
+ function rowsToCsv(rows, columns, options = {}) {
39
39
  const cols = columns ?? [...new Set(rows.flatMap((row) => Object.keys(row)))].map((key) => ({
40
40
  key,
41
41
  header: key
42
42
  }));
43
43
  return toDelimitedText(
44
44
  cols.map((c) => c.header),
45
- rows.map((row) => cols.map((c) => row[c.key] ?? null))
45
+ rows.map((row) => cols.map((c) => row[c.key] ?? null)),
46
+ options.spreadsheetSafe === void 0 ? {} : { spreadsheetSafe: options.spreadsheetSafe }
46
47
  );
47
48
  }
48
49
  function csvExportItem(rows, label = "CSV", columns) {
@@ -5,7 +5,6 @@ import { toast as copyToast } from "../../../host/notify.js";
5
5
  import { Button } from "@ai-matrx/design-system/controls";
6
6
  import { useCallback, useEffect, useRef, useState } from "react";
7
7
  import { FileText, Loader2, RefreshCw, Copy, Check } from "lucide-react";
8
- import { toast } from "../../../host/notify.js";
9
8
  import { createSandboxFilesystemAdapter } from "../../../host/ui-slots.js";
10
9
  import { ErrorAlchemyMenu } from "../../../host/ui-slots.js";
11
10
  function SandboxFileViewer({
@@ -49,9 +48,8 @@ function SandboxFileViewer({
49
48
  }, [initialPath, sandboxRowId, load]);
50
49
  const handleCopy = async () => {
51
50
  if (content == null) return;
52
- if (!await copyText(content)) return;
51
+ if (!await copyText(content, "File contents copied")) return;
53
52
  setCopied(true);
54
- toast.success("File contents copied");
55
53
  setTimeout(() => setCopied(false), 1500);
56
54
  };
57
55
  return /* @__PURE__ */ jsxs("div", { className: "flex h-full flex-col overflow-hidden", children: [
@@ -8,7 +8,6 @@ import { Stethoscope } from "lucide-react";
8
8
  import { useAppSelector, useAppStore } from "../../../store/hooks.js";
9
9
  import { selectMessageCount } from "../../redux/execution-system/messages/messages.selectors.js";
10
10
  import { selectStreamPhase } from "../../redux/execution-system/selectors/aggregate.selectors.js";
11
- import { toast } from "../../../host/notify.js";
12
11
  import { useDebugContext } from "../../../host/prefs-react.js";
13
12
  import {
14
13
  buildTranscriptIntegrityReport,
@@ -67,10 +66,11 @@ function TranscriptIntegrityCopyButtonInner({
67
66
  pathname,
68
67
  effectiveVisibleGroupLimit
69
68
  });
70
- if (!await copyText(formatTranscriptIntegrityReport(report))) return;
71
- toast.success(
69
+ if (!await copyText(
70
+ formatTranscriptIntegrityReport(report),
72
71
  report.anomalies.length === 0 ? "Transcript report copied \u2014 no anomalies detected" : `Transcript report copied \u2014 ${report.anomalies.length} anomal${report.anomalies.length === 1 ? "y" : "ies"} flagged`
73
- );
72
+ ))
73
+ return;
74
74
  };
75
75
  return /* @__PURE__ */ jsx(
76
76
  Button,
@@ -2,7 +2,7 @@
2
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import { RegistryActionMenu, buildChatMessageActions, useDocumentDialogsHost } from "../../../../host/rich-document-slots.js";
4
4
  import { useState, Suspense, useCallback } from "react";
5
- import { Copy, Check, Edit, Send, MoreHorizontal, Pin, PinOff, Loader2 } from "lucide-react";
5
+ import { Edit, Send, MoreHorizontal, Pin, PinOff, Loader2 } from "lucide-react";
6
6
  import {
7
7
  togglePinnedMessage,
8
8
  usePendingPinMessageIds,
@@ -12,8 +12,7 @@ import {
12
12
  TapTargetButtonForGroup,
13
13
  TapTargetButtonGroup
14
14
  } from "@ai-matrx/tap-target";
15
- import { copyRichContent } from "../../../../host/ui-slots.js";
16
- import { SpeakerButton } from "../../../../host/ui-slots.js";
15
+ import { CopySplit, SpeakerButton } from "../../../../host/ui-slots.js";
17
16
  import { useAppDispatch, useAppSelector, useAppStore } from "../../../../store/hooks.js";
18
17
  import { useOpenFullScreenMarkdownEditorBridge } from "../../../../host/window-openers.js";
19
18
  import { selectMessagePosition } from "../../../redux/execution-system/messages/messages.selectors.js";
@@ -69,7 +68,6 @@ function UserActionBar({
69
68
  const dispatch = useAppDispatch();
70
69
  const store = useAppStore();
71
70
  const openEditor = useOpenFullScreenMarkdownEditorBridge();
72
- const [isCopied, setIsCopied] = useState(false);
73
71
  const isPinned = usePinnedMessageIds().has(messageId);
74
72
  const pinPending = usePendingPinMessageIds().has(messageId);
75
73
  const [showOptionsMenu, setShowOptionsMenu] = useState(false);
@@ -82,12 +80,6 @@ function UserActionBar({
82
80
  const showOptions = useAppSelector(
83
81
  selectShowUserMessageOptions(conversationId)
84
82
  );
85
- const handleCopy = async () => {
86
- if (await copyRichContent(content, "default", { toast: false })) {
87
- setIsCopied(true);
88
- setTimeout(() => setIsCopied(false), 2e3);
89
- }
90
- };
91
83
  const handleEditAction = useCallback(
92
84
  (actionId, newContent) => routeUserEditAction(dispatch, {
93
85
  actionId,
@@ -183,14 +175,7 @@ function UserActionBar({
183
175
  const canFork = (messagePosition ?? 0) > 0;
184
176
  return /* @__PURE__ */ jsxs(Fragment, { children: [
185
177
  /* @__PURE__ */ jsxs(TapTargetButtonGroup, { surface: "solid", children: [
186
- /* @__PURE__ */ jsx(
187
- TapTargetButtonForGroup,
188
- {
189
- onClick: handleCopy,
190
- ariaLabel: "Copy message",
191
- icon: isCopied ? /* @__PURE__ */ jsx(Check, { className: "w-4 h-4 text-blue-500 dark:text-blue-400" }) : /* @__PURE__ */ jsx(Copy, { className: "w-4 h-4 text-muted-foreground" })
192
- }
193
- ),
178
+ /* @__PURE__ */ jsx(CopySplit, { text: content, label: "Copy message" }),
194
179
  /* @__PURE__ */ jsx(SpeakerButton, { text: content, variant: "group" }),
195
180
  /* @__PURE__ */ jsx(
196
181
  TapTargetButtonForGroup,
@@ -5,7 +5,6 @@ import { toast as copyToast } from "../../host/notify.js";
5
5
  import { useEffect, useState } from "react";
6
6
  import { Loader2, Copy, RefreshCw, AlertTriangle } from "lucide-react";
7
7
  import { Button } from "@ai-matrx/design-system/controls";
8
- import { toast } from "../../host/notify.js";
9
8
  import { useAppStore } from "../../store/hooks.js";
10
9
  import { requestPromptPreview } from "./service.js";
11
10
  import { ErrorAlchemyMenu } from "../../host/ui-slots.js";
@@ -43,8 +42,7 @@ function PromptPreviewContent({
43
42
  };
44
43
  }, [conversationId, store, nonce]);
45
44
  const copy = async (label, text) => {
46
- if (!await copyText(text)) return;
47
- toast.success(`${label} copied`);
45
+ if (!await copyText(text, `${label} copied`)) return;
48
46
  };
49
47
  return /* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-1 flex-col gap-3 bg-textured p-4", children: [
50
48
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
@@ -122,6 +122,20 @@ export declare const NON_ANSWER_BLOCK_TYPES: Set<string>;
122
122
  * chunk text. `editedText` wins, mirroring the selectors.
123
123
  */
124
124
  export declare function deriveAnswerText(request: Pick<ActiveRequest, "renderBlockOrder" | "renderBlocks" | "editedText">): string;
125
+ /**
126
+ * The answer text FOR DATA CAPTURE (JSON extraction). Identical to
127
+ * {@link deriveAnswerText} except where a JSON wrapper holding kinds was split
128
+ * for display (A7: kinds + a residual that has the kinds REMOVED): those
129
+ * blocks are replaced by the bytes they were cut from. Without this, a
130
+ * structured answer whose arrays hold `{"__kind": …}` items was captured with
131
+ * those arrays EMPTY — an Applet's `sources` saved as `[]` (2026-10-07).
132
+ * A split whose blocks are no longer all present (a rewind) falls back to the
133
+ * blocks themselves.
134
+ */
135
+ export declare function deriveAnswerDataText(request: Pick<ActiveRequest, "renderBlockOrder" | "renderBlocks" | "editedText">, wrapperSplits: ReadonlyArray<{
136
+ blockIds: string[];
137
+ source: string;
138
+ }>): string;
125
139
  /**
126
140
  * Lossless document projection of an answer for string destinations that
127
141
  * intentionally accept Content IR. Plain-answer consumers keep using
@@ -47,6 +47,35 @@ function deriveAnswerText(request) {
47
47
  const answer = order.map((id) => blocks[id]).filter((b) => b && !NON_ANSWER_BLOCK_TYPES.has(b.type)).map((b) => b?.content ?? "").filter(Boolean).join("\n");
48
48
  return stripThinkingStreaming(answer).visible;
49
49
  }
50
+ function deriveAnswerDataText(request, wrapperSplits) {
51
+ if (wrapperSplits.length === 0) return deriveAnswerText(request);
52
+ const { renderBlockOrder: order, renderBlocks: blocks, editedText } = request;
53
+ if (editedText !== null && editedText !== void 0) return editedText;
54
+ if (!order || !blocks || order.length === 0) return "";
55
+ const present = new Set(order);
56
+ const splitOf = /* @__PURE__ */ new Map();
57
+ for (const split of wrapperSplits) {
58
+ if (split.blockIds.length === 0) continue;
59
+ if (!split.blockIds.every((id) => present.has(id))) continue;
60
+ for (const id of split.blockIds) splitOf.set(id, split);
61
+ }
62
+ const emitted = /* @__PURE__ */ new Set();
63
+ const parts = [];
64
+ for (const id of order) {
65
+ const split = splitOf.get(id);
66
+ if (split) {
67
+ if (!emitted.has(split)) {
68
+ emitted.add(split);
69
+ parts.push(split.source);
70
+ }
71
+ continue;
72
+ }
73
+ const block = blocks[id];
74
+ if (!block || NON_ANSWER_BLOCK_TYPES.has(block.type)) continue;
75
+ if (block.content) parts.push(block.content);
76
+ }
77
+ return stripThinkingStreaming(parts.join("\n")).visible;
78
+ }
50
79
  function hasKindMarker(value) {
51
80
  return typeof value === "object" && value !== null && !Array.isArray(value) && typeof value.__kind === "string";
52
81
  }
@@ -1169,6 +1198,7 @@ export {
1169
1198
  NON_ANSWER_BLOCK_TYPES,
1170
1199
  blockCarriesDataNotText,
1171
1200
  blockHasSomethingToRender,
1201
+ deriveAnswerDataText,
1172
1202
  deriveAnswerDocumentText,
1173
1203
  deriveAnswerText,
1174
1204
  deriveDecisionResultText,
@@ -1,11 +1,20 @@
1
1
  import { getLiveDesktopInstance } from "./desktop-presence.js";
2
2
  import { registerClientCapability } from "./registry.js";
3
+ import { isChatHostConfigured, onChatHostConfigured } from "../../../../host/configure.js";
3
4
  function toSysPlatform(platform) {
4
5
  return platform === "windows" ? "win32" : platform;
5
6
  }
6
7
  const PRESENCE_BUDGET_MS = 150;
7
8
  if (typeof window !== "undefined") {
8
- setTimeout(() => void getLiveDesktopInstance(), 0);
9
+ const warm = () => setTimeout(() => void getLiveDesktopInstance(), 0);
10
+ if (isChatHostConfigured()) {
11
+ warm();
12
+ } else {
13
+ const stop = onChatHostConfigured(() => {
14
+ stop();
15
+ warm();
16
+ });
17
+ }
9
18
  }
10
19
  registerClientCapability({
11
20
  name: "desktop-native",
@@ -3,6 +3,7 @@ import { supabase } from "../../../../host/db.js";
3
3
  import { updateMessageRecord } from "../messages/messages.slice.js";
4
4
  import { messageRowToRecord } from "../thunks/conversation-bundle.js";
5
5
  import { durableRecordId } from "@ai-matrx/kit/ids";
6
+ import { isSignedOutVisitor } from "../../../../host/identity.js";
6
7
  function hasDisplayableMessageContent(value) {
7
8
  if (!Array.isArray(value)) return false;
8
9
  return value.some((part) => {
@@ -27,6 +28,9 @@ const refetchSingleMessage = createAsyncThunk(
27
28
  message: "This answer has no saved copy yet \u2014 nothing to reload."
28
29
  });
29
30
  }
31
+ if (await isSignedOutVisitor()) {
32
+ return { conversationId, messageId, found: false, refreshed: false };
33
+ }
30
34
  const read = () => {
31
35
  const byId = supabase.schema("chat").from("message").select("*").eq("id", messageId);
32
36
  const scoped = persistedConversationId ? byId.eq("conversation_id", persistedConversationId) : byId;
@@ -35,6 +39,7 @@ const refetchSingleMessage = createAsyncThunk(
35
39
  let result = await read();
36
40
  let delay = 250;
37
41
  for (let attempt = 0; waitForReadable && attempt < 7; attempt++) {
42
+ if (result.error) break;
38
43
  const hasBody = hasDisplayableMessageContent(
39
44
  result.data?.user_content ?? result.data?.content
40
45
  );
@@ -111,7 +111,7 @@ import { docKindForContextKey } from "../../../utils/workingDocumentContext.js";
111
111
  import { isCanvasItemContextKey } from "../../../utils/canvasItemContext.js";
112
112
  import { StreamingJsonTracker } from "@ai-matrx/kit/json-extract";
113
113
  import { StreamBlockAccumulator } from "../utils/stream-block-accumulator.js";
114
- import { deriveAnswerText } from "../active-requests/active-requests.selectors.js";
114
+ import { deriveAnswerDataText } from "../active-requests/active-requests.selectors.js";
115
115
  import { captureError } from "../../../../host/diagnostics.js";
116
116
  import { readReceiptThread, withReceiptHandle } from "../instance-resources/remark-handles.js";
117
117
  import { stampPersistedRemarkHandles } from "../message-crud/stamp-remark-handles.thunk.js";
@@ -351,7 +351,7 @@ async function processStream({
351
351
  const runJsonExtraction = (isFinal) => {
352
352
  if (!jsonTracker) return;
353
353
  const req = getState().activeRequests.byRequestId[requestId];
354
- const answerText = req ? deriveAnswerText(req) : "";
354
+ const answerText = req ? deriveAnswerDataText(req, blockAccumulator.getWrapperSplitSources()) : "";
355
355
  if (!reasoningLeakReported && hasReasoningLeak(answerText)) {
356
356
  reasoningLeakReported = true;
357
357
  captureError({
@@ -38,6 +38,15 @@ export declare class StreamBlockAccumulator {
38
38
  * to be a different typed block (see the bare-JSON duplication note there).
39
39
  */
40
40
  private currentBlockEmitted;
41
+ /**
42
+ * THE DATA KEEPS ITS KINDS. A JSON wrapper that holds kinds is drawn as its
43
+ * kinds plus a residual (A7) — the residual is the wrapper's data WITH ITS
44
+ * KINDS REMOVED, so it is a display projection, never the answer. Every
45
+ * such split remembers the bytes it was cut from, keyed by its first block
46
+ * index, so data capture (`deriveAnswerDataText`) reads what the model wrote.
47
+ * Live only; a rewind forgets every split at or past its boundary.
48
+ */
49
+ private wrapperSplits;
41
50
  private subState;
42
51
  /**
43
52
  * URL + label for the current `audio` block. The accumulator's single-line
@@ -148,6 +157,11 @@ export declare class StreamBlockAccumulator {
148
157
  * the local streaming state so the replacement provider attempt cannot
149
158
  * concatenate onto or resurrect the failed attempt.
150
159
  */
160
+ /** The source bytes behind every live A7 wrapper split (see `wrapperSplits`). */
161
+ getWrapperSplitSources(): Array<{
162
+ blockIds: string[];
163
+ source: string;
164
+ }>;
151
165
  rewindToBlockCount(blockCount: number): void;
152
166
  /**
153
167
  * Close the current text block at an interleaved-content boundary — a tool
@@ -321,6 +321,15 @@ class StreamBlockAccumulator {
321
321
  * to be a different typed block (see the bare-JSON duplication note there).
322
322
  */
323
323
  currentBlockEmitted = false;
324
+ /**
325
+ * THE DATA KEEPS ITS KINDS. A JSON wrapper that holds kinds is drawn as its
326
+ * kinds plus a residual (A7) — the residual is the wrapper's data WITH ITS
327
+ * KINDS REMOVED, so it is a display projection, never the answer. Every
328
+ * such split remembers the bytes it was cut from, keyed by its first block
329
+ * index, so data capture (`deriveAnswerDataText`) reads what the model wrote.
330
+ * Live only; a rewind forgets every split at or past its boundary.
331
+ */
332
+ wrapperSplits = /* @__PURE__ */ new Map();
324
333
  subState = { kind: "none" };
325
334
  /**
326
335
  * URL + label for the current `audio` block. The accumulator's single-line
@@ -524,7 +533,19 @@ ${this.pendingLineFragment}` : this.pendingLineFragment
524
533
  * the local streaming state so the replacement provider attempt cannot
525
534
  * concatenate onto or resurrect the failed attempt.
526
535
  */
536
+ /** The source bytes behind every live A7 wrapper split (see `wrapperSplits`). */
537
+ getWrapperSplitSources() {
538
+ return [...this.wrapperSplits.values()].map((split) => ({
539
+ blockIds: split.indices.map((i) => `client_block_${i}`),
540
+ source: split.source
541
+ }));
542
+ }
527
543
  rewindToBlockCount(blockCount) {
544
+ for (const [start, split] of this.wrapperSplits) {
545
+ if (start >= blockCount || split.indices.some((i) => i >= blockCount)) {
546
+ this.wrapperSplits.delete(start);
547
+ }
548
+ }
528
549
  this.irDisposeAll();
529
550
  this.currentBlockIndex = Math.max(0, blockCount);
530
551
  this.currentBlockType = "text";
@@ -1769,6 +1790,9 @@ ${this.currentBlockContent}
1769
1790
  const containerMetadata = genericXml || recoveredXmlContainer ? { genericXmlContainer: true } : void 0;
1770
1791
  let emitted = 0;
1771
1792
  let followsKind = false;
1793
+ const splitStart = this.currentBlockIndex;
1794
+ const splitIndices = [];
1795
+ const isWrapperSplit = pieces.some((piece) => piece.type === "residual");
1772
1796
  for (const piece of pieces) {
1773
1797
  if (piece.type === "chrome") continue;
1774
1798
  const isKind = piece.type === "kind";
@@ -1788,9 +1812,13 @@ ${this.currentBlockContent}
1788
1812
  metadata: asJson ? withIrEnvelope(content, void 0) : containerMetadata
1789
1813
  };
1790
1814
  dispatch(this.upsertAction({ requestId: this.requestId, block }));
1815
+ splitIndices.push(this.currentBlockIndex);
1791
1816
  emitted++;
1792
1817
  this.emitCount++;
1793
1818
  }
1819
+ if (isWrapperSplit && emitted > 0) {
1820
+ this.wrapperSplits.set(splitStart, { indices: splitIndices, source: recoverySource });
1821
+ }
1794
1822
  this.currentBlockEmitted = emitted > 0;
1795
1823
  return emitted > 0;
1796
1824
  }
@@ -20,7 +20,7 @@ import type { ComponentType } from "react";
20
20
  import type { AssociationSourceEdge, AssociationTargetEdge } from "@ai-matrx/associations";
21
21
  import type { ChatDatabase, Json } from "../../host/db-types.js";
22
22
  import { type AnyComponent, type AnyFn } from "../../host/registered-source.js";
23
- export type ContextItemRow = ChatDatabase["context"]["Tables"]["context_items"]["Row"];
23
+ export type ContextItemRow = ChatDatabase["deprecated"]["Tables"]["context_items"]["Row"];
24
24
  export type ContextItemValueType = ChatDatabase["public"]["Enums"]["context_value_type"];
25
25
  export type ContextValueType = ContextItemValueType;
26
26
  export interface ScopeNode {