> ## Documentation Index
> Fetch the complete documentation index at: https://docs.xpertai.cn/llms.txt
> Use this file to discover all available pages before exploring further.

# Workspace Files Runtime

> Read, write, resolve, understand, and search files in Xpert workspace volumes.

`WorkspaceFilesRuntimeCapability` exposes `WorkspaceFilesApi` under the stable ID `platform.workspace.files`. It is the plugin-facing file boundary for Xpert workspace Volumes.

Use this capability instead of reading host filesystem paths or implementing plugin-owned storage. It preserves platform scope, returns portable references for asynchronous work, and integrates existing files with the platform file-understanding pipeline.

## Catalogs and scope

The supported logical catalogs are:

```ts theme={null}
type WorkspaceFileCatalog =
  | 'projects'
  | 'users'
  | 'knowledges'
  | 'skills'
  | 'xperts'
  | 'user-xperts'
```

Explicit APIs accept `WorkspaceFileScope` fields such as `tenantId`, `organizationId`, `userId`, `catalog`, `scopeId`, `projectId`, `knowledgeId`, `rootId`, and `xpertId`. Runtime-aware APIs infer the current Agent workspace when possible.

For files in the current Agent runtime, the host exposes the capability only after binding a concrete data owner: Project mode binds `projectId`, while non-Project mode binds `xpertId`. A personal Xpert uses `catalog: 'user-xperts'`, where `scopeId` is the Xpert ID and `userId` remains a separate user-isolation field. Never concatenate `userId` and `xpertId` into one `scopeId`.

`filePath` is always relative to a workspace Volume. It is not `/workspace/...` and is never a host/API-process filesystem path.

## Resolve Workspace Files in a NestJS provider

Agent middleware uses `context.runtime.capabilities` directly. A View Provider, controller, or other NestJS service must inject `XPERT_AGENT_MIDDLEWARE_RUNTIME_TOKEN` and create a scoped API from host-resolved context:

```ts theme={null}
import { Inject, Optional } from '@nestjs/common'
import {
  WorkspaceFilesRuntimeCapability,
  XPERT_AGENT_MIDDLEWARE_RUNTIME_TOKEN,
  type AgentMiddlewareRuntimeScope,
  type AgentMiddlewareRuntimeServiceApi
} from '@xpert-ai/plugin-sdk'

export class DocumentService {
  constructor(
    @Optional()
    @Inject(XPERT_AGENT_MIDDLEWARE_RUNTIME_TOKEN)
    private readonly runtimeService?: AgentMiddlewareRuntimeServiceApi
  ) {}

  workspaceFiles(scope: AgentMiddlewareRuntimeScope) {
    const files = this.runtimeService
      ?.createScopedApi(scope)
      .capabilities?.get(WorkspaceFilesRuntimeCapability)

    if (!files) {
      throw new Error('Workspace Files is unavailable in the current runtime scope')
    }

    return files
  }
}
```

