@ai-matrx/chat 0.3.11 → 0.3.19

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 (129) hide show
  1. package/CHANGELOG.md +83 -0
  2. package/dist/agent-copy/export.d.ts +10 -2
  3. package/dist/agent-copy/export.js +3 -2
  4. package/dist/agents/components/agent-widgets/chat-assistant/CompactAssistantInput.js +1 -1
  5. package/dist/agents/components/debug/SandboxFileViewer.js +1 -3
  6. package/dist/agents/components/inputs/AgentVariablesInline.js +1 -1
  7. package/dist/agents/components/inputs/smart-input/AgentTextarea.js +1 -1
  8. package/dist/agents/components/inputs/smart-input/InputActionButtons.js +1 -1
  9. package/dist/agents/components/inputs/smart-input/composer/ComposerAgentPeek.js +6 -6
  10. package/dist/agents/components/messages-display/TranscriptIntegrityCopyButton.js +4 -4
  11. package/dist/agents/components/messages-display/user/UserActionBar.js +3 -18
  12. package/dist/agents/components/run/AgentRunHeader.js +1 -2
  13. package/dist/agents/components/shared/AgentConversationColumn.js +1 -1
  14. package/dist/agents/components/shared/AgentHeader.js +1 -2
  15. package/dist/agents/components/shared/AgentHeaderMobile.js +1 -2
  16. package/dist/agents/components/working-document/DocumentLinkPicker.d.ts +1 -1
  17. package/dist/agents/components/working-document/WorkingDocumentPanel.js +1 -1
  18. package/dist/agents/components/working-document/documents-workspace/DocumentsListRail.js +2 -2
  19. package/dist/agents/components/working-document/documents-workspace/DocumentsWorkspace.js +2 -2
  20. package/dist/agents/hooks/useInstanceInputUndoRedo.d.ts +1 -1
  21. package/dist/agents/hooks/useInstanceInputUndoRedo.js +1 -1
  22. package/dist/agents/hooks/useWorkingDocument.js +7 -6
  23. package/dist/agents/model-registry/model-classes.d.ts +1 -43
  24. package/dist/agents/model-registry/model-classes.js +1 -40
  25. package/dist/agents/model-registry/parse.d.ts +1 -88
  26. package/dist/agents/model-registry/parse.js +1 -246
  27. package/dist/agents/prompt-preview/PromptPreviewContent.js +1 -3
  28. package/dist/agents/redux/agent-definition/selectors.d.ts +8 -8
  29. package/dist/agents/redux/execution-system/active-requests/active-requests.selectors.d.ts +14 -0
  30. package/dist/agents/redux/execution-system/active-requests/active-requests.selectors.js +30 -0
  31. package/dist/agents/redux/execution-system/client-capabilities/desktop-native.provider.js +10 -1
  32. package/dist/agents/redux/execution-system/instance-working-document/cx-working-document.service.d.ts +18 -16
  33. package/dist/agents/redux/execution-system/instance-working-document/cx-working-document.service.js +101 -52
  34. package/dist/agents/redux/execution-system/instance-working-document/instance-working-document.slice.d.ts +2 -2
  35. package/dist/agents/redux/execution-system/instance-working-document/instance-working-document.thunks.d.ts +1 -1
  36. package/dist/agents/redux/execution-system/instance-working-document/scratchpad.thunks.d.ts +1 -1
  37. package/dist/agents/redux/execution-system/message-crud/refetch-single-message.thunk.js +5 -0
  38. package/dist/agents/redux/execution-system/thunks/process-stream.js +2 -2
  39. package/dist/agents/redux/execution-system/utils/stream-block-accumulator.d.ts +14 -0
  40. package/dist/agents/redux/execution-system/utils/stream-block-accumulator.js +28 -0
  41. package/dist/agents/utils/platform-keys.d.ts +15 -0
  42. package/dist/agents/utils/platform-keys.js +69 -0
  43. package/dist/context/sources/scopes.d.ts +1 -1
  44. package/dist/cx-chat/components/user-input/ConversationInput.js +1 -1
  45. package/dist/cx-conversation/ConversationInput.js +1 -1
  46. package/dist/host/db-types.d.ts +23242 -23071
  47. package/dist/host/ui-slots.d.ts +50 -64
  48. package/dist/host/ui-slots.js +12 -52
  49. package/dist/public-chat/components/GuidedVariableInputs.js +1 -1
  50. package/dist/public-chat/components/PublicVariableInputs.js +1 -1
  51. package/dist/react.cjs +11 -23
  52. package/dist/react.cjs.map +1 -1
  53. package/dist/react.js +11 -23
  54. package/dist/react.js.map +1 -1
  55. package/dist/surfaces/manifests/_conversation-document.manifest.js +1 -1
  56. package/dist/tool-call-visualization/registry/registry.js +13 -0
  57. package/dist/ui/composer/useTouchOnlyDevice.js +1 -1
  58. package/dist/ui/skills-types.d.ts +1 -275
  59. package/dist/ui/skills-types.js +1 -0
  60. package/dist/voice-agent/components/VoiceDebugPanel.js +1 -3
  61. package/package.json +60 -34
  62. package/dist/agents/components/run/run-sidebar/AgentRunsSidebar.d.ts +0 -15
  63. package/dist/agents/components/run/run-sidebar/AgentRunsSidebar.js +0 -201
  64. package/dist/agents/components/run-controls/AgentExecutionTestModal.d.ts +0 -36
  65. package/dist/agents/components/run-controls/AgentExecutionTestModal.js +0 -440
  66. package/dist/agents/components/run-controls/AgentLauncherSidebarTester.d.ts +0 -6
  67. package/dist/agents/components/run-controls/AgentLauncherSidebarTester.js +0 -127
  68. package/dist/agents/components/run-controls/AgentWidgetInvokerTester.d.ts +0 -6
  69. package/dist/agents/components/run-controls/AgentWidgetInvokerTester.js +0 -84
  70. package/dist/agents/components/run-controls/CreatorRunPanel.d.ts +0 -22
  71. package/dist/agents/components/run-controls/CreatorRunPanel.js +0 -108
  72. package/dist/agents/components/run-controls/CreatorRunTabContent.d.ts +0 -30
  73. package/dist/agents/components/run-controls/CreatorRunTabContent.js +0 -319
  74. package/dist/agents/components/run-controls/PayloadTab.d.ts +0 -5
  75. package/dist/agents/components/run-controls/PayloadTab.js +0 -388
  76. package/dist/agents/components/run-controls/TesterSettingsPanel.d.ts +0 -71
  77. package/dist/agents/components/run-controls/TesterSettingsPanel.js +0 -470
  78. package/dist/agents/components/shared/AgentOptionsMenu.d.ts +0 -8
  79. package/dist/agents/components/shared/AgentOptionsMenu.js +0 -776
  80. package/dist/agents/components/shared/AgentPeekDuplicateButton.d.ts +0 -8
  81. package/dist/agents/components/shared/AgentPeekDuplicateButton.js +0 -66
  82. package/dist/agents/components/shared/AgentSaveStatus.d.ts +0 -7
  83. package/dist/agents/components/shared/AgentSaveStatus.js +0 -128
  84. package/dist/agents/components/shared/AgentSaveTapButton.d.ts +0 -18
  85. package/dist/agents/components/shared/AgentSaveTapButton.js +0 -105
  86. package/dist/agents/components/shared/useAgentSaveAction.d.ts +0 -33
  87. package/dist/agents/components/shared/useAgentSaveAction.js +0 -179
  88. package/dist/agents/components/shell/AgentsListHeader.d.ts +0 -1
  89. package/dist/agents/components/shell/AgentsListHeader.js +0 -69
  90. package/dist/agents/components/undo-history/AgentEditHistory.d.ts +0 -9
  91. package/dist/agents/components/undo-history/AgentEditHistory.js +0 -244
  92. package/dist/agents/components/unused/AgentSharedHeader.d.ts +0 -3
  93. package/dist/agents/components/unused/AgentSharedHeader.js +0 -134
  94. package/dist/agents/components/widgets/AgentWidgetsPage.d.ts +0 -11
  95. package/dist/agents/components/widgets/AgentWidgetsPage.js +0 -571
  96. package/dist/agents/components/widgets/WidgetVariableInputs.d.ts +0 -8
  97. package/dist/agents/components/widgets/WidgetVariableInputs.js +0 -80
  98. package/dist/agents/components/widgets/build-widget-launch.d.ts +0 -70
  99. package/dist/agents/components/widgets/build-widget-launch.js +0 -90
  100. package/dist/agents/hooks/useAgentAutoSave.d.ts +0 -1
  101. package/dist/agents/hooks/useAgentAutoSave.js +0 -127
  102. package/dist/agents/hooks/useAgentBuilderWriteHandlers.d.ts +0 -9
  103. package/dist/agents/hooks/useAgentBuilderWriteHandlers.js +0 -102
  104. package/dist/agents/hooks/useAgentDuplicateFlow.d.ts +0 -25
  105. package/dist/agents/hooks/useAgentDuplicateFlow.js +0 -133
  106. package/dist/agents/hooks/useAgentUndoRedo.d.ts +0 -31
  107. package/dist/agents/hooks/useAgentUndoRedo.js +0 -134
  108. package/dist/agents/hooks/useAgentUsages.d.ts +0 -17
  109. package/dist/agents/hooks/useAgentUsages.js +0 -36
  110. package/dist/agents/hooks/useDriftAlerts.d.ts +0 -11
  111. package/dist/agents/hooks/useDriftAlerts.js +0 -36
  112. package/dist/agents/hooks/useDriftReport.d.ts +0 -20
  113. package/dist/agents/hooks/useDriftReport.js +0 -34
  114. package/dist/agents/lifecycle/useAgentLifecycleActions.d.ts +0 -17
  115. package/dist/agents/lifecycle/useAgentLifecycleActions.js +0 -63
  116. package/dist/agents/message-flags/useMessageFlags.d.ts +0 -8
  117. package/dist/agents/message-flags/useMessageFlags.js +0 -68
  118. package/dist/agents/services/agent-definition-snapshot.service.d.ts +0 -15
  119. package/dist/agents/services/agent-definition-snapshot.service.js +0 -20
  120. package/dist/agents/surface-catalog-profile.d.ts +0 -96
  121. package/dist/agents/surface-catalog-profile.js +0 -79
  122. package/dist/cx-chat/hooks/useAgentBootstrap.d.ts +0 -1
  123. package/dist/cx-chat/hooks/useAgentBootstrap.js +0 -110
  124. package/dist/host/builder-door.d.ts +0 -61
  125. package/dist/host/builder-door.js +0 -25
  126. package/dist/next/lazy/CreatorRunPanel.d.ts +0 -1
  127. package/dist/next/lazy/CreatorRunPanel.js +0 -11
  128. package/dist/ui/composer/composerSubmit.d.ts +0 -50
  129. package/dist/ui/composer/composerSubmit.js +0 -32
