]> git.giacomolli.uk - onechat/commitdiff
Show the user messages of the other clients
authorAnderson A. Giacomolli <agiacomolli@gmail.com>
Sat, 7 Nov 2026 13:12:48 +0000 (10:12 -0300)
committerAnderson A. Giacomolli <agiacomolli@gmail.com>
Sat, 7 Nov 2026 13:12:48 +0000 (10:12 -0300)
Each user message event names its source client. The socket clients
submit as terminal, and the terminal client shows a user message from
any other source with the source as a mark, such as "[telegram] text".
With more than one client on one agent, each client then shows the full
conversation, so a reply always comes with its question. The log does
not keep the source.

Issue: a143bd8

chat.go
daemon.go
daemon_test.go
turn.go
turn_test.go

diff --git a/chat.go b/chat.go
index 8bc5285163488df1b67a53e377ceba6720eed8b2..1e96b2458be159bdd8fe624f35c0af4668cb6dbc 100644 (file)
--- a/chat.go
+++ b/chat.go
@@ -143,12 +143,19 @@ func showEvents(conn net.Conn, out io.Writer) error {
 }
 
 // eventText returns the text that the client shows for event, or "" for none.
-// The client shows no user message, because the user typed it. A tool result
-// shows its first resultLines lines.
+// The client shows a user message only from another source, such as telegram,
+// because the user typed the others. A tool result shows its first resultLines
+// lines.
 func eventText(event Event) string {
        switch event.Kind {
        case "ready":
                return "[connected to " + event.Text + "]"
+       case "user":
+               if event.Source == terminalSource {
+                       return ""
+               }
+
+               return "[" + event.Source + "] " + event.Text
        case "reply":
                return event.Text
        case "tool":
index 58bf502b5bba1b31d226f9439ac9038310a253ad..68073d0be5342b81eb0dc93203226cb0ee063954 100644 (file)
--- a/daemon.go
+++ b/daemon.go
@@ -34,6 +34,10 @@ const (
 
        // socketPathBytes is the longest path of a Unix socket on Linux.
        socketPathBytes = 107
+
+       // terminalSource is the source of the user messages from the clients of
+       // the socket.
+       terminalSource = "terminal"
 )
 
 // errTokensInUse tells that another onechat process uses the token file.
@@ -305,7 +309,7 @@ func runRequest(l *loop, request clientRequest) error {
                        return errors.New("empty message")
                }
 
-               _, err := l.Submit(request.Text)
+               _, err := l.Submit(terminalSource, request.Text)
 
                return err
        case "interrupt":
index fe409293d85f50638eeb7cc017942e0e19488e01..00680e6fec697e028bdeeadf25688f698257b3fc 100644 (file)
@@ -154,10 +154,15 @@ func TestEventText(t *testing.T) {
                        text:  "Done.",
                },
                {
-                       name:  "user message not shown",
-                       event: Event{Kind: "user", Text: "typed"},
+                       name:  "own user message not shown",
+                       event: Event{Kind: "user", Text: "typed", Source: terminalSource},
                        text:  "",
                },
+               {
+                       name:  "user message of another source marked",
+                       event: Event{Kind: "user", Text: "from the phone", Source: "telegram"},
+                       text:  "[telegram] from the phone",
+               },
                {
                        name:  "long result cut",
                        event: Event{Kind: "echo", Text: strings.Repeat("line\n", 12)},
diff --git a/turn.go b/turn.go
index f8faad83d0a8be375b4e9fd3b2f9e6766284d2cf..0b2b29eed4fcc65d996c57e25570b2667a178037 100644 (file)
--- a/turn.go
+++ b/turn.go
@@ -18,11 +18,14 @@ type modelClient interface {
 
 // Event tells the clients what the agent does as it happens. Kind is the kind
 // of a logged message (user, reply, tool, echo), or one of ready, busy, idle,
-// and error. ID is the ID of a logged message.
+// and error. ID is the ID of a logged message. Source names the client that
+// sent a user message, such as terminal or telegram, so that each client can
+// show the messages of the others.
 type Event struct {
-       Kind string `json:"kind"`
-       ID   int    `json:"id,omitempty"`
-       Text string `json:"text,omitempty"`
+       Kind   string `json:"kind"`
+       ID     int    `json:"id,omitempty"`
+       Text   string `json:"text,omitempty"`
+       Source string `json:"source,omitempty"`
 }
 
 // loop runs the turns of one agent. A user message goes to the log when it
@@ -98,10 +101,11 @@ func newLoop(agent *Agent, memory *Memory, client modelClient, clock func() time
        return l, nil
 }
 
-// Submit logs a user message and wakes the loop. The message joins the next
-// request: the first one of a new turn, or the next one of a running turn.
-func (l *loop) Submit(text string) (Message, error) {
-       message, err := l.Append("user", text)
+// Submit logs a user message from the client source and wakes the loop. The
+// message joins the next request: the first one of a new turn, or the next one
+// of a running turn.
+func (l *loop) Submit(source, text string) (Message, error) {
+       message, err := l.record("user", source, text)
        if err != nil {
                return Message{}, err
        }
@@ -144,15 +148,22 @@ func (l *loop) Busy() bool {
 // Append logs a message and tells the clients. The toolbox logs its calls and
 // results through it.
 func (l *loop) Append(kind, text string) (Message, error) {
+       return l.record(kind, "", text)
+}
+
+// record logs a message and tells the clients, with the client source of a user
+// message.
+func (l *loop) record(kind, source, text string) (Message, error) {
        message, err := l.memory.Append(kind, text)
        if err != nil {
                return Message{}, err
        }
 
        l.notify(Event{
-               Kind: kind,
-               ID:   message.ID,
-               Text: text,
+               Kind:   kind,
+               ID:     message.ID,
+               Text:   text,
+               Source: source,
        })
 
        return message, nil
index f1295e938a54f25fc2b79ff3e8765c7cb360b43c..0de7dd7be85cf86c5498b977b221599fade3d033 100644 (file)
@@ -30,7 +30,7 @@ func TestTurnRunsToolsUntilTheFinalAnswer(t *testing.T) {
        }
 
        test := newTestLoop(t, server)
-       _, err := test.loop.Submit("Run echo hi.")
+       _, err := test.loop.Submit(terminalSource, "Run echo hi.")
        require.NoError(t, err)
        test.events.WaitFor(t, "idle", 1)
        require.Equal(t, 2, server.Calls())
@@ -74,7 +74,7 @@ func TestFirstRequestHoldsTheViewTheStateAndTheMessage(t *testing.T) {
        _, err := test.memory.Append("user", "an earlier message")
        require.NoError(t, err)
 
-       _, err = test.loop.Submit("The new message.")
+       _, err = test.loop.Submit(terminalSource, "The new message.")
        require.NoError(t, err)
        test.events.WaitFor(t, "idle", 1)
 
@@ -102,11 +102,11 @@ func TestMessageDuringATurnJoinsTheNextRequest(t *testing.T) {
        }
 
        test := newTestLoop(t, server)
-       _, err := test.loop.Submit("first")
+       _, err := test.loop.Submit(terminalSource, "first")
        require.NoError(t, err)
 
        <-arrived
-       _, err = test.loop.Submit("second, during the turn")
+       _, err = test.loop.Submit(terminalSource, "second, during the turn")
        require.NoError(t, err)
        close(release)
 
@@ -136,11 +136,11 @@ func TestMessageDuringTheFinalRequestStartsANewTurn(t *testing.T) {
        }
 
        test := newTestLoop(t, server)
-       _, err := test.loop.Submit("first")
+       _, err := test.loop.Submit(terminalSource, "first")
        require.NoError(t, err)
 
        <-arrived
-       _, err = test.loop.Submit("second")
+       _, err = test.loop.Submit(terminalSource, "second")
        require.NoError(t, err)
        close(release)
 
@@ -161,7 +161,7 @@ func TestTurnWaitsForTheSummaries(t *testing.T) {
        _, err := test.memory.Append("echo", strings.Repeat("long output ", 100))
        require.NoError(t, err)
 
-       _, err = test.loop.Submit("go on")
+       _, err = test.loop.Submit(terminalSource, "go on")
        require.NoError(t, err)
 
        time.Sleep(50 * time.Millisecond)
@@ -187,7 +187,7 @@ func TestInterruptEndsTheTurn(t *testing.T) {
        }
 
        test := newTestLoop(t, server)
-       _, err := test.loop.Submit("a long task")
+       _, err := test.loop.Submit(terminalSource, "a long task")
        require.NoError(t, err)
 
        <-arrived
@@ -199,7 +199,7 @@ func TestInterruptEndsTheTurn(t *testing.T) {
        require.Equal(t, []string{"user: a long task"}, test.Log(t))
        require.False(t, test.loop.Interrupt())
 
-       _, err = test.loop.Submit("again")
+       _, err = test.loop.Submit(terminalSource, "again")
        require.NoError(t, err)
        test.events.WaitFor(t, "idle", 2)
        require.Equal(t, "reply: answer", test.Log(t)[2])
@@ -212,7 +212,7 @@ func TestFailedCallEndsTheTurnWithoutARetry(t *testing.T) {
        }
 
        test := newTestLoop(t, server)
-       _, err := test.loop.Submit("hello")
+       _, err := test.loop.Submit(terminalSource, "hello")
        require.NoError(t, err)
        test.events.WaitFor(t, "idle", 1)
 
@@ -225,7 +225,7 @@ func TestViewMessagesOnlyGrowBetweenTurns(t *testing.T) {
        server := newFakeResponses(t)
        test := newTestLoop(t, server)
        for n, text := range []string{"one", "two", "three"} {
-               _, err := test.loop.Submit(text)
+               _, err := test.loop.Submit(terminalSource, text)
                require.NoError(t, err)
                test.events.WaitFor(t, "idle", n+1)
        }