-
Notifications
You must be signed in to change notification settings - Fork 629
feat(frontend): @agenta/entities/drive — the drive's headless layer leaves the app #5876
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| import {getEnv} from "@agenta/shared" | ||
|
|
||
| /** | ||
| * Agent file uploads. Default ON; only an explicit "false" disables it. | ||
| * Lives here rather than in the chat slice so the drive code carries no app-layer import. | ||
| */ | ||
| export const isAgentFileUploadsEnabled = (): boolean => | ||
| (getEnv("NEXT_PUBLIC_AGENT_FILE_UPLOADS") || "").toLowerCase() !== "false" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| import {type Mount} from "@agenta/entities/session" | ||
|
|
||
| /** Breadcrumb root label. Mount slugs are the RESERVED form (`__ag__<uuid5>__cwd`) — surface | ||
| * only the human tail ("cwd"), never the uuid (spec: raw ids stay out of labels). */ | ||
| export const driveRootLabel = (mount: Mount | null): string => | ||
| mount?.slug?.split("__").filter(Boolean).pop() ?? "cwd" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,18 +6,15 @@ | |
| * through the authenticated client once (cached blob → object URL). BACKEND ASK: an inline | ||
| * disposition (or real signed URLs) to let media stream natively. | ||
| */ | ||
| import {useCallback, useEffect, useMemo, useState} from "react" | ||
| import {useEffect, useMemo, useState} from "react" | ||
|
|
||
| import {type Mount} from "@agenta/entities/session" | ||
| import {App} from "antd" | ||
| import {axios, getAgentaApiUrl, getAuthToken} from "@agenta/shared/api" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
rg -n -C 4 'mounts/.*/files/download|mounts/files/export|axios\.(get|post)|fetch\(' \
web/packages/agenta-entities/src/drive web/packages/agenta-sdkRepository: Agenta-AI/agenta Length of output: 7798 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== Resources file =="
wc -l web/packages/agenta-sdk/src/resources.ts
sed -n '1,220p' web/packages/agenta-sdk/src/resources.ts
echo "== Drive media relevant sections =="
sed -n '1,80p' web/packages/agenta-entities/src/drive/driveMedia.ts
sed -n '230,345p' web/packages/agenta-entities/src/drive/driveMedia.ts
echo "== API package exports/axios config snippets =="
sed -n '1,140p' web/packages/agenta-shared/src/api.ts
sed -n '1,100p' web/packages/agenta-sdk/src/config.ts
echo "== SDK config package file list =="
fd -a . web/packages/agenta-sdk/src -t f | sed -n '1,120p'
echo "== Search for raw axios/fetch and Fern resource accessor patterns in web packages =="
rg -n "(from \"`@agenta/sdk`\"|from '`@agenta/sdk`'|axios\.get|axios\.post|axios\.blob|fetch\(|queryParams:|responseType: \"blob\"|responseType: \"stream\")" web/packages -g '*.ts' -g '*.tsx' | sed -n '1,220p'Repository: Agenta-AI/agenta Length of output: 14665 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== Locate api/config files =="
fd -a '(^|/)api\.ts$|(^|/)config\.ts$|resources\.ts$' web/packages | sed -n '1,200p'
echo "== Shared api file =="
api_file="$(fd '^api\.ts$' web/packages -d 4 | grep shared | head -n 1)"
echo "${api_file:-NOT_FOUND}"
if [ -n "${api_file:-}" ]; then
wc -l "$api_file"
sed -n '1,160p' "$api_file"
fi
echo "== SDK config =="
config_file="$(fd 'config\.ts$' web/packages/agenta-sdk/src | head -n 1 || true)"
if [ -n "${config_file:-}" ]; then
wc -l "$config_file"
sed -n '1,140p' "$config_file"
fi
echo "== Mounts resource client definition/imports =="
rg -n "file.*download|files.*download|files/download|export|download|FetchBlobs|Blobs|Blob" \
-S web/packages -g '*resource*' -g '*.ts' | sed -n '1,240p'
echo "== SDK package package.json refs =="
for f in web/packages/agenta-entities/package.json web/packages/agenta-sdk/package.json; do
echo "--- $f"
cat "$f"
done
echo "== MountsClient method names if declaration exists =="
tmp="$(mktemp -d)"
cd "$tmp"
set +e
npm view `@agentaai/api-client` 2>/dev/null | sed -n '1,160p'
set -eRepository: Agenta-AI/agenta Length of output: 1408 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== All repository files named api.ts =="
git ls-files '*api.ts' | sed -n '1,200p'
echo "== File under review imports =="
sed -n '1,20p' web/packages/agenta-entities/src/drive/driveMedia.ts
echo "== Repository-wide imports of `@agenta/shared/api` =="
rg -n '`@agenta/shared/api`|shared/api' -S . | sed -n '1,200p'
echo "== SDK build config package names =="
sed -n '1,220p' web/packages/agenta-sdk/src/config.ts
sed -n '1,120p' web/packages/agenta-sdk/package.json
echo "== Mount route and mount client route references =="
rg -n 'files/download|files/export|downloadMountArchive|fetchMountFileBlob|MountsClient' web -S | sed -n '1,260p'Repository: Agenta-AI/agenta Length of output: 33288 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== generated MountsClient get methods around files/download and export =="
sed -n '520,1010p' web/packages/agenta-api-client/src/generated/api/resources/mounts/client/Client.ts
echo "== MountsClient declarations for raw response methods =="
sed -n '1,70p' web/packages/agenta-api-client/src/generated/api/resources/mounts/client/Client.ts
echo "== check source package.json dependency version/workspace =="
sed -n '1,220p' web/packages/agenta-api-client/package.jsonRepository: Agenta-AI/agenta Length of output: 24664 Route mount binary downloads through
Source: Coding guidelines |
||
| import {projectIdAtom} from "@agenta/shared/state" | ||
| import {useAtomValue} from "jotai" | ||
| import {atomFamily} from "jotai/utils" | ||
| import {atomWithQuery} from "jotai-tanstack-query" | ||
|
|
||
| import axios from "@/oss/lib/api/assets/axiosConfig" | ||
| import {getAgentaApiUrl} from "@/oss/lib/helpers/api" | ||
| import {getJWT} from "@/oss/services/api" | ||
| import {projectIdAtom} from "@/oss/state/project" | ||
| import {type Mount} from "@agenta/entities/session" | ||
|
|
||
| import {renderPdfFirstPage} from "./pdfThumb" | ||
|
|
||
|
|
@@ -202,7 +199,7 @@ export async function uploadMountFile({ | |
|
|
||
| /** Raw-bytes save of one file. Module-private on purpose: it reports failure as `false`, which every | ||
| * call site used to drop on the floor — go through {@link useDriveFileDownload}, which reports it. */ | ||
| async function downloadMountFile({ | ||
| export async function downloadMountFile({ | ||
| mount, | ||
| path, | ||
| projectId, | ||
|
|
@@ -225,31 +222,6 @@ async function downloadMountFile({ | |
| return true | ||
| } | ||
|
|
||
| /** `download(mount, path)` for ONE drive file, reporting the outcome through the themed toast | ||
| * (App.useApp, so it renders correctly in dark mode). THE way to trigger a single-file download: | ||
| * {@link downloadMountFile} resolves `false` on failure, so a bare fire-and-forget call left a failed | ||
| * click looking exactly like a successful one. Stable — safe to pass down to list items. */ | ||
| export function useDriveFileDownload(): (mount: Mount | null, path: string) => Promise<boolean> { | ||
| const {message} = App.useApp() | ||
| const projectId = useAtomValue(projectIdAtom) | ||
| return useCallback( | ||
| async (mount: Mount | null, path: string) => { | ||
| // `path` is mount-RELATIVE, so it alone doesn't identify a file: `agent-files/notes.md` | ||
| // and a cwd `notes.md` both arrive here as "notes.md" and would share a toast. | ||
| const key = `drive-download:${mount?.id ?? "none"}:${path}` | ||
| message.open({type: "loading", key, content: "Downloading…", duration: 0}) | ||
| const ok = await downloadMountFile({mount, path, projectId}) | ||
| message.open( | ||
| ok | ||
| ? {type: "success", key, content: "Downloaded"} | ||
| : {type: "error", key, content: "Download failed"}, | ||
| ) | ||
| return ok | ||
| }, | ||
| [message, projectId], | ||
| ) | ||
| } | ||
|
|
||
| /** Download the WHOLE drive as ONE zip ("download all") — spanning every mount the drive folds in | ||
| * (cwd at the root, the agent's durable folder under `agent-files/`). The backend STREAMS the zip | ||
| * (never buffered whole) and reads file bodies with bounded concurrency. | ||
|
|
@@ -300,7 +272,7 @@ export async function downloadMountArchive({ | |
| let writable: WritableFileStreamLike | null = null | ||
| try { | ||
| writable = await handle.createWritable() | ||
| const jwt = await getJWT() | ||
| const jwt = await getAuthToken() | ||
| const url = `${getAgentaApiUrl()}/mounts/files/export?project_id=${encodeURIComponent(projectId)}` | ||
| const response = await fetch(url, { | ||
| method: "POST", | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -10,3 +10,12 @@ export interface DriveId { | |||||||||
| label: string | ||||||||||
| value: string | ||||||||||
| } | ||||||||||
|
|
||||||||||
| /** A file staged for upload but not yet committed to the drive. */ | ||||||||||
| export interface StagedTileItem { | ||||||||||
| id: string | ||||||||||
| name: string | ||||||||||
| file: File | ||||||||||
| /** Object URL for an image preview, else null (icon fallback). Owned by DriveExplorer. */ | ||||||||||
| previewUrl: string | null | ||||||||||
|
Comment on lines
+19
to
+20
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Update the ownership note for The object URL lifecycle now belongs to 📝 Proposed doc fix- /** Object URL for an image preview, else null (icon fallback). Owned by DriveExplorer. */
+ /** Object URL for an image preview, else null (icon fallback). Owned by `useImagePreviews`. */📝 Committable suggestion
Suggested change
|
||||||||||
| } | ||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| /** | ||
| * Drive (agent files) — HEADLESS. Queries, tree building, uploads/downloads, drop parsing and the | ||
| * session/artifact resolution behind the config panel's Files region. Zero JSX and zero UI imports: | ||
| * rendering lives in `@agenta/entity-ui/drive`, and anything app-specific (which conversation, | ||
| * which markdown renderer, the bearer token) arrives as a parameter or a registration. | ||
| */ | ||
| export * from "./agentDrive" | ||
| export * from "./configDrive" | ||
| export * from "./driveFlags" | ||
| export * from "./driveKeyboard" | ||
| export * from "./driveKinds" | ||
| export * from "./driveLabels" | ||
| export * from "./driveMedia" | ||
| export * from "./driveMotion" | ||
| export * from "./driveRepo" | ||
| export * from "./driveTree" | ||
| export * from "./driveTreeView" | ||
| export * from "./driveTypes" | ||
| export * from "./dropEntries" | ||
| export * from "./pdfThumb" | ||
| export * from "./recentChange" | ||
| export * from "./useDelayedTrue" | ||
| export * from "./useDriveDrop" | ||
| export * from "./useDriveFilters" | ||
| export * from "./useDriveSelection" | ||
| export * from "./useDriveTreeKeyboard" | ||
| export * from "./useDriveTreePane" | ||
| export * from "./useDriveTreeReveal" | ||
| export * from "./useDriveTreeViewport" | ||
| export * from "./useDriveUploads" | ||
| export * from "./useImagePreviews" | ||
| export * from "./useMountUpload" | ||
| export * from "./useSessionDrive" | ||
| export * from "./useTreeGroupScroll" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win
Commit the lockfile for the new runtime dependencies.
package.jsonnow declaresmotionandpdfjs-dist, but the pipeline reports thatpnpm install --frozen-lockfilefails becausepnpm-lock.yamllacks both entries. Regenerate and commit the lockfile before merging this change.Source: Pipeline failures