@reminix/runtime 0.0.6 → 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @reminix/runtime
2
2
 
3
- Core runtime package for serving AI agents and tools via REST APIs. Provides the `serve()` function, `Agent` class, `tool()` factory, and `BaseAdapter` for building framework integrations.
3
+ Core runtime package for serving AI agents and tools via REST APIs. Provides the `agent()`, `chatAgent()`, and `tool()` factory functions for building and serving AI agents.
4
4
 
5
5
  Built on [Hono](https://hono.dev) for portability across Node.js, Deno, Bun, and edge runtimes.
6
6
 
@@ -15,30 +15,27 @@ npm install @reminix/runtime
15
15
  ## Quick Start
16
16
 
17
17
  ```typescript
18
- import { serve, Agent } from '@reminix/runtime';
18
+ import { agent, chatAgent, serve } from '@reminix/runtime';
19
19
 
20
- // Create an agent with callbacks
21
- const agent = new Agent('my-agent');
22
-
23
- agent.onInvoke(async (request) => {
24
- const task = (request.input as Record<string, string>).task || 'unknown';
25
- return { output: `Completed: ${task}` };
20
+ // Create an agent for task-oriented operations
21
+ const calculator = agent('calculator', {
22
+ description: 'Add two numbers',
23
+ parameters: {
24
+ type: 'object',
25
+ properties: { a: { type: 'number' }, b: { type: 'number' } },
26
+ required: ['a', 'b'],
27
+ },
28
+ execute: async ({ a, b }) => (a as number) + (b as number),
26
29
  });
27
30
 
28
- agent.onChat(async (request) => {
29
- const userMsg = request.messages[request.messages.length - 1].content;
30
- const response = `You said: ${userMsg}`;
31
- return {
32
- output: response,
33
- messages: [
34
- ...request.messages,
35
- { role: 'assistant', content: response },
36
- ],
37
- };
31
+ // Create a chat agent for conversational interactions
32
+ const assistant = chatAgent('assistant', {
33
+ description: 'A helpful assistant',
34
+ execute: async (messages) => `You said: ${messages.at(-1)?.content}`,
38
35
  });
39
36
 
40
- // Serve the agent
41
- serve({ agents: [agent], port: 8080 });
37
+ // Serve the agents
38
+ serve({ agents: [calculator, assistant], port: 8080 });
42
39
  ```
43
40
 
44
41
  ## How It Works
@@ -49,8 +46,7 @@ The runtime creates a REST server (powered by [Hono](https://hono.dev)) with the
49
46
  |----------|--------|-------------|
50
47
  | `/health` | GET | Health check |
51
48
  | `/info` | GET | Runtime discovery (version, agents, tools) |
52
- | `/agents/{name}/invoke` | POST | Stateless invocation |
53
- | `/agents/{name}/chat` | POST | Conversational chat |
49
+ | `/agents/{name}/execute` | POST | Execute an agent |
54
50
  | `/tools/{name}/execute` | POST | Execute a tool |
55
51
 
56
52
  ### Health Endpoint
@@ -67,61 +63,93 @@ Returns `{"status": "ok"}` if the server is running.
67
63
  curl http://localhost:8080/info
68
64
  ```
69
65
 
70
- Returns runtime information and available agents:
66
+ Returns runtime information, available agents, and tools:
71
67
 
72
68
  ```json
73
69
  {
74
70
  "runtime": {
75
71
  "name": "reminix-runtime",
76
- "version": "0.0.6",
72
+ "version": "0.0.8",
77
73
  "language": "typescript",
78
74
  "framework": "hono"
79
75
  },
80
76
  "agents": [
81
77
  {
82
- "name": "my-agent",
83
- "type": "adapter",
84
- "adapter": "langchain",
85
- "invoke": { "streaming": true },
86
- "chat": { "streaming": true }
78
+ "name": "calculator",
79
+ "type": "agent",
80
+ "description": "Add two numbers",
81
+ "parameters": {
82
+ "type": "object",
83
+ "properties": { "a": { "type": "number" }, "b": { "type": "number" } },
84
+ "required": ["a", "b"]
85
+ },
86
+ "output": { "type": "number" },
87
+ "requestKeys": ["a", "b"],
88
+ "responseKeys": ["output"],
89
+ "streaming": false
90
+ },
91
+ {
92
+ "name": "assistant",
93
+ "type": "chat_agent",
94
+ "description": "A helpful assistant",
95
+ "parameters": {
96
+ "type": "object",
97
+ "properties": {
98
+ "messages": {
99
+ "type": "array",
100
+ "items": { "type": "object", "properties": { "role": { "type": "string" }, "content": { "type": "string" } }, "required": ["role", "content"] }
101
+ }
102
+ },
103
+ "required": ["messages"]
104
+ },
105
+ "output": { "type": "object" },
106
+ "requestKeys": ["messages"],
107
+ "responseKeys": ["message"],
108
+ "streaming": false
109
+ }
110
+ ],
111
+ "tools": [
112
+ {
113
+ "name": "get_weather",
114
+ "type": "tool",
115
+ "description": "Get current weather for a location",
116
+ "parameters": { ... },
117
+ "output": { ... }
87
118
  }
88
119
  ]
89
120
  }
90
121
  ```
91
122
 
92
- ### Invoke Endpoint
123
+ ### Agent Execute Endpoint
93
124
 
94
- `POST /agents/{name}/invoke` - For stateless operations.
125
+ `POST /agents/{name}/execute` - Execute an agent.
95
126
 
127
+ Request keys are defined by the agent's `parameters` schema. For example, a calculator agent with `parameters: { properties: { a, b } }` expects `a` and `b` at the top level:
128
+
129
+ **Task-oriented agent:**
96
130
  ```bash
97
- curl -X POST http://localhost:8080/agents/my-agent/invoke \
131
+ curl -X POST http://localhost:8080/agents/calculator/execute \
98
132
  -H "Content-Type: application/json" \
99
- -d '{
100
- "input": {
101
- "task": "summarize",
102
- "text": "Lorem ipsum..."
103
- }
104
- }'
133
+ -d '{"a": 5, "b": 3}'
105
134
  ```
106
135
 
107
136
  **Response:**
108
137
  ```json
109
138
  {
110
- "output": "Summary: ..."
139
+ "output": 8
111
140
  }
112
141
  ```
113
142
 
114
- ### Chat Endpoint
143
+ **Chat agent:**
115
144
 
116
- `POST /agents/{name}/chat` - For conversational interactions.
145
+ Chat agents expect `messages` at the top level and return `message`:
117
146
 
118
147
  ```bash
119
- curl -X POST http://localhost:8080/agents/my-agent/chat \
148
+ curl -X POST http://localhost:8080/agents/assistant/execute \
120
149
  -H "Content-Type: application/json" \
121
150
  -d '{
122
151
  "messages": [
123
- {"role": "system", "content": "You are helpful"},
124
- {"role": "user", "content": "What is the weather?"}
152
+ {"role": "user", "content": "Hello!"}
125
153
  ]
126
154
  }'
127
155
  ```
@@ -129,17 +157,13 @@ curl -X POST http://localhost:8080/agents/my-agent/chat \
129
157
  **Response:**
130
158
  ```json
131
159
  {
132
- "output": "The weather is 72°F and sunny!",
133
- "messages": [
134
- {"role": "system", "content": "You are helpful"},
135
- {"role": "user", "content": "What is the weather?"},
136
- {"role": "assistant", "content": "The weather is 72°F and sunny!"}
137
- ]
160
+ "message": {
161
+ "role": "assistant",
162
+ "content": "You said: Hello!"
163
+ }
138
164
  }
139
165
  ```
140
166
 
141
- The `output` field contains the assistant's response, while `messages` includes the full conversation history.
142
-
143
167
  ### Tool Execute Endpoint
144
168
 
145
169
  `POST /tools/{name}/execute` - Execute a standalone tool.
@@ -147,11 +171,7 @@ The `output` field contains the assistant's response, while `messages` includes
147
171
  ```bash
148
172
  curl -X POST http://localhost:8080/tools/get_weather/execute \
149
173
  -H "Content-Type: application/json" \
150
- -d '{
151
- "input": {
152
- "location": "San Francisco"
153
- }
154
- }'
174
+ -d '{"location": "San Francisco"}'
155
175
  ```
156
176
 
157
177
  **Response:**
@@ -161,9 +181,119 @@ curl -X POST http://localhost:8080/tools/get_weather/execute \
161
181
  }
162
182
  ```
