@personaai/react 0.15.0 → 0.17.0

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/dist/index.d.cts CHANGED
@@ -146,12 +146,13 @@ interface PersonaProviderProps {
146
146
  }
147
147
  interface PersonaThread {
148
148
  _id: string;
149
- /** A bare id string on create()/get(); populated as an object on list(). */
149
+ /** A bare id string on create(); populated (incl. sandboxEnabled) on get()/list(). */
150
150
  agentId: string | {
151
151
  _id: string;
152
152
  name: string;
153
153
  avatar?: string;
154
154
  slug: string;
155
+ sandboxEnabled: boolean;
155
156
  };
156
157
  title?: string;
157
158
  isArchived?: boolean;
@@ -170,7 +171,8 @@ interface PersonaFileItem {
170
171
  createdAt: string;
171
172
  }
172
173
  interface PersonaMemoryFile {
173
- scope?: "user" | "agent";
174
+ scope?: "user" | "agent" | "workspace";
175
+ /** Set when `scope` is `"agent"` or `"workspace"`. */
174
176
  agentId?: string;
175
177
  path: string;
176
178
  content: string;
@@ -178,7 +180,11 @@ interface PersonaMemoryFile {
178
180
  createdAt?: string;
179
181
  updatedAt?: string;
180
182
  }
181
- /** One agent's group of agent-scoped memory files, as returned by `GET /memory`. */
183
+ /**
184
+ * One agent's group of scoped memory files, as returned by `GET /memory` —
185
+ * used for both `agentMemories` (`scope: "agent"`) and `agentWorkspaces`
186
+ * (`scope: "workspace"`).
187
+ */
182
188
  interface PersonaMemoryAgentGroup {
183
189
  agentId: string;
184
190
  /** `null` if the agent no longer exists. */
@@ -188,6 +194,14 @@ interface PersonaMemoryAgentGroup {
188
194
  interface PersonaMemoryList {
189
195
  userFiles: PersonaMemoryFile[];
190
196
  agentMemories: PersonaMemoryAgentGroup[];
197
+ /**
198
+ * Files under an Agent's own `/workspace/` filesystem route — the SAME
199
+ * persistent store `write_file`/`read_file` tool calls under
200
+ * `/workspace/...` actually read and write. Scoped by Agent + Subject,
201
+ * shared across every Thread that Subject has with that Agent (not
202
+ * private to one conversation).
203
+ */
204
+ agentWorkspaces: PersonaMemoryAgentGroup[];
191
205
  }
192
206
  /**
193
207
  * AG-UI protocol events emitted during streaming — mirrors exactly what
@@ -1296,18 +1310,18 @@ declare function useMemory(autoFetch?: boolean): {
1296
1310
  refetch: () => Promise<PersonaMemoryList>;
1297
1311
  getFile: (params: {
1298
1312
  path: string;
1299
- scope?: "user" | "agent";
1313
+ scope?: "user" | "agent" | "workspace";
1300
1314
  agentId?: string;
1301
1315
  }) => Promise<PersonaMemoryFile>;
1302
1316
  writeFile: (params: {
1303
1317
  path: string;
1304
1318
  content: string;
1305
- scope?: "user" | "agent";
1319
+ scope?: "user" | "agent" | "workspace";
1306
1320
  agentId?: string;
1307
1321
  }) => Promise<PersonaMemoryFile>;
1308
1322
  deleteFile: (params: {
1309
1323
  path: string;
1310
- scope?: "user" | "agent";
1324
+ scope?: "user" | "agent" | "workspace";
1311
1325
  agentId?: string;
1312
1326
  }) => Promise<void>;
1313
1327
  };
@@ -1337,17 +1351,22 @@ declare function useThreads(autoFetch?: boolean): {
1337
1351
  };
1338
1352
 
1339
1353
  /**
1340
- * CRUD over one Thread's workspace files (the agent's own virtual
1341
- * filesystem) — `useChat()` already exposes a read-only `files` snapshot
1342
- * tied to that thread's live/loaded messages; this hook is for a
1343
- * standalone file-explorer UI that needs to list, read, write, and delete
1344
- * files independently of an active chat session.
1354
+ * CRUD over one Agent's `/workspace/` files — the SAME persistent store a
1355
+ * `write_file`/`read_file` tool call under `/workspace/...` actually reads
1356
+ * and writes (deepagents routes that path prefix to a Mongo-backed memory
1357
+ * store, NOT the LangGraph checkpoint's `files` state channel — a Thread's
1358
+ * live/checkpointed state is a different, much narrower thing that rarely
1359
+ * has anything in it, since agents are instructed to write all real output
1360
+ * under `/workspace/outputs/`). Scoped by Agent + Subject: shared across
1361
+ * every Thread that Subject has with that Agent, not private to one
1362
+ * conversation — the same model the platform's own Files panel uses.
1345
1363
  *
1346
- * KNOWN LIMITATION (inherited from the backend): no lock exists against a
1347
- * concurrent live run on the same Thread — writing here while the agent is
1348
- * actively mid-run could race with its own file writes.
1364
+ * Thin wrapper over `client.memory` (`scope: "workspace"`) — no parallel
1365
+ * type vocabulary, `PersonaWorkspaceFile` here is just `PersonaMemoryFile`
1366
+ * reshaped to the `{content, size, createdAt, modifiedAt}` display shape
1367
+ * `useChat()`'s own (read-only, live-run) `files` snapshot already uses.
1349
1368
  */
1350
- declare function useWorkspaceFiles(threadId: string | undefined, autoFetch?: boolean): {
1369
+ declare function useWorkspaceFiles(agentId: string | undefined, autoFetch?: boolean): {
1351
1370
  files: Record<string, PersonaWorkspaceFile>;
1352
1371
  isLoading: boolean;
1353
1372
  isSaving: boolean;
package/dist/index.d.ts CHANGED
@@ -146,12 +146,13 @@ interface PersonaProviderProps {
146
146
  }
147
147
  interface PersonaThread {
148
148
  _id: string;
149
- /** A bare id string on create()/get(); populated as an object on list(). */
149
+ /** A bare id string on create(); populated (incl. sandboxEnabled) on get()/list(). */
150
150
  agentId: string | {
151
151
  _id: string;
152
152
  name: string;
153
153
  avatar?: string;
154
154
  slug: string;
155
+ sandboxEnabled: boolean;
155
156
  };
156
157
  title?: string;
157
158
  isArchived?: boolean;
@@ -170,7 +171,8 @@ interface PersonaFileItem {
170
171
  createdAt: string;
171
172
  }
172
173
  interface PersonaMemoryFile {
173
- scope?: "user" | "agent";
174
+ scope?: "user" | "agent" | "workspace";
175
+ /** Set when `scope` is `"agent"` or `"workspace"`. */
174
176
  agentId?: string;
175
177
  path: string;
176
178
  content: string;
@@ -178,7 +180,11 @@ interface PersonaMemoryFile {
178
180
  createdAt?: string;
179
181
  updatedAt?: string;
180
182
  }
181
- /** One agent's group of agent-scoped memory files, as returned by `GET /memory`. */
183
+ /**
184
+ * One agent's group of scoped memory files, as returned by `GET /memory` —
185
+ * used for both `agentMemories` (`scope: "agent"`) and `agentWorkspaces`
186
+ * (`scope: "workspace"`).
187
+ */
182
188
  interface PersonaMemoryAgentGroup {
183
189
  agentId: string;
184
190
  /** `null` if the agent no longer exists. */
@@ -188,6 +194,14 @@ interface PersonaMemoryAgentGroup {
188
194
  interface PersonaMemoryList {
189
195
  userFiles: PersonaMemoryFile[];
190
196
  agentMemories: PersonaMemoryAgentGroup[];
197
+ /**
198
+ * Files under an Agent's own `/workspace/` filesystem route — the SAME
199
+ * persistent store `write_file`/`read_file` tool calls under
200
+ * `/workspace/...` actually read and write. Scoped by Agent + Subject,
201
+ * shared across every Thread that Subject has with that Agent (not
202
+ * private to one conversation).
203
+ */
204
+ agentWorkspaces: PersonaMemoryAgentGroup[];
191
205
  }
192
206
  /**
193
207
  * AG-UI protocol events emitted during streaming — mirrors exactly what
@@ -1296,18 +1310,18 @@ declare function useMemory(autoFetch?: boolean): {
1296
1310
  refetch: () => Promise<PersonaMemoryList>;
1297
1311
  getFile: (params: {
1298
1312
  path: string;
1299
- scope?: "user" | "agent";
1313
+ scope?: "user" | "agent" | "workspace";
1300
1314
  agentId?: string;
1301
1315
  }) => Promise<PersonaMemoryFile>;
1302
1316
  writeFile: (params: {
1303
1317
  path: string;
1304
1318
  content: string;
1305
- scope?: "user" | "agent";
1319
+ scope?: "user" | "agent" | "workspace";
1306
1320
  agentId?: string;
1307
1321
  }) => Promise<PersonaMemoryFile>;
1308
1322
  deleteFile: (params: {
1309
1323
  path: string;
1310
- scope?: "user" | "agent";
1324
+ scope?: "user" | "agent" | "workspace";
1311
1325
  agentId?: string;
1312
1326
  }) => Promise<void>;
1313
1327
  };
@@ -1337,17 +1351,22 @@ declare function useThreads(autoFetch?: boolean): {
1337
1351
  };
1338
1352
 
1339
1353
  /**
1340
- * CRUD over one Thread's workspace files (the agent's own virtual
1341
- * filesystem) — `useChat()` already exposes a read-only `files` snapshot
1342
- * tied to that thread's live/loaded messages; this hook is for a
1343
- * standalone file-explorer UI that needs to list, read, write, and delete
1344
- * files independently of an active chat session.
1354
+ * CRUD over one Agent's `/workspace/` files — the SAME persistent store a
1355
+ * `write_file`/`read_file` tool call under `/workspace/...` actually reads
1356
+ * and writes (deepagents routes that path prefix to a Mongo-backed memory
1357
+ * store, NOT the LangGraph checkpoint's `files` state channel — a Thread's
1358
+ * live/checkpointed state is a different, much narrower thing that rarely
1359
+ * has anything in it, since agents are instructed to write all real output
1360
+ * under `/workspace/outputs/`). Scoped by Agent + Subject: shared across
1361
+ * every Thread that Subject has with that Agent, not private to one
1362
+ * conversation — the same model the platform's own Files panel uses.
1345
1363
  *
1346
- * KNOWN LIMITATION (inherited from the backend): no lock exists against a
1347
- * concurrent live run on the same Thread — writing here while the agent is
1348
- * actively mid-run could race with its own file writes.
1364
+ * Thin wrapper over `client.memory` (`scope: "workspace"`) — no parallel
1365
+ * type vocabulary, `PersonaWorkspaceFile` here is just `PersonaMemoryFile`
1366
+ * reshaped to the `{content, size, createdAt, modifiedAt}` display shape
1367
+ * `useChat()`'s own (read-only, live-run) `files` snapshot already uses.
1349
1368
  */
1350
- declare function useWorkspaceFiles(threadId: string | undefined, autoFetch?: boolean): {
1369
+ declare function useWorkspaceFiles(agentId: string | undefined, autoFetch?: boolean): {
1351
1370
  files: Record<string, PersonaWorkspaceFile>;
1352
1371
  isLoading: boolean;
1353
1372
  isSaving: boolean;
package/dist/index.js CHANGED
@@ -1666,7 +1666,8 @@ function useMemory(autoFetch = true) {
1666
1666
  const { fetchWithAuth } = usePersonaContext();
1667
1667
  const [memory, setMemory] = useState3({
1668
1668
  userFiles: [],
1669
- agentMemories: []
1669
+ agentMemories: [],
1670
+ agentWorkspaces: []
1670
1671
  });
1671
1672
  const [isLoading, setIsLoading] = useState3(false);
1672
1673
  const [error, setError] = useState3(null);
@@ -1684,7 +1685,7 @@ function useMemory(autoFetch = true) {
1684
1685
  } catch (err) {
1685
1686
  const errorObj = err instanceof Error ? err : new Error(String(err));
1686
1687
  setError(errorObj);
1687
- return { userFiles: [], agentMemories: [] };
1688
+ return { userFiles: [], agentMemories: [], agentWorkspaces: [] };
1688
1689
  } finally {
1689
1690
  setIsLoading(false);
1690
1691
  }
@@ -1891,21 +1892,36 @@ function useThreads(autoFetch = true) {
1891
1892
 
1892
1893
  // src/hooks/useWorkspaceFiles.ts
1893
1894
  import { useCallback as useCallback5, useEffect as useEffect5, useState as useState5 } from "react";
1894
- function useWorkspaceFiles(threadId, autoFetch = true) {
1895
+ function toWorkspaceFile(file) {
1896
+ return {
1897
+ content: file.content,
1898
+ size: file.content.length,
1899
+ createdAt: file.createdAt ?? null,
1900
+ modifiedAt: file.updatedAt ?? null
1901
+ };
1902
+ }
1903
+ function useWorkspaceFiles(agentId, autoFetch = true) {
1895
1904
  const { fetchWithAuth } = usePersonaContext();
1896
1905
  const [files, setFiles] = useState5({});
1897
1906
  const [isLoading, setIsLoading] = useState5(false);
1898
1907
  const [isSaving, setIsSaving] = useState5(false);
1899
1908
  const [error, setError] = useState5(null);
1900
1909
  const fetchFiles = useCallback5(async () => {
1901
- if (!threadId) return {};
1910
+ if (!agentId) return {};
1902
1911
  setIsLoading(true);
1903
1912
  setError(null);
1904
1913
  try {
1905
- const res = await fetchWithAuth(`/threads/${threadId}/files`);
1914
+ const res = await fetchWithAuth("/memory");
1906
1915
  if (!res.ok) throw new Error(`Failed to list workspace files: ${res.statusText}`);
1907
1916
  const data = await res.json();
1908
- const normalized = normalizeWorkspaceFiles(data?.data ?? data ?? {});
1917
+ const body = data?.data ?? data;
1918
+ const group = (body?.agentWorkspaces ?? []).find(
1919
+ (g) => g.agentId === agentId
1920
+ );
1921
+ const normalized = {};
1922
+ for (const file of group?.files ?? []) {
1923
+ normalized[file.path] = toWorkspaceFile(file);
1924
+ }
1909
1925
  setFiles(normalized);
1910
1926
  return normalized;
1911
1927
  } catch (err) {
@@ -1915,35 +1931,34 @@ function useWorkspaceFiles(threadId, autoFetch = true) {
1915
1931
  } finally {
1916
1932
  setIsLoading(false);
1917
1933
  }
1918
- }, [fetchWithAuth, threadId]);
1934
+ }, [fetchWithAuth, agentId]);
1919
1935
  const getFile = useCallback5(
1920
1936
  async (path) => {
1921
- if (!threadId) throw new Error("useWorkspaceFiles: no threadId set");
1922
- const res = await fetchWithAuth(
1923
- `/threads/${threadId}/file?path=${encodeURIComponent(path)}`
1924
- );
1937
+ if (!agentId) throw new Error("useWorkspaceFiles: no agentId set");
1938
+ const query = new URLSearchParams({ path, scope: "workspace", agentId });
1939
+ const res = await fetchWithAuth(`/memory/file?${query.toString()}`);
1925
1940
  if (!res.ok) throw new Error(`Failed to read workspace file: ${res.statusText}`);
1926
1941
  const data = await res.json();
1927
1942
  const raw = data?.data ?? data;
1928
- return normalizeWorkspaceFiles({ [path]: raw })[path];
1943
+ return toWorkspaceFile(raw);
1929
1944
  },
1930
- [fetchWithAuth, threadId]
1945
+ [fetchWithAuth, agentId]
1931
1946
  );
1932
1947
  const writeFile = useCallback5(
1933
1948
  async (path, content) => {
1934
- if (!threadId) throw new Error("useWorkspaceFiles: no threadId set");
1949
+ if (!agentId) throw new Error("useWorkspaceFiles: no agentId set");
1935
1950
  setIsSaving(true);
1936
1951
  setError(null);
1937
1952
  try {
1938
- const res = await fetchWithAuth(`/threads/${threadId}/file`, {
1953
+ const res = await fetchWithAuth("/memory/file", {
1939
1954
  method: "PUT",
1940
1955
  headers: { "Content-Type": "application/json" },
1941
- body: JSON.stringify({ path, content })
1956
+ body: JSON.stringify({ path, content, scope: "workspace", agentId })
1942
1957
  });
1943
1958
  if (!res.ok) throw new Error(`Failed to write workspace file: ${res.statusText}`);
1944
1959
  const data = await res.json();
1945
1960
  const raw = data?.data ?? data;
1946
- const file = normalizeWorkspaceFiles({ [path]: raw })[path];
1961
+ const file = toWorkspaceFile(raw);
1947
1962
  setFiles((prev) => ({ ...prev, [path]: file }));
1948
1963
  return file;
1949
1964
  } catch (err) {
@@ -1954,15 +1969,13 @@ function useWorkspaceFiles(threadId, autoFetch = true) {
1954
1969
  setIsSaving(false);
1955
1970
  }
1956
1971
  },
1957
- [fetchWithAuth, threadId]
1972
+ [fetchWithAuth, agentId]
1958
1973
  );
1959
1974
  const deleteFile = useCallback5(
1960
1975
  async (path) => {
1961
- if (!threadId) throw new Error("useWorkspaceFiles: no threadId set");
1962
- const res = await fetchWithAuth(
1963
- `/threads/${threadId}/file?path=${encodeURIComponent(path)}`,
1964
- { method: "DELETE" }
1965
- );
1976
+ if (!agentId) throw new Error("useWorkspaceFiles: no agentId set");
1977
+ const query = new URLSearchParams({ path, scope: "workspace", agentId });
1978
+ const res = await fetchWithAuth(`/memory/file?${query.toString()}`, { method: "DELETE" });
1966
1979
  if (!res.ok) throw new Error(`Failed to delete workspace file: ${res.statusText}`);
1967
1980
  setFiles((prev) => {
1968
1981
  const next = { ...prev };
@@ -1970,15 +1983,15 @@ function useWorkspaceFiles(threadId, autoFetch = true) {
1970
1983
  return next;
1971
1984
  });
1972
1985
  },
1973
- [fetchWithAuth, threadId]
1986
+ [fetchWithAuth, agentId]
1974
1987
  );
1975
1988
  useEffect5(() => {
1976
- if (autoFetch && threadId) {
1989
+ if (autoFetch && agentId) {
1977
1990
  void fetchFiles();
1978
- } else if (!threadId) {
1991
+ } else if (!agentId) {
1979
1992
  setFiles({});
1980
1993
  }
1981
- }, [autoFetch, threadId, fetchFiles]);
1994
+ }, [autoFetch, agentId, fetchFiles]);
1982
1995
  return {
1983
1996
  files,
1984
1997
  isLoading,