@hatchway/cli 0.50.63 → 0.50.64

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/dist/index.js CHANGED
@@ -4335,9 +4335,10 @@ function buildPromptWithImages(prompt, messageParts) {
4335
4335
  * query() SDK function -> minimal transformation -> output
4336
4336
  *
4337
4337
  * Sentry Integration:
4338
- * - The query() function is auto-instrumented by Sentry's claudeCodeAgentSdkIntegration
4339
- * - Instrumentation hooks into @anthropic-ai/claude-agent-sdk via OpenTelemetry
4340
- * - IMPORTANT: Sentry must be initialized BEFORE claude-agent-sdk is imported
4338
+ * - Manual gen_ai.* spans for AI Agent Monitoring in Sentry
4339
+ * - gen_ai.invoke_agent wraps the full query lifecycle
4340
+ * - gen_ai.execute_tool spans are emitted per tool call
4341
+ * - Token usage and cost are captured from the SDK result message
4341
4342
  */
4342
4343
  function createNativeClaudeQuery(modelId = DEFAULT_CLAUDE_MODEL_ID, abortController) {
4343
4344
  return async function* nativeClaudeQuery(prompt, workingDirectory, systemPrompt, _agent, _codexThreadId, messageParts) {
@@ -4423,20 +4424,44 @@ function createNativeClaudeQuery(modelId = DEFAULT_CLAUDE_MODEL_ID, abortControl
4423
4424
  let messageCount = 0;
4424
4425
  let toolCallCount = 0;
4425
4426
  let textBlockCount = 0;
4427
+ // Wrap the entire agent execution in a gen_ai.invoke_agent span for Sentry AI monitoring
4428
+ const agentSpan = Sentry.startInactiveSpan({
4429
+ op: 'gen_ai.invoke_agent',
4430
+ name: `Claude Agent (${modelId})`,
4431
+ attributes: {
4432
+ 'gen_ai.agent.name': 'hatchway-builder',
4433
+ 'gen_ai.request.model': modelId,
4434
+ 'gen_ai.agent.input': finalPrompt.substring(0, 500),
4435
+ 'gen_ai.system_prompt.length': appendedSystemPrompt.length,
4436
+ 'gen_ai.agent.available_tools': JSON.stringify(['Bash', 'Read', 'Write', 'Edit', 'Glob', 'Grep', 'Task', 'TodoWrite', 'WebFetch']),
4437
+ },
4438
+ });
4426
4439
  try {
4427
4440
  // Stream messages directly from the SDK
4428
- // NOTE: query() is auto-instrumented by Sentry's claudeCodeAgentSdkIntegration
4429
4441
  for await (const sdkMessage of query({ prompt: finalPrompt, options })) {
4430
4442
  messageCount++;
4431
4443
  // Transform SDK message to our internal format
4432
4444
  const transformed = transformSDKMessage(sdkMessage);
4433
4445
  if (transformed) {
4434
- // Track stats for logging
4446
+ // Track stats and emit gen_ai.execute_tool spans for tool calls
4435
4447
  if (transformed.type === 'assistant' && transformed.message?.content) {
4436
4448
  for (const block of transformed.message.content) {
4437
4449
  if (block.type === 'tool_use') {
4438
4450
  toolCallCount++;
4439
4451
  debugLog$4(`[runner] [native-sdk] 🔧 Tool call: ${block.name}\n`);
4452
+ // Emit a gen_ai.execute_tool span for each tool invocation
4453
+ const toolSpan = Sentry.startInactiveSpan({
4454
+ op: 'gen_ai.execute_tool',
4455
+ name: `Tool: ${block.name}`,
4456
+ attributes: {
4457
+ 'gen_ai.tool.name': block.name,
4458
+ 'gen_ai.tool.call_id': block.id,
4459
+ 'gen_ai.tool.input': JSON.stringify(block.input).substring(0, 1000),
4460
+ },
4461
+ });
4462
+ // Tool spans are completed immediately since we get input and output
4463
+ // as separate messages — the output is captured in the tool_result handler below
4464
+ toolSpan?.end();
4440
4465
  }
4441
4466
  else if (block.type === 'text') {
4442
4467
  textBlockCount++;
@@ -4485,13 +4510,31 @@ function createNativeClaudeQuery(modelId = DEFAULT_CLAUDE_MODEL_ID, abortControl
4485
4510
  process.stderr.write(`[native-sdk] Tool use summary: ${summaryMsg.summary}\n`);
4486
4511
  }
4487
4512
  }
4488
- // Log result messages
4513
+ // Capture result messages — record token usage and cost on the agent span
4489
4514
  if (sdkMessage.type === 'result') {
4490
- if (sdkMessage.subtype === 'success') {
4491
- debugLog$4(`[runner] [native-sdk] ✅ Query complete - ${sdkMessage.num_turns} turns, $${sdkMessage.total_cost_usd?.toFixed(4)} USD\n`);
4515
+ const resultMsg = sdkMessage;
4516
+ if (agentSpan) {
4517
+ agentSpan.setAttribute('gen_ai.usage.input_tokens', resultMsg.usage?.input_tokens ?? 0);
4518
+ agentSpan.setAttribute('gen_ai.usage.output_tokens', resultMsg.usage?.output_tokens ?? 0);
4519
+ agentSpan.setAttribute('gen_ai.usage.total_tokens', (resultMsg.usage?.input_tokens ?? 0) + (resultMsg.usage?.output_tokens ?? 0));
4520
+ agentSpan.setAttribute('gen_ai.usage.cost_usd', resultMsg.total_cost_usd ?? 0);
4521
+ agentSpan.setAttribute('gen_ai.agent.num_turns', resultMsg.num_turns ?? 0);
4522
+ agentSpan.setAttribute('gen_ai.agent.num_tool_calls', toolCallCount);
4523
+ agentSpan.setAttribute('gen_ai.agent.result', resultMsg.subtype ?? 'unknown');
4524
+ agentSpan.setAttribute('gen_ai.agent.duration_ms', resultMsg.duration_ms ?? 0);
4525
+ agentSpan.setAttribute('gen_ai.agent.duration_api_ms', resultMsg.duration_api_ms ?? 0);
4526
+ if (resultMsg.usage?.cache_read_input_tokens) {
4527
+ agentSpan.setAttribute('gen_ai.usage.cache_read_tokens', resultMsg.usage.cache_read_input_tokens);
4528
+ }
4529
+ if (resultMsg.usage?.cache_creation_input_tokens) {
4530
+ agentSpan.setAttribute('gen_ai.usage.cache_creation_tokens', resultMsg.usage.cache_creation_input_tokens);
4531
+ }
4532
+ }
4533
+ if (resultMsg.subtype === 'success') {
4534
+ debugLog$4(`[runner] [native-sdk] ✅ Query complete - ${resultMsg.num_turns} turns, $${resultMsg.total_cost_usd?.toFixed(4)} USD\n`);
4492
4535
  }
4493
4536
  else {
4494
- debugLog$4(`[runner] [native-sdk] ⚠️ Query ended with: ${sdkMessage.subtype}\n`);
4537
+ debugLog$4(`[runner] [native-sdk] ⚠️ Query ended with: ${resultMsg.subtype}\n`);
4495
4538
  }
4496
4539
  }
4497
4540
  }
@@ -4499,9 +4542,16 @@ function createNativeClaudeQuery(modelId = DEFAULT_CLAUDE_MODEL_ID, abortControl
4499
4542
  }
4500
4543
  catch (error) {
4501
4544
  debugLog$4(`[runner] [native-sdk] ❌ Error: ${error instanceof Error ? error.message : String(error)}\n`);
4545
+ if (agentSpan) {
4546
+ agentSpan.setStatus({ code: 2, message: error instanceof Error ? error.message : String(error) });
4547
+ }
4502
4548
  Sentry.captureException(error);
4503
4549
  throw error;
4504
4550
  }
4551
+ finally {
4552
+ // End the agent span regardless of success/failure
4553
+ agentSpan?.end();
4554
+ }
4505
4555
  };
4506
4556
  }
4507
4557