OpenMAIC Material Management for Agents: File Upload and Extraction Pipeline

OpenMAIC manages agent file uploads through a six-phase pipeline covering MIME validation, slot accounting with a 20-file limit, concurrency-limited scheduling with identity bootstrapping, automatic retry logic, and server-side extraction before the content reaches the AI model.

OpenMAIC, developed in the THU-MAIC/OpenMAIC repository, treats every file that an agent or user attaches to a workbench session as material. The system enforces strict governance over these uploads to ensure security, capacity limits, and reliable extraction for downstream AI processing.

Material Validation and Policy Enforcement

Before any upload begins, OpenMAIC validates files against a predefined whitelist. The material-upload-policy.ts file defines the acceptable MIME types and extensions through the WORKBENCH_MATERIAL_ACCEPT constant, while the isWorkbenchMaterialMime() function performs runtime checks.

Only whitelisted file types pass validation, preventing binary abuse and ensuring the extraction pipeline receives compatible formats. This gatekeeper logic runs client-side to fail fast before network transfer begins.

Slot Accounting and Capacity Management

The workbench enforces a hard limit of 20 materials per session via MAX_COMPOSER_MATERIALS in material-upload-scheduling.ts. The MaterialSlotLedger class tracks occupied slots, pending uploads, and available capacity through helper methods:

  • canAccept() – Checks if the session has room for additional files
  • reserve() – Pre-allocates slots for pending uploads
  • settle() – Confirms successful uploads and commits slot usage
  • clearCompleted() – Removes finished uploads from the ledger

This accounting prevents UI overflow and ensures the agent never receives more context than the interface can display.

Upload Scheduling and Concurrency Control

OpenMAIC limits parallel uploads to three concurrent HTTP requests using MATERIAL_UPLOAD_CONCURRENCY in material-upload-scheduling.ts. The MaterialUploadQueue class serializes excess tasks, maintaining a buffer until a slot frees up.

The scheduleMaterialUploadBatch() function serves as the public entry point for UI components. It receives the identity gate, validated files, and an upload function, then orchestrates the entire flow including queue management and concurrency limits.

Identity Bootstrapping and Session Ownership

The first successful upload performs identity bootstrapping by returning an HttpOnly owner cookie that identifies the session owner. Until this cookie is obtained:

  1. Uploads run serially via uploadFirstSuccessfulThenParallel()
  2. The gate.identityEstablished flag remains false
  3. Subsequent uploads queue behind the bootstrap operation

Once the cookie sets gate.identityEstablished = true, the system dispatches uploads in parallel up to the concurrency limit. This pattern ensures the server establishes session ownership before handling parallel streams.

Retry Handling and Error Resilience

Transient HTTP errors (429 Too Many Requests, 503 Service Unavailable) trigger automatic retry logic. The retryMaterialUpload() function implements exponential backoff with up to three attempts before surfacing failure to the user.

This resilience layer runs transparently within the scheduling queue, smoothing over temporary network instability without requiring manual intervention from the agent or user.

Batch Upload Implementation

Front-end components interact with the material management system through a typed API exported from material-upload-scheduling.ts. The typical implementation follows this pattern:

import {
  scheduleMaterialUploadBatch,
  createMaterialUploadIdentityGate,
  MAX_COMPOSER_MATERIALS,
  canAcceptMaterialFiles,
} from '@/lib/workbench/material-upload-scheduling';
import { isWorkbenchMaterialMime } from '@/lib/workbench/material-upload-policy';

// Initialize the identity gate once per session
const uploadGate = createMaterialUploadIdentityGate();

// Collect files from user interaction
const files: File[] = [...]; // from drag-drop or file input

// Validate MIME types against whitelist
const accepted = files.filter(f => isWorkbenchMaterialMime(f.type));

// Verify capacity against the 20-file limit
if (!canAcceptMaterialFiles(currentOccupied, accepted.length)) {
  throw new Error(
    `Too many materials – the workbench can hold at most ${MAX_COMPOSER_MATERIALS} items.`
  );
}

// Define the low-level upload function
async function uploadFile(file: File): Promise<boolean> {
  const resp = await fetch('/api/material/upload', {
    method: 'POST',
    body: file,
    credentials: 'include', // Required for HttpOnly cookie
  });
  return resp.ok;
}

// Execute the batch with automatic concurrency and retry management
await scheduleMaterialUploadBatch(uploadGate, accepted, uploadFile);

Content Extraction Pipeline

After successful storage, the backend triggers the material-extraction pipeline referenced in the upload endpoint. This server-side process handles format normalization such as PDF-to-images, Office documents to plain text, or audio to waveform data.

Extraction workers apply the same MIME whitelist used during upload validation. Once processing completes, the backend emits a "material-ready" event that the client observes. The AI model then consumes these normalized artifacts rather than raw binary files, ensuring consistent downstream behavior.

Summary

  • Validation occurs first: isWorkbenchMaterialMime() in material-upload-policy.ts enforces the MIME whitelist before network transfer.
  • Capacity is strictly capped: MaterialSlotLedger maintains the 20-file limit per session via MAX_COMPOSER_MATERIALS.
  • Concurrency is throttled: MaterialUploadQueue limits parallel requests to three, with serial fallback during identity bootstrapping.
  • Identity bootstrapping secures sessions: The first upload must complete serially to establish the HttpOnly owner cookie before parallel processing begins.
  • Failures retry automatically: Transient errors trigger exponential backoff with three attempts through retryMaterialUpload().
  • Extraction normalizes content: Server-side pipelines convert uploaded files into AI-ready formats, emitting completion events to the client.

Frequently Asked Questions

What is the maximum number of files an agent can upload in a single OpenMAIC session?

OpenMAIC enforces a hard limit of 20 materials per workbench session through the MAX_COMPOSER_MATERIALS constant in material-upload-scheduling.ts. The MaterialSlotLedger class tracks occupied slots and rejects uploads that would exceed this capacity.

How does OpenMAIC handle the first file upload differently from subsequent uploads?

The first successful upload performs identity bootstrapping by obtaining an HttpOnly owner cookie from the server. Until this cookie is received, uploads execute serially via uploadFirstSuccessfulThenParallel(). Once gate.identityEstablished becomes true, the system permits parallel uploads up to the three-request concurrency limit.

What happens if an upload fails due to server rate limiting or temporary unavailability?

OpenMAIC automatically retries failed uploads experiencing transient HTTP errors (429 or 503 status codes). The retryMaterialUpload() function implements exponential backoff and attempts the upload up to three times before propagating the error to the user interface.

Which file types does OpenMAIC accept for agent material uploads?

OpenMAIC accepts only MIME types and extensions defined in the WORKBENCH_MATERIAL_ACCEPT whitelist within material-upload-policy.ts. The isWorkbenchMaterialMime() function validates files against this list client-side before initiating the upload request, blocking unsupported binary formats.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →