





















@@ -108,6 +108,8 @@ type ToolTranscriptResultInput = {
108108export class CodexAppServerEventProjector {
109109private readonly assistantTextByItem = new Map<string, string>();
110110private readonly assistantItemOrder: string[] = [];
111+private readonly assistantPhaseByItem = new Map<string, string>();
112+private readonly lastCommentaryProgressTextByItem = new Map<string, string>();
111113private readonly reasoningTextByItem = new Map<string, string>();
112114private readonly planTextByItem = new Map<string, string>();
113115private readonly activeItemIds = new Set<string>();
@@ -395,6 +397,9 @@ export class CodexAppServerEventProjector {
395397this.rememberAssistantItem(itemId);
396398const text = `${this.assistantTextByItem.get(itemId) ?? ""}${delta}`;
397399this.assistantTextByItem.set(itemId, text);
400+if (this.isCommentaryAssistantItem(itemId)) {
401+this.emitCommentaryProgress({ itemId, text });
402+}
398403// Codex app-server can emit multiple agentMessage items per turn, including
399404// intermediate coordination/progress prose. Keep those deltas internal until
400405// turn completion chooses the last assistant item as the user-visible reply.
@@ -445,6 +450,7 @@ export class CodexAppServerEventProjector {
445450private async handleItemStarted(params: JsonObject): Promise<void> {
446451const item = readItem(params.item);
447452const itemId = item?.id ?? readString(params, "itemId") ?? readString(params, "id");
453+this.rememberAssistantPhase(item);
448454if (itemId) {
449455this.activeItemIds.add(itemId);
450456}
@@ -492,9 +498,13 @@ export class CodexAppServerEventProjector {
492498this.activeItemIds.delete(itemId);
493499this.completedItemIds.add(itemId);
494500}
501+this.rememberAssistantPhase(item);
495502if (item?.type === "agentMessage" && typeof item.text === "string" && item.text) {
496503this.rememberAssistantItem(item.id);
497504this.assistantTextByItem.set(item.id, item.text);
505+if (this.isCommentaryAssistantItem(item.id)) {
506+this.emitCommentaryProgress({ itemId: item.id, text: item.text });
507+}
498508}
499509this.recordNativeGeneratedMedia(item);
500510if (item?.type === "plan" && typeof item.text === "string" && item.text) {
@@ -628,6 +638,7 @@ export class CodexAppServerEventProjector {
628638this.promptErrorSource = "prompt";
629639}
630640for (const item of turn.items ?? []) {
641+this.rememberAssistantPhase(item);
631642if (item.type === "agentMessage" && typeof item.text === "string" && item.text) {
632643this.rememberAssistantItem(item.id);
633644this.assistantTextByItem.set(item.id, item.text);
@@ -755,6 +766,42 @@ export class CodexAppServerEventProjector {
755766});
756767}
757768769+private rememberAssistantPhase(item: CodexThreadItem | undefined): void {
770+if (item?.type !== "agentMessage") {
771+return;
772+}
773+const phase = readItemString(item, "phase");
774+if (phase) {
775+this.assistantPhaseByItem.set(item.id, phase);
776+}
777+}
778+779+private isCommentaryAssistantItem(itemId: string): boolean {
780+return this.assistantPhaseByItem.get(itemId) === "commentary";
781+}
782+783+private emitCommentaryProgress(params: { itemId: string; text: string }): void {
784+const progressText = params.text.replace(/\s+/g, " ").trim();
785+if (
786+!progressText ||
787+this.lastCommentaryProgressTextByItem.get(params.itemId) === progressText
788+) {
789+return;
790+}
791+this.lastCommentaryProgressTextByItem.set(params.itemId, progressText);
792+this.emitAgentEvent({
793+stream: "item",
794+data: {
795+itemId: params.itemId,
796+kind: "preamble",
797+title: "Preamble",
798+phase: "update",
799+ progressText,
800+source: "codex-app-server",
801+},
802+});
803+}
804+758805private emitStandardItemEvent(params: {
759806phase: "start" | "end";
760807item: CodexThreadItem | undefined;
@@ -1118,6 +1165,9 @@ export class CodexAppServerEventProjector {
11181165continue;
11191166}
11201167const text = this.assistantTextByItem.get(itemId)?.trim();
1168+if (this.assistantPhaseByItem.get(itemId) === "commentary") {
1169+continue;
1170+}
11211171if (text && !this.toolProgressTexts.has(text)) {
11221172return text;
11231173}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。