163
183
 
184
+ ## Agents
185
+
186
+ Agents handle requests via the `/agents/{name}/execute` endpoint.
187
+
188
+ ### Task-Oriented Agent
189
+
190
+ Use `agent()` for task-oriented agents that take structured input and return output:
191
+
192
+ ```typescript
193
+ import { agent, serve } from '@reminix/runtime';
194
+
195
+ const calculator = agent('calculator', {
196
+ description: 'Add two numbers',
197
+ parameters: {
198
+ type: 'object',
199
+ properties: {
200
+ a: { type: 'number' },
201
+ b: { type: 'number' },
202
+ },
203
+ required: ['a', 'b'],
204
+ },
205
+ execute: async ({ a, b }) => (a as number) + (b as number),
206
+ });
207
+
208
+ const textProcessor = agent('text-processor', {
209
+ description: 'Process text in various ways',
210
+ parameters: {
211
+ type: 'object',
212
+ properties: {
213
+ text: { type: 'string' },
214
+ operation: { type: 'string', enum: ['uppercase', 'lowercase'] },
215
+ },
216
+ required: ['text'],
217
+ },
218
+ execute: async ({ text, operation }) => {
219
+ const t = text as string;
220
+ return operation === 'uppercase' ? t.toUpperCase() : t.toLowerCase();
221
+ },
222
+ });
223
+
224
+ serve({ agents: [calculator, textProcessor], port: 8080 });
225
+ ```
226
+
227
+ ### Chat Agent
228
+
229
+ Use `chatAgent()` for conversational agents that handle message history:
230
+
231
+ ```typescript
232
+ import { chatAgent, serve } from '@reminix/runtime';
233
+
234
+ const assistant = chatAgent('assistant', {
235
+ description: 'A helpful assistant',
236
+ execute: async (messages) => {
237
+ const lastMsg = messages.at(-1)?.content ?? '';
238
+ return `You said: ${lastMsg}`;
239
+ },
240
+ });
241
+
242
+ // With context support
243
+ const contextualBot = chatAgent('contextual-bot', {
244
+ description: 'Bot with context awareness',
245
+ execute: async (messages, context) => {
246
+ const userId = context?.user_id ?? 'unknown';
247
+ return `Hello user ${userId}!`;
248
+ },
249
+ });
250
+
251
+ serve({ agents: [assistant, contextualBot], port: 8080 });
252
+ ```
253
+
254
+ ### Streaming
255
+
256
+ Both factories support streaming via async generators. When you use an async generator function, the agent automatically supports streaming:
257
+
258
+ ```typescript
259
+ import { agent, chatAgent, serve } from '@reminix/runtime';
260
+
261
+ // Streaming task agent
262
+ const streamer = agent('streamer', {
263
+ description: 'Stream text word by word',
264
+ parameters: {
265
+ type: 'object',
266
+ properties: { text: { type: 'string' } },
267
+ required: ['text'],
268
+ },
269
+ execute: async function* ({ text }) {
270
+ for (const word of (text as string).split(' ')) {
271
+ yield word + ' ';
272
+ }
273
+ },
274
+ });
275
+
276
+ // Streaming chat agent
277
+ const streamingAssistant = chatAgent('streaming-assistant', {
278
+ description: 'Stream responses token by token',
279
+ execute: async function* (messages) {
280
+ const response = `You said: ${messages.at(-1)?.content}`;
281
+ for (const char of response) {
282
+ yield char;
283
+ }
284
+ },
285
+ });
286
+
287
+ serve({ agents: [streamer, streamingAssistant], port: 8080 });
288
+ ```
289
+
290
+ For streaming agents:
291
+ - `stream: true` in the request → chunks are sent via SSE
292
+ - `stream: false` in the request → chunks are collected and returned as a single response
293
+
164
294
  ## Tools
