@agentapplicationprotocol/server 0.6.0 → 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 +1 -1
- package/dist/src/examples/basic/index.js +2 -2
- package/dist/src/examples/compact-history/index.js +2 -2
- package/dist/src/model.js +1 -1
- package/dist/src/server.d.ts +1 -2
- package/dist/src/server.js +12 -12
- package/dist/src/server.test.js +48 -32
- package/dist/src/session.d.ts +3 -5
- package/dist/src/session.js +2 -14
- package/dist/src/session.test.js +88 -21
- package/package.json +2 -2
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
|
-
|
|
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
|
|
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
|
|
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.
|
|
171
|
+
inputSchema: (0, ai_1.jsonSchema)(t.parameters),
|
|
172
172
|
});
|
|
173
173
|
}
|
|
174
174
|
return res;
|
package/dist/src/server.d.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
}
|
package/dist/src/server.js
CHANGED
|
@@ -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:
|
|
45
|
-
router.
|
|
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("/
|
|
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("/
|
|
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("/
|
|
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);
|
|
@@ -74,9 +69,14 @@ function aap(handler) {
|
|
|
74
69
|
});
|
|
75
70
|
router.get("/sessions", async (c) => {
|
|
76
71
|
const after = c.req.query("after");
|
|
77
|
-
|
|
72
|
+
const result = await handler.listSessions({ after });
|
|
73
|
+
const { agents } = handler.getMeta();
|
|
74
|
+
return c.json({
|
|
75
|
+
...result,
|
|
76
|
+
sessions: result.sessions.map((s) => redactSecretOptions(s, agents)),
|
|
77
|
+
});
|
|
78
78
|
});
|
|
79
|
-
router.delete("/
|
|
79
|
+
router.delete("/sessions/:id", async (c) => {
|
|
80
80
|
await handler.deleteSession(c.req.param("id"));
|
|
81
81
|
return new Response(null, { status: 204 });
|
|
82
82
|
});
|
package/dist/src/server.test.js
CHANGED
|
@@ -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:
|
|
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 = {
|
|
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", "/
|
|
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", "/
|
|
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,
|
|
@@ -88,45 +88,61 @@ function req(method, path, body, headers) {
|
|
|
88
88
|
(0, vitest_1.expect)(await res.json()).toEqual(sessionList);
|
|
89
89
|
(0, vitest_1.expect)(handler.listSessions).toHaveBeenCalledWith({ after: "cursor1" });
|
|
90
90
|
});
|
|
91
|
-
(0, vitest_1.it)("
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
agent: { name: "a" },
|
|
95
|
-
|
|
91
|
+
(0, vitest_1.it)("GET /sessions redacts secret options in each session", async () => {
|
|
92
|
+
const secretSession = {
|
|
93
|
+
sessionId: "s1",
|
|
94
|
+
agent: { name: "a", options: { key: "mysecret", model: "gpt-4" } },
|
|
95
|
+
};
|
|
96
|
+
const app = makeApp(makeHandler({
|
|
97
|
+
listSessions: vitest_1.vi.fn().mockResolvedValue({ sessions: [secretSession] }),
|
|
98
|
+
getMeta: vitest_1.vi.fn().mockReturnValue({
|
|
99
|
+
agents: [
|
|
100
|
+
{
|
|
101
|
+
name: "a",
|
|
102
|
+
version: "1.0.0",
|
|
103
|
+
options: [
|
|
104
|
+
{ type: "secret", name: "key", default: "" },
|
|
105
|
+
{ type: "text", name: "model", default: "" },
|
|
106
|
+
],
|
|
107
|
+
},
|
|
108
|
+
],
|
|
109
|
+
}),
|
|
96
110
|
}));
|
|
97
|
-
(
|
|
111
|
+
const res = await app.fetch(req("GET", "/sessions"));
|
|
112
|
+
(0, vitest_1.expect)(res.status).toBe(200);
|
|
113
|
+
(0, vitest_1.expect)(await res.json()).toEqual({
|
|
114
|
+
sessions: [
|
|
115
|
+
{
|
|
116
|
+
...secretSession,
|
|
117
|
+
agent: { ...secretSession.agent, options: { key: "***", model: "gpt-4" } },
|
|
118
|
+
},
|
|
119
|
+
],
|
|
120
|
+
});
|
|
98
121
|
});
|
|
99
|
-
(0, vitest_1.it)("
|
|
122
|
+
(0, vitest_1.it)("POST /sessions returns 201 with sessionId", async () => {
|
|
100
123
|
const app = makeApp(makeHandler());
|
|
101
|
-
const res = await app.fetch(req("
|
|
124
|
+
const res = await app.fetch(req("POST", "/sessions", { agent: { name: "a" } }));
|
|
102
125
|
(0, vitest_1.expect)(res.status).toBe(201);
|
|
103
126
|
(0, vitest_1.expect)(await res.json()).toEqual(createSessionResponse);
|
|
104
127
|
});
|
|
105
|
-
(0, vitest_1.it)("
|
|
106
|
-
const app = makeApp(makeHandler({ createSession: vitest_1.vi.fn().mockResolvedValue(sseEvents()) }));
|
|
107
|
-
const res = await app.fetch(req("PUT", "/session", {
|
|
108
|
-
agent: { name: "a" },
|
|
109
|
-
messages: [{ role: "user", content: "hi" }],
|
|
110
|
-
stream: "message",
|
|
111
|
-
}));
|
|
112
|
-
(0, vitest_1.expect)(res.status).toBe(200);
|
|
113
|
-
(0, vitest_1.expect)(res.headers.get("content-type")).toContain("text/event-stream");
|
|
114
|
-
});
|
|
115
|
-
(0, vitest_1.it)("POST /session/:id returns AgentResponse", async () => {
|
|
128
|
+
(0, vitest_1.it)("POST /sessions/:id/turns returns AgentResponse", async () => {
|
|
116
129
|
const app = makeApp(makeHandler());
|
|
117
|
-
const res = await app.fetch(req("POST", "/
|
|
130
|
+
const res = await app.fetch(req("POST", "/sessions/s1/turns", { messages: [{ role: "user", content: "hi" }] }));
|
|
118
131
|
(0, vitest_1.expect)(res.status).toBe(200);
|
|
119
132
|
(0, vitest_1.expect)(await res.json()).toEqual(agentResponse);
|
|
120
133
|
});
|
|
121
|
-
(0, vitest_1.it)("POST /
|
|
134
|
+
(0, vitest_1.it)("POST /sessions/:id/turns with stream returns SSE", async () => {
|
|
122
135
|
const app = makeApp(makeHandler({ sendTurn: vitest_1.vi.fn().mockResolvedValue(sseEvents()) }));
|
|
123
|
-
const res = await app.fetch(req("POST", "/
|
|
136
|
+
const res = await app.fetch(req("POST", "/sessions/s1/turns", {
|
|
137
|
+
messages: [{ role: "user", content: "hi" }],
|
|
138
|
+
stream: "delta",
|
|
139
|
+
}));
|
|
124
140
|
(0, vitest_1.expect)(res.status).toBe(200);
|
|
125
141
|
(0, vitest_1.expect)(res.headers.get("content-type")).toContain("text/event-stream");
|
|
126
142
|
});
|
|
127
143
|
(0, vitest_1.it)("DELETE /session/:id returns 204", async () => {
|
|
128
144
|
const app = makeApp(makeHandler());
|
|
129
|
-
const res = await app.fetch(req("DELETE", "/
|
|
145
|
+
const res = await app.fetch(req("DELETE", "/sessions/s1"));
|
|
130
146
|
(0, vitest_1.expect)(res.status).toBe(204);
|
|
131
147
|
});
|
|
132
148
|
(0, vitest_1.it)("auth middleware: returns 401 when rejected", async () => {
|
|
@@ -159,7 +175,7 @@ function req(method, path, body, headers) {
|
|
|
159
175
|
getSession: vitest_1.vi.fn().mockResolvedValue(sessionWithOptions),
|
|
160
176
|
getMeta: vitest_1.vi.fn().mockReturnValue({ agents: [] }),
|
|
161
177
|
}));
|
|
162
|
-
const res = await app.fetch(req("GET", "/
|
|
178
|
+
const res = await app.fetch(req("GET", "/sessions/s1"));
|
|
163
179
|
(0, vitest_1.expect)(await res.json()).toEqual(sessionWithOptions);
|
|
164
180
|
});
|
|
165
181
|
(0, vitest_1.it)("GET /session/:id does not redact when agent has no secret options", async () => {
|
|
@@ -179,14 +195,14 @@ function req(method, path, body, headers) {
|
|
|
179
195
|
],
|
|
180
196
|
}),
|
|
181
197
|
}));
|
|
182
|
-
const res = await app.fetch(req("GET", "/
|
|
198
|
+
const res = await app.fetch(req("GET", "/sessions/s1"));
|
|
183
199
|
(0, vitest_1.expect)(await res.json()).toEqual(sessionWithOptions);
|
|
184
200
|
});
|
|
185
201
|
(0, vitest_1.it)("GET /session/:id/history?type=compacted calls getSessionHistory", async () => {
|
|
186
202
|
const messages = [{ role: "user", content: "hi" }];
|
|
187
203
|
const handler = makeHandler({ getSessionHistory: vitest_1.vi.fn().mockResolvedValue(messages) });
|
|
188
204
|
const app = makeApp(handler);
|
|
189
|
-
const res = await app.fetch(req("GET", "/
|
|
205
|
+
const res = await app.fetch(req("GET", "/sessions/s1/history?type=compacted"));
|
|
190
206
|
(0, vitest_1.expect)(res.status).toBe(200);
|
|
191
207
|
(0, vitest_1.expect)(handler.getSessionHistory).toHaveBeenCalledWith("s1", "compacted");
|
|
192
208
|
(0, vitest_1.expect)(await res.json()).toEqual({ history: { compacted: messages } });
|
|
@@ -195,14 +211,14 @@ function req(method, path, body, headers) {
|
|
|
195
211
|
const messages = [{ role: "user", content: "hi" }];
|
|
196
212
|
const handler = makeHandler({ getSessionHistory: vitest_1.vi.fn().mockResolvedValue(messages) });
|
|
197
213
|
const app = makeApp(handler);
|
|
198
|
-
const res = await app.fetch(req("GET", "/
|
|
214
|
+
const res = await app.fetch(req("GET", "/sessions/s1/history?type=full"));
|
|
199
215
|
(0, vitest_1.expect)(res.status).toBe(200);
|
|
200
216
|
(0, vitest_1.expect)(handler.getSessionHistory).toHaveBeenCalledWith("s1", "full");
|
|
201
217
|
(0, vitest_1.expect)(await res.json()).toEqual({ history: { full: messages } });
|
|
202
218
|
});
|
|
203
219
|
(0, vitest_1.it)("GET /session/:id/history without valid ?type returns 400", async () => {
|
|
204
220
|
const app = makeApp(makeHandler());
|
|
205
|
-
const res = await app.fetch(req("GET", "/
|
|
221
|
+
const res = await app.fetch(req("GET", "/sessions/s1/history"));
|
|
206
222
|
(0, vitest_1.expect)(res.status).toBe(400);
|
|
207
223
|
});
|
|
208
224
|
});
|
package/dist/src/session.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { HistoryMessage, ToolPermissionMessage, AgentConfig, ToolSpec, SSEEvent, AgentResponse,
|
|
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
|
-
/**
|
|
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
|
|
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. */
|
package/dist/src/session.js
CHANGED
|
@@ -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
|
package/dist/src/session.test.js
CHANGED
|
@@ -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: "",
|
|
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: "",
|
|
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)("
|
|
285
|
-
(0, vitest_1.it)("
|
|
286
|
-
const
|
|
287
|
-
const
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
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
|
|
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
|
|
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.
|
|
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.
|
|
34
|
+
"@agentapplicationprotocol/core": "0.7.0"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@ai-sdk/openai": "^3.0.48",
|