Skip to content
Merged
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
1 change: 0 additions & 1 deletion src/agents/respond-to-ci.ts
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,6 @@ Use these values when calling GitHub gadgets (GetPRDetails, PostPRComment, Updat

const ciAgentDefinition: GitHubAgentDefinition<RespondToCIAgentInput, CIContextData> = {
agentType: 'respond-to-ci',
headerMessage: '🤖 Working on fixing CI failures...',
initialCommentDescription: 'Acknowledge CI failures',
timeoutMessage: '⚠️ CI fix agent timed out while attempting to fix failures.',
loggerPrefix: 'ci',
Expand Down
1 change: 0 additions & 1 deletion src/agents/respond-to-pr-comment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ const respondToPRCommentDefinition: GitHubAgentDefinition<
PRResponseContextData
> = {
agentType: 'respond-to-pr-comment',
headerMessage: '🤖 Working on your request...',
initialCommentDescription: 'Acknowledge PR comment request',
timeoutMessage: '⚠️ PR comment agent timed out while working on the request.',
loggerPrefix: 'pr-comment',
Expand Down
1 change: 0 additions & 1 deletion src/agents/respond-to-review.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ const respondToReviewDefinition: GitHubAgentDefinition<
PRResponseContextData
> = {
agentType: 'respond-to-review',
headerMessage: '🤖 Working on addressing the review feedback...',
initialCommentDescription: 'Acknowledge review feedback',
timeoutMessage: '⚠️ Review agent timed out while addressing feedback.',
loggerPrefix: 'review',
Expand Down
1 change: 0 additions & 1 deletion src/agents/review.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,6 @@ ${skippedFiles.map((f) => `- ${f}`).join('\n')}`;

const reviewAgentDefinition: GitHubAgentDefinition<ReviewAgentInput, ReviewContextData> = {
agentType: 'review',
headerMessage: '🔍 Reviewing PR...',
initialCommentDescription: 'Post initial review status comment',
timeoutMessage: '⚠️ Review agent timed out while reviewing the PR.',
loggerPrefix: 'review',
Expand Down
47 changes: 36 additions & 11 deletions src/agents/shared/githubAgent.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { ModelSpec } from 'llmist';

import { createProgressMonitor } from '../../backends/progress.js';
import { INITIAL_MESSAGES } from '../../config/agentMessages.js';
import { CUSTOM_MODELS } from '../../config/customModels.js';
import { recordInitialComment } from '../../gadgets/sessionState.js';
import { githubClient, withGitHubToken } from '../../github/client.js';
Expand Down Expand Up @@ -51,7 +52,8 @@ export interface GitHubAgentDefinition<
TContext extends GitHubAgentContext,
> {
agentType: string;
headerMessage: string;
/** Static header message — last-resort fallback when no ackMessage or INITIAL_MESSAGES entry. */
headerMessage?: string;
initialCommentDescription: string;
timeoutMessage: string;
loggerPrefix: string;
Expand Down Expand Up @@ -124,6 +126,16 @@ export async function executeGitHubAgent<
if (earlyResult) return earlyResult;
}

// Resolve effective header: ackMessage (LLM-generated) > INITIAL_MESSAGES > definition fallback
const effectiveHeader =
(input.ackMessage as string | undefined) ??
INITIAL_MESSAGES[definition.agentType] ??
definition.headerMessage ??
INITIAL_MESSAGES.implementation;

// Pre-existing ack comment from router or webhook handler
const preExistingAckId = input.ackCommentId as number | undefined;

const runLifecycle = () =>
executeAgentLifecycle<TContext>({
loggerIdentifier: `${definition.loggerPrefix}-${prNumber}`,
Expand Down Expand Up @@ -172,24 +184,37 @@ export async function executeGitHubAgent<
}),

injectSyntheticCalls: async ({ builder, ctx, trackingContext, repoDir }) => {
const initialComment = await definition.postInitialComment(
input,
id,
definition.headerMessage,
);
recordInitialComment(initialComment.id);
let initialCommentId: number;
let initialCommentHtmlUrl: string;
let gadgetName: string;

if (preExistingAckId) {
// Ack comment already posted by router/webhook-handler — reuse it
recordInitialComment(preExistingAckId);
initialCommentId = preExistingAckId;
initialCommentHtmlUrl = `https://github.com/${owner}/${repo}/pull/${prNumber}#issuecomment-${preExistingAckId}`;
gadgetName = 'PostPRComment';
} else {
// No pre-existing ack — post initial comment now
const initialComment = await definition.postInitialComment(input, id, effectiveHeader);
recordInitialComment(initialComment.id);
initialCommentId = initialComment.id;
initialCommentHtmlUrl = initialComment.htmlUrl;
gadgetName = initialComment.gadgetName;
}

const withComment = injectSyntheticCall(
builder,
trackingContext,
initialComment.gadgetName,
gadgetName,
{
comment: definition.initialCommentDescription,
owner,
repo,
prNumber,
body: definition.headerMessage,
body: effectiveHeader,
},
`Comment posted (id: ${initialComment.id}): ${initialComment.htmlUrl}`,
`Comment posted (id: ${initialCommentId}): ${initialCommentHtmlUrl}`,
'gc_initial_comment',
);

Expand All @@ -211,7 +236,7 @@ export async function executeGitHubAgent<
progressModel: input.config.defaults.progressModel,
intervalMinutes: input.config.defaults.progressIntervalMinutes,
customModels: CUSTOM_MODELS as ModelSpec[],
github: { owner, repo, headerMessage: definition.headerMessage },
github: { owner, repo, headerMessage: effectiveHeader },
}),

interactive,
Expand Down
19 changes: 2 additions & 17 deletions src/agents/shared/prResponseAgent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,8 @@ import { githubClient } from '../../github/client.js';
import type { CascadeConfig, ProjectConfig } from '../../types/index.js';
import type { TrackingContext } from '../utils/tracking.js';
import type { BuilderType } from './builderFactory.js';
import type {
GitHubAgentContext,
GitHubAgentInput,
InitialCommentResult,
RepoIdentifier,
} from './githubAgent.js';
import { createInitialPRComment } from './githubAgent.js';
import type { GitHubAgentContext, GitHubAgentInput, RepoIdentifier } from './githubAgent.js';
import { type InitialCommentResult, createInitialPRComment } from './githubAgent.js';
import { resolveModelConfig } from './modelResolution.js';
import {
formatPRComments,
Expand All @@ -33,7 +28,6 @@ export interface PRResponseAgentInput extends GitHubAgentInput {
triggerCommentBody: string;
triggerCommentPath: string;
triggerCommentUrl: string;
acknowledgmentCommentId?: number;
}

export interface PRResponseContextData extends GitHubAgentContext {
Expand Down Expand Up @@ -138,15 +132,6 @@ export async function postInitialPRResponseComment(
id: RepoIdentifier,
headerMessage: string,
): Promise<InitialCommentResult> {
if (input.acknowledgmentCommentId) {
const comment = await githubClient.updatePRComment(
id.owner,
id.repo,
input.acknowledgmentCommentId,
headerMessage,
);
return { id: comment.id, htmlUrl: comment.htmlUrl, gadgetName: 'UpdatePRComment' };
}
return createInitialPRComment(input.prNumber, id, headerMessage);
}

Expand Down
18 changes: 11 additions & 7 deletions src/backends/agent-profiles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
buildWorkItemPrompt,
} from '../agents/shared/taskPrompts.js';
import type { ContextFile } from '../agents/utils/setup.js';
import { INITIAL_MESSAGES } from '../config/agentMessages.js';
import { ListDirectory } from '../gadgets/ListDirectory.js';
import { formatCheckStatus } from '../gadgets/github/core/getPRChecks.js';
import { readWorkItem } from '../gadgets/pm/core/readWorkItem.js';
Expand Down Expand Up @@ -480,12 +481,16 @@ const reviewProfile: AgentProfile = {
getLlmistGadgets: (_agentType) => buildReviewGadgets(),

async preExecute({ input, logWriter }: PreExecuteParams): Promise<void> {
// Skip if ack comment already posted by router or webhook handler
if (input.ackCommentId) return;

const repoFullName = input.repoFullName as string;
const prNumber = input.prNumber as number;
const { owner, repo } = parseRepoFullName(repoFullName);

const message = (input.ackMessage as string | undefined) ?? INITIAL_MESSAGES.review;
logWriter('INFO', 'Posting initial review comment', { owner, repo, prNumber });
await githubClient.createPRComment(owner, repo, prNumber, '🔍 Reviewing PR...');
await githubClient.createPRComment(owner, repo, prNumber, message);
},
};

Expand Down Expand Up @@ -519,17 +524,16 @@ const respondToCIProfile: AgentProfile = {
getLlmistGadgets: (_agentType) => buildPRAgentGadgets(),

async preExecute({ input, logWriter }: PreExecuteParams): Promise<void> {
// Skip if ack comment already posted by router or webhook handler
if (input.ackCommentId) return;

const repoFullName = input.repoFullName as string;
const prNumber = input.prNumber as number;
const { owner, repo } = parseRepoFullName(repoFullName);

const message = (input.ackMessage as string | undefined) ?? INITIAL_MESSAGES['respond-to-ci'];
logWriter('INFO', 'Posting initial CI fix comment', { owner, repo, prNumber });
await githubClient.createPRComment(
owner,
repo,
prNumber,
'🤖 Working on fixing CI failures...',
);
await githubClient.createPRComment(owner, repo, prNumber, message);
},
};

Expand Down
14 changes: 10 additions & 4 deletions src/router/github.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@ import { sendAcknowledgeReaction } from './reactions.js';

/**
* Try to match a trigger and post an ack comment for a GitHub webhook.
* Returns the ack comment ID if posted, undefined otherwise.
* Returns the ack comment ID and message text if posted, undefined otherwise.
*/
export async function tryPostGitHubAck(
eventType: string,
repoFullName: string,
payload: unknown,
triggerRegistry: TriggerRegistry,
): Promise<number | undefined> {
): Promise<{ commentId: number; message: string } | undefined> {
const config = await loadProjectConfig();
const fullProject = config.fullProjects.find((fp) => fp.repo === repoFullName);
if (!fullProject) return undefined;
Expand Down Expand Up @@ -66,7 +66,7 @@ export async function tryPostGitHubAck(
if (!prNumber) return undefined;

const commentId = await postGitHubAck(repoFullName, prNumber, message, resolved.token);
return commentId ?? undefined;
return commentId != null ? { commentId, message } : undefined;
}

export async function isSelfAuthoredGitHubComment(
Expand Down Expand Up @@ -147,8 +147,13 @@ export async function processGitHubWebhookEvent(

// Try to post an ack comment via trigger matching (non-blocking best-effort)
let ackCommentId: number | undefined;
let ackMessage: string | undefined;
try {
ackCommentId = await tryPostGitHubAck(eventType, repoFullName, payload, triggerRegistry);
const ackResult = await tryPostGitHubAck(eventType, repoFullName, payload, triggerRegistry);
if (ackResult) {
ackCommentId = ackResult.commentId;
ackMessage = ackResult.message;
}
} catch (err) {
console.warn('[Router] GitHub ack comment failed (non-fatal):', String(err));
}
Expand All @@ -161,6 +166,7 @@ export async function processGitHubWebhookEvent(
repoFullName,
receivedAt: new Date().toISOString(),
ackCommentId,
ackMessage,
};

// Fire pre-actions (non-blocking) before queueing
Expand Down
1 change: 1 addition & 0 deletions src/router/queue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export interface GitHubJob {
repoFullName: string;
receivedAt: string;
ackCommentId?: number;
ackMessage?: string;
}

export interface JiraJob {
Expand Down
Loading