@andreprado/agentkit 0.1.0-alpha.17 → 0.1.0-alpha.18

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.
@@ -70,6 +70,27 @@ export async function startDeployChatUiServer(
70
70
  return;
71
71
  }
72
72
 
73
+ if (
74
+ request.method === "GET" &&
75
+ (requestUrl.pathname === "/v1/conversations" ||
76
+ /^\/v1\/conversations\/[^/]+(?:\/trace)?$/.test(requestUrl.pathname))
77
+ ) {
78
+ const token = await readDeployAccessTokenForUi(tokenPath);
79
+ const upstream = await fetch(new URL(requestUrl.pathname, deployUrl).href, {
80
+ method: "GET",
81
+ headers: {
82
+ Authorization: `Bearer ${token}`,
83
+ },
84
+ });
85
+ const text = await upstream.text();
86
+
87
+ response.writeHead(upstream.status, {
88
+ "content-type": upstream.headers.get("content-type") ?? "application/json",
89
+ });
90
+ response.end(text);
91
+ return;
92
+ }
93
+
73
94
  sendJson(response, 404, { error: { code: "not_found", message: "Route not found." } });
74
95
  } catch (error) {
75
96
  sendJson(response, 500, {
@@ -196,6 +217,8 @@ function renderDeployChatUi(input: { deployUrl: string }): string {
196
217
  header {
197
218
  border-bottom: 1px solid var(--line);
198
219
  padding: 18px 22px;
220
+ display: grid;
221
+ gap: 10px;
199
222
  }
200
223
 
201
224
  h1 {
@@ -204,12 +227,27 @@ function renderDeployChatUi(input: { deployUrl: string }): string {
204
227
  line-height: 1.2;
205
228
  }
206
229
 
230
+ .topline {
231
+ display: flex;
232
+ align-items: start;
233
+ justify-content: space-between;
234
+ gap: 12px;
235
+ }
236
+
207
237
  code {
208
238
  color: var(--muted);
209
239
  font-size: 12px;
210
240
  overflow-wrap: anywhere;
211
241
  }
212
242
 
243
+ .meta {
244
+ display: flex;
245
+ flex-wrap: wrap;
246
+ gap: 8px 14px;
247
+ color: var(--muted);
248
+ font-size: 12px;
249
+ }
250
+
213
251
  #messages {
214
252
  padding: 22px;
215
253
  overflow: auto;
@@ -255,6 +293,39 @@ function renderDeployChatUi(input: { deployUrl: string }): string {
255
293
  background: #fff6f6;
256
294
  }
257
295
 
296
+ #trace {
297
+ border-top: 1px solid var(--line);
298
+ padding: 12px 22px;
299
+ display: grid;
300
+ gap: 8px;
301
+ background: #fbfbfa;
302
+ }
303
+
304
+ .tool-call {
305
+ border: 1px solid var(--line);
306
+ border-radius: 8px;
307
+ padding: 10px 12px;
308
+ display: grid;
309
+ gap: 6px;
310
+ font-size: 12px;
311
+ }
312
+
313
+ .tool-head {
314
+ display: flex;
315
+ flex-wrap: wrap;
316
+ gap: 8px;
317
+ align-items: center;
318
+ color: var(--muted);
319
+ font-weight: 700;
320
+ }
321
+
322
+ .tool-call pre {
323
+ margin: 0;
324
+ max-height: 180px;
325
+ overflow: auto;
326
+ white-space: pre-wrap;
327
+ }
328
+
258
329
  form {
259
330
  border-top: 1px solid var(--line);
260
331
  padding: 14px;
@@ -286,6 +357,13 @@ function renderDeployChatUi(input: { deployUrl: string }): string {
286
357
  cursor: pointer;
287
358
  }
288
359
 
360
+ button.secondary {
361
+ min-height: 34px;
362
+ color: var(--ink);
363
+ background: #eef1ee;
364
+ border: 1px solid var(--line);
365
+ }
366
+
289
367
  button:disabled {
290
368
  cursor: wait;
291
369
  opacity: 0.65;
@@ -305,10 +383,20 @@ function renderDeployChatUi(input: { deployUrl: string }): string {
305
383
  <body>
306
384
  <main>
307
385
  <header>
308
- <h1>AgentKit Hosted Chat</h1>
309
- <code>${deployUrl}</code>
386
+ <div class="topline">
387
+ <div>
388
+ <h1>AgentKit Hosted Chat</h1>
389
+ <code>${deployUrl}</code>
390
+ </div>
391
+ <button class="secondary" id="new-conversation" type="button">New conversation</button>
392
+ </div>
393
+ <div class="meta">
394
+ <span>Conversation: <code id="conversation-id">none</code></span>
395
+ <span>Tool calls: <code id="tool-count">0</code></span>
396
+ </div>
310
397
  </header>
311
398
  <section id="messages" aria-live="polite"></section>
399
+ <section id="trace" aria-live="polite"></section>
312
400
  <form id="form">
313
401
  <textarea id="message" name="message" placeholder="Type a message" required autofocus></textarea>
314
402
  <button id="send" type="submit">Send</button>
@@ -319,6 +407,10 @@ function renderDeployChatUi(input: { deployUrl: string }): string {
319
407
  const textarea = document.querySelector("#message");
320
408
  const send = document.querySelector("#send");
321
409
  const messages = document.querySelector("#messages");
410
+ const trace = document.querySelector("#trace");
411
+ const conversationLabel = document.querySelector("#conversation-id");
412
+ const toolCount = document.querySelector("#tool-count");
413
+ const newConversation = document.querySelector("#new-conversation");
322
414
  let conversationId = null;
323
415
 
324
416
  function addMessage(role, content, className = "") {
@@ -335,6 +427,55 @@ function renderDeployChatUi(input: { deployUrl: string }): string {
335
427
  messages.scrollTop = messages.scrollHeight;
336
428
  }
337
429
 
430
+ function setConversationId(value) {
431
+ conversationId = value || null;
432
+ conversationLabel.textContent = conversationId || "none";
433
+ }
434
+
435
+ function renderTrace(payload) {
436
+ const calls = [];
437
+ for (const run of payload.runs || []) {
438
+ for (const call of run.toolCalls || []) {
439
+ calls.push(call);
440
+ }
441
+ }
442
+
443
+ toolCount.textContent = String(calls.length);
444
+ trace.innerHTML = "";
445
+
446
+ for (const call of calls.slice(-8)) {
447
+ const item = document.createElement("article");
448
+ item.className = "tool-call";
449
+ const head = document.createElement("div");
450
+ head.className = "tool-head";
451
+ head.textContent = [call.name, call.status, call.errorMessage].filter(Boolean).join(" · ");
452
+ const input = document.createElement("pre");
453
+ input.textContent = JSON.stringify(call.input || {}, null, 2);
454
+ item.append(head, input);
455
+ trace.append(item);
456
+ }
457
+ }
458
+
459
+ async function refreshTrace() {
460
+ if (!conversationId) {
461
+ toolCount.textContent = "0";
462
+ trace.innerHTML = "";
463
+ return;
464
+ }
465
+
466
+ const response = await fetch("/v1/conversations/" + encodeURIComponent(conversationId) + "/trace");
467
+ const payload = await response.json();
468
+ if (response.ok) renderTrace(payload);
469
+ }
470
+
471
+ newConversation.addEventListener("click", () => {
472
+ setConversationId(null);
473
+ messages.innerHTML = "";
474
+ trace.innerHTML = "";
475
+ toolCount.textContent = "0";
476
+ textarea.focus();
477
+ });
478
+
338
479
  textarea.addEventListener("keydown", (event) => {
339
480
  if (event.key !== "Enter" || event.shiftKey || event.isComposing) return;
340
481
  event.preventDefault();
@@ -363,10 +504,12 @@ function renderDeployChatUi(input: { deployUrl: string }): string {
363
504
  throw new Error(payload.error?.message || "Request failed");
364
505
  }
365
506
 
366
- conversationId = payload.conversationId || conversationId;
507
+ setConversationId(payload.conversationId || conversationId);
367
508
  addMessage("assistant", payload.message?.content || "");
509
+ await refreshTrace();
368
510
  } catch (error) {
369
511
  addMessage("error", error instanceof Error ? error.message : String(error), "error");
512
+ await refreshTrace().catch(() => {});
370
513
  } finally {
371
514
  send.disabled = false;
372
515
  textarea.focus();
@@ -2,13 +2,14 @@ import { findAgentCapsuleRoot, loadAgentCapsule } from "../runtime/config";
2
2
  import { loadDeployReadinessContext, type DeployReadinessContext } from "../runtime/deploy-readiness";
3
3
  import { loadCapsuleEnv } from "../runtime/env";
4
4
  import { isAgentKitError } from "../runtime/errors";
5
- import { MANAGED_COMPOSIO_ENTITLEMENT } from "../runtime/integrations/composio";
5
+ import { MANAGED_COMPOSIO_API_KEY_SECRET, MANAGED_COMPOSIO_ENTITLEMENT } from "../runtime/integrations/composio";
6
6
  import {
7
7
  cloudApiRequest,
8
8
  formatCloudAuthSource,
9
9
  readCloudAuthForApiUrl,
10
10
  type CloudCapabilitiesResponse,
11
11
  type CloudLimitsResponse,
12
+ type CloudManagedComposioResponse,
12
13
  type CloudMeResponse,
13
14
  type CloudProjectResolveResponse,
14
15
  type CloudSecretsResponse,
@@ -203,6 +204,58 @@ export async function checkDeployReadiness(options: {
203
204
  message: `Could not verify online deploy capacity. ${formatReadinessError(error)}`,
204
205
  });
205
206
  }
207
+
208
+ if (context.integrations.managedComposio.enabled) {
209
+ try {
210
+ const payload = (await cloudApiRequest(
211
+ options.apiUrl,
212
+ `/v1/me/managed-composio?toolkits=${encodeURIComponent(context.integrations.managedComposio.toolkits.join(","))}`,
213
+ {
214
+ method: "GET",
215
+ },
216
+ )) as CloudManagedComposioResponse;
217
+ const managed = payload.managed_composio;
218
+ const missingToolkits = (managed?.toolkits ?? []).filter((toolkit) => toolkit.auth_config !== "set");
219
+
220
+ if (managed?.api_key !== "set") {
221
+ checks.push({
222
+ status: "fail",
223
+ title: "Managed Composio API key",
224
+ message: `AgentKit Cloud is missing AGENTKIT_SECRET_${MANAGED_COMPOSIO_API_KEY_SECRET}.`,
225
+ action: "ask the AgentKit Cloud operator to configure managed Composio",
226
+ });
227
+ } else {
228
+ checks.push({
229
+ status: "pass",
230
+ title: "Managed Composio API key",
231
+ message: "AgentKit Cloud has the managed COMPOSIO_API_KEY.",
232
+ });
233
+ }
234
+
235
+ if (missingToolkits.length > 0) {
236
+ checks.push({
237
+ status: "fail",
238
+ title: "Managed Composio auth configs",
239
+ message: `Missing ${missingToolkits
240
+ .map((toolkit) => `${toolkit.auth_config_env ?? "auth config"} for ${toolkit.toolkit ?? "unknown"}`)
241
+ .join(", ")}.`,
242
+ action: "ask the AgentKit Cloud operator to configure the missing toolkit auth config",
243
+ });
244
+ } else {
245
+ checks.push({
246
+ status: "pass",
247
+ title: "Managed Composio auth configs",
248
+ message: `Auth configs are available for ${context.integrations.managedComposio.toolkits.join(", ")}.`,
249
+ });
250
+ }
251
+ } catch (error) {
252
+ checks.push({
253
+ status: "warn",
254
+ title: "Managed Composio auth configs",
255
+ message: `Could not verify managed Composio auth configs. ${formatReadinessError(error)}`,
256
+ });
257
+ }
258
+ }
206
259
  }
207
260
 
208
261
  if (context.agentKitManagedSecrets.length > 0) {
package/src/cli/help.ts CHANGED
@@ -73,9 +73,9 @@ Usage:
73
73
  agentkit db seed [--file <path>]
74
74
  agentkit eval run
75
75
  agentkit eval from-conversation <conversation-id> [--out <path>] [--force]
76
- agentkit conversations list
77
- agentkit conversations show <conversation-id>
78
- agentkit conversations trace <conversation-id>
76
+ agentkit conversations list [--deploy]
77
+ agentkit conversations show <conversation-id> [--deploy]
78
+ agentkit conversations trace <conversation-id> [--deploy]
79
79
  agentkit channels list
80
80
  agentkit channels add <website|telegram|whatsapp> <name> [--provider zapster|meta] [--api <url>]
81
81
  agentkit channels connect <website|telegram|whatsapp> <name> [--provider zapster|meta] [--api <url>]
@@ -91,7 +91,7 @@ Usage:
91
91
  agentkit channels buffers flush [<name>] <conversation-id> [--api <url>]
92
92
  agentkit channels buffers clear [<name>] <conversation-id> [--api <url>]
93
93
  agentkit channels buffers retry [<name>] <conversation-id> [--api <url>]
94
- agentkit integrations status [--api <url>]
94
+ agentkit integrations status [--toolkit <slug>] [--api <url>]
95
95
  agentkit integrations connect composio [--toolkit <slug>] [--callback-url <url>] [--api <url>]
96
96
  agentkit skills status
97
97
  agentkit skills sync
@@ -182,7 +182,7 @@ export function renderCommandHelp(command: string): string {
182
182
  sync: "agentkit sync init [--force] | agentkit sync run [--file <path>]",
183
183
  eval: "agentkit eval run | agentkit eval from-conversation <conversation-id> [--out <path>] [--force]",
184
184
  conversations:
185
- "agentkit conversations list | agentkit conversations show <conversation-id> | agentkit conversations trace <conversation-id>",
185
+ "agentkit conversations list [--deploy] | agentkit conversations show <conversation-id> [--deploy] | agentkit conversations trace <conversation-id> [--deploy]",
186
186
  channels:
187
187
  "agentkit channels list | add | connect | setup | status | doctor | test | test-audio | deliveries | buffers",
188
188
  skills: "agentkit skills status | agentkit skills sync",
@@ -192,7 +192,7 @@ export function renderCommandHelp(command: string): string {
192
192
  secret:
193
193
  "agentkit secret set <NAME> --stdin | --from-env [ENV_NAME] | --from-local-env | agentkit secret sync --from-local | list | unset <NAME>",
194
194
  access: "agentkit access token create <name> [--out <path>] | list | revoke <token-id>",
195
- integrations: "agentkit integrations status | agentkit integrations connect composio [--toolkit <slug>]",
195
+ integrations: "agentkit integrations status [--toolkit <slug>] | agentkit integrations connect composio [--toolkit <slug>]",
196
196
  env: "agentkit env set <NAME> --stdin | --from-env [ENV_NAME] | list | unset <NAME>",
197
197
  docs: "agentkit docs path | llms | full",
198
198
  handoff: "agentkit handoff codex [goal] | agentkit handoff claude [goal]",
package/src/cli/index.ts CHANGED
@@ -338,8 +338,15 @@ async function main() {
338
338
 
339
339
  if (args.command === "conversations") {
340
340
  const [subcommand, conversationId] = args.positional;
341
+ const fromDeploy = args.flags.deploy === true;
341
342
 
342
343
  if (subcommand === "list") {
344
+ if (fromDeploy) {
345
+ const payload = await fetchDeployConversationJson(process.cwd(), "/v1/conversations");
346
+ printJson(payload);
347
+ return;
348
+ }
349
+
343
350
  const conversations = await listConversationsFromCwd(process.cwd());
344
351
 
345
352
  if (conversations.length === 0) {
@@ -365,7 +372,16 @@ async function main() {
365
372
 
366
373
  if (subcommand === "show") {
367
374
  if (!conversationId) {
368
- throw new Error("Missing conversation id. Usage: agentkit conversations show <conversation-id>");
375
+ throw new Error("Missing conversation id. Usage: agentkit conversations show <conversation-id> [--deploy]");
376
+ }
377
+
378
+ if (fromDeploy) {
379
+ const payload = await fetchDeployConversationJson(
380
+ process.cwd(),
381
+ `/v1/conversations/${encodeURIComponent(conversationId)}`,
382
+ );
383
+ printJson(payload);
384
+ return;
369
385
  }
370
386
 
371
387
  const conversation = await getConversationFromCwd(process.cwd(), conversationId);
@@ -384,7 +400,16 @@ async function main() {
384
400
 
385
401
  if (subcommand === "trace") {
386
402
  if (!conversationId) {
387
- throw new Error("Missing conversation id. Usage: agentkit conversations trace <conversation-id>");
403
+ throw new Error("Missing conversation id. Usage: agentkit conversations trace <conversation-id> [--deploy]");
404
+ }
405
+
406
+ if (fromDeploy) {
407
+ const trace = await fetchDeployConversationJson(
408
+ process.cwd(),
409
+ `/v1/conversations/${encodeURIComponent(conversationId)}/trace`,
410
+ );
411
+ printJson(trace);
412
+ return;
388
413
  }
389
414
 
390
415
  const trace = await getConversationTraceFromCwd(process.cwd(), conversationId);
@@ -392,7 +417,7 @@ async function main() {
392
417
  return;
393
418
  }
394
419
 
395
- throw new Error("Usage: agentkit conversations list | agentkit conversations show <conversation-id> | agentkit conversations trace <conversation-id>");
420
+ throw new Error("Usage: agentkit conversations list [--deploy] | agentkit conversations show <conversation-id> [--deploy] | agentkit conversations trace <conversation-id> [--deploy]");
396
421
  }
397
422
 
398
423
  if (args.command === "knowledge") {
@@ -805,9 +830,16 @@ async function main() {
805
830
  const apiUrl = await resolveCloudApiUrl(args.flags.api);
806
831
 
807
832
  if (subcommand === "status") {
833
+ const toolkit = parseOptionalStringFlag(
834
+ args.flags.toolkit,
835
+ "toolkit",
836
+ "agentkit integrations status --toolkit <slug>",
837
+ );
808
838
  const payload = await cloudApiRequest(
809
839
  apiUrl,
810
- `/v1/deploys/${encodeURIComponent(state.deploy_id)}/integrations/composio/status`,
840
+ `/v1/deploys/${encodeURIComponent(state.deploy_id)}/integrations/composio/status${
841
+ toolkit ? `?toolkit=${encodeURIComponent(toolkit)}` : ""
842
+ }`,
811
843
  { method: "GET" },
812
844
  );
813
845
  printJson(payload);
@@ -1180,6 +1212,39 @@ async function runDeploySmoke(input: { deployUrl: string; message: string; token
1180
1212
  }
1181
1213
  }
1182
1214
 
1215
+ async function fetchDeployConversationJson(cwd: string, path: string): Promise<unknown> {
1216
+ const state = await requireLocalDeployState();
1217
+ const deployUrl = requireDeployUrl(state);
1218
+ const token = await readDeployAccessTokenForSmoke(cwd, state);
1219
+ const headers = new Headers();
1220
+
1221
+ if (token) {
1222
+ headers.set("Authorization", `Bearer ${token}`);
1223
+ }
1224
+
1225
+ let response: Response;
1226
+
1227
+ try {
1228
+ response = await fetch(new URL(path, deployUrl).href, {
1229
+ method: "GET",
1230
+ headers,
1231
+ });
1232
+ } catch (error) {
1233
+ throw new AgentKitError(
1234
+ "deploy_conversation_request_failed",
1235
+ `Could not read hosted conversation data: ${error instanceof Error ? error.message : String(error)}`,
1236
+ );
1237
+ }
1238
+
1239
+ const payload = await response.json().catch(() => null);
1240
+
1241
+ if (!response.ok) {
1242
+ throw new AgentKitError("deploy_conversation_request_failed", readCloudError(payload, response.status));
1243
+ }
1244
+
1245
+ return payload;
1246
+ }
1247
+
1183
1248
  function printJson(value: unknown): void {
1184
1249
  console.log(JSON.stringify(value, null, 2));
1185
1250
  }
package/src/index.ts CHANGED
@@ -155,6 +155,7 @@ export type ComposioManagedIntegrationInput = {
155
155
  toolkits: string[];
156
156
  tools?: Record<string, string[]>;
157
157
  tags?: IntegrationTagsConfig;
158
+ confirmExternalWrites?: boolean;
158
159
  };
159
160
 
160
161
  export type ComposioManagedIntegration = {
@@ -165,6 +166,7 @@ export type ComposioManagedIntegration = {
165
166
  toolkits: string[];
166
167
  tools?: Record<string, string[]>;
167
168
  tags?: IntegrationTagsConfig;
169
+ confirmExternalWrites?: boolean;
168
170
  };
169
171
 
170
172
  export type AgentIntegration = ComposioManagedIntegration;
@@ -368,6 +370,7 @@ export function composioManaged(input: ComposioManagedIntegrationInput): Composi
368
370
  const toolkits = normalizeToolkitList(input.toolkits, "composioManaged.toolkits");
369
371
  const tools = input.tools ? normalizeComposioToolMap(input.tools, toolkits) : undefined;
370
372
  const tags = input.tags ? normalizeIntegrationTags(input.tags) : undefined;
373
+ const confirmExternalWrites = input.confirmExternalWrites;
371
374
 
372
375
  return {
373
376
  type: "composio",
@@ -377,6 +380,7 @@ export function composioManaged(input: ComposioManagedIntegrationInput): Composi
377
380
  toolkits,
378
381
  ...(tools ? { tools } : {}),
379
382
  ...(tags ? { tags } : {}),
383
+ ...(confirmExternalWrites === undefined ? {} : { confirmExternalWrites }),
380
384
  };
381
385
  }
382
386
 
@@ -371,6 +371,10 @@ function validateComposioManagedIntegration(value: AgentIntegration, index: numb
371
371
  if (value.tags !== undefined) {
372
372
  validateIntegrationTags(value.tags, `integrations[${index}].tags`);
373
373
  }
374
+
375
+ if (value.confirmExternalWrites !== undefined && typeof value.confirmExternalWrites !== "boolean") {
376
+ throw invalidConfig(`integrations[${index}].confirmExternalWrites must be a boolean when provided.`);
377
+ }
374
378
  }
375
379
 
376
380
  function validateIntegrationTags(value: unknown, label: string): void {
@@ -40,6 +40,7 @@ export type SharedAgentManifest = {
40
40
  toolkits: string[];
41
41
  tools: string[];
42
42
  userId: string;
43
+ confirmExternalWrites: boolean;
43
44
  }>;
44
45
  channels: NonNullable<AgentConfig["channels"]>;
45
46
  transcription: {
@@ -174,6 +175,7 @@ export function buildSharedAgentManifest(
174
175
  toolkits: integration.toolkits,
175
176
  tools: integration.allowedTools,
176
177
  userId: integration.userId,
178
+ confirmExternalWrites: integration.confirmExternalWrites !== false,
177
179
  })),
178
180
  channels: capsule.config.channels ?? [],
179
181
  transcription: {
@@ -203,6 +203,7 @@ export function buildInspectState(
203
203
  toolkits: integration.toolkits,
204
204
  tools: integration.allowedTools,
205
205
  userId: integration.userId,
206
+ confirmExternalWrites: integration.confirmExternalWrites !== false,
206
207
  })),
207
208
  channels: (capsule.config.channels ?? []).map((channel) => ({
208
209
  name: channel.name,