@hatchway/cli 0.50.64 → 0.50.65
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 +66 -33
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4424,8 +4424,22 @@ function createNativeClaudeQuery(modelId = DEFAULT_CLAUDE_MODEL_ID, abortControl
|
|
|
4424
4424
|
let messageCount = 0;
|
|
4425
4425
|
let toolCallCount = 0;
|
|
4426
4426
|
let textBlockCount = 0;
|
|
4427
|
-
//
|
|
4428
|
-
|
|
4427
|
+
// Create the gen_ai.invoke_agent span using startSpanManual.
|
|
4428
|
+
//
|
|
4429
|
+
// Why startSpanManual and not startSpan?
|
|
4430
|
+
// startSpan() takes a callback and ends the span when the callback returns.
|
|
4431
|
+
// But this is an async generator — we can't yield from inside a callback.
|
|
4432
|
+
// startSpanManual() makes the span active on the current scope AND gives us
|
|
4433
|
+
// a handle to end it ourselves in the finally block.
|
|
4434
|
+
//
|
|
4435
|
+
// Why this works now (it didn't before):
|
|
4436
|
+
// engine.ts captures the parent build.runner span before creating the
|
|
4437
|
+
// ReadableStream, then restores it via Sentry.withActiveSpan() inside the
|
|
4438
|
+
// stream's start() callback. So when this generator runs, the build.runner
|
|
4439
|
+
// span is the active parent, and our gen_ai.invoke_agent becomes its child.
|
|
4440
|
+
// Tool spans created with startSpan() inside the loop become children of
|
|
4441
|
+
// gen_ai.invoke_agent because it's the active span at that point.
|
|
4442
|
+
const agentSpan = Sentry.startSpanManual({
|
|
4429
4443
|
op: 'gen_ai.invoke_agent',
|
|
4430
4444
|
name: `Claude Agent (${modelId})`,
|
|
4431
4445
|
attributes: {
|
|
@@ -4435,7 +4449,8 @@ function createNativeClaudeQuery(modelId = DEFAULT_CLAUDE_MODEL_ID, abortControl
|
|
|
4435
4449
|
'gen_ai.system_prompt.length': appendedSystemPrompt.length,
|
|
4436
4450
|
'gen_ai.agent.available_tools': JSON.stringify(['Bash', 'Read', 'Write', 'Edit', 'Glob', 'Grep', 'Task', 'TodoWrite', 'WebFetch']),
|
|
4437
4451
|
},
|
|
4438
|
-
})
|
|
4452
|
+
}, (span) => span // Return the span so we control its lifecycle
|
|
4453
|
+
);
|
|
4439
4454
|
try {
|
|
4440
4455
|
// Stream messages directly from the SDK
|
|
4441
4456
|
for await (const sdkMessage of query({ prompt: finalPrompt, options })) {
|
|
@@ -4449,8 +4464,12 @@ function createNativeClaudeQuery(modelId = DEFAULT_CLAUDE_MODEL_ID, abortControl
|
|
|
4449
4464
|
if (block.type === 'tool_use') {
|
|
4450
4465
|
toolCallCount++;
|
|
4451
4466
|
debugLog$4(`[runner] [native-sdk] 🔧 Tool call: ${block.name}\n`);
|
|
4452
|
-
// Emit a gen_ai.execute_tool span
|
|
4453
|
-
|
|
4467
|
+
// Emit a gen_ai.execute_tool span as a child of gen_ai.invoke_agent.
|
|
4468
|
+
// Using startSpan (active) with an empty callback — the span is created,
|
|
4469
|
+
// becomes briefly active, records the tool invocation, and ends when
|
|
4470
|
+
// the callback returns. This gives Sentry the tool call event with
|
|
4471
|
+
// proper parent-child nesting.
|
|
4472
|
+
Sentry.startSpan({
|
|
4454
4473
|
op: 'gen_ai.execute_tool',
|
|
4455
4474
|
name: `Tool: ${block.name}`,
|
|
4456
4475
|
attributes: {
|
|
@@ -4458,10 +4477,9 @@ function createNativeClaudeQuery(modelId = DEFAULT_CLAUDE_MODEL_ID, abortControl
|
|
|
4458
4477
|
'gen_ai.tool.call_id': block.id,
|
|
4459
4478
|
'gen_ai.tool.input': JSON.stringify(block.input).substring(0, 1000),
|
|
4460
4479
|
},
|
|
4480
|
+
}, () => {
|
|
4481
|
+
// Span created and ended — marks the tool invocation point
|
|
4461
4482
|
});
|
|
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();
|
|
4465
4483
|
}
|
|
4466
4484
|
else if (block.type === 'text') {
|
|
4467
4485
|
textBlockCount++;
|
|
@@ -7177,38 +7195,53 @@ async function createBuildStream(options) {
|
|
|
7177
7195
|
debugLog$1();
|
|
7178
7196
|
const generator = query(fullPrompt, actualWorkingDir, systemPrompt, agent, options.codexThreadId, messageParts);
|
|
7179
7197
|
debugLog$1();
|
|
7198
|
+
// Capture the active Sentry span BEFORE creating the ReadableStream.
|
|
7199
|
+
// The ReadableStream.start() callback runs in a new async context where the
|
|
7200
|
+
// parent build.runner span is no longer active. We restore it with withActiveSpan()
|
|
7201
|
+
// so that gen_ai.invoke_agent spans created inside the query generator are
|
|
7202
|
+
// properly nested as children of the build.runner span.
|
|
7203
|
+
const parentSpan = Sentry.getActiveSpan();
|
|
7180
7204
|
// Create a ReadableStream from the AsyncGenerator
|
|
7181
7205
|
const stream = new ReadableStream({
|
|
7182
7206
|
async start(controller) {
|
|
7183
7207
|
debugLog$1();
|
|
7184
|
-
|
|
7185
|
-
|
|
7186
|
-
|
|
7187
|
-
|
|
7188
|
-
|
|
7189
|
-
|
|
7190
|
-
|
|
7191
|
-
|
|
7192
|
-
|
|
7193
|
-
|
|
7194
|
-
|
|
7195
|
-
|
|
7196
|
-
|
|
7197
|
-
|
|
7198
|
-
|
|
7199
|
-
|
|
7208
|
+
const consume = async () => {
|
|
7209
|
+
let chunkCount = 0;
|
|
7210
|
+
try {
|
|
7211
|
+
for await (const chunk of generator) {
|
|
7212
|
+
chunkCount++;
|
|
7213
|
+
if (chunkCount % 5 === 0) {
|
|
7214
|
+
debugLog$1(`[runner] [build-engine] Processed ${chunkCount} chunks from generator\n`);
|
|
7215
|
+
}
|
|
7216
|
+
// Convert chunk to appropriate format
|
|
7217
|
+
if (typeof chunk === 'string') {
|
|
7218
|
+
controller.enqueue(new TextEncoder().encode(chunk));
|
|
7219
|
+
}
|
|
7220
|
+
else if (chunk instanceof Uint8Array) {
|
|
7221
|
+
controller.enqueue(chunk);
|
|
7222
|
+
}
|
|
7223
|
+
else if (typeof chunk === 'object') {
|
|
7224
|
+
controller.enqueue(new TextEncoder().encode(JSON.stringify(chunk)));
|
|
7225
|
+
}
|
|
7200
7226
|
}
|
|
7227
|
+
debugLog$1(`[runner] [build-engine] ✅ Generator exhausted after ${chunkCount} chunks, closing stream\n`);
|
|
7228
|
+
controller.close();
|
|
7201
7229
|
}
|
|
7202
|
-
|
|
7203
|
-
|
|
7204
|
-
|
|
7205
|
-
|
|
7206
|
-
|
|
7207
|
-
|
|
7230
|
+
catch (error) {
|
|
7231
|
+
debugLog$1();
|
|
7232
|
+
controller.error(error);
|
|
7233
|
+
}
|
|
7234
|
+
finally {
|
|
7235
|
+
// Restore the original working directory
|
|
7236
|
+
process.chdir(originalCwd);
|
|
7237
|
+
}
|
|
7238
|
+
};
|
|
7239
|
+
// Restore the parent span context so child spans nest correctly
|
|
7240
|
+
if (parentSpan) {
|
|
7241
|
+
await Sentry.withActiveSpan(parentSpan, consume);
|
|
7208
7242
|
}
|
|
7209
|
-
|
|
7210
|
-
|
|
7211
|
-
process.chdir(originalCwd);
|
|
7243
|
+
else {
|
|
7244
|
+
await consume();
|
|
7212
7245
|
}
|
|
7213
7246
|
},
|
|
7214
7247
|
});
|