@agentapplicationprotocol/server 0.6.1 → 0.7.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/src/agent.js CHANGED
@@ -62,7 +62,7 @@ class Agent {
62
62
  name,
63
63
  title: options.title,
64
64
  description: options.description ?? "",
65
- inputSchema: zod_1.default.toJSONSchema(options.inputSchema),
65
+ parameters: zod_1.default.toJSONSchema(options.inputSchema),
66
66
  });
67
67
  this.tools.set(name, async (input) => {
68
68
  const output = await exec(options.inputSchema.parse(JSON.parse(input)));
@@ -64,9 +64,9 @@ const handler = {
64
64
  apiKey: req.agent.options?.apiKey || undefined,
65
65
  });
66
66
  const model = new model_js_1.AiModelProvider(openai.chat(req.agent.options?.model ?? "gpt-4o"));
67
- const session = new session_js_1.Session(sessionId, agent, model, req.agent, req.tools);
67
+ const session = new session_js_1.Session(sessionId, agent, model, req.agent, req.tools ?? [], req.messages ?? []);
68
68
  sessions.set(sessionId, session);
69
- return session.runNewSession(req);
69
+ return Promise.resolve({ sessionId });
70
70
  },
71
71
  sendTurn(sessionId, req) {
72
72
  const session = sessions.get(sessionId);
@@ -62,9 +62,9 @@ const handler = {
62
62
  apiKey: req.agent.options?.apiKey || undefined,
63
63
  });
64
64
  const model = new model_js_1.AiModelProvider(openai.chat(req.agent.options?.model ?? "gpt-4o"));
65
- const session = new session_js_1.TruncatedHistorySession(sessionId, agent, model, req.agent, req.tools);
65
+ const session = new session_js_1.TruncatedHistorySession(sessionId, agent, model, req.agent, req.tools ?? [], req.messages ?? []);
66
66
  session_js_1.sessions.set(sessionId, session);
67
- return session.runNewSession(req);
67
+ return Promise.resolve({ sessionId });
68
68
  },
69
69
  sendTurn(sessionId, req) {
70
70
  const session = session_js_1.sessions.get(sessionId);
package/dist/src/model.js CHANGED
@@ -168,7 +168,7 @@ function toToolSet(tools) {
168
168
  for (const t of tools) {
169
169
  res[t.name] = (0, ai_1.tool)({
170
170
  description: t.description,
171
- inputSchema: (0, ai_1.jsonSchema)(t.inputSchema),
171
+ inputSchema: (0, ai_1.jsonSchema)(t.parameters),
172
172
  });
173
173
  }
174
174
  return res;
@@ -7,8 +7,7 @@ export interface Handler {
7
7
  }): Promise<SessionListResponse>;
8
8
  getSession(sessionId: string): Promise<SessionResponse>;
9
9
  getSessionHistory(sessionId: string, type: "compacted" | "full"): Promise<HistoryMessage[]>;
10
- /** The last message in `req.messages` is guaranteed to be a user message. */
11
- createSession(req: CreateSessionRequest): Promise<CreateSessionResponse> | AsyncIterable<SSEEvent>;
10
+ createSession(req: CreateSessionRequest): Promise<CreateSessionResponse>;
12
11
  sendTurn(sessionId: string, req: SessionTurnRequest): Promise<AgentResponse> | AsyncIterable<SSEEvent>;
13
12
  deleteSession(sessionId: string): Promise<void>;
14
13
  }
