Skip to content

Commit e12ecad

Browse files
authored
Merge pull request #8838 from continuedev/nate/agent-metadata-tracking
Add agent session metadata tracking and reporting to control plane
2 parents d3652a4 + 10ae310 commit e12ecad

File tree

4 files changed

+661
-5
lines changed

4 files changed

+661
-5
lines changed

extensions/cli/src/commands/serve.ts

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import { messageQueue } from "../stream/messageQueue.js";
2828
import { constructSystemMessage } from "../systemMessage.js";
2929
import { telemetryService } from "../telemetry/telemetryService.js";
3030
import { reportFailureTool } from "../tools/reportFailure.js";
31-
import { gracefulExit } from "../util/exit.js";
31+
import { gracefulExit, updateAgentMetadata } from "../util/exit.js";
3232
import { formatError } from "../util/formatError.js";
3333
import { getGitDiffSnapshot } from "../util/git.js";
3434
import { logger } from "../util/logger.js";
@@ -355,9 +355,18 @@ export async function serve(prompt?: string, options: ServeOptions = {}) {
355355
}
356356

357357
// Give a moment for the response to be sent
358-
const handleExitResponse = () => {
359-
server.close(() => {
358+
const handleExitResponse = async () => {
359+
server.close(async () => {
360360
telemetryService.stopActiveTime();
361+
362+
// Update metadata one final time before exiting
363+
try {
364+
const history = services.chatHistory?.getHistory();
365+
await updateAgentMetadata(history);
366+
} catch (err) {
367+
logger.debug("Failed to update metadata (non-critical)", err as any);
368+
}
369+
361370
gracefulExit(0).catch((err) => {
362371
logger.error(`Graceful exit failed: ${formatError(err)}`);
363372
process.exit(1);
@@ -470,6 +479,18 @@ export async function serve(prompt?: string, options: ServeOptions = {}) {
470479
// No direct persistence here; ChatHistoryService handles persistence when appropriate
471480

472481
state.lastActivity = Date.now();
482+
483+
// Update metadata after successful agent turn
484+
try {
485+
const history = services.chatHistory?.getHistory();
486+
await updateAgentMetadata(history);
487+
} catch (metadataErr) {
488+
// Non-critical: log but don't fail the agent execution
489+
logger.debug(
490+
"Failed to update metadata after turn (non-critical)",
491+
metadataErr as any,
492+
);
493+
}
473494
} catch (e: any) {
474495
if (e.name === "AbortError") {
475496
logger.debug("Response interrupted");
@@ -526,8 +547,17 @@ export async function serve(prompt?: string, options: ServeOptions = {}) {
526547
);
527548
state.serverRunning = false;
528549
stopStorageSync();
529-
server.close(() => {
550+
server.close(async () => {
530551
telemetryService.stopActiveTime();
552+
553+
// Update metadata one final time before exiting
554+
try {
555+
const history = services.chatHistory?.getHistory();
556+
await updateAgentMetadata(history);
557+
} catch (err) {
558+
logger.debug("Failed to update metadata (non-critical)", err as any);
559+
}
560+
531561
gracefulExit(0).catch((err) => {
532562
logger.error(`Graceful exit failed: ${formatError(err)}`);
533563
process.exit(1);
@@ -549,8 +579,17 @@ export async function serve(prompt?: string, options: ServeOptions = {}) {
549579
clearInterval(inactivityChecker);
550580
inactivityChecker = null;
551581
}
552-
server.close(() => {
582+
server.close(async () => {
553583
telemetryService.stopActiveTime();
584+
585+
// Update metadata one final time before exiting
586+
try {
587+
const history = services.chatHistory?.getHistory();
588+
await updateAgentMetadata(history);
589+
} catch (err) {
590+
logger.debug("Failed to update metadata (non-critical)", err as any);
591+
}
592+
554593
gracefulExit(0).catch((err) => {
555594
logger.error(`Graceful exit failed: ${formatError(err)}`);
556595
process.exit(1);

extensions/cli/src/util/exit.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,74 @@
1+
import type { ChatHistoryItem } from "core/index.js";
2+
13
import { sentryService } from "../sentry.js";
24
import { telemetryService } from "../telemetry/telemetryService.js";
35

6+
import { getGitDiffSnapshot } from "./git.js";
47
import { logger } from "./logger.js";
8+
import {
9+
calculateDiffStats,
10+
extractSummary,
11+
getAgentIdFromArgs,
12+
postAgentMetadata,
13+
} from "./metadata.js";
14+
15+
/**
16+
* Update agent session metadata in control plane
17+
* Collects diff stats and conversation summary, posts to control plane
18+
* This is called both during execution (after each turn) and before exit
19+
*
20+
* @param history - Chat history to extract summary from (optional, will fetch if not provided)
21+
*/
22+
export async function updateAgentMetadata(
23+
history?: ChatHistoryItem[],
24+
): Promise<void> {
25+
try {
26+
const agentId = getAgentIdFromArgs();
27+
if (!agentId) {
28+
logger.debug("No agent ID found, skipping metadata update");
29+
return;
30+
}
31+
32+
const metadata: Record<string, any> = {};
33+
34+
// Calculate diff stats
35+
try {
36+
const { diff, repoFound } = await getGitDiffSnapshot();
37+
if (repoFound && diff) {
38+
const { additions, deletions } = calculateDiffStats(diff);
39+
if (additions > 0 || deletions > 0) {
40+
metadata.additions = additions;
41+
metadata.deletions = deletions;
42+
}
43+
}
44+
} catch (err) {
45+
logger.debug("Failed to calculate diff stats (non-critical)", err as any);
46+
}
47+
48+
// Extract summary from conversation
49+
if (history && history.length > 0) {
50+
try {
51+
const summary = extractSummary(history);
52+
if (summary) {
53+
metadata.summary = summary;
54+
}
55+
} catch (err) {
56+
logger.debug(
57+
"Failed to extract conversation summary (non-critical)",
58+
err as any,
59+
);
60+
}
61+
}
62+
63+
// Post metadata if we have any
64+
if (Object.keys(metadata).length > 0) {
65+
await postAgentMetadata(agentId, metadata);
66+
}
67+
} catch (err) {
68+
// Non-critical: log but don't fail the process
69+
logger.debug("Error in updateAgentMetadata (ignored)", err as any);
70+
}
71+
}
572

673
/**
774
* Exit the process after flushing telemetry and error reporting.

0 commit comments

Comments
 (0)