diff --git a/chat/src/app/header.tsx b/chat/src/app/header.tsx
index 85c2836d..47b370fe 100644
--- a/chat/src/app/header.tsx
+++ b/chat/src/app/header.tsx
@@ -1,10 +1,10 @@
"use client";
-import { useChat } from "@/components/chat-provider";
-import { ModeToggle } from "../components/mode-toggle";
+import {AgentType, useChat} from "@/components/chat-provider";
+import {ModeToggle} from "@/components/mode-toggle";
export function Header() {
- const { serverStatus } = useChat();
+ const {serverStatus, agentType} = useChat();
return (
);
diff --git a/chat/src/components/chat-provider.tsx b/chat/src/components/chat-provider.tsx
index baaf2e87..21a2ee3f 100644
--- a/chat/src/components/chat-provider.tsx
+++ b/chat/src/components/chat-provider.tsx
@@ -9,7 +9,7 @@ import {
PropsWithChildren,
useContext,
} from "react";
-import { toast } from "sonner";
+import {toast} from "sonner";
import {getErrorMessage} from "@/lib/error-utils";
interface Message {
@@ -33,6 +33,7 @@ interface MessageUpdateEvent {
interface StatusChangeEvent {
status: string;
+ agent_type: string;
}
interface APIErrorDetail {
@@ -64,12 +65,35 @@ export interface FileUploadResponse {
filePath?: string;
}
+export type AgentType = "claude" | "goose" | "aider" | "gemini" | "amp" | "codex" | "cursor" | "cursor-agent" | "copilot" | "auggie" | "amazonq" | "opencode" | "custom" | "unknown";
+
+export type AgentColorDisplayNamePair = {
+ displayName: string;
+}
+
+export const AgentType: Record, AgentColorDisplayNamePair> = {
+ claude: {displayName: "Claude Code"},
+ goose: {displayName: "Goose"},
+ aider: {displayName: "Aider"},
+ gemini: { displayName: "Gemini"},
+ amp: {displayName: "Amp"},
+ codex: {displayName: "Codex"},
+ cursor: { displayName: "Cursor Agent"},
+ "cursor-agent": { displayName: "Cursor Agent"},
+ copilot: {displayName: "Copilot"},
+ auggie: {displayName: "Auggie"},
+ amazonq: {displayName: "Amazon Q"},
+ opencode: {displayName: "Opencode"},
+ custom: { displayName: "Custom"}
+}
+
interface ChatContextValue {
messages: (Message | DraftMessage)[];
loading: boolean;
serverStatus: ServerStatus;
sendMessage: (message: string, type?: MessageType) => void;
uploadFiles: (formData: FormData) => Promise;
+ agentType: AgentType;
}
const ChatContext = createContext(undefined);
@@ -113,6 +137,7 @@ export function ChatProvider({ children }: PropsWithChildren) {
const [messages, setMessages] = useState<(Message | DraftMessage)[]>([]);
const [loading, setLoading] = useState(false);
const [serverStatus, setServerStatus] = useState("unknown");
+ const [agentType, setAgentType] = useState("custom");
const eventSourceRef = useRef(null);
const agentAPIUrl = useAgentAPIUrl();
@@ -185,6 +210,9 @@ export function ChatProvider({ children }: PropsWithChildren) {
} else {
setServerStatus("unknown");
}
+
+ // Set agent type
+ setAgentType(data.agent_type === "" ? "unknown" : data.agent_type as AgentType);
});
// Handle connection open (server is online)
@@ -311,7 +339,7 @@ export function ChatProvider({ children }: PropsWithChildren) {
} else {
result = (await response.json()) as FileUploadResponse;
}
-
+
} catch (error) {
result.ok = false;
console.error("Error uploading files:", error);
@@ -332,6 +360,7 @@ export function ChatProvider({ children }: PropsWithChildren) {
sendMessage,
serverStatus,
uploadFiles,
+ agentType,
}}
>
{children}
diff --git a/lib/httpapi/events.go b/lib/httpapi/events.go
index 1e6281d7..73eff07b 100644
--- a/lib/httpapi/events.go
+++ b/lib/httpapi/events.go
@@ -44,7 +44,8 @@ type MessageUpdateBody struct {
}
type StatusChangeBody struct {
- Status AgentStatus `json:"status" doc:"Agent status"`
+ Status AgentStatus `json:"status" doc:"Agent status"`
+ AgentType mf.AgentType `json:"agent_type" doc:"Type of the agent being used by the server."`
}
type ScreenUpdateBody struct {
@@ -60,6 +61,7 @@ type EventEmitter struct {
mu sync.Mutex
messages []st.ConversationMessage
status AgentStatus
+ agentType mf.AgentType
chans map[int]chan Event
chanIdx int
subscriptionBufSize int
@@ -147,7 +149,7 @@ func (e *EventEmitter) UpdateMessagesAndEmitChanges(newMessages []st.Conversatio
e.messages = newMessages
}
-func (e *EventEmitter) UpdateStatusAndEmitChanges(newStatus st.ConversationStatus) {
+func (e *EventEmitter) UpdateStatusAndEmitChanges(newStatus st.ConversationStatus, agentType mf.AgentType) {
e.mu.Lock()
defer e.mu.Unlock()
@@ -156,8 +158,9 @@ func (e *EventEmitter) UpdateStatusAndEmitChanges(newStatus st.ConversationStatu
return
}
- e.notifyChannels(EventTypeStatusChange, StatusChangeBody{Status: newAgentStatus})
+ e.notifyChannels(EventTypeStatusChange, StatusChangeBody{Status: newAgentStatus, AgentType: agentType})
e.status = newAgentStatus
+ e.agentType = agentType
}
func (e *EventEmitter) UpdateScreenAndEmitChanges(newScreen string) {
@@ -183,7 +186,7 @@ func (e *EventEmitter) currentStateAsEvents() []Event {
}
events = append(events, Event{
Type: EventTypeStatusChange,
- Payload: StatusChangeBody{Status: e.status},
+ Payload: StatusChangeBody{Status: e.status, AgentType: e.agentType},
})
events = append(events, Event{
Type: EventTypeScreenUpdate,
diff --git a/lib/httpapi/events_test.go b/lib/httpapi/events_test.go
index 23a1d365..46ccea56 100644
--- a/lib/httpapi/events_test.go
+++ b/lib/httpapi/events_test.go
@@ -5,6 +5,7 @@ import (
"testing"
"time"
+ mf "github.com/coder/agentapi/lib/msgfmt"
st "github.com/coder/agentapi/lib/screentracker"
"github.com/stretchr/testify/assert"
)
@@ -51,11 +52,11 @@ func TestEventEmitter(t *testing.T) {
Payload: MessageUpdateBody{Id: 2, Message: "What's up?", Role: st.ConversationRoleAgent, Time: now},
}, newEvent)
- emitter.UpdateStatusAndEmitChanges(st.ConversationStatusStable)
+ emitter.UpdateStatusAndEmitChanges(st.ConversationStatusStable, mf.AgentTypeAider)
newEvent = <-ch
assert.Equal(t, Event{
Type: EventTypeStatusChange,
- Payload: StatusChangeBody{Status: AgentStatusStable},
+ Payload: StatusChangeBody{Status: AgentStatusStable, AgentType: mf.AgentTypeAider},
}, newEvent)
})
diff --git a/lib/httpapi/models.go b/lib/httpapi/models.go
index 7d86e81b..7ed52c43 100644
--- a/lib/httpapi/models.go
+++ b/lib/httpapi/models.go
@@ -3,6 +3,7 @@ package httpapi
import (
"time"
+ mf "github.com/coder/agentapi/lib/msgfmt"
st "github.com/coder/agentapi/lib/screentracker"
"github.com/coder/agentapi/lib/util"
"github.com/danielgtaylor/huma/v2"
@@ -35,7 +36,8 @@ type Message struct {
// StatusResponse represents the server status
type StatusResponse struct {
Body struct {
- Status AgentStatus `json:"status" doc:"Current agent status. 'running' means that the agent is processing a message, 'stable' means that the agent is idle and waiting for input."`
+ Status AgentStatus `json:"status" doc:"Current agent status. 'running' means that the agent is processing a message, 'stable' means that the agent is idle and waiting for input."`
+ AgentType mf.AgentType `json:"agent_type" doc:"Type of the agent being used by the server."`
}
}
diff --git a/lib/httpapi/server.go b/lib/httpapi/server.go
index d0917669..75cc6dc3 100644
--- a/lib/httpapi/server.go
+++ b/lib/httpapi/server.go
@@ -334,7 +334,7 @@ func (s *Server) StartSnapshotLoop(ctx context.Context) {
s.logger.Info("Initial prompt sent successfully")
}
}
- s.emitter.UpdateStatusAndEmitChanges(currentStatus)
+ s.emitter.UpdateStatusAndEmitChanges(currentStatus, s.agentType)
s.emitter.UpdateMessagesAndEmitChanges(s.conversation.Messages())
s.emitter.UpdateScreenAndEmitChanges(s.conversation.Screen())
time.Sleep(snapshotInterval)
@@ -404,6 +404,7 @@ func (s *Server) getStatus(ctx context.Context, input *struct{}) (*StatusRespons
resp := &StatusResponse{}
resp.Body.Status = agentStatus
+ resp.Body.AgentType = s.agentType
return resp, nil
}
diff --git a/lib/msgfmt/msgfmt.go b/lib/msgfmt/msgfmt.go
index dbe2e65b..0cf1ca8e 100644
--- a/lib/msgfmt/msgfmt.go
+++ b/lib/msgfmt/msgfmt.go
@@ -231,6 +231,7 @@ func trimEmptyLines(message string) string {
type AgentType string
+// Remember to add the display name to the agentapi/chat/src/components/chat-provider.tsx
const (
AgentTypeClaude AgentType = "claude"
AgentTypeGoose AgentType = "goose"
diff --git a/openapi.json b/openapi.json
index a36154ed..4c387f3e 100644
--- a/openapi.json
+++ b/openapi.json
@@ -270,12 +270,17 @@
"StatusChangeBody": {
"additionalProperties": false,
"properties": {
+ "agent_type": {
+ "description": "Type of the agent being used by the server.",
+ "type": "string"
+ },
"status": {
"$ref": "#/components/schemas/AgentStatus",
"description": "Agent status"
}
},
"required": [
+ "agent_type",
"status"
],
"type": "object"
@@ -292,12 +297,17 @@
"readOnly": true,
"type": "string"
},
+ "agent_type": {
+ "description": "Type of the agent being used by the server.",
+ "type": "string"
+ },
"status": {
"$ref": "#/components/schemas/AgentStatus",
"description": "Current agent status. 'running' means that the agent is processing a message, 'stable' means that the agent is idle and waiting for input."
}
},
"required": [
+ "agent_type",
"status"
],
"type": "object"