From 039a7abc684b584840eb85a42c8efae42908e793 Mon Sep 17 00:00:00 2001 From: Parteek Singh Date: Fri, 16 Jan 2026 15:54:12 -0800 Subject: [PATCH 1/9] Sandbox and runtime updates --- content/Agents/standalone-execution.mdx | 65 ++++++++++----- content/Reference/CLI/configuration.mdx | 52 ++++++++++++ content/Reference/CLI/deployment.mdx | 19 +++-- content/Reference/CLI/getting-started.mdx | 99 +++++++++++++++++++++-- content/Reference/CLI/storage.mdx | 4 + content/Services/Sandbox/index.mdx | 17 ++++ content/Services/Sandbox/sdk-usage.mdx | 89 ++++++++++++++++++++ 7 files changed, 314 insertions(+), 31 deletions(-) diff --git a/content/Agents/standalone-execution.mdx b/content/Agents/standalone-execution.mdx index c402cb4b..5076fca6 100644 --- a/content/Agents/standalone-execution.mdx +++ b/content/Agents/standalone-execution.mdx @@ -16,10 +16,20 @@ import { createAgentContext } from '@agentuity/runtime'; import chatAgent from '@agent/chat'; const ctx = createAgentContext(); -const result = await ctx.invoke(() => chatAgent.run({ message: 'Hello' })); +const result = await ctx.run(chatAgent, { message: 'Hello' }); ``` -The `invoke()` method executes your agent with full infrastructure support: tracing, session management, and access to all storage services. +The `run()` method executes your agent with full infrastructure support: tracing, session management, and access to all storage services. + +For agents that don't require input: + +```typescript +const result = await ctx.run(statusAgent); +``` + + +The older `ctx.invoke(() => agent.run(input))` pattern still works but `ctx.run(agent, input)` is preferred for its cleaner syntax. + ## Options @@ -45,10 +55,7 @@ await createApp(); // Run cleanup every hour cron.schedule('0 * * * *', async () => { const ctx = createAgentContext({ trigger: 'cron' }); - - await ctx.invoke(async () => { - await cleanupAgent.run({ task: 'expired-sessions' }); - }); + await ctx.run(cleanupAgent, { task: 'expired-sessions' }); }); ``` @@ -58,25 +65,23 @@ For most scheduled tasks, use the [`cron()` middleware](/Routes/cron) instead. I ## Multiple Agents in Sequence -Run multiple agents within a single `invoke()` call to share the same session and tracing context: +Run multiple agents in sequence with the same context: ```typescript const ctx = createAgentContext(); -const result = await ctx.invoke(async () => { - // First agent analyzes the input - const analysis = await analyzeAgent.run({ text: userInput }); - - // Second agent generates response based on analysis - const response = await respondAgent.run({ - analysis: analysis.summary, - sentiment: analysis.sentiment, - }); +// First agent analyzes the input +const analysis = await ctx.run(analyzeAgent, { text: userInput }); - return response; +// Second agent generates response based on analysis +const response = await ctx.run(respondAgent, { + analysis: analysis.summary, + sentiment: analysis.sentiment, }); ``` +Each `ctx.run()` call shares the same session and tracing context. + ## Reusing Contexts Create a context once and reuse it for multiple invocations: @@ -84,9 +89,9 @@ Create a context once and reuse it for multiple invocations: ```typescript const ctx = createAgentContext({ trigger: 'websocket' }); -// Each invoke() gets its own session and tracing span +// Each run() gets its own session and tracing span websocket.on('message', async (data) => { - const result = await ctx.invoke(() => messageAgent.run(data)); + const result = await ctx.run(messageAgent, data); websocket.send(result); }); ``` @@ -104,6 +109,28 @@ Standalone contexts provide the same infrastructure as HTTP request handlers: - **Session events**: Start/complete events for observability +## Detecting Runtime Context + +Use `isInsideAgentRuntime()` to check if code is running within the Agentuity runtime: + +```typescript +import { isInsideAgentRuntime, createAgentContext } from '@agentuity/runtime'; +import myAgent from '@agent/my-agent'; + +async function processRequest(data: unknown) { + if (isInsideAgentRuntime()) { + // Already in runtime context, call agent directly + return myAgent.run(data); + } + + // Outside runtime, create context first + const ctx = createAgentContext(); + return ctx.run(myAgent, data); +} +``` + +This is useful for writing utility functions that work both inside agent handlers and in standalone scripts. + ## Next Steps - [Calling Other Agents](/Agents/calling-other-agents): Agent-to-agent communication patterns diff --git a/content/Reference/CLI/configuration.mdx b/content/Reference/CLI/configuration.mdx index 812c850b..198d4efd 100644 --- a/content/Reference/CLI/configuration.mdx +++ b/content/Reference/CLI/configuration.mdx @@ -118,6 +118,58 @@ agentuity cloud secret import .env.secrets **In agents:** Access secrets via `process.env.API_KEY`. Secrets are injected at runtime and never logged. +## Organization-Level Configuration + +Set environment variables and secrets at the organization level to share them across all projects in that organization. Use the `--org` flag with any `env` or `secret` command. + +### Set Org-Level Variables + +```bash +# Set using your default org +agentuity cloud env set DATABASE_URL "postgresql://..." --org + +# Set for a specific org +agentuity cloud env set DATABASE_URL "postgresql://..." --org org_abc123 +``` + +### Set Org-Level Secrets + +```bash +# Set shared secret for default org +agentuity cloud secret set SHARED_API_KEY "sk_..." --org + +# Set for specific org +agentuity cloud secret set SHARED_API_KEY "sk_..." --org org_abc123 +``` + +### List Org-Level Values + +```bash +# List org environment variables +agentuity cloud env list --org + +# List org secrets +agentuity cloud secret list --org +``` + +### Get/Delete Org-Level Values + +```bash +# Get an org variable +agentuity cloud env get DATABASE_URL --org + +# Delete an org secret +agentuity cloud secret delete OLD_KEY --org +``` + + +Organization-level values are inherited by all projects in that organization. Project-level values take precedence over organization-level values when both are set. + + + +Set a default organization with `agentuity auth org select` to avoid specifying `--org` on every command. See [Getting Started](/Reference/CLI/getting-started) for details. + + ## API Keys Create and manage API keys for programmatic access to your project. diff --git a/content/Reference/CLI/deployment.mdx b/content/Reference/CLI/deployment.mdx index 2ae513da..911c19d4 100644 --- a/content/Reference/CLI/deployment.mdx +++ b/content/Reference/CLI/deployment.mdx @@ -124,16 +124,19 @@ agentuity cloud deployment show dep_abc123xyz --project-id=proj_abc123 - Active status - Tags and custom domains - Cloud region +- DNS records (for custom domain configuration) - Git metadata (repo, commit, branch, PR) - Build information (SDK version, runtime, platform) ``` -ID: dep_abc123xyz -Project: proj_456def -State: completed -Active: Yes -Created: 12/1/25, 3:45 PM -Tags: staging, hotfix-123 +ID: dep_abc123xyz +Project: proj_456def +State: completed +Active: Yes +Created: 12/1/25, 3:45 PM +Tags: staging, hotfix-123 +Domains: api.example.com +DNS Records: api.example.com CNAME p1234567890.agentuity.cloud Git Information Repo: myorg/myapp @@ -148,6 +151,10 @@ Build Information Arch: x64 ``` + +When custom domains are configured, the `show` command displays the required DNS records. Use this to verify your CNAME configuration. + + ## Viewing Logs Fetch logs for a deployment: diff --git a/content/Reference/CLI/getting-started.mdx b/content/Reference/CLI/getting-started.mdx index ed49eb60..83b5fa2c 100644 --- a/content/Reference/CLI/getting-started.mdx +++ b/content/Reference/CLI/getting-started.mdx @@ -92,6 +92,38 @@ agentuity auth logout agentuity logout ``` +## Preferences + +Set default organization and region to avoid specifying them on every command. + +### Organization Preferences + +```bash +# Set default organization +agentuity auth org select org_abc123 + +# Show current default organization +agentuity auth org current + +# Clear default organization +agentuity auth org unselect +``` + +When a default organization is set, commands that support `--org` will use it automatically. + +### Region Preferences + +```bash +# Set default region for deployments +agentuity cloud region select us-west-1 + +# Show current default region +agentuity cloud region current + +# Clear default region +agentuity cloud region unselect +``` + ## Creating Projects ### Create a New Project @@ -130,12 +162,17 @@ The agent uses the OpenAI SDK routed through Agentuity's AI Gateway. This means ### Project Creation Options -- `--name `: Project name -- `--dir `: Directory to create the project in -- `--template `: Template to use (optional, defaults to "default") -- `--no-install`: Skip dependency installation -- `--no-build`: Skip initial build -- `--no-register`: Don't register the project with Agentuity Cloud +| Option | Description | +|--------|-------------| +| `--name ` | Project name | +| `--dir ` | Directory to create the project in | +| `--template ` | Template to use (optional, defaults to "default") | +| `--no-install` | Skip dependency installation | +| `--no-build` | Skip initial build | +| `--no-register` | Don't register the project with Agentuity Cloud | +| `--database ` | Database option: `skip`, `new`, or existing database name | +| `--storage ` | Storage option: `skip`, `new`, or existing bucket name | +| `--enable-auth` | Enable Agentuity Auth for the project | Example: @@ -146,6 +183,35 @@ agentuity project create \ --no-build ``` +### Headless Project Creation + +Create projects non-interactively for CI/CD pipelines and automation: + +```bash +# Create with new database and storage +agentuity project create \ + --name my-agent \ + --database new \ + --storage new \ + --enable-auth + +# Skip optional resources +agentuity project create \ + --name my-agent \ + --database skip \ + --storage skip + +# Use existing resources +agentuity project create \ + --name my-agent \ + --database my-existing-db \ + --storage my-bucket +``` + + +The `--database` and `--storage` options require authentication. Run `agentuity login` first or the command will fail. + + ## Managing Projects ### List Projects @@ -194,6 +260,27 @@ agentuity project delete --confirm Project deletion is permanent and cannot be undone. Deployed agents and associated resources will be removed. +### Import a Project + +Register an existing local Agentuity project with the cloud: + +```bash +# Import current directory +agentuity project import + +# Import from specific directory +agentuity project import --dir ./my-existing-project + +# Validate without importing +agentuity project import --validate-only +``` + +The import command verifies that the directory contains a valid Agentuity project (requires `@agentuity/runtime` dependency and either `agentuity.config.ts` or an `agentuity/` directory). + + +When you run `agentuity dev` or `agentuity deploy` on an unregistered project while authenticated, the CLI will prompt you to import it automatically. + + ## Command Shortcuts Several common commands have shortcuts that let you skip the subcommand prefix: diff --git a/content/Reference/CLI/storage.mdx b/content/Reference/CLI/storage.mdx index a19c6764..76b64e84 100644 --- a/content/Reference/CLI/storage.mdx +++ b/content/Reference/CLI/storage.mdx @@ -13,6 +13,10 @@ All storage commands require the `cloud` prefix. For example: `agentuity cloud k Inspect and manage key-value data organized into namespaces. + +KV storage is scoped to your organization, not individual projects. You can run KV commands from any directory without needing a project context. + + ### Interactive REPL Start an interactive session for faster exploration: diff --git a/content/Services/Sandbox/index.mdx b/content/Services/Sandbox/index.mdx index 8b6845d3..266f812b 100644 --- a/content/Services/Sandbox/index.mdx +++ b/content/Services/Sandbox/index.mdx @@ -62,6 +62,21 @@ Pre-configured AI coding assistants: Run `agentuity cloud sandbox runtime list` to see all available runtimes, or view them in the [Agentuity App](https://app-v1.agentuity.com) under **Services > Sandbox > Runtimes**. +### Runtime Metadata + +Each runtime includes metadata for identification and resource planning: + +| Field | Description | +|-------|-------------| +| `description` | What the runtime provides | +| `icon` | URL to runtime icon | +| `brandColor` | Hex color for UI display | +| `documentationUrl` | Link to runtime documentation | +| `tags` | Categories like `language`, `ai-agent` | +| `requirements` | Minimum memory, CPU, disk, and network needs | + +View runtime details with `agentuity cloud sandbox runtime list --json`. + ## Snapshots A snapshot captures the filesystem state of a sandbox. You create new sandboxes *from* a snapshot rather than running it directly. @@ -143,6 +158,8 @@ The API is identical in both contexts. | `resources.cpu` | CPU limit in millicores | `'500m'`, `'1000m'` | | `resources.disk` | Disk space limit | `'1Gi'` | | `network.enabled` | Allow outbound network | `true` (default: `false`) | +| `network.port` | Port to expose to internet (1024-65535) | `3000` | +| `projectId` | Associate sandbox with a project | `'proj_abc123'` | | `timeout.idle` | Idle timeout before cleanup | `'10m'`, `'1h'` | | `timeout.execution` | Max execution time per command | `'5m'`, `'30s'` | | `dependencies` | Apt packages to install | `['python3', 'git']` | diff --git a/content/Services/Sandbox/sdk-usage.mdx b/content/Services/Sandbox/sdk-usage.mdx index c0d2c313..18589211 100644 --- a/content/Services/Sandbox/sdk-usage.mdx +++ b/content/Services/Sandbox/sdk-usage.mdx @@ -121,6 +121,49 @@ await sandbox.writeFiles([ ]); ``` +### Exposing Ports + +Expose a port from the sandbox to make it accessible via a public URL: + +```typescript +const sandbox = await ctx.sandbox.create({ + network: { + enabled: true, + port: 3000, // Expose port 3000 (valid range: 1024-65535) + }, + resources: { memory: '512Mi' }, +}); + +// Start a web server inside the sandbox +await sandbox.execute({ command: ['npm', 'run', 'serve'] }); + +// Get the public URL +const info = await ctx.sandbox.get(sandbox.id); +if (info.url) { + ctx.logger.info('Server accessible at', { url: info.url, port: info.networkPort }); +} +``` + + +Setting `network.port` requires `network.enabled: true`. The sandbox must have network access to expose ports. + + +### Project Association + +Associate sandboxes with a project for organization and filtering: + +```typescript +const sandbox = await ctx.sandbox.create({ + projectId: 'proj_abc123', // Associate with project + resources: { memory: '512Mi' }, +}); + +// List sandboxes by project +const { sandboxes } = await ctx.sandbox.list({ + projectId: 'proj_abc123', +}); +``` + ### Reading Files Read files from the sandbox as streams: @@ -350,6 +393,8 @@ await ctx.sandbox.destroy('sbx_abc123'); | `resources.cpu` | `string` | CPU in millicores: `'500m'`, `'1000m'` | | `resources.disk` | `string` | Disk limit: `'512Mi'`, `'2Gi'` | | `network.enabled` | `boolean` | Enable outbound network (default: `false`) | +| `network.port` | `number` | Port to expose to internet (1024-65535) | +| `projectId` | `string` | Associate sandbox with a project | | `timeout.idle` | `string` | Auto-destroy after idle: `'10m'`, `'1h'` | | `timeout.execution` | `string` | Max command duration: `'30s'`, `'5m'` | | `dependencies` | `string[]` | Apt packages: `['python3', 'git']` | @@ -389,6 +434,50 @@ Returned by `sandbox.execute()`: | `stdout` | `string` | Captured stdout (if available) | | `stderr` | `string` | Captured stderr (if available) | +### SandboxInfo + +Returned by `ctx.sandbox.get()` and in list results: + +| Field | Type | Description | +|-------|------|-------------| +| `sandboxId` | `string` | Unique sandbox identifier | +| `status` | `string` | `'idle'`, `'busy'`, `'terminated'` | +| `createdAt` | `string` | ISO timestamp | +| `snapshotId` | `string` | Source snapshot (if created from snapshot) | +| `networkPort` | `number` | Port exposed from sandbox (if configured) | +| `url` | `string` | Public URL (when port is configured) | +| `user` | `SandboxUserInfo` | User who created the sandbox | +| `agent` | `SandboxAgentInfo` | Agent that created the sandbox | +| `project` | `SandboxProjectInfo` | Associated project | +| `org` | `SandboxOrgInfo` | Organization (always present) | + +Access context information from sandbox info: + +```typescript +const info = await ctx.sandbox.get('sbx_abc123'); + +// Organization is always present +ctx.logger.info('Organization', { id: info.org.id, name: info.org.name }); + +// User info (when created by a user) +if (info.user) { + ctx.logger.info('Created by', { + userId: info.user.id, + name: `${info.user.firstName} ${info.user.lastName}`, + }); +} + +// Agent info (when created by an agent) +if (info.agent) { + ctx.logger.info('Agent', { id: info.agent.id, name: info.agent.name }); +} + +// Project info (when associated with a project) +if (info.project) { + ctx.logger.info('Project', { id: info.project.id, name: info.project.name }); +} +``` + ## Best Practices - **Set [resource limits](#sandboxcreateoptions)**: Control memory and CPU usage for predictable performance From 2b618c6e2aebe08aac7df0eb05bc0d7ed112c6ad Mon Sep 17 00:00:00 2001 From: Parteek Singh Date: Wed, 21 Jan 2026 11:14:49 -0600 Subject: [PATCH 2/9] Update the "why" for features --- content/Agents/evaluations.mdx | 15 + content/Agents/workbench.mdx | 13 + content/Reference/CLI/deployment.mdx | 13 + content/Reference/CLI/development.mdx | 13 + .../Observability/sessions-debugging.mdx | 15 + content/Services/Sandbox/index.mdx | 15 + content/Services/Storage/durable-streams.mdx | 15 + content/Services/Storage/key-value.mdx | 25 +- content/Services/queues.mdx | 322 ++++++++++++++++++ 9 files changed, 443 insertions(+), 3 deletions(-) create mode 100644 content/Services/queues.mdx diff --git a/content/Agents/evaluations.mdx b/content/Agents/evaluations.mdx index fd0c1804..97b5c245 100644 --- a/content/Agents/evaluations.mdx +++ b/content/Agents/evaluations.mdx @@ -5,6 +5,21 @@ description: Automatically test and validate agent outputs for quality and compl Evaluations (evals) are automated tests that run after your agent completes. They validate output quality, check compliance, and monitor performance without blocking agent responses. +## Why Evals? + +Most evaluation tools test the LLM: did the model respond appropriately? That's fine for chatbots, but agents aren't single LLM calls. They're entire runs with multiple model calls, tool executions, and orchestration working together. + +When an agent fails, the problem is often not the model output. It might be a tool call that returned bad data, a state bug that corrupted context, or an orchestration issue that called the wrong agent. Testing just the LLM response misses all of this. + +Agentuity evals test the whole run, not just model outputs. And they run on every session in production, not just during development. You catch issues with real traffic, not synthetic test cases. + +**The result:** + +- **Full-run evaluation**: Test the entire agent execution, not just LLM responses +- **Production monitoring**: Evals run on every session automatically, catching issues with real users +- **Async by default**: Evals don't block responses, so users aren't waiting +- **Preset library**: Common checks (PII, safety, hallucination) available out of the box + Evals come in two types: **binary** (pass/fail) for yes/no criteria, and **score** (0-1) for quality gradients. diff --git a/content/Agents/workbench.mdx b/content/Agents/workbench.mdx index c45ce29c..f0e1e01c 100644 --- a/content/Agents/workbench.mdx +++ b/content/Agents/workbench.mdx @@ -5,6 +5,19 @@ description: Use the built-in development UI to test agents, validate schemas, a Workbench is a built-in UI for testing your agents during development. It automatically discovers your agents, displays their input/output schemas, and lets you execute them with real inputs. +## Why Workbench? + +Testing agents isn't like testing traditional APIs. You need to validate input schemas, see how responses format, test multi-turn conversations, and understand execution timing. Using `curl` or Postman means manually constructing JSON payloads and parsing responses. + +Workbench understands your agents. It reads your schemas, generates test forms, maintains conversation threads, and shows execution metrics. When something goes wrong, you see exactly what the agent received and returned. + +**Key capabilities:** + +- **Schema-aware testing**: Input forms generated from your actual schemas +- **Thread persistence**: Test multi-turn conversations without manual state tracking +- **Execution metrics**: See token usage and response times for every request +- **Quick iteration**: Test prompts display in the UI for one-click execution + ## Enabling Workbench Add a `workbench` section to your `agentuity.config.ts`: diff --git a/content/Reference/CLI/deployment.mdx b/content/Reference/CLI/deployment.mdx index 911c19d4..81eb437f 100644 --- a/content/Reference/CLI/deployment.mdx +++ b/content/Reference/CLI/deployment.mdx @@ -5,6 +5,19 @@ description: Deploy your agents to Agentuity Cloud with automatic infrastructure Deploy your Agentuity project to the cloud with a single command. The platform handles infrastructure, scaling, and monitoring automatically. +## Why Deploy to Agentuity? + +Deploying agents isn't like deploying a web app. Agents need long-running processes, persistent storage, sandbox environments, and observability built in. Setting this up yourself means stitching together containers, databases, secret management, and monitoring. + +Agentuity handles all of this automatically. You push code, and the platform provisions everything your agents need: compute, storage, networking, and observability. No infrastructure configuration, no Docker files, no Kubernetes. + +**What this gives you:** + +- **Single-command deployment**: `agentuity deploy` handles build, upload, and provisioning +- **Automatic HTTPS**: Every deployment gets secure URLs with SSL certificates +- **Built-in rollback**: Revert to any previous deployment instantly +- **Zero infrastructure management**: No containers, orchestration, or scaling to configure + ## Deploy Your Project ```bash diff --git a/content/Reference/CLI/development.mdx b/content/Reference/CLI/development.mdx index c5f0b8e8..fcf92195 100644 --- a/content/Reference/CLI/development.mdx +++ b/content/Reference/CLI/development.mdx @@ -150,6 +150,19 @@ agentuity dev agentuity dev --public ``` +### Why Public URLs? + +Testing webhooks and external integrations during local development is painful. You either deploy constantly, configure port forwarding, or pay for third-party tunneling services. Each option adds friction to the development loop. + +Agentuity's Gravity network handles this automatically. When you run `agentuity dev`, your local server gets a public HTTPS URL instantly. No configuration, no separate tools, no accounts to manage. External services can reach your local agents as if they were already deployed. + +**This means:** + +- **Instant HTTPS URLs**: Public URLs with automatic certificate generation +- **No configuration**: Works out of the box, no firewall rules or port forwarding +- **Secure tunneling**: Encrypted connections through Agentuity's edge network +- **Automatic reconnection**: Handles network interruptions gracefully + The `--public` flag creates a secure tunnel through Agentuity's edge network, giving your local server a public HTTPS URL for testing webhooks, sharing with teammates, or external integrations. **Example output:** diff --git a/content/Services/Observability/sessions-debugging.mdx b/content/Services/Observability/sessions-debugging.mdx index 97006031..084744ee 100644 --- a/content/Services/Observability/sessions-debugging.mdx +++ b/content/Services/Observability/sessions-debugging.mdx @@ -5,6 +5,21 @@ description: Debug agents using session IDs, CLI commands, and trace timelines Every request to your agents gets a unique session ID (`sess_...`). Sessions link logs, traces, and state, making them essential for debugging. +## Why Sessions? + +Traditional observability tracks individual HTTP requests. That works for stateless web servers, but agents aren't single requests. A single conversation might span dozens of LLM calls, tool executions, and orchestration steps across multiple interactions. + +Without sessions, debugging becomes guesswork. You see individual logs but can't connect them. Was that error from the same conversation? What happened before it failed? Which user was affected? + +Agentuity tracks all of this automatically. Every run ties to a session. Every conversation thread is preserved. You get full context into what happened, when, and why, without writing tracking code. + +**In practice:** + +- **Unified tracing**: All logs, spans, and state from a single request are linked by session ID +- **Conversation context**: Sessions group into threads, so you can trace multi-turn conversations +- **Automatic correlation**: No manual tracking code, every call in a session is connected +- **Time-travel debugging**: Replay what happened in a session to reproduce issues + ## Sessions vs Threads | Scope | Lifetime | ID Prefix | Use For | diff --git a/content/Services/Sandbox/index.mdx b/content/Services/Sandbox/index.mdx index 266f812b..0c2bd377 100644 --- a/content/Services/Sandbox/index.mdx +++ b/content/Services/Sandbox/index.mdx @@ -5,6 +5,21 @@ description: Run code in isolated, secure containers with configurable resources Execute code in isolated Linux containers with configurable resource limits, network controls, and execution timeouts. +## Why Sandboxes? + +Agents that reason about code need somewhere safe to execute it. Whether generating Python scripts, validating builds, or running user-provided code, you can't let arbitrary execution happen on your infrastructure. + +The pattern keeps repeating: spin up a secure environment, run code, tear it down. Without proper isolation, a single bad script could access sensitive data, exhaust resources, or compromise your systems. + +Agentuity sandboxes handle this automatically. Every execution runs in an isolated container with its own filesystem, configurable resource limits, and network controls. When execution completes, the environment is destroyed. + +**What this gives you:** + +- **Security by default**: Network disabled, filesystem isolated, resource limits enforced +- **No infrastructure management**: Containers spin up and tear down automatically +- **Multi-language support**: Your agents are TypeScript, but sandboxes can run Python, Node.js, shell scripts, or anything available via `apt install` +- **Consistent environments**: Use snapshots to get the same setup every time, with dependencies pre-installed + ## Three Ways to Use Sandboxes | Method | Best For | diff --git a/content/Services/Storage/durable-streams.mdx b/content/Services/Storage/durable-streams.mdx index 413836c4..2555b5f7 100644 --- a/content/Services/Storage/durable-streams.mdx +++ b/content/Services/Storage/durable-streams.mdx @@ -5,6 +5,21 @@ description: Streaming storage for large exports, audit logs, and real-time data Durable streams provide streaming storage for large data exports, audit logs, and real-time processing. Streams follow a **write-once, read-many** pattern: once data is written and the stream is closed, the content is immutable and can be accessed via a permanent public URL. +## Why Durable Streams? + +WebSocket and SSE connections are straightforward to set up, but they're fragile in practice. Tabs get suspended, networks disconnect, pages get refreshed. When the connection drops, any in-flight data is lost—unless you build custom reconnection logic on top. + +This becomes a real problem when you're streaming LLM responses. Token streaming is often the primary UI for chat applications, and agentic apps stream tool outputs and progress events over long-running sessions. If someone refreshes mid-generation, you're faced with two bad options: re-run an expensive inference call, or lose the response entirely. + +Durable streams solve this by decoupling the data from the connection. Instead of streaming directly to the client, you write to persistent storage and return a URL. The stream continues writing in the background regardless of what happens on the client side. + +**What this gives you:** + +- **Refresh-safe**: If someone refreshes the page mid-stream, the URL still works and the content is preserved. The expensive work you already did isn't wasted. +- **Background processing**: Return the stream URL immediately and write data asynchronously with `ctx.waitUntil()`. Your handler responds fast while the stream continues writing. +- **Shareable URLs**: A stream is just a URL. You can share it, open it on another device, or have multiple viewers access the same output. +- **Permanent artifacts**: Once closed, streams are immutable and accessible via their public URL. Useful for audit logs, exports, and generated reports. + ## When to Use Durable Streams | Storage Type | Best For | diff --git a/content/Services/Storage/key-value.mdx b/content/Services/Storage/key-value.mdx index 577cad1f..33d4b68c 100644 --- a/content/Services/Storage/key-value.mdx +++ b/content/Services/Storage/key-value.mdx @@ -152,8 +152,15 @@ Create and delete namespaces programmatically: ```typescript const agent = createAgent('NamespaceManager', { handler: async (ctx, input) => { - // Create a new namespace - await ctx.kv.createNamespace('tenant-123'); + // Create a namespace with default TTL for all keys + await ctx.kv.createNamespace('cache', { + defaultTTLSeconds: 3600, // all keys expire in 1 hour by default + }); + + // Create a namespace with no expiration + await ctx.kv.createNamespace('config', { + defaultTTLSeconds: 0, // keys never expire + }); // Delete a namespace (removes all keys) await ctx.kv.deleteNamespace('old-cache'); @@ -163,13 +170,25 @@ const agent = createAgent('NamespaceManager', { }); ``` +**Default TTL options:** + +| Value | Behavior | +|-------|----------| +| `undefined` | Keys expire after 7 days (default) | +| `0` | Keys never expire | +| `60` - `7776000` | Custom TTL in seconds (1 minute to 90 days) | + + +When a key is read with less than 50% of its TTL remaining, the expiration is automatically extended. This keeps frequently-accessed data alive without manual renewal. + + `deleteNamespace()` permanently removes the namespace and all its keys. This operation cannot be undone. ## TTL Strategy -Keys persist indefinitely by default. Use TTL for temporary data: +Keys expire after 7 days by default unless a namespace-level or per-key TTL is set. Use TTL for temporary data: | Data Type | Suggested TTL | |-----------|---------------| diff --git a/content/Services/queues.mdx b/content/Services/queues.mdx new file mode 100644 index 00000000..2f64419a --- /dev/null +++ b/content/Services/queues.mdx @@ -0,0 +1,322 @@ +--- +title: Queues +description: Publish messages for async processing, webhooks, and event-driven workflows +--- + +Queues enable asynchronous message processing for background tasks, webhooks, and event-driven workflows. Publish messages from agents and consume them with workers or webhook destinations. + +## When to Use Queues + +| Pattern | Best For | +|---------|----------| +| **Queues** | Background jobs, webhooks, event-driven processing, decoupled services | +| [Durable Streams](/Services/Storage/durable-streams) | Large exports, audit logs, streaming data | +| [Key-Value](/Services/Storage/key-value) | Fast lookups, caching, configuration | + +**Use queues when you need to:** +- Process tasks asynchronously (email sending, report generation) +- Decouple services with reliable message delivery +- Deliver webhooks to external endpoints +- Handle bursty workloads with rate limiting +- Retry failed operations with exponential backoff + +## Queue Types + +| Type | Behavior | +|------|----------| +| `worker` | Each message is processed by exactly one consumer. Use for background jobs. | +| `pubsub` | Messages are broadcast to all subscribers. Use for event notifications. | + +## Publishing Messages + +Publish messages from agents using `ctx.queue.publish()`: + +```typescript +import { createAgent } from '@agentuity/runtime'; + +const agent = createAgent('OrderProcessor', { + handler: async (ctx, input) => { + // Queue an email to be sent asynchronously + const result = await ctx.queue.publish('email-queue', { + to: input.customerEmail, + subject: 'Order Confirmed', + orderId: input.orderId, + }); + + ctx.logger.info('Email queued', { messageId: result.id }); + + return { success: true, messageId: result.id }; + }, +}); +``` + +### Publish Options + +```typescript +const agent = createAgent('TaskScheduler', { + handler: async (ctx, input) => { + await ctx.queue.publish('task-queue', input.task, { + // Attach metadata for filtering or routing + metadata: { priority: 'high', region: 'us-west' }, + + // Guarantee ordering for messages with the same key + partitionKey: input.customerId, + + // Prevent duplicate messages + idempotencyKey: `task-${input.taskId}-v1`, + + // Auto-expire after 1 hour + ttl: 3600, + }); + + return { queued: true }; + }, +}); +``` + +| Option | Description | +|--------|-------------| +| `metadata` | Key-value pairs for routing or filtering | +| `partitionKey` | Messages with the same key are processed in order | +| `idempotencyKey` | Prevents duplicate messages if the same key is published again | +| `ttl` | Time-to-live in seconds before the message expires | + +### Publish Result + +```typescript +interface QueuePublishResult { + id: string; // Unique message ID (msg_...) + offset: number; // Sequential position in the queue + publishedAt: string; // ISO 8601 timestamp +} +``` + +## Error Handling + +```typescript +import { QueueNotFoundError, QueueValidationError } from '@agentuity/runtime'; + +const agent = createAgent('SafePublisher', { + handler: async (ctx, input) => { + try { + await ctx.queue.publish('notifications', input.notification); + return { success: true }; + } catch (error) { + if (error instanceof QueueNotFoundError) { + ctx.logger.error('Queue does not exist', { queue: 'notifications' }); + return { success: false, error: 'Queue not found' }; + } + if (error instanceof QueueValidationError) { + ctx.logger.error('Invalid message', { field: error.field }); + return { success: false, error: 'Validation failed' }; + } + throw error; + } + }, +}); +``` + +## Queue Management + +Create and manage queues using the CLI or `@agentuity/server` package. + +### CLI Commands + +```bash +# Create a worker queue +agentuity cloud queue create --name order-processing --type worker + +# Create a pubsub queue for broadcasting +agentuity cloud queue create --name events --type pubsub + +# List all queues +agentuity cloud queue list + +# Get queue details and stats +agentuity cloud queue get order-processing + +# Pause/resume processing +agentuity cloud queue pause order-processing +agentuity cloud queue resume order-processing + +# Delete a queue +agentuity cloud queue delete order-processing +``` + +### Programmatic Management + +Use `@agentuity/server` for full queue management in routes or standalone scripts: + +```typescript +import { createQueue, listQueues, deleteQueue } from '@agentuity/server'; + +// Create a queue +const queue = await createQueue(client, { + name: 'order-processing', + queue_type: 'worker', + settings: { + default_max_retries: 5, + default_visibility_timeout_seconds: 60, + }, +}); + +// List queues +const { queues } = await listQueues(client); + +// Delete a queue +await deleteQueue(client, 'old-queue'); +``` + +## Consuming Messages + +### Webhook Destinations + +Configure a webhook destination to automatically deliver messages to an HTTP endpoint: + +```typescript +import { createDestination } from '@agentuity/server'; + +await createDestination(client, 'order-processing', { + destination_type: 'http', + config: { + url: 'https://api.example.com/webhook/orders', + method: 'POST', + headers: { 'X-API-Key': 'secret' }, + timeout_ms: 30000, + retry_policy: { + max_attempts: 5, + initial_backoff_ms: 1000, + max_backoff_ms: 60000, + backoff_multiplier: 2.0, + }, + }, +}); +``` + +### Pull-Based Consumption + +For workers that pull messages: + +```typescript +import { receiveMessage, ackMessage, nackMessage } from '@agentuity/server'; + +// Receive a message (blocks until available or timeout) +const message = await receiveMessage(client, 'order-processing'); + +if (message) { + try { + // Process the message + await processOrder(message.payload); + + // Acknowledge success + await ackMessage(client, 'order-processing', message.id); + } catch (error) { + // Negative acknowledge - message returns to queue for retry + await nackMessage(client, 'order-processing', message.id); + } +} +``` + +## Dead Letter Queue + +Messages that exceed the retry limit are moved to the dead letter queue (DLQ). Inspect and replay failed messages: + +```bash +# List failed messages +agentuity cloud queue dlq order-processing + +# Replay a message back to the queue +agentuity cloud queue dlq replay order-processing --message-id msg_abc123 + +# Purge all DLQ messages +agentuity cloud queue dlq purge order-processing +``` + +Programmatically: + +```typescript +import { + listDeadLetterMessages, + replayDeadLetterMessage, + purgeDeadLetter, +} from '@agentuity/server'; + +// List failed messages +const { messages } = await listDeadLetterMessages(client, 'order-processing'); + +for (const msg of messages) { + ctx.logger.warn('Failed message', { + id: msg.id, + reason: msg.failure_reason, + attempts: msg.delivery_attempts, + }); + + // Replay back to the queue + await replayDeadLetterMessage(client, 'order-processing', msg.id); +} +``` + +## HTTP Ingestion Sources + +Create public HTTP endpoints to ingest data into queues from external services: + +```typescript +import { createSource } from '@agentuity/server'; + +const source = await createSource(client, 'webhook-queue', { + name: 'stripe-webhooks', + description: 'Receives Stripe payment events', + auth_type: 'header', + auth_value: 'Bearer whsec_...', +}); + +// External services POST to this URL +console.log(`Webhook URL: ${source.url}`); +``` + +| Auth Type | Description | +|-----------|-------------| +| `none` | No authentication | +| `basic` | HTTP Basic Auth (`username:password`) | +| `header` | Custom header value (`Bearer token`) | + +## Queue Settings + +Configure queue behavior when creating or updating: + +| Setting | Default | Description | +|---------|---------|-------------| +| `default_ttl_seconds` | null | Message expiration (null = never) | +| `default_visibility_timeout_seconds` | 30 | Processing timeout before message returns to queue | +| `default_max_retries` | 5 | Attempts before moving to DLQ | +| `default_retry_backoff_ms` | 1000 | Initial retry delay | +| `default_retry_max_backoff_ms` | 60000 | Maximum retry delay | +| `default_retry_multiplier` | 2.0 | Exponential backoff multiplier | +| `max_in_flight_per_client` | 10 | Concurrent messages per consumer | +| `retention_seconds` | 2592000 | How long to keep acknowledged messages (30 days) | + +## Validation Limits + +| Limit | Value | +|-------|-------| +| Queue name length | 1-256 characters | +| Queue name format | Lowercase letters, digits, underscores, hyphens. Must start with letter or underscore. | +| Payload size | 1 MB max | +| Partition key length | 256 characters max | +| Idempotency key length | 256 characters max | +| Batch size | 1000 messages max | + +## Best Practices + +- **Use idempotency keys** for operations that shouldn't be duplicated (payments, emails) +- **Set appropriate TTLs** for time-sensitive messages +- **Use partition keys** when message ordering matters within a group +- **Monitor DLQ** regularly to catch and fix processing failures +- **Configure webhook retry policies** to handle transient failures gracefully + +## Next Steps + +- [Durable Streams](/Services/Storage/durable-streams): Streaming storage for large exports +- [Key-Value Storage](/Services/Storage/key-value): Fast caching and configuration +- [Background Tasks](/Learn/Cookbook/Patterns/background-tasks): Patterns for async processing +- [Webhook Handler](/Learn/Cookbook/Patterns/webhook-handler): Receiving external webhooks From f2e077fbdb2260aad9f51e8996a090caaff485f8 Mon Sep 17 00:00:00 2001 From: Parteek Singh Date: Wed, 21 Jan 2026 11:54:28 -0600 Subject: [PATCH 3/9] Tweaks --- content/Agents/evaluations.mdx | 4 ++-- content/Reference/CLI/deployment.mdx | 2 +- content/Reference/CLI/development.mdx | 6 ++---- content/Services/Observability/sessions-debugging.mdx | 4 ++-- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/content/Agents/evaluations.mdx b/content/Agents/evaluations.mdx index 97b5c245..6bd53d1a 100644 --- a/content/Agents/evaluations.mdx +++ b/content/Agents/evaluations.mdx @@ -9,9 +9,9 @@ Evaluations (evals) are automated tests that run after your agent completes. The Most evaluation tools test the LLM: did the model respond appropriately? That's fine for chatbots, but agents aren't single LLM calls. They're entire runs with multiple model calls, tool executions, and orchestration working together. -When an agent fails, the problem is often not the model output. It might be a tool call that returned bad data, a state bug that corrupted context, or an orchestration issue that called the wrong agent. Testing just the LLM response misses all of this. +Agent failures can happen anywhere in the run—a tool call that returned bad data, a state bug that corrupted context, and more. Testing just the LLM response misses most of this. -Agentuity evals test the whole run, not just model outputs. And they run on every session in production, not just during development. You catch issues with real traffic, not synthetic test cases. +Agentuity evals test the whole run—every tool call, state change, and orchestration step. They run on every session in production, so you catch issues with real traffic. **The result:** diff --git a/content/Reference/CLI/deployment.mdx b/content/Reference/CLI/deployment.mdx index 81eb437f..2c0da001 100644 --- a/content/Reference/CLI/deployment.mdx +++ b/content/Reference/CLI/deployment.mdx @@ -7,7 +7,7 @@ Deploy your Agentuity project to the cloud with a single command. The platform h ## Why Deploy to Agentuity? -Deploying agents isn't like deploying a web app. Agents need long-running processes, persistent storage, sandbox environments, and observability built in. Setting this up yourself means stitching together containers, databases, secret management, and monitoring. +Deploying agents *should be* as easy as deploying a web app. But agents need long-running processes, persistent storage, sandbox environments, and observability built in. Setting this up yourself means stitching together containers, databases, secret management, and monitoring. Agentuity handles all of this automatically. You push code, and the platform provisions everything your agents need: compute, storage, networking, and observability. No infrastructure configuration, no Docker files, no Kubernetes. diff --git a/content/Reference/CLI/development.mdx b/content/Reference/CLI/development.mdx index fcf92195..7b40e933 100644 --- a/content/Reference/CLI/development.mdx +++ b/content/Reference/CLI/development.mdx @@ -158,13 +158,11 @@ Agentuity's Gravity network handles this automatically. When you run `agentuity **This means:** -- **Instant HTTPS URLs**: Public URLs with automatic certificate generation -- **No configuration**: Works out of the box, no firewall rules or port forwarding +- **Instant HTTPS URLs**: Automatic certificate generation +- **Zero setup**: Works out of the box, no firewall rules or port forwarding - **Secure tunneling**: Encrypted connections through Agentuity's edge network - **Automatic reconnection**: Handles network interruptions gracefully -The `--public` flag creates a secure tunnel through Agentuity's edge network, giving your local server a public HTTPS URL for testing webhooks, sharing with teammates, or external integrations. - **Example output:** ``` ⨺ Agentuity DevMode diff --git a/content/Services/Observability/sessions-debugging.mdx b/content/Services/Observability/sessions-debugging.mdx index 084744ee..fa89dd1a 100644 --- a/content/Services/Observability/sessions-debugging.mdx +++ b/content/Services/Observability/sessions-debugging.mdx @@ -7,11 +7,11 @@ Every request to your agents gets a unique session ID (`sess_...`). Sessions lin ## Why Sessions? -Traditional observability tracks individual HTTP requests. That works for stateless web servers, but agents aren't single requests. A single conversation might span dozens of LLM calls, tool executions, and orchestration steps across multiple interactions. +Traditional observability tracks individual HTTP requests. That works for stateless web servers. Agents go beyond single requests—a conversation might span dozens of LLM calls, tool executions, and orchestration steps across multiple interactions. Without sessions, debugging becomes guesswork. You see individual logs but can't connect them. Was that error from the same conversation? What happened before it failed? Which user was affected? -Agentuity tracks all of this automatically. Every run ties to a session. Every conversation thread is preserved. You get full context into what happened, when, and why, without writing tracking code. +Agentuity tracks all of this automatically. Every run ties to a session. Every conversation thread is preserved. You get full context into what happened, when, and why—*without* writing tracking code. **In practice:** From 0683aa9f7e745624a29936156d219c2353aedc0c Mon Sep 17 00:00:00 2001 From: Parteek Singh Date: Wed, 21 Jan 2026 14:32:30 -0600 Subject: [PATCH 4/9] Tweaks --- content/Agents/evaluations.mdx | 2 +- .../Services/Observability/sessions-debugging.mdx | 2 +- content/Services/Storage/durable-streams.mdx | 4 ++-- content/Services/queues.mdx | 12 +++++++----- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/content/Agents/evaluations.mdx b/content/Agents/evaluations.mdx index 6bd53d1a..e8c6267b 100644 --- a/content/Agents/evaluations.mdx +++ b/content/Agents/evaluations.mdx @@ -16,7 +16,7 @@ Agentuity evals test the whole run—every tool call, state change, and orchestr **The result:** - **Full-run evaluation**: Test the entire agent execution, not just LLM responses -- **Production monitoring**: Evals run on every session automatically, catching issues with real users +- **Production monitoring**: Once configured, evals run automatically on every session - **Async by default**: Evals don't block responses, so users aren't waiting - **Preset library**: Common checks (PII, safety, hallucination) available out of the box diff --git a/content/Services/Observability/sessions-debugging.mdx b/content/Services/Observability/sessions-debugging.mdx index fa89dd1a..7d118ec1 100644 --- a/content/Services/Observability/sessions-debugging.mdx +++ b/content/Services/Observability/sessions-debugging.mdx @@ -18,7 +18,7 @@ Agentuity tracks all of this automatically. Every run ties to a session. Every c - **Unified tracing**: All logs, spans, and state from a single request are linked by session ID - **Conversation context**: Sessions group into threads, so you can trace multi-turn conversations - **Automatic correlation**: No manual tracking code, every call in a session is connected -- **Time-travel debugging**: Replay what happened in a session to reproduce issues +- **Session inspection**: Review what happened in a session to reproduce issues ## Sessions vs Threads diff --git a/content/Services/Storage/durable-streams.mdx b/content/Services/Storage/durable-streams.mdx index 2555b5f7..29c8caf1 100644 --- a/content/Services/Storage/durable-streams.mdx +++ b/content/Services/Storage/durable-streams.mdx @@ -3,7 +3,7 @@ title: Durable Streams description: Streaming storage for large exports, audit logs, and real-time data --- -Durable streams provide streaming storage for large data exports, audit logs, and real-time processing. Streams follow a **write-once, read-many** pattern: once data is written and the stream is closed, the content is immutable and can be accessed via a permanent public URL. +Durable streams provide streaming storage for large data exports, audit logs, and real-time processing. Streams follow a **write-once, read-many** pattern: once data is written and the stream is closed, the content is immutable and accessible via URL until deleted. ## Why Durable Streams? @@ -18,7 +18,7 @@ Durable streams solve this by decoupling the data from the connection. Instead o - **Refresh-safe**: If someone refreshes the page mid-stream, the URL still works and the content is preserved. The expensive work you already did isn't wasted. - **Background processing**: Return the stream URL immediately and write data asynchronously with `ctx.waitUntil()`. Your handler responds fast while the stream continues writing. - **Shareable URLs**: A stream is just a URL. You can share it, open it on another device, or have multiple viewers access the same output. -- **Permanent artifacts**: Once closed, streams are immutable and accessible via their public URL. Useful for audit logs, exports, and generated reports. +- **Durable artifacts**: Once closed, streams are immutable and remain accessible via their public URL. Useful for audit logs, exports, and generated reports. ## When to Use Durable Streams diff --git a/content/Services/queues.mdx b/content/Services/queues.mdx index 2f64419a..a7dbce4f 100644 --- a/content/Services/queues.mdx +++ b/content/Services/queues.mdx @@ -235,11 +235,11 @@ agentuity cloud queue dlq purge order-processing Programmatically: ```typescript -import { - listDeadLetterMessages, - replayDeadLetterMessage, - purgeDeadLetter, -} from '@agentuity/server'; +import { createApp, createAgentContext } from '@agentuity/runtime'; +import { listDeadLetterMessages, replayDeadLetterMessage } from '@agentuity/server'; + +await createApp(); +const ctx = createAgentContext(); // List failed messages const { messages } = await listDeadLetterMessages(client, 'order-processing'); @@ -256,6 +256,8 @@ for (const msg of messages) { } ``` +This example uses `createAgentContext()` for logging and tracing. See [Running Agents Without HTTP](/Agents/standalone-execution) for more on standalone execution. + ## HTTP Ingestion Sources Create public HTTP endpoints to ingest data into queues from external services: From dc25f10b028819b0e654f9fe11b9c92b3331ce06 Mon Sep 17 00:00:00 2001 From: Parteek Singh Date: Wed, 21 Jan 2026 16:38:53 -0600 Subject: [PATCH 5/9] Tweaks --- .../Cookbook/Patterns/server-utilities.mdx | 146 +++++++++++++++++- content/Services/queues.mdx | 115 ++------------ package-lock.json | 33 +++- 3 files changed, 184 insertions(+), 110 deletions(-) diff --git a/content/Learn/Cookbook/Patterns/server-utilities.mdx b/content/Learn/Cookbook/Patterns/server-utilities.mdx index 90480c73..983f3577 100644 --- a/content/Learn/Cookbook/Patterns/server-utilities.mdx +++ b/content/Learn/Cookbook/Patterns/server-utilities.mdx @@ -1,6 +1,6 @@ --- title: SDK Utilities for External Apps -description: Use storage, logging, error handling, and schema utilities from external backends like Next.js or Express +description: Use storage, queues, logging, and error handling utilities from external backends like Next.js or Express --- Use `@agentuity/server` and `@agentuity/core` utilities in external apps, scripts, or backends that integrate with Agentuity. @@ -122,6 +122,146 @@ export async function GET(request: NextRequest) { } ``` +## Queue Management + +Manage queues programmatically from external apps or scripts using `APIClient`: + +```typescript title="lib/agentuity-queues.ts" +import { APIClient, createLogger, getServiceUrls } from '@agentuity/server'; + +export const logger = createLogger('info'); +const urls = getServiceUrls(process.env.AGENTUITY_REGION!); + +export const client = new APIClient( + urls.catalyst, + logger, + process.env.AGENTUITY_SDK_KEY +); +``` + +### Creating and Managing Queues + +```typescript +import { + createQueue, + listQueues, + deleteQueue, + pauseQueue, + resumeQueue, +} from '@agentuity/server'; +import { client } from '@/lib/agentuity-queues'; + +// Create a worker queue +const queue = await createQueue(client, { + name: 'order-processing', + queue_type: 'worker', + settings: { + default_max_retries: 5, + default_visibility_timeout_seconds: 60, + }, +}); + +// List all queues +const { queues } = await listQueues(client); + +// Pause and resume +await pauseQueue(client, 'order-processing'); +await resumeQueue(client, 'order-processing'); + +// Delete a queue +await deleteQueue(client, 'old-queue'); +``` + +### Dead Letter Queue Operations + +```typescript +import { + listDeadLetterMessages, + replayDeadLetterMessage, + purgeDeadLetter, +} from '@agentuity/server'; +import { client, logger } from '@/lib/agentuity-queues'; + +// List failed messages +const { messages } = await listDeadLetterMessages(client, 'order-processing'); + +for (const msg of messages) { + logger.warn('Failed message', { id: msg.id, reason: msg.failure_reason }); + + // Replay back to the queue + await replayDeadLetterMessage(client, 'order-processing', msg.id); +} + +// Purge all DLQ messages +await purgeDeadLetter(client, 'order-processing'); +``` + +### Webhook Destinations + +```typescript +import { createDestination } from '@agentuity/server'; +import { client } from '@/lib/agentuity-queues'; + +await createDestination(client, 'order-processing', { + destination_type: 'http', + config: { + url: 'https://api.example.com/webhook/orders', + method: 'POST', + headers: { 'X-API-Key': 'secret' }, + timeout_ms: 30000, + retry_policy: { + max_attempts: 5, + initial_backoff_ms: 1000, + max_backoff_ms: 60000, + backoff_multiplier: 2.0, + }, + }, +}); +``` + +### HTTP Ingestion Sources + +```typescript +import { createSource } from '@agentuity/server'; +import { client, logger } from '@/lib/agentuity-queues'; + +const source = await createSource(client, 'webhook-queue', { + name: 'stripe-webhooks', + description: 'Receives Stripe payment events', + auth_type: 'header', + auth_value: 'Bearer whsec_...', +}); + +// External services POST to this URL +logger.info('Source created', { url: source.url }); +``` + +### Pull-Based Consumption + +For workers that pull and acknowledge messages: + +```typescript +import { receiveMessage, ackMessage, nackMessage } from '@agentuity/server'; +import { client } from '@/lib/agentuity-queues'; + +// Receive a message (blocks until available or timeout) +const message = await receiveMessage(client, 'order-processing'); + +if (message) { + try { + await processOrder(message.payload); + await ackMessage(client, 'order-processing', message.id); + } catch (error) { + // Message returns to queue for retry + await nackMessage(client, 'order-processing', message.id); + } +} +``` + + +For one-off queue management, use the CLI instead: `agentuity cloud queue create`, `agentuity cloud queue dlq`, etc. See [Queues](/Services/queues) for CLI commands. + + ## Alternative: HTTP Routes If you want to centralize storage logic in your Agentuity project (for [middleware](/Routes/middleware), sharing across multiple apps, or avoiding SDK key distribution), use [HTTP routes](/Routes/http) instead. @@ -182,7 +322,8 @@ export default router; Add authentication middleware to protect storage endpoints: ```typescript title="src/api/sessions/route.ts" -import { createRouter, createMiddleware } from '@agentuity/runtime'; +import { createRouter } from '@agentuity/runtime'; +import { createMiddleware } from 'hono/factory'; const router = createRouter(); @@ -330,6 +471,7 @@ const jsonSchema = toJSONSchema(schema); ## See Also +- [Queues](/Services/queues): Queue concepts and CLI commands - [HTTP Routes](/Routes/http): Route creation with `createRouter` - [Route Middleware](/Routes/middleware): Authentication patterns - [RPC Client](/Frontend/rpc-client): Typed client generation diff --git a/content/Services/queues.mdx b/content/Services/queues.mdx index a7dbce4f..20f6ba2d 100644 --- a/content/Services/queues.mdx +++ b/content/Services/queues.mdx @@ -94,7 +94,7 @@ interface QueuePublishResult { ## Error Handling ```typescript -import { QueueNotFoundError, QueueValidationError } from '@agentuity/runtime'; +import { QueueNotFoundError, QueueValidationError } from '@agentuity/core'; const agent = createAgent('SafePublisher', { handler: async (ctx, input) => { @@ -143,79 +143,22 @@ agentuity cloud queue resume order-processing agentuity cloud queue delete order-processing ``` -### Programmatic Management - -Use `@agentuity/server` for full queue management in routes or standalone scripts: - -```typescript -import { createQueue, listQueues, deleteQueue } from '@agentuity/server'; - -// Create a queue -const queue = await createQueue(client, { - name: 'order-processing', - queue_type: 'worker', - settings: { - default_max_retries: 5, - default_visibility_timeout_seconds: 60, - }, -}); - -// List queues -const { queues } = await listQueues(client); - -// Delete a queue -await deleteQueue(client, 'old-queue'); -``` +For programmatic queue management, see [SDK Utilities for External Apps](/Learn/Cookbook/Patterns/server-utilities#queue-management). ## Consuming Messages ### Webhook Destinations -Configure a webhook destination to automatically deliver messages to an HTTP endpoint: +Configure webhook destinations to automatically deliver messages to HTTP endpoints. Set these up in the [App](https://app-v1.agentuity.com) or [programmatically](/Learn/Cookbook/Patterns/server-utilities#webhook-destinations). -```typescript -import { createDestination } from '@agentuity/server'; - -await createDestination(client, 'order-processing', { - destination_type: 'http', - config: { - url: 'https://api.example.com/webhook/orders', - method: 'POST', - headers: { 'X-API-Key': 'secret' }, - timeout_ms: 30000, - retry_policy: { - max_attempts: 5, - initial_backoff_ms: 1000, - max_backoff_ms: 60000, - backoff_multiplier: 2.0, - }, - }, -}); -``` +Webhook destinations support: +- Custom headers and authentication +- Configurable timeouts (up to 30 seconds) +- Retry policies with exponential backoff ### Pull-Based Consumption -For workers that pull messages: - -```typescript -import { receiveMessage, ackMessage, nackMessage } from '@agentuity/server'; - -// Receive a message (blocks until available or timeout) -const message = await receiveMessage(client, 'order-processing'); - -if (message) { - try { - // Process the message - await processOrder(message.payload); - - // Acknowledge success - await ackMessage(client, 'order-processing', message.id); - } catch (error) { - // Negative acknowledge - message returns to queue for retry - await nackMessage(client, 'order-processing', message.id); - } -} -``` +For workers that pull and process messages, see [Pull-Based Consumption](/Learn/Cookbook/Patterns/server-utilities#pull-based-consumption). This pattern is useful for long-running workers that need fine-grained control over message processing. ## Dead Letter Queue @@ -232,49 +175,11 @@ agentuity cloud queue dlq replay order-processing --message-id msg_abc123 agentuity cloud queue dlq purge order-processing ``` -Programmatically: - -```typescript -import { createApp, createAgentContext } from '@agentuity/runtime'; -import { listDeadLetterMessages, replayDeadLetterMessage } from '@agentuity/server'; - -await createApp(); -const ctx = createAgentContext(); - -// List failed messages -const { messages } = await listDeadLetterMessages(client, 'order-processing'); - -for (const msg of messages) { - ctx.logger.warn('Failed message', { - id: msg.id, - reason: msg.failure_reason, - attempts: msg.delivery_attempts, - }); - - // Replay back to the queue - await replayDeadLetterMessage(client, 'order-processing', msg.id); -} -``` - -This example uses `createAgentContext()` for logging and tracing. See [Running Agents Without HTTP](/Agents/standalone-execution) for more on standalone execution. +For programmatic DLQ access, see [Dead Letter Queue Operations](/Learn/Cookbook/Patterns/server-utilities#dead-letter-queue-operations). ## HTTP Ingestion Sources -Create public HTTP endpoints to ingest data into queues from external services: - -```typescript -import { createSource } from '@agentuity/server'; - -const source = await createSource(client, 'webhook-queue', { - name: 'stripe-webhooks', - description: 'Receives Stripe payment events', - auth_type: 'header', - auth_value: 'Bearer whsec_...', -}); - -// External services POST to this URL -console.log(`Webhook URL: ${source.url}`); -``` +Create public HTTP endpoints to ingest data into queues from external services. Configure these in the [App](https://app-v1.agentuity.com) or [programmatically](/Learn/Cookbook/Patterns/server-utilities#http-ingestion-sources). | Auth Type | Description | |-----------|-------------| diff --git a/package-lock.json b/package-lock.json index 11105c1e..51cf7968 100644 --- a/package-lock.json +++ b/package-lock.json @@ -213,6 +213,7 @@ "resolved": "https://registry.npmjs.org/@agentuity/react/-/react-0.1.20.tgz", "integrity": "sha512-2Ae9T3zZu8hWkj/RCGUs5EkLwJPGeAyVQiZw3sd3ZgunapDsAhrHuYS8clWsthxWsxxdwmJe1HxdQph17Um4Fw==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@agentuity/core": "0.1.20", "@agentuity/frontend": "0.1.20" @@ -8904,6 +8905,7 @@ "version": "1.4.15", "resolved": "https://registry.npmjs.org/@better-auth/core/-/core-1.4.15.tgz", "integrity": "sha512-uAvq8YA7SaS7v+TrvH/Kwt7LAJihzUqB3FX8VweDsqu3gn5t51M+Bve+V1vVWR9qBAtC6cN68V6b+scxZxDY4A==", + "peer": true, "dependencies": { "@standard-schema/spec": "^1.0.0", "zod": "^4.1.12" @@ -8933,12 +8935,14 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/@better-auth/utils/-/utils-0.3.0.tgz", "integrity": "sha512-W+Adw6ZA6mgvnSnhOki270rwJ42t4XzSK6YWGF//BbVXL6SwCLWfyzBc1lN2m/4RM28KubdBKQ4X5VMoLRNPQw==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@better-fetch/fetch": { "version": "1.1.21", "resolved": "https://registry.npmjs.org/@better-fetch/fetch/-/fetch-1.1.21.tgz", - "integrity": "sha512-/ImESw0sskqlVR94jB+5+Pxjf+xBwDZF/N5+y2/q4EqD7IARUTSpPfIo8uf39SYpCxyOCtbyYpUrZ3F/k0zT4A==" + "integrity": "sha512-/ImESw0sskqlVR94jB+5+Pxjf+xBwDZF/N5+y2/q4EqD7IARUTSpPfIo8uf39SYpCxyOCtbyYpUrZ3F/k0zT4A==", + "peer": true }, "node_modules/@biomejs/biome": { "version": "2.1.2", @@ -10633,6 +10637,7 @@ "integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": "^14.21.3 || >=16" }, @@ -14018,6 +14023,7 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.8.tgz", "integrity": "sha512-3MbSL37jEchWZz2p2mjntRZtPt837ij10ApxKfgmXCTuHWagYg7iA5bqPw6C8BMPfwidlvfPI/fxOc42HLhcyg==", "license": "MIT", + "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -14028,6 +14034,7 @@ "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "devOptional": true, "license": "MIT", + "peer": true, "peerDependencies": { "@types/react": "^19.2.0" } @@ -14095,6 +14102,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -14241,6 +14249,7 @@ "resolved": "https://registry.npmjs.org/better-call/-/better-call-1.1.8.tgz", "integrity": "sha512-XMQ2rs6FNXasGNfMjzbyroSwKwYbZ/T3IxruSS6U2MJRsSYh3wYtG3o6H00ZlKZ/C/UPOAD97tqgQJNsxyeTXw==", "license": "MIT", + "peer": true, "dependencies": { "@better-auth/utils": "^0.3.0", "@better-fetch/fetch": "^1.1.4", @@ -14865,6 +14874,7 @@ "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.33.1.tgz", "integrity": "sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10" } @@ -15286,6 +15296,7 @@ "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", "license": "ISC", + "peer": true, "engines": { "node": ">=12" } @@ -15516,6 +15527,7 @@ "resolved": "https://registry.npmjs.org/drizzle-orm/-/drizzle-orm-0.45.1.tgz", "integrity": "sha512-Te0FOdKIistGNPMq2jscdqngBRfBpC8uMFVwqjf6gtTVJHIQ/dosgV/CLBU2N4ZJBsXL5savCba9b0YJskKdcA==", "license": "Apache-2.0", + "peer": true, "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", "@cloudflare/workers-types": ">=4", @@ -16405,6 +16417,7 @@ "resolved": "https://registry.npmjs.org/fumadocs-core/-/fumadocs-core-15.2.6.tgz", "integrity": "sha512-5+Bq8iQGXAQ5K5igw612rzGPdup1bnROWa3F0UbdECsSiSQvNkHLlCL0Hyptvll1NV4zx6YFMf8vQRt1aSt4vA==", "license": "MIT", + "peer": true, "dependencies": { "@formatjs/intl-localematcher": "^0.6.1", "@orama/orama": "^3.1.4", @@ -16519,6 +16532,7 @@ "resolved": "https://registry.npmjs.org/fumadocs-ui/-/fumadocs-ui-15.8.5.tgz", "integrity": "sha512-9pyB+9rOOsrFnmmZ9xREp/OgVhyaSq2ocEpqTNbeQ7tlJ6JWbdFWfW0C9lRXprQEB6DJWUDtDxqKS5QXLH0EGA==", "license": "MIT", + "peer": true, "dependencies": { "@radix-ui/react-accordion": "^1.2.12", "@radix-ui/react-collapsible": "^1.1.12", @@ -17298,6 +17312,7 @@ "resolved": "https://registry.npmjs.org/jose/-/jose-6.1.3.tgz", "integrity": "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==", "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/panva" } @@ -17369,6 +17384,7 @@ "resolved": "https://registry.npmjs.org/kysely/-/kysely-0.28.10.tgz", "integrity": "sha512-ksNxfzIW77OcZ+QWSAPC7yDqUSaIVwkTWnTPNiIy//vifNbwsSgQ57OkkncHxxpcBHM3LRfLAZVEh7kjq5twVA==", "license": "MIT", + "peer": true, "engines": { "node": ">=20.0.0" } @@ -17704,6 +17720,7 @@ "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.525.0.tgz", "integrity": "sha512-Tm1txJ2OkymCGkvwoHt33Y2JpN5xucVq1slHcgE6Lk0WjDfjgKWor5CdVER8U6DvcfMwh4M8XxmpTiyzfmfDYQ==", "license": "ISC", + "peer": true, "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } @@ -19064,6 +19081,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": "^20.0.0 || >=22.0.0" } @@ -19082,6 +19100,7 @@ "resolved": "https://registry.npmjs.org/next/-/next-15.3.8.tgz", "integrity": "sha512-L+4c5Hlr84fuaNADZbB9+ceRX9/CzwxJ+obXIGHupboB/Q1OLbSUapFs4bO8hnS/E6zV/JDX7sG1QpKVR2bguA==", "license": "MIT", + "peer": true, "dependencies": { "@next/env": "15.3.8", "@swc/counter": "0.1.3", @@ -19427,6 +19446,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -19594,6 +19614,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -19603,6 +19624,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz", "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==", "license": "MIT", + "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -20604,7 +20626,8 @@ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz", "integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==", "devOptional": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/tapable": { "version": "2.3.0", @@ -20765,6 +20788,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -20802,6 +20826,7 @@ "integrity": "sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "pathe": "^2.0.3" } @@ -21147,6 +21172,7 @@ "dev": true, "hasInstallScript": true, "license": "Apache-2.0", + "peer": true, "bin": { "workerd": "bin/workerd" }, @@ -21167,6 +21193,7 @@ "integrity": "sha512-Z4xn6jFZTaugcOKz42xvRAYKgkVUERHVbuCJ5+f+gK+R6k12L02unakPGOA0L0ejhUl16dqDjKe4tmL9sedHcw==", "dev": true, "license": "MIT OR Apache-2.0", + "peer": true, "dependencies": { "@cloudflare/kv-asset-handler": "0.4.2", "@cloudflare/unenv-preset": "2.10.0", From 7a08bef3f2ddfac91c396fbfbb11415472bd05ea Mon Sep 17 00:00:00 2001 From: Parteek Singh Date: Thu, 22 Jan 2026 09:06:04 -0600 Subject: [PATCH 6/9] Tweaks --- content/Agents/creating-agents.mdx | 10 ++++++++- content/Agents/streaming-responses.mdx | 4 ++++ content/Routes/sse.mdx | 29 ++++++++++++++++++++++++++ content/Services/Sandbox/sdk-usage.mdx | 6 ++++++ content/Services/Storage/vector.mdx | 6 ++++++ 5 files changed, 54 insertions(+), 1 deletion(-) diff --git a/content/Agents/creating-agents.mdx b/content/Agents/creating-agents.mdx index c6ad7334..3534af31 100644 --- a/content/Agents/creating-agents.mdx +++ b/content/Agents/creating-agents.mdx @@ -197,7 +197,15 @@ All implement [StandardSchema](https://github.com/standard-schema/standard-schem ### Type Inference -TypeScript automatically infers types from your schemas: +TypeScript automatically infers types from your schemas. Don't add explicit type annotations to handler parameters: + +```typescript +// Good: types inferred from schema +handler: async (ctx, input) => { ... } + +// Bad: explicit types can cause issues +handler: async (ctx: AgentContext, input: MyInput) => { ... } +``` ```typescript const agent = createAgent('Search', { diff --git a/content/Agents/streaming-responses.mdx b/content/Agents/streaming-responses.mdx index 9edc0b70..6c041339 100644 --- a/content/Agents/streaming-responses.mdx +++ b/content/Agents/streaming-responses.mdx @@ -50,6 +50,10 @@ Streaming requires both: `schema.stream: true` in your agent (so the handler ret Enable streaming by setting `stream: true` in your schema and returning a `textStream`: + +The `textStream` from AI SDK's `streamText()` works directly with Agentuity's streaming middleware. Return it from your handler without additional processing. + + ```typescript import { createAgent } from '@agentuity/runtime'; import { streamText } from 'ai'; diff --git a/content/Routes/sse.mdx b/content/Routes/sse.mdx index 2841c1a7..8f78943a 100644 --- a/content/Routes/sse.mdx +++ b/content/Routes/sse.mdx @@ -229,6 +229,35 @@ Or with cURL: curl -N https://your-project.agentuity.cloud/agent-name ``` +## Streaming LLM Responses + +Use SSE to stream AI SDK responses to clients: + +```typescript +import { createRouter, sse } from '@agentuity/runtime'; +import { streamText } from 'ai'; +import { anthropic } from '@ai-sdk/anthropic'; + +const router = createRouter(); + +router.post('/chat', sse(async (c, stream) => { + const { message } = await c.req.json(); + + const result = streamText({ + model: anthropic('claude-sonnet-4-5'), + prompt: message, + }); + + for await (const chunk of result.textStream) { + await stream.write(chunk); + } + + stream.close(); +})); + +export default router; +``` + ## SSE vs WebSocket | Aspect | SSE | WebSocket | diff --git a/content/Services/Sandbox/sdk-usage.mdx b/content/Services/Sandbox/sdk-usage.mdx index 18589211..20889d2f 100644 --- a/content/Services/Sandbox/sdk-usage.mdx +++ b/content/Services/Sandbox/sdk-usage.mdx @@ -423,6 +423,9 @@ Returned by `sandbox.execute()`: | `durationMs` | `number` | Execution duration in milliseconds | | `stdoutStreamUrl` | `string` | URL to fetch stdout stream | | `stderrStreamUrl` | `string` | URL to fetch stderr stream | +| `cpuTimeMs` | `number` | CPU time consumed in milliseconds | +| `memoryByteSec` | `number` | Memory usage in byte-seconds | +| `networkEgressBytes` | `number` | Outbound network traffic in bytes | ### SandboxRunResult @@ -433,6 +436,9 @@ Returned by `sandbox.execute()`: | `durationMs` | `number` | Execution duration | | `stdout` | `string` | Captured stdout (if available) | | `stderr` | `string` | Captured stderr (if available) | +| `cpuTimeMs` | `number` | CPU time consumed in milliseconds | +| `memoryByteSec` | `number` | Memory usage in byte-seconds | +| `networkEgressBytes` | `number` | Outbound network traffic in bytes | ### SandboxInfo diff --git a/content/Services/Storage/vector.mdx b/content/Services/Storage/vector.mdx index 97ebb43e..abb87374 100644 --- a/content/Services/Storage/vector.mdx +++ b/content/Services/Storage/vector.mdx @@ -143,6 +143,8 @@ const agent = createAgent('BatchRetriever', { ### exists() - Check Namespace +Check if a namespace contains any vectors: + ```typescript const hasData = await ctx.vector.exists('knowledge-base'); if (!hasData) { @@ -150,6 +152,10 @@ if (!hasData) { } ``` + +`exists()` returns `false` for namespaces that exist but contain no vectors. Use this to verify your knowledge base has been populated with data before searching. + + ## Deleting Vectors ```typescript From 11dfa20bf482803a9ee5690b57491df4a28784f0 Mon Sep 17 00:00:00 2001 From: Parteek Singh Date: Fri, 30 Jan 2026 17:35:26 -0800 Subject: [PATCH 7/9] Updates --- content/Agents/creating-agents.mdx | 4 + content/Agents/evaluations.mdx | 31 ++ content/Agents/schema-libraries.mdx | 23 ++ content/Get-Started/quickstart.mdx | 8 + .../Cookbook/Patterns/llm-as-a-judge.mdx | 368 ++++++++++++++++++ content/Learn/Cookbook/Patterns/meta.json | 1 + content/Learn/Cookbook/overview.mdx | 1 + content/Reference/CLI/ai-commands.mdx | 72 ++-- content/Reference/CLI/debugging.mdx | 64 ++- content/Reference/CLI/deployment.mdx | 85 ++-- content/Reference/CLI/development.mdx | 79 ++-- content/Reference/CLI/getting-started.mdx | 1 + content/Reference/CLI/meta.json | 3 +- content/Reference/CLI/opencode-plugin.mdx | 270 +++++++++++++ content/Services/Storage/database.mdx | 133 +++++++ content/Services/Storage/durable-streams.mdx | 35 +- content/Services/Storage/key-value.mdx | 35 +- content/Services/Storage/meta.json | 2 +- content/Services/Storage/queue.mdx | 171 ++++++++ content/Services/Storage/vector.mdx | 26 +- 20 files changed, 1291 insertions(+), 121 deletions(-) create mode 100644 content/Learn/Cookbook/Patterns/llm-as-a-judge.mdx create mode 100644 content/Reference/CLI/opencode-plugin.mdx create mode 100644 content/Services/Storage/queue.mdx diff --git a/content/Agents/creating-agents.mdx b/content/Agents/creating-agents.mdx index 3534af31..0c0e570e 100644 --- a/content/Agents/creating-agents.mdx +++ b/content/Agents/creating-agents.mdx @@ -372,6 +372,10 @@ handler: async (ctx, input) => { ## Next Steps + +The [OpenCode plugin](/Reference/CLI/opencode-plugin) provides AI-assisted development for full-stack Agentuity projects, including agents, routes, frontend, and deployment. + + - [Using the AI SDK](/Agents/ai-sdk-integration): Add LLM capabilities with generateText and streamText - [Managing State](/Agents/state-management): Persist data across requests with thread and session state - [Calling Other Agents](/Agents/calling-other-agents): Build multi-agent workflows diff --git a/content/Agents/evaluations.mdx b/content/Agents/evaluations.mdx index e8c6267b..c9f56323 100644 --- a/content/Agents/evaluations.mdx +++ b/content/Agents/evaluations.mdx @@ -441,6 +441,37 @@ export const politenessCheck = agent.createEval(politeness({ All preset evals use a default model optimized for cost and speed. Override `model` when you need specific capabilities. +### Lifecycle Hooks + +Preset evals support `onStart` and `onComplete` hooks for custom logic around eval execution: + +```typescript +import { politeness } from '@agentuity/evals'; + +export const politenessCheck = agent.createEval(politeness({ + onStart: async (ctx, input, output) => { + ctx.logger.info('Starting politeness eval', { + inputLength: input.request?.length, + }); + }, + onComplete: async (ctx, result) => { + // Track results in external monitoring + if (!result.passed) { + ctx.logger.warn('Politeness check failed', { + score: result.score, + reason: result.reason, + }); + } + }, +})); +``` + +**Use cases for lifecycle hooks:** +- Log eval execution for debugging +- Send results to external monitoring systems +- Track eval performance metrics +- Trigger alerts on failures + ### Schema Middleware Preset evals expect a standard input/output format: diff --git a/content/Agents/schema-libraries.mdx b/content/Agents/schema-libraries.mdx index d8e4a207..b34d45c2 100644 --- a/content/Agents/schema-libraries.mdx +++ b/content/Agents/schema-libraries.mdx @@ -103,6 +103,29 @@ type User = s.infer; // { name: string; age: number; role: 'admin' | 'user' } ``` +### JSON Schema Generation + +Convert schemas to JSON Schema for use with LLM structured output: + +```typescript +import { s } from '@agentuity/schema'; + +const ResponseSchema = s.object({ + answer: s.string(), + confidence: s.number(), +}); + +// Generate JSON Schema +const jsonSchema = s.toJSONSchema(ResponseSchema); + +// Generate strict JSON Schema for LLM structured output +const strictSchema = s.toJSONSchema(ResponseSchema, { strict: true }); +``` + + +Use `{ strict: true }` when generating schemas for LLM structured output (e.g., OpenAI's `response_format`). Strict mode ensures the schema is compatible with model constraints and produces more reliable outputs. + + Use `@agentuity/schema` for simple validation needs. For advanced features like email validation, string length constraints, or complex transformations, consider Zod or Valibot. diff --git a/content/Get-Started/quickstart.mdx b/content/Get-Started/quickstart.mdx index f9854aa4..576d2218 100644 --- a/content/Get-Started/quickstart.mdx +++ b/content/Get-Started/quickstart.mdx @@ -212,6 +212,14 @@ After your first deployment, the App populates with: ## What's Next? + +Install the [OpenCode plugin](/Reference/CLI/opencode-plugin) for AI-assisted agent development. Get help writing agents, debugging, and deploying directly from your editor: + +```bash +agentuity ai opencode install +``` + + **Learn the concepts:** - [Understanding How Agents Work](/Learn/Cookbook/Tutorials/understanding-agents): Tools, loops, and autonomous behavior diff --git a/content/Learn/Cookbook/Patterns/llm-as-a-judge.mdx b/content/Learn/Cookbook/Patterns/llm-as-a-judge.mdx new file mode 100644 index 00000000..69a8b2dd --- /dev/null +++ b/content/Learn/Cookbook/Patterns/llm-as-a-judge.mdx @@ -0,0 +1,368 @@ +--- +title: LLM as a Judge +description: Use LLMs to evaluate and score agent outputs for quality, safety, and compliance +--- + +The LLM-as-judge pattern uses one model to evaluate another model's output. This is useful for subjective quality assessments that can't be checked programmatically. + +## When to Use This Pattern + +| Use Case | Good Fit? | +|----------|-----------| +| Checking factual accuracy against sources | Yes | +| Evaluating tone and politeness | Yes | +| Detecting hallucinations in RAG | Yes | +| Comparing response quality | Yes | +| Validating JSON structure | No (use schema validation) | +| Checking string length | No (use code) | + +Use LLM-as-judge when the evaluation requires understanding context, nuance, or subjective criteria. + +## Inline vs Evals + +LLM-as-judge can be used in two contexts: + +| Context | When to Use | Blocks Response? | +|---------|-------------|------------------| +| **Inline** (in handler) | Scores needed in UI, model comparison, user-facing quality metrics | Yes | +| **Evals** (in eval.ts) | Background quality monitoring, compliance checks, production observability | No | + +**Inline example**: A model arena that shows users which response "won" needs scores returned with the response. + +**Eval example**: Checking all responses for PII or hallucinations in production without slowing down users. + + +Agentuity has a built-in [evaluation system](/Agents/evaluations) that runs LLM-as-judge checks asynchronously after each response. Use evals for production monitoring without impacting response times. + + +## Basic Pattern + +Use a fast model to judge outputs. Groq provides low-latency inference for judge calls: + +```typescript +import { createAgent } from '@agentuity/runtime'; +import { generateText, generateObject } from 'ai'; +import { openai } from '@ai-sdk/openai'; +import { groq } from '@ai-sdk/groq'; +import { z } from 'zod'; + +const JudgmentSchema = z.object({ + score: z.number().min(0).max(1).describe('Quality score from 0 to 1'), + passed: z.boolean().describe('Whether the response meets quality threshold'), + reason: z.string().describe('Brief explanation of the judgment'), +}); + +const agent = createAgent('QualityChecker', { + schema: { + input: z.object({ question: z.string() }), + output: z.object({ + answer: z.string(), + judgment: JudgmentSchema, + }), + }, + handler: async (ctx, input) => { + // Generate answer with primary model + const { text: answer } = await generateText({ + model: openai('gpt-5-mini'), + prompt: input.question, + }); + + // Judge with fast model + const { object: judgment } = await generateObject({ + model: groq('openai/gpt-oss-120b'), + schema: JudgmentSchema, + prompt: `Evaluate this response on a 0-1 scale. + +Question: ${input.question} +Response: ${answer} + +Consider: +- Does it directly answer the question? +- Is the information accurate? +- Is it clear and easy to understand? + +Score 0.7+ to pass.`, + }); + + return { answer, judgment }; + }, +}); + +export default agent; +``` + +## Model Comparison Arena + +Compare responses from multiple providers and declare a winner. This pattern is useful for benchmarking, A/B testing, or letting users see quality differences: + +```typescript +import { createAgent } from '@agentuity/runtime'; +import { generateText, generateObject } from 'ai'; +import { openai } from '@ai-sdk/openai'; +import { anthropic } from '@ai-sdk/anthropic'; +import { groq } from '@ai-sdk/groq'; +import { z } from 'zod'; + +const ArenaJudgment = z.object({ + winner: z.enum(['openai', 'anthropic']), + reasoning: z.string(), + scores: z.object({ + openai: z.number().min(0).max(1), + anthropic: z.number().min(0).max(1), + }), +}); + +const arenaAgent = createAgent('ModelArena', { + schema: { + input: z.object({ + prompt: z.string(), + tone: z.enum(['whimsical', 'suspenseful', 'comedic']), + }), + output: z.object({ + results: z.array(z.object({ + provider: z.string(), + story: z.string(), + generationMs: z.number(), + })), + judgment: ArenaJudgment, + }), + }, + handler: async (ctx, input) => { + // Generate from both models in parallel + const [openaiResult, anthropicResult] = await Promise.all([ + generateWithTiming(openai('gpt-5-nano'), input.prompt, input.tone), + generateWithTiming(anthropic('claude-haiku-4-5'), input.prompt, input.tone), + ]); + + const results = [ + { provider: 'openai', ...openaiResult }, + { provider: 'anthropic', ...anthropicResult }, + ]; + + // Judge with fast model + const { object: judgment } = await generateObject({ + model: groq('openai/gpt-oss-120b'), + schema: ArenaJudgment, + prompt: `Compare these ${input.tone} stories and pick a winner. + +PROMPT: "${input.prompt}" + +--- OpenAI --- +${openaiResult.story} + +--- Anthropic --- +${anthropicResult.story} + +Score each on creativity, engagement, and tone match (0-1). +Declare a winner with reasoning.`, + }); + + ctx.logger.info('Arena complete', { winner: judgment.winner }); + return { results, judgment }; + }, +}); + +async function generateWithTiming(model: any, prompt: string, tone: string) { + const start = Date.now(); + const { text } = await generateText({ + model, + system: `Write a short ${tone} story (max 200 words) with a beginning, middle, and end.`, + prompt, + }); + return { story: text, generationMs: Date.now() - start }; +} + +export default arenaAgent; +``` + + +The [SDK Explorer](/) includes a working Model Arena demo that demonstrates this pattern in more detail. + + +## Grounding Check for RAG + +Detect hallucinations by checking if claims are supported by retrieved sources: + +```typescript +import { createAgent } from '@agentuity/runtime'; +import { generateText, generateObject } from 'ai'; +import { openai } from '@ai-sdk/openai'; +import { groq } from '@ai-sdk/groq'; +import { z } from 'zod'; + +const GroundingJudgment = z.object({ + isGrounded: z.boolean().describe('Whether all claims are supported by sources'), + score: z.number().min(0).max(1).describe('Percentage of claims supported'), + unsupportedClaims: z.array(z.string()).describe('Claims not found in sources'), + reason: z.string(), +}); + +const ragAgent = createAgent('RAGWithGrounding', { + schema: { + input: z.object({ question: z.string() }), + output: z.object({ + answer: z.string(), + grounding: GroundingJudgment, + sources: z.array(z.string()), + }), + }, + handler: async (ctx, input) => { + // Retrieve relevant documents + const results = await ctx.vector.search('knowledge-base', { + query: input.question, + limit: 3, + }); + + const sources = results.map(r => r.metadata?.content || '').filter(Boolean); + + // Generate answer + const { text: answer } = await generateText({ + model: openai('gpt-5-mini'), + prompt: `Answer based on these sources:\n\n${sources.join('\n\n')}\n\nQuestion: ${input.question}`, + }); + + // Check grounding with fast model + const { object: grounding } = await generateObject({ + model: groq('openai/gpt-oss-120b'), + schema: GroundingJudgment, + prompt: `Check if this answer is supported by the sources. + +Answer: ${answer} + +Sources: +${sources.map((s, i) => `[${i + 1}] ${s}`).join('\n\n')} + +Are all factual claims in the answer supported by the sources? +List any unsupported claims.`, + }); + + if (!grounding.isGrounded) { + ctx.logger.warn('Hallucination detected', { + claims: grounding.unsupportedClaims, + }); + } + + return { answer, grounding, sources: results.map(r => r.key) }; + }, +}); + +export default ragAgent; +``` + +## Using in Evals + +For background quality monitoring, use LLM-as-judge in your eval files. Evals run asynchronously after the response is sent, so they don't slow down users: + +```typescript title="src/agent/qa-agent/eval.ts" +import agent from './agent'; +import { generateObject } from 'ai'; +import { groq } from '@ai-sdk/groq'; +import { z } from 'zod'; + +const HelpfulnessJudgment = z.object({ + scores: z.object({ + helpfulness: z.object({ + score: z.number().min(0).max(1), + reason: z.string(), + }), + }), + checks: z.object({ + answersQuestion: z.object({ + passed: z.boolean(), + reason: z.string(), + }), + actionable: z.object({ + passed: z.boolean(), + reason: z.string(), + }), + }), +}); + +export const helpfulnessEval = agent.createEval('helpfulness', { + description: 'Judges if responses are helpful and actionable', + handler: async (ctx, input, output) => { + const { object } = await generateObject({ + model: groq('openai/gpt-oss-120b'), + schema: HelpfulnessJudgment, + prompt: `Evaluate this response. + +Question: ${input.question} +Response: ${output.answer} + +SCORE: +- helpfulness: How useful is this response? (0 = useless, 1 = extremely helpful) + +CHECKS: +- answersQuestion: Does it directly answer what was asked? +- actionable: Can the user act on this information?`, + }); + + // Combine score and checks into eval result + const allChecksPassed = object.checks.answersQuestion.passed && object.checks.actionable.passed; + + return { + passed: allChecksPassed && object.scores.helpfulness.score >= 0.7, + score: object.scores.helpfulness.score, + reason: object.scores.helpfulness.reason, + metadata: { + checks: object.checks, + }, + }; + }, +}); +``` + +See [Adding Evaluations](/Agents/evaluations) for more patterns including preset evals. + +## Structuring Judge Prompts + +Good judge prompts have clear structure: + +```typescript +const judgePrompt = `You are evaluating a ${taskType} response. + +CONTEXT: +${context} + +RESPONSE TO EVALUATE: +${response} + +SCORING CRITERIA (0.0-1.0): +- criterion1: What specifically to look for +- criterion2: What specifically to look for + +CHECKS (pass/fail): +- check1: Specific yes/no question +- check2: Specific yes/no question + +Evaluate each criterion, then provide an overall assessment.`; +``` + +**Tips:** +- Be explicit about the scale (0-1, not 1-10) +- Define what each score level means +- Make checks binary questions with clear answers +- Include the original context/prompt for reference + +## Best Practices + +- **Use fast models for judging**: Groq, gpt-5-nano, or claude-haiku-4-5 work well +- **Separate scores from checks**: Scores for gradients, checks for pass/fail +- **Structure prompts clearly**: List criteria explicitly with descriptions +- **Log low scores**: Track failures for debugging and model improvement +- **Consider latency**: Use inline judging only when users need to see results + +## Cost Optimization + +LLM-as-judge adds model calls. Optimize by: + +- Using the fastest capable model (Groq for speed, nano models for cost) +- Running evals asynchronously (default in Agentuity) +- Sampling a percentage of requests in high-volume scenarios +- Batching judgments when evaluating multiple items + +## Next Steps + +- [Adding Evaluations](/Agents/evaluations): Background quality monitoring with evals +- [Using the AI SDK](/Agents/ai-sdk-integration): Model selection and configuration +- [Vector Storage](/Services/Storage/vector): Build RAG systems to ground responses diff --git a/content/Learn/Cookbook/Patterns/meta.json b/content/Learn/Cookbook/Patterns/meta.json index 13f51da0..bab83255 100644 --- a/content/Learn/Cookbook/Patterns/meta.json +++ b/content/Learn/Cookbook/Patterns/meta.json @@ -4,6 +4,7 @@ "background-tasks", "chat-with-history", "cron-with-storage", + "llm-as-a-judge", "server-utilities", "product-search", "tailwind-setup", diff --git a/content/Learn/Cookbook/overview.mdx b/content/Learn/Cookbook/overview.mdx index 5e0c68f3..78b566c1 100644 --- a/content/Learn/Cookbook/overview.mdx +++ b/content/Learn/Cookbook/overview.mdx @@ -34,6 +34,7 @@ Copy-paste solutions for common tasks. - [Background Tasks](/Learn/Cookbook/Patterns/background-tasks) — Use `waitUntil` for fire-and-forget work - [Chat with Conversation History](/Learn/Cookbook/Patterns/chat-with-history) — Maintain conversation context using thread state - [Cron with Storage](/Learn/Cookbook/Patterns/cron-with-storage) — Cache scheduled task results in KV for later retrieval +- [LLM as a Judge](/Learn/Cookbook/Patterns/llm-as-a-judge) — Use LLMs to evaluate and score agent outputs - [SDK Utilities for External Apps](/Learn/Cookbook/Patterns/server-utilities) — Use storage, logging, and error handling from Next.js or external backends - [Product Search with Vector](/Learn/Cookbook/Patterns/product-search) — Semantic search with metadata filtering - [Tailwind CSS Setup](/Learn/Cookbook/Patterns/tailwind-setup) — Add Tailwind styling to your frontend diff --git a/content/Reference/CLI/ai-commands.mdx b/content/Reference/CLI/ai-commands.mdx index dd0e0e25..f151e0b5 100644 --- a/content/Reference/CLI/ai-commands.mdx +++ b/content/Reference/CLI/ai-commands.mdx @@ -36,28 +36,6 @@ The schema includes: - Parameter types and validation rules - Command descriptions and examples -## Agent Skills Generation - -Generate [Agent Skills](https://agentskills.io) documentation from the CLI schema: - -```bash -# Generate SKILL.md files -agentuity ai skills generate - -# Output to custom directory -agentuity ai skills generate --output ./skills -``` - -This creates machine-readable skill definitions that enable coding agents (e.g., Claude Code, Cursor, Codex) to discover and use Agentuity CLI capabilities. - -**Generated files:** -- `SKILL.md` files with YAML frontmatter for each command -- `README.md` with a skill index - - -In dev mode (`agentuity dev`), skills are automatically regenerated when the CLI version changes. - - ## Project Context Files During development (`agentuity dev` or `bun run dev`), the CLI generates AGENTS.md files to help AI coding assistants (e.g., Claude Code, Codex, Cursor) understand Agentuity patterns: @@ -111,23 +89,21 @@ These prompts are useful for: Use the CLI to provide autocomplete, validation, and project configuration help: ```typescript -// Get CLI capabilities for IDE integration -const { execSync } = require('child_process'); +import { execSync } from 'child_process'; +// Get CLI capabilities for IDE integration const capabilities = JSON.parse( execSync('agentuity --json ai capabilities show').toString() ); - -// Use capabilities to build autocomplete, validation, etc. ``` ```typescript +import { execSync } from 'child_process'; + // Get agentuity.json schema for configuration validation const schema = JSON.parse( execSync('agentuity --json ai schema generate').toString() ); - -// Use schema to validate project configuration files ``` ### AI Coding Agent @@ -140,6 +116,46 @@ agentuity ai prompt agent > /tmp/agentuity-context.md cat /tmp/agentuity-context.md ``` +## OpenCode Plugin + +Install the Agentuity plugin for [OpenCode](https://opencode.ai) to enable AI-assisted development: + +```bash +# Install the plugin +agentuity ai opencode install + +# Remove the plugin +agentuity ai opencode uninstall + +# Run a task in headless mode +agentuity ai opencode run "Create an agent that validates email addresses" +``` + +The plugin provides specialized agents, slash commands, and MCP servers for Agentuity development. See [OpenCode Plugin](/Reference/CLI/opencode-plugin) for full documentation. + +## Cadence Commands + +Manage long-running AI tasks with the cadence system: + +```bash +# List all cadence tasks +agentuity ai cadence list + +# Check task status +agentuity ai cadence status + +# Pause a running task +agentuity ai cadence pause + +# Resume a paused task +agentuity ai cadence resume + +# Stop a task +agentuity ai cadence stop +``` + +Cadence tasks persist across sessions, allowing complex operations to continue in the background. + ## Next Steps - [Getting Started with the CLI](/Reference/CLI/getting-started): Install and authenticate diff --git a/content/Reference/CLI/debugging.mdx b/content/Reference/CLI/debugging.mdx index dc08a0f8..986a787a 100644 --- a/content/Reference/CLI/debugging.mdx +++ b/content/Reference/CLI/debugging.mdx @@ -36,7 +36,7 @@ agentuity cloud ssh --show Add your SSH public key before connecting: ```bash -agentuity auth ssh add ~/.ssh/id_rsa.pub +agentuity auth ssh add --file ~/.ssh/id_rsa.pub ``` See [Getting Started](/Reference/CLI/getting-started) for authentication setup. @@ -102,22 +102,16 @@ agentuity cloud agent list --project-id=proj_abc123xyz # Get details for a specific agent agentuity cloud agent get agent_abc123xyz - -# View agent input/output schema -agentuity cloud agent schema agent_abc123xyz ``` **Agent details include:** - Agent ID and name - Description and metadata - Associated routes -- Schema definitions (input/output types) Use agent inspection to: - Verify deployed agent configuration -- Debug schema mismatches - Explore available agents in a project -- Generate client code from schemas ## Session Logs @@ -208,6 +202,33 @@ Use thread inspection to: - Verify thread metadata - Track thread lifecycle +## Evaluation Commands + +Monitor and inspect evaluation runs to understand agent performance against test cases. + +List and inspect evaluations and evaluation runs: + +```bash +# List all evaluations +agentuity cloud eval list + +# Get details for a specific evaluation +agentuity cloud eval get eval_abc123xyz + +# List evaluation runs +agentuity cloud eval-run list + +# Get details for a specific run +agentuity cloud eval-run get run_abc123xyz +``` + +Use evaluation commands to: +- Monitor eval progress and results +- Debug failing evaluations +- Track evaluation history across deployments + +For more on configuring evaluations, see [Evaluations](/Agents/evaluations). + ## Deployment Logs Stream logs from a specific deployment: @@ -286,6 +307,35 @@ agentuity --json cloud thread list --count=100 | \ jq -r '.[] | [.id, .user_data] | @csv' > threads.csv ``` +## Support Commands + +Generate diagnostic information for troubleshooting or support requests: + +```bash +# Generate a support report (system info, project config, recent errors) +agentuity support report + +# Export recent logs +agentuity support logs +agentuity support logs > debug-logs.txt +agentuity support logs --limit 100 + +# Show system information +agentuity support system +``` + +**What's included:** + +| Command | Output | +|---------|--------| +| `support report` | CLI/runtime versions, project config (sanitized), recent errors, system info | +| `support logs` | Recent log entries for local analysis | +| `support system` | OS, Node.js/Bun versions, CLI version, memory/disk | + + +Support reports automatically redact sensitive information like API keys, tokens, and environment variable values. Review the output before sharing. + + ## Next Steps - [Managing Cloud Data](/Reference/CLI/data-management): Inspect storage, env vars, and secrets diff --git a/content/Reference/CLI/deployment.mdx b/content/Reference/CLI/deployment.mdx index 2c0da001..2b24e251 100644 --- a/content/Reference/CLI/deployment.mdx +++ b/content/Reference/CLI/deployment.mdx @@ -78,25 +78,6 @@ agentuity deploy Both URLs are automatically provisioned with HTTPS. The project URL always routes to the active deployment. -## Tagging Deployments - -Add tags to label deployments: - -```bash -# Single tag -agentuity deploy --tag staging - -# Multiple tags -agentuity deploy --tag staging --tag hotfix-123 -``` - -Tags appear in deployment lists and help organize versions: - -```bash -agentuity cloud deployment list -# Shows tags column for each deployment -``` - ## Viewing Deployments List recent deployments: @@ -262,6 +243,23 @@ agentuity cloud deployment remove dep_abc123xyz --project-id=proj_abc123xyz Removing a deployment permanently deletes it. You cannot rollback to a removed deployment. +## Preview Environments + +Deploy pull requests to isolated preview environments for testing before merging. Enable this feature in the [Agentuity App](https://app-v1.agentuity.com): + +1. Navigate to your project's **Settings** page +2. Under **GitHub**, find the **Preview Environments** toggle +3. Enable "Deploy PRs to preview environments for this repository" + +Once enabled, every pull request to your connected repository automatically deploys to a unique preview URL. This allows you to: +- Test changes in an isolated environment before merging +- Share preview links with reviewers +- Run integration tests against the preview deployment + + +Preview environments require a connected GitHub repository. Connect your repo in **Project > Settings > GitHub**. + + ## Custom Domains Configure custom domains in `agentuity.json`: @@ -308,6 +306,10 @@ agentuity deploy Deployment fails if DNS records are missing or incorrect. The CLI shows the required CNAME value in the error message. + +After adding a custom domain to your configuration, you must deploy to activate it. The domain becomes active only after a successful deployment. + + ### Multiple Domains Custom domains replace the default URLs: @@ -375,31 +377,56 @@ Variables are automatically: Variables with suffixes like `_SECRET`, `_KEY`, `_TOKEN`, `_PASSWORD`, or `_PRIVATE` are automatically encrypted as secrets. All other variables are stored as regular environment variables. +## Machine Management + +Machines are the compute instances running your deployments. View and manage them when debugging infrastructure issues. + +View and manage the machines running your deployments: + +```bash +# List all machines for current project +agentuity cloud machine list + +# Get details for a specific machine +agentuity cloud machine get mach_abc123xyz + +# Delete a machine +agentuity cloud machine delete mach_abc123xyz + +# List deployments running on a machine +agentuity cloud machine deployments mach_abc123xyz +``` + +Machine details include status, region, resource allocation, and uptime. Use machine management to: +- Monitor infrastructure health +- Debug performance issues +- Clean up unused machines + ## Deploy Options | Option | Description | |--------|-------------| -| `--tag ` | Add deployment tag (repeatable) | -| `-f, --force` | Force deployment without confirmation | -| `--dry-run` | Simulate deployment without executing | +| `-y, --confirm` | Confirm region change without prompting | +| `--dry-run` | Simulate deployment without executing (global option) | | `--log-level debug` | Show verbose output | | `--project-id ` | Deploy specific project | +| `--message ` | Message to associate with this build | ```bash # Verbose deployment agentuity deploy --log-level=debug -# Multiple tags -agentuity deploy --tag staging --tag v1.2.0 - # Specific project agentuity deploy --project-id=proj_abc123xyz -# Simulate deployment -agentuity deploy --dry-run +# With a message +agentuity deploy --message "Fix authentication bug" + +# Simulate deployment (dry run) +agentuity --dry-run deploy -# Force deployment -agentuity deploy --force +# Skip confirmation prompts (for CI/CD) +agentuity deploy --confirm ``` ## CI/CD Pipelines diff --git a/content/Reference/CLI/development.mdx b/content/Reference/CLI/development.mdx index 7b40e933..dd58dec0 100644 --- a/content/Reference/CLI/development.mdx +++ b/content/Reference/CLI/development.mdx @@ -25,11 +25,11 @@ The server starts on port 3500 by default with: |--------|---------|-------------| | `--port` | 3500 (or `PORT` env) | TCP port for the dev server | | `--local` | false | Use local services instead of cloud | -| `--public` | true | Enable public URL tunneling | | `--no-public` | - | Disable public URL tunneling | -| `--interactive` | true | Enable interactive keyboard shortcuts | -| `--watch ` | - | Additional paths to watch for changes (repeatable) | -| `--profile ` | - | Load `.env.` in addition to `.env` | +| `--no-interactive` | - | Disable interactive keyboard shortcuts | +| `--inspect` | - | Enable Bun debugger for debugging | +| `--inspect-wait` | - | Enable debugger and wait for connection before starting | +| `--inspect-brk` | - | Enable debugger and break on first line | The dev server respects the `PORT` environment variable. The `--port` flag takes precedence if both are set. @@ -47,13 +47,36 @@ agentuity dev --no-public # Non-interactive mode (useful for CI/CD) agentuity dev --no-interactive +``` + +## Debugging with Inspector -# Watch additional directories (e.g., local packages) -agentuity dev --watch ../my-shared-lib/dist --watch ../other-package/src +Use the inspector flags to debug your agents with Chrome DevTools or VS Code: + +```bash +# Enable inspector (attach debugger anytime) +agentuity dev --inspect + +# Wait for debugger before starting the server +agentuity dev --inspect-wait + +# Break on first line of executed code +agentuity dev --inspect-brk ``` - -Use `--watch` when developing with local packages outside your project. Changes in watched paths trigger rebuilds just like changes in your source directory. +After starting with `--inspect`, open `chrome://inspect` in Chrome or use VS Code's debugger to attach. The inspector listens on port 9229 by default. + + +Create a launch configuration that attaches to the running process: + +```json +{ + "type": "node", + "request": "attach", + "name": "Attach to Agentuity", + "port": 9229 +} +``` ## Keyboard Shortcuts @@ -104,50 +127,16 @@ const app = createApp({ See [Custom Storage](/Services/Storage/custom) for implementation details. -## Profile-Specific Environment Variables - -Load different environment variables based on a profile name: - -```bash -# Load .env.staging -agentuity dev --profile staging - -# Load .env.testing -agentuity dev --profile testing -``` - -Profile-specific files are loaded in addition to the base `.env` file. Variables in the profile file override those in `.env`. - -**Example setup:** - -```bash -# .env (base, always loaded) -DATABASE_URL=postgres://localhost:5432/myapp -LOG_LEVEL=info - -# .env.staging (loaded with --profile staging) -DATABASE_URL=postgres://staging.example.com:5432/myapp -API_BASE_URL=https://staging-api.example.com - -# .env.testing (loaded with --profile testing) -DATABASE_URL=postgres://localhost:5432/myapp_test -MOCK_EXTERNAL_APIS=true -``` - - -Use profiles to switch between configurations without editing files. Common profiles: `staging`, `testing`, `local`, `demo`. - - ## Public URLs -Enable public URLs to share your local dev server or receive webhooks: +Public URLs are enabled by default to share your local dev server or receive webhooks: ```bash # Public URL enabled by default agentuity dev -# Or explicitly -agentuity dev --public +# Disable if not needed +agentuity dev --no-public ``` ### Why Public URLs? diff --git a/content/Reference/CLI/getting-started.mdx b/content/Reference/CLI/getting-started.mdx index aee297ca..64c6ecca 100644 --- a/content/Reference/CLI/getting-started.mdx +++ b/content/Reference/CLI/getting-started.mdx @@ -414,3 +414,4 @@ Now that you have the CLI installed and authenticated: - [Local Development](/Reference/CLI/development): Run agents locally with hot reload - [Deploying to the Cloud](/Reference/CLI/deployment): Deploy agents to Agentuity Cloud - [Managing Cloud Data](/Reference/CLI/data-management): Inspect storage, env vars, and secrets +- [OpenCode Plugin](/Reference/CLI/opencode-plugin): AI-assisted development with specialized agents diff --git a/content/Reference/CLI/meta.json b/content/Reference/CLI/meta.json index cdcea172..edc1da11 100644 --- a/content/Reference/CLI/meta.json +++ b/content/Reference/CLI/meta.json @@ -9,6 +9,7 @@ "sandbox", "configuration", "debugging", - "ai-commands" + "ai-commands", + "opencode-plugin" ] } diff --git a/content/Reference/CLI/opencode-plugin.mdx b/content/Reference/CLI/opencode-plugin.mdx new file mode 100644 index 00000000..d0b983af --- /dev/null +++ b/content/Reference/CLI/opencode-plugin.mdx @@ -0,0 +1,270 @@ +--- +title: OpenCode Plugin +description: Install the Agentuity plugin for OpenCode to enable AI-assisted development with specialized agents +--- + +The OpenCode plugin adds Agentuity-aware AI agents and slash commands to [OpenCode](https://opencode.ai), providing intelligent assistance for building, deploying, and managing your agents. + +## Installation + +Install the plugin using the CLI: + +```bash +agentuity ai opencode install +``` + +This configures OpenCode with: +- Specialized AI agents for different development tasks +- Slash commands for common Agentuity operations +- MCP servers for library documentation and code search + +To remove the plugin: + +```bash +agentuity ai opencode uninstall +``` + +## Specialized Agents + +The plugin provides various specialized agents, each focused on a specific aspect of development: + +| Agent | Role | When to Use | +|-------|------|-------------| +| **Lead** | Orchestrator | Automatically coordinates all work | +| **Scout** | Explorer | Finding files, patterns, codebase analysis (read-only) | +| **Builder** | Implementer | Interactive code changes, quick fixes, guided implementation | +| **Architect** | Autonomous Implementer | Cadence mode, complex multi-file features, long-running tasks | +| **Reviewer** | Code Reviewer | Reviewing changes, catching issues, suggesting fixes | +| **Memory** | Context Manager | Storing/retrieving context, decisions, patterns across sessions | +| **Expert** | Agentuity Specialist | CLI commands, cloud services, SDK questions | +| **Planner** | Strategic Advisor | Complex architecture decisions, deep technical planning (read-only) | +| **Runner** | Command Executor | Run lint/build/test/typecheck/format, returns structured summaries | + +OpenCode automatically routes tasks to the appropriate agent based on context. + +### Builder vs Architect + +| Aspect | Builder | Architect | +|--------|---------|-----------| +| **Mode** | Interactive | Autonomous | +| **Best for** | Quick fixes, guided work | Cadence mode, complex features | +| **Model** | Claude Opus 4.5 | GPT 5.2 Codex | +| **Reasoning** | High | Maximum (xhigh) | +| **Context** | Session-based | Checkpoint-based | + +Use **Builder** when working interactively, making quick changes, or needing guidance. Use **Architect** when running Cadence mode, implementing complex multi-file features, or needing autonomous execution with deep reasoning. + +### Model Configuration + +Override any agent's model via `opencode.json`: + +```json +{ + "agent": { + "Agentuity Coder Builder": { + "model": "anthropic/claude-sonnet-4-5-20250514", + "temperature": 0.7 + }, + "Agentuity Coder Architect": { + "model": "openai/gpt-5.2-codex", + "reasoningEffort": "xhigh" + } + } +} +``` + +**Configuration options:** + +| Option | Description | +|--------|-------------| +| `model` | Model identifier (e.g., `anthropic/claude-sonnet-4-5-20250514`) | +| `temperature` | Number between 0-1 (lower = more deterministic) | +| `reasoningEffort` | For OpenAI: `low`, `medium`, `high`, `xhigh` | +| `variant` | For Anthropic: `low`, `medium`, `high`, `max` (extended thinking) | +| `thinking` | For Anthropic: `{ "type": "enabled", "budgetTokens": 10000 }` | +| `maxSteps` | Maximum tool use steps per turn | + +Run `opencode models` to see all available models. + + +Sensitive CLI commands are blocked by default: `agentuity cloud secrets`, `agentuity cloud apikey`, and `agentuity auth token`. + + +## Slash Commands + +The plugin adds slash commands for common Agentuity tasks: + +| Command | Description | +|---------|-------------| +| `/agentuity-coder` | Run tasks with the full agent team (Lead orchestrates) | +| `/agentuity-cadence` | Start a long-running autonomous loop | +| `/agentuity-cloud` | Interact with any Agentuity cloud service | +| `/agentuity-sandbox` | Run code in isolated sandbox environments | +| `/agentuity-memory-save` | Save session context to memory | + +Use these commands by typing them directly in OpenCode: + +``` +/agentuity-coder Create a new agent that processes webhooks from Stripe +``` + +## MCP Servers + +The plugin configures two MCP servers that provide additional context to the AI agents: + +**context7**: Provides access to library documentation for common dependencies (AI SDK, Zod, Hono, etc.). Agents can look up API references and examples without leaving your editor. + +**grep_app**: Enables searching GitHub repositories for code examples and patterns. Useful when you need to see how others have implemented similar features. + +## Headless Mode + +Run Agentuity tasks from the command line without opening OpenCode: + +```bash +agentuity ai opencode run "Create an agent that validates email addresses" +``` + +This executes the task using the appropriate agents and outputs the result. Useful for: +- CI/CD pipelines that need AI-assisted code generation +- Scripting repetitive development tasks +- Batch processing multiple requests + +## Cadence System + +Cadence manages long-running AI tasks that continue in the background. This is useful for complex multi-step operations that take time to complete. + +### Managing Cadence Tasks + +```bash +# List all cadence tasks +agentuity ai cadence list + +# Check status of a specific task +agentuity ai cadence status + +# Pause a running task +agentuity ai cadence pause + +# Resume a paused task +agentuity ai cadence resume + +# Stop a task completely +agentuity ai cadence stop +``` + +Tasks persist across sessions, so you can start a complex operation, close your editor, and check on it later. + +## Background Agents + +Run agents in the background while continuing other work. Background agents execute asynchronously and notify you when complete. + +The plugin provides three tools for background task management: + +| Tool | Description | +|------|-------------| +| `background_task` | Launch an agent task in the background | +| `background_output` | Retrieve the result of a completed task | +| `background_cancel` | Cancel a running or pending background task | + +### Concurrency Control + +Background tasks are rate-limited to prevent overwhelming providers. Configure in your Agentuity CLI profile (`~/.config/agentuity/production.yaml`): + +```yaml +coder: + background: + enabled: true + defaultConcurrency: 3 +``` + +| Option | Default | Description | +|--------|---------|-------------| +| `enabled` | `true` | Enable/disable background tasks | +| `defaultConcurrency` | `1` | Default max concurrent tasks per model | +| `staleTimeoutMs` | `1800000` | Timeout for stale tasks (30 minutes) | +| `providerConcurrency` | `{}` | Per-provider concurrency limits | +| `modelConcurrency` | `{}` | Per-model concurrency limits | + +## Tmux Integration + +When running inside tmux, background agents can spawn in separate panes for visual multi-agent execution. + + +Tmux integration requires OpenCode to run with an HTTP server enabled. Start OpenCode with `opencode --port 4096`. + + +Configure in your Agentuity CLI profile: + +```yaml +coder: + tmux: + enabled: true + maxPanes: 6 +``` + +| Option | Default | Description | +|--------|---------|-------------| +| `enabled` | `false` | Enable tmux pane spawning | +| `maxPanes` | `4` | Max agent panes before rotating oldest out | +| `mainPaneMinWidth` | `100` | Minimum width for main pane (columns) | +| `agentPaneMinWidth` | `40` | Minimum width for agent panes (columns) | + +When enabled, agents spawn in a dedicated "Agents" window with a tiled grid layout. Oldest panes close automatically when `maxPanes` is reached. + +## Configuration + +Agent models are configured in `~/.config/opencode/opencode.json`. Plugin behavior settings go in your Agentuity CLI profile (`~/.config/agentuity/production.yaml`). + +### Agent Model Overrides + +Override any agent's model in `opencode.json`: + +```json +{ + "agent": { + "Agentuity Coder Builder": { + "model": "anthropic/claude-sonnet-4-5-20250514" + }, + "Agentuity Coder Architect": { + "model": "openai/gpt-5.2-codex", + "reasoningEffort": "xhigh" + } + } +} +``` + +### MCP Server Configuration + +Add MCP servers for enhanced agent capabilities in `opencode.json`: + +```json +{ + "mcp": { + "context7": { "type": "remote", "url": "https://mcp.context7.com/mcp" }, + "grep_app": { "type": "remote", "url": "https://mcp.grep.app" } + } +} +``` + +| MCP | Purpose | Free Tier | +|-----|---------|-----------| +| **context7** | Library docs lookup | 500 req/month | +| **grep_app** | GitHub code search | Unlimited | + +### Plugin Behavior Settings + +Configure plugin behavior in your Agentuity CLI profile (`~/.config/agentuity/production.yaml`): + +```yaml +coder: + tmux: + enabled: true + background: + defaultConcurrency: 3 +``` + +## Next Steps + +- [AI Commands](/Reference/CLI/ai-commands): Other AI-related CLI commands +- [Local Development](/Reference/CLI/development): Run agents locally +- [Creating Agents](/Agents/creating-agents): Build your first agent diff --git a/content/Services/Storage/database.mdx b/content/Services/Storage/database.mdx index f21fc7eb..a33a3b94 100644 --- a/content/Services/Storage/database.mdx +++ b/content/Services/Storage/database.mdx @@ -19,6 +19,20 @@ Database storage provides relational database capabilities using [Bun's native S When you create a database with `agentuity cloud db create`, the `DATABASE_URL` is automatically added to your `.env` file. During deployment, credentials are injected automatically. +## Creating a Database + +Create a database from the CLI with optional name and description: + +```bash +# Create with default settings +agentuity cloud db create + +# Create with name and description +agentuity cloud db create --name "users-db" --description "Primary user data store" +``` + +The name and description help identify databases in the dashboard when managing multiple databases. + Database access uses Bun's native SQL API directly. The same `import { sql } from "bun"` code works identically in agents, routes, and standalone scripts with no wrapper needed. @@ -151,6 +165,125 @@ const sqlite = new SQL("sqlite://data/app.db"); For complete API documentation including dynamic queries, bulk inserts, savepoints, array operations, error handling, and connection pooling, see the [Bun SQL documentation](https://bun.com/docs/runtime/sql). +## Resilient Postgres Client + +The `@agentuity/postgres` package provides a connection-resilient PostgreSQL client built for serverless environments. It handles connection drops, cold starts, and automatic reconnection. + +```bash +bun add @agentuity/postgres +``` + +```typescript +import { postgres } from '@agentuity/postgres'; + +// Create client (uses DATABASE_URL by default) +const sql = postgres(); + +// Queries automatically retry on connection errors +const users = await sql`SELECT * FROM users WHERE active = ${true}`; + +// Transactions +const tx = await sql.begin(); +await tx`INSERT INTO users (name) VALUES (${'Alice'})`; +await tx.commit(); +``` + +### Configuration + +```typescript +import { postgres } from '@agentuity/postgres'; + +const sql = postgres({ + url: 'postgres://user:pass@localhost:5432/mydb', + + // Reconnection + reconnect: { + maxAttempts: 10, + initialDelayMs: 100, + maxDelayMs: 30000, + }, + + // Callbacks + onclose: (error) => console.log('Connection closed', error), + onreconnected: () => console.log('Reconnected!'), +}); +``` + +**Key features:** + +- **Automatic reconnection**: Exponential backoff with jitter on connection drops +- **Query retry**: Retries queries on retryable errors (connection reset, timeout) +- **Transaction support**: Full transaction and savepoint support with `sql.begin()` +- **Graceful shutdown**: Skips reconnection on SIGTERM/SIGINT for clean process exit +- **Connection stats**: Track connection health via `sql.stats` + + +Use `@agentuity/postgres` when you need resilient connections in serverless environments, especially with Neon or similar providers that have cold start latency. + + +## Drizzle ORM Integration + +The `@agentuity/drizzle` package provides type-safe database access with [Drizzle ORM](https://orm.drizzle.team/) and automatic reconnection. + +```bash +bun add @agentuity/drizzle +``` + +```typescript +import { createPostgresDrizzle, eq } from '@agentuity/drizzle'; +import * as schema from './schema'; + +// Create database instance (uses DATABASE_URL by default) +const { db, client, close } = createPostgresDrizzle({ schema }); + +// Type-safe queries with schema inference +const activeUsers = await db.select().from(schema.users).where(eq(schema.users.active, true)); + +// Access connection stats via the underlying client +console.log(client.stats); + +// Clean up when done +await close(); +``` + +**Key features:** + +- **Type-safe queries**: Full TypeScript inference from your Drizzle schema +- **Resilient connection**: Built on `@agentuity/postgres` for automatic reconnection +- **Convenient re-exports**: Common Drizzle utilities (`eq`, `and`, `pgTable`, etc.) available from a single import +- **Auth integration**: Works with `@agentuity/auth` via `drizzleAdapter` + +```typescript +import { createPostgresDrizzle, eq } from '@agentuity/drizzle'; +import * as schema from './schema'; + +const { db, close } = createPostgresDrizzle({ + schema, + logger: true, // Enable query logging + reconnect: { + maxAttempts: 5, + initialDelayMs: 100, + }, + onReconnected: () => console.log('Reconnected to database'), +}); + +// Drizzle infers the joined type +const usersWithPosts = await db + .select() + .from(schema.users) + .leftJoin(schema.posts, eq(schema.users.id, schema.posts.authorId)); + +// Insert with returning +const [newUser] = await db + .insert(schema.users) + .values({ name: 'Alice', email: 'alice@example.com' }) + .returning(); +``` + + +Define your schema in a separate file and use `drizzle-kit` for migrations. See the [Drizzle documentation](https://orm.drizzle.team/docs/sql-schema-declaration) for schema syntax. + + ## Next Steps - [Key-Value Storage](/Services/Storage/key-value): Fast caching and session data diff --git a/content/Services/Storage/durable-streams.mdx b/content/Services/Storage/durable-streams.mdx index 29c8caf1..b5d51ac8 100644 --- a/content/Services/Storage/durable-streams.mdx +++ b/content/Services/Storage/durable-streams.mdx @@ -61,6 +61,7 @@ const agent = createAgent('StreamCreator', { contentType: 'text/csv', compress: true, // optional gzip compression metadata: { userId: input.userId }, + ttl: 86400 * 7, // expires in 7 days }); // Stream is ready immediately @@ -78,12 +79,27 @@ const agent = createAgent('StreamCreator', { - `contentType`: MIME type for the stream content (e.g., `text/csv`, `application/json`) - `compress`: Enable gzip compression for smaller storage and faster transfers - `metadata`: Key-value pairs for tracking stream context (user IDs, timestamps, etc.) +- `ttl`: Time-to-live in seconds (see TTL semantics below) + +**TTL semantics:** + +| Value | Behavior | +|-------|----------| +| `undefined` | Streams expire after 30 days (default) | +| `null` or `0` | Streams never expire | +| `>= 60` | Custom TTL in seconds (minimum 60 seconds, maximum 90 days) | + + +TTL is enforced only in cloud deployments. During local development, streams persist indefinitely regardless of the TTL value. The `expiresAt` field will not be populated in local stream metadata. + ## Writing Data Write data incrementally, then close the stream: ```typescript +import { createAgent } from '@agentuity/runtime'; + const agent = createAgent('CSVExporter', { handler: async (ctx, input) => { const stream = await ctx.stream.create('export', { @@ -119,6 +135,8 @@ Streams must be closed manually with `stream.close()`. They do not auto-close. F Use `ctx.waitUntil()` to write data in the background while returning immediately: ```typescript +import { createAgent } from '@agentuity/runtime'; + const agent = createAgent('BackgroundExporter', { handler: async (ctx, input) => { const stream = await ctx.stream.create('report', { @@ -148,11 +166,13 @@ const agent = createAgent('BackgroundExporter', { ### Listing Streams ```typescript +import { createAgent } from '@agentuity/runtime'; + const agent = createAgent('StreamManager', { handler: async (ctx, input) => { // List streams with optional filtering const result = await ctx.stream.list({ - name: 'export', // filter by stream name + namespace: 'export', // filter by stream namespace metadata: { userId: input.userId }, // filter by metadata limit: 100, // max 1000, default 100 offset: 0, // pagination offset @@ -171,11 +191,17 @@ const agent = createAgent('StreamManager', { ### Reading Stream Content ```typescript +import { createAgent } from '@agentuity/runtime'; + const agent = createAgent('StreamReader', { handler: async (ctx, input) => { // Get stream metadata by ID const info = await ctx.stream.get(input.streamId); - ctx.logger.info('Stream info', { name: info.name, sizeBytes: info.sizeBytes }); + ctx.logger.info('Stream info', { + namespace: info.namespace, + sizeBytes: info.sizeBytes, + expiresAt: info.expiresAt, // ISO timestamp when stream expires + }); // Download stream content as ReadableStream const content = await ctx.stream.download(input.streamId); @@ -198,6 +224,8 @@ const agent = createAgent('StreamReader', { ### Deleting Streams ```typescript +import { createAgent } from '@agentuity/runtime'; + const agent = createAgent('StreamCleaner', { handler: async (ctx, input) => { await ctx.stream.delete(input.streamId); @@ -265,6 +293,7 @@ const agent = createAgent('DualStreamWriter', { Buffer and evaluate content in real-time using an LLM-as-a-judge pattern: ```typescript +import { createAgent } from '@agentuity/runtime'; import { generateObject, streamText } from 'ai'; import { openai } from '@ai-sdk/openai'; import { groq } from '@ai-sdk/groq'; @@ -317,7 +346,7 @@ const agent = createAgent('ModeratedStreamer', { // Use Groq for low-latency moderation async function moderateContent(text: string): Promise { const { object } = await generateObject({ - model: groq('openai/gpt-oss-20b'), + model: groq('openai/gpt-oss-120b'), schema: s.object({ safe: s.boolean(), reason: s.optional(s.string()), diff --git a/content/Services/Storage/key-value.mdx b/content/Services/Storage/key-value.mdx index 33d4b68c..2b9df669 100644 --- a/content/Services/Storage/key-value.mdx +++ b/content/Services/Storage/key-value.mdx @@ -44,16 +44,21 @@ import { createAgent } from '@agentuity/runtime'; const agent = createAgent('CacheManager', { handler: async (ctx, input) => { - // Store with optional TTL (minimum 60 seconds) + // Store with custom TTL (minimum 60 seconds, maximum 90 days) await ctx.kv.set('cache', 'api-response', responseData, { ttl: 3600, // expires in 1 hour contentType: 'application/json', }); - // Store without TTL (persists indefinitely) + // Store with default TTL (7 days) + await ctx.kv.set('cache', 'user-prefs', { theme: 'dark' }); + + // Store with no expiration (persists indefinitely) await ctx.kv.set('config', 'feature-flags', { darkMode: true, betaFeatures: false, + }, { + ttl: null, // never expires (0 also works) }); return { success: true }; @@ -64,12 +69,17 @@ const agent = createAgent('CacheManager', { ### Retrieving Data ```typescript +import { createAgent } from '@agentuity/runtime'; + const agent = createAgent('CacheRetriever', { handler: async (ctx, input) => { const result = await ctx.kv.get('cache', 'api-response'); if (result.exists) { - ctx.logger.info('Cache hit', { contentType: result.contentType }); + ctx.logger.info('Cache hit', { + contentType: result.contentType, + expiresAt: result.expiresAt, // ISO timestamp when key expires (if TTL set) + }); return { data: result.data }; } @@ -82,6 +92,8 @@ const agent = createAgent('CacheRetriever', { ### Deleting Data ```typescript +import { createAgent } from '@agentuity/runtime'; + const agent = createAgent('SessionCleaner', { handler: async (ctx, input) => { await ctx.kv.delete('sessions', input.sessionId); @@ -95,6 +107,7 @@ const agent = createAgent('SessionCleaner', { Use generics for type-safe data access: ```typescript +import { createAgent } from '@agentuity/runtime'; import { type } from 'arktype'; const UserPreferences = type({ @@ -123,6 +136,8 @@ const agent = createAgent('PreferenceLoader', { ## Additional Methods ```typescript +import { createAgent } from '@agentuity/runtime'; + const agent = createAgent('StorageExplorer', { handler: async (ctx, input) => { // Search keys by keyword (returns keys with metadata) @@ -150,6 +165,8 @@ const agent = createAgent('StorageExplorer', { Create and delete namespaces programmatically: ```typescript +import { createAgent } from '@agentuity/runtime'; + const agent = createAgent('NamespaceManager', { handler: async (ctx, input) => { // Create a namespace with default TTL for all keys @@ -170,13 +187,17 @@ const agent = createAgent('NamespaceManager', { }); ``` -**Default TTL options:** +**TTL semantics:** | Value | Behavior | |-------|----------| | `undefined` | Keys expire after 7 days (default) | -| `0` | Keys never expire | -| `60` - `7776000` | Custom TTL in seconds (1 minute to 90 days) | +| `null` or `0` | Keys never expire | +| `>= 60` | Custom TTL in seconds (minimum 60 seconds, maximum 90 days) | + + +The minimum TTL is 60 seconds. The maximum is 90 days (7,776,000 seconds). Values outside this range will be rejected. + When a key is read with less than 50% of its TTL remaining, the expiration is automatically extended. This keeps frequently-accessed data alive without manual renewal. @@ -198,6 +219,8 @@ Keys expire after 7 days by default unless a namespace-level or per-key TTL is s | Feature flags | No TTL (persistent) | ```typescript +import { createAgent } from '@agentuity/runtime'; + interface UserSession { userId: string; email: string; diff --git a/content/Services/Storage/meta.json b/content/Services/Storage/meta.json index 9ce78fef..0f996d6c 100644 --- a/content/Services/Storage/meta.json +++ b/content/Services/Storage/meta.json @@ -1,4 +1,4 @@ { "title": "Storage", - "pages": ["key-value", "vector", "object", "database", "durable-streams", "custom"] + "pages": ["key-value", "vector", "object", "database", "queue", "durable-streams", "custom"] } diff --git a/content/Services/Storage/queue.mdx b/content/Services/Storage/queue.mdx new file mode 100644 index 00000000..23e71c0e --- /dev/null +++ b/content/Services/Storage/queue.mdx @@ -0,0 +1,171 @@ +--- +title: Queues +description: Publish and process messages asynchronously with durable message queues +--- + +Queues enable asynchronous message processing between agents. Use them to decouple work, handle spikes in load, and build reliable event-driven architectures. + +## When to Use Queues + +| Storage Type | Best For | +|--------------|----------| +| **Queue** | Async processing, event-driven workflows, load balancing | +| [Key-Value](/Services/Storage/key-value) | Fast lookups, caching, configuration | +| [Vector](/Services/Storage/vector) | Semantic search, embeddings, RAG | +| [Object (S3)](/Services/Storage/object) | Files, images, documents, media | +| [Database](/Services/Storage/database) | Structured data, complex queries, transactions | +| [Durable Streams](/Services/Storage/durable-streams) | Large exports, audit logs | + +## Access Patterns + +| Context | Access | Details | +|---------|--------|---------| +| Agents | `ctx.queue` | See examples below | +| Routes | `c.var.queue` | See [Using in Routes](#using-in-routes) | +| Standalone | `createAgentContext()` | See [Standalone Usage](#standalone-usage) | + + +The Queue API is identical in all contexts. `ctx.queue.publish()` and `c.var.queue.publish()` work the same way. + + +## Publishing Messages + +Send messages to a queue for async processing: + +```typescript +import { createAgent } from '@agentuity/runtime'; + +const agent = createAgent('OrderProcessor', { + handler: async (ctx, input) => { + // Publish order for async processing + await ctx.queue.publish('orders', { + orderId: input.orderId, + items: input.items, + customerId: input.customerId, + }); + + ctx.logger.info('Order queued', { orderId: input.orderId }); + return { status: 'queued', orderId: input.orderId }; + }, +}); +``` + +Messages are delivered at least once to consumers subscribed to the queue. + +## Synchronous Publishing + +Use `sync: true` when you need to wait for the message to be acknowledged by a consumer: + +```typescript +import { createAgent } from '@agentuity/runtime'; + +const agent = createAgent('CriticalProcessor', { + handler: async (ctx, input) => { + // Wait for message to be processed + const result = await ctx.queue.publish('critical-tasks', { + taskId: input.taskId, + payload: input.data, + }, { + sync: true, // blocks until consumer acknowledges + }); + + ctx.logger.info('Task processed synchronously', { taskId: input.taskId }); + return { status: 'completed', result }; + }, +}); +``` + + +Synchronous publishing blocks until the message is processed. Use it only when you need confirmation that work completed. For most use cases, async publishing is faster and more resilient. + + +## Publishing with Options + +Customize message delivery with additional options: + +```typescript +await ctx.queue.publish('notifications', { + userId: input.userId, + message: input.message, +}, { + metadata: { // attach metadata for routing or filtering + source: 'api', + version: '2', + }, + partitionKey: input.userId, // ensure ordering for this user + idempotencyKey: input.messageId, // prevent duplicate processing + ttl: 3600, // message expires after 1 hour +}); +``` + +## Using in Routes + +Routes have the same queue access via `c.var.queue`: + +```typescript +import { createRouter } from '@agentuity/runtime'; + +const router = createRouter(); + +router.post('/webhook/stripe', async (c) => { + const event = await c.req.json(); + + // Queue webhook for async processing + await c.var.queue.publish('stripe-webhooks', { + type: event.type, + data: event.data, + }); + + // Return 200 immediately (Stripe expects fast responses) + return c.json({ received: true }); +}); + +export default router; +``` + + +Use queues for webhooks that need quick acknowledgment. Return 200 immediately, then process the payload asynchronously. + + +## Standalone Usage + +Use queues from background jobs with `createAgentContext()`: + +```typescript +import { createApp, createAgentContext } from '@agentuity/runtime'; + +await createApp(); + +// Scheduled job to send daily reports +async function sendDailyReports() { + const ctx = createAgentContext({ trigger: 'cron' }); + + await ctx.invoke(async () => { + const users = await getActiveUsers(); + + for (const user of users) { + await ctx.queue.publish('email-reports', { + userId: user.id, + reportType: 'daily', + }); + } + + ctx.logger.info('Queued daily reports', { count: users.length }); + }); +} +``` + +See [Running Agents Without HTTP](/Agents/standalone-execution) for more patterns. + +## Best Practices + +- **Use async by default**: Only use `sync: true` when you need confirmation +- **Keep messages small**: Store large payloads in Object Storage, pass references in messages +- **Handle failures**: Consumers should handle retries gracefully +- **Use metadata**: Add context for routing, debugging, and filtering + +## Next Steps + +- [Key-Value Storage](/Services/Storage/key-value): Fast caching and configuration +- [Durable Streams](/Services/Storage/durable-streams): Large data exports +- [Background Tasks](/Learn/Cookbook/Patterns/background-tasks): Patterns for async processing diff --git a/content/Services/Storage/vector.mdx b/content/Services/Storage/vector.mdx index abb87374..b3fc2be8 100644 --- a/content/Services/Storage/vector.mdx +++ b/content/Services/Storage/vector.mdx @@ -44,11 +44,13 @@ const agent = createAgent('KnowledgeLoader', { key: 'doc-1', document: 'Agentuity is an agent-native cloud platform', metadata: { category: 'platform', source: 'docs' }, + ttl: 86400 * 7, // expires in 7 days }, { key: 'doc-2', document: 'Vector storage enables semantic search capabilities', metadata: { category: 'features', source: 'docs' }, + // No TTL specified: uses 30-day default } ); @@ -58,6 +60,18 @@ const agent = createAgent('KnowledgeLoader', { }); ``` +**TTL semantics:** + +| Value | Behavior | +|-------|----------| +| `undefined` | Vectors expire after 30 days (default) | +| `null` or `0` | Vectors never expire | +| `>= 60` | Custom TTL in seconds (minimum 60 seconds, maximum 90 days) | + + +TTL is enforced only in cloud deployments. During local development, vectors persist indefinitely regardless of the TTL value. The `expiresAt` field will not be populated in local search results. + + **With pre-computed embeddings:** ```typescript @@ -65,6 +79,7 @@ await ctx.vector.upsert('custom-embeddings', { key: 'embedding-1', embeddings: [0.1, 0.2, 0.3, 0.4, ...], metadata: { source: 'external' }, + ttl: null, // never expires }); ``` @@ -74,6 +89,8 @@ await ctx.vector.upsert('custom-embeddings', { Find semantically similar documents: ```typescript +import { createAgent } from '@agentuity/runtime'; + const agent = createAgent('SemanticSearch', { handler: async (ctx, input) => { const results = await ctx.vector.search('knowledge-base', { @@ -83,12 +100,13 @@ const agent = createAgent('SemanticSearch', { metadata: { category: 'platform' }, // filter by metadata }); - // Each result includes: id, key, similarity, metadata + // Each result includes: id, key, similarity, metadata, expiresAt return { results: results.map(r => ({ key: r.key, similarity: r.similarity, title: r.metadata?.title, + expiresAt: r.expiresAt, // ISO timestamp when vector expires })), }; }, @@ -102,6 +120,8 @@ const agent = createAgent('SemanticSearch', { Retrieve a specific vector by key without similarity search: ```typescript +import { createAgent } from '@agentuity/runtime'; + const agent = createAgent('DocumentRetriever', { handler: async (ctx, input) => { const result = await ctx.vector.get('knowledge-base', 'doc-1'); @@ -124,6 +144,8 @@ const agent = createAgent('DocumentRetriever', { Retrieve multiple vectors efficiently: ```typescript +import { createAgent } from '@agentuity/runtime'; + const agent = createAgent('BatchRetriever', { handler: async (ctx, input) => { const keys = ['doc-1', 'doc-2', 'doc-3']; @@ -171,6 +193,8 @@ const count = await ctx.vector.delete('knowledge-base', 'doc-1', 'doc-2', 'doc-3 Use generics for type-safe metadata: ```typescript +import { createAgent } from '@agentuity/runtime'; + interface DocumentMetadata { title: string; category: 'guide' | 'api' | 'tutorial'; From d9683f9a086bf2e5c9987f6be5c40878c616343f Mon Sep 17 00:00:00 2001 From: Parteek Singh Date: Fri, 30 Jan 2026 17:51:40 -0800 Subject: [PATCH 8/9] Clean up --- content/Reference/CLI/ai-commands.mdx | 2 +- content/Reference/CLI/deployment.mdx | 12 +- content/Reference/CLI/development.mdx | 8 +- content/Reference/CLI/opencode-plugin.mdx | 40 ++--- content/Services/Sandbox/index.mdx | 2 +- content/Services/Storage/meta.json | 2 +- content/Services/Storage/queue.mdx | 171 ---------------------- content/Services/queues.mdx | 98 +++++++++++++ 8 files changed, 127 insertions(+), 208 deletions(-) delete mode 100644 content/Services/Storage/queue.mdx diff --git a/content/Reference/CLI/ai-commands.mdx b/content/Reference/CLI/ai-commands.mdx index f151e0b5..53ad2a4f 100644 --- a/content/Reference/CLI/ai-commands.mdx +++ b/content/Reference/CLI/ai-commands.mdx @@ -135,7 +135,7 @@ The plugin provides specialized agents, slash commands, and MCP servers for Agen ## Cadence Commands -Manage long-running AI tasks with the cadence system: +Manage long-running AI tasks with the cadence system. See [OpenCode Plugin](/Reference/CLI/opencode-plugin#cadence-system) for details on how Cadence integrates with the agent workflow. ```bash # List all cadence tasks diff --git a/content/Reference/CLI/deployment.mdx b/content/Reference/CLI/deployment.mdx index 2b24e251..0b832f10 100644 --- a/content/Reference/CLI/deployment.mdx +++ b/content/Reference/CLI/deployment.mdx @@ -146,7 +146,7 @@ Build Information ``` -When custom domains are configured, the `show` command displays the required DNS records. Use this to verify your CNAME configuration. +When custom domains are configured, the `show` command displays the required DNS records for each domain. Use this to verify your CNAME configuration. ## Viewing Logs @@ -256,6 +256,8 @@ Once enabled, every pull request to your connected repository automatically depl - Share preview links with reviewers - Run integration tests against the preview deployment +Preview environments are automatically cleaned up when the pull request is closed or merged. + Preview environments require a connected GitHub repository. Connect your repo in **Project > Settings > GitHub**. @@ -402,12 +404,16 @@ Machine details include status, region, resource allocation, and uptime. Use mac - Debug performance issues - Clean up unused machines + +Deleting a machine immediately terminates any deployments running on it. Only delete machines that are unused or when intentionally removing capacity. + + ## Deploy Options | Option | Description | |--------|-------------| -| `-y, --confirm` | Confirm region change without prompting | -| `--dry-run` | Simulate deployment without executing (global option) | +| `-y, --confirm` | Skip all confirmation prompts (useful for CI/CD) | +| `--dry-run` | Simulate deployment without executing (place before `deploy`) | | `--log-level debug` | Show verbose output | | `--project-id ` | Deploy specific project | | `--message ` | Message to associate with this build | diff --git a/content/Reference/CLI/development.mdx b/content/Reference/CLI/development.mdx index dd58dec0..431b79a6 100644 --- a/content/Reference/CLI/development.mdx +++ b/content/Reference/CLI/development.mdx @@ -64,17 +64,19 @@ agentuity dev --inspect-wait agentuity dev --inspect-brk ``` -After starting with `--inspect`, open `chrome://inspect` in Chrome or use VS Code's debugger to attach. The inspector listens on port 9229 by default. +Bun dynamically selects an available port and prints it to the console. Check the output for the debugger URL and port number. + +After starting, open `chrome://inspect` in Chrome or use VS Code's debugger to attach. -Create a launch configuration that attaches to the running process: +Create a launch configuration that attaches to the running process. Update the port to match the one shown in the console output: ```json { "type": "node", "request": "attach", "name": "Attach to Agentuity", - "port": 9229 + "port": 6499 } ``` diff --git a/content/Reference/CLI/opencode-plugin.mdx b/content/Reference/CLI/opencode-plugin.mdx index d0b983af..76fde533 100644 --- a/content/Reference/CLI/opencode-plugin.mdx +++ b/content/Reference/CLI/opencode-plugin.mdx @@ -56,33 +56,7 @@ Use **Builder** when working interactively, making quick changes, or needing gui ### Model Configuration -Override any agent's model via `opencode.json`: - -```json -{ - "agent": { - "Agentuity Coder Builder": { - "model": "anthropic/claude-sonnet-4-5-20250514", - "temperature": 0.7 - }, - "Agentuity Coder Architect": { - "model": "openai/gpt-5.2-codex", - "reasoningEffort": "xhigh" - } - } -} -``` - -**Configuration options:** - -| Option | Description | -|--------|-------------| -| `model` | Model identifier (e.g., `anthropic/claude-sonnet-4-5-20250514`) | -| `temperature` | Number between 0-1 (lower = more deterministic) | -| `reasoningEffort` | For OpenAI: `low`, `medium`, `high`, `xhigh` | -| `variant` | For Anthropic: `low`, `medium`, `high`, `max` (extended thinking) | -| `thinking` | For Anthropic: `{ "type": "enabled", "budgetTokens": 10000 }` | -| `maxSteps` | Maximum tool use steps per turn | +Override any agent's model in `opencode.json`. See [Agent Model Overrides](#agent-model-overrides) for full configuration options. Run `opencode models` to see all available models. @@ -223,7 +197,8 @@ Override any agent's model in `opencode.json`: { "agent": { "Agentuity Coder Builder": { - "model": "anthropic/claude-sonnet-4-5-20250514" + "model": "anthropic/claude-sonnet-4-5-20250514", + "temperature": 0.7 }, "Agentuity Coder Architect": { "model": "openai/gpt-5.2-codex", @@ -233,6 +208,15 @@ Override any agent's model in `opencode.json`: } ``` +| Option | Description | +|--------|-------------| +| `model` | Model identifier (e.g., `anthropic/claude-sonnet-4-5-20250514`) | +| `temperature` | Number between 0-1 (lower = more deterministic) | +| `reasoningEffort` | For OpenAI: `low`, `medium`, `high`, `xhigh` | +| `variant` | For Anthropic: `low`, `medium`, `high`, `max` (extended thinking) | +| `thinking` | For Anthropic: `{ "type": "enabled", "budgetTokens": 10000 }` | +| `maxSteps` | Maximum tool use steps per turn | + ### MCP Server Configuration Add MCP servers for enhanced agent capabilities in `opencode.json`: diff --git a/content/Services/Sandbox/index.mdx b/content/Services/Sandbox/index.mdx index d0c4cb01..f8156230 100644 --- a/content/Services/Sandbox/index.mdx +++ b/content/Services/Sandbox/index.mdx @@ -17,7 +17,7 @@ Agentuity sandboxes handle this automatically. Every execution runs in an isolat - **Security by default**: Network disabled, filesystem isolated, resource limits enforced - **No infrastructure management**: Containers spin up and tear down automatically -- **Multi-language support**: Your agents are TypeScript, but sandboxes can run Python, Node.js, shell scripts, or anything available via `apt install` +- **Multi-language support**: Run Python, Node.js, shell scripts, and more - **Consistent environments**: Use snapshots to get the same setup every time, with dependencies pre-installed ## Three Ways to Use Sandboxes diff --git a/content/Services/Storage/meta.json b/content/Services/Storage/meta.json index 0f996d6c..9ce78fef 100644 --- a/content/Services/Storage/meta.json +++ b/content/Services/Storage/meta.json @@ -1,4 +1,4 @@ { "title": "Storage", - "pages": ["key-value", "vector", "object", "database", "queue", "durable-streams", "custom"] + "pages": ["key-value", "vector", "object", "database", "durable-streams", "custom"] } diff --git a/content/Services/Storage/queue.mdx b/content/Services/Storage/queue.mdx deleted file mode 100644 index 23e71c0e..00000000 --- a/content/Services/Storage/queue.mdx +++ /dev/null @@ -1,171 +0,0 @@ ---- -title: Queues -description: Publish and process messages asynchronously with durable message queues ---- - -Queues enable asynchronous message processing between agents. Use them to decouple work, handle spikes in load, and build reliable event-driven architectures. - -## When to Use Queues - -| Storage Type | Best For | -|--------------|----------| -| **Queue** | Async processing, event-driven workflows, load balancing | -| [Key-Value](/Services/Storage/key-value) | Fast lookups, caching, configuration | -| [Vector](/Services/Storage/vector) | Semantic search, embeddings, RAG | -| [Object (S3)](/Services/Storage/object) | Files, images, documents, media | -| [Database](/Services/Storage/database) | Structured data, complex queries, transactions | -| [Durable Streams](/Services/Storage/durable-streams) | Large exports, audit logs | - -## Access Patterns - -| Context | Access | Details | -|---------|--------|---------| -| Agents | `ctx.queue` | See examples below | -| Routes | `c.var.queue` | See [Using in Routes](#using-in-routes) | -| Standalone | `createAgentContext()` | See [Standalone Usage](#standalone-usage) | - - -The Queue API is identical in all contexts. `ctx.queue.publish()` and `c.var.queue.publish()` work the same way. - - -## Publishing Messages - -Send messages to a queue for async processing: - -```typescript -import { createAgent } from '@agentuity/runtime'; - -const agent = createAgent('OrderProcessor', { - handler: async (ctx, input) => { - // Publish order for async processing - await ctx.queue.publish('orders', { - orderId: input.orderId, - items: input.items, - customerId: input.customerId, - }); - - ctx.logger.info('Order queued', { orderId: input.orderId }); - return { status: 'queued', orderId: input.orderId }; - }, -}); -``` - -Messages are delivered at least once to consumers subscribed to the queue. - -## Synchronous Publishing - -Use `sync: true` when you need to wait for the message to be acknowledged by a consumer: - -```typescript -import { createAgent } from '@agentuity/runtime'; - -const agent = createAgent('CriticalProcessor', { - handler: async (ctx, input) => { - // Wait for message to be processed - const result = await ctx.queue.publish('critical-tasks', { - taskId: input.taskId, - payload: input.data, - }, { - sync: true, // blocks until consumer acknowledges - }); - - ctx.logger.info('Task processed synchronously', { taskId: input.taskId }); - return { status: 'completed', result }; - }, -}); -``` - - -Synchronous publishing blocks until the message is processed. Use it only when you need confirmation that work completed. For most use cases, async publishing is faster and more resilient. - - -## Publishing with Options - -Customize message delivery with additional options: - -```typescript -await ctx.queue.publish('notifications', { - userId: input.userId, - message: input.message, -}, { - metadata: { // attach metadata for routing or filtering - source: 'api', - version: '2', - }, - partitionKey: input.userId, // ensure ordering for this user - idempotencyKey: input.messageId, // prevent duplicate processing - ttl: 3600, // message expires after 1 hour -}); -``` - -## Using in Routes - -Routes have the same queue access via `c.var.queue`: - -```typescript -import { createRouter } from '@agentuity/runtime'; - -const router = createRouter(); - -router.post('/webhook/stripe', async (c) => { - const event = await c.req.json(); - - // Queue webhook for async processing - await c.var.queue.publish('stripe-webhooks', { - type: event.type, - data: event.data, - }); - - // Return 200 immediately (Stripe expects fast responses) - return c.json({ received: true }); -}); - -export default router; -``` - - -Use queues for webhooks that need quick acknowledgment. Return 200 immediately, then process the payload asynchronously. - - -## Standalone Usage - -Use queues from background jobs with `createAgentContext()`: - -```typescript -import { createApp, createAgentContext } from '@agentuity/runtime'; - -await createApp(); - -// Scheduled job to send daily reports -async function sendDailyReports() { - const ctx = createAgentContext({ trigger: 'cron' }); - - await ctx.invoke(async () => { - const users = await getActiveUsers(); - - for (const user of users) { - await ctx.queue.publish('email-reports', { - userId: user.id, - reportType: 'daily', - }); - } - - ctx.logger.info('Queued daily reports', { count: users.length }); - }); -} -``` - -See [Running Agents Without HTTP](/Agents/standalone-execution) for more patterns. - -## Best Practices - -- **Use async by default**: Only use `sync: true` when you need confirmation -- **Keep messages small**: Store large payloads in Object Storage, pass references in messages -- **Handle failures**: Consumers should handle retries gracefully -- **Use metadata**: Add context for routing, debugging, and filtering - -## Next Steps - -- [Key-Value Storage](/Services/Storage/key-value): Fast caching and configuration -- [Durable Streams](/Services/Storage/durable-streams): Large data exports -- [Background Tasks](/Learn/Cookbook/Patterns/background-tasks): Patterns for async processing diff --git a/content/Services/queues.mdx b/content/Services/queues.mdx index 20f6ba2d..deb4fb82 100644 --- a/content/Services/queues.mdx +++ b/content/Services/queues.mdx @@ -20,6 +20,18 @@ Queues enable asynchronous message processing for background tasks, webhooks, an - Handle bursty workloads with rate limiting - Retry failed operations with exponential backoff +## Access Patterns + +| Context | Access | Details | +|---------|--------|---------| +| Agents | `ctx.queue` | See examples below | +| Routes | `c.var.queue` | See [Using in Routes](#using-in-routes) | +| Standalone | `createAgentContext()` | See [Standalone Usage](#standalone-usage) | + + +The Queue API is identical in all contexts. `ctx.queue.publish()` and `c.var.queue.publish()` work the same way. + + ## Queue Types | Type | Behavior | @@ -91,6 +103,92 @@ interface QueuePublishResult { } ``` +### Synchronous Publishing + +Use `sync: true` when you need to wait for the message to be persisted before returning: + +```typescript +import { createAgent } from '@agentuity/runtime'; + +const agent = createAgent('CriticalProcessor', { + handler: async (ctx, input) => { + // Wait for message to be persisted + const result = await ctx.queue.publish('critical-tasks', { + taskId: input.taskId, + payload: input.data, + }, { + sync: true, + }); + + ctx.logger.info('Task queued synchronously', { taskId: input.taskId }); + return { status: 'queued', messageId: result.id }; + }, +}); +``` + + +Synchronous publishing blocks until the message is persisted. Use it only when you need confirmation that the message was accepted. For most use cases, async publishing is faster and more resilient. + + +## Using in Routes + +Routes have the same queue access via `c.var.queue`: + +```typescript +import { createRouter } from '@agentuity/runtime'; + +const router = createRouter(); + +router.post('/webhook/stripe', async (c) => { + const event = await c.req.json(); + + // Queue webhook for async processing + await c.var.queue.publish('stripe-webhooks', { + type: event.type, + data: event.data, + }); + + // Return 200 immediately (Stripe expects fast responses) + return c.json({ received: true }); +}); + +export default router; +``` + + +Use queues for webhooks that need quick acknowledgment. Return 200 immediately, then process the payload asynchronously. + + +## Standalone Usage + +Use queues from background jobs with `createAgentContext()`: + +```typescript +import { createApp, createAgentContext } from '@agentuity/runtime'; + +await createApp(); + +// Scheduled job to send daily reports +async function sendDailyReports() { + const ctx = createAgentContext({ trigger: 'cron' }); + + await ctx.invoke(async () => { + const users = await getActiveUsers(); + + for (const user of users) { + await ctx.queue.publish('email-reports', { + userId: user.id, + reportType: 'daily', + }); + } + + ctx.logger.info('Queued daily reports', { count: users.length }); + }); +} +``` + +See [Running Agents Without HTTP](/Agents/standalone-execution) for more patterns. + ## Error Handling ```typescript From 2070fc6da2dc61d0ee1679704714e3a8e862d353 Mon Sep 17 00:00:00 2001 From: Parteek Singh Date: Fri, 30 Jan 2026 17:59:18 -0800 Subject: [PATCH 9/9] Fixes --- content/Reference/CLI/ai-commands.mdx | 21 ++------------------- content/Reference/CLI/opencode-plugin.mdx | 2 +- 2 files changed, 3 insertions(+), 20 deletions(-) diff --git a/content/Reference/CLI/ai-commands.mdx b/content/Reference/CLI/ai-commands.mdx index 53ad2a4f..0f1b4462 100644 --- a/content/Reference/CLI/ai-commands.mdx +++ b/content/Reference/CLI/ai-commands.mdx @@ -135,26 +135,9 @@ The plugin provides specialized agents, slash commands, and MCP servers for Agen ## Cadence Commands -Manage long-running AI tasks with the cadence system. See [OpenCode Plugin](/Reference/CLI/opencode-plugin#cadence-system) for details on how Cadence integrates with the agent workflow. +Manage long-running AI tasks with the cadence system. Tasks persist across sessions, allowing complex operations to continue in the background. -```bash -# List all cadence tasks -agentuity ai cadence list - -# Check task status -agentuity ai cadence status - -# Pause a running task -agentuity ai cadence pause - -# Resume a paused task -agentuity ai cadence resume - -# Stop a task -agentuity ai cadence stop -``` - -Cadence tasks persist across sessions, allowing complex operations to continue in the background. +See [OpenCode Plugin: Cadence System](/Reference/CLI/opencode-plugin#cadence-system) for commands and integration details. ## Next Steps diff --git a/content/Reference/CLI/opencode-plugin.mdx b/content/Reference/CLI/opencode-plugin.mdx index 76fde533..4cf1f147 100644 --- a/content/Reference/CLI/opencode-plugin.mdx +++ b/content/Reference/CLI/opencode-plugin.mdx @@ -148,7 +148,7 @@ Background tasks are rate-limited to prevent overwhelming providers. Configure i coder: background: enabled: true - defaultConcurrency: 3 + defaultConcurrency: 3 # Override the default of 1 ``` | Option | Default | Description |