@agent-native/core 0.164.12 → 0.164.17

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 (90) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/netlify.toml +4 -0
  3. package/corpus/templates/clips/actions/request-transcript.ts +10 -6
  4. package/corpus/templates/content/actions/_database-source-utils.ts +25 -2
  5. package/corpus/templates/content/actions/_database-utils.ts +2 -0
  6. package/corpus/templates/content/actions/_local-folder-source.ts +186 -2
  7. package/corpus/templates/content/actions/connect-local-folder-source.ts +23 -8
  8. package/corpus/templates/content/actions/disconnect-local-folder-source.ts +3 -3
  9. package/corpus/templates/content/actions/resolve-local-folder-conflict.ts +2 -2
  10. package/corpus/templates/content/actions/sync-local-folder-source.ts +130 -12
  11. package/corpus/templates/content/app/components/LocalFolderLiveSync.tsx +77 -0
  12. package/corpus/templates/content/app/components/editor/BubbleToolbar.tsx +298 -3
  13. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +354 -8
  14. package/corpus/templates/content/app/components/editor/DocumentToolbar.tsx +23 -1
  15. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +81 -1
  16. package/corpus/templates/content/app/components/editor/database/sidebar.tsx +36 -0
  17. package/corpus/templates/content/app/components/editor/extensions/NotionExtensions.tsx +18 -4
  18. package/corpus/templates/content/app/components/editor/local-source-write-state.ts +52 -0
  19. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +244 -6
  20. package/corpus/templates/content/app/components/sidebar/local-source-hierarchy.ts +189 -0
  21. package/corpus/templates/content/app/hooks/use-create-page.ts +26 -4
  22. package/corpus/templates/content/app/hooks/use-documents.ts +83 -0
  23. package/corpus/templates/content/app/i18n-data.ts +239 -0
  24. package/corpus/templates/content/app/lib/desktop-content-files.ts +57 -1
  25. package/corpus/templates/content/app/lib/local-content-source-files.ts +210 -34
  26. package/corpus/templates/content/app/lib/local-folder-live-sync.ts +191 -0
  27. package/corpus/templates/content/app/root.tsx +2 -1
  28. package/corpus/templates/content/app/routes/_app.local-files.tsx +103 -38
  29. package/corpus/templates/content/docs/product/capabilities/content.source.file-folder.md +2 -2
  30. package/corpus/templates/content/docs/product/features/content.feature.bring-your-local-work.md +3 -3
  31. package/corpus/templates/content/docs/product/roadmap.md +2 -2
  32. package/corpus/templates/content/docs/solutions/2026-08-18-colored-text-highlighting-shape.md +693 -0
  33. package/corpus/templates/content/docs/solutions/2026-08-18-content-navigation-coherence-shape.md +482 -0
  34. package/corpus/templates/content/package.json +1 -0
  35. package/corpus/templates/content/shared/api.ts +25 -0
  36. package/corpus/templates/content/shared/nfm.ts +14 -3
  37. package/corpus/templates/factory/AGENTS.md +24 -29
  38. package/corpus/templates/factory/actions/get-slack-feedback-context.ts +8 -28
  39. package/corpus/templates/factory/actions/get-triage-config.ts +1 -0
  40. package/corpus/templates/factory/actions/govern-agent-native-pull-request.ts +67 -59
  41. package/corpus/templates/factory/actions/list-triage-items.ts +6 -1
  42. package/corpus/templates/factory/actions/reconcile-triage-run.ts +7 -1
  43. package/corpus/templates/factory/actions/save-triage-config.ts +27 -7
  44. package/corpus/templates/factory/actions/start-builder-for-item.ts +113 -31
  45. package/corpus/templates/factory/actions/view-screen.ts +12 -0
  46. package/corpus/templates/factory/app/components/factory/FactorySettingsView.tsx +457 -0
  47. package/corpus/templates/factory/app/components/factory/FactoryWorkspaceActions.tsx +53 -0
  48. package/corpus/templates/factory/app/components/layout/Layout.tsx +2 -0
  49. package/corpus/templates/factory/app/components/layout/Sidebar.tsx +3 -1
  50. package/corpus/templates/factory/app/hooks/use-navigation-state.ts +10 -7
  51. package/corpus/templates/factory/app/i18n/en-US.ts +3 -0
  52. package/corpus/templates/factory/app/routes/agents.tsx +22 -1
  53. package/corpus/templates/factory/app/routes/factory-settings.tsx +34 -0
  54. package/corpus/templates/factory/app/routes/factory.tsx +33 -323
  55. package/corpus/templates/factory/server/connectors/slack.ts +62 -0
  56. package/corpus/templates/factory/server/db/schema.ts +1 -0
  57. package/corpus/templates/factory/server/lib/slack-feedback-prompt.ts +30 -0
  58. package/corpus/templates/factory/server/plugins/agent-chat.ts +9 -8
  59. package/corpus/templates/factory/server/plugins/factory-migrations.ts +7 -0
  60. package/corpus/templates/factory/server/plugins/factory-scheduler-job.ts +43 -25
  61. package/corpus/templates/factory/server/triage/ai-services-git.ts +2 -0
  62. package/corpus/templates/factory/server/triage/pr-monitor.ts +2 -1
  63. package/corpus/templates/factory/server/triage/pr-policy.ts +197 -19
  64. package/corpus/templates/factory/server/triage/review-skill-alignment.ts +127 -0
  65. package/corpus/templates/factory/server/triage/slack-client.ts +79 -6
  66. package/dist/app-config/auth.d.ts +4 -0
  67. package/dist/app-config/auth.js +7 -0
  68. package/dist/app-config/schema.d.ts +3 -0
  69. package/dist/app-config/schema.js +2 -0
  70. package/dist/cli/code-agent-executor.js +6 -1
  71. package/dist/cli/code-agent-mcp-config.d.ts +2 -2
  72. package/dist/cli/code-agent-mcp-config.js +34 -9
  73. package/dist/client/MultiTabAssistantChat.js +28 -1
  74. package/dist/client/org/OrgSwitcher.js +8 -4
  75. package/dist/client/use-chat-threads.d.ts +1 -0
  76. package/dist/client/use-chat-threads.js +13 -3
  77. package/dist/collab/awareness.d.ts +2 -2
  78. package/dist/collab/routes.d.ts +1 -1
  79. package/dist/mcp-client/config.d.ts +11 -0
  80. package/dist/mcp-client/config.js +33 -26
  81. package/dist/notifications/routes.d.ts +3 -3
  82. package/dist/observability/routes.d.ts +5 -5
  83. package/dist/progress/routes.d.ts +1 -1
  84. package/dist/resources/handlers.d.ts +2 -2
  85. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  86. package/dist/server/auth.js +17 -3
  87. package/dist/server/realtime-token.d.ts +1 -1
  88. package/dist/server/transcribe-voice.d.ts +1 -1
  89. package/dist/vite/client.js +2 -0
  90. package/package.json +1 -1