@@ -41,18 +41,13 @@ function redactSecretOptions(session, agents) {
41
41
  */
42
42
  function aap(handler) {
43
43
  const router = new hono_1.Hono();
44
- router.get("/meta", (c) => c.json({ version: 2, ...handler.getMeta() }));
45
- router.put("/session", async (c) => {
44
+ router.get("/meta", (c) => c.json({ version: 3, ...handler.getMeta() }));
45
+ router.post("/sessions", async (c) => {
46
46
  const req = await c.req.json();
47
- if (req.messages.at(-1)?.role !== "user")
48
- return c.json({ error: "Last message must be a user message" }, 400);
49
47
  const result = await handler.createSession(req);
50
- if (req.stream === "delta" || req.stream === "message") {
51
- return (0, streaming_1.streamSSE)(c, (stream) => writeSSEEvents(stream, result));
52
- }
53
48
  return c.json(result, 201);
54
49
  });
55
- router.post("/session/:id", async (c) => {
50
+ router.post("/sessions/:id/turns", async (c) => {
56
51
  const req = await c.req.json();
57
52
  const result = await handler.sendTurn(c.req.param("id"), req);
58
53
  if (req.stream === "delta" || req.stream === "message") {
@@ -60,12 +55,12 @@ function aap(handler) {
60
55
  }
61
56
  return c.json(result);
62
57
  });
63
- router.get("/session/:id", async (c) => {
58
+ router.get("/sessions/:id", async (c) => {
64
59
  const session = await handler.getSession(c.req.param("id"));
65
60
  const { agents } = handler.getMeta();
66
61
  return c.json(redactSecretOptions(session, agents));
67
62
  });
68
- router.get("/session/:id/history", async (c) => {
63
+ router.get("/sessions/:id/history", async (c) => {
69
64
  const typeParam = c.req.query("type");
70
65
  if (typeParam !== "compacted" && typeParam !== "full")
71
66
  return c.json({ error: 'type must be "compacted" or "full"' }, 400);
@@ -81,7 +76,7 @@ function aap(handler) {
81
76
  sessions: result.sessions.map((s) => redactSecretOptions(s, agents)),
82
77
  });
83
78
  });
84
- router.delete("/session/:id", async (c) => {
79
+ router.delete("/sessions/:id", async (c) => {
85
80
  await handler.deleteSession(c.req.param("id"));
86
81
  return new Response(null, { status: 204 });
87
82
  });
@@ -5,10 +5,10 @@ const hono_1 = require("hono");
5
5
  const bearer_auth_1 = require("hono/bearer-auth");
6
6
  const cors_1 = require("hono/cors");
7
7
  const server_1 = require("./server");
8
- const meta = { version: 2, agents: [] };
8
+ const meta = { version: 3, agents: [] };
9
9
  const session = { sessionId: "s1", agent: { name: "a" } };
10
10
  const agentResponse = { stopReason: "end_turn", messages: [] };
11
- const createSessionResponse = { ...agentResponse, sessionId: "s1" };
11
+ const createSessionResponse = { sessionId: "s1" };
12
12
  const sessionList = { sessions: [session] };
13
13
  async function* sseEvents() {
14
14
  yield { event: "turn_start" };
@@ -49,7 +49,7 @@ function req(method, path, body, headers) {
49
49
  });
50
50
  (0, vitest_1.it)("GET /session/:id returns session", async () => {
51
51
  const app = makeApp(makeHandler());
52
- const res = await app.fetch(req("GET", "/session/s1"));
52
+ const res = await app.fetch(req("GET", "/sessions/s1"));
53
53
  (0, vitest_1.expect)(res.status).toBe(200);
54
54
  (0, vitest_1.expect)(await res.json()).toEqual(session);
55
55
  });
@@ -73,7 +73,7 @@ function req(method, path, body, headers) {
73
73
  ],
74
74
  }),
75
75
  }));
76
- const res = await app.fetch(req("GET", "/session/s1"));
76
+ const res = await app.fetch(req("GET", "/sessions/s1"));
77
77
  (0, vitest_1.expect)(res.status).toBe(200);
78
78
  (0, vitest_1.expect)(await res.json()).toEqual({
79
79
  ...secretSession,
@@ -119,45 +119,30 @@ function req(method, path, body, headers) {
119
119
  ],
120
120
  });
121
121
  });
122
- (0, vitest_1.it)("PUT /session returns 400 if last message is not a user message", async () => {
122
+ (0, vitest_1.it)("POST /sessions returns 201 with sessionId", async () => {
123
123
  const app = makeApp(makeHandler());
124
- const res = await app.fetch(req("PUT", "/session", {
125
- agent: { name: "a" },
126
- messages: [{ role: "assistant", content: "hi" }],
127
- }));
128
- (0, vitest_1.expect)(res.status).toBe(400);
129
- });
130
- (0, vitest_1.it)("PUT /session returns 201 with AgentResponse", async () => {
131
- const app = makeApp(makeHandler());
132
- const res = await app.fetch(req("PUT", "/session", { agent: { name: "a" }, messages: [{ role: "user", content: "hi" }] }));
124
+ const res = await app.fetch(req("POST", "/sessions", { agent: { name: "a" } }));
133
125
  (0, vitest_1.expect)(res.status).toBe(201);
134
126
  (0, vitest_1.expect)(await res.json()).toEqual(createSessionResponse);
135
127
  });
136
- (0, vitest_1.it)("PUT /session with stream returns SSE", async () => {
137
- const app = makeApp(makeHandler({ createSession: vitest_1.vi.fn().mockResolvedValue(sseEvents()) }));
138
- const res = await app.fetch(req("PUT", "/session", {
139
- agent: { name: "a" },
140
- messages: [{ role: "user", content: "hi" }],
141
- stream: "message",
142
- }));
143
- (0, vitest_1.expect)(res.status).toBe(200);
144
- (0, vitest_1.expect)(res.headers.get("content-type")).toContain("text/event-stream");
145
- });
146
- (0, vitest_1.it)("POST /session/:id returns AgentResponse", async () => {
128
+ (0, vitest_1.it)("POST /sessions/:id/turns returns AgentResponse", async () => {
147
129
  const app = makeApp(makeHandler());
148
- const res = await app.fetch(req("POST", "/session/s1", { messages: [{ role: "user", content: "hi" }] }));
130
+ const res = await app.fetch(req("POST", "/sessions/s1/turns", { messages: [{ role: "user", content: "hi" }] }));
149
131
  (0, vitest_1.expect)(res.status).toBe(200);
150
132
  (0, vitest_1.expect)(await res.json()).toEqual(agentResponse);
151
133
  });
152
- (0, vitest_1.it)("POST /session/:id with stream returns SSE", async () => {
134
+ (0, vitest_1.it)("POST /sessions/:id/turns with stream returns SSE", async () => {
153
135
  const app = makeApp(makeHandler({ sendTurn: vitest_1.vi.fn().mockResolvedValue(sseEvents()) }));
154
- const res = await app.fetch(req("POST", "/session/s1", { messages: [{ role: "user", content: "hi" }], stream: "delta" }));
136
+ const res = await app.fetch(req("POST", "/sessions/s1/turns", {
137
+ messages: [{ role: "user", content: "hi" }],
138
+ stream: "delta",
139
+ }));
155
140
  (0, vitest_1.expect)(res.status).toBe(200);
156
141
  (0, vitest_1.expect)(res.headers.get("content-type")).toContain("text/event-stream");
157
142
  });
158
143
  (0, vitest_1.it)("DELETE /session/:id returns 204", async () => {
159
144
  const app = makeApp(makeHandler());
160
- const res = await app.fetch(req("DELETE", "/session/s1"));
145
+ const res = await app.fetch(req("DELETE", "/sessions/s1"));
161
146
  (0, vitest_1.expect)(res.status).toBe(204);
162
147
  });
163
148
  (0, vitest_1.it)("auth middleware: returns 401 when rejected", async () => {
@@ -190,7 +175,7 @@ function req(method, path, body, headers) {
190
175
  getSession: vitest_1.vi.fn().mockResolvedValue(sessionWithOptions),
191
176
  getMeta: vitest_1.vi.fn().mockReturnValue({ agents: [] }),
192
177
  }));
193
- const res = await app.fetch(req("GET", "/session/s1"));
178
+ const res = await app.fetch(req("GET", "/sessions/s1"));
194
179
  (0, vitest_1.expect)(await res.json()).toEqual(sessionWithOptions);
195
180
  });
196
181
  (0, vitest_1.it)("GET /session/:id does not redact when agent has no secret options", async () => {
@@ -210,14 +195,14 @@ function req(method, path, body, headers) {
210
195
  ],
211
196
  }),
212
197
  }));
213
- const res = await app.fetch(req("GET", "/session/s1"));
198
+ const res = await app.fetch(req("GET", "/sessions/s1"));
214
199
  (0, vitest_1.expect)(await res.json()).toEqual(sessionWithOptions);
215
200
  });
216
201
  (0, vitest_1.it)("GET /session/:id/history?type=compacted calls getSessionHistory", async () => {
217
202
  const messages = [{ role: "user", content: "hi" }];
218
203
  const handler = makeHandler({ getSessionHistory: vitest_1.vi.fn().mockResolvedValue(messages) });
219
204
  const app = makeApp(handler);
220
- const res = await app.fetch(req("GET", "/session/s1/history?type=compacted"));
205
+ const res = await app.fetch(req("GET", "/sessions/s1/history?type=compacted"));
221
206
  (0, vitest_1.expect)(res.status).toBe(200);
222
207
  (0, vitest_1.expect)(handler.getSessionHistory).toHaveBeenCalledWith("s1", "compacted");
223
208
  (0, vitest_1.expect)(await res.json()).toEqual({ history: { compacted: messages } });
@@ -226,14 +211,14 @@ function req(method, path, body, headers) {
226
211
  const messages = [{ role: "user", content: "hi" }];
227
212
  const handler = makeHandler({ getSessionHistory: vitest_1.vi.fn().mockResolvedValue(messages) });
228
213
  const app = makeApp(handler);
229
- const res = await app.fetch(req("GET", "/session/s1/history?type=full"));
214
+ const res = await app.fetch(req("GET", "/sessions/s1/history?type=full"));
230
215
  (0, vitest_1.expect)(res.status).toBe(200);
231
216
  (0, vitest_1.expect)(handler.getSessionHistory).toHaveBeenCalledWith("s1", "full");
232
217
  (0, vitest_1.expect)(await res.json()).toEqual({ history: { full: messages } });
233
218
  });
234
219
  (0, vitest_1.it)("GET /session/:id/history without valid ?type returns 400", async () => {
235
220
  const app = makeApp(makeHandler());
236
- const res = await app.fetch(req("GET", "/session/s1/history"));
221
+ const res = await app.fetch(req("GET", "/sessions/s1/history"));
237
222
  (0, vitest_1.expect)(res.status).toBe(400);
238
223
  });
239
224
  });
@@ -1,4 +1,4 @@
1
- import { HistoryMessage, ToolPermissionMessage, AgentConfig, ToolSpec, SSEEvent, AgentResponse, CreateSessionRequest, CreateSessionResponse, SessionTurnRequest, SessionResponse, DeltaSSEEvent } from "@agentapplicationprotocol/core";
1
+ import { HistoryMessage, ToolPermissionMessage, AgentConfig, ToolSpec, SSEEvent, AgentResponse, SessionTurnRequest, SessionResponse, DeltaSSEEvent } from "@agentapplicationprotocol/core";
2
2
  import { ModelProvider } from "./model";
3
3
  import { Agent } from "./agent";
4
4
  /** Messages accepted as input to a turn: regular history messages or client-side tool permission responses. */
@@ -8,12 +8,12 @@ export declare class Session {
8
8
  sessionId: string;
9
9
  agent: Agent;
10
10
  agentConfig: AgentConfig;
11
- /** Application-side tools declared for this session. */
11
+ /** Client-side tools declared for this session. */
12
12
  clientTools: ToolSpec[];
13
13
  /** Accumulated conversation history across all turns. */
14
14
  history: HistoryMessage[];
15
15
  model: ModelProvider;
16
- constructor(sessionId: string, agent: Agent, model: ModelProvider, agentConfig: AgentConfig, clientTools?: ToolSpec[]);
16
+ constructor(sessionId: string, agent: Agent, model: ModelProvider, agentConfig: AgentConfig, clientTools: ToolSpec[], history: HistoryMessage[]);
17
17
  /** Resolves tool_permission messages into tool result messages by executing granted tools. */
18
18
  private resolvePermissions;
19
19
  /** Returns the set of trusted server tool names from agentConfig. */
@@ -54,8 +54,6 @@ export declare class Session {
54
54
  * Loops the LLM call as long as all tool calls are trusted and executed inline.
55
55
  */
56
56
  private runTurnNone;
57
- /** Runs the first turn and wraps the result with `sessionId` for a `createSession` response. */
58
- runNewSession(req: CreateSessionRequest): Promise<CreateSessionResponse> | AsyncIterable<SSEEvent>;
59
57
  /** Applies config overrides from the request that persist for the session lifetime: client tools, enabled/trusted agent tools, and agent options. */
60
58
  private applySessionOverrides;
61
59
  /** Routes to the appropriate turn runner based on the requested stream mode. */
@@ -4,13 +4,13 @@ exports.Session = void 0;
4
4
  const core_1 = require("@agentapplicationprotocol/core");
5
5
  /** Manages a stateful conversation session, accumulating history across turns. */
6
6
  class Session {
7
- constructor(sessionId, agent, model, agentConfig, clientTools = []) {
7
+ constructor(sessionId, agent, model, agentConfig, clientTools, history) {
8
8
  this.sessionId = sessionId;
9
9
  this.agent = agent;
10
10
  this.model = model;
11
11
  this.agentConfig = agentConfig;
12
12
  this.clientTools = clientTools;
13
- this.history = [];
13
+ this.history = history;
14
14
  }
15
15
  /** Resolves tool_permission messages into tool result messages by executing granted tools. */
16
16
  async resolvePermissions(messages) {
@@ -210,18 +210,6 @@ class Session {
210
210
  newMessages.push(...next);
211
211
  }
212
212
  }
213
- /** Runs the first turn and wraps the result with `sessionId` for a `createSession` response. */
214
- runNewSession(req) {
215
- const sessionId = this.sessionId;
216
- const result = this.dispatchTurn(req.stream, req.messages);
217
- if (result instanceof Promise) {
218
- return result.then((res) => ({ ...res, sessionId }));
219
- }
220
- return (async function* () {
221
- yield { event: "session_start", sessionId };
222
- yield* result;
223
- })();
224
- }
225
213
  /** Applies config overrides from the request that persist for the session lifetime: client tools, enabled/trusted agent tools, and agent options. */
226
214
  applySessionOverrides(req) {
227
215
  // Override client-provided tools
@@ -26,7 +26,7 @@ function makeAgent() {
26
26
  return agent;
27
27
  }
28
28
  function makeSession(agentConfig = { name: "test-agent" }, model = makeModel(), agent = makeAgent()) {
29
- return new session_1.Session("sess-1", agent, model, agentConfig);
29
+ return new session_1.Session("sess-1", agent, model, agentConfig, [], []);
30
30
  }
31
31
  const userMsg = { role: "user", content: "hello" };
32
32
  (0, vitest_1.describe)("Session", () => {
@@ -45,7 +45,7 @@ const userMsg = { role: "user", content: "hello" };
45
45
  });
46
46
  (0, vitest_1.it)("toSessionResponse includes tools when clientTools is set", () => {
47
47
  const s = makeSession();
48
- s.clientTools = [{ name: "t", description: "", inputSchema: {} }];
48
+ s.clientTools = [{ name: "t", description: "", parameters: {} }];
49
49
  (0, vitest_1.expect)(s.toSessionResponse().tools).toHaveLength(1);
50
50
  });
51
51
  (0, vitest_1.describe)("runTurn (none mode)", () => {
@@ -84,7 +84,7 @@ const userMsg = { role: "user", content: "hello" };
84
84
  messages: [{ role: "assistant", content: "done" }],
85
85
  }),
86
86
  });
87
- const s = new session_1.Session("s", agent, model, agentConfig);
87
+ const s = new session_1.Session("s", agent, model, agentConfig, [], []);
88
88
  const res = await s.runTurn({ messages: [userMsg] });
89
89
  (0, vitest_1.expect)(res.stopReason).toBe("end_turn");
90
90
  (0, vitest_1.expect)(model.call).toHaveBeenCalledTimes(2);
@@ -106,7 +106,7 @@ const userMsg = { role: "user", content: "hello" };
106
106
  ],
107
107
  }),
108
108
  });
109
- const s = new session_1.Session("s", agent, model, agentConfig);
109
+ const s = new session_1.Session("s", agent, model, agentConfig, [], []);
110
110
  const res = (await s.runTurn({ messages: [userMsg] }));
111
111
  (0, vitest_1.expect)(res.stopReason).toBe("tool_use");
112
112
  (0, vitest_1.expect)(model.call).toHaveBeenCalledTimes(1);
@@ -145,7 +145,7 @@ const userMsg = { role: "user", content: "hello" };
145
145
  yield { event: "turn_stop", stopReason: "end_turn" };
146
146
  }),
