@ai-matrx/chat 0.3.15 → 0.3.20

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 (116) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/dist/agents/components/agent-widgets/chat-assistant/CompactAssistantInput.js +1 -1
  3. package/dist/agents/components/inputs/AgentVariablesInline.js +1 -1
  4. package/dist/agents/components/inputs/smart-input/AgentTextarea.js +1 -1
  5. package/dist/agents/components/inputs/smart-input/InputActionButtons.js +1 -1
  6. package/dist/agents/components/inputs/smart-input/composer/ComposerAgentPeek.js +6 -6
  7. package/dist/agents/components/run/AgentRunHeader.js +1 -2
  8. package/dist/agents/components/shared/AgentConversationColumn.js +1 -1
  9. package/dist/agents/components/shared/AgentHeader.js +1 -2
  10. package/dist/agents/components/shared/AgentHeaderMobile.js +1 -2
  11. package/dist/agents/components/working-document/DocumentLinkPicker.d.ts +1 -1
  12. package/dist/agents/components/working-document/WorkingDocumentPanel.js +1 -1
  13. package/dist/agents/components/working-document/documents-workspace/DocumentsListRail.js +2 -2
  14. package/dist/agents/components/working-document/documents-workspace/DocumentsWorkspace.js +2 -2
  15. package/dist/agents/hooks/useInstanceInputUndoRedo.d.ts +1 -1
  16. package/dist/agents/hooks/useInstanceInputUndoRedo.js +1 -1
  17. package/dist/agents/hooks/useWorkingDocument.js +11 -7
  18. package/dist/agents/model-registry/model-classes.d.ts +1 -43
  19. package/dist/agents/model-registry/model-classes.js +1 -40
  20. package/dist/agents/model-registry/parse.d.ts +1 -88
  21. package/dist/agents/model-registry/parse.js +1 -246
  22. package/dist/agents/redux/agent-definition/selectors.d.ts +8 -8
  23. package/dist/agents/redux/execution-system/instance-working-document/cx-working-document.service.d.ts +18 -16
  24. package/dist/agents/redux/execution-system/instance-working-document/cx-working-document.service.js +101 -52
  25. package/dist/agents/redux/execution-system/instance-working-document/instance-working-document.slice.d.ts +2 -2
  26. package/dist/agents/redux/execution-system/instance-working-document/instance-working-document.thunks.d.ts +1 -1
  27. package/dist/agents/redux/execution-system/instance-working-document/scratchpad.thunks.d.ts +1 -1
  28. package/dist/agents/utils/platform-keys.d.ts +15 -0
  29. package/dist/agents/utils/platform-keys.js +69 -0
  30. package/dist/cx-chat/components/user-input/ConversationInput.js +1 -1
  31. package/dist/cx-conversation/ConversationInput.js +1 -1
  32. package/dist/host/db-types.d.ts +1123 -1691
  33. package/dist/host/ui-slots.d.ts +47 -64
  34. package/dist/host/ui-slots.js +10 -52
  35. package/dist/public-chat/components/GuidedVariableInputs.js +1 -1
  36. package/dist/public-chat/components/PublicVariableInputs.js +1 -1
  37. package/dist/react.cjs +11 -23
  38. package/dist/react.cjs.map +1 -1
  39. package/dist/react.js +11 -23
  40. package/dist/react.js.map +1 -1
  41. package/dist/surfaces/manifests/_conversation-document.manifest.js +1 -1
  42. package/dist/tool-call-visualization/registry/registry.js +13 -0
  43. package/dist/tool-call-visualization/result-fields/KeyValueGrid.d.ts +6 -0
  44. package/dist/tool-call-visualization/result-fields/KeyValueGrid.js +10 -2
  45. package/dist/ui/composer/useTouchOnlyDevice.js +1 -1
  46. package/dist/ui/skills-types.d.ts +1 -275
  47. package/dist/ui/skills-types.js +1 -0
  48. package/package.json +59 -33
  49. package/dist/agents/components/run/run-sidebar/AgentRunsSidebar.d.ts +0 -15
  50. package/dist/agents/components/run/run-sidebar/AgentRunsSidebar.js +0 -201
  51. package/dist/agents/components/run-controls/AgentExecutionTestModal.d.ts +0 -36
  52. package/dist/agents/components/run-controls/AgentExecutionTestModal.js +0 -440
  53. package/dist/agents/components/run-controls/AgentLauncherSidebarTester.d.ts +0 -6
  54. package/dist/agents/components/run-controls/AgentLauncherSidebarTester.js +0 -127
  55. package/dist/agents/components/run-controls/AgentWidgetInvokerTester.d.ts +0 -6
  56. package/dist/agents/components/run-controls/AgentWidgetInvokerTester.js +0 -84
  57. package/dist/agents/components/run-controls/CreatorRunPanel.d.ts +0 -22
  58. package/dist/agents/components/run-controls/CreatorRunPanel.js +0 -108
  59. package/dist/agents/components/run-controls/CreatorRunTabContent.d.ts +0 -30
  60. package/dist/agents/components/run-controls/CreatorRunTabContent.js +0 -319
  61. package/dist/agents/components/run-controls/PayloadTab.d.ts +0 -5
  62. package/dist/agents/components/run-controls/PayloadTab.js +0 -388
  63. package/dist/agents/components/run-controls/TesterSettingsPanel.d.ts +0 -71
  64. package/dist/agents/components/run-controls/TesterSettingsPanel.js +0 -470
  65. package/dist/agents/components/shared/AgentOptionsMenu.d.ts +0 -8
  66. package/dist/agents/components/shared/AgentOptionsMenu.js +0 -776
  67. package/dist/agents/components/shared/AgentPeekDuplicateButton.d.ts +0 -8
  68. package/dist/agents/components/shared/AgentPeekDuplicateButton.js +0 -66
  69. package/dist/agents/components/shared/AgentSaveStatus.d.ts +0 -7
  70. package/dist/agents/components/shared/AgentSaveStatus.js +0 -128
  71. package/dist/agents/components/shared/AgentSaveTapButton.d.ts +0 -18
  72. package/dist/agents/components/shared/AgentSaveTapButton.js +0 -105
  73. package/dist/agents/components/shared/useAgentSaveAction.d.ts +0 -33
  74. package/dist/agents/components/shared/useAgentSaveAction.js +0 -179
  75. package/dist/agents/components/shell/AgentsListHeader.d.ts +0 -1
  76. package/dist/agents/components/shell/AgentsListHeader.js +0 -69
  77. package/dist/agents/components/undo-history/AgentEditHistory.d.ts +0 -9
  78. package/dist/agents/components/undo-history/AgentEditHistory.js +0 -244
  79. package/dist/agents/components/unused/AgentSharedHeader.d.ts +0 -3
  80. package/dist/agents/components/unused/AgentSharedHeader.js +0 -134
  81. package/dist/agents/components/widgets/AgentWidgetsPage.d.ts +0 -11
  82. package/dist/agents/components/widgets/AgentWidgetsPage.js +0 -571
  83. package/dist/agents/components/widgets/WidgetVariableInputs.d.ts +0 -8
  84. package/dist/agents/components/widgets/WidgetVariableInputs.js +0 -80
  85. package/dist/agents/components/widgets/build-widget-launch.d.ts +0 -70
  86. package/dist/agents/components/widgets/build-widget-launch.js +0 -90
  87. package/dist/agents/hooks/useAgentAutoSave.d.ts +0 -1
  88. package/dist/agents/hooks/useAgentAutoSave.js +0 -127
  89. package/dist/agents/hooks/useAgentBuilderWriteHandlers.d.ts +0 -9
  90. package/dist/agents/hooks/useAgentBuilderWriteHandlers.js +0 -102
  91. package/dist/agents/hooks/useAgentDuplicateFlow.d.ts +0 -25
  92. package/dist/agents/hooks/useAgentDuplicateFlow.js +0 -133
  93. package/dist/agents/hooks/useAgentUndoRedo.d.ts +0 -31
  94. package/dist/agents/hooks/useAgentUndoRedo.js +0 -134
  95. package/dist/agents/hooks/useAgentUsages.d.ts +0 -17
  96. package/dist/agents/hooks/useAgentUsages.js +0 -36
  97. package/dist/agents/hooks/useDriftAlerts.d.ts +0 -11
  98. package/dist/agents/hooks/useDriftAlerts.js +0 -36
  99. package/dist/agents/hooks/useDriftReport.d.ts +0 -20
  100. package/dist/agents/hooks/useDriftReport.js +0 -34
  101. package/dist/agents/lifecycle/useAgentLifecycleActions.d.ts +0 -17
  102. package/dist/agents/lifecycle/useAgentLifecycleActions.js +0 -63
  103. package/dist/agents/message-flags/useMessageFlags.d.ts +0 -8
  104. package/dist/agents/message-flags/useMessageFlags.js +0 -68
  105. package/dist/agents/services/agent-definition-snapshot.service.d.ts +0 -15
  106. package/dist/agents/services/agent-definition-snapshot.service.js +0 -20
  107. package/dist/agents/surface-catalog-profile.d.ts +0 -96
  108. package/dist/agents/surface-catalog-profile.js +0 -79
  109. package/dist/cx-chat/hooks/useAgentBootstrap.d.ts +0 -1
  110. package/dist/cx-chat/hooks/useAgentBootstrap.js +0 -110
  111. package/dist/host/builder-door.d.ts +0 -61
  112. package/dist/host/builder-door.js +0 -25
  113. package/dist/next/lazy/CreatorRunPanel.d.ts +0 -1
  114. package/dist/next/lazy/CreatorRunPanel.js +0 -11
  115. package/dist/ui/composer/composerSubmit.d.ts +0 -50
  116. 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
@@ -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
+ };
@@ -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,