165
295
 
166
- Tools are standalone functions that can be served via the runtime. They're useful for exposing utility functions, external API integrations, or any reusable logic.
296
+ Tools are standalone functions served via `/tools/{name}/execute`. They're useful for exposing utility functions, external API integrations, or any reusable logic.
167
297
 
168
298
  ### Creating Tools
169
299
 
@@ -182,54 +312,50 @@ const getWeather = tool('get_weather', {
182
312
  },
183
313
  required: ['location'],
184
314
  },
185
- // Optional: define output schema for documentation and type inference
186
315
  output: {
187
316
  type: 'object',
188
317
  properties: {
189
318
  temp: { type: 'number' },
190
319
  condition: { type: 'string' },
191
- location: { type: 'string' },
192
320
  },
193
321
  },
194
322
  execute: async (input) => {
195
323
  const location = input.location as string;
196
- // Call weather API...
197
324
  return { temp: 72, condition: 'sunny', location };
198
325
  },
199
326
  });
200
327
 
201
- // Serve tools (with or without agents)
202
328
  serve({ tools: [getWeather], port: 8080 });
203
329
  ```
204
330
 
205
- The optional `output` property defines the JSON Schema for the tool's return value. This is included in the `/info` endpoint for documentation and enables better type inference for clients.
206
-
207
331
  ### Serving Agents and Tools Together
208
332
 
209
333
  You can serve both agents and tools from the same runtime:
210
334
 
211
335
  ```typescript