147
147
  });
148
- const s = new session_1.Session("s", agent, model, agentConfig);
148
+ const s = new session_1.Session("s", agent, model, agentConfig, [], []);
149
149
  const events = [];
150
150
  for await (const e of s.runTurn({
151
151
  messages: [userMsg],
@@ -210,7 +210,7 @@ const userMsg = { role: "user", content: "hello" };
210
210
  messages: [{ role: "assistant", content: "done" }],
211
211
  }),
212
212
  });
213
- const s = new session_1.Session("s", agent, model, agentConfig);
213
+ const s = new session_1.Session("s", agent, model, agentConfig, [], []);
214
214
  const events = [];
215
215
  for await (const e of s.runTurn({
216
216
  messages: [userMsg],
@@ -228,7 +228,7 @@ const userMsg = { role: "user", content: "hello" };
228
228
  const agentConfig = { name: "test-agent" };
229
229
  // Seed history with an assistant message containing a tool_use
230
230
  const model = makeModel();
231
- const s = new session_1.Session("s", agent, model, agentConfig);
231
+ const s = new session_1.Session("s", agent, model, agentConfig, [], []);
232
232
  s.history = [
233
233
  { role: "user", content: "hi" },
234
234
  {
@@ -248,7 +248,7 @@ const userMsg = { role: "user", content: "hello" };
248
248
  (0, vitest_1.it)("resolves denied tool_permission with denial message", async () => {
249
249
  const agent = makeAgent();
250
250
  const model = makeModel();
251
- const s = new session_1.Session("s", agent, model, { name: "test-agent" });
251
+ const s = new session_1.Session("s", agent, model, { name: "test-agent" }, [], []);
252
252
  s.history = [
253
253
  {
254
254
  role: "assistant",
@@ -266,7 +266,7 @@ const userMsg = { role: "user", content: "hello" };
266
266
  (0, vitest_1.describe)("applySessionOverrides via runTurn", () => {
267
267
  (0, vitest_1.it)("overrides clientTools when req.tools is provided", async () => {
268
268
  const s = makeSession();
269
- const newTool = { name: "x", description: "", inputSchema: {} };
269
+ const newTool = { name: "x", description: "", parameters: {} };
270
270
  await s.runTurn({ messages: [userMsg], tools: [newTool] });
271
271
  (0, vitest_1.expect)(s.clientTools).toEqual([newTool]);
272
272
  });
@@ -281,23 +281,90 @@ const userMsg = { role: "user", content: "hello" };
281
281
  (0, vitest_1.expect)(s.agentConfig.options).toEqual({ model: "gpt-5" });
282
282
  });
283
283
  });
284
- (0, vitest_1.describe)("runNewSession", () => {
285
- (0, vitest_1.it)("returns CreateSessionResponse with sessionId (none mode)", async () => {
286
- const s = makeSession();
287
- const res = await s.runNewSession({ agent: { name: "test-agent" }, messages: [userMsg] });
288
- (0, vitest_1.expect)(res.sessionId).toBe("sess-1");
289
- });
290
- (0, vitest_1.it)("yields session_start event first (delta mode)", async () => {
291
- const s = makeSession();
292
- const result = s.runNewSession({
293
- agent: { name: "test-agent" },
284
+ (0, vitest_1.describe)("runTurn (delta mode) — untrusted tool stops loop", () => {
285
+ (0, vitest_1.it)("stops at untrusted tool in delta mode", async () => {
286
+ const agent = makeAgent();
287
+ const agentConfig = { name: "test-agent", tools: [] }; // echo not trusted
288
+ const model = makeModel({
289
+ stream: vitest_1.vi.fn().mockImplementationOnce(async function* () {
290
+ yield {
291
+ event: "tool_call",
292
+ toolCallId: "c1",
293
+ name: "echo",
294
+ input: { msg: "hi" },
295
+ };
296
+ yield { event: "turn_stop", stopReason: "tool_use" };
297
+ }),
298
+ });
299
+ const s = new session_1.Session("s", agent, model, agentConfig, [], []);
300
+ const events = [];
301
+ for await (const e of s.runTurn({
294
302
  messages: [userMsg],
295
303
  stream: "delta",
304
+ }))
305
+ events.push(e);
306
+ (0, vitest_1.expect)(events.at(-1).event).toBe("turn_stop");
307
+ (0, vitest_1.expect)(model.stream).toHaveBeenCalledTimes(1);
308
+ });
309
+ });
310
+ (0, vitest_1.describe)("runTurn (message mode) — tool_use block and untrusted stop", () => {
311
+ (0, vitest_1.it)("yields tool_call event for tool_use blocks in message mode", async () => {
312
+ const model = makeModel({
313
+ call: vitest_1.vi.fn().mockResolvedValue({
314
+ stopReason: "tool_use",
315
+ messages: [
316
+ {
317
+ role: "assistant",
318
+ content: [{ type: "tool_use", toolCallId: "c1", name: "echo", input: { msg: "hi" } }],
319
+ },
320
+ ],
321
+ }),
296
322
  });
323
+ const s = makeSession({ name: "test-agent" }, model);
297
324
  const events = [];
298
- for await (const e of result)
325
+ for await (const e of s.runTurn({
326
+ messages: [userMsg],
327
+ stream: "message",
328
+ }))
329
+ events.push(e);
330
+ (0, vitest_1.expect)(events.some((e) => e.event === "tool_call")).toBe(true);
331
+ });
332
+ (0, vitest_1.it)("stops at untrusted tool in message mode", async () => {
333
+ const agent = makeAgent();
334
+ const agentConfig = { name: "test-agent", tools: [] };
335
+ const model = makeModel({
336
+ call: vitest_1.vi.fn().mockResolvedValue({
337
+ stopReason: "tool_use",
338
+ messages: [
339
+ {
340
+ role: "assistant",
341
+ content: [{ type: "tool_use", toolCallId: "c1", name: "echo", input: { msg: "hi" } }],
342
+ },
343
+ ],
344
+ }),
345
+ });
346
+ const s = new session_1.Session("s", agent, model, agentConfig, [], []);
347
+ const events = [];
348
+ for await (const e of s.runTurn({
349
+ messages: [userMsg],
350
+ stream: "message",
351
+ }))
299
352
  events.push(e);
300
- (0, vitest_1.expect)(events[0]).toEqual({ event: "session_start", sessionId: "sess-1" });
353
+ (0, vitest_1.expect)(events.at(-1).event).toBe("turn_stop");
354
+ (0, vitest_1.expect)(model.call).toHaveBeenCalledTimes(1);
355
+ });
356
+ });
357
+ (0, vitest_1.describe)("lastToolUses", () => {
358
+ (0, vitest_1.it)("returns empty array when last assistant message has string content", async () => {
359
+ const s = makeSession();
360
+ s.history = [{ role: "assistant", content: "hello" }];
361
+ // runTurnNone will call lastToolUses; with string content it should return []
362
+ const model = makeModel({
363
+ call: vitest_1.vi.fn().mockResolvedValue({ stopReason: "end_turn", messages: [] }),
364
+ });
365
+ const s2 = new session_1.Session("s", makeAgent(), model, { name: "test-agent" }, [], [{ role: "assistant", content: "hello" }]);
366
+ const res = (await s2.runTurn({ messages: [userMsg] }));
367
+ (0, vitest_1.expect)(res.stopReason).toBe("end_turn");
301
368
  });
302
369
  });
303
370
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentapplicationprotocol/server",
3
- "version": "0.6.1",
3
+ "version": "0.7.0",
4
4
  "description": "AAP server",
5
5
  "keywords": [
6
6
  "aap",
@@ -31,7 +31,7 @@
31
31
  "ai": "^6.0.141",
32
32
  "hono": "^4.12.8",
33
33
  "zod": "^4.3.6",
34
- "@agentapplicationprotocol/core": "0.6.1"
34
+ "@agentapplicationprotocol/core": "0.7.0"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@ai-sdk/openai": "^3.0.48",