This is an automated email from the ASF dual-hosted git repository.

hsluoyz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/casbin-gateway.git

commit 0ddd45549f82cc34980cbbbbcdf9f5ca8709b2ba
Author: Yang Luo <[email protected]>
AuthorDate: Mon Sep 7 00:05:10 2026 +0800

    fix(chat): keep Codex's passing notices out of the reply
---
 agentsession/event.go       |  6 +++++-
 agentsession/parse_codex.go |  5 ++++-
 imbridge/render.go          | 19 ++++++++++++++-----
 web/src/pages/ChatPage.tsx  | 14 +++++++++++++-
 web/src/types.ts            |  2 +-
 5 files changed, 37 insertions(+), 9 deletions(-)

diff --git a/agentsession/event.go b/agentsession/event.go
index 57a663e..9483510 100644
--- a/agentsession/event.go
+++ b/agentsession/event.go
@@ -30,6 +30,10 @@ const (
        EventUsage      EventType = "usage"
        EventDone       EventType = "done"
        EventError      EventType = "error"
+       // EventNotice is something the agent mentioned in passing - a setting 
it
+       // ignored, a limit it worked around. The turn carried on, so it is 
kept out
+       // of the answer and shown beside it.
+       EventNotice EventType = "notice"
 )
 
 // Event is one piece of what an agent said, in a shape every agent is 
flattened
@@ -44,7 +48,7 @@ type Event struct {
        CreatedTime string `json:"createdTime"`
 
        // Text is the answer for EventText and EventThinking, and the message 
for
-       // EventError.
+       // EventError and EventNotice.
        Text string `json:"text,omitempty"`
 
        ToolName  string `json:"toolName,omitempty"`
diff --git a/agentsession/parse_codex.go b/agentsession/parse_codex.go
index 4e2ec37..1a16255 100644
--- a/agentsession/parse_codex.go
+++ b/agentsession/parse_codex.go
@@ -138,9 +138,12 @@ func emitCodexItem(item *codexItem, emit func(Event)) {
                event.ToolName = "Edit"
                event.ToolInput = firstOf(item.Text, item.Message)
                emit(event)
+       // An error item is Codex saying something went sideways without the 
turn
+       // ending: a setting it dropped, a budget it trimmed to fit. A turn that
+       // really failed says so through turn.failed, which fails the run.
        case "error":
                if text := firstOf(item.Message, item.Text); text != "" {
-                       emit(textEvent(EventError, text))
+                       emit(textEvent(EventNotice, text))
                }
        }
 }
diff --git a/imbridge/render.go b/imbridge/render.go
index 7d1d200..f2f366a 100644
--- a/imbridge/render.go
+++ b/imbridge/render.go
@@ -25,6 +25,9 @@ import (
 // what an agent did is most of what somebody watching wants to see.
 type answerBuffer struct {
        parts []string
+       // prose says the last part is the agent talking, which is the only 
part a
+       // fragment of prose may be joined onto.
+       prose bool
        // dirty says something has arrived since the last time this was 
written out.
        dirty bool
 }
@@ -37,25 +40,31 @@ func (b *answerBuffer) add(event agentsession.Event) {
                }
                // Prose arrives in fragments, so it is joined onto the piece 
before it
                // rather than starting a new one.
-               if len(b.parts) > 0 && 
!strings.HasPrefix(b.parts[len(b.parts)-1], toolMarker) {
+               if b.prose {
                        b.parts[len(b.parts)-1] += event.Text
                } else {
                        b.parts = append(b.parts, event.Text)
+                       b.prose = true
                }
        case agentsession.EventToolUse:
                b.parts = append(b.parts, toolMarker+toolLine(event))
+               b.prose = false
        case agentsession.EventError:
-               if event.Text != "" {
-                       b.parts = append(b.parts, "\n⚠ "+event.Text)
+               if event.Text == "" {
+                       return
                }
+               b.parts = append(b.parts, "\n⚠ "+event.Text)
+               b.prose = false
+       // What the agent mentioned in passing stays out of the chat: the 
answer is
+       // what somebody asked for, and a warning about a setting on top of 
every
+       // reply is noise. The session page still shows it.
        default:
                return
        }
        b.dirty = true
 }
 
-// toolMarker starts the pieces that are a tool rather than prose, so a 
fragment
-// of prose is not appended onto one.
+// toolMarker sets a tool line apart from the prose around it.
 const toolMarker = "· "
 
 // toolInputLimit keeps a command in the message without the message becoming 
the
diff --git a/web/src/pages/ChatPage.tsx b/web/src/pages/ChatPage.tsx
index 883e190..93933ce 100644
--- a/web/src/pages/ChatPage.tsx
+++ b/web/src/pages/ChatPage.tsx
@@ -49,6 +49,9 @@ interface Turn {
   seq: number;
   prompt: string;
   blocks: Block[];
+  // notices are what the agent mentioned in passing; they are shown beside the
+  // answer rather than in it, because the turn carried on regardless.
+  notices: string[];
   error: string;
   usage?: DrivenEvent;
   done: boolean;
@@ -78,7 +81,7 @@ function toTurns(events: DrivenEvent[]): Turn[] {
   for (const event of events) {
     switch (event.type) {
     case "prompt":
-      turn = {seq: event.seq, prompt: event.text ?? "", blocks: [], error: "", 
done: false};
+      turn = {seq: event.seq, prompt: event.text ?? "", blocks: [], notices: 
[], error: "", done: false};
       turns.push(turn);
       break;
     case "text":
@@ -112,6 +115,11 @@ function toTurns(events: DrivenEvent[]): Turn[] {
         turn.error = event.text ?? "";
       }
       break;
+    case "notice":
+      if (turn && event.text && !turn.notices.includes(event.text)) {
+        turn.notices.push(event.text);
+      }
+      break;
     case "done":
       if (turn) {
         turn.done = true;
@@ -166,6 +174,10 @@ function TurnView({turn}: {turn: Turn}) {
         );
       })}
 
+      {turn.notices.map(notice => (
+        <MessageAlert key={notice} variant="warning" description={notice} />
+      ))}
+
       {turn.error ? <MessageAlert variant="destructive" title={turn.error} /> 
: null}
 
       {turn.usage ? (
diff --git a/web/src/types.ts b/web/src/types.ts
index 978122c..adc40e6 100644
--- a/web/src/types.ts
+++ b/web/src/types.ts
@@ -553,7 +553,7 @@ export interface DrivenSession {
 
 /** One piece of what a driven agent said, in the shape every agent is 
flattened into. */
 export interface DrivenEvent {
-  type: "prompt" | "text" | "thinking" | "toolUse" | "toolResult" | "usage" | 
"done" | "error";
+  type: "prompt" | "text" | "thinking" | "toolUse" | "toolResult" | "usage" | 
"done" | "error" | "notice";
   seq: number;
   createdTime: string;
   text?: string;


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to