fix(agents): unwrap read_agent_context DTO content (#140)

Le contexte projet d'un agent s'affichait "[object Object]" et se
vidait à la réouverture du panneau : read_agent_context renvoie déjà
{content} côté backend, mais les gateways front traitaient la réponse
comme une string brute. Les deux gateways (Tauri + HTTP) unwrap
maintenant .content ; type AgentContextDocument ajouté au domaine.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-03 14:35:36 +02:00
parent cf61060ffb
commit 863d9b7277
10 changed files with 135 additions and 6 deletions

View File

@ -42,12 +42,13 @@ describe("TauriAgentGateway invoke payloads", () => {
});
});
it("list_agents / read / delete pass top-level args (no request wrapper)", async () => {
it("list_agents / read / delete pass top-level args and unwrap read context DTO", async () => {
const gw = new TauriAgentGateway();
await gw.listAgents("p");
expect(invoke).toHaveBeenCalledWith("list_agents", { projectId: "p" });
await gw.readContext("p", "a");
invoke.mockResolvedValueOnce({ content: "# context" });
await expect(gw.readContext("p", "a")).resolves.toBe("# context");
expect(invoke).toHaveBeenCalledWith("read_agent_context", {
projectId: "p",
agentId: "a",

View File

@ -16,6 +16,7 @@ import { Channel, invoke } from "@tauri-apps/api/core";
import type {
Agent,
AgentContextDocument,
EffortSelection,
ResumableAgent,
TerminalSession,
@ -117,7 +118,10 @@ export class TauriAgentGateway implements AgentGateway {
}
readContext(projectId: string, agentId: string): Promise<string> {
return invoke<string>("read_agent_context", { projectId, agentId });
return invoke<AgentContextDocument>("read_agent_context", {
projectId,
agentId,
}).then((res) => res.content);
}
async updateContext(

View File

@ -8,7 +8,7 @@
import { describe, it, expect, vi } from "vitest";
import { HttpAgentGateway } from "./streamGateways";
import { HttpInvoker } from "./httpInvoker";
import { HttpInvoker, type FetchLike } from "./httpInvoker";
import { WsLiveClient, type WebSocketLike } from "./wsLiveClient";
import { bytesToBase64 } from "./frames";
@ -45,6 +45,27 @@ function gateway(): { gw: HttpAgentGateway; sockets: FakeSocket[] } {
return { gw, sockets };
}
function httpAgentGateway(
fetchImpl: FetchLike,
): { gw: HttpAgentGateway; calls: { url: string; init: unknown }[] } {
const calls: { url: string; init: unknown }[] = [];
const recordingFetch: FetchLike = async (url, init) => {
calls.push({ url, init });
return fetchImpl(url, init);
};
const ws = new WsLiveClient({
wsUrl: "wss://host",
socketFactory: () => new FakeSocket(),
});
return {
gw: new HttpAgentGateway(
new HttpInvoker({ baseUrl: "https://host", fetchImpl: recordingFetch }),
ws,
),
calls,
};
}
async function replyToLast(
socket: FakeSocket,
index: number,
@ -78,6 +99,26 @@ function attachedAck(
const OPTS = { cwd: "/srv/app", rows: 24, cols: 80, nodeId: "node-1" };
describe("HttpAgentGateway WS round-trip (B6 frames)", () => {
it("readContext unwraps the read_agent_context DTO content over HTTP", async () => {
const { gw, calls } = httpAgentGateway(async () => ({
ok: true,
status: 200,
json: async () => ({ content: "## Restored context" }),
text: async () => JSON.stringify({ content: "## Restored context" }),
}));
await expect(gw.readContext("proj-1", "agent-1")).resolves.toBe(
"## Restored context",
);
expect(calls).toHaveLength(1);
const init = calls[0].init as { body: string };
expect(JSON.parse(init.body)).toEqual({
command: "read_agent_context",
args: { projectId: "proj-1", agentId: "agent-1" },
});
});
it("launch → attached: conforming agent.launch frame, assignedConversationId consumed", async () => {
const { gw, sockets } = gateway();
const chunks: Uint8Array[] = [];

View File

@ -21,6 +21,7 @@
import type {
Agent,
AgentContextDocument,
AppExitWorkGuardState,
DomainEvent,
EffortSelection,
@ -234,7 +235,9 @@ export class HttpAgentGateway implements AgentGateway {
});
}
readContext(projectId: string, agentId: string): Promise<string> {
return this.http.invoke<string>("read_agent_context", { projectId, agentId });
return this.http
.invoke<AgentContextDocument>("read_agent_context", { projectId, agentId })
.then((res) => res.content);
}
async updateContext(projectId: string, agentId: string, content: string): Promise<void> {
await this.http.invoke("update_agent_context", { request: { projectId, agentId, content } });