212
- import { Agent, tool, serve } from '@reminix/runtime';
336
+ import { agent, tool, serve } from '@reminix/runtime';
213
337
 
214
- const agent = new Agent('my-agent');
215
- agent.onInvoke(async (req) => ({ output: 'Hello!' }));
338
+ const summarizer = agent('summarizer', {
339
+ description: 'Summarize text',
340
+ parameters: {
341
+ type: 'object',
342
+ properties: { text: { type: 'string' } },
343
+ required: ['text'],
344
+ },
345
+ execute: async ({ text }) => (text as string).slice(0, 100) + '...',
346
+ });
216
347
 
217
348
  const calculator = tool('calculate', {
218
349
  description: 'Perform basic math operations',
219
350
  parameters: {
220
351
  type: 'object',
221
- properties: {
222
- expression: { type: 'string', description: 'Math expression to evaluate' },
223
- },
352
+ properties: { expression: { type: 'string' } },
224
353
  required: ['expression'],
225
354
  },
226
- execute: async (input) => {
227
- const expr = input.expression as string;
228
- return { result: eval(expr) }; // Note: use a safe evaluator in production
229
- },
355
+ execute: async (input) => ({ result: eval(input.expression as string) }),
230
356
  });
231
357
 
232
- serve({ agents: [agent], tools: [calculator], port: 8080 });
358
+ serve({ agents: [summarizer], tools: [calculator], port: 8080 });
233
359
  ```
234
360
 
235
361
  ## Framework Adapters
@@ -270,6 +396,77 @@ const app = createApp({ agents: [myAgent], tools: [myTool] });
270
396
  // Use with any runtime: Node.js, Deno, Bun, Cloudflare Workers, etc.
271
397
  ```
272
398
 
