@mariozechner/pi-ai 0.5.31 → 0.5.32
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 +275 -10
- package/dist/agent/agent.d.ts +5 -0
- package/dist/agent/agent.d.ts.map +1 -0
- package/dist/agent/agent.js +161 -0
- package/dist/agent/agent.js.map +1 -0
- package/dist/agent/index.d.ts +4 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/agent/index.js +3 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/agent/tools/calculate.d.ts +17 -0
- package/dist/agent/tools/calculate.d.ts.map +1 -0
- package/dist/agent/tools/calculate.js +23 -0
- package/dist/agent/tools/calculate.js.map +1 -0
- package/dist/agent/tools/get-current-time.d.ts +20 -0
- package/dist/agent/tools/get-current-time.d.ts.map +1 -0
- package/dist/agent/tools/get-current-time.js +36 -0
- package/dist/agent/tools/get-current-time.js.map +1 -0
- package/dist/agent/tools/index.d.ts +3 -0
- package/dist/agent/tools/index.d.ts.map +1 -0
- package/dist/agent/tools/index.js +3 -0
- package/dist/agent/tools/index.js.map +1 -0
- package/dist/agent/types.d.ts +53 -0
- package/dist/agent/types.d.ts.map +1 -0
- package/dist/agent/types.js +2 -0
- package/dist/agent/types.js.map +1 -0
- package/dist/event-stream.d.ts +19 -0
- package/dist/event-stream.d.ts.map +1 -0
- package/dist/event-stream.js +77 -0
- package/dist/event-stream.js.map +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/models.generated.d.ts +17 -0
- package/dist/models.generated.d.ts.map +1 -1
- package/dist/models.generated.js +17 -0
- package/dist/models.generated.js.map +1 -1
- package/dist/providers/anthropic.d.ts +3 -3
- package/dist/providers/anthropic.d.ts.map +1 -1
- package/dist/providers/anthropic.js +107 -55
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/google.d.ts +3 -3
- package/dist/providers/google.d.ts.map +1 -1
- package/dist/providers/google.js +49 -11
- package/dist/providers/google.js.map +1 -1
- package/dist/providers/openai-completions.d.ts +3 -3
- package/dist/providers/openai-completions.d.ts.map +1 -1
- package/dist/providers/openai-completions.js +85 -148
- package/dist/providers/openai-completions.js.map +1 -1
- package/dist/providers/openai-responses.d.ts +3 -3
- package/dist/providers/openai-responses.d.ts.map +1 -1
- package/dist/providers/openai-responses.js +59 -9
- package/dist/providers/openai-responses.js.map +1 -1
- package/dist/providers/{utils.d.ts → transorm-messages.d.ts} +1 -1
- package/dist/providers/transorm-messages.d.ts.map +1 -0
- package/dist/providers/{utils.js → transorm-messages.js} +1 -1
- package/dist/providers/transorm-messages.js.map +1 -0
- package/dist/stream.d.ts +10 -0
- package/dist/stream.d.ts.map +1 -0
- package/dist/{generate.js → stream.js} +3 -65
- package/dist/stream.js.map +1 -0
- package/dist/types.d.ts +28 -11
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/validation.d.ts +10 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +30 -0
- package/dist/validation.js.map +1 -0
- package/package.json +7 -5
- package/dist/generate.d.ts +0 -22
- package/dist/generate.d.ts.map +0 -1
- package/dist/generate.js.map +0 -1
- package/dist/providers/utils.d.ts.map +0 -1
- package/dist/providers/utils.js.map +0 -1
package/README.md
CHANGED
|
@@ -24,20 +24,18 @@ npm install @mariozechner/pi-ai
|
|
|
24
24
|
## Quick Start
|
|
25
25
|
|
|
26
26
|
```typescript
|
|
27
|
-
import { getModel, stream, complete, Context, Tool } from '@mariozechner/pi-ai';
|
|
27
|
+
import { getModel, stream, complete, Context, Tool, z } from '@mariozechner/pi-ai';
|
|
28
28
|
|
|
29
29
|
// Fully typed with auto-complete support for both providers and models
|
|
30
30
|
const model = getModel('openai', 'gpt-4o-mini');
|
|
31
31
|
|
|
32
|
-
// Define tools
|
|
32
|
+
// Define tools with Zod schemas for type safety and validation
|
|
33
33
|
const tools: Tool[] = [{
|
|
34
34
|
name: 'get_time',
|
|
35
35
|
description: 'Get the current time',
|
|
36
|
-
parameters: {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
required: []
|
|
40
|
-
}
|
|
36
|
+
parameters: z.object({
|
|
37
|
+
timezone: z.string().optional().describe('Optional timezone (e.g., America/New_York)')
|
|
38
|
+
})
|
|
41
39
|
}];
|
|
42
40
|
|
|
43
41
|
// Build a conversation context (easily serializable and transferable between models)
|
|
@@ -94,7 +92,11 @@ const toolCalls = finalMessage.content.filter(b => b.type === 'toolCall');
|
|
|
94
92
|
for (const call of toolCalls) {
|
|
95
93
|
// Execute the tool
|
|
96
94
|
const result = call.name === 'get_time'
|
|
97
|
-
? new Date().
|
|
95
|
+
? new Date().toLocaleString('en-US', {
|
|
96
|
+
timeZone: call.arguments.timezone || 'UTC',
|
|
97
|
+
dateStyle: 'full',
|
|
98
|
+
timeStyle: 'long'
|
|
99
|
+
})
|
|
98
100
|
: 'Unknown tool';
|
|
99
101
|
|
|
100
102
|
// Add tool result to context
|
|
@@ -102,7 +104,7 @@ for (const call of toolCalls) {
|
|
|
102
104
|
role: 'toolResult',
|
|
103
105
|
toolCallId: call.id,
|
|
104
106
|
toolName: call.name,
|
|
105
|
-
|
|
107
|
+
output: result,
|
|
106
108
|
isError: false
|
|
107
109
|
});
|
|
108
110
|
}
|
|
@@ -129,6 +131,70 @@ for (const block of response.content) {
|
|
|
129
131
|
}
|
|
130
132
|
```
|
|
131
133
|
|
|
134
|
+
## Tools
|
|
135
|
+
|
|
136
|
+
Tools enable LLMs to interact with external systems. This library uses Zod schemas for type-safe tool definitions with automatic validation.
|
|
137
|
+
|
|
138
|
+
### Defining Tools
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
import { z, Tool } from '@mariozechner/pi-ai';
|
|
142
|
+
|
|
143
|
+
// Define tool parameters with Zod
|
|
144
|
+
const weatherTool: Tool = {
|
|
145
|
+
name: 'get_weather',
|
|
146
|
+
description: 'Get current weather for a location',
|
|
147
|
+
parameters: z.object({
|
|
148
|
+
location: z.string().describe('City name or coordinates'),
|
|
149
|
+
units: z.enum(['celsius', 'fahrenheit']).default('celsius')
|
|
150
|
+
})
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
// Complex validation with Zod refinements
|
|
154
|
+
const bookMeetingTool: Tool = {
|
|
155
|
+
name: 'book_meeting',
|
|
156
|
+
description: 'Schedule a meeting',
|
|
157
|
+
parameters: z.object({
|
|
158
|
+
title: z.string().min(1),
|
|
159
|
+
startTime: z.string().datetime(),
|
|
160
|
+
endTime: z.string().datetime(),
|
|
161
|
+
attendees: z.array(z.string().email()).min(1)
|
|
162
|
+
}).refine(
|
|
163
|
+
data => new Date(data.endTime) > new Date(data.startTime),
|
|
164
|
+
{ message: 'End time must be after start time' }
|
|
165
|
+
)
|
|
166
|
+
};
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Handling Tool Calls
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
const context: Context = {
|
|
173
|
+
messages: [{ role: 'user', content: 'What is the weather in London?' }],
|
|
174
|
+
tools: [weatherTool]
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
const response = await complete(model, context);
|
|
178
|
+
|
|
179
|
+
// Check for tool calls in the response
|
|
180
|
+
for (const block of response.content) {
|
|
181
|
+
if (block.type === 'toolCall') {
|
|
182
|
+
// Arguments are automatically validated against the Zod schema
|
|
183
|
+
// If validation fails, an error event is emitted
|
|
184
|
+
const result = await executeWeatherApi(block.arguments);
|
|
185
|
+
|
|
186
|
+
// Add tool result to continue the conversation
|
|
187
|
+
context.messages.push({
|
|
188
|
+
role: 'toolResult',
|
|
189
|
+
toolCallId: block.id,
|
|
190
|
+
toolName: block.name,
|
|
191
|
+
output: JSON.stringify(result),
|
|
192
|
+
isError: false
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
132
198
|
## Image Input
|
|
133
199
|
|
|
134
200
|
Models with vision capabilities can process images. You can check if a model supports images via the `input` property. If you pass images to a non-vision model, they are silently ignored.
|
|
@@ -260,7 +326,7 @@ for await (const event of s) {
|
|
|
260
326
|
|
|
261
327
|
## Errors & Abort Signal
|
|
262
328
|
|
|
263
|
-
When a request ends with an error (including aborts), the API returns an `AssistantMessage` with:
|
|
329
|
+
When a request ends with an error (including aborts and tool call validation errors), the API returns an `AssistantMessage` with:
|
|
264
330
|
- `stopReason: 'error'` - Indicates the request ended with an error
|
|
265
331
|
- `error: string` - Error message describing what happened
|
|
266
332
|
- `content: array` - **Partial content** accumulated before the error
|
|
@@ -503,6 +569,189 @@ const continuation = await complete(newModel, restored);
|
|
|
503
569
|
|
|
504
570
|
> **Note**: If the context contains images (encoded as base64 as shown in the Image Input section), those will also be serialized.
|
|
505
571
|
|
|
572
|
+
## Agent API
|
|
573
|
+
|
|
574
|
+
The Agent API provides a higher-level interface for building agents with tools. It handles tool execution, validation, and provides detailed event streaming for interactive applications.
|
|
575
|
+
|
|
576
|
+
### Event System
|
|
577
|
+
|
|
578
|
+
The Agent API streams events during execution, allowing you to build reactive UIs and track agent progress. The agent processes prompts in **turns**, where each turn consists of:
|
|
579
|
+
1. An assistant message (the LLM's response)
|
|
580
|
+
2. Optional tool executions if the assistant calls tools
|
|
581
|
+
3. Tool result messages that are fed back to the LLM
|
|
582
|
+
|
|
583
|
+
This continues until the assistant produces a response without tool calls.
|
|
584
|
+
|
|
585
|
+
### Event Flow Example
|
|
586
|
+
|
|
587
|
+
Given a prompt asking to calculate two expressions and sum them:
|
|
588
|
+
|
|
589
|
+
```typescript
|
|
590
|
+
import { prompt, AgentContext, calculateTool } from '@mariozechner/pi-ai';
|
|
591
|
+
|
|
592
|
+
const context: AgentContext = {
|
|
593
|
+
systemPrompt: 'You are a helpful math assistant.',
|
|
594
|
+
messages: [],
|
|
595
|
+
tools: [calculateTool]
|
|
596
|
+
};
|
|
597
|
+
|
|
598
|
+
const stream = prompt(
|
|
599
|
+
{ role: 'user', content: 'Calculate 15 * 20 and 30 * 40, then sum the results' },
|
|
600
|
+
context,
|
|
601
|
+
{ model: getModel('openai', 'gpt-4o-mini') }
|
|
602
|
+
);
|
|
603
|
+
|
|
604
|
+
// Expected event sequence:
|
|
605
|
+
// 1. agent_start - Agent begins processing
|
|
606
|
+
// 2. turn_start - First turn begins
|
|
607
|
+
// 3. message_start - User message starts
|
|
608
|
+
// 4. message_end - User message ends
|
|
609
|
+
// 5. message_start - Assistant message starts
|
|
610
|
+
// 6. message_update - Assistant streams response with tool calls
|
|
611
|
+
// 7. message_end - Assistant message ends
|
|
612
|
+
// 8. tool_execution_start - First calculation (15 * 20)
|
|
613
|
+
// 9. tool_execution_end - Result: 300
|
|
614
|
+
// 10. tool_execution_start - Second calculation (30 * 40)
|
|
615
|
+
// 11. tool_execution_end - Result: 1200
|
|
616
|
+
// 12. message_start - Tool result message for first calculation
|
|
617
|
+
// 13. message_end - Tool result message ends
|
|
618
|
+
// 14. message_start - Tool result message for second calculation
|
|
619
|
+
// 15. message_end - Tool result message ends
|
|
620
|
+
// 16. turn_end - First turn ends with 2 tool results
|
|
621
|
+
// 17. turn_start - Second turn begins
|
|
622
|
+
// 18. message_start - Assistant message starts
|
|
623
|
+
// 19. message_update - Assistant streams response with sum calculation
|
|
624
|
+
// 20. message_end - Assistant message ends
|
|
625
|
+
// 21. tool_execution_start - Sum calculation (300 + 1200)
|
|
626
|
+
// 22. tool_execution_end - Result: 1500
|
|
627
|
+
// 23. message_start - Tool result message for sum
|
|
628
|
+
// 24. message_end - Tool result message ends
|
|
629
|
+
// 25. turn_end - Second turn ends with 1 tool result
|
|
630
|
+
// 26. turn_start - Third turn begins
|
|
631
|
+
// 27. message_start - Final assistant message starts
|
|
632
|
+
// 28. message_update - Assistant streams final answer
|
|
633
|
+
// 29. message_end - Final assistant message ends
|
|
634
|
+
// 30. turn_end - Third turn ends with 0 tool results
|
|
635
|
+
// 31. agent_end - Agent completes with all messages
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
### Handling Events
|
|
639
|
+
|
|
640
|
+
```typescript
|
|
641
|
+
for await (const event of stream) {
|
|
642
|
+
switch (event.type) {
|
|
643
|
+
case 'agent_start':
|
|
644
|
+
console.log('Agent started');
|
|
645
|
+
break;
|
|
646
|
+
|
|
647
|
+
case 'turn_start':
|
|
648
|
+
console.log('New turn started');
|
|
649
|
+
break;
|
|
650
|
+
|
|
651
|
+
case 'message_start':
|
|
652
|
+
console.log(`${event.message.role} message started`);
|
|
653
|
+
break;
|
|
654
|
+
|
|
655
|
+
case 'message_update':
|
|
656
|
+
// Only for assistant messages during streaming
|
|
657
|
+
if (event.message.content.some(c => c.type === 'text')) {
|
|
658
|
+
console.log('Assistant:', event.message.content);
|
|
659
|
+
}
|
|
660
|
+
break;
|
|
661
|
+
|
|
662
|
+
case 'tool_execution_start':
|
|
663
|
+
console.log(`Calling ${event.toolName} with:`, event.args);
|
|
664
|
+
break;
|
|
665
|
+
|
|
666
|
+
case 'tool_execution_end':
|
|
667
|
+
if (event.isError) {
|
|
668
|
+
console.error(`Tool failed:`, event.result);
|
|
669
|
+
} else {
|
|
670
|
+
console.log(`Tool result:`, event.result.output);
|
|
671
|
+
}
|
|
672
|
+
break;
|
|
673
|
+
|
|
674
|
+
case 'turn_end':
|
|
675
|
+
console.log(`Turn ended with ${event.toolResults.length} tool calls`);
|
|
676
|
+
break;
|
|
677
|
+
|
|
678
|
+
case 'agent_end':
|
|
679
|
+
console.log(`Agent completed with ${event.messages.length} new messages`);
|
|
680
|
+
break;
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
// Get all messages generated during this agent execution
|
|
685
|
+
// These include the user message and can be directly appended to context.messages
|
|
686
|
+
const messages = await stream.result();
|
|
687
|
+
context.messages.push(...messages);
|
|
688
|
+
```
|
|
689
|
+
|
|
690
|
+
### Defining Tools with Zod
|
|
691
|
+
|
|
692
|
+
Tools use Zod schemas for runtime validation and type inference:
|
|
693
|
+
|
|
694
|
+
```typescript
|
|
695
|
+
import { z } from 'zod';
|
|
696
|
+
import { AgentTool, AgentToolResult } from '@mariozechner/pi-ai';
|
|
697
|
+
|
|
698
|
+
const weatherSchema = z.object({
|
|
699
|
+
city: z.string().min(1, 'City is required'),
|
|
700
|
+
units: z.enum(['celsius', 'fahrenheit']).default('celsius')
|
|
701
|
+
});
|
|
702
|
+
|
|
703
|
+
const weatherTool: AgentTool<typeof weatherSchema, { temp: number }> = {
|
|
704
|
+
label: 'Get Weather',
|
|
705
|
+
name: 'get_weather',
|
|
706
|
+
description: 'Get current weather for a city',
|
|
707
|
+
parameters: weatherSchema,
|
|
708
|
+
execute: async (toolCallId, args) => {
|
|
709
|
+
// args is fully typed: { city: string, units: 'celsius' | 'fahrenheit' }
|
|
710
|
+
const temp = Math.round(Math.random() * 30);
|
|
711
|
+
return {
|
|
712
|
+
output: `Temperature in ${args.city}: ${temp}°${args.units[0].toUpperCase()}`,
|
|
713
|
+
details: { temp }
|
|
714
|
+
};
|
|
715
|
+
}
|
|
716
|
+
};
|
|
717
|
+
```
|
|
718
|
+
|
|
719
|
+
### Validation and Error Handling
|
|
720
|
+
|
|
721
|
+
Tool arguments are automatically validated using the Zod schema. Invalid arguments result in detailed error messages:
|
|
722
|
+
|
|
723
|
+
```typescript
|
|
724
|
+
// If the LLM calls with invalid arguments:
|
|
725
|
+
// get_weather({ city: '', units: 'kelvin' })
|
|
726
|
+
|
|
727
|
+
// The tool execution will fail with:
|
|
728
|
+
/*
|
|
729
|
+
Validation failed for tool "get_weather":
|
|
730
|
+
- city: City is required
|
|
731
|
+
- units: Invalid enum value. Expected 'celsius' | 'fahrenheit', received 'kelvin'
|
|
732
|
+
|
|
733
|
+
Received arguments:
|
|
734
|
+
{
|
|
735
|
+
"city": "",
|
|
736
|
+
"units": "kelvin"
|
|
737
|
+
}
|
|
738
|
+
*/
|
|
739
|
+
```
|
|
740
|
+
|
|
741
|
+
### Built-in Example Tools
|
|
742
|
+
|
|
743
|
+
The library includes example tools for common operations:
|
|
744
|
+
|
|
745
|
+
```typescript
|
|
746
|
+
import { calculateTool, getCurrentTimeTool } from '@mariozechner/pi-ai';
|
|
747
|
+
|
|
748
|
+
const context: AgentContext = {
|
|
749
|
+
systemPrompt: 'You are a helpful assistant.',
|
|
750
|
+
messages: [],
|
|
751
|
+
tools: [calculateTool, getCurrentTimeTool]
|
|
752
|
+
};
|
|
753
|
+
```
|
|
754
|
+
|
|
506
755
|
## Browser Usage
|
|
507
756
|
|
|
508
757
|
The library supports browser environments. You must pass the API key explicitly since environment variables are not available in browsers:
|
|
@@ -533,6 +782,7 @@ GEMINI_API_KEY=...
|
|
|
533
782
|
GROQ_API_KEY=gsk_...
|
|
534
783
|
CEREBRAS_API_KEY=csk-...
|
|
535
784
|
XAI_API_KEY=xai-...
|
|
785
|
+
ZAI_API_KEY=...
|
|
536
786
|
OPENROUTER_API_KEY=sk-or-...
|
|
537
787
|
```
|
|
538
788
|
|
|
@@ -549,6 +799,21 @@ const response = await complete(model, context, {
|
|
|
549
799
|
});
|
|
550
800
|
```
|
|
551
801
|
|
|
802
|
+
### Programmatic API Key Management
|
|
803
|
+
|
|
804
|
+
You can also set and get API keys programmatically:
|
|
805
|
+
|
|
806
|
+
```typescript
|
|
807
|
+
import { setApiKey, getApiKey } from '@mariozechner/pi-ai';
|
|
808
|
+
|
|
809
|
+
// Set API key for a provider
|
|
810
|
+
setApiKey('openai', 'sk-...');
|
|
811
|
+
setApiKey('anthropic', 'sk-ant-...');
|
|
812
|
+
|
|
813
|
+
// Get API key for a provider (checks both programmatic and env vars)
|
|
814
|
+
const key = getApiKey('openai');
|
|
815
|
+
```
|
|
816
|
+
|
|
552
817
|
## License
|
|
553
818
|
|
|
554
819
|
MIT
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { EventStream } from "../event-stream";
|
|
2
|
+
import type { UserMessage } from "../types.js";
|
|
3
|
+
import type { AgentContext, AgentEvent, PromptConfig } from "./types";
|
|
4
|
+
export declare function prompt(prompt: UserMessage, context: AgentContext, config: PromptConfig, signal?: AbortSignal): EventStream<AgentEvent, AgentContext["messages"]>;
|
|
5
|
+
//# sourceMappingURL=agent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/agent/agent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAE9C,OAAO,KAAK,EAAyD,WAAW,EAAE,MAAM,aAAa,CAAC;AAEtG,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAA8B,YAAY,EAAE,MAAM,SAAS,CAAC;AAGlG,wBAAgB,MAAM,CACrB,MAAM,EAAE,WAAW,EACnB,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,YAAY,EACpB,MAAM,CAAC,EAAE,WAAW,GAClB,WAAW,CAAC,UAAU,EAAE,YAAY,CAAC,UAAU,CAAC,CAAC,CAwDnD"}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { EventStream } from "../event-stream";
|
|
2
|
+
import { streamSimple } from "../stream.js";
|
|
3
|
+
import { validateToolArguments } from "../validation.js";
|
|
4
|
+
// Main prompt function - returns a stream of events
|
|
5
|
+
export function prompt(prompt, context, config, signal) {
|
|
6
|
+
const stream = new EventStream((event) => event.type === "agent_end", (event) => (event.type === "agent_end" ? event.messages : []));
|
|
7
|
+
// Run the prompt async
|
|
8
|
+
(async () => {
|
|
9
|
+
// Track new messages generated during this prompt
|
|
10
|
+
const newMessages = [];
|
|
11
|
+
// Create user message for the prompt
|
|
12
|
+
const messages = [...context.messages, prompt];
|
|
13
|
+
newMessages.push(prompt);
|
|
14
|
+
stream.push({ type: "agent_start" });
|
|
15
|
+
stream.push({ type: "turn_start" });
|
|
16
|
+
stream.push({ type: "message_start", message: prompt });
|
|
17
|
+
stream.push({ type: "message_end", message: prompt });
|
|
18
|
+
// Update context with new messages
|
|
19
|
+
const currentContext = {
|
|
20
|
+
...context,
|
|
21
|
+
messages,
|
|
22
|
+
};
|
|
23
|
+
// Keep looping while we have tool calls
|
|
24
|
+
let hasMoreToolCalls = true;
|
|
25
|
+
let firstTurn = true;
|
|
26
|
+
while (hasMoreToolCalls) {
|
|
27
|
+
if (!firstTurn) {
|
|
28
|
+
stream.push({ type: "turn_start" });
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
firstTurn = false;
|
|
32
|
+
}
|
|
33
|
+
// Stream assistant response
|
|
34
|
+
const assistantMessage = await streamAssistantResponse(currentContext, config, signal, stream);
|
|
35
|
+
newMessages.push(assistantMessage);
|
|
36
|
+
// Check for tool calls
|
|
37
|
+
const toolCalls = assistantMessage.content.filter((c) => c.type === "toolCall");
|
|
38
|
+
hasMoreToolCalls = toolCalls.length > 0;
|
|
39
|
+
const toolResults = [];
|
|
40
|
+
if (hasMoreToolCalls) {
|
|
41
|
+
// Execute tool calls
|
|
42
|
+
toolResults.push(...(await executeToolCalls(currentContext.tools, assistantMessage, signal, stream)));
|
|
43
|
+
currentContext.messages.push(...toolResults);
|
|
44
|
+
newMessages.push(...toolResults);
|
|
45
|
+
}
|
|
46
|
+
stream.push({ type: "turn_end", assistantMessage, toolResults: toolResults });
|
|
47
|
+
}
|
|
48
|
+
stream.push({ type: "agent_end", messages: newMessages });
|
|
49
|
+
stream.end(newMessages);
|
|
50
|
+
})();
|
|
51
|
+
return stream;
|
|
52
|
+
}
|
|
53
|
+
// Helper functions
|
|
54
|
+
async function streamAssistantResponse(context, config, signal, stream) {
|
|
55
|
+
// Convert AgentContext to Context for streamSimple
|
|
56
|
+
// Use a copy of messages to avoid mutating the original context
|
|
57
|
+
const processedMessages = config.preprocessor
|
|
58
|
+
? await config.preprocessor(context.messages, signal)
|
|
59
|
+
: [...context.messages];
|
|
60
|
+
const processedContext = {
|
|
61
|
+
systemPrompt: context.systemPrompt,
|
|
62
|
+
messages: [...processedMessages].map((m) => {
|
|
63
|
+
if (m.role === "toolResult") {
|
|
64
|
+
const { details, ...rest } = m;
|
|
65
|
+
return rest;
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
return m;
|
|
69
|
+
}
|
|
70
|
+
}),
|
|
71
|
+
tools: context.tools, // AgentTool extends Tool, so this works
|
|
72
|
+
};
|
|
73
|
+
const response = await streamSimple(config.model, processedContext, { ...config, signal });
|
|
74
|
+
let partialMessage = null;
|
|
75
|
+
let addedPartial = false;
|
|
76
|
+
for await (const event of response) {
|
|
77
|
+
switch (event.type) {
|
|
78
|
+
case "start":
|
|
79
|
+
partialMessage = event.partial;
|
|
80
|
+
context.messages.push(partialMessage);
|
|
81
|
+
addedPartial = true;
|
|
82
|
+
stream.push({ type: "message_start", message: { ...partialMessage } });
|
|
83
|
+
break;
|
|
84
|
+
case "text_start":
|
|
85
|
+
case "text_delta":
|
|
86
|
+
case "text_end":
|
|
87
|
+
case "thinking_start":
|
|
88
|
+
case "thinking_delta":
|
|
89
|
+
case "thinking_end":
|
|
90
|
+
case "toolcall_start":
|
|
91
|
+
case "toolcall_delta":
|
|
92
|
+
case "toolcall_end":
|
|
93
|
+
if (partialMessage) {
|
|
94
|
+
partialMessage = event.partial;
|
|
95
|
+
context.messages[context.messages.length - 1] = partialMessage;
|
|
96
|
+
stream.push({ type: "message_update", assistantMessageEvent: event, message: { ...partialMessage } });
|
|
97
|
+
}
|
|
98
|
+
break;
|
|
99
|
+
case "done":
|
|
100
|
+
case "error": {
|
|
101
|
+
const finalMessage = await response.result();
|
|
102
|
+
if (addedPartial) {
|
|
103
|
+
context.messages[context.messages.length - 1] = finalMessage;
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
context.messages.push(finalMessage);
|
|
107
|
+
}
|
|
108
|
+
stream.push({ type: "message_end", message: finalMessage });
|
|
109
|
+
return finalMessage;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return await response.result();
|
|
114
|
+
}
|
|
115
|
+
async function executeToolCalls(tools, assistantMessage, signal, stream) {
|
|
116
|
+
const toolCalls = assistantMessage.content.filter((c) => c.type === "toolCall");
|
|
117
|
+
const results = [];
|
|
118
|
+
for (const toolCall of toolCalls) {
|
|
119
|
+
const tool = tools?.find((t) => t.name === toolCall.name);
|
|
120
|
+
stream.push({
|
|
121
|
+
type: "tool_execution_start",
|
|
122
|
+
toolCallId: toolCall.id,
|
|
123
|
+
toolName: toolCall.name,
|
|
124
|
+
args: toolCall.arguments,
|
|
125
|
+
});
|
|
126
|
+
let resultOrError;
|
|
127
|
+
let isError = false;
|
|
128
|
+
try {
|
|
129
|
+
if (!tool)
|
|
130
|
+
throw new Error(`Tool ${toolCall.name} not found`);
|
|
131
|
+
// Validate arguments using shared validation function
|
|
132
|
+
const validatedArgs = validateToolArguments(tool, toolCall);
|
|
133
|
+
// Execute with validated, typed arguments
|
|
134
|
+
resultOrError = await tool.execute(toolCall.id, validatedArgs, signal);
|
|
135
|
+
}
|
|
136
|
+
catch (e) {
|
|
137
|
+
resultOrError = e instanceof Error ? e.message : String(e);
|
|
138
|
+
isError = true;
|
|
139
|
+
}
|
|
140
|
+
stream.push({
|
|
141
|
+
type: "tool_execution_end",
|
|
142
|
+
toolCallId: toolCall.id,
|
|
143
|
+
toolName: toolCall.name,
|
|
144
|
+
result: resultOrError,
|
|
145
|
+
isError,
|
|
146
|
+
});
|
|
147
|
+
const toolResultMessage = {
|
|
148
|
+
role: "toolResult",
|
|
149
|
+
toolCallId: toolCall.id,
|
|
150
|
+
toolName: toolCall.name,
|
|
151
|
+
output: typeof resultOrError === "string" ? resultOrError : resultOrError.output,
|
|
152
|
+
details: typeof resultOrError === "string" ? {} : resultOrError.details,
|
|
153
|
+
isError,
|
|
154
|
+
};
|
|
155
|
+
results.push(toolResultMessage);
|
|
156
|
+
stream.push({ type: "message_start", message: toolResultMessage });
|
|
157
|
+
stream.push({ type: "message_end", message: toolResultMessage });
|
|
158
|
+
}
|
|
159
|
+
return results;
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=agent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent.js","sourceRoot":"","sources":["../../src/agent/agent.ts"],"names":[],"mappings":"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","sourcesContent":["import { EventStream } from \"../event-stream\";\nimport { streamSimple } from \"../stream.js\";\nimport type { AssistantMessage, Context, Message, ToolResultMessage, UserMessage } from \"../types.js\";\nimport { validateToolArguments } from \"../validation.js\";\nimport type { AgentContext, AgentEvent, AgentTool, AgentToolResult, PromptConfig } from \"./types\";\n\n// Main prompt function - returns a stream of events\nexport function prompt(\n\tprompt: UserMessage,\n\tcontext: AgentContext,\n\tconfig: PromptConfig,\n\tsignal?: AbortSignal,\n): EventStream<AgentEvent, AgentContext[\"messages\"]> {\n\tconst stream = new EventStream<AgentEvent, AgentContext[\"messages\"]>(\n\t\t(event) => event.type === \"agent_end\",\n\t\t(event) => (event.type === \"agent_end\" ? event.messages : []),\n\t);\n\n\t// Run the prompt async\n\t(async () => {\n\t\t// Track new messages generated during this prompt\n\t\tconst newMessages: AgentContext[\"messages\"] = [];\n\t\t// Create user message for the prompt\n\t\tconst messages = [...context.messages, prompt];\n\t\tnewMessages.push(prompt);\n\n\t\tstream.push({ type: \"agent_start\" });\n\t\tstream.push({ type: \"turn_start\" });\n\t\tstream.push({ type: \"message_start\", message: prompt });\n\t\tstream.push({ type: \"message_end\", message: prompt });\n\n\t\t// Update context with new messages\n\t\tconst currentContext: AgentContext = {\n\t\t\t...context,\n\t\t\tmessages,\n\t\t};\n\n\t\t// Keep looping while we have tool calls\n\t\tlet hasMoreToolCalls = true;\n\t\tlet firstTurn = true;\n\t\twhile (hasMoreToolCalls) {\n\t\t\tif (!firstTurn) {\n\t\t\t\tstream.push({ type: \"turn_start\" });\n\t\t\t} else {\n\t\t\t\tfirstTurn = false;\n\t\t\t}\n\t\t\t// Stream assistant response\n\t\t\tconst assistantMessage = await streamAssistantResponse(currentContext, config, signal, stream);\n\t\t\tnewMessages.push(assistantMessage);\n\n\t\t\t// Check for tool calls\n\t\t\tconst toolCalls = assistantMessage.content.filter((c) => c.type === \"toolCall\");\n\t\t\thasMoreToolCalls = toolCalls.length > 0;\n\n\t\t\tconst toolResults: ToolResultMessage[] = [];\n\t\t\tif (hasMoreToolCalls) {\n\t\t\t\t// Execute tool calls\n\t\t\t\ttoolResults.push(...(await executeToolCalls(currentContext.tools, assistantMessage, signal, stream)));\n\t\t\t\tcurrentContext.messages.push(...toolResults);\n\t\t\t\tnewMessages.push(...toolResults);\n\t\t\t}\n\t\t\tstream.push({ type: \"turn_end\", assistantMessage, toolResults: toolResults });\n\t\t}\n\t\tstream.push({ type: \"agent_end\", messages: newMessages });\n\t\tstream.end(newMessages);\n\t})();\n\n\treturn stream;\n}\n\n// Helper functions\nasync function streamAssistantResponse(\n\tcontext: AgentContext,\n\tconfig: PromptConfig,\n\tsignal: AbortSignal | undefined,\n\tstream: EventStream<AgentEvent, AgentContext[\"messages\"]>,\n): Promise<AssistantMessage> {\n\t// Convert AgentContext to Context for streamSimple\n\t// Use a copy of messages to avoid mutating the original context\n\tconst processedMessages = config.preprocessor\n\t\t? await config.preprocessor(context.messages, signal)\n\t\t: [...context.messages];\n\tconst processedContext: Context = {\n\t\tsystemPrompt: context.systemPrompt,\n\t\tmessages: [...processedMessages].map((m) => {\n\t\t\tif (m.role === \"toolResult\") {\n\t\t\t\tconst { details, ...rest } = m;\n\t\t\t\treturn rest;\n\t\t\t} else {\n\t\t\t\treturn m;\n\t\t\t}\n\t\t}),\n\t\ttools: context.tools, // AgentTool extends Tool, so this works\n\t};\n\n\tconst response = await streamSimple(config.model, processedContext, { ...config, signal });\n\n\tlet partialMessage: AssistantMessage | null = null;\n\tlet addedPartial = false;\n\n\tfor await (const event of response) {\n\t\tswitch (event.type) {\n\t\t\tcase \"start\":\n\t\t\t\tpartialMessage = event.partial;\n\t\t\t\tcontext.messages.push(partialMessage);\n\t\t\t\taddedPartial = true;\n\t\t\t\tstream.push({ type: \"message_start\", message: { ...partialMessage } });\n\t\t\t\tbreak;\n\n\t\t\tcase \"text_start\":\n\t\t\tcase \"text_delta\":\n\t\t\tcase \"text_end\":\n\t\t\tcase \"thinking_start\":\n\t\t\tcase \"thinking_delta\":\n\t\t\tcase \"thinking_end\":\n\t\t\tcase \"toolcall_start\":\n\t\t\tcase \"toolcall_delta\":\n\t\t\tcase \"toolcall_end\":\n\t\t\t\tif (partialMessage) {\n\t\t\t\t\tpartialMessage = event.partial;\n\t\t\t\t\tcontext.messages[context.messages.length - 1] = partialMessage;\n\t\t\t\t\tstream.push({ type: \"message_update\", assistantMessageEvent: event, message: { ...partialMessage } });\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase \"done\":\n\t\t\tcase \"error\": {\n\t\t\t\tconst finalMessage = await response.result();\n\t\t\t\tif (addedPartial) {\n\t\t\t\t\tcontext.messages[context.messages.length - 1] = finalMessage;\n\t\t\t\t} else {\n\t\t\t\t\tcontext.messages.push(finalMessage);\n\t\t\t\t}\n\t\t\t\tstream.push({ type: \"message_end\", message: finalMessage });\n\t\t\t\treturn finalMessage;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn await response.result();\n}\n\nasync function executeToolCalls<T>(\n\ttools: AgentTool<any, T>[] | undefined,\n\tassistantMessage: AssistantMessage,\n\tsignal: AbortSignal | undefined,\n\tstream: EventStream<AgentEvent, Message[]>,\n): Promise<ToolResultMessage<T>[]> {\n\tconst toolCalls = assistantMessage.content.filter((c) => c.type === \"toolCall\");\n\tconst results: ToolResultMessage<any>[] = [];\n\n\tfor (const toolCall of toolCalls) {\n\t\tconst tool = tools?.find((t) => t.name === toolCall.name);\n\n\t\tstream.push({\n\t\t\ttype: \"tool_execution_start\",\n\t\t\ttoolCallId: toolCall.id,\n\t\t\ttoolName: toolCall.name,\n\t\t\targs: toolCall.arguments,\n\t\t});\n\n\t\tlet resultOrError: AgentToolResult<T> | string;\n\t\tlet isError = false;\n\n\t\ttry {\n\t\t\tif (!tool) throw new Error(`Tool ${toolCall.name} not found`);\n\n\t\t\t// Validate arguments using shared validation function\n\t\t\tconst validatedArgs = validateToolArguments(tool, toolCall);\n\n\t\t\t// Execute with validated, typed arguments\n\t\t\tresultOrError = await tool.execute(toolCall.id, validatedArgs, signal);\n\t\t} catch (e) {\n\t\t\tresultOrError = e instanceof Error ? e.message : String(e);\n\t\t\tisError = true;\n\t\t}\n\n\t\tstream.push({\n\t\t\ttype: \"tool_execution_end\",\n\t\t\ttoolCallId: toolCall.id,\n\t\t\ttoolName: toolCall.name,\n\t\t\tresult: resultOrError,\n\t\t\tisError,\n\t\t});\n\n\t\tconst toolResultMessage: ToolResultMessage<T> = {\n\t\t\trole: \"toolResult\",\n\t\t\ttoolCallId: toolCall.id,\n\t\t\ttoolName: toolCall.name,\n\t\t\toutput: typeof resultOrError === \"string\" ? resultOrError : resultOrError.output,\n\t\t\tdetails: typeof resultOrError === \"string\" ? ({} as T) : resultOrError.details,\n\t\t\tisError,\n\t\t};\n\n\t\tresults.push(toolResultMessage);\n\t\tstream.push({ type: \"message_start\", message: toolResultMessage });\n\t\tstream.push({ type: \"message_end\", message: toolResultMessage });\n\t}\n\n\treturn results;\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/agent/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,cAAc,SAAS,CAAC;AACxB,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/agent/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,cAAc,SAAS,CAAC","sourcesContent":["export { prompt } from \"./agent\";\nexport * from \"./tools\";\nexport type { AgentContext, AgentEvent, AgentTool, PromptConfig } from \"./types\";\n"]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { AgentTool } from "../../agent";
|
|
3
|
+
export interface CalculateResult {
|
|
4
|
+
output: string;
|
|
5
|
+
details: undefined;
|
|
6
|
+
}
|
|
7
|
+
export declare function calculate(expression: string): CalculateResult;
|
|
8
|
+
declare const calculateSchema: z.ZodObject<{
|
|
9
|
+
expression: z.ZodString;
|
|
10
|
+
}, "strip", z.ZodTypeAny, {
|
|
11
|
+
expression: string;
|
|
12
|
+
}, {
|
|
13
|
+
expression: string;
|
|
14
|
+
}>;
|
|
15
|
+
export declare const calculateTool: AgentTool<typeof calculateSchema, undefined>;
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=calculate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"calculate.d.ts","sourceRoot":"","sources":["../../../src/agent/tools/calculate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,MAAM,WAAW,eAAe;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,SAAS,CAAC;CACnB;AAED,wBAAgB,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,eAAe,CAO7D;AAED,QAAA,MAAM,eAAe;;;;;;EAEnB,CAAC;AAEH,eAAO,MAAM,aAAa,EAAE,SAAS,CAAC,OAAO,eAAe,EAAE,SAAS,CAQtE,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export function calculate(expression) {
|
|
3
|
+
try {
|
|
4
|
+
const result = new Function("return " + expression)();
|
|
5
|
+
return { output: `${expression} = ${result}`, details: undefined };
|
|
6
|
+
}
|
|
7
|
+
catch (e) {
|
|
8
|
+
throw new Error(e.message || String(e));
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
const calculateSchema = z.object({
|
|
12
|
+
expression: z.string().describe("The mathematical expression to evaluate"),
|
|
13
|
+
});
|
|
14
|
+
export const calculateTool = {
|
|
15
|
+
label: "Calculator",
|
|
16
|
+
name: "calculate",
|
|
17
|
+
description: "Evaluate mathematical expressions",
|
|
18
|
+
parameters: calculateSchema,
|
|
19
|
+
execute: async (_toolCallId, args) => {
|
|
20
|
+
return calculate(args.expression);
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=calculate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"calculate.js","sourceRoot":"","sources":["../../../src/agent/tools/calculate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAQxB,MAAM,UAAU,SAAS,CAAC,UAAkB;IAC3C,IAAI,CAAC;QACJ,MAAM,MAAM,GAAG,IAAI,QAAQ,CAAC,SAAS,GAAG,UAAU,CAAC,EAAE,CAAC;QACtD,OAAO,EAAE,MAAM,EAAE,GAAG,UAAU,MAAM,MAAM,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;IACpE,CAAC;IAAC,OAAO,CAAM,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,CAAC,CAAC,OAAO,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IACzC,CAAC;AACF,CAAC;AAED,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC;IAChC,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,yCAAyC,CAAC;CAC1E,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,aAAa,GAAiD;IAC1E,KAAK,EAAE,YAAY;IACnB,IAAI,EAAE,WAAW;IACjB,WAAW,EAAE,mCAAmC;IAChD,UAAU,EAAE,eAAe;IAC3B,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,EAAE;QACpC,OAAO,SAAS,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACnC,CAAC;CACD,CAAC","sourcesContent":["import { z } from \"zod\";\nimport type { AgentTool } from \"../../agent\";\n\nexport interface CalculateResult {\n\toutput: string;\n\tdetails: undefined;\n}\n\nexport function calculate(expression: string): CalculateResult {\n\ttry {\n\t\tconst result = new Function(\"return \" + expression)();\n\t\treturn { output: `${expression} = ${result}`, details: undefined };\n\t} catch (e: any) {\n\t\tthrow new Error(e.message || String(e));\n\t}\n}\n\nconst calculateSchema = z.object({\n\texpression: z.string().describe(\"The mathematical expression to evaluate\"),\n});\n\nexport const calculateTool: AgentTool<typeof calculateSchema, undefined> = {\n\tlabel: \"Calculator\",\n\tname: \"calculate\",\n\tdescription: \"Evaluate mathematical expressions\",\n\tparameters: calculateSchema,\n\texecute: async (_toolCallId, args) => {\n\t\treturn calculate(args.expression);\n\t},\n};\n"]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { AgentTool } from "../../agent";
|
|
3
|
+
import type { AgentToolResult } from "../types";
|
|
4
|
+
export interface GetCurrentTimeResult extends AgentToolResult<{
|
|
5
|
+
utcTimestamp: number;
|
|
6
|
+
}> {
|
|
7
|
+
}
|
|
8
|
+
export declare function getCurrentTime(timezone?: string): Promise<GetCurrentTimeResult>;
|
|
9
|
+
declare const getCurrentTimeSchema: z.ZodObject<{
|
|
10
|
+
timezone: z.ZodOptional<z.ZodString>;
|
|
11
|
+
}, "strip", z.ZodTypeAny, {
|
|
12
|
+
timezone?: string | undefined;
|
|
13
|
+
}, {
|
|
14
|
+
timezone?: string | undefined;
|
|
15
|
+
}>;
|
|
16
|
+
export declare const getCurrentTimeTool: AgentTool<typeof getCurrentTimeSchema, {
|
|
17
|
+
utcTimestamp: number;
|
|
18
|
+
}>;
|
|
19
|
+
export {};
|
|
20
|
+
//# sourceMappingURL=get-current-time.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-current-time.d.ts","sourceRoot":"","sources":["../../../src/agent/tools/get-current-time.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAEhD,MAAM,WAAW,oBAAqB,SAAQ,eAAe,CAAC;IAAE,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC;CAAG;AAE1F,wBAAsB,cAAc,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAoBrF;AAED,QAAA,MAAM,oBAAoB;;;;;;EAExB,CAAC;AAEH,eAAO,MAAM,kBAAkB,EAAE,SAAS,CAAC,OAAO,oBAAoB,EAAE;IAAE,YAAY,EAAE,MAAM,CAAA;CAAE,CAQ/F,CAAC"}
|