# Agent Middleware

Agent middleware lets you inject cross-cutting logic into the Agent lifecycle without changing the core orchestration flow.

## Core model

- `IAgentMiddlewareStrategy`: Strategy contract for each provider.
- `createMiddleware`: Factory that returns one middleware instance.
- Middleware metadata: UI label, description, icon, and config schema.
- Workflow binding: Attach middleware nodes to Agent nodes and control execution order.

## Lifecycle hooks

| Hook            | When it runs             | Typical use                                   |
| --------------- | ------------------------ | --------------------------------------------- |
| `beforeAgent`   | Once before agent starts | State initialization, system prompt injection |
| `beforeModel`   | Before each model call   | Context shaping, dynamic tool injection       |
| `afterModel`    | After model response     | Logging, call normalization                   |
| `afterAgent`    | After agent completes    | Persistence, cleanup                          |
| `wrapModelCall` | Around model call        | Retry, cache, guardrails                      |
| `wrapToolCall`  | Around tool call         | Auth, rate limit, post-processing             |

## Platform Knowledgebase Capability

Agent middleware can use the platform knowledgebase runtime capability instead of calling knowledgebase controllers directly. The shared capability includes:

- `list`: List knowledgebases available to the current workspace.
- `search`: Retrieve chunks by query, knowledgebase IDs, `k`, score threshold, metadata filter, and optional retrieval settings.
- `writeChunk`: Write an Agent-managed or business-plugin-managed text chunk into a knowledgebase.
- `deleteChunks`: Delete Agent-managed chunks by `writeKey` or `writeKeyPrefix`.

`writeChunk` stores content as a chunk under a system-managed document. Business metadata lives on the chunk metadata, so `search` applies metadata filters against both document metadata and chunk metadata before constraining vector search by `documentId` or `chunkId`. This lets business plugins use filters such as `{ documentType: 'bom-product-profile' }` without requiring the system-managed document itself to carry that metadata.

Retrieval guidance:

- Omit `retrieval` to use the knowledgebase's saved retrieval settings, such as `vector`, `graph`, or `hybrid`.
- Pass `retrieval.mode` only when a business flow must override the knowledgebase default.
- `graph` and `hybrid` depend on GraphRAG readiness. Callers should record the requested mode, effective mode, fallback, and graph status in trace or warnings so raw retrieval scores are not confused with business-level scores.

Write/delete guidance:

- Use stable `writeKey` prefixes for each business data type, for example `domain:indexVersion:entityId:`.
- Before re-indexing the same business entity, delete existing chunks with `deleteChunks({ writeKeyPrefix })`, then write the new chunks.
- When deleting the business entity, also delete chunks with the corresponding prefix so removed business data cannot be recalled later.

## Workspace Files Runtime Capability

When Agent middleware needs to read files produced by sandbox tools, use the `platform.workspace.files` runtime capability instead of calling `fs.readFile('/workspace/...')` inside the plugin.

```ts
import { createHash } from 'node:crypto'
import { WorkspaceFilesRuntimeCapability } from '@xpert-ai/plugin-sdk'

const workspaceFiles = context.runtime.capabilities.require(WorkspaceFilesRuntimeCapability)
const file = await workspaceFiles.readRuntimeBuffer({ path: '/workspace/report.docx' })
const sha256 = createHash('sha256').update(file.buffer).digest('hex')

await outboundQueue.enqueueFile(integration, {
  type: 'file',
  uuid,
  contactId,
  filePath: file.filePath,
  fileRef: file.reference,
  fileName: file.name,
  mimeType: file.mimeType,
  size: file.size ?? file.buffer.length,
  sha256
})
```

Capability rules:

- `readRuntimeBuffer()` accepts `/workspace/a.docx`, `a.docx`, `./a.docx`, or a platform portable reference.
- The current Agent runtime automatically binds `tenantId`, `userId`, `projectId` / `xpertId`, and the workspace root.
- The returned `file.reference` is persistable and suitable for queues, callbacks, and async tasks; queues should store only the `reference`, `size`, `sha256`, and similar validation fields.
- `writeRuntimeBuffer()` writes plugin-generated bytes back to the current workspace and returns the same kind of persistable reference.

This keeps business middleware focused on business files without depending on Volume roots, Docker bind mounts, or `/workspace` to `/sandbox` path mappings.

## Best practices

- Keep hooks idempotent and lightweight.
- Declare persistent state through `stateSchema`.
- Call the provided `handler` first in wrappers, then layer custom logic.
- Use middleware composition for logging, auditing, caching, and policy control.