Do not resolve Workspace Files only from `XPERT_RUNTIME_CAPABILITIES_TOKEN` inside a provider. That token represents the global capability set without a bound data owner, and current hosts intentionally omit `WorkspaceFilesRuntimeCapability` from it. See [Runtime Capabilities](./runtime-capabilities#resolve-a-capability-in-a-nestjs-provider) for the complete provider rules.

## Choose the right reference

| Type | Use |
| - | - |
| `WorkspaceFileReference` | Address a known file with explicit scope and a Volume-relative `filePath`. |
| `WorkspaceRuntimeFileDescriptor` | Accept paths and metadata produced by Agent tools, including `/workspace/...` aliases. |
| `WorkspacePortableFileReference` | Persist or enqueue a scope-aware reference for a later callback, queue retry, Artifact version, or Sandbox Job. |
| `WorkspaceFileLocator` | Accept a string, runtime descriptor, or portable reference in a runtime-aware API. |

A portable reference has `source: 'platform.workspace.files'`, a stable `filePath`, scope metadata, and a runtime-facing `workspacePath`. Persist the complete reference; do not reduce it to a sandbox path.

## File operations

| Method | Result | Use |
| - | - | - |
| `uploadBuffer(input)` | `WorkspaceFile` | Upload bytes into an explicitly scoped Volume. |
| `resolveFile(input)` | `WorkspaceFile` | Resolve metadata and an openable URL without loading bytes. |
| `readBuffer(input)` | `WorkspaceFileBuffer` | Read bytes from an explicitly scoped file. |
| `deleteFile(input)` | `void` | Delete an explicitly scoped file. |
| `resolveRuntimeReference(input)` | `WorkspacePortableFileReference` | Normalize a runtime locator without reading bytes. |
| `readRuntimeBuffer(input)` | `WorkspaceRuntimeFileBuffer` | Resolve and read a locator in the current Agent workspace. |
| `writeRuntimeBuffer(input)` | `WorkspaceFile` plus `reference` | Write generated bytes into the current runtime workspace. |

Read a path received from an Agent tool:

```ts theme={null}
import { WorkspaceFilesRuntimeCapability } from '@xpert-ai/plugin-sdk'

const files = context.runtime.capabilities?.require(
  WorkspaceFilesRuntimeCapability
)

if (!files) throw new Error('Workspace Files is unavailable')

const input = await files.readRuntimeBuffer('/workspace/input/specification.pdf')

console.log(input.mimeType, input.size)
// input.buffer contains the bytes for this operation.
// input.reference can be persisted or sent to a background job.
```

Write a generated result:

```ts theme={null}
const output = await files.writeRuntimeBuffer({
  buffer: reportBuffer,
  originalName: 'quality-report.pdf',
  mimeType: 'application/pdf',
  folder: 'reports',
  metadata: {
    resourceType: 'inspection',
    resourceId: inspectionId
  }
})

await queue.enqueue({
  // Store output.reference in the typed queue payload, not output.buffer.
})
```

## File understanding

The understanding APIs reuse the platform FileAsset and FileChunk index. They do not create a plugin-owned duplicate index.

| Method | Use |
| - | - |
| `understandFile(input)` | Register an existing workspace file for parsing and semantic indexing. |
| `getUnderstandingStatus(input)` | Read compact parse and vector-index readiness without returning parsed text. |
| `retryUnderstanding(input)` | Retry parsing and indexing for one failed FileAsset. |
| `listUnderstandingChunks(input)` | Page through bounded chunks in parser order. |
| `searchUnderstandingChunks(input)` | Run hybrid search against the existing chunk index. |
| `validateUnderstandingReferences(input)` | Validate bounded `fileAssetId`/`chunkId` evidence and obtain excerpts. |

Register and search a file:

```ts theme={null}
const understood = await files.understandFile({
  catalog: 'projects',
  projectId,
  filePath: uploaded.filePath,
  originalName: uploaded.name,
  mimeType: uploaded.mimeType,
  purpose: 'workspace',
  parseMode: 'deep'
})

const status = await files.getUnderstandingStatus({
  catalog: 'projects',
  projectId,
  fileAssetId: understood.fileAssetId
})

if (status.vectorIndexStatus === 'ready') {
  const chunks = await files.searchUnderstandingChunks({
    catalog: 'projects',
    projectId,
    fileAssetId: understood.fileAssetId,
    query: 'acceptance criteria for operating pressure',
    limit: 8,
    contentLength: 1200
  })
}
```

`listUnderstandingChunks()` is one-based and returns `hasMore`. The host clamps page sizes, search limits, excerpt lengths, and per-chunk content lengths. Consumers must page rather than assume a full document is returned.

`vectorIndexStatus` is `pending`, `ready`, `failed`, or `unavailable`. Check it separately from the general parse `status` before exposing semantic search.

`WorkspaceMediaFilesApi<TLocator>` is a narrower type for media-generation adapters. It requires `uploadBuffer()` and `readBuffer()` and optionally exposes `readRuntimeBuffer()` and `deleteFile()`; use it when a component should not depend on the complete Workspace Files API.

## Safety and lifecycle

* Create a scoped API from host-resolved server context for each request. Do not cache file capability instances across users, Xperts, Projects, or requests.
* Prefer `resolveFile()` when only metadata or an openable URL is required; use byte-reading methods only for actual processing.
* Never pass `/workspace/...` paths into a delayed job. Convert them with `resolveRuntimeReference()` first.
* Validate stored evidence with `validateUnderstandingReferences()` before presenting or acting on it.
* Scope every explicit operation. Do not construct `filePath` from an untrusted absolute path.
* Raw `Buffer` values belong only to the current server operation. Persist portable references for later work.
