Skip to content

Commit e2c9992

Browse files
patel-lyzrclaude
andcommitted
fix(gitagent+eval): capture the real answer, not gitclaw status lines
gitagent (gitclaw) emits lifecycle status messages ("Agent X started/finished") as plain text around the real answer and, unlike the Claude SDK, never sends a `result` frame. So output-capturing consumers fell back to the last text line — the trailing "Agent X finished" status — instead of the answer. Surfaced in the Simulation Engine: a gitagent agent that answered "4" was scored against "Agent agent-designer finished". - engine-gitagent: track the real assistant text (extractAssistantText already ignores status lines) and emit a synthetic `result` frame at end-of-turn, mirroring engine-deepagents. - eval-runner: defensively skip "Agent X started/finished" lines in the last-text fallback, so it works even before the engine fix is everywhere. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent e2033fb commit e2c9992

2 files changed

Lines changed: 20 additions & 1 deletion

File tree

packages/agentos-server/src/eval-runner.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,11 @@ export async function runAgainstHarness(
296296

297297
const transcript = tOrder.flatMap((id) => tMessages.get(id) ?? []);
298298
const toolCalls = transcript.filter((e) => e.type === "tool_use" && e.tool).map((e) => e.tool as string);
299-
const lastText = [...transcript].reverse().find((e) => e.type === "text")?.text ?? "";
299+
// Fallback to the last real assistant text, skipping gitclaw lifecycle status
300+
// lines ("Agent X started" / "Agent X finished") — those are wrapper output,
301+
// not the answer, and would otherwise be captured as the final output.
302+
const isLifecycle = (t: string): boolean => /^Agent .+ (started|finished)$/.test(t.trim());
303+
const lastText = [...transcript].reverse().find((e) => e.type === "text" && e.text && !isLifecycle(e.text))?.text ?? "";
300304

301305
const policyDenials: PolicyDenial[] = permissionDenials.map((d) => ({
302306
tool: d.tool_name ?? "unknown",

packages/engine-gitagent/src/engine.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,12 @@ export class GitAgentEngine implements EngineDriver<GitclawForwardOptions & { di
156156

157157
// Stream this turn's messages. Each assistant text turn we observe
158158
// gets persisted + accumulated so the next iteration sees it.
159+
// `finalText` tracks the real assistant answer (extractAssistantText
160+
// ignores gitclaw's lifecycle/status lines) so we can emit a synthetic
161+
// `result` frame at end-of-turn — gitclaw, unlike the Claude SDK, never
162+
// sends one, which left output-capturing consumers (e.g. the eval runner)
163+
// grabbing the trailing "Agent X finished" status instead of the answer.
164+
let finalText = "";
159165
try {
160166
for await (const message of query(options)) {
161167
if (ctx.abortSignal.aborted) break;
@@ -181,6 +187,7 @@ export class GitAgentEngine implements EngineDriver<GitclawForwardOptions & { di
181187

182188
const text = extractAssistantText(message);
183189
if (text) {
190+
finalText = text;
184191
const assistantIndex = indexer.next();
185192
if (store) {
186193
await appendAssistantTurn(store, ctx.sessionId, text, assistantIndex);
@@ -194,6 +201,14 @@ export class GitAgentEngine implements EngineDriver<GitclawForwardOptions & { di
194201
} as SessionStoreEntry);
195202
}
196203
}
204+
// Synthetic result frame carrying this turn's final answer. gitclaw
205+
// emits no Claude-SDK-style `result` message, so without this the
206+
// answer is only available as mid-stream assistant text — and raw
207+
// consumers fall back to the last text line, which is gitclaw's
208+
// "Agent X finished" status. (deepagents synthesizes one too.)
209+
if (finalText) {
210+
yield { kind: "sdk_message", payload: { type: "result", subtype: "success", result: finalText } };
211+
}
197212
log.info("engine.turn.end", {
198213
engine: "gitagent",
199214
sessionId: ctx.sessionId,

0 commit comments

Comments
 (0)