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.
Files changed (219) hide show
  1. package/README.md +230 -0
  2. package/dist/__tests__/agent.test.d.ts +1 -0
  3. package/dist/__tests__/agent.test.js +131 -0
  4. package/dist/__tests__/api.test.d.ts +1 -0
  5. package/dist/__tests__/api.test.js +1227 -0
  6. package/dist/__tests__/audit.test.d.ts +1 -0
  7. package/dist/__tests__/audit.test.js +122 -0
  8. package/dist/__tests__/cache.test.d.ts +1 -0
  9. package/dist/__tests__/cache.test.js +65 -0
  10. package/dist/__tests__/channels.test.d.ts +1 -0
  11. package/dist/__tests__/channels.test.js +85 -0
  12. package/dist/__tests__/cli.integration.test.d.ts +1 -0
  13. package/dist/__tests__/cli.integration.test.js +16 -0
  14. package/dist/__tests__/cli.test.d.ts +1 -0
  15. package/dist/__tests__/cli.test.js +230 -0
  16. package/dist/__tests__/code-agents-executor.test.d.ts +1 -0
  17. package/dist/__tests__/code-agents-executor.test.js +75 -0
  18. package/dist/__tests__/code-agents-orchestrator.test.d.ts +1 -0
  19. package/dist/__tests__/code-agents-orchestrator.test.js +149 -0
  20. package/dist/__tests__/code-agents-parser.test.d.ts +1 -0
  21. package/dist/__tests__/code-agents-parser.test.js +39 -0
  22. package/dist/__tests__/code-agents-utils.test.d.ts +1 -0
  23. package/dist/__tests__/code-agents-utils.test.js +41 -0
  24. package/dist/__tests__/config.test.d.ts +1 -0
  25. package/dist/__tests__/config.test.js +46 -0
  26. package/dist/__tests__/cron.test.d.ts +1 -0
  27. package/dist/__tests__/cron.test.js +66 -0
  28. package/dist/__tests__/dashboard-mode.test.d.ts +1 -0
  29. package/dist/__tests__/dashboard-mode.test.js +145 -0
  30. package/dist/__tests__/dashboard.test.d.ts +1 -0
  31. package/dist/__tests__/dashboard.test.js +43 -0
  32. package/dist/__tests__/doctor.formatters.test.d.ts +1 -0
  33. package/dist/__tests__/doctor.formatters.test.js +65 -0
  34. package/dist/__tests__/doctor.index.test.d.ts +1 -0
  35. package/dist/__tests__/doctor.index.test.js +48 -0
  36. package/dist/__tests__/doctor.runner.test.d.ts +1 -0
  37. package/dist/__tests__/doctor.runner.test.js +204 -0
  38. package/dist/__tests__/exec-approval.test.d.ts +1 -0
  39. package/dist/__tests__/exec-approval.test.js +323 -0
  40. package/dist/__tests__/file-lock.test.d.ts +1 -0
  41. package/dist/__tests__/file-lock.test.js +92 -0
  42. package/dist/__tests__/langfuse.test.d.ts +1 -0
  43. package/dist/__tests__/langfuse.test.js +40 -0
  44. package/dist/__tests__/model-selection.test.d.ts +1 -0
  45. package/dist/__tests__/model-selection.test.js +62 -0
  46. package/dist/__tests__/orchestrator.test.d.ts +1 -0
  47. package/dist/__tests__/orchestrator.test.js +425 -0
  48. package/dist/__tests__/providers-init.test.d.ts +1 -0
  49. package/dist/__tests__/providers-init.test.js +32 -0
  50. package/dist/__tests__/providers-routing.test.d.ts +1 -0
  51. package/dist/__tests__/providers-routing.test.js +25 -0
  52. package/dist/__tests__/providers-utils.test.d.ts +1 -0
  53. package/dist/__tests__/providers-utils.test.js +54 -0
  54. package/dist/__tests__/security.test.d.ts +1 -0
  55. package/dist/__tests__/security.test.js +22 -0
  56. package/dist/__tests__/sessions.test.d.ts +1 -0
  57. package/dist/__tests__/sessions.test.js +147 -0
  58. package/dist/__tests__/setup.test.d.ts +1 -0
  59. package/dist/__tests__/setup.test.js +114 -0
  60. package/dist/__tests__/skills.test.d.ts +1 -0
  61. package/dist/__tests__/skills.test.js +333 -0
  62. package/dist/__tests__/subagent.test.d.ts +1 -0
  63. package/dist/__tests__/subagent.test.js +240 -0
  64. package/dist/__tests__/telegram-utils.test.d.ts +1 -0
  65. package/dist/__tests__/telegram-utils.test.js +22 -0
  66. package/dist/__tests__/telegram.test.d.ts +1 -0
  67. package/dist/__tests__/telegram.test.js +42 -0
  68. package/dist/__tests__/token-efficiency.test.d.ts +1 -0
  69. package/dist/__tests__/token-efficiency.test.js +38 -0
  70. package/dist/__tests__/tool-guard.test.d.ts +1 -0
  71. package/dist/__tests__/tool-guard.test.js +105 -0
  72. package/dist/__tests__/tools.test.d.ts +1 -0
  73. package/dist/__tests__/tools.test.js +589 -0
  74. package/dist/__tests__/usage.test.d.ts +1 -0
  75. package/dist/__tests__/usage.test.js +197 -0
  76. package/dist/__tests__/voice.test.d.ts +1 -0
  77. package/dist/__tests__/voice.test.js +214 -0
  78. package/dist/agent.d.ts +24 -0
  79. package/dist/agent.js +269 -0
  80. package/dist/api.d.ts +3 -0
  81. package/dist/api.js +943 -0
  82. package/dist/audit.d.ts +26 -0
  83. package/dist/audit.js +121 -0
  84. package/dist/cache.d.ts +8 -0
  85. package/dist/cache.js +24 -0
  86. package/dist/channels/telegram/handlers.d.ts +41 -0
  87. package/dist/channels/telegram/handlers.js +498 -0
  88. package/dist/channels/telegram/index.d.ts +14 -0
  89. package/dist/channels/telegram/index.js +326 -0
  90. package/dist/channels/telegram/types.d.ts +26 -0
  91. package/dist/channels/telegram/types.js +31 -0
  92. package/dist/channels/telegram/utils.d.ts +25 -0
  93. package/dist/channels/telegram/utils.js +256 -0
  94. package/dist/channels.d.ts +11 -0
  95. package/dist/channels.js +118 -0
  96. package/dist/cli.d.ts +5 -0
  97. package/dist/cli.js +768 -0
  98. package/dist/code-agents/executor.d.ts +5 -0
  99. package/dist/code-agents/executor.js +463 -0
  100. package/dist/code-agents/index.d.ts +22 -0
  101. package/dist/code-agents/index.js +199 -0
  102. package/dist/code-agents/orchestrator.d.ts +23 -0
  103. package/dist/code-agents/orchestrator.js +403 -0
  104. package/dist/code-agents/parser.d.ts +21 -0
  105. package/dist/code-agents/parser.js +197 -0
  106. package/dist/code-agents/registry.d.ts +27 -0
  107. package/dist/code-agents/registry.js +147 -0
  108. package/dist/code-agents/types.d.ts +66 -0
  109. package/dist/code-agents/types.js +4 -0
  110. package/dist/code-agents/utils.d.ts +36 -0
  111. package/dist/code-agents/utils.js +236 -0
  112. package/dist/config.d.ts +19 -0
  113. package/dist/config.js +123 -0
  114. package/dist/cron.d.ts +49 -0
  115. package/dist/cron.js +400 -0
  116. package/dist/dashboard/assets/index-CZJCvMSN.js +65 -0
  117. package/dist/dashboard/assets/index-EAg6lqF5.css +1 -0
  118. package/dist/dashboard/favicon.svg +3 -0
  119. package/dist/dashboard/index.html +21 -0
  120. package/dist/dashboard-frontend.d.ts +7 -0
  121. package/dist/dashboard-frontend.js +86 -0
  122. package/dist/dashboard.d.ts +8 -0
  123. package/dist/dashboard.js +4071 -0
  124. package/dist/digests.d.ts +36 -0
  125. package/dist/digests.js +338 -0
  126. package/dist/discord.d.ts +8 -0
  127. package/dist/discord.js +828 -0
  128. package/dist/doctor/checks.d.ts +18 -0
  129. package/dist/doctor/checks.js +368 -0
  130. package/dist/doctor/formatters.d.ts +3 -0
  131. package/dist/doctor/formatters.js +44 -0
  132. package/dist/doctor/index.d.ts +8 -0
  133. package/dist/doctor/index.js +7 -0
  134. package/dist/doctor/runner.d.ts +3 -0
  135. package/dist/doctor/runner.js +109 -0
  136. package/dist/doctor/types.d.ts +20 -0
  137. package/dist/doctor/types.js +1 -0
  138. package/dist/exec-approval.d.ts +101 -0
  139. package/dist/exec-approval.js +432 -0
  140. package/dist/file-lock.d.ts +34 -0
  141. package/dist/file-lock.js +81 -0
  142. package/dist/gateway.d.ts +8 -0
  143. package/dist/gateway.js +114 -0
  144. package/dist/heartbeat.d.ts +4 -0
  145. package/dist/heartbeat.js +101 -0
  146. package/dist/index.d.ts +1 -0
  147. package/dist/index.js +75 -0
  148. package/dist/langfuse.d.ts +34 -0
  149. package/dist/langfuse.js +145 -0
  150. package/dist/mcp-context-a8c.d.ts +13 -0
  151. package/dist/mcp-context-a8c.js +34 -0
  152. package/dist/model-selection.d.ts +18 -0
  153. package/dist/model-selection.js +50 -0
  154. package/dist/orchestrator.d.ts +15 -0
  155. package/dist/orchestrator.js +676 -0
  156. package/dist/providers/anthropic.d.ts +7 -0
  157. package/dist/providers/anthropic.js +319 -0
  158. package/dist/providers/codex.d.ts +17 -0
  159. package/dist/providers/codex.js +508 -0
  160. package/dist/providers/content.d.ts +21 -0
  161. package/dist/providers/content.js +55 -0
  162. package/dist/providers/index.d.ts +13 -0
  163. package/dist/providers/index.js +138 -0
  164. package/dist/providers/observability.d.ts +19 -0
  165. package/dist/providers/observability.js +94 -0
  166. package/dist/providers/openai.d.ts +10 -0
  167. package/dist/providers/openai.js +310 -0
  168. package/dist/providers/tool-guard.d.ts +30 -0
  169. package/dist/providers/tool-guard.js +89 -0
  170. package/dist/providers/types.d.ts +34 -0
  171. package/dist/providers/types.js +2 -0
  172. package/dist/providers/utils.d.ts +65 -0
  173. package/dist/providers/utils.js +199 -0
  174. package/dist/security.d.ts +8 -0
  175. package/dist/security.js +113 -0
  176. package/dist/service.d.ts +8 -0
  177. package/dist/service.js +38 -0
  178. package/dist/sessions.d.ts +35 -0
  179. package/dist/sessions.js +142 -0
  180. package/dist/setup.d.ts +36 -0
  181. package/dist/setup.js +821 -0
  182. package/dist/skills-types.d.ts +65 -0
  183. package/dist/skills-types.js +2 -0
  184. package/dist/skills.d.ts +32 -0
  185. package/dist/skills.js +260 -0
  186. package/dist/subagent.d.ts +19 -0
  187. package/dist/subagent.js +376 -0
  188. package/dist/telegram.d.ts +2 -0
  189. package/dist/telegram.js +11 -0
  190. package/dist/tools/bash-tool.d.ts +3 -0
  191. package/dist/tools/bash-tool.js +59 -0
  192. package/dist/tools/browser-tool.d.ts +3 -0
  193. package/dist/tools/browser-tool.js +265 -0
  194. package/dist/tools/definitions.d.ts +432 -0
  195. package/dist/tools/definitions.js +181 -0
  196. package/dist/tools/execute-context.d.ts +26 -0
  197. package/dist/tools/execute-context.js +1 -0
  198. package/dist/tools/file-tools.d.ts +8 -0
  199. package/dist/tools/file-tools.js +67 -0
  200. package/dist/tools/path-utils.d.ts +1 -0
  201. package/dist/tools/path-utils.js +8 -0
  202. package/dist/tools.d.ts +24 -0
  203. package/dist/tools.js +281 -0
  204. package/dist/types.d.ts +259 -0
  205. package/dist/types.js +2 -0
  206. package/dist/usage.d.ts +76 -0
  207. package/dist/usage.js +150 -0
  208. package/dist/voice.d.ts +37 -0
  209. package/dist/voice.js +461 -0
  210. package/package.json +70 -0
  211. package/templates/AGENTS.md +38 -0
  212. package/templates/BOOT.md +23 -0
  213. package/templates/BOOTSTRAP.md +26 -0
  214. package/templates/HEARTBEAT.md +5 -0
  215. package/templates/IDENTITY.md +5 -0
  216. package/templates/MEMORY.md +24 -0
  217. package/templates/SOUL.md +92 -0
  218. package/templates/TOOLS.md +30 -0
  219. 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 {};