



























@@ -47,6 +47,12 @@ import {
4747buildTurnStartParams,
4848startOrResumeThread,
4949} from "./thread-lifecycle.js";
50+import {
51+createCodexTrajectoryRecorder,
52+normalizeCodexTrajectoryError,
53+recordCodexTrajectoryCompletion,
54+recordCodexTrajectoryContext,
55+} from "./trajectory.js";
5056import { mirrorCodexAppServerTranscript } from "./transcript-mirror.js";
51575258let clientFactory = defaultCodexAppServerClientFactory;
@@ -129,8 +135,16 @@ export async function runCodexAppServerAttempt(
129135messages: historyMessages,
130136ctx: hookContext,
131137});
138+const trajectoryRecorder = createCodexTrajectoryRecorder({
139+attempt: params,
140+cwd: effectiveWorkspace,
141+developerInstructions: promptBuild.developerInstructions,
142+prompt: promptBuild.prompt,
143+tools: toolBridge.specs,
144+});
132145let client: CodexAppServerClient;
133146let thread: CodexAppServerThreadBinding;
147+let trajectoryEndRecorded = false;
134148try {
135149({ client, thread } = await withCodexStartupTimeout({
136150timeoutMs: params.timeoutMs,
@@ -154,6 +168,20 @@ export async function runCodexAppServerAttempt(
154168params.abortSignal?.removeEventListener("abort", abortFromUpstream);
155169throw error;
156170}
171+trajectoryRecorder?.recordEvent("session.started", {
172+sessionFile: params.sessionFile,
173+threadId: thread.threadId,
174+authProfileId: startupAuthProfileId,
175+workspaceDir: effectiveWorkspace,
176+toolCount: toolBridge.specs.length,
177+});
178+recordCodexTrajectoryContext(trajectoryRecorder, {
179+attempt: params,
180+cwd: effectiveWorkspace,
181+developerInstructions: promptBuild.developerInstructions,
182+prompt: promptBuild.prompt,
183+tools: toolBridge.specs,
184+});
157185158186let projector: CodexAppServerEventProjector | undefined;
159187let turnId: string | undefined;
@@ -230,7 +258,23 @@ export async function runCodexAppServerAttempt(
230258if (!call || call.threadId !== thread.threadId || call.turnId !== turnId) {
231259return undefined;
232260}
233-return toolBridge.handleToolCall(call) as Promise<JsonValue>;
261+trajectoryRecorder?.recordEvent("tool.call", {
262+threadId: call.threadId,
263+turnId: call.turnId,
264+toolCallId: call.callId,
265+name: call.tool,
266+arguments: call.arguments,
267+});
268+const response = await toolBridge.handleToolCall(call);
269+trajectoryRecorder?.recordEvent("tool.result", {
270+threadId: call.threadId,
271+turnId: call.turnId,
272+toolCallId: call.callId,
273+name: call.tool,
274+success: response.success,
275+contentItems: response.contentItems,
276+});
277+return response as JsonValue;
234278});
235279236280const llmInputEvent = {
@@ -268,6 +312,14 @@ export async function runCodexAppServerAttempt(
268312{ timeoutMs: params.timeoutMs, signal: runAbortController.signal },
269313);
270314} catch (error) {
315+trajectoryRecorder?.recordEvent("session.ended", {
316+status: "error",
317+threadId: thread.threadId,
318+ timedOut,
319+aborted: runAbortController.signal.aborted,
320+promptError: normalizeCodexTrajectoryError(error),
321+});
322+trajectoryEndRecorded = true;
271323runAgentHarnessLlmOutputHook({
272324event: {
273325runId: params.runId,
@@ -289,10 +341,17 @@ export async function runCodexAppServerAttempt(
289341});
290342notificationCleanup();
291343requestCleanup();
344+await trajectoryRecorder?.flush();
292345params.abortSignal?.removeEventListener("abort", abortFromUpstream);
293346throw error;
294347}
295348turnId = turn.turn.id;
349+trajectoryRecorder?.recordEvent("prompt.submitted", {
350+threadId: thread.threadId,
351+ turnId,
352+prompt: promptBuild.prompt,
353+imagesCount: params.images?.length ?? 0,
354+});
296355projector = new CodexAppServerEventProjector(params, thread.threadId, turnId);
297356const activeTurnId = turnId;
298357const activeProjector = projector;
@@ -353,6 +412,23 @@ export async function runCodexAppServerAttempt(
353412const finalAborted = result.aborted || runAbortController.signal.aborted;
354413const finalPromptError = timedOut ? "codex app-server attempt timed out" : result.promptError;
355414const finalPromptErrorSource = timedOut ? "prompt" : result.promptErrorSource;
415+recordCodexTrajectoryCompletion(trajectoryRecorder, {
416+attempt: params,
417+ result,
418+threadId: thread.threadId,
419+turnId: activeTurnId,
420+ timedOut,
421+ yieldDetected,
422+});
423+trajectoryRecorder?.recordEvent("session.ended", {
424+status: finalPromptError ? "error" : finalAborted || timedOut ? "interrupted" : "success",
425+threadId: thread.threadId,
426+turnId: activeTurnId,
427+ timedOut,
428+ yieldDetected,
429+promptError: normalizeCodexTrajectoryError(finalPromptError),
430+});
431+trajectoryEndRecorded = true;
356432await mirrorTranscriptBestEffort({
357433 params,
358434agentId: sessionAgentId,
@@ -390,6 +466,16 @@ export async function runCodexAppServerAttempt(
390466promptErrorSource: finalPromptErrorSource,
391467};
392468} finally {
469+if (trajectoryRecorder && !trajectoryEndRecorded) {
470+trajectoryRecorder.recordEvent("session.ended", {
471+status: timedOut || runAbortController.signal.aborted ? "interrupted" : "cleanup",
472+threadId: thread.threadId,
473+turnId: activeTurnId,
474+ timedOut,
475+aborted: runAbortController.signal.aborted,
476+});
477+}
478+await trajectoryRecorder?.flush();
393479clearTimeout(timeout);
394480notificationCleanup();
395481requestCleanup();
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。