skimpyclaw 0.1.0
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 +230 -0
- package/dist/__tests__/agent.test.d.ts +1 -0
- package/dist/__tests__/agent.test.js +131 -0
- package/dist/__tests__/api.test.d.ts +1 -0
- package/dist/__tests__/api.test.js +1227 -0
- package/dist/__tests__/audit.test.d.ts +1 -0
- package/dist/__tests__/audit.test.js +122 -0
- package/dist/__tests__/cache.test.d.ts +1 -0
- package/dist/__tests__/cache.test.js +65 -0
- package/dist/__tests__/channels.test.d.ts +1 -0
- package/dist/__tests__/channels.test.js +85 -0
- package/dist/__tests__/cli.integration.test.d.ts +1 -0
- package/dist/__tests__/cli.integration.test.js +16 -0
- package/dist/__tests__/cli.test.d.ts +1 -0
- package/dist/__tests__/cli.test.js +230 -0
- package/dist/__tests__/code-agents-executor.test.d.ts +1 -0
- package/dist/__tests__/code-agents-executor.test.js +75 -0
- package/dist/__tests__/code-agents-orchestrator.test.d.ts +1 -0
- package/dist/__tests__/code-agents-orchestrator.test.js +149 -0
- package/dist/__tests__/code-agents-parser.test.d.ts +1 -0
- package/dist/__tests__/code-agents-parser.test.js +39 -0
- package/dist/__tests__/code-agents-utils.test.d.ts +1 -0
- package/dist/__tests__/code-agents-utils.test.js +41 -0
- package/dist/__tests__/config.test.d.ts +1 -0
- package/dist/__tests__/config.test.js +46 -0
- package/dist/__tests__/cron.test.d.ts +1 -0
- package/dist/__tests__/cron.test.js +66 -0
- package/dist/__tests__/dashboard-mode.test.d.ts +1 -0
- package/dist/__tests__/dashboard-mode.test.js +145 -0
- package/dist/__tests__/dashboard.test.d.ts +1 -0
- package/dist/__tests__/dashboard.test.js +43 -0
- package/dist/__tests__/doctor.formatters.test.d.ts +1 -0
- package/dist/__tests__/doctor.formatters.test.js +65 -0
- package/dist/__tests__/doctor.index.test.d.ts +1 -0
- package/dist/__tests__/doctor.index.test.js +48 -0
- package/dist/__tests__/doctor.runner.test.d.ts +1 -0
- package/dist/__tests__/doctor.runner.test.js +204 -0
- package/dist/__tests__/exec-approval.test.d.ts +1 -0
- package/dist/__tests__/exec-approval.test.js +323 -0
- package/dist/__tests__/file-lock.test.d.ts +1 -0
- package/dist/__tests__/file-lock.test.js +92 -0
- package/dist/__tests__/langfuse.test.d.ts +1 -0
- package/dist/__tests__/langfuse.test.js +40 -0
- package/dist/__tests__/model-selection.test.d.ts +1 -0
- package/dist/__tests__/model-selection.test.js +62 -0
- package/dist/__tests__/orchestrator.test.d.ts +1 -0
- package/dist/__tests__/orchestrator.test.js +425 -0
- package/dist/__tests__/providers-init.test.d.ts +1 -0
- package/dist/__tests__/providers-init.test.js +32 -0
- package/dist/__tests__/providers-routing.test.d.ts +1 -0
- package/dist/__tests__/providers-routing.test.js +25 -0
- package/dist/__tests__/providers-utils.test.d.ts +1 -0
- package/dist/__tests__/providers-utils.test.js +54 -0
- package/dist/__tests__/security.test.d.ts +1 -0
- package/dist/__tests__/security.test.js +22 -0
- package/dist/__tests__/sessions.test.d.ts +1 -0
- package/dist/__tests__/sessions.test.js +147 -0
- package/dist/__tests__/setup.test.d.ts +1 -0
- package/dist/__tests__/setup.test.js +114 -0
- package/dist/__tests__/skills.test.d.ts +1 -0
- package/dist/__tests__/skills.test.js +333 -0
- package/dist/__tests__/subagent.test.d.ts +1 -0
- package/dist/__tests__/subagent.test.js +240 -0
- package/dist/__tests__/telegram-utils.test.d.ts +1 -0
- package/dist/__tests__/telegram-utils.test.js +22 -0
- package/dist/__tests__/telegram.test.d.ts +1 -0
- package/dist/__tests__/telegram.test.js +42 -0
- package/dist/__tests__/token-efficiency.test.d.ts +1 -0
- package/dist/__tests__/token-efficiency.test.js +38 -0
- package/dist/__tests__/tool-guard.test.d.ts +1 -0
- package/dist/__tests__/tool-guard.test.js +105 -0
- package/dist/__tests__/tools.test.d.ts +1 -0
- package/dist/__tests__/tools.test.js +589 -0
- package/dist/__tests__/usage.test.d.ts +1 -0
- package/dist/__tests__/usage.test.js +197 -0
- package/dist/__tests__/voice.test.d.ts +1 -0
- package/dist/__tests__/voice.test.js +214 -0
- package/dist/agent.d.ts +24 -0
- package/dist/agent.js +269 -0
- package/dist/api.d.ts +3 -0
- package/dist/api.js +943 -0
- package/dist/audit.d.ts +26 -0
- package/dist/audit.js +121 -0
- package/dist/cache.d.ts +8 -0
- package/dist/cache.js +24 -0
- package/dist/channels/telegram/handlers.d.ts +41 -0
- package/dist/channels/telegram/handlers.js +498 -0
- package/dist/channels/telegram/index.d.ts +14 -0
- package/dist/channels/telegram/index.js +326 -0
- package/dist/channels/telegram/types.d.ts +26 -0
- package/dist/channels/telegram/types.js +31 -0
- package/dist/channels/telegram/utils.d.ts +25 -0
- package/dist/channels/telegram/utils.js +256 -0
- package/dist/channels.d.ts +11 -0
- package/dist/channels.js +118 -0
- package/dist/cli.d.ts +5 -0
- package/dist/cli.js +768 -0
- package/dist/code-agents/executor.d.ts +5 -0
- package/dist/code-agents/executor.js +463 -0
- package/dist/code-agents/index.d.ts +22 -0
- package/dist/code-agents/index.js +199 -0
- package/dist/code-agents/orchestrator.d.ts +23 -0
- package/dist/code-agents/orchestrator.js +403 -0
- package/dist/code-agents/parser.d.ts +21 -0
- package/dist/code-agents/parser.js +197 -0
- package/dist/code-agents/registry.d.ts +27 -0
- package/dist/code-agents/registry.js +147 -0
- package/dist/code-agents/types.d.ts +66 -0
- package/dist/code-agents/types.js +4 -0
- package/dist/code-agents/utils.d.ts +36 -0
- package/dist/code-agents/utils.js +236 -0
- package/dist/config.d.ts +19 -0
- package/dist/config.js +123 -0
- package/dist/cron.d.ts +49 -0
- package/dist/cron.js +400 -0
- package/dist/dashboard/assets/index-CZJCvMSN.js +65 -0
- package/dist/dashboard/assets/index-EAg6lqF5.css +1 -0
- package/dist/dashboard/favicon.svg +3 -0
- package/dist/dashboard/index.html +21 -0
- package/dist/dashboard-frontend.d.ts +7 -0
- package/dist/dashboard-frontend.js +86 -0
- package/dist/dashboard.d.ts +8 -0
- package/dist/dashboard.js +4071 -0
- package/dist/digests.d.ts +36 -0
- package/dist/digests.js +338 -0
- package/dist/discord.d.ts +8 -0
- package/dist/discord.js +828 -0
- package/dist/doctor/checks.d.ts +18 -0
- package/dist/doctor/checks.js +368 -0
- package/dist/doctor/formatters.d.ts +3 -0
- package/dist/doctor/formatters.js +44 -0
- package/dist/doctor/index.d.ts +8 -0
- package/dist/doctor/index.js +7 -0
- package/dist/doctor/runner.d.ts +3 -0
- package/dist/doctor/runner.js +109 -0
- package/dist/doctor/types.d.ts +20 -0
- package/dist/doctor/types.js +1 -0
- package/dist/exec-approval.d.ts +101 -0
- package/dist/exec-approval.js +432 -0
- package/dist/file-lock.d.ts +34 -0
- package/dist/file-lock.js +81 -0
- package/dist/gateway.d.ts +8 -0
- package/dist/gateway.js +114 -0
- package/dist/heartbeat.d.ts +4 -0
- package/dist/heartbeat.js +101 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +75 -0
- package/dist/langfuse.d.ts +34 -0
- package/dist/langfuse.js +145 -0
- package/dist/mcp-context-a8c.d.ts +13 -0
- package/dist/mcp-context-a8c.js +34 -0
- package/dist/model-selection.d.ts +18 -0
- package/dist/model-selection.js +50 -0
- package/dist/orchestrator.d.ts +15 -0
- package/dist/orchestrator.js +676 -0
- package/dist/providers/anthropic.d.ts +7 -0
- package/dist/providers/anthropic.js +319 -0
- package/dist/providers/codex.d.ts +17 -0
- package/dist/providers/codex.js +508 -0
- package/dist/providers/content.d.ts +21 -0
- package/dist/providers/content.js +55 -0
- package/dist/providers/index.d.ts +13 -0
- package/dist/providers/index.js +138 -0
- package/dist/providers/observability.d.ts +19 -0
- package/dist/providers/observability.js +94 -0
- package/dist/providers/openai.d.ts +10 -0
- package/dist/providers/openai.js +310 -0
- package/dist/providers/tool-guard.d.ts +30 -0
- package/dist/providers/tool-guard.js +89 -0
- package/dist/providers/types.d.ts +34 -0
- package/dist/providers/types.js +2 -0
- package/dist/providers/utils.d.ts +65 -0
- package/dist/providers/utils.js +199 -0
- package/dist/security.d.ts +8 -0
- package/dist/security.js +113 -0
- package/dist/service.d.ts +8 -0
- package/dist/service.js +38 -0
- package/dist/sessions.d.ts +35 -0
- package/dist/sessions.js +142 -0
- package/dist/setup.d.ts +36 -0
- package/dist/setup.js +821 -0
- package/dist/skills-types.d.ts +65 -0
- package/dist/skills-types.js +2 -0
- package/dist/skills.d.ts +32 -0
- package/dist/skills.js +260 -0
- package/dist/subagent.d.ts +19 -0
- package/dist/subagent.js +376 -0
- package/dist/telegram.d.ts +2 -0
- package/dist/telegram.js +11 -0
- package/dist/tools/bash-tool.d.ts +3 -0
- package/dist/tools/bash-tool.js +59 -0
- package/dist/tools/browser-tool.d.ts +3 -0
- package/dist/tools/browser-tool.js +265 -0
- package/dist/tools/definitions.d.ts +432 -0
- package/dist/tools/definitions.js +181 -0
- package/dist/tools/execute-context.d.ts +26 -0
- package/dist/tools/execute-context.js +1 -0
- package/dist/tools/file-tools.d.ts +8 -0
- package/dist/tools/file-tools.js +67 -0
- package/dist/tools/path-utils.d.ts +1 -0
- package/dist/tools/path-utils.js +8 -0
- package/dist/tools.d.ts +24 -0
- package/dist/tools.js +281 -0
- package/dist/types.d.ts +259 -0
- package/dist/types.js +2 -0
- package/dist/usage.d.ts +76 -0
- package/dist/usage.js +150 -0
- package/dist/voice.d.ts +37 -0
- package/dist/voice.js +461 -0
- package/package.json +70 -0
- package/templates/AGENTS.md +38 -0
- package/templates/BOOT.md +23 -0
- package/templates/BOOTSTRAP.md +26 -0
- package/templates/HEARTBEAT.md +5 -0
- package/templates/IDENTITY.md +5 -0
- package/templates/MEMORY.md +24 -0
- package/templates/SOUL.md +92 -0
- package/templates/TOOLS.md +30 -0
- package/templates/USER.md +31 -0
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
// Anthropic Provider
|
|
2
|
+
import { startObservation } from '@langfuse/tracing';
|
|
3
|
+
import { buildSystemParam, addToolCacheBreakpoint, contentToText, stripProvider, buildThinkingConfig, truncateToolResult } from './utils.js';
|
|
4
|
+
import { toAnthropicUsageDetails, toCostDetails } from './observability.js';
|
|
5
|
+
import { getToolDefinitions, executeTool } from '../tools.js';
|
|
6
|
+
import { ToolCallGuard } from './tool-guard.js';
|
|
7
|
+
import { startTrace, addEvent } from '../audit.js';
|
|
8
|
+
import { buildUsageRecord, recordUsage } from '../usage.js';
|
|
9
|
+
let anthropicClient = null;
|
|
10
|
+
export function setAnthropicClient(client) {
|
|
11
|
+
anthropicClient = client;
|
|
12
|
+
}
|
|
13
|
+
export function getAnthropicClient() {
|
|
14
|
+
return anthropicClient;
|
|
15
|
+
}
|
|
16
|
+
export function isAnthropicAvailable() {
|
|
17
|
+
return anthropicClient !== null;
|
|
18
|
+
}
|
|
19
|
+
const LANGFUSE_APP_NAME = 'skimpyclaw';
|
|
20
|
+
function recordAnthropicUsage(params) {
|
|
21
|
+
const usage = params.usage;
|
|
22
|
+
const inputTokens = typeof usage?.input_tokens === 'number' ? usage.input_tokens : 0;
|
|
23
|
+
const outputTokens = typeof usage?.output_tokens === 'number' ? usage.output_tokens : 0;
|
|
24
|
+
if (inputTokens === 0 && outputTokens === 0)
|
|
25
|
+
return;
|
|
26
|
+
const cost = toCostDetails(params.model, usage);
|
|
27
|
+
recordUsage(buildUsageRecord({
|
|
28
|
+
model: params.model,
|
|
29
|
+
provider: 'anthropic',
|
|
30
|
+
inputTokens,
|
|
31
|
+
outputTokens,
|
|
32
|
+
inputCost: cost?.input ?? 0,
|
|
33
|
+
outputCost: cost?.output ?? 0,
|
|
34
|
+
totalCost: cost?.total ?? 0,
|
|
35
|
+
trigger: params.trigger || 'api',
|
|
36
|
+
agentId: params.agentId,
|
|
37
|
+
cacheReadTokens: typeof usage?.cache_read_input_tokens === 'number' ? usage.cache_read_input_tokens : undefined,
|
|
38
|
+
cacheCreationTokens: typeof usage?.cache_creation_input_tokens === 'number' ? usage.cache_creation_input_tokens : undefined,
|
|
39
|
+
}));
|
|
40
|
+
}
|
|
41
|
+
async function startGenerationObservation(name, attributes) {
|
|
42
|
+
// Check langfuse enabled through dynamic import to avoid circular deps
|
|
43
|
+
const { isLangfuseEnabled } = await import('../langfuse.js');
|
|
44
|
+
if (!isLangfuseEnabled())
|
|
45
|
+
return null;
|
|
46
|
+
attributes.metadata = { app: LANGFUSE_APP_NAME, ...attributes.metadata };
|
|
47
|
+
return startObservation(name, attributes, { asType: 'generation' });
|
|
48
|
+
}
|
|
49
|
+
export async function chatAnthropic(params) {
|
|
50
|
+
if (!anthropicClient) {
|
|
51
|
+
throw new Error('Anthropic client not initialized');
|
|
52
|
+
}
|
|
53
|
+
const { messages, options, config } = params;
|
|
54
|
+
const modelId = stripProvider(options.model);
|
|
55
|
+
const systemMessage = messages.find(m => m.role === 'system');
|
|
56
|
+
const chatMessages = messages
|
|
57
|
+
.filter(m => m.role !== 'system')
|
|
58
|
+
.map(m => ({
|
|
59
|
+
role: m.role,
|
|
60
|
+
content: m.content,
|
|
61
|
+
}));
|
|
62
|
+
// Build request parameters
|
|
63
|
+
const cacheEnabled = config.models?.promptCaching !== false;
|
|
64
|
+
const anthropicParams = {
|
|
65
|
+
model: modelId,
|
|
66
|
+
max_tokens: options.maxTokens || 4096,
|
|
67
|
+
messages: chatMessages,
|
|
68
|
+
};
|
|
69
|
+
const systemParam = buildSystemParam(contentToText(systemMessage?.content || ''), cacheEnabled);
|
|
70
|
+
if (systemParam) {
|
|
71
|
+
anthropicParams.system = systemParam;
|
|
72
|
+
}
|
|
73
|
+
// Add extended thinking if requested
|
|
74
|
+
const thinkingConfig = buildThinkingConfig(options.thinking);
|
|
75
|
+
if (thinkingConfig) {
|
|
76
|
+
anthropicParams.thinking = {
|
|
77
|
+
type: 'enabled',
|
|
78
|
+
budget_tokens: thinkingConfig.budget,
|
|
79
|
+
};
|
|
80
|
+
anthropicParams.max_tokens = Math.max(anthropicParams.max_tokens, thinkingConfig.maxTokens);
|
|
81
|
+
}
|
|
82
|
+
const genObs = await startGenerationObservation(`anthropic:${modelId}`, {
|
|
83
|
+
input: { system: systemMessage?.content, messages: chatMessages },
|
|
84
|
+
model: modelId,
|
|
85
|
+
modelParameters: {
|
|
86
|
+
max_tokens: anthropicParams.max_tokens,
|
|
87
|
+
...(options.thinking && options.thinking !== 'none' ? { thinking: options.thinking } : {}),
|
|
88
|
+
},
|
|
89
|
+
metadata: { provider: 'anthropic' },
|
|
90
|
+
});
|
|
91
|
+
try {
|
|
92
|
+
const response = await anthropicClient.messages.create(anthropicParams);
|
|
93
|
+
const usage = response.usage;
|
|
94
|
+
// Log cache metrics
|
|
95
|
+
if (usage?.cache_read_input_tokens > 0 || usage?.cache_creation_input_tokens > 0) {
|
|
96
|
+
console.log(`[cache] read=${usage.cache_read_input_tokens || 0} created=${usage.cache_creation_input_tokens || 0}`);
|
|
97
|
+
}
|
|
98
|
+
recordAnthropicUsage({ model: modelId, usage, trigger: 'api' });
|
|
99
|
+
// Extract text content
|
|
100
|
+
const textContent = response.content.find(c => c.type === 'text');
|
|
101
|
+
const text = textContent?.text || '';
|
|
102
|
+
genObs?.update({
|
|
103
|
+
output: { text },
|
|
104
|
+
usageDetails: toAnthropicUsageDetails(usage),
|
|
105
|
+
costDetails: toCostDetails(modelId, usage),
|
|
106
|
+
});
|
|
107
|
+
genObs?.end();
|
|
108
|
+
return text;
|
|
109
|
+
}
|
|
110
|
+
catch (err) {
|
|
111
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
112
|
+
genObs?.update({ level: 'ERROR', statusMessage: errorMessage, output: { error: errorMessage } });
|
|
113
|
+
genObs?.end();
|
|
114
|
+
throw err;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
export async function chatWithToolsAnthropic(params) {
|
|
118
|
+
if (!anthropicClient) {
|
|
119
|
+
throw new Error('Anthropic client not initialized');
|
|
120
|
+
}
|
|
121
|
+
const { messages, options, config, toolConfig, toolContext } = params;
|
|
122
|
+
const modelId = stripProvider(options.model);
|
|
123
|
+
const maxIterations = toolConfig.maxIterations || 20;
|
|
124
|
+
// Resolve tools once at start of agent loop
|
|
125
|
+
const includeSpawn = !!(toolContext?.chatId && toolContext?.fullConfig);
|
|
126
|
+
const toolDefs = await getToolDefinitions(toolConfig, { includeSpawnSubagent: includeSpawn, projects: toolContext?.fullConfig?.projects });
|
|
127
|
+
// Enable prompt caching for system + tools
|
|
128
|
+
const cacheEnabled = config.models?.promptCaching !== false;
|
|
129
|
+
if (cacheEnabled)
|
|
130
|
+
addToolCacheBreakpoint(toolDefs);
|
|
131
|
+
// Build system param with OAuth identity guard
|
|
132
|
+
const systemMessage = messages.find(m => m.role === 'system');
|
|
133
|
+
const systemParam = buildSystemParam(contentToText(systemMessage?.content || ''), cacheEnabled);
|
|
134
|
+
// Build initial messages (exclude system)
|
|
135
|
+
const apiMessages = messages
|
|
136
|
+
.filter(m => m.role !== 'system')
|
|
137
|
+
.map(m => ({ role: m.role, content: m.content }));
|
|
138
|
+
// Track tool calls for logging
|
|
139
|
+
const toolLog = [];
|
|
140
|
+
// Guard: spin detection, no-progress detection, token budget
|
|
141
|
+
const guard = new ToolCallGuard(toolConfig.maxTurnTokens);
|
|
142
|
+
// Start audit trace
|
|
143
|
+
const auditTraceId = toolContext?.auditTraceId || startTrace((toolContext?.trigger || 'api'));
|
|
144
|
+
for (let i = 0; i < maxIterations; i++) {
|
|
145
|
+
// Check abort signal before each iteration
|
|
146
|
+
if (toolContext?.abortSignal?.aborted) {
|
|
147
|
+
return {
|
|
148
|
+
response: `[Cancelled after ${toolLog.length} tool calls]`,
|
|
149
|
+
toolCalls: toolLog,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
const anthropicParams = {
|
|
153
|
+
model: modelId,
|
|
154
|
+
max_tokens: options.maxTokens || 16384,
|
|
155
|
+
messages: apiMessages,
|
|
156
|
+
tools: toolDefs,
|
|
157
|
+
};
|
|
158
|
+
if (systemParam) {
|
|
159
|
+
anthropicParams.system = systemParam;
|
|
160
|
+
}
|
|
161
|
+
// Add thinking if configured
|
|
162
|
+
const thinkingConfig = buildThinkingConfig(options.thinking);
|
|
163
|
+
if (thinkingConfig) {
|
|
164
|
+
anthropicParams.thinking = { type: 'enabled', budget_tokens: thinkingConfig.budget };
|
|
165
|
+
anthropicParams.max_tokens = Math.max(anthropicParams.max_tokens, thinkingConfig.maxTokens);
|
|
166
|
+
}
|
|
167
|
+
console.log(`[agent:tools] Iteration ${i + 1}/${maxIterations}`);
|
|
168
|
+
const genObs = await startGenerationObservation(`anthropic:${modelId}`, {
|
|
169
|
+
input: { messages: apiMessages },
|
|
170
|
+
model: modelId,
|
|
171
|
+
modelParameters: { max_tokens: anthropicParams.max_tokens },
|
|
172
|
+
metadata: { provider: 'anthropic', iteration: i + 1 },
|
|
173
|
+
});
|
|
174
|
+
let response;
|
|
175
|
+
try {
|
|
176
|
+
response = await anthropicClient.messages.create(anthropicParams);
|
|
177
|
+
const usage = response.usage;
|
|
178
|
+
// Log cache metrics
|
|
179
|
+
if (usage?.cache_read_input_tokens > 0 || usage?.cache_creation_input_tokens > 0) {
|
|
180
|
+
console.log(`[cache] read=${usage.cache_read_input_tokens || 0} created=${usage.cache_creation_input_tokens || 0}`);
|
|
181
|
+
}
|
|
182
|
+
recordAnthropicUsage({
|
|
183
|
+
model: modelId,
|
|
184
|
+
usage,
|
|
185
|
+
trigger: toolContext?.trigger || 'api',
|
|
186
|
+
agentId: toolContext?.agentId,
|
|
187
|
+
});
|
|
188
|
+
genObs?.update({
|
|
189
|
+
output: response.content,
|
|
190
|
+
usageDetails: toAnthropicUsageDetails(usage),
|
|
191
|
+
costDetails: toCostDetails(modelId, usage),
|
|
192
|
+
});
|
|
193
|
+
genObs?.end();
|
|
194
|
+
// Guard: track token usage
|
|
195
|
+
const tokenResult = guard.recordTokens(response.usage?.input_tokens ?? 0, response.usage?.output_tokens ?? 0);
|
|
196
|
+
if (tokenResult.warning)
|
|
197
|
+
console.warn(`[agent:tools:guard] ${tokenResult.warning}`);
|
|
198
|
+
if (tokenResult.exceeded) {
|
|
199
|
+
return {
|
|
200
|
+
response: `[Stopped: ${tokenResult.warning}]`,
|
|
201
|
+
toolCalls: toolLog,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
catch (err) {
|
|
206
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
207
|
+
genObs?.update({ level: 'ERROR', statusMessage: errorMessage, output: { error: errorMessage } });
|
|
208
|
+
genObs?.end();
|
|
209
|
+
throw err;
|
|
210
|
+
}
|
|
211
|
+
// If no tool use, we're done — extract text
|
|
212
|
+
if (response.stop_reason !== 'tool_use') {
|
|
213
|
+
const textBlocks = response.content.filter((c) => c.type === 'text');
|
|
214
|
+
let responseText = textBlocks.map((b) => b.text).join('\n') || '';
|
|
215
|
+
// Fallback when model did tool work but returned no text summary
|
|
216
|
+
if (!responseText && toolLog.length > 0) {
|
|
217
|
+
responseText = `[Completed with ${toolLog.length} tool calls, no text response]`;
|
|
218
|
+
}
|
|
219
|
+
const usage = response.usage;
|
|
220
|
+
return {
|
|
221
|
+
response: responseText,
|
|
222
|
+
toolCalls: toolLog,
|
|
223
|
+
usage: {
|
|
224
|
+
prompt_tokens: usage?.input_tokens ?? 0,
|
|
225
|
+
completion_tokens: usage?.output_tokens ?? 0,
|
|
226
|
+
total_tokens: (usage?.input_tokens ?? 0) + (usage?.output_tokens ?? 0),
|
|
227
|
+
},
|
|
228
|
+
cost: toCostDetails(modelId, usage),
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
// Add assistant response (full content including tool_use blocks)
|
|
232
|
+
apiMessages.push({ role: 'assistant', content: response.content });
|
|
233
|
+
// Execute each tool_use block
|
|
234
|
+
const toolResults = [];
|
|
235
|
+
for (const block of response.content) {
|
|
236
|
+
if (block.type !== 'tool_use')
|
|
237
|
+
continue;
|
|
238
|
+
const inputStr = JSON.stringify(block.input).slice(0, 200);
|
|
239
|
+
console.log(`[agent:tools] -> ${block.name}(${inputStr})`);
|
|
240
|
+
// Dynamic import to avoid circular dependency
|
|
241
|
+
const { isLangfuseEnabled } = await import('../langfuse.js');
|
|
242
|
+
const { startObservation } = await import('@langfuse/tracing');
|
|
243
|
+
const toolObs = isLangfuseEnabled()
|
|
244
|
+
? startObservation(`tool:${block.name}`, { input: block.input, metadata: { app: LANGFUSE_APP_NAME, tool: block.name } }, { asType: 'tool' })
|
|
245
|
+
: null;
|
|
246
|
+
// Guard: spin detection
|
|
247
|
+
const guardResult = guard.recordCall(block.name, block.input);
|
|
248
|
+
if (guardResult.warning)
|
|
249
|
+
console.warn(`[agent:tools:guard] ${guardResult.warning}`);
|
|
250
|
+
if (guardResult.blocked) {
|
|
251
|
+
toolResults.push({
|
|
252
|
+
type: 'tool_result',
|
|
253
|
+
tool_use_id: block.id,
|
|
254
|
+
content: guardResult.warning || 'Blocked: repeated identical call',
|
|
255
|
+
is_error: true,
|
|
256
|
+
});
|
|
257
|
+
toolLog.push(`${block.name} [BLOCKED: spin detected]`);
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
const toolStart = Date.now();
|
|
261
|
+
try {
|
|
262
|
+
const result = await executeTool(block.name, block.input, toolConfig, toolContext);
|
|
263
|
+
const truncatedResult = truncateToolResult(result);
|
|
264
|
+
const resultPreview = result.slice(0, 200) + (result.length > 200 ? '...' : '');
|
|
265
|
+
console.log(`[agent:tools] <- ${resultPreview}`);
|
|
266
|
+
toolLog.push(`${block.name}(${inputStr}) → ${resultPreview}`);
|
|
267
|
+
toolObs?.update({ output: result });
|
|
268
|
+
toolObs?.end();
|
|
269
|
+
// Record audit event
|
|
270
|
+
if (toolContext?.auditTraceId) {
|
|
271
|
+
addEvent(toolContext.auditTraceId, {
|
|
272
|
+
type: 'tool_use',
|
|
273
|
+
summary: `${block.name}(${inputStr})`,
|
|
274
|
+
durationMs: Date.now() - toolStart,
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
toolResults.push({
|
|
278
|
+
type: 'tool_result',
|
|
279
|
+
tool_use_id: block.id,
|
|
280
|
+
content: truncatedResult,
|
|
281
|
+
});
|
|
282
|
+
// Guard: no-progress detection
|
|
283
|
+
const progressResult = guard.recordResult(result);
|
|
284
|
+
if (progressResult.nudge) {
|
|
285
|
+
console.warn(`[agent:tools:guard] ${progressResult.nudge}`);
|
|
286
|
+
toolResults[toolResults.length - 1].content += `\n\n[System: ${progressResult.nudge}]`;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
catch (err) {
|
|
290
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
291
|
+
toolObs?.update({ level: 'ERROR', statusMessage: errorMessage, output: { error: errorMessage } });
|
|
292
|
+
toolObs?.end();
|
|
293
|
+
if (toolContext?.auditTraceId) {
|
|
294
|
+
addEvent(toolContext.auditTraceId, {
|
|
295
|
+
type: 'tool_error',
|
|
296
|
+
summary: `${block.name} error: ${errorMessage.slice(0, 150)}`,
|
|
297
|
+
durationMs: Date.now() - toolStart,
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
throw err;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
// Send tool results back
|
|
304
|
+
apiMessages.push({ role: 'user', content: toolResults });
|
|
305
|
+
}
|
|
306
|
+
console.warn(`[agent:tools] Max iterations (${maxIterations}) reached`);
|
|
307
|
+
return {
|
|
308
|
+
response: '[Tool use loop reached maximum iterations]',
|
|
309
|
+
toolCalls: toolLog,
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
/** Build UsageDetails from Anthropic usage response */
|
|
313
|
+
function buildUsageDetails(usage) {
|
|
314
|
+
return {
|
|
315
|
+
prompt_tokens: usage?.input_tokens ?? 0,
|
|
316
|
+
completion_tokens: usage?.output_tokens ?? 0,
|
|
317
|
+
total_tokens: (usage?.input_tokens ?? 0) + (usage?.output_tokens ?? 0),
|
|
318
|
+
};
|
|
319
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ProviderChatParams, ProviderToolChatParams, ToolChatResult } from './types.js';
|
|
2
|
+
export declare function addResponsesApiProvider(name: string): void;
|
|
3
|
+
export declare function isResponsesApiProvider(name: string): boolean;
|
|
4
|
+
export declare function resetCodexProviderState(): void;
|
|
5
|
+
export declare function setCodexAuthPath(path: string): void;
|
|
6
|
+
export declare function setCodexBaseUrl(url: string): void;
|
|
7
|
+
interface CodexAuth {
|
|
8
|
+
accessToken: string;
|
|
9
|
+
accountId: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function loadCodexAuth(authPath?: string): CodexAuth | null;
|
|
12
|
+
export declare function initCodexAuth(path?: string, baseUrl?: string): boolean;
|
|
13
|
+
export declare function getCodexAuth(): CodexAuth | null;
|
|
14
|
+
export declare function isCodexAvailable(): boolean;
|
|
15
|
+
export declare function chatCodex(params: ProviderChatParams): Promise<string>;
|
|
16
|
+
export declare function chatWithToolsCodex(params: ProviderToolChatParams): Promise<ToolChatResult>;
|
|
17
|
+
export {};
|