fix(workspace): pre-create watched roots so late-appearing dirs emit events

chokidar v4 silently skips a watched directory that doesn't exist at
watch time, so e.g. calendar_sync/ created by the first sync after app
start would never emit events until restart. Create the dir roots up
front; for the file root only its parent dir is needed.
This commit is contained in:
Gagan 2026-07-13 17:45:51 +05:30
parent 4db72e1fe4
commit 5bf743f93d

View file

@ -16,20 +16,20 @@ export type WorkspaceChangeCallback = (event: z.infer<typeof WorkspaceChangeEven
// unboundedly with usage; chokidar v4 holds one OS watch handle per file, so
// watching all of WorkDir exhausts the process fd limit (EMFILE crash) once
// the workdir gets big enough.
const WATCHED_ROOTS = [
const WATCHED_DIR_ROOTS = [
'knowledge',
'bases',
'inbox_lists',
'gmail_sync',
'calendar_sync',
'bg-tasks',
'config/agent-schedule.json',
];
const WATCHED_FILE_ROOTS = ['config/agent-schedule.json'];
/**
* Create a workspace watcher
* Watches the user-facing workspace roots (WATCHED_ROOTS) recursively and
* emits WorkDir-relative change events via callback.
* Watches the user-facing workspace roots (WATCHED_DIR_ROOTS / WATCHED_FILE_ROOTS)
* recursively and emits WorkDir-relative change events via callback.
*
* Returns a watcher instance that can be closed.
* The watcher emits events immediately without debouncing.
@ -40,7 +40,18 @@ export async function createWorkspaceWatcher(
): Promise<FSWatcher> {
await ensureWorkspaceRoot();
const roots = WATCHED_ROOTS.map((rel) => path.join(WorkDir, rel));
// chokidar v4 never picks up a watched directory that doesn't exist yet
// (a missing file is fine as long as its parent dir exists), so create the
// roots up front — otherwise e.g. calendar_sync/ appearing after app start
// would stay invisible until restart.
for (const rel of WATCHED_DIR_ROOTS) {
await fs.mkdir(path.join(WorkDir, rel), { recursive: true });
}
for (const rel of WATCHED_FILE_ROOTS) {
await fs.mkdir(path.dirname(path.join(WorkDir, rel)), { recursive: true });
}
const roots = [...WATCHED_DIR_ROOTS, ...WATCHED_FILE_ROOTS].map((rel) => path.join(WorkDir, rel));
const watcher = chokidar.watch(roots, {
ignoreInitial: true,
// knowledge/ is a git repo (version history) — its .git object store is