+
WebRTC Data Channel Test
+
+
+
+
+
+
+
+
+
+
+
+ {state === 'idle' ? (
+
+ ) : (
+
+ )}
+
+
+
+
+ State: {state}
+
+
+ My Peer ID: {peerId || 'N/A'}
+
+
+ Remote Peers:{' '}
+ {remotePeerIds.length > 0 ? remotePeerIds.join(', ') : 'Waiting...'}
+
+
+ Data Channel: {dataChannelOpen ? 'Open' : 'Closed'}
+
+ {error && (
+
+ Error: {error}
+
+ )}
+
+
+ {/* Cursor Tracking Canvas */}
+ {state !== 'idle' && (
+
+
Cursor Tracking
+
+ Move your mouse over the canvas to share your cursor position with peers.
+ {remoteCursors.size > 0 && ` (${remoteCursors.size} remote cursor${remoteCursors.size > 1 ? 's' : ''})`}
+
+
+
+ )}
+
+ {/* Video Section */}
+ {(enableVideo || enableAudio) && state !== 'idle' && (
+
+
Media
+
+ {/* Local Video */}
+
+
You
+
+
+ {enableAudio && (
+
+ )}
+ {enableVideo && (
+
+ )}
+
+
+
+ {/* Remote Videos */}
+ {remotePeerIds.map((remotePeerId) => (
+
+
+ {remotePeerId.slice(0, 15)}...
+
+
+ ))}
+
+
+ )}
+
+ {dataChannelOpen && (
+
+
+ setInputMessage(e.target.value)}
+ onKeyDown={(e) => e.key === 'Enter' && sendMessage()}
+ placeholder="Type a message..."
+ data-testid="message-input"
+ style={{ flex: 1, padding: '0.5rem' }}
+ />
+
+
+
+
+ )}
+
+
+
Messages
+ {messages.length === 0 ? (
+
No messages yet
+ ) : (
+ messages.map((msg, i) => (
+
+ {msg.from === 'local' ? 'You' : `Remote (${msg.peerId})`}:{' '}
+ {msg.data}
+
+ ))
+ )}
+
+
+ );
+}
diff --git a/apps/testing/integration-suite/src/test/cli-env-secrets.ts b/apps/testing/integration-suite/src/test/cli-env-secrets.ts
index 07fddeadd..5f2375a9c 100644
--- a/apps/testing/integration-suite/src/test/cli-env-secrets.ts
+++ b/apps/testing/integration-suite/src/test/cli-env-secrets.ts
@@ -134,9 +134,7 @@ test('cli-env-secrets', 'env-set-allows-whitelisted-agentuity-auth-secret', asyn
// Should succeed or at least get past validation
// Note: Due to _SECRET suffix, auto-detection may prompt and store as secret (default Y in non-TTY)
assert(
- output.includes('set successfully') ||
- output.includes('Setting') ||
- result.success === true,
+ output.includes('set successfully') || output.includes('Setting') || result.success === true,
`Should allow AGENTUITY_AUTH_SECRET: ${output}`
);
diff --git a/apps/testing/integration-suite/src/test/cli-org-env-secrets.ts b/apps/testing/integration-suite/src/test/cli-org-env-secrets.ts
index cdbf6f59b..d05d1500d 100644
--- a/apps/testing/integration-suite/src/test/cli-org-env-secrets.ts
+++ b/apps/testing/integration-suite/src/test/cli-org-env-secrets.ts
@@ -36,8 +36,10 @@ test('cli-org-env-secrets', 'org-env-set-creates-variable', async () => {
});
const output = (result.stdout || '') + (result.stderr || '');
- console.log(`[DEBUG] Set result - exitCode: ${result.exitCode}, stdout: ${result.stdout?.slice(0, 200)}, stderr: ${result.stderr?.slice(0, 200)}`);
-
+ console.log(
+ `[DEBUG] Set result - exitCode: ${result.exitCode}, stdout: ${result.stdout?.slice(0, 200)}, stderr: ${result.stderr?.slice(0, 200)}`
+ );
+
assert(
Boolean(result.success || output.includes('set successfully')),
`Org env set should succeed: ${output}`
@@ -94,10 +96,12 @@ test('cli-org-env-secrets', 'org-env-get-retrieves-variable', async () => {
const setResult = await cliAgent.run({
command: `cloud env set ${testKey} ${testValue} --org`,
});
- console.log(`[DEBUG] get-test: Set result - exitCode: ${setResult.exitCode}, success: ${setResult.success}`);
+ console.log(
+ `[DEBUG] get-test: Set result - exitCode: ${setResult.exitCode}, success: ${setResult.success}`
+ );
console.log(`[DEBUG] get-test: Set stdout: ${setResult.stdout?.slice(0, 300)}`);
console.log(`[DEBUG] get-test: Set stderr: ${setResult.stderr?.slice(0, 300)}`);
-
+
assert(setResult.success, `Set should succeed before get: ${setResult.stderr}`);
// Get the variable
@@ -105,7 +109,9 @@ test('cli-org-env-secrets', 'org-env-get-retrieves-variable', async () => {
const getResult = await cliAgent.run({
command: `cloud env get ${testKey} --org`,
});
- console.log(`[DEBUG] get-test: Get result - exitCode: ${getResult.exitCode}, success: ${getResult.success}`);
+ console.log(
+ `[DEBUG] get-test: Get result - exitCode: ${getResult.exitCode}, success: ${getResult.success}`
+ );
console.log(`[DEBUG] get-test: Get stdout: ${getResult.stdout?.slice(0, 300)}`);
console.log(`[DEBUG] get-test: Get stderr: ${getResult.stderr?.slice(0, 300)}`);
@@ -132,9 +138,11 @@ test('cli-org-env-secrets', 'org-env-list-shows-variables', async () => {
const setResult = await cliAgent.run({
command: `cloud env set ${testKey} ${testValue} --org`,
});
- console.log(`[DEBUG] Set result - exitCode: ${setResult.exitCode}, stdout: ${setResult.stdout?.slice(0, 300)}`);
+ console.log(
+ `[DEBUG] Set result - exitCode: ${setResult.exitCode}, stdout: ${setResult.stdout?.slice(0, 300)}`
+ );
console.log(`[DEBUG] Set stderr: ${setResult.stderr?.slice(0, 300)}`);
-
+
assert(setResult.success, `Set should succeed before list: ${setResult.stderr}`);
// List org variables
@@ -142,7 +150,9 @@ test('cli-org-env-secrets', 'org-env-list-shows-variables', async () => {
const listResult = await cliAgent.run({
command: 'cloud env list --org',
});
- console.log(`[DEBUG] List result - exitCode: ${listResult.exitCode}, stdout: ${listResult.stdout?.slice(0, 500)}`);
+ console.log(
+ `[DEBUG] List result - exitCode: ${listResult.exitCode}, stdout: ${listResult.stdout?.slice(0, 500)}`
+ );
const listOutput = (listResult.stdout || '') + (listResult.stderr || '');
assert(listOutput.includes(testKey), `Should list key: ${listOutput}`);
@@ -166,20 +176,26 @@ test('cli-org-env-secrets', 'org-env-delete-removes-variable', async () => {
const setResult = await cliAgent.run({
command: `cloud env set ${testKey} ${testValue} --org`,
});
- console.log(`[DEBUG] delete-test: Set result - exitCode: ${setResult.exitCode}, success: ${setResult.success}, stdout: ${setResult.stdout?.slice(0, 200)}`);
+ console.log(
+ `[DEBUG] delete-test: Set result - exitCode: ${setResult.exitCode}, success: ${setResult.success}, stdout: ${setResult.stdout?.slice(0, 200)}`
+ );
// Verify the variable was set before trying to delete
const verifyResult = await cliAgent.run({
command: `cloud env get ${testKey} --org`,
});
- console.log(`[DEBUG] delete-test: Verify after set - exitCode: ${verifyResult.exitCode}, success: ${verifyResult.success}`);
-
+ console.log(
+ `[DEBUG] delete-test: Verify after set - exitCode: ${verifyResult.exitCode}, success: ${verifyResult.success}`
+ );
+
// Delete the variable
console.log(`[DEBUG] delete-test: Deleting ${testKey}`);
const deleteResult = await cliAgent.run({
command: `cloud env delete ${testKey} --org`,
});
- console.log(`[DEBUG] delete-test: Delete result - exitCode: ${deleteResult.exitCode}, success: ${deleteResult.success}, stdout: ${deleteResult.stdout?.slice(0, 200)}`);
+ console.log(
+ `[DEBUG] delete-test: Delete result - exitCode: ${deleteResult.exitCode}, success: ${deleteResult.success}, stdout: ${deleteResult.stdout?.slice(0, 200)}`
+ );
const deleteOutput = (deleteResult.stdout || '') + (deleteResult.stderr || '');
assert(
@@ -207,9 +223,11 @@ test('cli-org-env-secrets', 'org-env-list-no-mask-shows-secrets', async () => {
const setResult = await cliAgent.run({
command: `cloud env set ${testKey} ${testValue} --secret --org`,
});
- console.log(`[DEBUG] no-mask-test: Set result - exitCode: ${setResult.exitCode}, success: ${setResult.success}`);
+ console.log(
+ `[DEBUG] no-mask-test: Set result - exitCode: ${setResult.exitCode}, success: ${setResult.success}`
+ );
console.log(`[DEBUG] no-mask-test: Set stdout: ${setResult.stdout?.slice(0, 300)}`);
-
+
assert(setResult.success, `Set secret should succeed: ${setResult.stderr}`);
// List with --no-mask
@@ -594,17 +612,21 @@ test('cli-org-env-secrets', 'org-env-list-env-only-filter', async () => {
const setEnvResult = await cliAgent.run({
command: `cloud env set ${envKey} env_value --org`,
});
- console.log(`[DEBUG] env-only-test: Set env result - exitCode: ${setEnvResult.exitCode}, success: ${setEnvResult.success}`);
+ console.log(
+ `[DEBUG] env-only-test: Set env result - exitCode: ${setEnvResult.exitCode}, success: ${setEnvResult.success}`
+ );
console.log(`[DEBUG] env-only-test: Set env stdout: ${setEnvResult.stdout?.slice(0, 300)}`);
-
+
assert(setEnvResult.success, `Set env var should succeed: ${setEnvResult.stderr}`);
-
+
console.log(`[DEBUG] env-only-test: Setting secret ${secretKey}`);
const setSecretResult = await cliAgent.run({
command: `cloud env set ${secretKey} secret_value --secret --org`,
});
- console.log(`[DEBUG] env-only-test: Set secret result - exitCode: ${setSecretResult.exitCode}, success: ${setSecretResult.success}`);
-
+ console.log(
+ `[DEBUG] env-only-test: Set secret result - exitCode: ${setSecretResult.exitCode}, success: ${setSecretResult.success}`
+ );
+
assert(setSecretResult.success, `Set secret should succeed: ${setSecretResult.stderr}`);
// List with --env-only filter
@@ -614,7 +636,7 @@ test('cli-org-env-secrets', 'org-env-list-env-only-filter', async () => {
});
console.log(`[DEBUG] env-only-test: List result - exitCode: ${listResult.exitCode}`);
console.log(`[DEBUG] env-only-test: List stdout: ${listResult.stdout?.slice(0, 500)}`);
-
+
const listOutput = (listResult.stdout || '') + (listResult.stderr || '');
assert(listOutput.includes(envKey), `Should include env var: ${listOutput}`);
diff --git a/apps/testing/webrtc-test/.agents/agentuity/sdk/agent/AGENTS.md b/apps/testing/webrtc-test/.agents/agentuity/sdk/agent/AGENTS.md
new file mode 100644
index 000000000..3c5330d3c
--- /dev/null
+++ b/apps/testing/webrtc-test/.agents/agentuity/sdk/agent/AGENTS.md
@@ -0,0 +1,308 @@
+# Agents Folder Guide
+
+This folder contains AI agents for your Agentuity application. Each agent is organized in its own subdirectory.
+
+## Generated Types
+
+The `src/generated/` folder contains auto-generated TypeScript files:
+
+- `registry.ts` - Agent registry with strongly-typed agent definitions and schema types
+- `routes.ts` - Route registry for API, WebSocket, and SSE endpoints
+- `app.ts` - Application entry point (regenerated on every build)
+
+**Important:** Never edit files in `src/generated/` - they are overwritten on every build.
+
+Import generated types in your agents:
+
+```typescript
+import type { HelloInput, HelloOutput } from '../generated/registry';
+```
+
+## Directory Structure
+
+Each agent folder must contain:
+
+- **agent.ts** (required) - Agent definition with schema and handler
+
+Example structure:
+
+```
+src/agent/
+├── hello/
+│ └── agent.ts
+├── process-data/
+│ └── agent.ts
+└── (generated files in src/generated/)
+```
+
+**Note:** HTTP routes are defined separately in `src/api/` - see the API folder guide for details.
+
+## Creating an Agent
+
+### Basic Agent (agent.ts)
+
+```typescript
+import { createAgent } from '@agentuity/runtime';
+import { s } from '@agentuity/schema';
+
+const agent = createAgent('my-agent', {
+ description: 'What this agent does',
+ schema: {
+ input: s.object({
+ name: s.string(),
+ age: s.number(),
+ }),
+ output: s.string(),
+ },
+ handler: async (ctx, input) => {
+ // Access context: ctx.app, ctx.config, ctx.logger, ctx.kv, ctx.vector, ctx.stream
+ return `Hello, ${input.name}! You are ${input.age} years old.`;
+ },
+});
+
+export default agent;
+```
+
+### Agent with Lifecycle (setup/shutdown)
+
+```typescript
+import { createAgent } from '@agentuity/runtime';
+import { s } from '@agentuity/schema';
+
+const agent = createAgent('lifecycle-agent', {
+ description: 'Agent with setup and shutdown',
+ schema: {
+ input: s.object({ message: s.string() }),
+ output: s.object({ result: s.string() }),
+ },
+ setup: async (app) => {
+ // Initialize resources (runs once on startup)
+ // app contains: appName, version, startedAt, config
+ return {
+ agentId: `agent-${Math.random().toString(36).substr(2, 9)}`,
+ connectionPool: ['conn-1', 'conn-2'],
+ };
+ },
+ handler: async (ctx, input) => {
+ // Access setup config via ctx.config (fully typed)
+ ctx.logger.info('Agent ID:', ctx.config.agentId);
+ ctx.logger.info('Connections:', ctx.config.connectionPool);
+ return { result: `Processed: ${input.message}` };
+ },
+ shutdown: async (app, config) => {
+ // Cleanup resources (runs on shutdown)
+ console.log('Shutting down agent:', config.agentId);
+ },
+});
+
+export default agent;
+```
+
+### Agent with Event Listeners
+
+```typescript
+import { createAgent } from '@agentuity/runtime';
+import { s } from '@agentuity/schema';
+
+const agent = createAgent('event-agent', {
+ schema: {
+ input: s.object({ data: s.string() }),
+ output: s.string(),
+ },
+ handler: async (ctx, input) => {
+ return `Processed: ${input.data}`;
+ },
+});
+
+agent.addEventListener('started', (eventName, agent, ctx) => {
+ ctx.logger.info('Agent started');
+});
+
+agent.addEventListener('completed', (eventName, agent, ctx) => {
+ ctx.logger.info('Agent completed');
+});
+
+agent.addEventListener('errored', (eventName, agent, ctx, error) => {
+ ctx.logger.error('Agent errored:', error);
+});
+
+export default agent;
+```
+
+## Agent Context (ctx)
+
+The handler receives a context object with:
+
+- **ctx.app** - Application state (appName, version, startedAt, config from createApp)
+- **ctx.config** - Agent-specific config (from setup return value, fully typed)
+- **ctx.logger** - Structured logger (info, warn, error, debug, trace)
+- **ctx.tracer** - OpenTelemetry tracer for custom spans
+- **ctx.sessionId** - Unique session identifier
+- **ctx.kv** - Key-value storage
+- **ctx.vector** - Vector storage for embeddings
+- **ctx.stream** - Stream storage for real-time data
+- **ctx.state** - In-memory request-scoped state (Map)
+- **ctx.thread** - Thread information for multi-turn conversations
+- **ctx.session** - Session information
+- **ctx.waitUntil** - Schedule background tasks
+
+## Examples
+
+### Using Key-Value Storage
+
+```typescript
+handler: async (ctx, input) => {
+ await ctx.kv.set('user:123', { name: 'Alice', age: 30 });
+ const user = await ctx.kv.get('user:123');
+ await ctx.kv.delete('user:123');
+ const keys = await ctx.kv.list('user:*');
+ return user;
+};
+```
+
+### Using Vector Storage
+
+```typescript
+handler: async (ctx, input) => {
+ await ctx.vector.upsert('docs', [
+ { id: '1', values: [0.1, 0.2, 0.3], metadata: { text: 'Hello' } },
+ ]);
+ const results = await ctx.vector.query('docs', [0.1, 0.2, 0.3], { topK: 5 });
+ return results;
+};
+```
+
+### Using Streams
+
+```typescript
+handler: async (ctx, input) => {
+ const stream = await ctx.stream.create('agent-logs');
+ await ctx.stream.write(stream.id, 'Processing step 1');
+ await ctx.stream.write(stream.id, 'Processing step 2');
+ return { streamId: stream.id };
+};
+```
+
+### Background Tasks with waitUntil
+
+```typescript
+handler: async (ctx, input) => {
+ // Schedule background work that continues after response
+ ctx.waitUntil(async () => {
+ await ctx.kv.set('processed', Date.now());
+ ctx.logger.info('Background task complete');
+ });
+
+ return { status: 'processing' };
+};
+```
+
+### Calling Another Agent
+
+```typescript
+// Import the agent directly
+import otherAgent from '../other-agent/agent';
+
+handler: async (ctx, input) => {
+ const result = await otherAgent.run({ data: input.value });
+ return `Other agent returned: ${result}`;
+};
+```
+
+## Subagents (Nested Agents)
+
+Agents can have subagents organized one level deep. This is useful for grouping related functionality.
+
+### Directory Structure for Subagents
+
+```
+src/agent/
+└── team/ # Parent agent
+ ├── agent.ts # Parent agent
+ ├── members/ # Subagent
+ │ └── agent.ts
+ └── tasks/ # Subagent
+ └── agent.ts
+```
+
+### Parent Agent
+
+```typescript
+import { createAgent } from '@agentuity/runtime';
+import { s } from '@agentuity/schema';
+
+const agent = createAgent('team', {
+ description: 'Team Manager',
+ schema: {
+ input: s.object({ action: s.union([s.literal('info'), s.literal('count')]) }),
+ output: s.object({
+ message: s.string(),
+ timestamp: s.string(),
+ }),
+ },
+ handler: async (ctx, { action }) => {
+ return {
+ message: 'Team parent agent - manages members and tasks',
+ timestamp: new Date().toISOString(),
+ };
+ },
+});
+
+export default agent;
+```
+
+### Subagent (Accessing Parent)
+
+```typescript
+import { createAgent } from '@agentuity/runtime';
+import { s } from '@agentuity/schema';
+import parentAgent from '../agent';
+
+const agent = createAgent('team.members', {
+ description: 'Members Subagent',
+ schema: {
+ input: s.object({
+ action: s.union([s.literal('list'), s.literal('add'), s.literal('remove')]),
+ name: s.optional(s.string()),
+ }),
+ output: s.object({
+ members: s.array(s.string()),
+ parentInfo: s.optional(s.string()),
+ }),
+ },
+ handler: async (ctx, { action, name }) => {
+ // Call parent agent directly
+ const parentResult = await parentAgent.run({ action: 'info' });
+ const parentInfo = `Parent says: ${parentResult.message}`;
+
+ let members = ['Alice', 'Bob'];
+ if (action === 'add' && name) {
+ members.push(name);
+ }
+
+ return { members, parentInfo };
+ },
+});
+
+export default agent;
+```
+
+### Key Points About Subagents
+
+- **One level deep**: Only one level of nesting is supported (no nested subagents)
+- **Access parent**: Import and call parent agents directly
+- **Agent names**: Subagents have dotted names like `"team.members"`
+- **Shared context**: Subagents share the same app context (kv, logger, etc.)
+
+## Rules
+
+- Each agent folder name becomes the agent's route name (e.g., `hello/` → `/agent/hello`)
+- **agent.ts** must export default the agent instance
+- The first argument to `createAgent()` is the agent name (must match folder structure)
+- Input/output schemas are enforced with @agentuity/schema validation
+- Setup return value type automatically flows to ctx.config (fully typed)
+- Use ctx.logger for logging, not console.log
+- Import agents directly to call them (recommended approach)
+- Subagents are one level deep only (team/members/, not team/members/subagent/)
+
+
diff --git a/apps/testing/webrtc-test/.agents/agentuity/sdk/api/AGENTS.md b/apps/testing/webrtc-test/.agents/agentuity/sdk/api/AGENTS.md
new file mode 100644
index 000000000..e6c32b3fb
--- /dev/null
+++ b/apps/testing/webrtc-test/.agents/agentuity/sdk/api/AGENTS.md
@@ -0,0 +1,367 @@
+# APIs Folder Guide
+
+This folder contains REST API routes for your Agentuity application. Each API is organized in its own subdirectory.
+
+## Generated Types
+
+The `src/generated/` folder contains auto-generated TypeScript files:
+
+- `routes.ts` - Route registry with strongly-typed route definitions and schema types
+- `registry.ts` - Agent registry (for calling agents from routes)
+- `app.ts` - Application entry point (regenerated on every build)
+
+**Important:** Never edit files in `src/generated/` - they are overwritten on every build.
+
+Import generated types in your routes:
+
+```typescript
+import type { POST_Api_UsersInput, POST_Api_UsersOutput } from '../generated/routes';
+```
+
+## Directory Structure
+
+Each API folder must contain:
+
+- **route.ts** (required) - HTTP route definitions using Hono router
+
+Example structure:
+
+```
+src/api/
+├── index.ts (optional, mounted at /api)
+├── status/
+│ └── route.ts (mounted at /api/status)
+├── users/
+│ └── route.ts (mounted at /api/users)
+├── agent-call/
+ └── route.ts (mounted at /api/agent-call)
+```
+
+## Creating an API
+
+### Basic API (route.ts)
+
+```typescript
+import { createRouter } from '@agentuity/runtime';
+
+const router = createRouter();
+
+// GET /api/status
+router.get('/', (c) => {
+ return c.json({
+ status: 'ok',
+ timestamp: new Date().toISOString(),
+ version: '1.0.0',
+ });
+});
+
+// POST /api/status
+router.post('/', async (c) => {
+ const body = await c.req.json();
+ return c.json({ received: body });
+});
+
+export default router;
+```
+
+### API with Request Validation
+
+```typescript
+import { createRouter } from '@agentuity/runtime';
+import { s } from '@agentuity/schema';
+import { validator } from 'hono/validator';
+
+const router = createRouter();
+
+const createUserSchema = s.object({
+ name: s.string(),
+ email: s.string(),
+ age: s.number(),
+});
+
+router.post(
+ '/',
+ validator('json', (value, c) => {
+ const result = createUserSchema['~standard'].validate(value);
+ if (result.issues) {
+ return c.json({ error: 'Validation failed', issues: result.issues }, 400);
+ }
+ return result.value;
+ }),
+ async (c) => {
+ const data = c.req.valid('json');
+ // data is fully typed: { name: string, email: string, age: number }
+ return c.json({
+ success: true,
+ user: data,
+ });
+ }
+);
+
+export default router;
+```
+
+### API Calling Agents
+
+APIs can call agents directly by importing them:
+
+```typescript
+import { createRouter } from '@agentuity/runtime';
+import helloAgent from '@agent/hello';
+
+const router = createRouter();
+
+router.get('/', async (c) => {
+ // Call an agent directly
+ const result = await helloAgent.run({ name: 'API Caller', age: 42 });
+
+ return c.json({
+ success: true,
+ agentResult: result,
+ });
+});
+
+router.post('/with-input', async (c) => {
+ const body = await c.req.json();
+ const { name, age } = body;
+
+ // Call agent with dynamic input
+ const result = await helloAgent.run({ name, age });
+
+ return c.json({
+ success: true,
+ agentResult: result,
+ });
+});
+
+export default router;
+```
+
+### API with Agent Validation
+
+Use `agent.validator()` for automatic input validation from agent schemas:
+
+```typescript
+import { createRouter } from '@agentuity/runtime';
+import myAgent from '@agent/my-agent';
+
+const router = createRouter();
+
+// POST with automatic validation using agent's input schema
+router.post('/', myAgent.validator(), async (c) => {
+ const data = c.req.valid('json'); // Fully typed from agent schema!
+ const result = await myAgent.run(data);
+ return c.json({ success: true, result });
+});
+
+export default router;
+```
+
+### API with Logging
+
+```typescript
+import { createRouter } from '@agentuity/runtime';
+
+const router = createRouter();
+
+router.get('/log-test', (c) => {
+ c.var.logger.info('Info message');
+ c.var.logger.error('Error message');
+ c.var.logger.warn('Warning message');
+ c.var.logger.debug('Debug message');
+ c.var.logger.trace('Trace message');
+
+ return c.text('Check logs');
+});
+
+export default router;
+```
+
+## Route Context (c)
+
+The route handler receives a Hono context object with:
+
+- **c.req** - Request object (c.req.json(), c.req.param(), c.req.query(), etc.)
+- **c.json()** - Return JSON response
+- **c.text()** - Return text response
+- **c.html()** - Return HTML response
+- **c.redirect()** - Redirect to URL
+- **c.var.logger** - Structured logger (info, warn, error, debug, trace)
+- **c.var.kv** - Key-value storage
+- **c.var.vector** - Vector storage
+- **c.var.stream** - Stream management
+- **Import agents directly** - Import and call agents directly (recommended)
+
+## HTTP Methods
+
+```typescript
+const router = createRouter();
+
+router.get('/path', (c) => {
+ /* ... */
+});
+router.post('/path', (c) => {
+ /* ... */
+});
+router.put('/path', (c) => {
+ /* ... */
+});
+router.patch('/path', (c) => {
+ /* ... */
+});
+router.delete('/path', (c) => {
+ /* ... */
+});
+router.options('/path', (c) => {
+ /* ... */
+});
+```
+
+## Path Parameters
+
+```typescript
+// GET /api/users/:id
+router.get('/:id', (c) => {
+ const id = c.req.param('id');
+ return c.json({ userId: id });
+});
+
+// GET /api/posts/:postId/comments/:commentId
+router.get('/:postId/comments/:commentId', (c) => {
+ const postId = c.req.param('postId');
+ const commentId = c.req.param('commentId');
+ return c.json({ postId, commentId });
+});
+```
+
+## Query Parameters
+
+```typescript
+// GET /api/search?q=hello&limit=10
+router.get('/search', (c) => {
+ const query = c.req.query('q');
+ const limit = c.req.query('limit') || '20';
+ return c.json({ query, limit: parseInt(limit) });
+});
+```
+
+## Request Body
+
+```typescript
+// JSON body
+router.post('/', async (c) => {
+ const body = await c.req.json();
+ return c.json({ received: body });
+});
+
+// Form data
+router.post('/upload', async (c) => {
+ const formData = await c.req.formData();
+ const file = formData.get('file');
+ return c.json({ fileName: file?.name });
+});
+```
+
+## Error Handling
+
+```typescript
+import myAgent from '@agent/my-agent';
+
+router.get('/', async (c) => {
+ try {
+ const result = await myAgent.run({ data: 'test' });
+ return c.json({ success: true, result });
+ } catch (error) {
+ c.var.logger.error('Agent call failed:', error);
+ return c.json(
+ {
+ success: false,
+ error: error instanceof Error ? error.message : String(error),
+ },
+ 500
+ );
+ }
+});
+```
+
+## Response Types
+
+```typescript
+// JSON response
+return c.json({ data: 'value' });
+
+// Text response
+return c.text('Hello World');
+
+// HTML response
+return c.html('