@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.
- package/docs/guides/add-channel.md +2 -2
- package/docs/guides/add-managed-composio.md +40 -16
- package/docs/guides/channel-security.md +36 -39
- package/docs/guides/debug-channel.md +141 -0
- package/docs/guides/prepare-deploy.md +5 -10
- package/docs/llms-full.txt +8 -6
- package/docs/llms.txt +5 -3
- package/package.json +1 -3
- package/src/cli/cloud-client.ts +12 -0
- package/src/cli/deploy-chat-ui.ts +146 -3
- package/src/cli/deploy-readiness.ts +54 -1
- package/src/cli/help.ts +6 -6
- package/src/cli/index.ts +69 -4
- package/src/index.ts +4 -0
- package/src/runtime/config.ts +4 -0
- package/src/runtime/core/manifest.ts +2 -0
- package/src/runtime/inspect.ts +1 -0
- package/src/runtime/integrations/composio.ts +168 -2
- package/src/runtime/targets/cloudflare/build.ts +235 -5
- package/src/templates/skills/agentkit-integrations/SKILL.md +11 -2
- package/docs/guides/agentkit-skills-architecture.md +0 -472
- package/docs/guides/channels-implementation-map.md +0 -243
- package/docs/guides/channels-production-handoff.md +0 -118
- package/docs/portable-deploy-release-checklist.md +0 -41
|
@@ -20,6 +20,7 @@ export type ManagedComposioResolutionOptions = {
|
|
|
20
20
|
type ManagedComposioToolInput = {
|
|
21
21
|
toolSlug: string;
|
|
22
22
|
arguments: Record<string, unknown>;
|
|
23
|
+
confirmed?: boolean;
|
|
23
24
|
};
|
|
24
25
|
|
|
25
26
|
type ManagedComposioToolOutput = {
|
|
@@ -32,6 +33,13 @@ type ManagedComposioToolOutput = {
|
|
|
32
33
|
userId: string;
|
|
33
34
|
};
|
|
34
35
|
|
|
36
|
+
type ComposioActionGuide = {
|
|
37
|
+
summary: string;
|
|
38
|
+
argumentNotes: string[];
|
|
39
|
+
dangerousDefaults?: string[];
|
|
40
|
+
examples?: Array<Record<string, unknown>>;
|
|
41
|
+
};
|
|
42
|
+
|
|
35
43
|
export function resolveManagedComposioIntegrations(
|
|
36
44
|
config: Pick<AgentConfig, "name" | "integrations">,
|
|
37
45
|
options: ManagedComposioResolutionOptions = {},
|
|
@@ -74,8 +82,13 @@ export function createManagedComposioTools(
|
|
|
74
82
|
|
|
75
83
|
const tool: AgentTool<ManagedComposioToolInput, ManagedComposioToolOutput> = {
|
|
76
84
|
name: MANAGED_COMPOSIO_TOOL_NAME,
|
|
77
|
-
description:
|
|
85
|
+
description: [
|
|
78
86
|
"Executes one explicitly allowed AgentKit-managed Composio action for this deployed agent. Use only for configured connected apps.",
|
|
87
|
+
integration.confirmExternalWrites === false
|
|
88
|
+
? "External write confirmation is disabled for this integration by agentkit.config.ts."
|
|
89
|
+
: "For write actions, pass confirmed: true only after the user confirms the exact external change.",
|
|
90
|
+
describeAllowedComposioActions(integration.allowedTools),
|
|
91
|
+
].join("\n\n"),
|
|
79
92
|
visibility: "user",
|
|
80
93
|
secrets: [MANAGED_COMPOSIO_API_KEY_SECRET],
|
|
81
94
|
permissions: integration.allowedTools.map((toolName) => `composio:${toolName.toLowerCase()}`),
|
|
@@ -91,7 +104,13 @@ export function createManagedComposioTools(
|
|
|
91
104
|
arguments: {
|
|
92
105
|
type: "object",
|
|
93
106
|
additionalProperties: true,
|
|
94
|
-
description:
|
|
107
|
+
description:
|
|
108
|
+
"Structured arguments for the selected Composio action. Use the action notes in the tool description; do not rely on Composio defaults for dates, times, durations, recipients, or write behavior.",
|
|
109
|
+
},
|
|
110
|
+
confirmed: {
|
|
111
|
+
type: "boolean",
|
|
112
|
+
description:
|
|
113
|
+
"Set true only after explicit user confirmation for external write actions such as create, update, delete, send, patch, move, insert, clear, remove, import, or quick add.",
|
|
95
114
|
},
|
|
96
115
|
},
|
|
97
116
|
required: ["toolSlug", "arguments"],
|
|
@@ -121,6 +140,9 @@ export function createManagedComposioTools(
|
|
|
121
140
|
);
|
|
122
141
|
}
|
|
123
142
|
|
|
143
|
+
validateComposioArguments(input.toolSlug, input.arguments);
|
|
144
|
+
validateComposioConfirmation(input.toolSlug, input.confirmed, integration);
|
|
145
|
+
|
|
124
146
|
const result = await executeComposioTool({
|
|
125
147
|
apiKey: ctx.secrets[MANAGED_COMPOSIO_API_KEY_SECRET],
|
|
126
148
|
userId: integration.userId,
|
|
@@ -173,6 +195,150 @@ function allowedComposioTools(integration: ComposioManagedIntegration): string[]
|
|
|
173
195
|
return Array.from(new Set(Object.values(integration.tools ?? {}).flat())).sort();
|
|
174
196
|
}
|
|
175
197
|
|
|
198
|
+
function describeAllowedComposioActions(actions: string[]): string {
|
|
199
|
+
const lines = ["Allowed Composio actions:"];
|
|
200
|
+
|
|
201
|
+
for (const action of actions) {
|
|
202
|
+
const guide = COMPOSIO_ACTION_GUIDES[action];
|
|
203
|
+
|
|
204
|
+
if (!guide) {
|
|
205
|
+
lines.push(`- ${action}: Pass explicit arguments from the current Composio action schema. Do not invent fields or rely on hidden defaults.`);
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
lines.push(`- ${action}: ${guide.summary}`);
|
|
210
|
+
|
|
211
|
+
for (const note of guide.argumentNotes) {
|
|
212
|
+
lines.push(` - ${note}`);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
for (const warning of guide.dangerousDefaults ?? []) {
|
|
216
|
+
lines.push(` - Dangerous default: ${warning}`);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
for (const example of guide.examples ?? []) {
|
|
220
|
+
lines.push(` - Example arguments: ${JSON.stringify(example)}`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return lines.join("\n");
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function validateComposioArguments(toolSlug: string, args: Record<string, unknown>): void {
|
|
228
|
+
if (toolSlug !== "GOOGLECALENDAR_CREATE_EVENT") {
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const start = args.start_datetime;
|
|
233
|
+
|
|
234
|
+
if (typeof start !== "string" || !isUtcDateTime(start)) {
|
|
235
|
+
throw new AgentKitError(
|
|
236
|
+
"composio_argument_invalid",
|
|
237
|
+
"GOOGLECALENDAR_CREATE_EVENT requires start_datetime as an explicit UTC RFC3339 timestamp ending in Z or +00:00. Convert user-local times using the runtime time zone before calling Composio.",
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const minutes = args.event_duration_minutes;
|
|
242
|
+
const hours = args.event_duration_hour;
|
|
243
|
+
|
|
244
|
+
if (!isPositiveNumber(minutes) && !isPositiveNumber(hours)) {
|
|
245
|
+
throw new AgentKitError(
|
|
246
|
+
"composio_argument_required",
|
|
247
|
+
"GOOGLECALENDAR_CREATE_EVENT requires explicit event_duration_minutes or event_duration_hour. Composio defaults event_duration_minutes to 30, so AgentKit blocks implicit duration.",
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function validateComposioConfirmation(
|
|
253
|
+
toolSlug: string,
|
|
254
|
+
confirmed: boolean | undefined,
|
|
255
|
+
integration: ComposioManagedIntegration,
|
|
256
|
+
): void {
|
|
257
|
+
if (integration.confirmExternalWrites === false || !isComposioWriteAction(toolSlug)) {
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (confirmed !== true) {
|
|
262
|
+
throw new AgentKitError(
|
|
263
|
+
"composio_confirmation_required",
|
|
264
|
+
`Composio action ${toolSlug} changes an external system. Ask the user to confirm the exact action and call again with confirmed: true.`,
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function isComposioWriteAction(toolSlug: string): boolean {
|
|
270
|
+
return /_(?:BATCH|CLEAR|CREATE|DELETE|DUPLICATE|IMPORT|INSERT|MOVE|PATCH|QUICK_ADD|REMOVE|REVOKE|SEND|UPDATE)(?:_|$)/.test(
|
|
271
|
+
toolSlug,
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function isUtcDateTime(value: string): boolean {
|
|
276
|
+
return /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|\+00:00)$/.test(value);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function isPositiveNumber(value: unknown): boolean {
|
|
280
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const COMPOSIO_ACTION_GUIDES: Record<string, ComposioActionGuide> = {
|
|
284
|
+
GOOGLECALENDAR_EVENTS_LIST: {
|
|
285
|
+
summary: "List events from a calendar before answering schedule/availability questions.",
|
|
286
|
+
argumentNotes: [
|
|
287
|
+
"calendarId is required by Composio; use primary unless the user selected a different calendar.",
|
|
288
|
+
"Use explicit timeMin and timeMax RFC3339 bounds for the requested window.",
|
|
289
|
+
"Pass timeZone when the user or business timezone matters.",
|
|
290
|
+
],
|
|
291
|
+
examples: [
|
|
292
|
+
{
|
|
293
|
+
calendarId: "primary",
|
|
294
|
+
timeMin: "2026-06-03T00:00:00-03:00",
|
|
295
|
+
timeMax: "2026-06-04T00:00:00-03:00",
|
|
296
|
+
singleEvents: true,
|
|
297
|
+
orderBy: "startTime",
|
|
298
|
+
timeZone: "America/Sao_Paulo",
|
|
299
|
+
},
|
|
300
|
+
],
|
|
301
|
+
},
|
|
302
|
+
GOOGLECALENDAR_CREATE_EVENT: {
|
|
303
|
+
summary: "Create an event only after confirming the exact title, date, start time, duration, timezone, and attendees/location when relevant.",
|
|
304
|
+
argumentNotes: [
|
|
305
|
+
"start_datetime is required and must be UTC RFC3339, for example 2026-06-03T15:00:00Z for 12:00 in America/Sao_Paulo.",
|
|
306
|
+
"calendar_id defaults to primary; pass it explicitly when known.",
|
|
307
|
+
"event_duration_minutes or event_duration_hour must be explicit.",
|
|
308
|
+
"Set timezone to the user/business IANA timezone when known.",
|
|
309
|
+
],
|
|
310
|
+
dangerousDefaults: [
|
|
311
|
+
"event_duration_minutes defaults to 30 if omitted.",
|
|
312
|
+
"calendar_id defaults to primary if omitted.",
|
|
313
|
+
],
|
|
314
|
+
examples: [
|
|
315
|
+
{
|
|
316
|
+
calendar_id: "primary",
|
|
317
|
+
summary: "Client call",
|
|
318
|
+
start_datetime: "2026-06-03T15:00:00Z",
|
|
319
|
+
event_duration_minutes: 60,
|
|
320
|
+
timezone: "America/Sao_Paulo",
|
|
321
|
+
},
|
|
322
|
+
],
|
|
323
|
+
},
|
|
324
|
+
GOOGLECALENDAR_UPDATE_EVENT: {
|
|
325
|
+
summary: "Update an existing event only after identifying the event and confirming the exact change.",
|
|
326
|
+
argumentNotes: [
|
|
327
|
+
"Read or find the event first so event_id/calendar_id are correct.",
|
|
328
|
+
"When changing time, use explicit UTC RFC3339 values and preserve duration unless the user changed it.",
|
|
329
|
+
"Do not infer cancellation/deletion from vague wording.",
|
|
330
|
+
],
|
|
331
|
+
},
|
|
332
|
+
GMAIL_FETCH_EMAILS: {
|
|
333
|
+
summary: "Fetch emails for read/search requests.",
|
|
334
|
+
argumentNotes: ["Use narrow query/window arguments when available; do not fetch broad mailbox history unnecessarily."],
|
|
335
|
+
},
|
|
336
|
+
GMAIL_SEND_EMAIL: {
|
|
337
|
+
summary: "Send email only after confirming recipients, subject, body, and attachments/links.",
|
|
338
|
+
argumentNotes: ["Pass recipient fields explicitly and avoid using draft-like placeholders in production sends."],
|
|
339
|
+
},
|
|
340
|
+
};
|
|
341
|
+
|
|
176
342
|
async function executeComposioTool(input: {
|
|
177
343
|
apiKey: string | undefined;
|
|
178
344
|
userId: string;
|
|
@@ -731,6 +731,22 @@ export class AgentKitConversationStore extends DurableObject {
|
|
|
731
731
|
return this.getMessages(url.searchParams.get("conversationId"));
|
|
732
732
|
}
|
|
733
733
|
|
|
734
|
+
if (request.method === "GET" && url.pathname === "/conversations") {
|
|
735
|
+
return this.listConversations();
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
const conversationTraceMatch = url.pathname.match(/^\\/conversations\\/([^/]+)\\/trace$/);
|
|
739
|
+
|
|
740
|
+
if (request.method === "GET" && conversationTraceMatch) {
|
|
741
|
+
return this.getConversationTrace(decodeURIComponent(conversationTraceMatch[1] || ""));
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
const conversationMatch = url.pathname.match(/^\\/conversations\\/([^/]+)$/);
|
|
745
|
+
|
|
746
|
+
if (request.method === "GET" && conversationMatch) {
|
|
747
|
+
return this.getConversation(decodeURIComponent(conversationMatch[1] || ""));
|
|
748
|
+
}
|
|
749
|
+
|
|
734
750
|
return Response.json({ error: { code: "not_found", message: "Unknown Durable Object route." } }, { status: 404 });
|
|
735
751
|
}
|
|
736
752
|
|
|
@@ -754,6 +770,128 @@ export class AgentKitConversationStore extends DurableObject {
|
|
|
754
770
|
});
|
|
755
771
|
}
|
|
756
772
|
|
|
773
|
+
listConversations() {
|
|
774
|
+
const rows = this.sql.exec(\`
|
|
775
|
+
SELECT conversations.id, conversations.agent_name, conversations.title, conversations.created_at, conversations.updated_at, COUNT(messages.id) AS message_count
|
|
776
|
+
FROM conversations
|
|
777
|
+
LEFT JOIN messages ON messages.conversation_id = conversations.id
|
|
778
|
+
GROUP BY conversations.id, conversations.agent_name, conversations.title, conversations.created_at, conversations.updated_at
|
|
779
|
+
ORDER BY conversations.updated_at DESC
|
|
780
|
+
\`).toArray();
|
|
781
|
+
|
|
782
|
+
return Response.json({
|
|
783
|
+
conversations: rows.map((row) => ({
|
|
784
|
+
id: String(row.id),
|
|
785
|
+
agentName: String(row.agent_name),
|
|
786
|
+
title: row.title === null || row.title === undefined ? null : String(row.title),
|
|
787
|
+
createdAt: String(row.created_at),
|
|
788
|
+
updatedAt: String(row.updated_at),
|
|
789
|
+
messageCount: Number(row.message_count || 0),
|
|
790
|
+
})),
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
getConversation(conversationId) {
|
|
795
|
+
const conversation = this.readConversation(conversationId);
|
|
796
|
+
|
|
797
|
+
if (!conversation) {
|
|
798
|
+
return Response.json(
|
|
799
|
+
{ error: { code: "conversation_not_found", message: \`Conversation "\${conversationId}" was not found.\` } },
|
|
800
|
+
{ status: 404 },
|
|
801
|
+
);
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
return Response.json(conversation);
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
getConversationTrace(conversationId) {
|
|
808
|
+
const conversation = this.readConversation(conversationId);
|
|
809
|
+
|
|
810
|
+
if (!conversation) {
|
|
811
|
+
return Response.json(
|
|
812
|
+
{ error: { code: "conversation_not_found", message: \`Conversation "\${conversationId}" was not found.\` } },
|
|
813
|
+
{ status: 404 },
|
|
814
|
+
);
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
const runs = this.sql.exec(
|
|
818
|
+
"SELECT id, conversation_id, provider, model, status, error_code, error_message, created_at, completed_at FROM runs WHERE conversation_id = ? ORDER BY rowid ASC",
|
|
819
|
+
conversationId,
|
|
820
|
+
).toArray();
|
|
821
|
+
|
|
822
|
+
return Response.json({
|
|
823
|
+
...conversation,
|
|
824
|
+
runs: runs.map((run) => ({
|
|
825
|
+
id: String(run.id),
|
|
826
|
+
conversationId: String(run.conversation_id),
|
|
827
|
+
provider: String(run.provider),
|
|
828
|
+
model: String(run.model),
|
|
829
|
+
status: String(run.status),
|
|
830
|
+
errorCode: run.error_code === null || run.error_code === undefined ? null : String(run.error_code),
|
|
831
|
+
errorMessage: run.error_message === null || run.error_message === undefined ? null : String(run.error_message),
|
|
832
|
+
startedAt: String(run.created_at),
|
|
833
|
+
completedAt: run.completed_at === null || run.completed_at === undefined ? null : String(run.completed_at),
|
|
834
|
+
toolCalls: this.readToolCalls(String(run.id)),
|
|
835
|
+
})),
|
|
836
|
+
});
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
readConversation(conversationId) {
|
|
840
|
+
if (!conversationId) {
|
|
841
|
+
return null;
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
const row = this.sql.exec(
|
|
845
|
+
"SELECT id, agent_name, title, created_at, updated_at FROM conversations WHERE id = ?",
|
|
846
|
+
conversationId,
|
|
847
|
+
).one();
|
|
848
|
+
|
|
849
|
+
if (!row) {
|
|
850
|
+
return null;
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
const messages = this.sql.exec(
|
|
854
|
+
"SELECT id, conversation_id, role, content, created_at FROM messages WHERE conversation_id = ? ORDER BY rowid ASC",
|
|
855
|
+
conversationId,
|
|
856
|
+
).toArray();
|
|
857
|
+
|
|
858
|
+
return {
|
|
859
|
+
id: String(row.id),
|
|
860
|
+
agentName: String(row.agent_name),
|
|
861
|
+
title: row.title === null || row.title === undefined ? null : String(row.title),
|
|
862
|
+
createdAt: String(row.created_at),
|
|
863
|
+
updatedAt: String(row.updated_at),
|
|
864
|
+
messages: messages.map((message) => ({
|
|
865
|
+
id: String(message.id),
|
|
866
|
+
conversationId: String(message.conversation_id),
|
|
867
|
+
role: String(message.role),
|
|
868
|
+
content: String(message.content),
|
|
869
|
+
createdAt: String(message.created_at),
|
|
870
|
+
})),
|
|
871
|
+
};
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
readToolCalls(runId) {
|
|
875
|
+
const rows = this.sql.exec(
|
|
876
|
+
"SELECT id, run_id, conversation_id, tool_name, visibility, status, input_json, output_json, error_message, created_at, completed_at FROM tool_calls WHERE run_id = ? ORDER BY rowid ASC",
|
|
877
|
+
runId,
|
|
878
|
+
).toArray();
|
|
879
|
+
|
|
880
|
+
return rows.map((row) => ({
|
|
881
|
+
id: String(row.id),
|
|
882
|
+
runId: String(row.run_id),
|
|
883
|
+
conversationId: String(row.conversation_id),
|
|
884
|
+
name: String(row.tool_name),
|
|
885
|
+
visibility: String(row.visibility),
|
|
886
|
+
status: String(row.status),
|
|
887
|
+
input: parseStoredJson(row.input_json, {}),
|
|
888
|
+
output: row.output_json === null || row.output_json === undefined ? undefined : parseStoredJson(row.output_json, null),
|
|
889
|
+
errorMessage: row.error_message === null || row.error_message === undefined ? null : String(row.error_message),
|
|
890
|
+
createdAt: String(row.created_at),
|
|
891
|
+
completedAt: row.completed_at === null || row.completed_at === undefined ? null : String(row.completed_at),
|
|
892
|
+
}));
|
|
893
|
+
}
|
|
894
|
+
|
|
757
895
|
persistMessages(input) {
|
|
758
896
|
const now = new Date().toISOString();
|
|
759
897
|
const conversationId = String(input.conversationId || crypto.randomUUID());
|
|
@@ -887,6 +1025,22 @@ export default {
|
|
|
887
1025
|
return handleChat(request, env);
|
|
888
1026
|
}
|
|
889
1027
|
|
|
1028
|
+
if (request.method === "GET" && (url.pathname === "/conversations" || url.pathname === "/v1/conversations")) {
|
|
1029
|
+
return handleConversationRead(request, env, "list");
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
const conversationTraceMatch = url.pathname.match(/^\\/(?:v1\\/)?conversations\\/([^/]+)\\/trace$/);
|
|
1033
|
+
|
|
1034
|
+
if (request.method === "GET" && conversationTraceMatch) {
|
|
1035
|
+
return handleConversationRead(request, env, "trace", decodeURIComponent(conversationTraceMatch[1] || ""));
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
const conversationMatch = url.pathname.match(/^\\/(?:v1\\/)?conversations\\/([^/]+)$/);
|
|
1039
|
+
|
|
1040
|
+
if (request.method === "GET" && conversationMatch) {
|
|
1041
|
+
return handleConversationRead(request, env, "show", decodeURIComponent(conversationMatch[1] || ""));
|
|
1042
|
+
}
|
|
1043
|
+
|
|
890
1044
|
if (request.method === "POST" && url.pathname.startsWith("/tools/")) {
|
|
891
1045
|
return handleTool(request, env, decodeURIComponent(url.pathname.slice("/tools/".length)));
|
|
892
1046
|
}
|
|
@@ -1233,6 +1387,39 @@ async function handleChat(request, env) {
|
|
|
1233
1387
|
});
|
|
1234
1388
|
}
|
|
1235
1389
|
|
|
1390
|
+
async function handleConversationRead(request, env, action, conversationId) {
|
|
1391
|
+
const auth = await authorizeChat(request, env, {});
|
|
1392
|
+
|
|
1393
|
+
if (auth) {
|
|
1394
|
+
return auth;
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
if (!env.AGENTKIT_CONVERSATIONS) {
|
|
1398
|
+
return Response.json({ error: { code: "conversations_not_configured", message: "Hosted conversation storage is not configured." } }, { status: 503 });
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
if (action === "list") {
|
|
1402
|
+
const stub = hostedConversationStore(env);
|
|
1403
|
+
return stub.fetch("https://agentkit.internal/conversations");
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
if (!conversationId) {
|
|
1407
|
+
return Response.json({ error: { code: "validation_error", message: "Missing conversation id." } }, { status: 400 });
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1410
|
+
const stub = hostedConversationStore(env);
|
|
1411
|
+
const suffix = action === "trace" ? "/trace" : "";
|
|
1412
|
+
const path = \`/conversations/\${encodeURIComponent(conversationId)}\${suffix}\`;
|
|
1413
|
+
const response = await stub.fetch(\`https://agentkit.internal\${path}\`);
|
|
1414
|
+
|
|
1415
|
+
if (response.status !== 404) {
|
|
1416
|
+
return response;
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
const legacyStub = legacyConversationStore(env, conversationId);
|
|
1420
|
+
return legacyStub.fetch(\`https://agentkit.internal\${path}\`);
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1236
1423
|
function readChatMessage(body) {
|
|
1237
1424
|
if (typeof body.message === "string") {
|
|
1238
1425
|
return body.message.trim();
|
|
@@ -2276,8 +2463,7 @@ async function persist(env, input) {
|
|
|
2276
2463
|
return {};
|
|
2277
2464
|
}
|
|
2278
2465
|
|
|
2279
|
-
const
|
|
2280
|
-
const stub = env.AGENTKIT_CONVERSATIONS.get(id);
|
|
2466
|
+
const stub = hostedConversationStore(env);
|
|
2281
2467
|
const response = await stub.fetch("https://agentkit.internal/messages", {
|
|
2282
2468
|
method: "POST",
|
|
2283
2469
|
body: JSON.stringify(input),
|
|
@@ -2295,8 +2481,7 @@ async function loadConversationMessages(env, conversationId) {
|
|
|
2295
2481
|
return [];
|
|
2296
2482
|
}
|
|
2297
2483
|
|
|
2298
|
-
const
|
|
2299
|
-
const stub = env.AGENTKIT_CONVERSATIONS.get(id);
|
|
2484
|
+
const stub = hostedConversationStore(env);
|
|
2300
2485
|
const response = await stub.fetch(
|
|
2301
2486
|
\`https://agentkit.internal/messages?conversationId=\${encodeURIComponent(conversationId)}\`,
|
|
2302
2487
|
);
|
|
@@ -2306,7 +2491,7 @@ async function loadConversationMessages(env, conversationId) {
|
|
|
2306
2491
|
}
|
|
2307
2492
|
|
|
2308
2493
|
const payload = await response.json();
|
|
2309
|
-
|
|
2494
|
+
const messages = Array.isArray(payload.messages)
|
|
2310
2495
|
? payload.messages.filter((message) =>
|
|
2311
2496
|
message &&
|
|
2312
2497
|
typeof message === "object" &&
|
|
@@ -2314,6 +2499,39 @@ async function loadConversationMessages(env, conversationId) {
|
|
|
2314
2499
|
typeof message.content === "string"
|
|
2315
2500
|
)
|
|
2316
2501
|
: [];
|
|
2502
|
+
|
|
2503
|
+
if (messages.length > 0) {
|
|
2504
|
+
return messages;
|
|
2505
|
+
}
|
|
2506
|
+
|
|
2507
|
+
const legacyStub = legacyConversationStore(env, conversationId);
|
|
2508
|
+
const legacyResponse = await legacyStub.fetch(
|
|
2509
|
+
\`https://agentkit.internal/messages?conversationId=\${encodeURIComponent(conversationId)}\`,
|
|
2510
|
+
);
|
|
2511
|
+
|
|
2512
|
+
if (!legacyResponse.ok) {
|
|
2513
|
+
return [];
|
|
2514
|
+
}
|
|
2515
|
+
|
|
2516
|
+
const legacyPayload = await legacyResponse.json();
|
|
2517
|
+
return Array.isArray(legacyPayload.messages)
|
|
2518
|
+
? legacyPayload.messages.filter((message) =>
|
|
2519
|
+
message &&
|
|
2520
|
+
typeof message === "object" &&
|
|
2521
|
+
(message.role === "user" || message.role === "assistant") &&
|
|
2522
|
+
typeof message.content === "string"
|
|
2523
|
+
)
|
|
2524
|
+
: [];
|
|
2525
|
+
}
|
|
2526
|
+
|
|
2527
|
+
function hostedConversationStore(env) {
|
|
2528
|
+
const id = env.AGENTKIT_CONVERSATIONS.idFromName("agentkit-conversations");
|
|
2529
|
+
return env.AGENTKIT_CONVERSATIONS.get(id);
|
|
2530
|
+
}
|
|
2531
|
+
|
|
2532
|
+
function legacyConversationStore(env, conversationId) {
|
|
2533
|
+
const id = env.AGENTKIT_CONVERSATIONS.idFromName(conversationId);
|
|
2534
|
+
return env.AGENTKIT_CONVERSATIONS.get(id);
|
|
2317
2535
|
}
|
|
2318
2536
|
|
|
2319
2537
|
function normalizeConversationId(value) {
|
|
@@ -2590,6 +2808,18 @@ function titleFromMessage(content) {
|
|
|
2590
2808
|
const compact = content.split(/\\r?\\n/, 1)[0].replace(/\\s+/g, " ").trim();
|
|
2591
2809
|
return compact.length <= 80 ? compact : \`\${compact.slice(0, 77)}...\`;
|
|
2592
2810
|
}
|
|
2811
|
+
|
|
2812
|
+
function parseStoredJson(value, fallback) {
|
|
2813
|
+
if (typeof value !== "string") {
|
|
2814
|
+
return fallback;
|
|
2815
|
+
}
|
|
2816
|
+
|
|
2817
|
+
try {
|
|
2818
|
+
return JSON.parse(value);
|
|
2819
|
+
} catch {
|
|
2820
|
+
return fallback;
|
|
2821
|
+
}
|
|
2822
|
+
}
|
|
2593
2823
|
`;
|
|
2594
2824
|
}
|
|
2595
2825
|
|
|
@@ -26,8 +26,13 @@ export default defineAgent({
|
|
|
26
26
|
toolkits: ["gmail", "googlecalendar"],
|
|
27
27
|
tools: {
|
|
28
28
|
gmail: ["GMAIL_FETCH_EMAILS", "GMAIL_SEND_EMAIL"],
|
|
29
|
-
googlecalendar: [
|
|
29
|
+
googlecalendar: [
|
|
30
|
+
"GOOGLECALENDAR_EVENTS_LIST",
|
|
31
|
+
"GOOGLECALENDAR_CREATE_EVENT",
|
|
32
|
+
"GOOGLECALENDAR_UPDATE_EVENT",
|
|
33
|
+
],
|
|
30
34
|
},
|
|
35
|
+
confirmExternalWrites: true,
|
|
31
36
|
}),
|
|
32
37
|
],
|
|
33
38
|
});
|
|
@@ -35,13 +40,17 @@ export default defineAgent({
|
|
|
35
40
|
|
|
36
41
|
Keep the action list explicit. Do not expose the whole Composio catalog by default.
|
|
37
42
|
|
|
43
|
+
For Google Calendar, do not configure create-only access. Include `GOOGLECALENDAR_EVENTS_LIST` so the agent can inspect availability before writing. For `GOOGLECALENDAR_CREATE_EVENT`, pass UTC `start_datetime` and explicit `event_duration_minutes` or `event_duration_hour`; AgentKit blocks Composio's implicit 30-minute duration default.
|
|
44
|
+
|
|
45
|
+
Managed Composio write actions require tool input `confirmed: true` by default. Set it only after the owner/user confirms the exact external change. Use `confirmExternalWrites: false` only when the capsule implements an equivalent confirmation guard elsewhere.
|
|
46
|
+
|
|
38
47
|
## Commands
|
|
39
48
|
|
|
40
49
|
```sh
|
|
41
50
|
npm run agentkit -- inspect
|
|
42
51
|
npm run agentkit -- deploy doctor
|
|
43
52
|
npm run agentkit -- deploy
|
|
44
|
-
npm run agentkit -- integrations status
|
|
53
|
+
npm run agentkit -- integrations status --toolkit googlecalendar
|
|
45
54
|
npm run agentkit -- integrations connect composio --toolkit gmail
|
|
46
55
|
```
|
|
47
56
|
|