399
+ ### `agent(name, options)`
400
+
401
+ Factory function to create a task-oriented agent.
402
+
403
+ | Parameter | Type | Description |
404
+ |-----------|------|-------------|
405
+ | `name` | `string` | Unique identifier for the agent |
406
+ | `options.description` | `string` | Human-readable description |
407
+ | `options.parameters` | `object` | JSON Schema for input parameters |
408
+ | `options.output` | `object` | Optional JSON Schema for output |
409
+ | `options.execute` | `function` | Async function or async generator to execute |
410
+
411
+ ```typescript
412
+ import { agent } from '@reminix/runtime';
413
+
414
+ // Regular agent
415
+ const myAgent = agent('my-agent', {
416
+ description: 'Does something useful',
417
+ parameters: {
418
+ type: 'object',
419
+ properties: { input: { type: 'string' } },
420
+ required: ['input'],
421
+ },
422
+ execute: async ({ input }) => ({ result: input }),
423
+ });
424
+
425
+ // Streaming agent
426
+ const streamingAgent = agent('streaming-agent', {
427
+ description: 'Streams output',
428
+ parameters: { type: 'object', properties: { text: { type: 'string' } }, required: ['text'] },
429
+ execute: async function* ({ text }) {
430
+ for (const word of (text as string).split(' ')) {
431
+ yield word + ' ';
432
+ }
433
+ },
434
+ });
435
+ ```
436
+
437
+ ### `chatAgent(name, options)`
438
+
439
+ Factory function to create a chat agent.
440
+
441
+ | Parameter | Type | Description |
442
+ |-----------|------|-------------|
443
+ | `name` | `string` | Unique identifier for the agent |
444
+ | `options.description` | `string` | Human-readable description |
445
+ | `options.execute` | `function` | Async function or async generator receiving messages |
446
+
447
+ ```typescript
448
+ import { chatAgent } from '@reminix/runtime';
449
+
450
+ // Regular chat agent
451
+ const bot = chatAgent('bot', {
452
+ description: 'A simple bot',
453
+ execute: async (messages) => `You said: ${messages.at(-1)?.content}`,
454
+ });
455
+
456
+ // With context
457
+ const contextBot = chatAgent('context-bot', {
458
+ execute: async (messages, context) => `Hello ${context?.user_id}!`,
459
+ });
460
+
461
+ // Streaming chat agent
462
+ const streamingBot = chatAgent('streaming-bot', {
463
+ execute: async function* (messages) {
464
+ yield 'Hello';
465
+ yield ' world!';
466
+ },
467
+ });
468
+ ```
469
+
273
470
  ### `tool(name, options)`
274
471
 
275
472
  Factory function to create a tool.
@@ -279,6 +476,7 @@ Factory function to create a tool.
279
476
  | `name` | `string` | Unique identifier for the tool |
280
477
  | `options.description` | `string` | Human-readable description |
281
478
  | `options.parameters` | `object` | JSON Schema for input parameters |
479
+ | `options.output` | `object` | Optional JSON Schema for output |
282
480
  | `options.execute` | `function` | Async function to execute when called |
283
481
 
284
482
  ```typescript
@@ -288,150 +486,136 @@ const myTool = tool('my_tool', {
288
486
  description: 'Does something useful',
289
487
  parameters: {
290
488
  type: 'object',
291
- properties: {
292
- input: { type: 'string' },
293
- },
489
+ properties: { input: { type: 'string' } },
294
490
  required: ['input'],
295
491
  },
296
- execute: async (input) => {
297
- return { result: input.input };
298
- },
492
+ execute: async (input) => ({ result: input.input }),
299
493
  });
300
494
  ```
301
495
 
