@agentapplicationprotocol/sdk 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/client.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { AgentResponse, CreateSessionRequest, MetaResponse, SessionListResponse, SessionResponse, SessionTurnRequest, SSEEvent } from "./types";
1
+ import { AgentResponse, CreateSessionRequest, CreateSessionResponse, MetaResponse, SessionListResponse, SessionResponse, SessionTurnRequest, SSEEvent } from "./types";
2
2
  export interface ClientOptions {
3
3
  baseUrl: string;
4
4
  apiKey: string;
@@ -29,7 +29,7 @@ export declare class Client {
29
29
  /** PUT /session — non-streaming */
30
30
  createSession(req: CreateSessionRequest & {
31
31
  stream?: "none";
32
- }): Promise<AgentResponse>;
32
+ }): Promise<CreateSessionResponse>;
33
33
  /** PUT /session — SSE streaming */
34
34
  createSession(req: CreateSessionRequest & {
35
35
  stream: "delta" | "message";
package/dist/server.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Hono } from "hono";
2
2
  import type { Context } from "hono";
3
- import { AgentResponse, CreateSessionRequest, MetaResponse, SessionListResponse, SessionResponse, SessionTurnRequest, SSEEvent } from "./types";
3
+ import { AgentResponse, CreateSessionRequest, CreateSessionResponse, MetaResponse, SessionListResponse, SessionResponse, SessionTurnRequest, SSEEvent } from "./types";
4
4
  export interface ServerHandler {
5
5
  getMeta(): Promise<MetaResponse>;
6
6
  listSessions(params: {
@@ -8,7 +8,7 @@ export interface ServerHandler {
8
8
  }): Promise<SessionListResponse>;
9
9
  getSession(sessionId: string): Promise<SessionResponse>;
10
10
  /** The last message in `req.messages` is guaranteed to be a user message. */
11
- createSession(req: CreateSessionRequest): Promise<AgentResponse | AsyncIterable<SSEEvent>>;
11
+ createSession(req: CreateSessionRequest): Promise<CreateSessionResponse | AsyncIterable<SSEEvent>>;
12
12
  sendTurn(sessionId: string, req: SessionTurnRequest): Promise<AgentResponse | AsyncIterable<SSEEvent>>;
13
13
  deleteSession(sessionId: string): Promise<void>;
14
14
  }
package/dist/types.d.ts CHANGED
@@ -170,11 +170,12 @@ export interface SessionTurnRequest {
170
170
  }
171
171
  /** JSON response body for non-streaming (`stream: "none"`) requests. */
172
172
  export interface AgentResponse {
173
- /** Present in `PUT /session` response only. */
174
- sessionId?: string;
175
173
  stopReason: StopReason;
176
174
  messages: HistoryMessage[];
177
175
  }
176
+ export interface CreateSessionResponse extends AgentResponse {
177
+ sessionId: string;
178
+ }
178
179
  /** Response body for `GET /session/:id`. */
179
180
  export interface SessionResponse {
180
181
  sessionId: string;
package/package.json CHANGED
@@ -1,10 +1,12 @@
1
1
  {
2
2
  "name": "@agentapplicationprotocol/sdk",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "TypeScript SDK for the Agent Application Protocol (AAP)",
5
5
  "license": "Apache-2.0",
6
6
  "files": [
7
- "dist"
7
+ "dist",
8
+ "!dist/**/*.test.js",
9
+ "!dist/**/*.test.d.ts"
8
10
  ],
9
11
  "main": "dist/index.js",
10
12
  "types": "dist/index.d.ts",
@@ -1 +0,0 @@
1
- export {};
@@ -1,183 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const vitest_1 = require("vitest");
4
- const client_1 = require("./client");
5
- const BASE_URL = "https://example.com";
6
- const API_KEY = "test-key";
7
- function mockFetch(body, status = 200) {
8
- return vitest_1.vi.fn().mockResolvedValue({
9
- ok: status >= 200 && status < 300,
10
- status,
11
- statusText: "OK",
12
- json: () => Promise.resolve(body),
13
- text: () => Promise.resolve(String(body)),
14
- body: null,
15
- });
16
- }
17
- function mockSSEFetch(events, status = 200) {
18
- const chunks = events.map(({ event, ...data }) => new TextEncoder().encode(`event: ${event}\ndata: ${JSON.stringify(data)}\n\n`));
19
- let i = 0;
20
- const body = {
21
- getReader: () => ({
22
- read: async () => i < chunks.length ? { done: false, value: chunks[i++] } : { done: true, value: undefined },
23
- releaseLock: vitest_1.vi.fn(),
24
- }),
25
- };
26
- return vitest_1.vi
27
- .fn()
28
- .mockResolvedValue({
29
- ok: status >= 200 && status < 300,
30
- status,
31
- body,
32
- text: () => Promise.resolve(""),
33
- });
34
- }
35
- let client;
36
- (0, vitest_1.beforeEach)(() => {
37
- client = new client_1.Client({ baseUrl: BASE_URL, apiKey: API_KEY });
38
- });
39
- (0, vitest_1.describe)("Client", () => {
40
- (0, vitest_1.it)("strips trailing slash from baseUrl", () => {
41
- const c = new client_1.Client({ baseUrl: BASE_URL + "/", apiKey: API_KEY });
42
- const fetch = mockFetch({});
43
- vitest_1.vi.stubGlobal("fetch", fetch);
44
- c.getMeta();
45
- (0, vitest_1.expect)(fetch.mock.calls[0][0]).toBe(`${BASE_URL}/meta`);
46
- });
47
- (0, vitest_1.it)("sends Authorization header", async () => {
48
- const fetch = mockFetch({ version: 1, agents: [] });
49
- vitest_1.vi.stubGlobal("fetch", fetch);
50
- await client.getMeta();
51
- (0, vitest_1.expect)(fetch.mock.calls[0][1].headers["Authorization"]).toBe(`Bearer ${API_KEY}`);
52
- });
53
- (0, vitest_1.it)("getMeta: GET /meta", async () => {
54
- const meta = { version: 1, agents: [] };
55
- vitest_1.vi.stubGlobal("fetch", mockFetch(meta));
56
- (0, vitest_1.expect)(await client.getMeta()).toEqual(meta);
57
- });
58
- (0, vitest_1.it)("getSession: GET /session/:id", async () => {
59
- const session = { sessionId: "s1", agent: { name: "a" } };
60
- vitest_1.vi.stubGlobal("fetch", mockFetch(session));
61
- (0, vitest_1.expect)(await client.getSession("s1")).toEqual(session);
62
- });
63
- (0, vitest_1.it)("listSessions: GET /sessions without cursor", async () => {
64
- const res = { sessions: ["s1"] };
65
- const fetch = mockFetch(res);
66
- vitest_1.vi.stubGlobal("fetch", fetch);
67
- await client.listSessions();
68
- (0, vitest_1.expect)(fetch.mock.calls[0][0]).toBe(`${BASE_URL}/sessions`);
69
- });
70
- (0, vitest_1.it)("listSessions: appends after param", async () => {
71
- const fetch = mockFetch({ sessions: [] });
72
- vitest_1.vi.stubGlobal("fetch", fetch);
73
- await client.listSessions({ after: "cursor1" });
74
- (0, vitest_1.expect)(fetch.mock.calls[0][0]).toBe(`${BASE_URL}/sessions?after=cursor1`);
75
- });
76
- (0, vitest_1.it)("listAllSessions: paginates until no next", async () => {
77
- const fetch = vitest_1.vi
78
- .fn()
79
- .mockResolvedValueOnce({
80
- ok: true,
81
- status: 200,
82
- json: () => Promise.resolve({ sessions: ["s1"], next: "c1" }),
83
- })
84
- .mockResolvedValueOnce({
85
- ok: true,
86
- status: 200,
87
- json: () => Promise.resolve({ sessions: ["s2"] }),
88
- });
89
- vitest_1.vi.stubGlobal("fetch", fetch);
90
- (0, vitest_1.expect)(await client.listAllSessions()).toEqual(["s1", "s2"]);
91
- (0, vitest_1.expect)(fetch).toHaveBeenCalledTimes(2);
92
- });
93
- (0, vitest_1.it)("deleteSession: DELETE /session/:id returns void on 204", async () => {
94
- vitest_1.vi.stubGlobal("fetch", vitest_1.vi.fn().mockResolvedValue({ ok: true, status: 204 }));
95
- await (0, vitest_1.expect)(client.deleteSession("s1")).resolves.toBeUndefined();
96
- });
97
- (0, vitest_1.it)("createSession: throws if last message is not a user message", () => {
98
- vitest_1.vi.stubGlobal("fetch", mockFetch({}));
99
- (0, vitest_1.expect)(() => client.createSession({
100
- agent: { name: "a" },
101
- messages: [{ role: "assistant", content: "hi" }],
102
- })).toThrow("Last message must be a user message");
103
- });
104
- (0, vitest_1.it)("createSession: non-streaming returns AgentResponse", async () => {
105
- const res = { sessionId: "s1", stopReason: "end_turn", messages: [] };
106
- vitest_1.vi.stubGlobal("fetch", mockFetch(res, 201));
107
- const result = await client.createSession({
108
- agent: { name: "a" },
109
- messages: [{ role: "user", content: "hi" }],
110
- });
111
- (0, vitest_1.expect)(result).toEqual(res);
112
- });
113
- (0, vitest_1.it)("createSession: streaming returns SSE events", async () => {
114
- const events = [
115
- { event: "session_start", sessionId: "s1" },
116
- { event: "turn_start" },
117
- { event: "turn_stop", stopReason: "end_turn" },
118
- ];
119
- vitest_1.vi.stubGlobal("fetch", mockSSEFetch(events));
120
- const stream = await client.createSession({
121
- agent: { name: "a" },
122
- messages: [{ role: "user", content: "hi" }],
123
- stream: "message",
124
- });
125
- const received = [];
126
- for await (const e of stream)
127
- received.push(e);
128
- (0, vitest_1.expect)(received).toEqual(events);
129
- });
130
- (0, vitest_1.it)("sendTurn: non-streaming returns AgentResponse", async () => {
131
- const res = { stopReason: "end_turn", messages: [] };
132
- vitest_1.vi.stubGlobal("fetch", mockFetch(res));
133
- const result = await client.sendTurn("s1", { messages: [{ role: "user", content: "hi" }] });
134
- (0, vitest_1.expect)(result).toEqual(res);
135
- });
136
- (0, vitest_1.it)("sendTurn: streaming returns SSE events", async () => {
137
- const events = [
138
- { event: "turn_start" },
139
- { event: "text", text: "hello" },
140
- { event: "turn_stop", stopReason: "end_turn" },
141
- ];
142
- vitest_1.vi.stubGlobal("fetch", mockSSEFetch(events));
143
- const stream = await client.sendTurn("s1", {
144
- messages: [{ role: "user", content: "hi" }],
145
- stream: "message",
146
- });
147
- const received = [];
148
- for await (const e of stream)
149
- received.push(e);
150
- (0, vitest_1.expect)(received).toEqual(events);
151
- });
152
- (0, vitest_1.it)("streamRequest: throws ClientError on non-ok response", async () => {
153
- vitest_1.vi.stubGlobal("fetch", vitest_1.vi
154
- .fn()
155
- .mockResolvedValue({
156
- ok: false,
157
- status: 403,
158
- body: null,
159
- text: () => Promise.resolve("Forbidden"),
160
- }));
161
- await (0, vitest_1.expect)(client.createSession({
162
- agent: { name: "a" },
163
- messages: [{ role: "user", content: "hi" }],
164
- stream: "delta",
165
- })).rejects.toThrow(client_1.ClientError);
166
- });
167
- (0, vitest_1.it)("throws ClientError on non-ok response", async () => {
168
- vitest_1.vi.stubGlobal("fetch", vitest_1.vi
169
- .fn()
170
- .mockResolvedValue({ ok: false, status: 401, text: () => Promise.resolve("Unauthorized") }));
171
- await (0, vitest_1.expect)(client.getMeta()).rejects.toThrow(client_1.ClientError);
172
- });
173
- (0, vitest_1.it)("ClientError has correct properties", async () => {
174
- vitest_1.vi.stubGlobal("fetch", vitest_1.vi
175
- .fn()
176
- .mockResolvedValue({ ok: false, status: 404, text: () => Promise.resolve("Not Found") }));
177
- const err = await client.getSession("x").catch((e) => e);
178
- (0, vitest_1.expect)(err).toBeInstanceOf(client_1.ClientError);
179
- (0, vitest_1.expect)(err.status).toBe(404);
180
- (0, vitest_1.expect)(err.method).toBe("GET");
181
- (0, vitest_1.expect)(err.path).toBe("/session/x");
182
- });
183
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,124 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const vitest_1 = require("vitest");
4
- const server_1 = require("./server");
5
- const meta = { version: 1, agents: [] };
6
- const session = { sessionId: "s1", agent: { name: "a" } };
7
- const agentResponse = { stopReason: "end_turn", messages: [] };
8
- const sessionList = { sessions: ["s1"] };
9
- async function* sseEvents() {
10
- yield { event: "turn_start" };
11
- yield { event: "text", text: "hi" };
12
- yield { event: "turn_stop", stopReason: "end_turn" };
13
- }
14
- function makeHandler(overrides = {}) {
15
- return {
16
- getMeta: vitest_1.vi.fn().mockResolvedValue(meta),
17
- listSessions: vitest_1.vi.fn().mockResolvedValue(sessionList),
18
- getSession: vitest_1.vi.fn().mockResolvedValue(session),
19
- createSession: vitest_1.vi.fn().mockResolvedValue(agentResponse),
20
- sendTurn: vitest_1.vi.fn().mockResolvedValue(agentResponse),
21
- deleteSession: vitest_1.vi.fn().mockResolvedValue(undefined),
22
- ...overrides,
23
- };
24
- }
25
- function req(method, path, body, headers) {
26
- return new Request(`http://localhost${path}`, {
27
- method,
28
- headers: { "Content-Type": "application/json", ...headers },
29
- body: body !== undefined ? JSON.stringify(body) : undefined,
30
- });
31
- }
32
- (0, vitest_1.describe)("Server", () => {
33
- (0, vitest_1.it)("GET /meta returns meta", async () => {
34
- const server = new server_1.Server(makeHandler());
35
- const res = await server.app.fetch(req("GET", "/meta"));
36
- (0, vitest_1.expect)(res.status).toBe(200);
37
- (0, vitest_1.expect)(await res.json()).toEqual(meta);
38
- });
39
- (0, vitest_1.it)("GET /session/:id returns session", async () => {
40
- const server = new server_1.Server(makeHandler());
41
- const res = await server.app.fetch(req("GET", "/session/s1"));
42
- (0, vitest_1.expect)(res.status).toBe(200);
43
- (0, vitest_1.expect)(await res.json()).toEqual(session);
44
- });
45
- (0, vitest_1.it)("GET /sessions returns session list", async () => {
46
- const handler = makeHandler();
47
- const server = new server_1.Server(handler);
48
- const res = await server.app.fetch(req("GET", "/sessions?after=cursor1"));
49
- (0, vitest_1.expect)(res.status).toBe(200);
50
- (0, vitest_1.expect)(await res.json()).toEqual(sessionList);
51
- (0, vitest_1.expect)(handler.listSessions).toHaveBeenCalledWith({ after: "cursor1" });
52
- });
53
- (0, vitest_1.it)("PUT /session returns 400 if last message is not a user message", async () => {
54
- const server = new server_1.Server(makeHandler());
55
- const res = await server.app.fetch(req("PUT", "/session", {
56
- agent: { name: "a" },
57
- messages: [{ role: "assistant", content: "hi" }],
58
- }));
59
- (0, vitest_1.expect)(res.status).toBe(400);
60
- });
61
- (0, vitest_1.it)("PUT /session returns 201 with AgentResponse", async () => {
62
- const server = new server_1.Server(makeHandler());
63
- const res = await server.app.fetch(req("PUT", "/session", { agent: { name: "a" }, messages: [{ role: "user", content: "hi" }] }));
64
- (0, vitest_1.expect)(res.status).toBe(201);
65
- (0, vitest_1.expect)(await res.json()).toEqual(agentResponse);
66
- });
67
- (0, vitest_1.it)("PUT /session with stream returns SSE", async () => {
68
- const server = new server_1.Server(makeHandler({ createSession: vitest_1.vi.fn().mockResolvedValue(sseEvents()) }));
69
- const res = await server.app.fetch(req("PUT", "/session", {
70
- agent: { name: "a" },
71
- messages: [{ role: "user", content: "hi" }],
72
- stream: "message",
73
- }));
74
- (0, vitest_1.expect)(res.status).toBe(200);
75
- (0, vitest_1.expect)(res.headers.get("content-type")).toContain("text/event-stream");
76
- });
77
- (0, vitest_1.it)("POST /session/:id returns AgentResponse", async () => {
78
- const server = new server_1.Server(makeHandler());
79
- const res = await server.app.fetch(req("POST", "/session/s1", { messages: [{ role: "user", content: "hi" }] }));
80
- (0, vitest_1.expect)(res.status).toBe(200);
81
- (0, vitest_1.expect)(await res.json()).toEqual(agentResponse);
82
- });
83
- (0, vitest_1.it)("POST /session/:id with stream returns SSE", async () => {
84
- const server = new server_1.Server(makeHandler({ sendTurn: vitest_1.vi.fn().mockResolvedValue(sseEvents()) }));
85
- const res = await server.app.fetch(req("POST", "/session/s1", { messages: [{ role: "user", content: "hi" }], stream: "delta" }));
86
- (0, vitest_1.expect)(res.status).toBe(200);
87
- (0, vitest_1.expect)(res.headers.get("content-type")).toContain("text/event-stream");
88
- });
89
- (0, vitest_1.it)("DELETE /session/:id returns 204", async () => {
90
- const server = new server_1.Server(makeHandler());
91
- const res = await server.app.fetch(req("DELETE", "/session/s1"));
92
- (0, vitest_1.expect)(res.status).toBe(204);
93
- });
94
- (0, vitest_1.it)("authenticate: returns 401 when rejected", async () => {
95
- const server = new server_1.Server(makeHandler(), { authenticate: () => false });
96
- const res = await server.app.fetch(req("GET", "/meta"));
97
- (0, vitest_1.expect)(res.status).toBe(401);
98
- });
99
- (0, vitest_1.it)("authenticate: passes apiKey and context", async () => {
100
- const authenticate = vitest_1.vi.fn().mockReturnValue(true);
101
- const server = new server_1.Server(makeHandler(), { authenticate });
102
- await server.app.fetch(req("GET", "/meta", undefined, { Authorization: "Bearer mykey" }));
103
- (0, vitest_1.expect)(authenticate).toHaveBeenCalledWith("mykey", vitest_1.expect.objectContaining({ req: vitest_1.expect.anything() }));
104
- });
105
- (0, vitest_1.it)("authenticate: allows per-route logic", async () => {
106
- const server = new server_1.Server(makeHandler(), {
107
- authenticate: (_, c) => c.req.path === "/meta",
108
- });
109
- const metaRes = await server.app.fetch(req("GET", "/meta"));
110
- (0, vitest_1.expect)(metaRes.status).toBe(200);
111
- const sessRes = await server.app.fetch(req("GET", "/session/s1"));
112
- (0, vitest_1.expect)(sessRes.status).toBe(401);
113
- });
114
- (0, vitest_1.it)("base path prefixes all routes", async () => {
115
- const server = new server_1.Server(makeHandler(), { base: "/api/v1" });
116
- const res = await server.app.fetch(req("GET", "/api/v1/meta"));
117
- (0, vitest_1.expect)(res.status).toBe(200);
118
- });
119
- (0, vitest_1.it)("cors: sets Access-Control-Allow-Origin header", async () => {
120
- const server = new server_1.Server(makeHandler(), { cors: "https://example.com" });
121
- const res = await server.app.fetch(req("GET", "/meta", undefined, { Origin: "https://example.com" }));
122
- (0, vitest_1.expect)(res.headers.get("access-control-allow-origin")).toBe("https://example.com");
123
- });
124
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,129 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const vitest_1 = require("vitest");
4
- const utils_1 = require("./utils");
5
- (0, vitest_1.describe)("sseEventsToMessages", () => {
6
- (0, vitest_1.it)("converts text event to assistant message", () => {
7
- const events = [
8
- { event: "turn_start" },
9
- { event: "text", text: "hello" },
10
- { event: "turn_stop", stopReason: "end_turn" },
11
- ];
12
- (0, vitest_1.expect)((0, utils_1.sseEventsToMessages)(events)).toEqual([
13
- { role: "assistant", content: [{ type: "text", text: "hello" }] },
14
- ]);
15
- });
16
- (0, vitest_1.it)("accumulates text_delta into text block", () => {
17
- const events = [
18
- { event: "text_delta", delta: "hel" },
19
- { event: "text_delta", delta: "lo" },
20
- { event: "turn_stop", stopReason: "end_turn" },
21
- ];
22
- (0, vitest_1.expect)((0, utils_1.sseEventsToMessages)(events)).toEqual([
23
- { role: "assistant", content: [{ type: "text", text: "hello" }] },
24
- ]);
25
- });
26
- (0, vitest_1.it)("accumulates thinking_delta into thinking block", () => {
27
- const events = [
28
- { event: "thinking_delta", delta: "thin" },
29
- { event: "thinking_delta", delta: "king" },
30
- { event: "turn_stop", stopReason: "end_turn" },
31
- ];
32
- (0, vitest_1.expect)((0, utils_1.sseEventsToMessages)(events)).toEqual([
33
- { role: "assistant", content: [{ type: "thinking", thinking: "thinking" }] },
34
- ]);
35
- });
36
- (0, vitest_1.it)("converts thinking event to thinking block", () => {
37
- const events = [
38
- { event: "thinking", thinking: "deep thought" },
39
- { event: "turn_stop", stopReason: "end_turn" },
40
- ];
41
- (0, vitest_1.expect)((0, utils_1.sseEventsToMessages)(events)).toEqual([
42
- { role: "assistant", content: [{ type: "thinking", thinking: "deep thought" }] },
43
- ]);
44
- });
45
- (0, vitest_1.it)("flushes assistant message before tool_result", () => {
46
- const events = [
47
- { event: "tool_call", toolCallId: "c1", name: "search", input: { q: "x" } },
48
- { event: "tool_result", toolCallId: "c1", content: "result" },
49
- { event: "turn_stop", stopReason: "end_turn" },
50
- ];
51
- const messages = (0, utils_1.sseEventsToMessages)(events);
52
- (0, vitest_1.expect)(messages).toContainEqual({
53
- role: "assistant",
54
- content: [{ type: "tool_use", toolCallId: "c1", name: "search", input: { q: "x" } }],
55
- });
56
- (0, vitest_1.expect)(messages).toContainEqual({ role: "tool", toolCallId: "c1", content: "result" });
57
- });
58
- (0, vitest_1.it)("flushes delta accumulators when non-delta event arrives", () => {
59
- const events = [
60
- { event: "text_delta", delta: "hi" },
61
- { event: "tool_call", toolCallId: "c1", name: "fn", input: {} },
62
- { event: "turn_stop", stopReason: "end_turn" },
63
- ];
64
- const messages = (0, utils_1.sseEventsToMessages)(events);
65
- const assistant = messages.find((m) => m.role === "assistant");
66
- (0, vitest_1.expect)(Array.isArray(assistant?.content) && assistant.content[0]).toEqual({
67
- type: "text",
68
- text: "hi",
69
- });
70
- });
71
- (0, vitest_1.it)("returns empty array for events with no content", () => {
72
- const events = [
73
- { event: "turn_start" },
74
- { event: "turn_stop", stopReason: "end_turn" },
75
- ];
76
- (0, vitest_1.expect)((0, utils_1.sseEventsToMessages)(events)).toEqual([]);
77
- });
78
- });
79
- (0, vitest_1.describe)("resolvePendingToolUse", () => {
80
- const clientTool = {
81
- name: "client_tool",
82
- description: "d",
83
- inputSchema: { type: "object" },
84
- };
85
- (0, vitest_1.it)("classifies client and server tools", () => {
86
- const messages = [
87
- {
88
- role: "assistant",
89
- content: [
90
- { type: "tool_use", toolCallId: "c1", name: "client_tool", input: {} },
91
- { type: "tool_use", toolCallId: "s1", name: "server_tool", input: {} },
92
- ],
93
- },
94
- ];
95
- const { client, server } = (0, utils_1.resolvePendingToolUse)(messages, [clientTool]);
96
- (0, vitest_1.expect)(client).toEqual([{ toolCallId: "c1", name: "client_tool", input: {} }]);
97
- (0, vitest_1.expect)(server).toEqual([{ toolCallId: "s1", name: "server_tool", input: {} }]);
98
- });
99
- (0, vitest_1.it)("skips tool_use blocks already resolved by tool_result", () => {
100
- const messages = [
101
- {
102
- role: "assistant",
103
- content: [{ type: "tool_use", toolCallId: "c1", name: "client_tool", input: {} }],
104
- },
105
- { role: "tool", toolCallId: "c1", content: "done" },
106
- ];
107
- (0, vitest_1.expect)((0, utils_1.resolvePendingToolUse)(messages, [clientTool])).toEqual({ client: [], server: [] });
108
- });
109
- (0, vitest_1.it)("returns empty if last assistant message has string content", () => {
110
- const messages = [{ role: "assistant", content: "plain" }];
111
- (0, vitest_1.expect)((0, utils_1.resolvePendingToolUse)(messages)).toEqual({ client: [], server: [] });
112
- });
113
- (0, vitest_1.it)("returns empty if no assistant message in history", () => {
114
- const messages = [{ role: "user", content: "hi" }];
115
- (0, vitest_1.expect)((0, utils_1.resolvePendingToolUse)(messages)).toEqual({ client: [], server: [] });
116
- });
117
- (0, vitest_1.it)("finds last assistant message even if not the last message", () => {
118
- const messages = [
119
- {
120
- role: "assistant",
121
- content: [{ type: "tool_use", toolCallId: "c1", name: "client_tool", input: {} }],
122
- },
123
- { role: "tool", toolCallId: "c1", content: "done" },
124
- { role: "user", content: "thanks" },
125
- ];
126
- // last message is user, but last assistant has resolved tool — should return empty
127
- (0, vitest_1.expect)((0, utils_1.resolvePendingToolUse)(messages, [clientTool])).toEqual({ client: [], server: [] });
128
- });
129
- });