@andreprado/agentkit 0.1.1 → 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/README.md +8 -77
- package/docs/guides/add-channel.md +14 -92
- package/docs/guides/add-knowledge.md +0 -21
- package/docs/guides/add-tool.md +5 -11
- package/docs/guides/channel-security.md +3 -207
- package/docs/guides/connect-discord.md +7 -172
- package/docs/guides/connect-slack.md +6 -121
- package/docs/guides/connect-telegram.md +6 -165
- package/docs/guides/connect-whatsapp-evolution.md +6 -116
- package/docs/guides/connect-whatsapp-uazapi.md +6 -134
- package/docs/guides/connect-whatsapp-zapster.md +6 -202
- package/docs/guides/create-agent.md +5 -14
- package/docs/guides/debug-channel.md +4 -156
- package/docs/guides/improve-local.md +13 -0
- package/docs/guides/local-only-migration.md +35 -0
- package/docs/guides/replay-local-traces.md +11 -0
- package/docs/guides/run-evals.md +2 -4
- package/docs/guides/security-rules.md +5 -154
- package/docs/guides/use-jev.md +3 -6
- package/docs/guides/use-provider.md +5 -6
- package/docs/guides/write-feedback.md +10 -0
- package/docs/llms-full.txt +27 -448
- package/docs/llms.txt +8 -44
- package/package.json +3 -5
- package/src/cli/commands/channels.ts +8 -1613
- package/src/cli/commands/feedback.ts +8 -86
- package/src/cli/commands/provider.ts +29 -11
- package/src/cli/constants.ts +0 -3
- package/src/cli/flags.ts +0 -28
- package/src/cli/help.ts +16 -92
- package/src/cli/index.ts +15 -1091
- package/src/index.ts +6 -158
- package/src/providers/codex-auth.ts +16 -2
- package/src/providers/pi.ts +36 -19
- package/src/runtime/channels/discord.ts +2 -2
- package/src/runtime/chat.ts +5 -3
- package/src/runtime/config.ts +14 -148
- package/src/runtime/database.ts +2 -2
- package/src/runtime/dev-server.ts +8 -8
- package/src/runtime/env.ts +11 -0
- package/src/runtime/improve.ts +2 -262
- package/src/runtime/inspect.ts +13 -73
- package/src/runtime/knowledge/ingest.ts +1 -1
- package/src/runtime/knowledge/tool.ts +16 -2
- package/src/runtime/knowledge/vector.ts +1 -1
- package/src/runtime/tool-runner.ts +5 -3
- package/src/runtime/tools.ts +10 -14
- package/src/storage/sqlite.ts +11 -32
- package/src/templates/blank.ts +15 -102
- package/src/templates/common.ts +60 -0
- package/src/templates/dentista.ts +7 -74
- package/src/templates/skills/agentkit-capsule/SKILL.md +5 -7
- package/src/templates/skills/agentkit-capsule/references/docs-router.md +2 -3
- package/src/templates/skills/agentkit-channels/SKILL.md +6 -119
- package/src/templates/skills/agentkit-channels/references/channel-buffering.md +0 -9
- package/src/templates/skills/agentkit-channels/references/channel-debugging.md +1 -64
- package/src/templates/skills/agentkit-channels/references/discord.md +2 -92
- package/src/templates/skills/agentkit-channels/references/slack.md +2 -55
- package/src/templates/skills/agentkit-channels/references/telegram.md +2 -71
- package/src/templates/skills/agentkit-channels/references/whatsapp-evolution.md +2 -56
- package/src/templates/skills/agentkit-channels/references/whatsapp-uazapi.md +2 -53
- package/src/templates/skills/agentkit-channels/references/whatsapp-zapster.md +2 -70
- package/src/templates/skills/agentkit-database/SKILL.md +2 -4
- package/src/templates/skills/agentkit-evals/SKILL.md +1 -1
- package/src/templates/skills/agentkit-improve/SKILL.md +6 -85
- package/src/templates/skills/agentkit-improve/references/trace-packets.md +1 -1
- package/src/templates/skills/agentkit-provider/SKILL.md +1 -2
- package/src/templates/skills/agentkit-security/SKILL.md +1 -3
- package/src/templates/skills/agentkit-tools/SKILL.md +1 -1
- package/src/templates/skills/agentkit-tools/examples/database-write.tool.md +1 -2
- package/src/templates/skills/agentkit-troubleshooting/SKILL.md +5 -11
- package/src/templates/support.ts +8 -92
- package/docs/guides/add-managed-composio.md +0 -165
- package/docs/guides/improve-from-production.md +0 -151
- package/docs/guides/prepare-deploy.md +0 -227
- package/docs/guides/replay-production-traces.md +0 -72
- package/docs/guides/send-feedback.md +0 -135
- package/src/cli/cloud-client.ts +0 -377
- package/src/cli/deploy-chat-ui.ts +0 -606
- package/src/cli/deploy-readiness.ts +0 -561
- package/src/cloud/artifact.ts +0 -139
- package/src/cloud/client.ts +0 -80
- package/src/cloud/contracts.ts +0 -63
- package/src/cloud/index.ts +0 -3
- package/src/runtime/build.ts +0 -43
- package/src/runtime/core/deploy-state.ts +0 -54
- package/src/runtime/core/manifest.ts +0 -283
- package/src/runtime/core/targets.ts +0 -133
- package/src/runtime/deploy-readiness.ts +0 -135
- package/src/runtime/deploy.ts +0 -1
- package/src/runtime/integrations/composio.ts +0 -425
- package/src/runtime/targets/cloudflare/build.ts +0 -3319
- package/src/runtime/targets/container/build.ts +0 -146
- package/src/runtime/targets/container/server.ts +0 -33
- package/src/runtime/targets/vps/deploy.ts +0 -223
- package/src/templates/skills/agentkit-deploy/SKILL.md +0 -52
- package/src/templates/skills/agentkit-integrations/SKILL.md +0 -98
|
@@ -1,606 +0,0 @@
|
|
|
1
|
-
import { readFile } from "node:fs/promises";
|
|
2
|
-
import { createServer, type IncomingMessage, type Server, type ServerResponse } from "node:http";
|
|
3
|
-
import { join, relative, resolve } from "node:path";
|
|
4
|
-
|
|
5
|
-
import { findAgentCapsuleRoot } from "../runtime/config";
|
|
6
|
-
import { readLocalDeployStateIfExists } from "../runtime/core/deploy-state";
|
|
7
|
-
import { AgentKitError, isAgentKitError } from "../runtime/errors";
|
|
8
|
-
import { defaultDeployChatAccessTokenName, defaultDeployChatAccessTokenPath } from "./constants";
|
|
9
|
-
|
|
10
|
-
export async function startDeployChatUiServer(
|
|
11
|
-
cwd: string,
|
|
12
|
-
options: { port: number; tokenFile?: string; autoPort?: boolean },
|
|
13
|
-
): Promise<{
|
|
14
|
-
url: string;
|
|
15
|
-
deployUrl: string;
|
|
16
|
-
tokenSource: string;
|
|
17
|
-
port: number;
|
|
18
|
-
portConflict: { port: number } | null;
|
|
19
|
-
stop(): Promise<void>;
|
|
20
|
-
}> {
|
|
21
|
-
const root = await findAgentCapsuleRoot(cwd);
|
|
22
|
-
const state = await readLocalDeployStateIfExists(root);
|
|
23
|
-
|
|
24
|
-
if (!state?.url) {
|
|
25
|
-
throw new Error("No hosted deploy URL found. Run agentkit deploy first.");
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const tokenPath = options.tokenFile
|
|
29
|
-
? resolve(root, options.tokenFile)
|
|
30
|
-
: join(root, defaultDeployChatAccessTokenPath);
|
|
31
|
-
const hostname = "localhost";
|
|
32
|
-
const deployUrl = state.url;
|
|
33
|
-
const start = await listenForDeployChatUiServer({
|
|
34
|
-
hostname,
|
|
35
|
-
requestedPort: options.port,
|
|
36
|
-
autoPort: options.autoPort === true,
|
|
37
|
-
create: () => createDeployChatUiHttpServer({ root, tokenPath, hostname, requestedPort: options.port, deployUrl }),
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
return {
|
|
41
|
-
url: `http://${hostname}:${start.port}`,
|
|
42
|
-
deployUrl,
|
|
43
|
-
tokenSource: process.env.AGENTKIT_DEPLOY_ACCESS_TOKEN ? "AGENTKIT_DEPLOY_ACCESS_TOKEN" : relativePath(root, tokenPath),
|
|
44
|
-
port: start.port,
|
|
45
|
-
portConflict: start.portConflict,
|
|
46
|
-
stop: () =>
|
|
47
|
-
new Promise<void>((resolvePromise, reject) => {
|
|
48
|
-
start.server.close((error) => (error ? reject(error) : resolvePromise()));
|
|
49
|
-
}),
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function createDeployChatUiHttpServer(input: {
|
|
54
|
-
root: string;
|
|
55
|
-
tokenPath: string;
|
|
56
|
-
hostname: string;
|
|
57
|
-
requestedPort: number;
|
|
58
|
-
deployUrl: string;
|
|
59
|
-
}): Server {
|
|
60
|
-
const { root, tokenPath, hostname, requestedPort, deployUrl } = input;
|
|
61
|
-
const server = createServer(async (request, response) => {
|
|
62
|
-
try {
|
|
63
|
-
const requestUrl = new URL(request.url ?? "/", `http://${request.headers.host ?? `${hostname}:${requestedPort}`}`);
|
|
64
|
-
|
|
65
|
-
if (request.method === "GET" && requestUrl.pathname === "/") {
|
|
66
|
-
sendHtml(response, renderDeployChatUi({ deployUrl }));
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
if (request.method === "GET" && requestUrl.pathname === "/health") {
|
|
71
|
-
sendJson(response, 200, { status: "ok", target: "deploy-chat-ui", deployUrl });
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if (request.method === "GET" && requestUrl.pathname === "/_agentkit") {
|
|
76
|
-
sendJson(response, 200, {
|
|
77
|
-
deployUrl,
|
|
78
|
-
access: "proxied",
|
|
79
|
-
tokenSource: process.env.AGENTKIT_DEPLOY_ACCESS_TOKEN ? "AGENTKIT_DEPLOY_ACCESS_TOKEN" : relativePath(root, tokenPath),
|
|
80
|
-
});
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
if (request.method === "POST" && (requestUrl.pathname === "/v1/chat" || requestUrl.pathname === "/chat")) {
|
|
85
|
-
const token = await readDeployAccessTokenForUi(tokenPath);
|
|
86
|
-
const body = await readIncomingBody(request);
|
|
87
|
-
const contentTypeHeader = request.headers["content-type"];
|
|
88
|
-
const contentType = Array.isArray(contentTypeHeader)
|
|
89
|
-
? contentTypeHeader[0] ?? "application/json"
|
|
90
|
-
: contentTypeHeader ?? "application/json";
|
|
91
|
-
const upstream = await fetch(new URL("/v1/chat", deployUrl).href, {
|
|
92
|
-
method: "POST",
|
|
93
|
-
headers: {
|
|
94
|
-
"Authorization": `Bearer ${token}`,
|
|
95
|
-
"Content-Type": contentType,
|
|
96
|
-
},
|
|
97
|
-
body,
|
|
98
|
-
});
|
|
99
|
-
const text = await upstream.text();
|
|
100
|
-
|
|
101
|
-
response.writeHead(upstream.status, {
|
|
102
|
-
"content-type": upstream.headers.get("content-type") ?? "application/json",
|
|
103
|
-
});
|
|
104
|
-
response.end(text);
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if (
|
|
109
|
-
request.method === "GET" &&
|
|
110
|
-
(requestUrl.pathname === "/v1/conversations" ||
|
|
111
|
-
/^\/v1\/conversations\/[^/]+(?:\/trace)?$/.test(requestUrl.pathname))
|
|
112
|
-
) {
|
|
113
|
-
const token = await readDeployAccessTokenForUi(tokenPath);
|
|
114
|
-
const upstream = await fetch(new URL(requestUrl.pathname, deployUrl).href, {
|
|
115
|
-
method: "GET",
|
|
116
|
-
headers: {
|
|
117
|
-
Authorization: `Bearer ${token}`,
|
|
118
|
-
},
|
|
119
|
-
});
|
|
120
|
-
const text = await upstream.text();
|
|
121
|
-
|
|
122
|
-
response.writeHead(upstream.status, {
|
|
123
|
-
"content-type": upstream.headers.get("content-type") ?? "application/json",
|
|
124
|
-
});
|
|
125
|
-
response.end(text);
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
sendJson(response, 404, { error: { code: "not_found", message: "Route not found." } });
|
|
130
|
-
} catch (error) {
|
|
131
|
-
sendJson(response, 500, {
|
|
132
|
-
error: {
|
|
133
|
-
code: isAgentKitError(error) ? error.code : "deploy_chat_ui_error",
|
|
134
|
-
message: error instanceof Error ? error.message : String(error),
|
|
135
|
-
},
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
return server;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
async function listenForDeployChatUiServer(input: {
|
|
144
|
-
hostname: string;
|
|
145
|
-
requestedPort: number;
|
|
146
|
-
autoPort: boolean;
|
|
147
|
-
create: () => Server;
|
|
148
|
-
}): Promise<{ server: Server; port: number; portConflict: { port: number } | null }> {
|
|
149
|
-
let firstConflict: { port: number } | null = null;
|
|
150
|
-
const lastPort = input.autoPort ? input.requestedPort + 50 : input.requestedPort;
|
|
151
|
-
|
|
152
|
-
for (let port = input.requestedPort; port <= lastPort; port += 1) {
|
|
153
|
-
const server = input.create();
|
|
154
|
-
|
|
155
|
-
try {
|
|
156
|
-
await listen(server, input.hostname, port);
|
|
157
|
-
return { server, port, portConflict: port === input.requestedPort ? null : firstConflict };
|
|
158
|
-
} catch (error) {
|
|
159
|
-
await closeServerAfterListenError(server);
|
|
160
|
-
|
|
161
|
-
if (!input.autoPort || !isAddressInUseError(error)) {
|
|
162
|
-
throw error;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
firstConflict ??= { port };
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
throw new AgentKitError(
|
|
170
|
-
"runtime_error",
|
|
171
|
-
`Could not allocate a local AgentKit hosted chat UI port after ${input.requestedPort}-${lastPort}.`,
|
|
172
|
-
);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
function listen(server: Server, hostname: string, port: number): Promise<void> {
|
|
176
|
-
return new Promise((resolvePromise, reject) => {
|
|
177
|
-
server.once("error", reject);
|
|
178
|
-
server.listen(port, hostname, () => {
|
|
179
|
-
server.off("error", reject);
|
|
180
|
-
resolvePromise();
|
|
181
|
-
});
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
async function closeServerAfterListenError(server: Server): Promise<void> {
|
|
186
|
-
await new Promise<void>((resolvePromise) => {
|
|
187
|
-
server.close(() => resolvePromise());
|
|
188
|
-
});
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
function isAddressInUseError(error: unknown): boolean {
|
|
192
|
-
return Boolean(error && typeof error === "object" && "code" in error && error.code === "EADDRINUSE");
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
export async function readDeployAccessTokenForUi(tokenPath: string): Promise<string> {
|
|
196
|
-
if (process.env.AGENTKIT_DEPLOY_ACCESS_TOKEN) {
|
|
197
|
-
return process.env.AGENTKIT_DEPLOY_ACCESS_TOKEN;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
let payload: unknown;
|
|
201
|
-
|
|
202
|
-
try {
|
|
203
|
-
payload = JSON.parse(await readFile(tokenPath, "utf8"));
|
|
204
|
-
} catch {
|
|
205
|
-
throw new AgentKitError(
|
|
206
|
-
"deploy_access_token_missing",
|
|
207
|
-
`Could not read a clean deploy access token file at ${tokenPath}. Run: agentkit access token create ${defaultDeployChatAccessTokenName} --out ${defaultDeployChatAccessTokenPath}`,
|
|
208
|
-
);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
const token =
|
|
212
|
-
payload && typeof payload === "object"
|
|
213
|
-
? (payload as { access_token?: { token?: unknown }; token?: unknown; accessToken?: unknown }).access_token?.token ??
|
|
214
|
-
(payload as { token?: unknown }).token ??
|
|
215
|
-
(payload as { accessToken?: unknown }).accessToken
|
|
216
|
-
: undefined;
|
|
217
|
-
|
|
218
|
-
if (typeof token !== "string" || token.trim().length === 0) {
|
|
219
|
-
throw new AgentKitError(
|
|
220
|
-
"deploy_access_token_invalid",
|
|
221
|
-
`Deploy access token file ${tokenPath} does not contain access_token.token. Recreate it with: agentkit access token create ${defaultDeployChatAccessTokenName} --out ${defaultDeployChatAccessTokenPath}`,
|
|
222
|
-
);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
return token.trim();
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
function readIncomingBody(request: IncomingMessage): Promise<string> {
|
|
229
|
-
return new Promise((resolvePromise, reject) => {
|
|
230
|
-
let body = "";
|
|
231
|
-
request.setEncoding("utf8");
|
|
232
|
-
request.on("data", (chunk) => {
|
|
233
|
-
body += chunk;
|
|
234
|
-
});
|
|
235
|
-
request.on("end", () => resolvePromise(body));
|
|
236
|
-
request.on("error", reject);
|
|
237
|
-
});
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
function sendJson(response: ServerResponse, status: number, body: unknown): void {
|
|
241
|
-
response.writeHead(status, { "content-type": "application/json; charset=utf-8" });
|
|
242
|
-
response.end(JSON.stringify(body, null, 2));
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
function sendHtml(response: ServerResponse, body: string): void {
|
|
246
|
-
response.writeHead(200, { "content-type": "text/html; charset=utf-8" });
|
|
247
|
-
response.end(body);
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
function renderDeployChatUi(input: { deployUrl: string }): string {
|
|
251
|
-
const deployUrl = escapeHtml(input.deployUrl);
|
|
252
|
-
|
|
253
|
-
return `<!doctype html>
|
|
254
|
-
<html lang="en">
|
|
255
|
-
<head>
|
|
256
|
-
<meta charset="utf-8">
|
|
257
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
258
|
-
<title>AgentKit Hosted Chat</title>
|
|
259
|
-
<style>
|
|
260
|
-
:root {
|
|
261
|
-
color-scheme: light;
|
|
262
|
-
--bg: #f6f7f5;
|
|
263
|
-
--panel: #ffffff;
|
|
264
|
-
--ink: #182022;
|
|
265
|
-
--muted: #667175;
|
|
266
|
-
--line: #d9ded9;
|
|
267
|
-
--accent: #176b5d;
|
|
268
|
-
--danger: #9f2d2d;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
* { box-sizing: border-box; }
|
|
272
|
-
|
|
273
|
-
body {
|
|
274
|
-
margin: 0;
|
|
275
|
-
min-height: 100svh;
|
|
276
|
-
background: var(--bg);
|
|
277
|
-
color: var(--ink);
|
|
278
|
-
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
main {
|
|
282
|
-
min-height: 100svh;
|
|
283
|
-
display: grid;
|
|
284
|
-
grid-template-rows: auto 1fr auto;
|
|
285
|
-
background: var(--panel);
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
header {
|
|
289
|
-
border-bottom: 1px solid var(--line);
|
|
290
|
-
padding: 18px 22px;
|
|
291
|
-
display: grid;
|
|
292
|
-
gap: 10px;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
h1 {
|
|
296
|
-
margin: 0 0 4px;
|
|
297
|
-
font-size: 20px;
|
|
298
|
-
line-height: 1.2;
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
.topline {
|
|
302
|
-
display: flex;
|
|
303
|
-
align-items: start;
|
|
304
|
-
justify-content: space-between;
|
|
305
|
-
gap: 12px;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
code {
|
|
309
|
-
color: var(--muted);
|
|
310
|
-
font-size: 12px;
|
|
311
|
-
overflow-wrap: anywhere;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
.meta {
|
|
315
|
-
display: flex;
|
|
316
|
-
flex-wrap: wrap;
|
|
317
|
-
gap: 8px 14px;
|
|
318
|
-
color: var(--muted);
|
|
319
|
-
font-size: 12px;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
#messages {
|
|
323
|
-
padding: 22px;
|
|
324
|
-
overflow: auto;
|
|
325
|
-
display: flex;
|
|
326
|
-
flex-direction: column;
|
|
327
|
-
gap: 12px;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
.message {
|
|
331
|
-
max-width: 780px;
|
|
332
|
-
display: grid;
|
|
333
|
-
gap: 4px;
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
.message.user {
|
|
337
|
-
align-self: end;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
.role {
|
|
341
|
-
color: var(--muted);
|
|
342
|
-
font-size: 12px;
|
|
343
|
-
font-weight: 700;
|
|
344
|
-
text-transform: uppercase;
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
.bubble {
|
|
348
|
-
border: 1px solid var(--line);
|
|
349
|
-
border-radius: 8px;
|
|
350
|
-
padding: 12px 14px;
|
|
351
|
-
white-space: pre-wrap;
|
|
352
|
-
line-height: 1.45;
|
|
353
|
-
background: #fbfbfa;
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
.user .bubble {
|
|
357
|
-
border-color: #b8d8ce;
|
|
358
|
-
background: #e8f5f0;
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
.error .bubble {
|
|
362
|
-
border-color: #e1b1b1;
|
|
363
|
-
color: var(--danger);
|
|
364
|
-
background: #fff6f6;
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
#trace {
|
|
368
|
-
border-top: 1px solid var(--line);
|
|
369
|
-
padding: 12px 22px;
|
|
370
|
-
display: grid;
|
|
371
|
-
gap: 8px;
|
|
372
|
-
background: #fbfbfa;
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
.tool-call {
|
|
376
|
-
border: 1px solid var(--line);
|
|
377
|
-
border-radius: 8px;
|
|
378
|
-
padding: 10px 12px;
|
|
379
|
-
display: grid;
|
|
380
|
-
gap: 6px;
|
|
381
|
-
font-size: 12px;
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
.tool-head {
|
|
385
|
-
display: flex;
|
|
386
|
-
flex-wrap: wrap;
|
|
387
|
-
gap: 8px;
|
|
388
|
-
align-items: center;
|
|
389
|
-
color: var(--muted);
|
|
390
|
-
font-weight: 700;
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
.tool-call pre {
|
|
394
|
-
margin: 0;
|
|
395
|
-
max-height: 180px;
|
|
396
|
-
overflow: auto;
|
|
397
|
-
white-space: pre-wrap;
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
form {
|
|
401
|
-
border-top: 1px solid var(--line);
|
|
402
|
-
padding: 14px;
|
|
403
|
-
display: grid;
|
|
404
|
-
grid-template-columns: 1fr auto;
|
|
405
|
-
gap: 10px;
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
textarea {
|
|
409
|
-
min-height: 52px;
|
|
410
|
-
max-height: 180px;
|
|
411
|
-
resize: vertical;
|
|
412
|
-
border: 1px solid var(--line);
|
|
413
|
-
border-radius: 8px;
|
|
414
|
-
padding: 12px;
|
|
415
|
-
color: var(--ink);
|
|
416
|
-
font: inherit;
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
button {
|
|
420
|
-
min-width: 96px;
|
|
421
|
-
border: 0;
|
|
422
|
-
border-radius: 8px;
|
|
423
|
-
padding: 0 18px;
|
|
424
|
-
color: white;
|
|
425
|
-
background: var(--accent);
|
|
426
|
-
font: inherit;
|
|
427
|
-
font-weight: 700;
|
|
428
|
-
cursor: pointer;
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
button.secondary {
|
|
432
|
-
min-height: 34px;
|
|
433
|
-
color: var(--ink);
|
|
434
|
-
background: #eef1ee;
|
|
435
|
-
border: 1px solid var(--line);
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
button:disabled {
|
|
439
|
-
cursor: wait;
|
|
440
|
-
opacity: 0.65;
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
@media (max-width: 640px) {
|
|
444
|
-
form {
|
|
445
|
-
grid-template-columns: 1fr;
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
button {
|
|
449
|
-
min-height: 44px;
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
</style>
|
|
453
|
-
</head>
|
|
454
|
-
<body>
|
|
455
|
-
<main>
|
|
456
|
-
<header>
|
|
457
|
-
<div class="topline">
|
|
458
|
-
<div>
|
|
459
|
-
<h1>AgentKit Hosted Chat</h1>
|
|
460
|
-
<code>${deployUrl}</code>
|
|
461
|
-
</div>
|
|
462
|
-
<button class="secondary" id="new-conversation" type="button">New conversation</button>
|
|
463
|
-
</div>
|
|
464
|
-
<div class="meta">
|
|
465
|
-
<span>Conversation: <code id="conversation-id">none</code></span>
|
|
466
|
-
<span>Tool calls: <code id="tool-count">0</code></span>
|
|
467
|
-
</div>
|
|
468
|
-
</header>
|
|
469
|
-
<section id="messages" aria-live="polite"></section>
|
|
470
|
-
<section id="trace" aria-live="polite"></section>
|
|
471
|
-
<form id="form">
|
|
472
|
-
<textarea id="message" name="message" placeholder="Type a message" required autofocus></textarea>
|
|
473
|
-
<button id="send" type="submit">Send</button>
|
|
474
|
-
</form>
|
|
475
|
-
</main>
|
|
476
|
-
<script>
|
|
477
|
-
const form = document.querySelector("#form");
|
|
478
|
-
const textarea = document.querySelector("#message");
|
|
479
|
-
const send = document.querySelector("#send");
|
|
480
|
-
const messages = document.querySelector("#messages");
|
|
481
|
-
const trace = document.querySelector("#trace");
|
|
482
|
-
const conversationLabel = document.querySelector("#conversation-id");
|
|
483
|
-
const toolCount = document.querySelector("#tool-count");
|
|
484
|
-
const newConversation = document.querySelector("#new-conversation");
|
|
485
|
-
let conversationId = null;
|
|
486
|
-
|
|
487
|
-
function addMessage(role, content, className = "") {
|
|
488
|
-
const wrapper = document.createElement("article");
|
|
489
|
-
wrapper.className = ["message", role, className].filter(Boolean).join(" ");
|
|
490
|
-
const label = document.createElement("div");
|
|
491
|
-
label.className = "role";
|
|
492
|
-
label.textContent = role;
|
|
493
|
-
const bubble = document.createElement("div");
|
|
494
|
-
bubble.className = "bubble";
|
|
495
|
-
bubble.textContent = content;
|
|
496
|
-
wrapper.append(label, bubble);
|
|
497
|
-
messages.append(wrapper);
|
|
498
|
-
messages.scrollTop = messages.scrollHeight;
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
function setConversationId(value) {
|
|
502
|
-
conversationId = value || null;
|
|
503
|
-
conversationLabel.textContent = conversationId || "none";
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
function renderTrace(payload) {
|
|
507
|
-
const calls = [];
|
|
508
|
-
for (const run of payload.runs || []) {
|
|
509
|
-
for (const call of run.toolCalls || []) {
|
|
510
|
-
calls.push(call);
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
toolCount.textContent = String(calls.length);
|
|
515
|
-
trace.innerHTML = "";
|
|
516
|
-
|
|
517
|
-
for (const call of calls.slice(-8)) {
|
|
518
|
-
const item = document.createElement("article");
|
|
519
|
-
item.className = "tool-call";
|
|
520
|
-
const head = document.createElement("div");
|
|
521
|
-
head.className = "tool-head";
|
|
522
|
-
head.textContent = [call.name, call.status, call.errorMessage].filter(Boolean).join(" · ");
|
|
523
|
-
const input = document.createElement("pre");
|
|
524
|
-
input.textContent = JSON.stringify(call.input || {}, null, 2);
|
|
525
|
-
item.append(head, input);
|
|
526
|
-
trace.append(item);
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
async function refreshTrace() {
|
|
531
|
-
if (!conversationId) {
|
|
532
|
-
toolCount.textContent = "0";
|
|
533
|
-
trace.innerHTML = "";
|
|
534
|
-
return;
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
const response = await fetch("/v1/conversations/" + encodeURIComponent(conversationId) + "/trace");
|
|
538
|
-
const payload = await response.json();
|
|
539
|
-
if (response.ok) renderTrace(payload);
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
newConversation.addEventListener("click", () => {
|
|
543
|
-
setConversationId(null);
|
|
544
|
-
messages.innerHTML = "";
|
|
545
|
-
trace.innerHTML = "";
|
|
546
|
-
toolCount.textContent = "0";
|
|
547
|
-
textarea.focus();
|
|
548
|
-
});
|
|
549
|
-
|
|
550
|
-
textarea.addEventListener("keydown", (event) => {
|
|
551
|
-
if (event.key !== "Enter" || event.shiftKey || event.isComposing) return;
|
|
552
|
-
event.preventDefault();
|
|
553
|
-
if (send.disabled) return;
|
|
554
|
-
form.requestSubmit();
|
|
555
|
-
});
|
|
556
|
-
|
|
557
|
-
form.addEventListener("submit", async (event) => {
|
|
558
|
-
event.preventDefault();
|
|
559
|
-
const message = textarea.value.trim();
|
|
560
|
-
if (!message) return;
|
|
561
|
-
|
|
562
|
-
addMessage("user", message);
|
|
563
|
-
textarea.value = "";
|
|
564
|
-
send.disabled = true;
|
|
565
|
-
|
|
566
|
-
try {
|
|
567
|
-
const response = await fetch("/v1/chat", {
|
|
568
|
-
method: "POST",
|
|
569
|
-
headers: { "content-type": "application/json" },
|
|
570
|
-
body: JSON.stringify({ message, conversationId }),
|
|
571
|
-
});
|
|
572
|
-
const payload = await response.json();
|
|
573
|
-
|
|
574
|
-
if (!response.ok) {
|
|
575
|
-
throw new Error(payload.error?.message || "Request failed");
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
setConversationId(payload.conversationId || conversationId);
|
|
579
|
-
addMessage("assistant", payload.message?.content || "");
|
|
580
|
-
await refreshTrace();
|
|
581
|
-
} catch (error) {
|
|
582
|
-
addMessage("error", error instanceof Error ? error.message : String(error), "error");
|
|
583
|
-
await refreshTrace().catch(() => {});
|
|
584
|
-
} finally {
|
|
585
|
-
send.disabled = false;
|
|
586
|
-
textarea.focus();
|
|
587
|
-
}
|
|
588
|
-
});
|
|
589
|
-
</script>
|
|
590
|
-
</body>
|
|
591
|
-
</html>`;
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
function escapeHtml(value: string): string {
|
|
595
|
-
return value
|
|
596
|
-
.replace(/&/g, "&")
|
|
597
|
-
.replace(/</g, "<")
|
|
598
|
-
.replace(/>/g, ">")
|
|
599
|
-
.replace(/"/g, """)
|
|
600
|
-
.replace(/'/g, "'");
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
function relativePath(root: string, path: string): string {
|
|
604
|
-
const output = relative(root, path);
|
|
605
|
-
return output.startsWith("..") ? path : output || ".";
|
|
606
|
-
}
|