302
- ### `Agent`
496
+ ### Request/Response Types
497
+
498
+ ```typescript
499
+ // Request: top-level keys based on agent's requestKeys (derived from parameters)
500
+ // For a calculator agent with parameters { a: number, b: number }:
501
+ interface CalculatorRequest {
502
+ a: number; // Top-level key from parameters
503
+ b: number; // Top-level key from parameters
504
+ stream?: boolean; // Whether to stream the response
505
+ context?: Record<string, unknown>; // Optional metadata
506
+ }
507
+
508
+ // For a chat agent:
509
+ interface ChatRequest {
510
+ messages: Message[]; // Top-level key (requestKeys: ['messages'])
511
+ stream?: boolean;
512
+ context?: Record<string, unknown>;
513
+ }
514
+
515
+ // Response: keys based on agent's responseKeys
516
+ // Regular agent (responseKeys: ['output']):
517
+ interface AgentResponse {
518
+ output: unknown;
519
+ }
520
+
521
+ // Chat agent (responseKeys: ['message']):
522
+ interface ChatResponse {
523
+ message: { role: string; content: string };
524
+ }
525
+ ```
526
+
527
+ ## Advanced
528
+
529
+ ### Agent Class
303
530
 
304
- Concrete class for building agents with callbacks.
531
+ For more control, you can use the `Agent` class directly:
305
532
 
306
533
  ```typescript
307
- import { Agent } from '@reminix/runtime';
534
+ import { Agent, serve } from '@reminix/runtime';
308
535
 
309
536
  const agent = new Agent('my-agent', { metadata: { version: '1.0' } });
310
537
 
311
- agent.onInvoke(async (request) => {
538
+ agent.onExecute(async (request) => {
312
539
  return { output: 'Hello!' };
313
540
  });
314
541
 
315
- agent.onChat(async (request) => {
316
- return { output: 'Hi!', messages: [...] };
542
+ // Optional: streaming handler
543
+ agent.onExecuteStream(async function* (request) {
544
+ yield 'Hello';
545
+ yield ' world!';
317
546
  });
318
547
 
319
- // Optional: streaming handlers
320
- agent.onInvokeStream(async function* (request) {
321
- yield '{"chunk": "Hello"}';
322
- yield '{"chunk": " world!"}';
323
- });
324
-
325
- agent.onChatStream(async function* (request) {
326
- yield '{"chunk": "Hi"}';
327
- });
548
+ serve({ agents: [agent], port: 8080 });
328
549
  ```
329
550
 
330
- | Method | Description |
331
- |--------|-------------|
332
- | `onInvoke(fn)` | Register invoke handler, returns `this` for chaining |
333
- | `onChat(fn)` | Register chat handler, returns `this` for chaining |
334
- | `onInvokeStream(fn)` | Register streaming invoke handler |
335
- | `onChatStream(fn)` | Register streaming chat handler |
336
- | `toHandler()` | Returns a web-standard fetch handler for serverless |
551
+ ### Tool Class
337
552
 
338
- ### `agent.toHandler()`
339
-
340
- Returns a web-standard `(Request) => Promise<Response>` handler for serverless deployments.
553
+ For programmatic tool creation:
341
554
 
342
555
  ```typescript
343
- // Vercel Edge Function
344
- import { Agent } from '@reminix/runtime';
556
+ import { Tool, serve } from '@reminix/runtime';
345
557
 
346
- const agent = new Agent('my-agent');
347
- agent.onInvoke(async (req) => ({ output: 'Hello!' }));
348
-
349
- export const POST = agent.toHandler();
350
- export const GET = agent.toHandler();
351
-
352
- // Cloudflare Workers
353
- export default { fetch: agent.toHandler() };
354
-
355
- // Deno Deploy
356
- Deno.serve(agent.toHandler());
558
+ const myTool = new Tool('get_weather', {
559
+ description: 'Get weather for a location',
560
+ parameters: {
561
+ type: 'object',
562
+ properties: { location: { type: 'string' } },
563
+ required: ['location'],
564
+ },
565
+ execute: async (input) => ({ temp: 72, location: input.location }),
566
+ });
357
567
 
358
- // Bun
359
- Bun.serve({ fetch: agent.toHandler() });
568
+ serve({ tools: [myTool], port: 8080 });
360
569
  ```
