Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 7454e49

Browse files
committed
fix: handle new user messages correctly
Two things: 1. Message IDs can be zero-based. 2. We were not actually updating the last user message ID. I refactored a little to keep track of the last message in lastReport.
1 parent 1006b98 commit 7454e49

File tree

2 files changed

+64
-18
lines changed

2 files changed

+64
-18
lines changed

cli/exp_mcp.go

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -362,11 +362,19 @@ func (*RootCmd) mcpConfigureCursor() *serpent.Command {
362362
}
363363

364364
type taskReport struct {
365-
link string
366-
messageID int64
365+
// link is optional.
366+
link string
367+
// messageID must be set if this update is from a *user* message. A user
368+
// message only happens when interacting via the AI AgentAPI (as opposed to
369+
// interacting with the terminal directly).
370+
messageID *int64
371+
// selfReported must be set if the update is directly from the AI agent
372+
// (as opposed to the screen watcher).
367373
selfReported bool
368-
state codersdk.WorkspaceAppStatusState
369-
summary string
374+
// state must always be set.
375+
state codersdk.WorkspaceAppStatusState
376+
// summary is optional.
377+
summary string
370378
}
371379

372380
type mcpServer struct {
@@ -388,17 +396,17 @@ func (r *RootCmd) mcpServer() *serpent.Command {
388396
return &serpent.Command{
389397
Use: "server",
390398
Handler: func(inv *serpent.Invocation) error {
391-
// lastUserMessageID is the ID of the last *user* message that we saw. A
392-
// user message only happens when interacting via the AI AgentAPI (as
393-
// opposed to interacting with the terminal directly).
394-
var lastUserMessageID int64
395399
var lastReport taskReport
396400
// Create a queue that skips duplicates and preserves summaries.
397401
queue := cliutil.NewQueue[taskReport](512).WithPredicate(func(report taskReport) (taskReport, bool) {
398-
// Use "working" status if this is a new user message. If this is not a
399-
// new user message, and the status is "working" and not self-reported
400-
// (meaning it came from the screen watcher), then it means one of two
401-
// things:
402+
// If this is a user message, discard if it is not new.
403+
if report.messageID != nil &&
404+
lastReport.messageID != nil && *lastReport.messageID >= *report.messageID {
405+
return report, false
406+
}
407+
// If this is not a user message, and the status is "working" and not
408+
// self-reported (meaning it came from the screen watcher), then it
409+
// means one of two things:
402410
//
403411
// 1. The AI agent is not working; the user is interacting with the
404412
// terminal directly.
@@ -415,18 +423,27 @@ func (r *RootCmd) mcpServer() *serpent.Command {
415423
// user manually submits a new prompt and the AI agent becomes active
416424
// (and does not update itself), but it avoids spamming useless status
417425
// updates as the user is typing, so the tradeoff is worth it.
418-
if report.messageID > lastUserMessageID {
419-
report.state = codersdk.WorkspaceAppStatusStateWorking
420-
} else if report.state == codersdk.WorkspaceAppStatusStateWorking && !report.selfReported && lastReport.state != "" {
426+
if report.messageID == nil &&
427+
report.state == codersdk.WorkspaceAppStatusStateWorking &&
428+
!report.selfReported && lastReport.state != "" {
421429
return report, false
422430
}
431+
// Keep track of the last message ID.
432+
if report.messageID == nil {
433+
report.messageID = lastReport.messageID
434+
}
423435
// Preserve previous message and URI if there was no message.
424436
if report.summary == "" {
425437
report.summary = lastReport.summary
426438
if report.link == "" {
427439
report.link = lastReport.link
428440
}
429441
}
442+
// Avoid queuing empty statuses (this would probably indicate a
443+
// developer error)
444+
if report.state == "" {
445+
return report, false
446+
}
430447
// Avoid queueing duplicate updates.
431448
if report.state == lastReport.state &&
432449
report.link == lastReport.link &&
@@ -607,7 +624,8 @@ func (s *mcpServer) startWatcher(ctx context.Context, inv *serpent.Invocation) {
607624
case agentapi.EventMessageUpdate:
608625
if ev.Role == agentapi.RoleUser {
609626
err := s.queue.Push(taskReport{
610-
messageID: ev.Id,
627+
messageID: &ev.Id,
628+
state: codersdk.WorkspaceAppStatusStateWorking,
611629
})
612630
if err != nil {
613631
cliui.Warnf(inv.Stderr, "Failed to queue update: %s", err)

cli/exp_mcp_test.go

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -835,7 +835,7 @@ func TestExpMcpReporter(t *testing.T) {
835835
},
836836
// Agent messages are ignored.
837837
{
838-
event: makeMessageEvent(1, agentapi.RoleAgent),
838+
event: makeMessageEvent(0, agentapi.RoleAgent),
839839
},
840840
// AI agent reports that it failed and URI is blank.
841841
{
@@ -854,7 +854,7 @@ func TestExpMcpReporter(t *testing.T) {
854854
// ... but this time we have a new user message so we know there is AI
855855
// agent activity. This time the "working" update will not be skipped.
856856
{
857-
event: makeMessageEvent(2, agentapi.RoleUser),
857+
event: makeMessageEvent(1, agentapi.RoleUser),
858858
expected: &codersdk.WorkspaceAppStatus{
859859
State: codersdk.WorkspaceAppStatusStateWorking,
860860
Message: "oops",
@@ -895,6 +895,33 @@ func TestExpMcpReporter(t *testing.T) {
895895
URI: "",
896896
},
897897
},
898+
// Zero ID should be accepted.
899+
{
900+
event: makeMessageEvent(0, agentapi.RoleUser),
901+
expected: &codersdk.WorkspaceAppStatus{
902+
State: codersdk.WorkspaceAppStatusStateWorking,
903+
Message: "",
904+
URI: "",
905+
},
906+
},
907+
// Stable again.
908+
{
909+
event: makeStatusEvent(agentapi.StatusStable),
910+
expected: &codersdk.WorkspaceAppStatus{
911+
State: codersdk.WorkspaceAppStatusStateIdle,
912+
Message: "",
913+
URI: "",
914+
},
915+
},
916+
// Next ID.
917+
{
918+
event: makeMessageEvent(1, agentapi.RoleUser),
919+
expected: &codersdk.WorkspaceAppStatus{
920+
State: codersdk.WorkspaceAppStatusStateWorking,
921+
Message: "",
922+
URI: "",
923+
},
924+
},
898925
},
899926
},
900927
}
@@ -954,6 +981,7 @@ func TestExpMcpReporter(t *testing.T) {
954981
case w, ok := <-watcher:
955982
require.True(t, ok, "watch channel closed")
956983
if w.LatestAppStatus != nil && w.LatestAppStatus.ID != lastAppStatus.ID {
984+
t.Logf("Got status update: %s > %s", lastAppStatus.State, w.LatestAppStatus.State)
957985
lastAppStatus = *w.LatestAppStatus
958986
return lastAppStatus
959987
}

0 commit comments

Comments
 (0)