Skip to content

Commit 9438924

Browse files
authored
Merge pull request #8902 from continuedev/nate/agent-error-reporting
Add agent error reporting to API in serve mode
2 parents 3d0f57e + 2a81f4d commit 9438924

File tree

2 files changed

+46
-0
lines changed

2 files changed

+46
-0
lines changed

extensions/cli/src/commands/serve.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { prependPrompt } from "src/util/promptProcessor.js";
99
import { getAccessToken, getAssistantSlug } from "../auth/workos.js";
1010
import { runEnvironmentInstallSafe } from "../environment/environmentHandler.js";
1111
import { processCommandFlags } from "../flags/flagProcessor.js";
12+
import { setAgentId } from "../index.js";
1213
import { toolPermissionManager } from "../permissions/permissionManager.js";
1314
import {
1415
getService,
@@ -50,6 +51,9 @@ interface ServeOptions extends ExtendedCommandOptions {
5051
export async function serve(prompt?: string, options: ServeOptions = {}) {
5152
await posthogService.capture("sessionStart", {});
5253

54+
// Set agent ID for error reporting if provided
55+
setAgentId(options.id);
56+
5357
// Check if prompt should come from stdin instead of parameter
5458
let actualPrompt = prompt;
5559
if (!prompt) {

extensions/cli/src/index.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { configureConsoleForHeadless, safeStderr } from "./init.js";
2020
import { sentryService } from "./sentry.js";
2121
import { addCommonOptions, mergeParentOptions } from "./shared-options.js";
2222
import { posthogService } from "./telemetry/posthogService.js";
23+
import { post } from "./util/apiClient.js";
2324
import { gracefulExit } from "./util/exit.js";
2425
import { logger } from "./util/logger.js";
2526
import { readStdinSync } from "./util/stdin.js";
@@ -31,6 +32,9 @@ let showExitMessage: boolean;
3132
let exitMessageCallback: (() => void) | null;
3233
let lastCtrlCTime: number;
3334

35+
// Agent ID for serve mode - set when serve command is invoked with --id
36+
let agentId: string | undefined;
37+
3438
// Initialize state immediately to avoid temporal dead zone issues with exported functions
3539
(function initializeTUIState() {
3640
tuiUnmount = null;
@@ -39,6 +43,11 @@ let lastCtrlCTime: number;
3943
lastCtrlCTime = 0;
4044
})();
4145

46+
// Set the agent ID for error reporting (called by serve command)
47+
export function setAgentId(id: string | undefined) {
48+
agentId = id;
49+
}
50+
4251
// Register TUI cleanup function for graceful shutdown
4352
export function setTUIUnmount(unmount: () => void) {
4453
tuiUnmount = unmount;
@@ -89,6 +98,27 @@ export function shouldShowExitMessage(): boolean {
8998
return showExitMessage;
9099
}
91100

101+
// Helper to report unhandled errors to the API when running in serve mode
102+
async function reportUnhandledErrorToApi(error: Error): Promise<void> {
103+
if (!agentId) {
104+
// Not running in serve mode with an agent ID, skip API reporting
105+
return;
106+
}
107+
108+
try {
109+
await post(`agents/${agentId}/status`, {
110+
status: "FAILED",
111+
errorMessage: `Unhandled error: ${error.message}`,
112+
});
113+
logger.debug(`Reported unhandled error to API for agent ${agentId}`);
114+
} catch (apiError) {
115+
// If API reporting fails, just log it - don't crash
116+
logger.debug(
117+
`Failed to report error to API: ${apiError instanceof Error ? apiError.message : String(apiError)}`,
118+
);
119+
}
120+
}
121+
92122
// Add global error handlers to prevent uncaught errors from crashing the process
93123
process.on("unhandledRejection", (reason, promise) => {
94124
// Extract useful information from the reason
@@ -101,13 +131,21 @@ process.on("unhandledRejection", (reason, promise) => {
101131
// If reason is an Error, use it directly for better stack traces
102132
if (reason instanceof Error) {
103133
logger.error("Unhandled Promise Rejection", reason, errorDetails);
134+
// Report to API if running in serve mode
135+
reportUnhandledErrorToApi(reason).catch(() => {
136+
// Silently fail if API reporting errors - already logged in helper
137+
});
104138
} else {
105139
// Convert non-Error reasons to Error for consistent handling
106140
const error = new Error(`Unhandled rejection: ${String(reason)}`);
107141
logger.error("Unhandled Promise Rejection", error, {
108142
...errorDetails,
109143
originalReason: String(reason),
110144
});
145+
// Report to API if running in serve mode
146+
reportUnhandledErrorToApi(error).catch(() => {
147+
// Silently fail if API reporting errors - already logged in helper
148+
});
111149
}
112150

113151
// Note: Sentry capture is handled by logger.error() above
@@ -116,6 +154,10 @@ process.on("unhandledRejection", (reason, promise) => {
116154

117155
process.on("uncaughtException", (error) => {
118156
logger.error("Uncaught Exception:", error);
157+
// Report to API if running in serve mode
158+
reportUnhandledErrorToApi(error).catch(() => {
159+
// Silently fail if API reporting errors - already logged in helper
160+
});
119161
// Note: Sentry capture is handled by logger.error() above
120162
// Don't exit the process, just log the error
121163
});

0 commit comments

Comments
 (0)