361
570
 
362
- ### `BaseAdapter`
571
+ ### AgentAdapter
363
572
 
364
- Abstract base class for framework adapters. Use this when wrapping an existing AI framework.
573
+ For building framework integrations. See the [framework adapter packages](#framework-adapters) for examples.
365
574
 
366
575
  ```typescript
367
- import { BaseAdapter, InvokeRequest, InvokeResponse, ChatRequest, ChatResponse } from '@reminix/runtime';
576
+ import { AgentAdapter } from '@reminix/runtime';
368
577
 
369
- class MyFrameworkAdapter extends BaseAdapter {
370
- // Adapter name shown in /info endpoint
578
+ class MyFrameworkAdapter extends AgentAdapter {
371
579
  static adapterName = 'my-framework';
372
-
373
- // BaseAdapter defaults both to true; override if your adapter doesn't support streaming
374
- // override readonly invokeStreaming = false;
375
- // override readonly chatStreaming = false;
376
580
 
377
- private client: MyFrameworkClient;
378
- private _name: string;
379
-
380
- constructor(client: MyFrameworkClient, name = 'my-framework') {
581
+ constructor(private client: MyClient, private _name = 'my-framework') {
381
582
  super();
382
- this.client = client;
383
- this._name = name;
384
583
  }
385
584
 
386
- get name(): string {
585
+ get name() {
387
586
  return this._name;
388
587
  }
389
588
 
390
- async invoke(request: InvokeRequest): Promise<InvokeResponse> {
391
- // Pass input to your framework
589
+ async execute(request) {
392
590
  const result = await this.client.run(request.input);
393
591
  return { output: result };
394
592
  }
395
-
396
- async chat(request: ChatRequest): Promise<ChatResponse> {
397
- // Convert messages and call your framework
398
- const result = await this.client.chat(request.messages);
399
- return {
400
- output: result,
401
- messages: [...request.messages, { role: 'assistant', content: result }],
402
- };
403
- }
404
- }
405
-
406
- // Optional: provide a wrap() factory function
407
- export function wrap(client: MyFrameworkClient, name = 'my-framework'): MyFrameworkAdapter {
408
- return new MyFrameworkAdapter(client, name);
409
593
  }
410
594
  ```
411
595
 
412
- ### Request/Response Types
596
+ ### Serverless Deployment
597
+
598
+ Use `toHandler()` for serverless deployments:
413
599
 
414
600
  ```typescript
415
- interface InvokeRequest {
416
- input: Record<string, unknown>; // Arbitrary input for task execution
417
- stream?: boolean; // Whether to stream the response
418
- context?: Record<string, unknown>; // Optional metadata
419
- }
601
+ import { agent } from '@reminix/runtime';
420
602
 
421
- interface InvokeResponse {
422
- output: unknown; // The result (can be any type)
423
- }
603
+ const myAgent = agent('my-agent', {
604
+ execute: async ({ task }) => `Completed: ${task}`,
605
+ });
424
606
 
425
- interface ChatRequest {
426
- messages: Message[]; // Conversation history
427
- stream?: boolean; // Whether to stream the response
428
- context?: Record<string, unknown>; // Optional metadata
429
- }
607
+ // Vercel Edge Function
608
+ export const POST = myAgent.toHandler();
609
+ export const GET = myAgent.toHandler();
430
610
 
431
- interface ChatResponse {
432
- output: string; // The final answer
433
- messages: Message[]; // Full execution history
434
- }
611
+ // Cloudflare Workers
612
+ export default { fetch: myAgent.toHandler() };
613
+
614
+ // Deno Deploy
615
+ Deno.serve(myAgent.toHandler());
616
+
617
+ // Bun
618
+ Bun.serve({ fetch: myAgent.toHandler() });
435
619
  ```
436
620
 
437
621
  ## Deployment