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

Skip to content

fix: initial app status and zero-based id #18622

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 51 additions & 25 deletions cli/exp_mcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -362,11 +362,19 @@ func (*RootCmd) mcpConfigureCursor() *serpent.Command {
}

type taskReport struct {
link string
messageID int64
// link is optional.
link string
// messageID must be set if this update is from a *user* message. A user
// message only happens when interacting via the AI AgentAPI (as opposed to
// interacting with the terminal directly).
messageID *int64
// selfReported must be set if the update is directly from the AI agent
// (as opposed to the screen watcher).
selfReported bool
state codersdk.WorkspaceAppStatusState
summary string
// state must always be set.
state codersdk.WorkspaceAppStatusState
// summary is optional.
summary string
}

type mcpServer struct {
Expand All @@ -388,31 +396,48 @@ func (r *RootCmd) mcpServer() *serpent.Command {
return &serpent.Command{
Use: "server",
Handler: func(inv *serpent.Invocation) error {
// lastUserMessageID is the ID of the last *user* message that we saw. A
// user message only happens when interacting via the AI AgentAPI (as
// opposed to interacting with the terminal directly).
var lastUserMessageID int64
var lastReport taskReport
// Create a queue that skips duplicates and preserves summaries.
queue := cliutil.NewQueue[taskReport](512).WithPredicate(func(report taskReport) (taskReport, bool) {
// Use "working" status if this is a new user message. If this is not a
// new user message, and the status is "working" and not self-reported
// (meaning it came from the screen watcher), then it means one of two
// things:
// 1. The AI agent is still working, so there is nothing to update.
// 2. The AI agent stopped working, then the user has interacted with
// the terminal directly. For now, we are ignoring these updates.
// This risks missing cases where the user manually submits a new
// prompt and the AI agent becomes active and does not update itself,
// but it avoids spamming useless status updates as the user is
// typing, so the tradeoff is worth it. In the future, if we can
// reliably distinguish between user and AI agent activity, we can
// change this.
if report.messageID > lastUserMessageID {
report.state = codersdk.WorkspaceAppStatusStateWorking
} else if report.state == codersdk.WorkspaceAppStatusStateWorking && !report.selfReported {
// Avoid queuing empty statuses (this would probably indicate a
// developer error)
if report.state == "" {
return report, false
}
// If this is a user message, discard if it is not new.
if report.messageID != nil && lastReport.messageID != nil &&
*lastReport.messageID >= *report.messageID {
return report, false
}
// If this is not a user message, and the status is "working" and not
// self-reported (meaning it came from the screen watcher), then it
// means one of two things:
//
// 1. The AI agent is not working; the user is interacting with the
// terminal directly.
// 2. The AI agent is working.
//
// At the moment, we have no way to tell the difference between these
// two states. In the future, if we can reliably distinguish between
// user and AI agent activity, we can change this.
//
// If this is our first update, we assume it is the AI agent working and
// accept the update.
//
// Otherwise we discard the update. This risks missing cases where the
// user manually submits a new prompt and the AI agent becomes active
// (and does not update itself), but it avoids spamming useless status
// updates as the user is typing, so the tradeoff is worth it.
if report.messageID == nil &&
report.state == codersdk.WorkspaceAppStatusStateWorking &&
!report.selfReported && lastReport.state != "" {
return report, false
}
// Keep track of the last message ID so we can tell when a message is
// new or if it has been re-emitted.
if report.messageID == nil {
report.messageID = lastReport.messageID
}
// Preserve previous message and URI if there was no message.
if report.summary == "" {
report.summary = lastReport.summary
Expand Down Expand Up @@ -600,7 +625,8 @@ func (s *mcpServer) startWatcher(ctx context.Context, inv *serpent.Invocation) {
case agentapi.EventMessageUpdate:
if ev.Role == agentapi.RoleUser {
err := s.queue.Push(taskReport{
messageID: ev.Id,
messageID: &ev.Id,
state: codersdk.WorkspaceAppStatusStateWorking,
})
if err != nil {
cliui.Warnf(inv.Stderr, "Failed to queue update: %s", err)
Expand Down
Loading
Loading