Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions src/__tests__/wizard-abort.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,31 @@ describe('wizardAbort', () => {
expect(mockAnalytics.captureException).not.toHaveBeenCalled();
});

it('does not capture error when captureError is false (expected abort)', async () => {
const error = new WizardError('Agent aborted: github connection declined', {
integration: 'self-driving',
});

await expect(wizardAbort({ error, captureError: false })).rejects.toThrow(
'process.exit called',
);

// The exception is suppressed for well-handled exits...
expect(mockAnalytics.captureException).not.toHaveBeenCalled();
// ...but the run still shuts down so the rest of the flow is unaffected.
expect(mockAnalytics.shutdown).toHaveBeenCalled();
});

it('captures error when captureError is true (default)', async () => {
const error = new Error('something broke');

await expect(wizardAbort({ error, captureError: true })).rejects.toThrow(
'process.exit called',
);

expect(mockAnalytics.captureException).toHaveBeenCalledWith(error, {});
});

it('includes WizardError context in analytics capture', async () => {
const error = new WizardError('MCP missing', {
integration: 'nextjs',
Expand Down
5 changes: 5 additions & 0 deletions src/lib/agent/runner/linear.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,11 @@ export async function runLinearProgram(
error_type: AgentErrorType.ABORT,
reason,
}),
// A matched AbortCase is a deliberate, well-handled exit (e.g. the user
// declining the GitHub connection in self-driving). Skip error-tracking
// capture for these — the `agent aborted` product event above already
// records them. Reserve captureException for the generic/unmatched abort.
captureError: !matched,
});
}

Expand Down
14 changes: 12 additions & 2 deletions src/utils/wizard-abort.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@ interface WizardAbortOptions {
outroData?: OutroData;
error?: Error | WizardError;
exitCode?: number;
/**
* Whether to report `error` to error tracking via
* `analytics.captureException`. Defaults to `true`. Set `false` for
* expected, well-handled exits (e.g. a matched `AbortCase` such as a
* user declining the GitHub connection) so they don't mint spurious
* error-tracking issues. The product event (`wizardCapture`) is emitted
* separately by the caller regardless.
*/
captureError?: boolean;
}

const cleanupFns: Array<() => void> = [];
Expand Down Expand Up @@ -59,6 +68,7 @@ export async function wizardAbort(
outroData,
error,
exitCode = 1,
captureError = true,
} = options ?? {};

logToFile(`[wizard-abort] exitCode=${exitCode}, message: ${message}`);
Expand All @@ -69,8 +79,8 @@ export async function wizardAbort(
// 1. Run registered cleanup functions
runCleanups();

// 2. Capture error in analytics (if provided)
if (error) {
// 2. Capture error in analytics (if provided and not an expected abort)
if (error && captureError) {
analytics.captureException(error, {
...((error instanceof WizardError && error.context) || {}),
});
Expand Down
Loading