@@ -0,0 +1,189 @@
1
+ import type { ContentDatabaseItem, DocumentProperty } from "@shared/api";
2
+
3
+ const LOCAL_SOURCE_DIRECTORY_PREFIX = "local-source-directory:";
4
+
5
+ function normalizedSourcePath(path: string | undefined) {
6
+ const normalized = (path ?? "").replace(/\\/g, "/").replace(/^\/+/, "");
7
+ if (
8
+ !normalized ||
9
+ normalized.includes("\0") ||
10
+ normalized.split("/").some((part) => !part || part === "." || part === "..")
11
+ ) {
12
+ return null;
13
+ }
14
+ return normalized;
15
+ }
16
+
17
+ function parentPath(path: string) {
18
+ const separator = path.lastIndexOf("/");
19
+ return separator === -1 ? "" : path.slice(0, separator);
20
+ }
21
+
22
+ function sourceIds(item: ContentDatabaseItem) {
23
+ const value = item.properties.find(
24
+ (property) => property.definition.systemRole === "files_source",
25
+ )?.value;
26
+ return Array.isArray(value)
27
+ ? value.filter(
28
+ (candidate): candidate is string =>
29
+ typeof candidate === "string" && candidate !== "local",
30
+ )
31
+ : [];
32
+ }
33
+
34
+ export function localSourceItemIdentity(item: ContentDatabaseItem) {
35
+ return sourceIds(item)[0] ?? item.document.source?.rootPath ?? null;
36
+ }
37
+
38
+ function directoryId(sourceIdentity: string, path: string) {
39
+ return `${LOCAL_SOURCE_DIRECTORY_PREFIX}${encodeURIComponent(sourceIdentity)}:${encodeURIComponent(path)}`;
40
+ }
41
+
42
+ function systemProperties(
43
+ item: ContentDatabaseItem,
44
+ sourceIdentity: string,
45
+ parentId: string | null,
46
+ ): DocumentProperty[] {
47
+ return item.properties
48
+ .filter((property) => property.definition.systemRole)
49
+ .map((property) => {
50
+ if (property.definition.systemRole === "files_parent") {
51
+ return { ...property, value: parentId, editable: false };
52
+ }
53
+ if (property.definition.systemRole === "files_source") {
54
+ return { ...property, value: [sourceIdentity], editable: false };
55
+ }
56
+ return { ...property, editable: false };
57
+ });
58
+ }
59
+
60
+ function directoryItem(
61
+ template: ContentDatabaseItem,
62
+ sourceIdentity: string,
63
+ path: string,
64
+ position: number,
65
+ ): ContentDatabaseItem {
66
+ const parent = parentPath(path);
67
+ const parentId = parent ? directoryId(sourceIdentity, parent) : null;
68
+ const id = directoryId(sourceIdentity, path);
69
+ return {
70
+ id,
71
+ databaseId: template.databaseId,
72
+ document: {
73
+ ...template.document,
74
+ id,
75
+ parentId,
76
+ title: path.split("/").pop()?.replace(/[-_]+/g, " ") || path,
77
+ content: "",
78
+ description: "",
79
+ icon: null,
80
+ position,
81
+ isFavorite: false,
82
+ hideFromSearch: true,
83
+ accessRole: "viewer",
84
+ canView: true,
85
+ canComment: false,
86
+ canEdit: false,
87
+ canManage: false,
88
+ databaseMembership: undefined,
89
+ source: {
90
+ mode: "local-files",
91
+ kind: "folder",
92
+ path,
93
+ rootPath: template.document.source?.rootPath,
94
+ },
95
+ },
96
+ position,
97
+ properties: systemProperties(template, sourceIdentity, parentId),
98
+ };
99
+ }
100
+
101
+ export interface LocalSourceHierarchySelection {
102
+ sourceId?: string | null;
103
+ rootPath?: string | null;
104
+ }
105
+
106
+ export function firstLocalSourceDocumentId(items: ContentDatabaseItem[]) {
107
+ return items.find(
108
+ (item) =>
109
+ item.document.source?.mode === "local-files" &&
110
+ item.document.source.kind !== "folder",
111
+ )?.document.id;
112
+ }
113
+
114
+ export function projectLocalSourceHierarchy(
115
+ items: ContentDatabaseItem[],
116
+ selection: LocalSourceHierarchySelection = {},
117
+ ) {
118
+ const localFiles = items.filter((item) => {
119
+ if (
120
+ item.document.source?.mode !== "local-files" ||
121
+ item.document.source.kind === "folder"
122
+ ) {
123
+ return false;
124
+ }
125
+ if (selection.sourceId) {
126
+ return sourceIds(item).includes(selection.sourceId);
127
+ }
128
+ if (selection.rootPath) {
129
+ return item.document.source.rootPath === selection.rootPath;
130
+ }
131
+ return true;
132
+ });
133
+ const retained = items.filter(
134
+ (item) => item.document.source?.mode !== "local-files",
135
+ );
136
+ const directories = new Map<
137
+ string,
138
+ { template: ContentDatabaseItem; sourceIdentity: string; path: string }
139
+ >();
140
+ const projectedFiles: ContentDatabaseItem[] = [];
141
+
142
+ for (const item of localFiles) {
143
+ const path = normalizedSourcePath(item.document.source?.path);
144
+ const sourceIdentity =
145
+ selection.sourceId && sourceIds(item).includes(selection.sourceId)
146
+ ? selection.sourceId
147
+ : localSourceItemIdentity(item);
148
+ if (!path || !sourceIdentity) continue;
149
+ const folder = parentPath(path);
150
+ if (folder) {
151
+ const parts = folder.split("/");
152
+ for (let index = 1; index <= parts.length; index += 1) {
153
+ const directoryPath = parts.slice(0, index).join("/");
154
+ directories.set(`${sourceIdentity}\0${directoryPath}`, {
155
+ template: item,
156
+ sourceIdentity,
157
+ path: directoryPath,
158
+ });
159
+ }
160
+ }
161
+ const parentId = folder ? directoryId(sourceIdentity, folder) : null;
162
+ projectedFiles.push({
163
+ ...item,
164
+ document: { ...item.document, parentId },
165
+ properties: item.properties.map((property) =>
166
+ property.definition.systemRole === "files_parent"
167
+ ? { ...property, value: parentId }
168
+ : property,
169
+ ),
170
+ });
171
+ }
172
+
173
+ const projectedDirectories = [...directories.values()]
174
+ .sort((left, right) =>
175
+ left.sourceIdentity === right.sourceIdentity
176
+ ? left.path.localeCompare(right.path)
177
+ : left.sourceIdentity.localeCompare(right.sourceIdentity),
178
+ )
179
+ .map((directory, index) =>
180
+ directoryItem(
181
+ directory.template,
182
+ directory.sourceIdentity,
183
+ directory.path,
184
+ index,
185
+ ),
186
+ );
187
+
188
+ return [...retained, ...projectedDirectories, ...projectedFiles];
189
+ }
@@ -1,7 +1,7 @@
1
1
  import type { Document } from "@shared/api";
