

























@@ -21,6 +21,7 @@ import {
2121import { readCodexTurn } from "./protocol-validators.js";
2222import {
2323isJsonObject,
24+type CodexDynamicToolCallOutputContentItem,
2425type CodexServerNotification,
2526type CodexThreadItem,
2627type CodexTurn,
@@ -80,6 +81,20 @@ const CODEX_PROMPT_TOTAL_INPUT_KEYS = [
8081] as const;
81828283const MAX_TOOL_OUTPUT_DELTA_MESSAGES_PER_ITEM = 20;
84+const TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS = 12_000;
85+86+type ToolTranscriptCallInput = {
87+id: string;
88+name: string;
89+arguments?: unknown;
90+};
91+92+type ToolTranscriptResultInput = {
93+id: string;
94+name: string;
95+text?: string;
96+isError: boolean;
97+};
83988499export class CodexAppServerEventProjector {
85100private readonly assistantTextByItem = new Map<string, string>();
@@ -97,6 +112,9 @@ export class CodexAppServerEventProjector {
97112{ chars: number; messages: number; truncated: boolean }
98113>();
99114private readonly toolMetas = new Map<string, { toolName: string; meta?: string }>();
115+private readonly toolTranscriptMessages: AgentMessage[] = [];
116+private readonly toolTranscriptCallIds = new Set<string>();
117+private readonly toolTranscriptResultIds = new Set<string>();
100118private assistantStarted = false;
101119private reasoningStarted = false;
102120private reasoningEnded = false;
@@ -238,6 +256,7 @@ export class CodexAppServerEventProjector {
238256),
239257);
240258}
259+messagesSnapshot.push(...this.toolTranscriptMessages);
241260if (lastAssistant) {
242261messagesSnapshot.push(attachCodexMirrorIdentity(lastAssistant, `${turnId}:assistant`));
243262}
@@ -297,6 +316,28 @@ export class CodexAppServerEventProjector {
297316};
298317}
299318319+recordDynamicToolCall(params: { callId: string; tool: string; arguments?: JsonValue }): void {
320+this.recordToolTranscriptCall({
321+id: params.callId,
322+name: params.tool,
323+arguments: sanitizeCodexToolArguments(params.arguments),
324+});
325+}
326+327+recordDynamicToolResult(params: {
328+callId: string;
329+tool: string;
330+success: boolean;
331+contentItems: CodexDynamicToolCallOutputContentItem[];
332+}): void {
333+this.recordToolTranscriptResult({
334+id: params.callId,
335+name: params.tool,
336+text: collectDynamicToolContentText(params.contentItems),
337+isError: !params.success,
338+});
339+}
340+300341markTimedOut(): void {
301342this.aborted = true;
302343this.promptError = "codex app-server attempt timed out";
@@ -402,6 +443,7 @@ export class CodexAppServerEventProjector {
402443}
403444this.emitStandardItemEvent({ phase: "start", item });
404445this.emitNormalizedToolItemEvent({ phase: "start", item });
446+this.recordNativeToolTranscriptCall(item);
405447this.emitToolResultSummary(item);
406448this.emitAgentEvent({
407449stream: "codex_app_server.item",
@@ -456,6 +498,8 @@ export class CodexAppServerEventProjector {
456498this.recordToolMeta(item);
457499this.emitStandardItemEvent({ phase: "end", item });
458500this.emitNormalizedToolItemEvent({ phase: "result", item });
501+this.recordNativeToolTranscriptCall(item);
502+this.recordNativeToolTranscriptResult(item);
459503this.emitToolResultSummary(item);
460504this.emitToolResultOutput(item);
461505this.emitAgentEvent({
@@ -558,6 +602,8 @@ export class CodexAppServerEventProjector {
558602this.emitPlanUpdate({ explanation: undefined, steps: splitPlanText(item.text) });
559603}
560604this.recordToolMeta(item);
605+this.recordNativeToolTranscriptCall(item);
606+this.recordNativeToolTranscriptResult(item);
561607this.emitToolResultSummary(item);
562608this.emitToolResultOutput(item);
563609}
@@ -805,6 +851,63 @@ export class CodexAppServerEventProjector {
805851});
806852}
807853854+private recordNativeToolTranscriptCall(item: CodexThreadItem | undefined): void {
855+if (!item || !shouldSynthesizeToolProgressForItem(item)) {
856+return;
857+}
858+const name = itemName(item);
859+if (!name) {
860+return;
861+}
862+this.recordToolTranscriptCall({
863+id: item.id,
864+ name,
865+arguments: itemToolArgs(item),
866+});
867+}
868+869+private recordNativeToolTranscriptResult(item: CodexThreadItem | undefined): void {
870+if (!item || !shouldSynthesizeToolProgressForItem(item)) {
871+return;
872+}
873+const name = itemName(item);
874+if (!name) {
875+return;
876+}
877+this.recordToolTranscriptResult({
878+id: item.id,
879+ name,
880+text: itemTranscriptResultText(item),
881+isError: isNonSuccessItemStatus(itemStatus(item)),
882+});
883+}
884+885+private recordToolTranscriptCall(params: ToolTranscriptCallInput): void {
886+if (!params.id || !params.name || this.toolTranscriptCallIds.has(params.id)) {
887+return;
888+}
889+this.toolTranscriptCallIds.add(params.id);
890+this.toolTranscriptMessages.push(
891+attachCodexMirrorIdentity(
892+this.createToolCallMessage(params),
893+`${this.turnId}:tool:${params.id}:call`,
894+),
895+);
896+}
897+898+private recordToolTranscriptResult(params: ToolTranscriptResultInput): void {
899+if (!params.id || !params.name || this.toolTranscriptResultIds.has(params.id)) {
900+return;
901+}
902+this.toolTranscriptResultIds.add(params.id);
903+this.toolTranscriptMessages.push(
904+attachCodexMirrorIdentity(
905+this.createToolResultMessage(params),
906+`${this.turnId}:tool:${params.id}:result`,
907+),
908+);
909+}
910+808911private formatCodexErrorMessage(params: JsonObject): string | undefined {
809912const error = isJsonObject(params.error) ? params.error : undefined;
810913return (
@@ -912,6 +1015,47 @@ export class CodexAppServerEventProjector {
9121015};
9131016}
91410171018+private createToolCallMessage(params: ToolTranscriptCallInput): AgentMessage {
1019+return {
1020+role: "assistant",
1021+content: [
1022+{
1023+type: "toolCall",
1024+id: params.id,
1025+name: params.name,
1026+arguments: normalizeToolTranscriptArguments(params.arguments),
1027+},
1028+],
1029+api: this.params.model.api ?? "openai-codex-responses",
1030+provider: this.params.provider,
1031+model: this.params.modelId,
1032+usage: ZERO_USAGE,
1033+stopReason: "toolUse",
1034+timestamp: Date.now(),
1035+} as unknown as AgentMessage;
1036+}
1037+1038+private createToolResultMessage(params: ToolTranscriptResultInput): AgentMessage {
1039+const text = truncateToolTranscriptText(params.text?.trim() || toolResultStatusText(params));
1040+return {
1041+role: "toolResult",
1042+toolCallId: params.id,
1043+toolName: params.name,
1044+isError: params.isError,
1045+content: [
1046+{
1047+type: "toolResult",
1048+toolCallId: params.id,
1049+toolUseId: params.id,
1050+tool_use_id: params.id,
1051+content: text,
1052+ text,
1053+},
1054+],
1055+timestamp: Date.now(),
1056+} as unknown as AgentMessage;
1057+}
1058+9151059private isNotificationForTurn(params: JsonObject): boolean {
9161060const threadId = readString(params, "threadId");
9171061const turnId = readNotificationTurnId(params);
@@ -1268,6 +1412,22 @@ function itemOutputText(item: CodexThreadItem): string | undefined {
12681412return undefined;
12691413}
127014141415+function itemTranscriptResultText(item: CodexThreadItem): string | undefined {
1416+const output = itemOutputText(item);
1417+if (output) {
1418+return output;
1419+}
1420+const result = itemToolResult(item).result;
1421+return result ? stringifyJsonValue(result) : itemStatus(item);
1422+}
1423+1424+function normalizeToolTranscriptArguments(value: unknown): Record<string, unknown> {
1425+if (!value || typeof value !== "object" || Array.isArray(value)) {
1426+return {};
1427+}
1428+return value as Record<string, unknown>;
1429+}
1430+12711431function collectDynamicToolContentText(contentItems: CodexThreadItem["contentItems"]): string {
12721432if (!Array.isArray(contentItems)) {
12731433return "";
@@ -1283,6 +1443,17 @@ function collectDynamicToolContentText(contentItems: CodexThreadItem["contentIte
12831443.join("\n");
12841444}
128514451446+function truncateToolTranscriptText(text: string): string {
1447+if (text.length <= TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS) {
1448+return text;
1449+}
1450+return `${text.slice(0, TOOL_TRANSCRIPT_OUTPUT_MAX_CHARS)}\n...(truncated)...`;
1451+}
1452+1453+function toolResultStatusText(params: ToolTranscriptResultInput): string {
1454+return params.isError ? `${params.name} failed` : `${params.name} completed`;
1455+}
1456+12861457function stringifyJsonValue(value: unknown): string | undefined {
12871458try {
12881459return JSON.stringify(value, null, 2);
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。