@agent-native/core 0.164.12 → 0.164.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/corpus/README.md +1 -1
- package/corpus/templates/content/actions/_database-source-utils.ts +25 -2
- package/corpus/templates/content/actions/_database-utils.ts +2 -0
- package/corpus/templates/content/actions/_local-folder-source.ts +186 -2
- package/corpus/templates/content/actions/connect-local-folder-source.ts +23 -8
- package/corpus/templates/content/actions/disconnect-local-folder-source.ts +3 -3
- package/corpus/templates/content/actions/resolve-local-folder-conflict.ts +2 -2
- package/corpus/templates/content/actions/sync-local-folder-source.ts +130 -12
- package/corpus/templates/content/app/components/LocalFolderLiveSync.tsx +77 -0
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +354 -8
- package/corpus/templates/content/app/components/editor/DocumentToolbar.tsx +23 -1
- package/corpus/templates/content/app/components/editor/VisualEditor.tsx +78 -1
- package/corpus/templates/content/app/components/editor/database/sidebar.tsx +36 -0
- package/corpus/templates/content/app/components/editor/local-source-write-state.ts +52 -0
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +244 -6
- package/corpus/templates/content/app/components/sidebar/local-source-hierarchy.ts +189 -0
- package/corpus/templates/content/app/hooks/use-create-page.ts +26 -4
- package/corpus/templates/content/app/hooks/use-documents.ts +83 -0
- package/corpus/templates/content/app/i18n-data.ts +79 -0
- package/corpus/templates/content/app/lib/desktop-content-files.ts +57 -1
- package/corpus/templates/content/app/lib/local-content-source-files.ts +210 -34
- package/corpus/templates/content/app/lib/local-folder-live-sync.ts +191 -0
- package/corpus/templates/content/app/root.tsx +2 -1
- package/corpus/templates/content/app/routes/_app.local-files.tsx +103 -38
- package/corpus/templates/content/docs/product/capabilities/content.source.file-folder.md +2 -2
- package/corpus/templates/content/docs/product/features/content.feature.bring-your-local-work.md +3 -3
- package/corpus/templates/content/docs/product/roadmap.md +2 -2
- package/corpus/templates/content/docs/solutions/2026-08-18-content-navigation-coherence-shape.md +482 -0
- package/corpus/templates/content/shared/api.ts +25 -0
- package/dist/app-config/auth.d.ts +4 -0
- package/dist/app-config/auth.js +7 -0
- package/dist/app-config/schema.d.ts +3 -0
- package/dist/app-config/schema.js +2 -0
- package/dist/cli/code-agent-executor.js +6 -1
- package/dist/cli/code-agent-mcp-config.d.ts +2 -2
- package/dist/cli/code-agent-mcp-config.js +34 -9
- package/dist/client/org/OrgSwitcher.js +8 -4
- package/dist/collab/routes.d.ts +1 -1
- package/dist/mcp-client/config.d.ts +11 -0
- package/dist/mcp-client/config.js +33 -26
- package/dist/resources/handlers.d.ts +2 -2
- package/dist/server/auth.js +15 -1
- package/package.json +1 -1
|
@@ -45,6 +45,12 @@ import {
|
|
|
45
45
|
syncLocalControlResources,
|
|
46
46
|
type LocalControlResourceFiles,
|
|
47
47
|
} from "@/lib/local-control-resources";
|
|
48
|
+
import {
|
|
49
|
+
connectTemporaryLocalFolder,
|
|
50
|
+
rememberLiveLocalFolderSource,
|
|
51
|
+
requestLiveLocalFolderActivation,
|
|
52
|
+
syncLiveLocalFolder,
|
|
53
|
+
} from "@/lib/local-folder-live-sync";
|
|
48
54
|
import {
|
|
49
55
|
hasInterruptedNativeFolderPickerAttempt,
|
|
50
56
|
runNativeFolderPickerWithCrashSentinel,
|
|
@@ -149,18 +155,6 @@ interface ManifestFolderSyncResult extends ImportContentSourceResult {
|
|
|
149
155
|
requestedDocumentId: string | null;
|
|
150
156
|
}
|
|
151
157
|
|
|
152
|
-
interface RegisterLocalComponentWorkspaceResult {
|
|
153
|
-
ok: true;
|
|
154
|
-
workspace: {
|
|
155
|
-
id: string;
|
|
156
|
-
workspacePath: string;
|
|
157
|
-
componentPaths: string[];
|
|
158
|
-
};
|
|
159
|
-
componentDirs: string[];
|
|
160
|
-
componentCount: number;
|
|
161
|
-
reloadRequired: boolean;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
158
|
interface RemoveLocalFileSourceResult {
|
|
165
159
|
success: boolean;
|
|
166
160
|
deleted: number;
|
|
@@ -269,7 +263,7 @@ function desktopDirectoryFromFolder(
|
|
|
269
263
|
folder: DesktopContentFilesFolder,
|
|
270
264
|
directories: SelectedDirectory[] = [],
|
|
271
265
|
): SelectedDirectory {
|
|
272
|
-
const id = folder.id ?? opaqueDesktopDirectoryId(folder.
|
|
266
|
+
const id = folder.id ?? opaqueDesktopDirectoryId(folder.name);
|
|
273
267
|
return {
|
|
274
268
|
id,
|
|
275
269
|
kind: "desktop",
|
|
@@ -755,6 +749,7 @@ export default function LocalFilesRoute() {
|
|
|
755
749
|
const targetDatabaseId = searchParams.get("databaseId") || undefined;
|
|
756
750
|
const manifestConnectionId = searchParams.get("connectionId") || undefined;
|
|
757
751
|
const manifestFile = searchParams.get("file") || undefined;
|
|
752
|
+
const workingCopyId = searchParams.get("workingCopyId") || undefined;
|
|
758
753
|
const workspacePropertyValues = (
|
|
759
754
|
location.state as {
|
|
760
755
|
workspacePropertyValues?: Record<string, unknown>;
|
|
@@ -767,6 +762,7 @@ export default function LocalFilesRoute() {
|
|
|
767
762
|
const [restoringDirectory, setRestoringDirectory] = useState(false);
|
|
768
763
|
const supported = useMemo(supportsLocalFolderSync, []);
|
|
769
764
|
const manifestBootstrapRef = useRef<string | null>(null);
|
|
765
|
+
const workingCopyBootstrapRef = useRef<string | null>(null);
|
|
770
766
|
const documentSourceDirectories = useMemo(
|
|
771
767
|
() =>
|
|
772
768
|
directories.length === 0
|
|
@@ -850,6 +846,59 @@ export default function LocalFilesRoute() {
|
|
|
850
846
|
targetSpaceId,
|
|
851
847
|
]);
|
|
852
848
|
|
|
849
|
+
useEffect(() => {
|
|
850
|
+
if (!workingCopyId) return;
|
|
851
|
+
if (workingCopyBootstrapRef.current === workingCopyId) return;
|
|
852
|
+
workingCopyBootstrapRef.current = workingCopyId;
|
|
853
|
+
const desktopFiles = getDesktopContentFiles();
|
|
854
|
+
if (!desktopFiles) return;
|
|
855
|
+
setBusy("choose");
|
|
856
|
+
void desktopFiles
|
|
857
|
+
.getFolder({ folderId: workingCopyId })
|
|
858
|
+
.then(async (folderResult) => {
|
|
859
|
+
if (!folderResult.ok || folderResult.folder.kind !== "temporary") {
|
|
860
|
+
throw new Error(
|
|
861
|
+
folderResult.ok
|
|
862
|
+
? t("localFiles.chooseAnotherFolder")
|
|
863
|
+
: folderResult.error,
|
|
864
|
+
);
|
|
865
|
+
}
|
|
866
|
+
const sourceId = await connectTemporaryLocalFolder(folderResult.folder);
|
|
867
|
+
if (!sourceId) throw new Error(t("localFiles.chooseAnotherFolder"));
|
|
868
|
+
const outcome = await syncLiveLocalFolder(workingCopyId);
|
|
869
|
+
if (!outcome.synced) {
|
|
870
|
+
throw new Error(t("localFiles.chooseAnotherFolder"));
|
|
871
|
+
}
|
|
872
|
+
const result = outcome.result as ImportContentSourceResult;
|
|
873
|
+
const firstDocument = [
|
|
874
|
+
...result.created,
|
|
875
|
+
...result.updated,
|
|
876
|
+
...result.unchanged,
|
|
877
|
+
][0];
|
|
878
|
+
if (!firstDocument)
|
|
879
|
+
throw new Error(t("localFiles.chooseAnotherFolder"));
|
|
880
|
+
requestLiveLocalFolderActivation(workingCopyId);
|
|
881
|
+
await Promise.all([
|
|
882
|
+
queryClient.invalidateQueries({
|
|
883
|
+
queryKey: ["action", "list-documents"],
|
|
884
|
+
}),
|
|
885
|
+
queryClient.invalidateQueries({
|
|
886
|
+
queryKey: ["action", "get-content-database"],
|
|
887
|
+
}),
|
|
888
|
+
]);
|
|
889
|
+
navigate(`/page/${firstDocument.id}`, { replace: true });
|
|
890
|
+
})
|
|
891
|
+
.catch((err) => {
|
|
892
|
+
workingCopyBootstrapRef.current = null;
|
|
893
|
+
setStatus({
|
|
894
|
+
kind: "error",
|
|
895
|
+
title: t("localFiles.pullFailed"),
|
|
896
|
+
detail: err instanceof Error ? err.message : t("localFiles.tryAgain"),
|
|
897
|
+
});
|
|
898
|
+
})
|
|
899
|
+
.finally(() => setBusy(null));
|
|
900
|
+
}, [navigate, queryClient, t, workingCopyId]);
|
|
901
|
+
|
|
853
902
|
useEffect(() => {
|
|
854
903
|
if (!supported) return;
|
|
855
904
|
let cancelled = false;
|
|
@@ -937,6 +986,24 @@ export default function LocalFilesRoute() {
|
|
|
937
986
|
createSourceBackedSpace: !targetSpaceId && !targetDatabaseId,
|
|
938
987
|
propertyValues: workspacePropertyValues,
|
|
939
988
|
truthPolicy: "source_primary",
|
|
989
|
+
connectionMetadata:
|
|
990
|
+
directory.kind === "desktop"
|
|
991
|
+
? {
|
|
992
|
+
liveBridgeEnabled: true,
|
|
993
|
+
repository: directory.folder.repository
|
|
994
|
+
? { localId: directory.folder.repository.localId }
|
|
995
|
+
: undefined,
|
|
996
|
+
workingCopy: {
|
|
997
|
+
id: directory.id,
|
|
998
|
+
repositoryId: directory.folder.repository?.localId,
|
|
999
|
+
kind: directory.folder.kind ?? "persistent",
|
|
1000
|
+
name: directory.folder.name,
|
|
1001
|
+
branch: directory.folder.repository?.branch,
|
|
1002
|
+
commit: directory.folder.repository?.commit,
|
|
1003
|
+
deviceId: "agent-native-desktop",
|
|
1004
|
+
},
|
|
1005
|
+
}
|
|
1006
|
+
: undefined,
|
|
940
1007
|
dryRun,
|
|
941
1008
|
} as never,
|
|
942
1009
|
);
|
|
@@ -1012,6 +1079,16 @@ export default function LocalFilesRoute() {
|
|
|
1012
1079
|
if (!connection.sourceId) {
|
|
1013
1080
|
throw new Error("The local folder connection was not created");
|
|
1014
1081
|
}
|
|
1082
|
+
if (!dryRun && refreshedDirectory.kind === "desktop") {
|
|
1083
|
+
await rememberLiveLocalFolderSource(
|
|
1084
|
+
refreshedDirectory.folder,
|
|
1085
|
+
connection.sourceId,
|
|
1086
|
+
connection.filesDatabaseId,
|
|
1087
|
+
);
|
|
1088
|
+
await getDesktopContentFiles()?.subscribeChanges?.({
|
|
1089
|
+
folderId: refreshedDirectory.id,
|
|
1090
|
+
});
|
|
1091
|
+
}
|
|
1015
1092
|
const result = await callAction<ImportContentSourceResult>(
|
|
1016
1093
|
"sync-local-folder-source" as never,
|
|
1017
1094
|
{
|
|
@@ -1038,29 +1115,6 @@ export default function LocalFilesRoute() {
|
|
|
1038
1115
|
) {
|
|
1039
1116
|
for (const directory of selectedDirectories) {
|
|
1040
1117
|
if (directory.kind === "desktop") {
|
|
1041
|
-
const workspacePath = directory.folder.path;
|
|
1042
|
-
if (!workspacePath) continue;
|
|
1043
|
-
try {
|
|
1044
|
-
const result =
|
|
1045
|
-
await callAction<RegisterLocalComponentWorkspaceResult>(
|
|
1046
|
-
"register-local-component-workspace" as never,
|
|
1047
|
-
{ workspacePath } as never,
|
|
1048
|
-
);
|
|
1049
|
-
if (result.componentCount > 0 && showToast) {
|
|
1050
|
-
toast.success(t("localFiles.localComponentsConnected"), {
|
|
1051
|
-
description: t("localFiles.localComponentsConnectedDescription"),
|
|
1052
|
-
});
|
|
1053
|
-
}
|
|
1054
|
-
} catch (error) {
|
|
1055
|
-
if (showToast) {
|
|
1056
|
-
toast.info(t("localFiles.localFilesLinked"), {
|
|
1057
|
-
description:
|
|
1058
|
-
error instanceof Error
|
|
1059
|
-
? error.message
|
|
1060
|
-
: t("localFiles.componentPreviewsNeedBridge"),
|
|
1061
|
-
});
|
|
1062
|
-
}
|
|
1063
|
-
}
|
|
1064
1118
|
continue;
|
|
1065
1119
|
}
|
|
1066
1120
|
|
|
@@ -1139,9 +1193,21 @@ export default function LocalFilesRoute() {
|
|
|
1139
1193
|
if (!desktopFiles) {
|
|
1140
1194
|
throw new Error(t("localFiles.desktopFolderUnavailable"));
|
|
1141
1195
|
}
|
|
1196
|
+
const observed = await desktopFiles.readFiles({
|
|
1197
|
+
folderId: directory.id,
|
|
1198
|
+
});
|
|
1199
|
+
if (!observed.ok) throw new Error(observed.error);
|
|
1200
|
+
const expectedRevisions: Record<string, string | null> = {
|
|
1201
|
+
...(observed.revisions ?? {}),
|
|
1202
|
+
};
|
|
1203
|
+
for (const filePath of Object.keys(files)) {
|
|
1204
|
+
if (!(filePath in expectedRevisions))
|
|
1205
|
+
expectedRevisions[filePath] = null;
|
|
1206
|
+
}
|
|
1142
1207
|
const result = await desktopFiles.writeFiles({
|
|
1143
1208
|
folderId: directory.id,
|
|
1144
1209
|
files,
|
|
1210
|
+
expectedRevisions,
|
|
1145
1211
|
});
|
|
1146
1212
|
if (!result.ok) throw new Error(result.error);
|
|
1147
1213
|
setDirectories((current) =>
|
|
@@ -1478,8 +1544,7 @@ export default function LocalFilesRoute() {
|
|
|
1478
1544
|
? (directory.sourceRootPath ??
|
|
1479
1545
|
t("localFiles.importedSource"))
|
|
1480
1546
|
: directory.kind === "desktop"
|
|
1481
|
-
? (
|
|
1482
|
-
t("localFiles.desktopFolder"))
|
|
1547
|
+
? t("localFiles.desktopFolder")
|
|
1483
1548
|
: t("localFiles.browserFolder")}
|
|
1484
1549
|
</div>
|
|
1485
1550
|
</div>
|
|
@@ -22,7 +22,7 @@ proof_requirements:
|
|
|
22
22
|
]
|
|
23
23
|
evidence: []
|
|
24
24
|
superseded_by: null
|
|
25
|
-
last_reviewed: "2026-
|
|
25
|
+
last_reviewed: "2026-08-17"
|
|
26
26
|
---
|
|
27
27
|
|
|
28
28
|
# Files and folders as Sources
|
|
@@ -58,7 +58,7 @@ Given a browser without bridge authority, when it opens a materialized folder it
|
|
|
58
58
|
|
|
59
59
|
## Current evidence
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
Current implementation adds opaque Desktop grants, recursive Markdown materialization, watched reconciliation with a bounded fallback, atomic revision-guarded writes, and local working-copy identity. Focused tests cover the substrate, but packaged real-interface authority, recovery, redaction, and cleanup proof is still pending, and heterogeneous file families remain exploratory. This Capability therefore remains `exploring`.
|
|
62
62
|
|
|
63
63
|
## Proof plan
|
|
64
64
|
|
package/corpus/templates/content/docs/product/features/content.feature.bring-your-local-work.md
CHANGED
|
@@ -8,8 +8,8 @@ order: 12
|
|
|
8
8
|
roadmap_status: "partially_implemented"
|
|
9
9
|
summary: "Open a folder or repository through the same Source model while keeping device authority and browser limitations honest."
|
|
10
10
|
example_workflow: "A developer opens a local documentation folder in Content Desktop, edits the files through Content, sees external file changes synchronize back, and later reads the last synchronized representation from Safari without exposing the folder path."
|
|
11
|
-
works_today: "Local File Mode,
|
|
12
|
-
remains: "
|
|
11
|
+
works_today: "Local File Mode, connected-folder Sources, source-backed Pages, Desktop folder grants, watched reconciliation, revision-guarded writes, and named local working-copy identity establish the first end-to-end local Markdown path behind its release flag."
|
|
12
|
+
remains: "The packaged Desktop story still needs independent H1-H10 acceptance before release; broader file families, queued remote writes, and provider-bound GitHub review remain later increments."
|
|
13
13
|
required_capabilities:
|
|
14
14
|
[
|
|
15
15
|
"content.source.local-bridge",
|
|
@@ -22,7 +22,7 @@ enhancing_capabilities:
|
|
|
22
22
|
increments: []
|
|
23
23
|
feature_proof: null
|
|
24
24
|
publicness: "public"
|
|
25
|
-
last_reviewed: "2026-
|
|
25
|
+
last_reviewed: "2026-08-17"
|
|
26
26
|
---
|
|
27
27
|
|
|
28
28
|
# Feature 12: Bring your local work
|
|
@@ -336,9 +336,9 @@ Open a folder or repository through the same Source model while keeping device a
|
|
|
336
336
|
|
|
337
337
|
**Example workflow:** A developer opens a local documentation folder in Content Desktop, edits the files through Content, sees external file changes synchronize back, and later reads the last synchronized representation from Safari without exposing the folder path.
|
|
338
338
|
|
|
339
|
-
**What works today:** Local File Mode,
|
|
339
|
+
**What works today:** Local File Mode, connected-folder Sources, source-backed Pages, Desktop folder grants, watched reconciliation, revision-guarded writes, and named local working-copy identity establish the first end-to-end local Markdown path behind its release flag.
|
|
340
340
|
|
|
341
|
-
**What remains:**
|
|
341
|
+
**What remains:** The packaged Desktop story still needs independent H1-H10 acceptance before release; broader file families, queued remote writes, and provider-bound GitHub review remain later increments.
|
|
342
342
|
|
|
343
343
|
**What this Feature includes:**
|
|
344
344
|
|