@@ -10,27 +10,67 @@ function rowToCxWorkingDocument(row) {
10
10
  return {
11
11
  id: row.id,
12
12
  conversationId: row.metadata?.origin_conversation_id ?? null,
13
- kind: row.kind ?? "working",
13
+ kind: kindForTypeId(row.document_type_id),
14
14
  title: row.title,
15
- content: row.content,
15
+ content: row.body,
16
16
  version: row.version,
17
17
  createdAt: row.created_at,
18
18
  updatedAt: row.updated_at
19
19
  };
20
20
  }
21
- const WD = () => supabase.schema("workbench").from("working_documents");
21
+ const WORKING_DOCUMENT_ENTITY = "document";
22
+ const TYPE_SLUG = {
23
+ working: "working_document",
24
+ scratch: "scratch"
25
+ };
26
+ const WD_COLUMNS = "id, document_type_id, title, body, version, metadata, created_at, updated_at";
27
+ let typeIdsPromise = null;
28
+ let typeIdsResolved = null;
29
+ function workingDocumentTypeIds() {
30
+ if (!typeIdsPromise) {
31
+ typeIdsPromise = (async () => {
32
+ const { data, error } = await supabase.schema("platform").from("categories").select("id, slug").eq("dimension", "document_type").in("slug", [TYPE_SLUG.working, TYPE_SLUG.scratch]).is("deleted_at", null);
33
+ if (error) {
34
+ typeIdsPromise = null;
35
+ throw new Error(
36
+ `[working-document] document types read failed: ${error.message}`
37
+ );
38
+ }
39
+ const bySlug = new Map(
40
+ (data ?? []).map((r) => [r.slug, r.id])
41
+ );
42
+ const working = bySlug.get(TYPE_SLUG.working);
43
+ const scratch = bySlug.get(TYPE_SLUG.scratch);
44
+ if (!working || !scratch) {
45
+ typeIdsPromise = null;
46
+ throw new Error(
47
+ "[working-document] the content store has no Working document / Scratch type."
48
+ );
49
+ }
50
+ typeIdsResolved = { working, scratch };
51
+ return typeIdsResolved;
52
+ })();
53
+ }
54
+ return typeIdsPromise;
55
+ }
56
+ function kindForTypeId(typeId) {
57
+ return typeIdsResolved?.scratch === typeId ? "scratch" : "working";
58
+ }
59
+ const WD = () => supabase.schema("content").from("document");
22
60
  function getWorkingDocumentAccess(documentId) {
23
- return resolveResourceAccess(supabase, "working_document", documentId);
61
+ return resolveResourceAccess(supabase, WORKING_DOCUMENT_ENTITY, documentId);
24
62
  }
