@compilr-dev/agents 0.5.7 → 0.5.9
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 +3 -0
- package/dist/agent.d.ts +116 -6
- package/dist/agent.js +133 -7
- package/dist/context/manager.d.ts +23 -1
- package/dist/context/manager.js +23 -1
- package/dist/providers/claude.d.ts +29 -2
- package/dist/providers/claude.js +32 -1
- package/dist/providers/gemini-native.d.ts +3 -1
- package/dist/providers/gemini-native.js +6 -0
- package/dist/providers/mock.d.ts +3 -0
- package/dist/providers/mock.js +7 -0
- package/dist/providers/openai-compatible.d.ts +3 -1
- package/dist/providers/openai-compatible.js +6 -0
- package/dist/providers/types.d.ts +26 -2
- package/dist/rate-limit/provider-wrapper.d.ts +2 -0
- package/dist/rate-limit/provider-wrapper.js +6 -0
- package/dist/tools/types.d.ts +30 -1
- package/package.json +8 -2
package/README.md
CHANGED
|
@@ -14,6 +14,9 @@
|
|
|
14
14
|
|
|
15
15
|
[](https://www.npmjs.com/package/@compilr-dev/agents)
|
|
16
16
|
[](https://fsl.software/)
|
|
17
|
+
[](https://compilr-dev.github.io/agents/)
|
|
18
|
+
|
|
19
|
+
**[API Reference](https://compilr-dev.github.io/agents/)** | **[llms.txt](https://compilr.dev/llms.txt)** (for AI agents)
|
|
17
20
|
|
|
18
21
|
> [!WARNING]
|
|
19
22
|
> This package is in beta. APIs may change between minor versions.
|
package/dist/agent.d.ts
CHANGED
|
@@ -151,6 +151,10 @@ export type AgentEvent = {
|
|
|
151
151
|
type: 'iteration_limit_extended';
|
|
152
152
|
newMaxIterations: number;
|
|
153
153
|
addedIterations: number;
|
|
154
|
+
} | {
|
|
155
|
+
type: 'model_changed';
|
|
156
|
+
previousModel: string;
|
|
157
|
+
newModel: string;
|
|
154
158
|
} | {
|
|
155
159
|
type: 'llm_retry';
|
|
156
160
|
attempt: number;
|
|
@@ -1128,6 +1132,37 @@ export declare class Agent {
|
|
|
1128
1132
|
* Check if pins are enabled
|
|
1129
1133
|
*/
|
|
1130
1134
|
hasPins(): boolean;
|
|
1135
|
+
/**
|
|
1136
|
+
* Get the current model ID for this agent.
|
|
1137
|
+
*
|
|
1138
|
+
* @returns The model ID string (e.g., 'claude-sonnet-4-20250514')
|
|
1139
|
+
*/
|
|
1140
|
+
getModel(): string;
|
|
1141
|
+
/**
|
|
1142
|
+
* Change the model for subsequent LLM calls (same provider only).
|
|
1143
|
+
*
|
|
1144
|
+
* Takes effect on the next `run()` or `stream()` call — never interrupts
|
|
1145
|
+
* a running turn. Conversation history is preserved (it's provider-agnostic).
|
|
1146
|
+
* Emits a `model_changed` event.
|
|
1147
|
+
*
|
|
1148
|
+
* Use this to switch between models within the same provider, e.g.,
|
|
1149
|
+
* Claude Sonnet → Claude Opus for a harder task, then back.
|
|
1150
|
+
*
|
|
1151
|
+
* @param modelId - The new model ID (e.g., 'claude-opus-4-20250514')
|
|
1152
|
+
* @throws If modelId is empty or not a string
|
|
1153
|
+
*
|
|
1154
|
+
* @example
|
|
1155
|
+
* ```typescript
|
|
1156
|
+
* console.log(agent.getModel()); // 'claude-sonnet-4-20250514'
|
|
1157
|
+
* agent.setModel('claude-opus-4-20250514');
|
|
1158
|
+
* // Next run() uses Opus
|
|
1159
|
+
* const result = await agent.run('Solve this complex problem');
|
|
1160
|
+
* agent.setModel('claude-sonnet-4-20250514'); // Switch back
|
|
1161
|
+
* ```
|
|
1162
|
+
*
|
|
1163
|
+
* @since 0.5.8
|
|
1164
|
+
*/
|
|
1165
|
+
setModel(modelId: string): void;
|
|
1131
1166
|
/** @deprecated Use addPin() instead */
|
|
1132
1167
|
addAnchor(input: AnchorInput): Anchor | undefined;
|
|
1133
1168
|
/** @deprecated Use getPin() instead */
|
|
@@ -1632,11 +1667,38 @@ export declare class Agent {
|
|
|
1632
1667
|
*/
|
|
1633
1668
|
private generateContextSummary;
|
|
1634
1669
|
/**
|
|
1635
|
-
* Register a tool
|
|
1670
|
+
* Register a tool that the agent can call during conversations.
|
|
1671
|
+
*
|
|
1672
|
+
* Tools are functions the LLM can invoke to perform actions like reading
|
|
1673
|
+
* files, running commands, or querying APIs. The LLM sees the tool's name,
|
|
1674
|
+
* description, and parameter schema, then decides when to call it.
|
|
1675
|
+
*
|
|
1676
|
+
* @param tool - Tool definition created with `defineTool()`
|
|
1677
|
+
* @returns The agent instance (for chaining)
|
|
1678
|
+
*
|
|
1679
|
+
* @example
|
|
1680
|
+
* ```typescript
|
|
1681
|
+
* agent.registerTool(defineTool({
|
|
1682
|
+
* name: 'get_weather',
|
|
1683
|
+
* description: 'Get current weather for a city',
|
|
1684
|
+
* parameters: {
|
|
1685
|
+
* type: 'object',
|
|
1686
|
+
* properties: { city: { type: 'string' } },
|
|
1687
|
+
* required: ['city'],
|
|
1688
|
+
* },
|
|
1689
|
+
* execute: async ({ city }) => {
|
|
1690
|
+
* const data = await fetchWeather(city);
|
|
1691
|
+
* return { content: JSON.stringify(data) };
|
|
1692
|
+
* },
|
|
1693
|
+
* }));
|
|
1694
|
+
* ```
|
|
1636
1695
|
*/
|
|
1637
1696
|
registerTool(tool: Tool): this;
|
|
1638
1697
|
/**
|
|
1639
|
-
* Register multiple tools at once
|
|
1698
|
+
* Register multiple tools at once.
|
|
1699
|
+
*
|
|
1700
|
+
* @param tools - Array of tool definitions
|
|
1701
|
+
* @returns The agent instance (for chaining)
|
|
1640
1702
|
*/
|
|
1641
1703
|
registerTools(tools: Tool[]): this;
|
|
1642
1704
|
/**
|
|
@@ -1648,14 +1710,62 @@ export declare class Agent {
|
|
|
1648
1710
|
*/
|
|
1649
1711
|
isToolSilent(name: string): boolean;
|
|
1650
1712
|
/**
|
|
1651
|
-
* Run the agent with a user message
|
|
1713
|
+
* Run the agent with a user message and return the result.
|
|
1714
|
+
*
|
|
1715
|
+
* This is the main entry point for agent interaction. The agent will:
|
|
1716
|
+
* 1. Add the user message to conversation history
|
|
1717
|
+
* 2. Send the conversation to the LLM
|
|
1718
|
+
* 3. Execute any tool calls the LLM requests
|
|
1719
|
+
* 4. Repeat steps 2-3 until the LLM responds with text (no tool calls)
|
|
1720
|
+
* 5. Return the final text response and metadata
|
|
1721
|
+
*
|
|
1722
|
+
* Events are emitted throughout the process via the `onEvent` callback
|
|
1723
|
+
* configured at construction time.
|
|
1724
|
+
*
|
|
1725
|
+
* @param userMessage - The user's message (string or content blocks for images)
|
|
1726
|
+
* @param options - Optional run configuration (max iterations, abort signal, etc.)
|
|
1727
|
+
* @returns The agent's response, tool call history, and context stats
|
|
1728
|
+
*
|
|
1729
|
+
* @example
|
|
1730
|
+
* ```typescript
|
|
1731
|
+
* const result = await agent.run('What files are in this directory?');
|
|
1732
|
+
* console.log(result.response);
|
|
1733
|
+
* console.log(`Used ${result.toolCalls.length} tool calls`);
|
|
1734
|
+
* ```
|
|
1735
|
+
*
|
|
1736
|
+
* @example
|
|
1737
|
+
* ```typescript
|
|
1738
|
+
* // With abort signal
|
|
1739
|
+
* const controller = new AbortController();
|
|
1740
|
+
* const result = await agent.run('Refactor this file', {
|
|
1741
|
+
* signal: controller.signal,
|
|
1742
|
+
* });
|
|
1743
|
+
* ```
|
|
1652
1744
|
*/
|
|
1653
1745
|
run(userMessage: string | ContentBlock[], options?: RunOptions): Promise<AgentRunResult>;
|
|
1654
1746
|
/**
|
|
1655
|
-
* Stream the agent's response
|
|
1747
|
+
* Stream the agent's response as events.
|
|
1748
|
+
*
|
|
1749
|
+
* Yields `AgentEvent` objects in real time as the agent thinks, calls tools,
|
|
1750
|
+
* and generates text. Use this for building interactive UIs that show
|
|
1751
|
+
* progress as it happens.
|
|
1752
|
+
*
|
|
1753
|
+
* @param userMessage - The user's message
|
|
1754
|
+
* @param options - Optional run configuration
|
|
1755
|
+
* @returns An async iterable of agent events
|
|
1656
1756
|
*
|
|
1657
|
-
*
|
|
1658
|
-
*
|
|
1757
|
+
* @example
|
|
1758
|
+
* ```typescript
|
|
1759
|
+
* for await (const event of agent.stream('Explain this code')) {
|
|
1760
|
+
* if (event.type === 'llm_chunk') {
|
|
1761
|
+
* process.stdout.write(event.chunk.text ?? '');
|
|
1762
|
+
* } else if (event.type === 'tool_start') {
|
|
1763
|
+
* console.log(`\nCalling tool: ${event.name}`);
|
|
1764
|
+
* } else if (event.type === 'done') {
|
|
1765
|
+
* console.log('\n\nDone!');
|
|
1766
|
+
* }
|
|
1767
|
+
* }
|
|
1768
|
+
* ```
|
|
1659
1769
|
*/
|
|
1660
1770
|
stream(userMessage: string, options?: RunOptions): AsyncIterable<AgentEvent>;
|
|
1661
1771
|
/**
|
package/dist/agent.js
CHANGED
|
@@ -523,6 +523,47 @@ export class Agent {
|
|
|
523
523
|
hasPins() {
|
|
524
524
|
return this.pinManager !== undefined;
|
|
525
525
|
}
|
|
526
|
+
// --- Model management -------------------------------------------------------
|
|
527
|
+
/**
|
|
528
|
+
* Get the current model ID for this agent.
|
|
529
|
+
*
|
|
530
|
+
* @returns The model ID string (e.g., 'claude-sonnet-4-20250514')
|
|
531
|
+
*/
|
|
532
|
+
getModel() {
|
|
533
|
+
return this.provider.getModel();
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Change the model for subsequent LLM calls (same provider only).
|
|
537
|
+
*
|
|
538
|
+
* Takes effect on the next `run()` or `stream()` call — never interrupts
|
|
539
|
+
* a running turn. Conversation history is preserved (it's provider-agnostic).
|
|
540
|
+
* Emits a `model_changed` event.
|
|
541
|
+
*
|
|
542
|
+
* Use this to switch between models within the same provider, e.g.,
|
|
543
|
+
* Claude Sonnet → Claude Opus for a harder task, then back.
|
|
544
|
+
*
|
|
545
|
+
* @param modelId - The new model ID (e.g., 'claude-opus-4-20250514')
|
|
546
|
+
* @throws If modelId is empty or not a string
|
|
547
|
+
*
|
|
548
|
+
* @example
|
|
549
|
+
* ```typescript
|
|
550
|
+
* console.log(agent.getModel()); // 'claude-sonnet-4-20250514'
|
|
551
|
+
* agent.setModel('claude-opus-4-20250514');
|
|
552
|
+
* // Next run() uses Opus
|
|
553
|
+
* const result = await agent.run('Solve this complex problem');
|
|
554
|
+
* agent.setModel('claude-sonnet-4-20250514'); // Switch back
|
|
555
|
+
* ```
|
|
556
|
+
*
|
|
557
|
+
* @since 0.5.8
|
|
558
|
+
*/
|
|
559
|
+
setModel(modelId) {
|
|
560
|
+
if (!modelId || typeof modelId !== 'string') {
|
|
561
|
+
throw new Error('modelId must be a non-empty string');
|
|
562
|
+
}
|
|
563
|
+
const previousModel = this.provider.getModel();
|
|
564
|
+
this.provider.setModel(modelId);
|
|
565
|
+
this.onEvent?.({ type: 'model_changed', previousModel, newModel: modelId });
|
|
566
|
+
}
|
|
526
567
|
// --- Deprecated aliases (use pin methods instead) --------------------------
|
|
527
568
|
/** @deprecated Use addPin() instead */
|
|
528
569
|
addAnchor(input) {
|
|
@@ -1497,14 +1538,41 @@ export class Agent {
|
|
|
1497
1538
|
return summaryParts.join('\n');
|
|
1498
1539
|
}
|
|
1499
1540
|
/**
|
|
1500
|
-
* Register a tool
|
|
1541
|
+
* Register a tool that the agent can call during conversations.
|
|
1542
|
+
*
|
|
1543
|
+
* Tools are functions the LLM can invoke to perform actions like reading
|
|
1544
|
+
* files, running commands, or querying APIs. The LLM sees the tool's name,
|
|
1545
|
+
* description, and parameter schema, then decides when to call it.
|
|
1546
|
+
*
|
|
1547
|
+
* @param tool - Tool definition created with `defineTool()`
|
|
1548
|
+
* @returns The agent instance (for chaining)
|
|
1549
|
+
*
|
|
1550
|
+
* @example
|
|
1551
|
+
* ```typescript
|
|
1552
|
+
* agent.registerTool(defineTool({
|
|
1553
|
+
* name: 'get_weather',
|
|
1554
|
+
* description: 'Get current weather for a city',
|
|
1555
|
+
* parameters: {
|
|
1556
|
+
* type: 'object',
|
|
1557
|
+
* properties: { city: { type: 'string' } },
|
|
1558
|
+
* required: ['city'],
|
|
1559
|
+
* },
|
|
1560
|
+
* execute: async ({ city }) => {
|
|
1561
|
+
* const data = await fetchWeather(city);
|
|
1562
|
+
* return { content: JSON.stringify(data) };
|
|
1563
|
+
* },
|
|
1564
|
+
* }));
|
|
1565
|
+
* ```
|
|
1501
1566
|
*/
|
|
1502
1567
|
registerTool(tool) {
|
|
1503
1568
|
this.toolRegistry.register(tool);
|
|
1504
1569
|
return this;
|
|
1505
1570
|
}
|
|
1506
1571
|
/**
|
|
1507
|
-
* Register multiple tools at once
|
|
1572
|
+
* Register multiple tools at once.
|
|
1573
|
+
*
|
|
1574
|
+
* @param tools - Array of tool definitions
|
|
1575
|
+
* @returns The agent instance (for chaining)
|
|
1508
1576
|
*/
|
|
1509
1577
|
registerTools(tools) {
|
|
1510
1578
|
for (const tool of tools) {
|
|
@@ -1526,7 +1594,37 @@ export class Agent {
|
|
|
1526
1594
|
return tool?.silent === true;
|
|
1527
1595
|
}
|
|
1528
1596
|
/**
|
|
1529
|
-
* Run the agent with a user message
|
|
1597
|
+
* Run the agent with a user message and return the result.
|
|
1598
|
+
*
|
|
1599
|
+
* This is the main entry point for agent interaction. The agent will:
|
|
1600
|
+
* 1. Add the user message to conversation history
|
|
1601
|
+
* 2. Send the conversation to the LLM
|
|
1602
|
+
* 3. Execute any tool calls the LLM requests
|
|
1603
|
+
* 4. Repeat steps 2-3 until the LLM responds with text (no tool calls)
|
|
1604
|
+
* 5. Return the final text response and metadata
|
|
1605
|
+
*
|
|
1606
|
+
* Events are emitted throughout the process via the `onEvent` callback
|
|
1607
|
+
* configured at construction time.
|
|
1608
|
+
*
|
|
1609
|
+
* @param userMessage - The user's message (string or content blocks for images)
|
|
1610
|
+
* @param options - Optional run configuration (max iterations, abort signal, etc.)
|
|
1611
|
+
* @returns The agent's response, tool call history, and context stats
|
|
1612
|
+
*
|
|
1613
|
+
* @example
|
|
1614
|
+
* ```typescript
|
|
1615
|
+
* const result = await agent.run('What files are in this directory?');
|
|
1616
|
+
* console.log(result.response);
|
|
1617
|
+
* console.log(`Used ${result.toolCalls.length} tool calls`);
|
|
1618
|
+
* ```
|
|
1619
|
+
*
|
|
1620
|
+
* @example
|
|
1621
|
+
* ```typescript
|
|
1622
|
+
* // With abort signal
|
|
1623
|
+
* const controller = new AbortController();
|
|
1624
|
+
* const result = await agent.run('Refactor this file', {
|
|
1625
|
+
* signal: controller.signal,
|
|
1626
|
+
* });
|
|
1627
|
+
* ```
|
|
1530
1628
|
*/
|
|
1531
1629
|
async run(userMessage, options) {
|
|
1532
1630
|
let maxIterations = options?.maxIterations ?? this.maxIterations;
|
|
@@ -1814,9 +1912,19 @@ export class Agent {
|
|
|
1814
1912
|
durationMs: Date.now() - llmStartTime,
|
|
1815
1913
|
});
|
|
1816
1914
|
}
|
|
1915
|
+
// Accumulate every iteration's text into finalResponse, separated by
|
|
1916
|
+
// blank lines. The previous code only assigned finalResponse in the
|
|
1917
|
+
// terminal iteration (no tool uses), which silently dropped any
|
|
1918
|
+
// text the agent produced BEFORE intermediate tool calls. Pattern:
|
|
1919
|
+
// iter 1: "Running test 1..." → tool t1 ← text was lost
|
|
1920
|
+
// iter 2: "Now test 2..." → tool t2 ← text was lost
|
|
1921
|
+
// iter 3: "All done." → no tool ← only this survived
|
|
1922
|
+
// Now every text segment is preserved in finalResponse.
|
|
1923
|
+
if (text) {
|
|
1924
|
+
finalResponse = finalResponse ? finalResponse + '\n\n' + text : text;
|
|
1925
|
+
}
|
|
1817
1926
|
// If no tool uses, we're done
|
|
1818
1927
|
if (toolUses.length === 0) {
|
|
1819
|
-
finalResponse = text;
|
|
1820
1928
|
// Add final assistant response to history (only if non-empty)
|
|
1821
1929
|
// Empty responses can occur after silent tools like 'suggest'
|
|
1822
1930
|
if (text) {
|
|
@@ -2404,10 +2512,28 @@ export class Agent {
|
|
|
2404
2512
|
return result;
|
|
2405
2513
|
}
|
|
2406
2514
|
/**
|
|
2407
|
-
* Stream the agent's response
|
|
2515
|
+
* Stream the agent's response as events.
|
|
2516
|
+
*
|
|
2517
|
+
* Yields `AgentEvent` objects in real time as the agent thinks, calls tools,
|
|
2518
|
+
* and generates text. Use this for building interactive UIs that show
|
|
2519
|
+
* progress as it happens.
|
|
2520
|
+
*
|
|
2521
|
+
* @param userMessage - The user's message
|
|
2522
|
+
* @param options - Optional run configuration
|
|
2523
|
+
* @returns An async iterable of agent events
|
|
2408
2524
|
*
|
|
2409
|
-
*
|
|
2410
|
-
*
|
|
2525
|
+
* @example
|
|
2526
|
+
* ```typescript
|
|
2527
|
+
* for await (const event of agent.stream('Explain this code')) {
|
|
2528
|
+
* if (event.type === 'llm_chunk') {
|
|
2529
|
+
* process.stdout.write(event.chunk.text ?? '');
|
|
2530
|
+
* } else if (event.type === 'tool_start') {
|
|
2531
|
+
* console.log(`\nCalling tool: ${event.name}`);
|
|
2532
|
+
* } else if (event.type === 'done') {
|
|
2533
|
+
* console.log('\n\nDone!');
|
|
2534
|
+
* }
|
|
2535
|
+
* }
|
|
2536
|
+
* ```
|
|
2411
2537
|
*/
|
|
2412
2538
|
async *stream(userMessage, options) {
|
|
2413
2539
|
// Use a simple queue-based approach
|
|
@@ -52,7 +52,29 @@ export interface ContextManagerOptions {
|
|
|
52
52
|
fileTracker?: FileAccessTracker;
|
|
53
53
|
}
|
|
54
54
|
/**
|
|
55
|
-
*
|
|
55
|
+
* Manages the agent's context window — tracks token usage, triggers
|
|
56
|
+
* compaction when the conversation grows too large, and ensures the
|
|
57
|
+
* agent never exceeds the model's context limit.
|
|
58
|
+
*
|
|
59
|
+
* The context window is divided into budgets:
|
|
60
|
+
* - **System prompt** (~15%) — always present, never compacted
|
|
61
|
+
* - **Anchors/pins** (~10%) — critical info that survives compaction
|
|
62
|
+
* - **Conversation history** (~75%) — compacted when budget exceeded
|
|
63
|
+
*
|
|
64
|
+
* When conversation history exceeds its budget, the manager triggers
|
|
65
|
+
* smart windowing (3-zone compaction): recent messages preserved,
|
|
66
|
+
* middle messages summarized, old messages dropped.
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```typescript
|
|
70
|
+
* const agent = new Agent({
|
|
71
|
+
* provider,
|
|
72
|
+
* contextManager: new ContextManager({
|
|
73
|
+
* maxTokens: 100000,
|
|
74
|
+
* budgets: { system: 0.15, anchors: 0.10, conversation: 0.75 },
|
|
75
|
+
* }),
|
|
76
|
+
* });
|
|
77
|
+
* ```
|
|
56
78
|
*/
|
|
57
79
|
export declare class ContextManager {
|
|
58
80
|
private readonly provider;
|
package/dist/context/manager.js
CHANGED
|
@@ -62,7 +62,29 @@ export const DEFAULT_CONTEXT_CONFIG = {
|
|
|
62
62
|
},
|
|
63
63
|
};
|
|
64
64
|
/**
|
|
65
|
-
*
|
|
65
|
+
* Manages the agent's context window — tracks token usage, triggers
|
|
66
|
+
* compaction when the conversation grows too large, and ensures the
|
|
67
|
+
* agent never exceeds the model's context limit.
|
|
68
|
+
*
|
|
69
|
+
* The context window is divided into budgets:
|
|
70
|
+
* - **System prompt** (~15%) — always present, never compacted
|
|
71
|
+
* - **Anchors/pins** (~10%) — critical info that survives compaction
|
|
72
|
+
* - **Conversation history** (~75%) — compacted when budget exceeded
|
|
73
|
+
*
|
|
74
|
+
* When conversation history exceeds its budget, the manager triggers
|
|
75
|
+
* smart windowing (3-zone compaction): recent messages preserved,
|
|
76
|
+
* middle messages summarized, old messages dropped.
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* ```typescript
|
|
80
|
+
* const agent = new Agent({
|
|
81
|
+
* provider,
|
|
82
|
+
* contextManager: new ContextManager({
|
|
83
|
+
* maxTokens: 100000,
|
|
84
|
+
* budgets: { system: 0.15, anchors: 0.10, conversation: 0.75 },
|
|
85
|
+
* }),
|
|
86
|
+
* });
|
|
87
|
+
* ```
|
|
66
88
|
*/
|
|
67
89
|
export class ContextManager {
|
|
68
90
|
provider;
|
|
@@ -67,18 +67,45 @@ export interface ClaudeProviderConfig {
|
|
|
67
67
|
enableExtendedContext?: boolean;
|
|
68
68
|
}
|
|
69
69
|
/**
|
|
70
|
-
*
|
|
70
|
+
* LLM provider for Anthropic's Claude models (Opus, Sonnet, Haiku).
|
|
71
|
+
*
|
|
72
|
+
* Supports streaming, tool use, prompt caching, extended context (1M tokens),
|
|
73
|
+
* and token-efficient tool schemas.
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```typescript
|
|
77
|
+
* const provider = new ClaudeProvider({
|
|
78
|
+
* apiKey: process.env.ANTHROPIC_API_KEY,
|
|
79
|
+
* model: 'claude-sonnet-4-20250514',
|
|
80
|
+
* });
|
|
81
|
+
*
|
|
82
|
+
* const agent = new Agent({
|
|
83
|
+
* provider,
|
|
84
|
+
* systemPrompt: 'You are a helpful assistant.',
|
|
85
|
+
* });
|
|
86
|
+
* ```
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```typescript
|
|
90
|
+
* // Using the factory function
|
|
91
|
+
* const provider = createClaudeProvider({
|
|
92
|
+
* apiKey: 'sk-ant-...',
|
|
93
|
+
* enableExtendedContext: true, // 1M token context
|
|
94
|
+
* });
|
|
95
|
+
* ```
|
|
71
96
|
*/
|
|
72
97
|
export declare class ClaudeProvider implements LLMProvider {
|
|
73
98
|
readonly name = "claude";
|
|
74
99
|
private readonly client;
|
|
75
|
-
private
|
|
100
|
+
private defaultModel;
|
|
76
101
|
private readonly defaultMaxTokens;
|
|
77
102
|
private readonly enablePromptCaching;
|
|
78
103
|
private readonly enableTokenEfficientTools;
|
|
79
104
|
private readonly enableExtendedContext;
|
|
80
105
|
private readonly estimateTokensFn;
|
|
81
106
|
constructor(config: ClaudeProviderConfig);
|
|
107
|
+
getModel(): string;
|
|
108
|
+
setModel(modelId: string): void;
|
|
82
109
|
/**
|
|
83
110
|
* Send messages and stream the response
|
|
84
111
|
*/
|
package/dist/providers/claude.js
CHANGED
|
@@ -22,7 +22,32 @@ const DEFAULT_MODEL = 'claude-sonnet-4-6';
|
|
|
22
22
|
*/
|
|
23
23
|
const DEFAULT_MAX_TOKENS = 4096;
|
|
24
24
|
/**
|
|
25
|
-
*
|
|
25
|
+
* LLM provider for Anthropic's Claude models (Opus, Sonnet, Haiku).
|
|
26
|
+
*
|
|
27
|
+
* Supports streaming, tool use, prompt caching, extended context (1M tokens),
|
|
28
|
+
* and token-efficient tool schemas.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* const provider = new ClaudeProvider({
|
|
33
|
+
* apiKey: process.env.ANTHROPIC_API_KEY,
|
|
34
|
+
* model: 'claude-sonnet-4-20250514',
|
|
35
|
+
* });
|
|
36
|
+
*
|
|
37
|
+
* const agent = new Agent({
|
|
38
|
+
* provider,
|
|
39
|
+
* systemPrompt: 'You are a helpful assistant.',
|
|
40
|
+
* });
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```typescript
|
|
45
|
+
* // Using the factory function
|
|
46
|
+
* const provider = createClaudeProvider({
|
|
47
|
+
* apiKey: 'sk-ant-...',
|
|
48
|
+
* enableExtendedContext: true, // 1M token context
|
|
49
|
+
* });
|
|
50
|
+
* ```
|
|
26
51
|
*/
|
|
27
52
|
export class ClaudeProvider {
|
|
28
53
|
name = 'claude';
|
|
@@ -46,6 +71,12 @@ export class ClaudeProvider {
|
|
|
46
71
|
this.estimateTokensFn =
|
|
47
72
|
config.estimateTokens ?? ((s) => Math.ceil(s.length / 4));
|
|
48
73
|
}
|
|
74
|
+
getModel() {
|
|
75
|
+
return this.defaultModel;
|
|
76
|
+
}
|
|
77
|
+
setModel(modelId) {
|
|
78
|
+
this.defaultModel = modelId;
|
|
79
|
+
}
|
|
49
80
|
/**
|
|
50
81
|
* Send messages and stream the response
|
|
51
82
|
*/
|
|
@@ -50,10 +50,12 @@ export interface GeminiNativeProviderConfig {
|
|
|
50
50
|
export declare class GeminiNativeProvider implements LLMProvider {
|
|
51
51
|
readonly name = "gemini";
|
|
52
52
|
private readonly client;
|
|
53
|
-
private
|
|
53
|
+
private defaultModel;
|
|
54
54
|
private readonly defaultMaxTokens;
|
|
55
55
|
private readonly estimateTokensFn;
|
|
56
56
|
constructor(config: GeminiNativeProviderConfig);
|
|
57
|
+
getModel(): string;
|
|
58
|
+
setModel(modelId: string): void;
|
|
57
59
|
/**
|
|
58
60
|
* Send messages and stream the response
|
|
59
61
|
*/
|
|
@@ -45,6 +45,12 @@ export class GeminiNativeProvider {
|
|
|
45
45
|
this.estimateTokensFn =
|
|
46
46
|
config.estimateTokens ?? ((s) => Math.ceil(s.length / 4));
|
|
47
47
|
}
|
|
48
|
+
getModel() {
|
|
49
|
+
return this.defaultModel;
|
|
50
|
+
}
|
|
51
|
+
setModel(modelId) {
|
|
52
|
+
this.defaultModel = modelId;
|
|
53
|
+
}
|
|
48
54
|
/**
|
|
49
55
|
* Send messages and stream the response
|
|
50
56
|
*/
|
package/dist/providers/mock.d.ts
CHANGED
|
@@ -78,7 +78,10 @@ export declare class MockProvider implements LLMProvider {
|
|
|
78
78
|
private readonly throwOnEmpty;
|
|
79
79
|
private callCount;
|
|
80
80
|
private readonly callHistory;
|
|
81
|
+
private mockModel;
|
|
81
82
|
constructor(config?: MockProviderConfig);
|
|
83
|
+
getModel(): string;
|
|
84
|
+
setModel(modelId: string): void;
|
|
82
85
|
/**
|
|
83
86
|
* Add a response (text string or structured response with tool calls)
|
|
84
87
|
*/
|
package/dist/providers/mock.js
CHANGED
|
@@ -38,10 +38,17 @@ export class MockProvider {
|
|
|
38
38
|
throwOnEmpty;
|
|
39
39
|
callCount = 0;
|
|
40
40
|
callHistory = [];
|
|
41
|
+
mockModel = 'mock-model';
|
|
41
42
|
constructor(config = {}) {
|
|
42
43
|
this.defaultDelay = config.defaultDelay ?? 0;
|
|
43
44
|
this.throwOnEmpty = config.throwOnEmpty ?? true;
|
|
44
45
|
}
|
|
46
|
+
getModel() {
|
|
47
|
+
return this.mockModel;
|
|
48
|
+
}
|
|
49
|
+
setModel(modelId) {
|
|
50
|
+
this.mockModel = modelId;
|
|
51
|
+
}
|
|
45
52
|
/**
|
|
46
53
|
* Add a response (text string or structured response with tool calls)
|
|
47
54
|
*/
|
|
@@ -121,11 +121,13 @@ export declare abstract class OpenAICompatibleProvider implements LLMProvider {
|
|
|
121
121
|
*/
|
|
122
122
|
abstract readonly name: string;
|
|
123
123
|
protected readonly baseUrl: string;
|
|
124
|
-
protected
|
|
124
|
+
protected defaultModel: string;
|
|
125
125
|
protected readonly defaultMaxTokens: number;
|
|
126
126
|
protected readonly timeout: number;
|
|
127
127
|
protected readonly estimateTokensFn: (text: string) => number;
|
|
128
128
|
constructor(config: OpenAICompatibleConfig);
|
|
129
|
+
getModel(): string;
|
|
130
|
+
setModel(modelId: string): void;
|
|
129
131
|
/**
|
|
130
132
|
* Get authentication headers for API requests
|
|
131
133
|
* @returns Headers object with auth credentials
|
|
@@ -46,6 +46,12 @@ export class OpenAICompatibleProvider {
|
|
|
46
46
|
this.estimateTokensFn =
|
|
47
47
|
config.estimateTokens ?? ((s) => Math.ceil(s.length / 4));
|
|
48
48
|
}
|
|
49
|
+
getModel() {
|
|
50
|
+
return this.defaultModel;
|
|
51
|
+
}
|
|
52
|
+
setModel(modelId) {
|
|
53
|
+
this.defaultModel = modelId;
|
|
54
|
+
}
|
|
49
55
|
/**
|
|
50
56
|
* Extract cache statistics from response headers.
|
|
51
57
|
* Override in subclasses for providers that return cache stats in headers (e.g., Fireworks).
|
|
@@ -226,17 +226,41 @@ export interface ToolResult {
|
|
|
226
226
|
/**
|
|
227
227
|
* LLM Provider interface - all providers must implement this
|
|
228
228
|
*/
|
|
229
|
+
/**
|
|
230
|
+
* Interface for LLM providers. Implement this to add support for a new AI model.
|
|
231
|
+
*
|
|
232
|
+
* Built-in providers: `ClaudeProvider`, `OpenAIProvider`, `GeminiProvider`,
|
|
233
|
+
* `OllamaProvider`, `TogetherProvider`, `GroqProvider`, `FireworksProvider`,
|
|
234
|
+
* `PerplexityProvider`, `OpenRouterProvider`.
|
|
235
|
+
*
|
|
236
|
+
* For OpenAI-compatible APIs, extend `OpenAICompatibleProvider` instead of
|
|
237
|
+
* implementing this interface directly.
|
|
238
|
+
*/
|
|
229
239
|
export interface LLMProvider {
|
|
230
240
|
/**
|
|
231
|
-
* Provider
|
|
241
|
+
* Provider identifier (e.g., 'claude', 'openai', 'gemini')
|
|
232
242
|
*/
|
|
233
243
|
readonly name: string;
|
|
234
244
|
/**
|
|
235
|
-
* Send messages and
|
|
245
|
+
* Send messages to the LLM and stream the response.
|
|
246
|
+
*
|
|
247
|
+
* Yields `StreamChunk` objects containing text fragments, tool calls,
|
|
248
|
+
* usage stats, and other provider-specific data.
|
|
236
249
|
*/
|
|
237
250
|
chat(messages: Message[], options?: ChatOptions): AsyncIterable<StreamChunk>;
|
|
238
251
|
/**
|
|
239
252
|
* Count tokens in messages (optional, provider-specific)
|
|
240
253
|
*/
|
|
241
254
|
countTokens?(messages: Message[]): Promise<number>;
|
|
255
|
+
/**
|
|
256
|
+
* Get the current default model ID.
|
|
257
|
+
*/
|
|
258
|
+
getModel(): string;
|
|
259
|
+
/**
|
|
260
|
+
* Change the default model for subsequent calls. Same provider only.
|
|
261
|
+
* Takes effect on the next chat() call, not mid-stream.
|
|
262
|
+
*
|
|
263
|
+
* @param modelId - The new model ID (e.g., 'claude-opus-4-20250514')
|
|
264
|
+
*/
|
|
265
|
+
setModel(modelId: string): void;
|
|
242
266
|
}
|
|
@@ -32,6 +32,8 @@ export declare class RateLimitedProvider implements LLMProvider {
|
|
|
32
32
|
private readonly rateLimiter;
|
|
33
33
|
private readonly retryConfig;
|
|
34
34
|
constructor(provider: LLMProvider, config?: RateLimitRetryConfig);
|
|
35
|
+
getModel(): string;
|
|
36
|
+
setModel(modelId: string): void;
|
|
35
37
|
/**
|
|
36
38
|
* Get the rate limiter instance for statistics
|
|
37
39
|
*/
|
|
@@ -38,6 +38,12 @@ export class RateLimitedProvider {
|
|
|
38
38
|
this.rateLimiter = config.rateLimit ? createRateLimiter(config.rateLimit) : createRateLimiter();
|
|
39
39
|
this.retryConfig = config.retry ?? {};
|
|
40
40
|
}
|
|
41
|
+
getModel() {
|
|
42
|
+
return this.provider.getModel();
|
|
43
|
+
}
|
|
44
|
+
setModel(modelId) {
|
|
45
|
+
this.provider.setModel(modelId);
|
|
46
|
+
}
|
|
41
47
|
/**
|
|
42
48
|
* Get the rate limiter instance for statistics
|
|
43
49
|
*/
|
package/dist/tools/types.d.ts
CHANGED
|
@@ -73,7 +73,36 @@ export interface ToolExecutionContext {
|
|
|
73
73
|
*/
|
|
74
74
|
export type ToolHandler<T = object> = (input: T, context?: ToolExecutionContext) => Promise<ToolExecutionResult>;
|
|
75
75
|
/**
|
|
76
|
-
*
|
|
76
|
+
* A tool that the agent can call during conversations.
|
|
77
|
+
*
|
|
78
|
+
* Tools are the primary way agents interact with the outside world — reading
|
|
79
|
+
* files, running commands, querying APIs, etc. Each tool has a definition
|
|
80
|
+
* (name, description, parameters) that the LLM sees, and an execute function
|
|
81
|
+
* that runs when the LLM decides to call it.
|
|
82
|
+
*
|
|
83
|
+
* Use `defineTool()` to create tools with type-safe parameters.
|
|
84
|
+
*
|
|
85
|
+
* @typeParam T - The type of the tool's input parameters
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```typescript
|
|
89
|
+
* const readFileTool: Tool<{ path: string }> = {
|
|
90
|
+
* definition: {
|
|
91
|
+
* name: 'read_file',
|
|
92
|
+
* description: 'Read the contents of a file',
|
|
93
|
+
* parameters: {
|
|
94
|
+
* type: 'object',
|
|
95
|
+
* properties: { path: { type: 'string', description: 'File path' } },
|
|
96
|
+
* required: ['path'],
|
|
97
|
+
* },
|
|
98
|
+
* },
|
|
99
|
+
* execute: async ({ path }) => {
|
|
100
|
+
* const content = await fs.readFile(path, 'utf-8');
|
|
101
|
+
* return { content };
|
|
102
|
+
* },
|
|
103
|
+
* readonly: true, // Safe for parallel execution
|
|
104
|
+
* };
|
|
105
|
+
* ```
|
|
77
106
|
*/
|
|
78
107
|
export interface Tool<T = object> {
|
|
79
108
|
definition: ToolDefinition;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@compilr-dev/agents",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.9",
|
|
4
4
|
"description": "Lightweight multi-LLM agent library for building CLI AI assistants",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -28,7 +28,11 @@
|
|
|
28
28
|
"test:watch": "vitest",
|
|
29
29
|
"test:coverage": "vitest run --coverage",
|
|
30
30
|
"prepublishOnly": "npm run clean && npm run lint && npm run test && npm run build",
|
|
31
|
-
"typecheck": "tsc --noEmit"
|
|
31
|
+
"typecheck": "tsc --noEmit",
|
|
32
|
+
"docs": "typedoc && node scripts/inject-frontmatter.mjs && typedoc --options typedoc.llms.json && mv docs/llms/README.md docs/llms.txt",
|
|
33
|
+
"docs:api": "typedoc",
|
|
34
|
+
"docs:llms": "typedoc --options typedoc.llms.json",
|
|
35
|
+
"docs:watch": "typedoc --watch"
|
|
32
36
|
},
|
|
33
37
|
"repository": {
|
|
34
38
|
"type": "git",
|
|
@@ -76,6 +80,8 @@
|
|
|
76
80
|
"dotenv": "^17.2.3",
|
|
77
81
|
"eslint": "^9.39.1",
|
|
78
82
|
"prettier": "^3.7.1",
|
|
83
|
+
"typedoc": "^0.28.19",
|
|
84
|
+
"typedoc-plugin-markdown": "^4.11.0",
|
|
79
85
|
"typescript": "^5.3.0",
|
|
80
86
|
"typescript-eslint": "^8.48.0",
|
|
81
87
|
"vitest": "^4.0.18"
|