2
2
  import { useQueryClient } from "@tanstack/react-query";
3
3
  import { useCallback } from "react";
4
- import { useNavigate } from "react-router";
4
+ import { useLocation, useNavigate } from "react-router";
5
5
  import { toast } from "sonner";
6
6
 
7
7
  import {
@@ -10,7 +10,10 @@ import {
10
10
  SELECTED_CONTENT_SPACE_STORAGE_KEY,
11
11
  } from "@/components/sidebar/select-content-space";
12
12
  import { useContentSpaces } from "@/hooks/use-content-spaces";
13
- import { useCreateDocument } from "@/hooks/use-documents";
13
+ import {
14
+ rollbackOptimisticCreatedDocument,
15
+ useCreateDocument,
16
+ } from "@/hooks/use-documents";
14
17
  import { useLocalStorage } from "@/hooks/use-local-storage";
15
18
  import { documentQueryFilter } from "@/lib/document-query";
16
19
  import { markDocumentCreationPending } from "@/lib/optimistic-document";
@@ -34,6 +37,7 @@ export function useCreatePage(opts?: {
34
37
  awaitPersist?: boolean;
35
38
  }) {
36
39
  const navigate = useNavigate();
40
+ const location = useLocation();
37
41
  const queryClient = useQueryClient();
38
42
  const createDocument = useCreateDocument();
39
43
  const contentSpacesQuery = useContentSpaces();
@@ -78,11 +82,19 @@ export function useCreatePage(opts?: {
78
82
  createdAt: now,
79
83
  updatedAt: now,
80
84
  });
85
+ const previousDocuments = queryClient.getQueryData(
86
+ LIST_DOCUMENTS_QUERY_KEY,
87
+ );
88
+ const previousPath = `${location.pathname}${location.search}${location.hash}`;
81
89
 
82
90
  queryClient.setQueryData(LIST_DOCUMENTS_QUERY_KEY, (old: any) => {
83
91
  const docs: Document[] =
84
92
  old?.documents ?? (Array.isArray(old) ? old : []);
85
- return { documents: [...docs, tempDoc] };
93
+ if (Array.isArray(old)) return [...docs, tempDoc];
94
+ return {
95
+ ...(old && typeof old === "object" ? old : {}),
96
+ documents: [...docs, tempDoc],
97
+ };
86
98
  });
87
99
  queryClient.setQueryData(["action", "get-document", { id }], tempDoc);
88
100
 
@@ -111,11 +123,18 @@ export function useCreatePage(opts?: {
111
123
  };
112
124
 
113
125
  const onPersistError = (err: unknown) => {
126
+ rollbackOptimisticCreatedDocument(
127
+ queryClient,
128
+ id,
129
+ previousDocuments !== undefined,
130
+ );
114
131
  queryClient.invalidateQueries({
115
132
  queryKey: ["action", "list-documents"],
116
133
  });
117
134
  queryClient.removeQueries(documentQueryFilter(id));
118
- if (shouldNavigate) navigate("/");
135
+ if (shouldNavigate) {
136
+ navigate(previousPath, { replace: true, flushSync: true });
137
+ }
119
138
  toast.error("Failed to create page", {
120
139
  description:
121
140
  err instanceof Error ? err.message : "Something went wrong",
@@ -137,6 +156,9 @@ export function useCreatePage(opts?: {
137
156
  },
138
157
  [
139
158
  createDocument,
159
+ location.hash,
160
+ location.pathname,
161
+ location.search,
140
162
  navigate,
141
163
  onAfterNavigate,
142
164
  queryClient,
@@ -66,6 +66,89 @@ export const LIST_DOCUMENTS_QUERY_KEY = [
66
66
  undefined,
67
67
  ] as const;
68
68
 
69
+ export function restoreListDocumentsSnapshot(
70
+ queryClient: Pick<QueryClient, "removeQueries" | "setQueryData">,
71
+ snapshot: unknown,
72
+ ) {
73
+ if (snapshot === undefined) {
74
+ queryClient.removeQueries({ queryKey: LIST_DOCUMENTS_QUERY_KEY });
75
+ return;
76
+ }
77
+ queryClient.setQueryData(LIST_DOCUMENTS_QUERY_KEY, snapshot);
78
+ }
79
+
80
+ export function rollbackOptimisticCreatedDocument(
81
+ queryClient: Pick<
82
+ QueryClient,
83
+ "getQueryData" | "removeQueries" | "setQueryData"
84
+ >,
85
+ documentId: string,
86
+ hadListSnapshot: boolean,
87
+ ) {
88
+ const current = queryClient.getQueryData(LIST_DOCUMENTS_QUERY_KEY);
89
+ const documents: Document[] = Array.isArray(current)
90
+ ? current
91
+ : ((current as DocumentListResponse | undefined)?.documents ?? []);
92
+ const remaining = documents.filter((document) => document.id !== documentId);
93
+
94
+ if (!hadListSnapshot && remaining.length === 0) {
95
+ queryClient.removeQueries({ queryKey: LIST_DOCUMENTS_QUERY_KEY });
96
+ return;
97
+ }
98
+
99
+ if (Array.isArray(current)) {
100
+ queryClient.setQueryData(LIST_DOCUMENTS_QUERY_KEY, remaining);
101
+ return;
102
+ }
103
+
104
+ queryClient.setQueryData(LIST_DOCUMENTS_QUERY_KEY, {
105
+ ...(current && typeof current === "object" ? current : {}),
106
+ documents: remaining,
107
+ });
108
+ }
109
+
110
+ export function restoreDeletedDocumentSnapshots(
111
+ queryClient: Pick<QueryClient, "getQueryData" | "setQueryData">,
112
+ listSnapshot: unknown,
113
+ documentSnapshots: Array<[readonly unknown[], unknown]>,
114
+ deletedDocumentIds: Iterable<string>,
115
+ ) {
116
+ const deletedIds = new Set(deletedDocumentIds);
117
+ const snapshotDocuments: Document[] = Array.isArray(listSnapshot)
118
+ ? listSnapshot
119
+ : ((listSnapshot as DocumentListResponse | undefined)?.documents ?? []);
120
+ const current = queryClient.getQueryData(LIST_DOCUMENTS_QUERY_KEY);
121
+ const currentDocuments: Document[] = Array.isArray(current)
122
+ ? current
123
+ : ((current as DocumentListResponse | undefined)?.documents ?? []);
124
+ const currentDocumentIds = new Set(
125
+ currentDocuments.map((document) => document.id),
126
+ );
127
+ const restoredDocuments = snapshotDocuments.filter(
128
+ (document) =>
129
+ deletedIds.has(document.id) && !currentDocumentIds.has(document.id),
130
+ );
131
+ const documents = [...currentDocuments, ...restoredDocuments];
132
+
133
+ if (Array.isArray(current)) {
134
+ queryClient.setQueryData(LIST_DOCUMENTS_QUERY_KEY, documents);
135
+ } else {
136
+ queryClient.setQueryData(LIST_DOCUMENTS_QUERY_KEY, {
137
+ ...(current && typeof current === "object"
138
+ ? current
139
+ : listSnapshot && typeof listSnapshot === "object"
140
+ ? listSnapshot
141
+ : {}),
142
+ documents,
143
+ });
144
+ }
145
+ for (const [queryKey, data] of documentSnapshots) {
146
+ if (queryClient.getQueryData(queryKey) === undefined) {
147
+ queryClient.setQueryData(queryKey, data);
148
+ }
149
+ }
150
+ }
151
+
69
152
  const DOCUMENT_LIST_PAGE_SIZE = 200;
70
153
 
71
154
  export async function fetchCompleteDocumentList(