25
63
  async function getCxWorkingDocumentById(documentId) {
26
- const { data, error } = await WD().select("*").eq("id", documentId).is("deleted_at", null).maybeSingle();
64
+ await workingDocumentTypeIds();
65
+ const { data, error } = await WD().select(WD_COLUMNS).eq("id", documentId).is("deleted_at", null).maybeSingle();
27
66
  if (error) {
28
67
  throw new Error(`[working-document] get by id failed: ${error.message}`);
29
68
  }
30
69
  return data ? rowToCxWorkingDocument(data) : null;
31
70
  }
32
71
  async function updateCxWorkingDocumentTitle(id, title) {
33
- const { data, error } = await WD().update({ title }).eq("id", id).select("*").single();
72
+ await workingDocumentTypeIds();
73
+ const { data, error } = await WD().update({ title }).eq("id", id).select(WD_COLUMNS).single();
34
74
  if (error || !data) {
35
75
  throw new Error(
36
76
  `[working-document] title update failed: ${error?.message ?? "no row"}`
@@ -39,7 +79,8 @@ async function updateCxWorkingDocumentTitle(id, title) {
39
79
  return rowToCxWorkingDocument(data);
40
80
  }
41
81
  async function updateCxWorkingDocumentContent(id, content) {
42
- const { data, error } = await WD().update({ content }).eq("id", id).select("*").single();
82
+ await workingDocumentTypeIds();
83
+ const { data, error } = await WD().update({ body: content }).eq("id", id).select(WD_COLUMNS).single();
43
84
  if (error || !data) {
44
85
  throw new Error(
45
86
  `[working-document] content update failed: ${error?.message ?? "no row"}`
@@ -48,17 +89,18 @@ async function updateCxWorkingDocumentContent(id, content) {
48
89
  return rowToCxWorkingDocument(data);
49
90
  }
50
91
  async function commitWorkingDocumentContent(id, content, baseVersion) {
92
+ await workingDocumentTypeIds();
51
93
  const result = await guardedUpdate({
52
94
  expectedVersion: baseVersion,
53
95
  applyUpdate: async ({ expectedVersion, nextVersion }) => {
54
- const { data, error } = await WD().update({ content, version: nextVersion }).eq("id", id).eq("version", expectedVersion).select("*").maybeSingle();
96
+ const { data, error } = await WD().update({ body: content, version: nextVersion }).eq("id", id).eq("version", expectedVersion).select(WD_COLUMNS).maybeSingle();
55
97
  if (error) {
56
98
  throw new Error(`[working-document] commit failed: ${error.message}`);
57
99
  }
58
100
  return { data, error: null };
59
101
  },
60
102
  fetchCurrent: async () => {
61
- const { data, error } = await WD().select("*").eq("id", id).is("deleted_at", null).maybeSingle();
103
+ const { data, error } = await WD().select(WD_COLUMNS).eq("id", id).is("deleted_at", null).maybeSingle();
62
104
  if (error) {
63
105
  throw new Error(
64
106
  `[working-document] get by id failed: ${error.message}`
@@ -87,11 +129,7 @@ async function currentUserId() {
87
129
  } = await getClaimsUser(supabase);
88
130
  return user?.id ?? null;
89
131
  }
90
- const WD_GRANT_SPELLINGS = [
91
- "working_document",
92
- "working_documents",
93
- "workbench.working_documents"
94
- ];
132
+ const WD_GRANT_SPELLINGS = [WORKING_DOCUMENT_ENTITY, "content.document"];
95
133
  async function listGrantedDocumentIds(uid) {
96
134
  const nowIso = (/* @__PURE__ */ new Date()).toISOString();
97
135
  const { data, error } = await supabase.schema("iam").from("permissions").select("resource_id, status, expires_at").in("resource_type", WD_GRANT_SPELLINGS).eq("granted_to_user_id", uid).or("status.is.null,status.neq.rejected").or(`expires_at.is.null,expires_at.gt.${nowIso}`);
@@ -105,7 +143,8 @@ async function listGrantedDocumentIds(uid) {
105
143
  async function listUserDocuments(kind, limit = 50, scope = "mine") {
106
144
  const uid = await currentUserId();
107
145
  if (!uid) return [];
108
- let query = WD().select("*").eq("kind", kind).is("deleted_at", null).order("updated_at", { ascending: false }).limit(limit);
146
+ const typeIds = await workingDocumentTypeIds();
147
+ let query = WD().select(WD_COLUMNS).eq("document_type_id", typeIds[kind]).is("deleted_at", null).order("updated_at", { ascending: false }).limit(limit);
109
148
  if (scope === "mine") {
110
149
  query = query.eq("created_by", uid);
111
150
  } else {
@@ -122,7 +161,8 @@ async function listUserDocuments(kind, limit = 50, scope = "mine") {
122
161
  async function listRecentUserDocuments(limit = 100) {
123
162
  const uid = await currentUserId();
124
163
  if (!uid) return [];
125
- const { data, error } = await WD().select("id, metadata, kind, title, content, updated_at").eq("created_by", uid).is("deleted_at", null).order("updated_at", { ascending: false }).limit(limit);
164
+ const typeIds = await workingDocumentTypeIds();
165
+ const { data, error } = await WD().select("id, metadata, document_type_id, title, preview, updated_at").in("document_type_id", [typeIds.working, typeIds.scratch]).eq("created_by", uid).is("deleted_at", null).order("updated_at", { ascending: false }).limit(limit);
126
166
  if (error) {
127
167
  throw new Error(`[working-document] list recent failed: ${error.message}`);
128
168
  }
@@ -131,9 +171,9 @@ async function listRecentUserDocuments(limit = 100) {
131
171
  // Origin lives in metadata.origin_conversation_id (the legacy
132
172
  // conversation_id column was dropped in STEP 3).
133
173
  conversationId: row.metadata?.origin_conversation_id ?? null,
134
- kind: row.kind ?? "working",
174
+ kind: kindForTypeId(row.document_type_id),
135
175
  title: row.title,
136
- preview: (row.content ?? "").trim().slice(0, 200),
176
+ preview: (row.preview ?? "").trim().slice(0, 200),
137
177
  updatedAt: row.updated_at
138
178
  }));
139
179
  }
@@ -148,19 +188,26 @@ function materializeWorkingDocument(args) {
148
188
  return promise;
149
189
  }
150
190
  async function materializeWorkingDocumentImpl(args) {
191
+ const typeIds = await workingDocumentTypeIds();
151
192
  const { data, error } = await WD().upsert(
152
193
  {
153
194
  id: args.id,
154
195
  organization_id: args.organizationId,
155
- kind: args.kind,
196
+ document_type_id: typeIds[args.kind],
197
+ format: "markdown",
198
+ visibility: "personal",
156
199
  title: args.title,
157
- content: args.content,
200
+ body: args.content,
158
201
  metadata: {
159
202
  origin_conversation_id: args.conversationId
160
203
  }
204
+ // content_hash and data_class are derived by the database (the derive
205
+ // trigger and the per-type class floor); the generated Insert type
206
+ // lists them as required, so the row is passed as the canonical
207
+ // content-store writers pass it (features/rich-document createDocument).
161
208
  },
162
209
  { onConflict: "id" }
163
- ).select("*").single();
210
+ ).select(WD_COLUMNS).single();
164
211
  if (error || !data) {
165
212
  throw new Error(
166
213
  `[working-document] materialize failed: ${error?.message ?? "no row"}`
@@ -179,7 +226,7 @@ function edgeMetadata(kind, enabled) {
179
226
  }
180
227
  async function linkDocumentToConversation(args) {
181
228
  const res = await associationsService.add({
182
- sourceType: "working_document",
229
+ sourceType: WORKING_DOCUMENT_ENTITY,
183
230
  sourceId: args.documentId,
184
231
  targetType: "conversation",
185
232
  targetId: args.conversationId,
@@ -194,7 +241,7 @@ async function linkDocumentToConversation(args) {
194
241
  }
195
242
  async function unlinkDocumentFromConversation(documentId, conversationId) {
196
243
  const res = await associationsService.remove({
197
- sourceType: "working_document",
244
+ sourceType: WORKING_DOCUMENT_ENTITY,
198
245
  sourceId: documentId,
199
246
  targetType: "conversation",
200
247
  targetId: conversationId
@@ -223,7 +270,7 @@ async function listConversationDocuments(conversationId) {
223
270
  `[working-document] list links failed: ${res.error.message ?? res.error.code}`
224
271
  );
225
272
  }
226
- return res.data.edges.filter((e) => e.sourceType === "working_document").map((e) => {
273
+ return res.data.edges.filter((e) => e.sourceType === WORKING_DOCUMENT_ENTITY).map((e) => {
227
274
  const meta = e.metadata ?? {};
228
275
  return {
229
276
  documentId: e.sourceId,
@@ -234,7 +281,7 @@ async function listConversationDocuments(conversationId) {
234
281
  }
235
282
  async function listDocumentConversations(documentId) {
236
283
  const res = await associationsService.listForSources(
237
- "working_document",
284
+ WORKING_DOCUMENT_ENTITY,
238
285
  [documentId],
239
286
  "conversation"
240
287
  );
@@ -245,53 +292,54 @@ async function listDocumentConversations(documentId) {
245
292
  }
246
293
  return res.data.edges.map((e) => e.targetId);
247
294
  }
248
- const WORKING_DOCUMENT_TOKEN = "working_document";
249
295
  async function listWorkingDocumentVersions(documentId, limit = 50) {
250
- const { data, error } = await supabase.rpc("version_list", {
251
- p_token: WORKING_DOCUMENT_TOKEN,
252
- p_id: documentId,
253
- p_limit: limit
254
- });
296
+ const { data, error } = await supabase.schema("content").rpc("version_list", { p_document_id: documentId, p_limit: limit });
255
297
  if (error) {
256
298
  throw new Error(`[working-document] version_list failed: ${error.message}`);
257
299
  }
258
- return (data ?? []).map((r) => ({
259
- version: r.version,
260
- operation: r.operation,
300
+ const rows = data ?? [];
301
+ const newest = rows.reduce((m, r) => Math.max(m, r.content_version), 0);
302
+ return rows.map((r) => ({
303
+ version: r.content_version,
304
+ operation: r.change_kind,
261
305
  actorId: r.actor_id ?? null,
262
- occurredAt: r.occurred_at,
263
- isCurrent: r.is_current
306
+ occurredAt: r.created_at,
307
+ isCurrent: r.content_version === newest
264
308
  }));
265
309
  }
266
310
  async function getWorkingDocumentVersionContent(documentId, version) {
267
- const { data, error } = await supabase.rpc("version_snapshot", {
268
- p_token: WORKING_DOCUMENT_TOKEN,
269
- p_id: documentId,
270
- p_version: version
311
+ const { data, error } = await supabase.schema("content").rpc("version_get", {
312
+ p_document_id: documentId,
313
+ p_content_version: version
271
314
  });
272
315
  if (error) {
273
- throw new Error(
274
- `[working-document] version_snapshot failed: ${error.message}`
275
- );
316
+ throw new Error(`[working-document] version_get failed: ${error.message}`);
276
317
  }
277
- const content = data?.content;
278
- return typeof content === "string" ? content : null;
318
+ const row = Array.isArray(data) ? data[0] : data;
319
+ return typeof row?.body === "string" ? row.body : null;
279
320
  }
280
321
  async function restoreWorkingDocumentVersion(documentId, version) {
281
- const { data, error } = await supabase.rpc("version_restore", {
282
- p_token: WORKING_DOCUMENT_TOKEN,
283
- p_id: documentId,
284
- p_version: version
322
+ const { data: current, error: readError } = await WD().select("version").eq("id", documentId).maybeSingle();
323
+ if (readError || !current) {
324
+ throw new Error(
325
+ `[working-document] restore read failed: ${readError?.message ?? "no row"}`
326
+ );
327
+ }
328
+ const { data, error } = await supabase.schema("content").rpc("version_restore", {
329
+ p_document_id: documentId,
330
+ p_content_version: version,
331
+ p_expected_version: current.version
285
332
  });
286
333
  if (error) {
287
334
  throw new Error(
288
335
  `[working-document] version_restore failed: ${error.message}`
289
336
  );
290
337
  }
291
- return data;
338
+ const row = Array.isArray(data) ? data[0] : data;
339
+ return row?.content_version ?? version;
292
340
  }
293
341
  export {
294
- WORKING_DOCUMENT_TOKEN,
342
+ WORKING_DOCUMENT_ENTITY,
295
343
  commitWorkingDocumentContent,
296
344
  getCxWorkingDocumentById,
297
345
  getWorkingDocumentAccess,
@@ -309,5 +357,6 @@ export {
309
357
  softDeleteWorkingDocument,
310
358
  unlinkDocumentFromConversation,
311
359
  updateCxWorkingDocumentContent,
312
- updateCxWorkingDocumentTitle
360
+ updateCxWorkingDocumentTitle,
361
+ workingDocumentTypeIds
313
362
  };
@@ -19,7 +19,7 @@
19
19
  * - `content` — canonical document text shared by every mount.
20
20
  * - `binding` — durable source. `{ kind: "none" }` = ephemeral;
21
21
  * `{ kind: "cx_working_document", id }` = a row in
22
- * `cx_working_documents` (the chat default); `{ kind: "note" }`
22
+ * `content.document` (the chat default); `{ kind: "note" }`
23
23
  * = a `workbench.notes` row (working kind only); `studio_document`
24
24
  * = Scribe's source (shared context-value builder).
25
25
  * - `saving` — true while a bound-source persist is in flight.
@@ -56,7 +56,7 @@ export declare function workingDocKey(conversationId: string, kind?: WorkingDocu
56
56
  * Durable source the document is bound to.
57
57
  * - "none" — ephemeral (Redux only).
58
58
  * - "note" — a `workbench.notes` row (working kind only).
59
- * - "cx_working_document"— a `workbench.working_documents` row: the durable,
59
+ * - "cx_working_document"— a `content.document` row: the durable,
60
60
  * conversation-scoped default backing. The agent's
61
61
  * ctx_patch edits persist here (working kind) and
62
62
  * round-trip back via Supabase realtime.
@@ -5,7 +5,7 @@
5
5
  * scratch). MATERIALIZE-ON-WRITE model:
6
6
  *
7
7
  * - Enabling a document only RESERVES a client id (binding) — no DB row. The
8
- * durable `workbench.working_documents` row is created on the FIRST byte of
8
+ * durable `content.document` row is created on the FIRST byte of
9
9
  * content, by whichever party writes first
10
10
  * (`materializeWorkingDocumentThunk` for the user; the server's writeback
11
11
  * for the agent, reflected via the stream). The separate association edge
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Scratchpad thunks — the USER-GLOBAL scratchpad pool.
3
3
  *
4
- * Scratchpads are the user's personal notepads: `workbench.working_documents`
4
+ * Scratchpads are the user's personal notepads: `content.document`
5
5
  * rows (kind "scratch") owned by the user, DECOUPLED from conversations. One is
6
6
  * ACTIVE at all times (`userPreferences.scratchpad.activeId`, synced
7
7
  * cross-device); the active one is published read-only into a conversation's
@@ -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
  }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * platform-keys — which platform the person is on, and the undo/redo shortcut hints for it.
3
+ * Shared by the run-side input undo (useInstanceInputUndoRedo) and the agent builder's
4
+ * own undo hook (matrx-frontend features/agents/hooks/useAgentUndoRedo).
5
+ */
6
+ export type Platform = "mac" | "windows" | "ios" | "android" | "linux" | "unknown";
7
+ export declare function getPlatform(): Platform;
8
+ export declare function isMacLike(): boolean;
9
+ export declare function isTouchDevice(): boolean;
10
+ /**
11
+ * Returns a human-readable shortcut hint string for the current platform.
12
+ * Examples: "⌘Z" on Mac, "Ctrl+Z" on Windows, "Shake" on iOS.
13
+ */
14
+ export declare function getUndoShortcutHint(): string;
15
+ export declare function getRedoShortcutHint(): string;
@@ -0,0 +1,69 @@
1
+ let _cachedPlatform = null;
2
+ function getPlatform() {
3
+ if (_cachedPlatform) return _cachedPlatform;
4
+ if (typeof navigator === "undefined") return "unknown";
5
+ const ua = navigator.userAgent.toLowerCase();
6
+ if (/iphone|ipad|ipod/.test(ua)) {
7
+ _cachedPlatform = "ios";
8
+ } else if (/android/.test(ua)) {
9
+ _cachedPlatform = "android";
10
+ } else if (/mac/.test(ua)) {
11
+ _cachedPlatform = "mac";
12
+ } else if (/win/.test(ua)) {
13
+ _cachedPlatform = "windows";
14
+ } else if (/linux/.test(ua)) {
15
+ _cachedPlatform = "linux";
16
+ } else {
17
+ _cachedPlatform = "unknown";
18
+ }
19
+ return _cachedPlatform;
20
+ }
21
+ function isMacLike() {
22
+ const p = getPlatform();
23
+ return p === "mac" || p === "ios";
24
+ }
25
+ function isTouchDevice() {
26
+ const p = getPlatform();
27
+ return p === "ios" || p === "android";
28
+ }
29
+ function getUndoShortcutHint() {
30
+ const p = getPlatform();
31
+ switch (p) {
32
+ case "mac":
33
+ return "\u2318Z";
34
+ case "ios":
35
+ return "Shake to Undo";
36
+ case "android":
37
+ return "Ctrl+Z";
38
+ case "windows":
39
+ return "Ctrl+Z";
40
+ case "linux":
41
+ return "Ctrl+Z";
42
+ default:
43
+ return "Ctrl+Z";
44
+ }
45
+ }
46
+ function getRedoShortcutHint() {
47
+ const p = getPlatform();
48
+ switch (p) {
49
+ case "mac":
50
+ return "\u21E7\u2318Z";
51
+ case "ios":
52
+ return "Shake to Redo";
53
+ case "android":
54
+ return "Ctrl+Shift+Z";
55
+ case "windows":
56
+ return "Ctrl+Y";
57
+ case "linux":
58
+ return "Ctrl+Shift+Z";
59
+ default:
60
+ return "Ctrl+Shift+Z";
61
+ }
62
+ }
63
+ export {
64
+ getPlatform,
65
+ getRedoShortcutHint,
66
+ getUndoShortcutHint,
67
+ isMacLike,
68
+ isTouchDevice
69
+ };
@@ -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 {
@@ -53,7 +53,7 @@ import { useTouchOnlyDevice } from "../../../ui/composer/useTouchOnlyDevice.js";
53
53
  import {
54
54
  composerKeyIntent,
55
55
  intentTakesTheKey
56
- } from "../../../ui/composer/composerSubmit.js";
56
+ } from "@ai-matrx/kit/composer-keys";
57
57
  import { Button } from "@ai-matrx/design-system/controls";
58
58
  function uploadMimeToBlockType(mime) {
59
59
  return mime.startsWith("image/") ? "image" : "document";
@@ -64,7 +64,7 @@ import { useTouchOnlyDevice } from "../ui/composer/useTouchOnlyDevice.js";
64
64
  import {
65
65
  composerKeyIntent,
66
66
  intentTakesTheKey
67
- } from "../ui/composer/composerSubmit.js";
67
+ } from "@ai-matrx/kit/composer-keys";
68
68
  import { Button } from "@ai-matrx/design-system/controls";
69
69
  function ConversationInput({
70
70
  sessionId,