Skip to content
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
420 changes: 281 additions & 139 deletions crates/agent-gateway/internal/proto/v2/gateway.pb.go

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions crates/agent-gateway/internal/protocol/pbws/guard.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ func vetAgentRequest(sm session.AgentView, env *gatewayv2.GatewayEnvelope) error
*gatewayv2.GatewayEnvelope_HistoryDelete,
*gatewayv2.GatewayEnvelope_HistoryPrefix,
*gatewayv2.GatewayEnvelope_HistoryPin,
*gatewayv2.GatewayEnvelope_HistorySetCwd,
*gatewayv2.GatewayEnvelope_HistoryShareGet,
*gatewayv2.GatewayEnvelope_HistoryShareSet,
*gatewayv2.GatewayEnvelope_HistoryWorkdirs,
Expand Down
11 changes: 11 additions & 0 deletions crates/agent-gateway/proto/v2/gateway.proto
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ message GatewayEnvelope {
HistoryBranchRequest history_branch = 92;
ProviderUsageRequest provider_usage = 93;
ChatFileOpenRequest chat_file_open = 94;
HistorySetCwdRequest history_set_cwd = 95;
}

// Legacy tunnel control/frame payloads (pre-rewrite protocol) and the
Expand Down Expand Up @@ -135,6 +136,7 @@ message AgentEnvelope {
ChatIngressFragment chat_ingress_fragment = 97;
ChatFileOpenResponse chat_file_open_resp = 98;
ErrorResponse error = 99;
HistorySetCwdResponse history_set_cwd_resp = 100;
}

// Legacy tunnel control/frame payloads (pre-rewrite protocol) and the
Expand Down Expand Up @@ -1280,3 +1282,12 @@ message ChatIngressAck {
REJECTED = 4;
}
}

message HistorySetCwdRequest {
string conversation_id = 1;
string cwd = 2;
}

message HistorySetCwdResponse {
ConversationSummary conversation = 1;
}
38 changes: 38 additions & 0 deletions crates/agent-gateway/test/webui/gateway-socket-client.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1043,6 +1043,44 @@ test("GatewayWebSocketClient sends history branch requests with the base message
resetGatewayWebSocketClient();
});

test("GatewayWebSocketClient sends history cwd update requests", async () => {
installBrowser();
const { codec, getGatewayWebSocketClient, resetGatewayWebSocketClient } = loadGatewaySocket();
resetGatewayWebSocketClient();

const client = getGatewayWebSocketClient("token");
const updatePromise = client.setHistoryCwd("conversation-1", "/tmp/project-b");
const socket = await connectAndAuth(codec);
await waitFor(() => findAgentRequest(codec, socket, "history_set_cwd"), "history cwd frame");
const request = findAgentRequest(codec, socket, "history_set_cwd");
assert.deepEqual(request.json.agent_request.history_set_cwd, {
conversation_id: "conversation-1",
cwd: "/tmp/project-b",
});
socket.receiveBinary(
codec.encodeServerFrame({
request_id: request.requestId,
agent_response: {
history_set_cwd_resp: {
conversation: {
id: "conversation-1",
title: "Moved conversation",
cwd: "/tmp/project-b",
message_count: 6,
created_at: 1700000000100,
updated_at: 1700000000200,
},
},
},
}),
);
const updated = await updatePromise;
assert.equal(updated.id, "conversation-1");
assert.equal(updated.cwd, "/tmp/project-b");

resetGatewayWebSocketClient();
});

test("GatewayWebSocketClient reconnects before read requests when an authenticated socket goes stale", async () => {
installBrowser();
const { codec, getGatewayWebSocketClient, resetGatewayWebSocketClient } = loadGatewaySocket();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,27 @@ export function GatewaySidebarContainer(props: GatewaySidebarContainerProps) {
void store.setPinned(id, isPinned);
});

const handleMoveToWorkspace = useStableCallback((id: string, cwd: string) => {
if (sectionsDisabled) {
return;
}
clearMutationErrors();
void store.setCwd(id, cwd);
});

const handleMoveConversationsToWorkspace = useStableCallback(
async (ids: readonly string[], cwd: string) => {
if (sectionsDisabled) {
return ids;
}
clearMutationErrors();
const results = await Promise.all(
ids.map(async (id) => ({ id, moved: await store.setCwd(id, cwd) })),
);
return results.filter((result) => !result.moved).map((result) => result.id);
},
);

const handleDeleteConversation = useStableCallback((id: string) => {
if (sectionsDisabled) {
return;
Expand Down Expand Up @@ -369,6 +390,8 @@ export function GatewaySidebarContainer(props: GatewaySidebarContainerProps) {
onCommitRename={handleCommitRename}
onCancelRename={handleCancelRename}
onSetPinned={handleSetPinned}
onMoveToWorkspace={handleMoveToWorkspace}
onMoveConversationsToWorkspace={handleMoveConversationsToWorkspace}
canShareConversations={props.canShareConversations}
sharedConversationCount={props.sharedConversationCount}
onShareConversation={props.onShareConversation}
Expand Down
Loading