strapi-plugin-ai-sdk 0.7.7 → 0.8.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.
Files changed (32) hide show
  1. package/dist/_chunks/{App-joEmdpxi.js → App-DTbAnq7K.js} +710 -51
  2. package/dist/_chunks/{App-DMdQymB3.mjs → App-DZVBmpvs.mjs} +712 -53
  3. package/dist/_chunks/{index-B7qLITWV.js → index-C3xn2IND.js} +1 -1
  4. package/dist/_chunks/{index-DrLcqX__.mjs → index-Ca2F2wBp.mjs} +1 -1
  5. package/dist/admin/index.js +1 -1
  6. package/dist/admin/index.mjs +1 -1
  7. package/dist/admin/src/components/NotePanel.d.ts +15 -0
  8. package/dist/admin/src/hooks/useNotes.d.ts +22 -0
  9. package/dist/admin/src/pages/NoteStorePage.d.ts +2 -0
  10. package/dist/admin/src/utils/notes-api.d.ts +28 -0
  11. package/dist/server/index.js +536 -121
  12. package/dist/server/index.mjs +536 -121
  13. package/dist/server/src/content-types/index.d.ts +46 -0
  14. package/dist/server/src/content-types/note/index.d.ts +47 -0
  15. package/dist/server/src/controllers/index.d.ts +9 -0
  16. package/dist/server/src/controllers/note.d.ts +12 -0
  17. package/dist/server/src/index.d.ts +55 -0
  18. package/dist/server/src/lib/trim-messages.d.ts +11 -0
  19. package/dist/server/src/mcp/resources/tool-guide.d.ts +5 -0
  20. package/dist/server/src/tool-logic/aggregate-content.d.ts +1 -1
  21. package/dist/server/src/tool-logic/create-content.d.ts +1 -1
  22. package/dist/server/src/tool-logic/find-one-content.d.ts +1 -1
  23. package/dist/server/src/tool-logic/index.d.ts +4 -0
  24. package/dist/server/src/tool-logic/list-content-types.d.ts +1 -1
  25. package/dist/server/src/tool-logic/manage-task.d.ts +1 -1
  26. package/dist/server/src/tool-logic/recall-notes.d.ts +22 -0
  27. package/dist/server/src/tool-logic/save-note.d.ts +17 -0
  28. package/dist/server/src/tool-logic/update-content.d.ts +1 -1
  29. package/dist/server/src/tool-logic/upload-media.d.ts +1 -1
  30. package/dist/server/src/tools/definitions/recall-notes.d.ts +2 -0
  31. package/dist/server/src/tools/definitions/save-note.d.ts +2 -0
  32. package/package.json +1 -1
@@ -10,6 +10,90 @@ import * as fs from "node:fs";
10
10
  import * as path from "node:path";
11
11
  import { randomUUID } from "node:crypto";
12
12
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
13
+ function toSnakeCase$1(str) {
14
+ return str.replace(/:/g, "__").replace(/-/g, "_").replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
15
+ }
16
+ function extractType(field) {
17
+ const def = field?._zod?.def;
18
+ if (!def) return "unknown";
19
+ switch (def.type) {
20
+ case "string":
21
+ return "string";
22
+ case "number":
23
+ return "number";
24
+ case "boolean":
25
+ return "boolean";
26
+ case "enum":
27
+ return Object.keys(def.entries).join(" | ");
28
+ case "optional":
29
+ return extractType({ _zod: { def: def.innerType } });
30
+ case "default":
31
+ return extractType({ _zod: { def: def.innerType } });
32
+ case "record":
33
+ return "object";
34
+ case "array":
35
+ return "array";
36
+ case "union":
37
+ return "string | array | object";
38
+ default:
39
+ return def.type || "unknown";
40
+ }
41
+ }
42
+ function formatParams(schema2) {
43
+ const shape = schema2.shape;
44
+ if (!shape || Object.keys(shape).length === 0) {
45
+ return "_No parameters._\n";
46
+ }
47
+ const rows = [];
48
+ rows.push("| Parameter | Type | Required | Description |");
49
+ rows.push("|-----------|------|----------|-------------|");
50
+ for (const [name, field] of Object.entries(shape)) {
51
+ const required = !field.isOptional?.();
52
+ const type = extractType(field);
53
+ const desc = (field.description || "").replace(/\|/g, "\\|");
54
+ rows.push(`| ${name} | ${type} | ${required ? "Yes" : "No"} | ${desc} |`);
55
+ }
56
+ return rows.join("\n") + "\n";
57
+ }
58
+ function generateToolGuide(registry) {
59
+ const tools = registry.getPublic();
60
+ const sources = registry.getToolSources();
61
+ const sections = [];
62
+ sections.push("# Strapi AI Tools Guide\n");
63
+ sections.push("## Getting Started\n");
64
+ sections.push(
65
+ "Start with `list_content_types` to discover your content types and their fields, then use `search_content` to query them.\n"
66
+ );
67
+ for (const source of sources) {
68
+ const heading = source.id === "built-in" ? "Built-in Tools" : source.label;
69
+ sections.push(`## ${heading}
70
+ `);
71
+ for (const toolName of source.tools) {
72
+ const def = tools.get(toolName);
73
+ if (!def) continue;
74
+ const mcpName = toSnakeCase$1(toolName);
75
+ sections.push(`### ${mcpName}
76
+ `);
77
+ sections.push(`${def.description}
78
+ `);
79
+ sections.push(formatParams(def.schema));
80
+ }
81
+ }
82
+ sections.push("## Common Workflows\n");
83
+ sections.push("### Create a blog post\n");
84
+ sections.push(
85
+ "1. Call `list_content_types` to discover the article/blog content type and its fields\n2. (Optional) Call `upload_media` with a URL to upload a cover image\n3. Call `create_content` with the content type UID and field data\n"
86
+ );
87
+ sections.push("### Find and update content\n");
88
+ sections.push(
89
+ "1. Call `search_content` with filters or a query to find the document\n2. Note the `documentId` from the results\n3. Call `update_content` with the content type, documentId, and fields to change\n"
90
+ );
91
+ sections.push("### Content analytics\n");
92
+ sections.push(
93
+ "1. Call `aggregate_content` with operation `count` for totals\n2. Use `countByField` with `groupByField` to see distribution (e.g. articles per category)\n3. Use `countByDateRange` with `granularity` to see trends over time\n"
94
+ );
95
+ return sections.join("\n");
96
+ }
13
97
  function toSnakeCase(str) {
14
98
  return str.replace(/:/g, "__").replace(/-/g, "_").replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
15
99
  }
@@ -26,7 +110,8 @@ function createMcpServer(strapi) {
26
110
  },
27
111
  {
28
112
  capabilities: {
29
- tools: {}
113
+ tools: {},
114
+ resources: {}
30
115
  }
31
116
  }
32
117
  );
@@ -54,6 +139,24 @@ function createMcpServer(strapi) {
54
139
  }
55
140
  );
56
141
  }
142
+ const guideMarkdown = generateToolGuide(registry);
143
+ server.registerResource(
144
+ "Tool Guide",
145
+ "strapi://tools/guide",
146
+ {
147
+ description: "Complete guide to all available Strapi AI tools with parameters and usage examples",
148
+ mimeType: "text/markdown"
149
+ },
150
+ async () => ({
151
+ contents: [
152
+ {
153
+ uri: "strapi://tools/guide",
154
+ mimeType: "text/markdown",
155
+ text: guideMarkdown
156
+ }
157
+ ]
158
+ })
159
+ );
57
160
  strapi.log.info("[ai-sdk:mcp] MCP server created with tools:", { tools: toolNames });
58
161
  return server;
59
162
  }
@@ -219,7 +322,7 @@ class ToolRegistry {
219
322
  }
220
323
  }
221
324
  const listContentTypesSchema = z.object({});
222
- const listContentTypesDescription = "List all Strapi content types and components with their fields, relations, and structure.";
325
+ const listContentTypesDescription = 'List all Strapi content types and components with their fields, relations, and structure. This is the starting point for any content operation — call it first to discover content type UIDs (e.g. "api::article.article"), field names, relation targets, and components. No parameters required. Results are cached.';
223
326
  const INTERNAL_FIELDS = /* @__PURE__ */ new Set([
224
327
  "createdAt",
225
328
  "updatedAt",
@@ -373,7 +476,7 @@ const createContentSchema = z.object({
373
476
  status: z.enum(["draft", "published"]).optional().describe("Document status. Defaults to draft."),
374
477
  locale: z.string().optional().describe('Locale code for i18n content, e.g. "en" or "fr"')
375
478
  });
376
- const createContentDescription = "Create a new document in any Strapi content type (articles, blog posts, pages, etc.). Use listContentTypes first to discover available content types and their fields.";
479
+ const createContentDescription = "Create a new document in any Strapi content type. Requires contentType (UID) and data (field values). Call listContentTypes first to discover UIDs and required fields. To attach media, upload it first with uploadMedia, then pass the file ID in data. Returns the created document with all fields populated.";
377
480
  async function createContent(strapi, params) {
378
481
  const { contentType, data, status, locale } = params;
379
482
  if (!strapi.contentTypes[contentType]) {
@@ -395,7 +498,7 @@ const updateContentSchema = z.object({
395
498
  status: z.enum(["draft", "published"]).optional().describe("Document status. Defaults to draft."),
396
499
  locale: z.string().optional().describe('Locale code for i18n content, e.g. "en" or "fr"')
397
500
  });
398
- const updateContentDescription = "Update an existing document in any Strapi content type. Use searchContent to find the document ID first.";
501
+ const updateContentDescription = "Update an existing document in any Strapi content type. Requires contentType, documentId, and data (fields to change — only include fields you want to modify). Typical workflow: searchContent to find the documentId, then updateContent to modify it. Returns the updated document.";
399
502
  async function updateContent(strapi, params) {
400
503
  const { contentType, documentId, data, status, locale } = params;
401
504
  if (!strapi.contentTypes[contentType]) {
@@ -468,7 +571,7 @@ async function sendEmail(strapi, params) {
468
571
  subject: params.subject
469
572
  };
470
573
  }
471
- const CONTENT_TYPE$7 = "plugin::ai-sdk.memory";
574
+ const CONTENT_TYPE$a = "plugin::ai-sdk.memory";
472
575
  const saveMemorySchema = z.object({
473
576
  content: z.string().describe('A short, factual statement to remember about the user (e.g. "User prefers dark mode", "Company name is Acme Corp")'),
474
577
  category: z.string().optional().describe('Category for the memory (e.g. "preference", "project", "personal", "general"). Defaults to "general".')
@@ -479,7 +582,7 @@ async function saveMemory(strapi, params, context) {
479
582
  return { success: false, message: "Cannot save memory: user context not available." };
480
583
  }
481
584
  try {
482
- await strapi.documents(CONTENT_TYPE$7).create({
585
+ await strapi.documents(CONTENT_TYPE$a).create({
483
586
  data: {
484
587
  content: params.content,
485
588
  category: params.category || "general",
@@ -492,7 +595,7 @@ async function saveMemory(strapi, params, context) {
492
595
  return { success: false, message: `Failed to save memory: ${detail}` };
493
596
  }
494
597
  }
495
- const CONTENT_TYPE$6 = "plugin::ai-sdk.memory";
598
+ const CONTENT_TYPE$9 = "plugin::ai-sdk.memory";
496
599
  const recallMemoriesSchema = z.object({
497
600
  query: z.string().optional().describe("Optional search term to filter memories by content"),
498
601
  category: z.string().optional().describe('Optional category to filter by (e.g. "preference", "project", "personal")')
@@ -510,7 +613,7 @@ async function recallMemories(strapi, params, context) {
510
613
  if (params.query) {
511
614
  filters.content = { $containsi: params.query };
512
615
  }
513
- const memories = await strapi.documents(CONTENT_TYPE$6).findMany({
616
+ const memories = await strapi.documents(CONTENT_TYPE$9).findMany({
514
617
  filters,
515
618
  fields: ["content", "category"],
516
619
  sort: { createdAt: "desc" }
@@ -536,7 +639,7 @@ const findOneContentSchema = z.object({
536
639
  status: z.enum(["draft", "published"]).optional().describe("Document status filter."),
537
640
  locale: z.string().optional().describe('Locale code for i18n content, e.g. "en" or "fr"')
538
641
  });
539
- const findOneContentDescription = "Fetch a single document by its documentId from any Strapi content type. Returns the full document with all fields and populated relations. Use searchContent first to discover document IDs.";
642
+ const findOneContentDescription = "Fetch a single document by its exact documentId. Use this when you already have a documentId and need full details with populated relations. If you need to find documents by title, field values, or full-text search, use searchContent instead. Requires contentType and documentId.";
540
643
  async function findOneContent(strapi, params) {
541
644
  const { contentType, documentId, populate = "*", fields, status, locale } = params;
542
645
  if (!strapi.contentTypes[contentType]) {
@@ -562,7 +665,7 @@ const uploadMediaSchema = z.object({
562
665
  caption: z.string().optional().describe("Caption for the media file"),
563
666
  alternativeText: z.string().optional().describe("Alternative text for accessibility")
564
667
  });
565
- const uploadMediaDescription = "Upload a media file from a URL to the Strapi media library. Returns the uploaded file data. To link media to a content type field, use createContent or updateContent with the file ID.";
668
+ const uploadMediaDescription = "Upload a media file from a URL to the Strapi media library. This is step 1 of a two-step process: (1) upload the file with this tool to get a file ID, then (2) link it to a content entry using createContent or updateContent with { fieldName: fileId }. Requires url. Optional: name, caption, alternativeText.";
566
669
  async function uploadMedia(strapi, params) {
567
670
  const { url, name, caption, alternativeText } = params;
568
671
  let parsedUrl;
@@ -633,7 +736,7 @@ async function uploadMedia(strapi, params) {
633
736
  usage: `To link this file to a content type field, use createContent or updateContent with: { "fieldName": ${uploadedFile.id} }`
634
737
  };
635
738
  }
636
- const CONTENT_TYPE$5 = "plugin::ai-sdk.public-memory";
739
+ const CONTENT_TYPE$8 = "plugin::ai-sdk.public-memory";
637
740
  const recallPublicMemoriesSchema = z.object({
638
741
  query: z.string().optional().describe("Optional search term to filter public memories by content"),
639
742
  category: z.string().optional().describe('Optional category to filter by (e.g. "faq", "product", "policy")')
@@ -644,7 +747,7 @@ async function recallPublicMemories(strapi, params) {
644
747
  const filters = {};
645
748
  if (params.category) filters.category = params.category;
646
749
  if (params.query) filters.content = { $containsi: params.query };
647
- const memories = await strapi.documents(CONTENT_TYPE$5).findMany({
750
+ const memories = await strapi.documents(CONTENT_TYPE$8).findMany({
648
751
  filters,
649
752
  fields: ["content", "category"],
650
753
  sort: { createdAt: "desc" }
@@ -726,7 +829,7 @@ const aggregateContentSchema = z.object({
726
829
  status: z.enum(["draft", "published"]).optional().describe("Filter by document status"),
727
830
  locale: z.string().optional().describe("Locale code for i18n content")
728
831
  });
729
- const aggregateContentDescription = 'Aggregate and count content entries. Use for analytics questions like "how many articles per category", "content trends by month", or total counts with filters. Prefer this over searchContent for counting and grouping.';
832
+ const aggregateContentDescription = 'Count and group content entries for analytics. Answers questions like "how many articles?", "posts per category", "content published by month". Three operations: count (total), countByField (group by a field), countByDateRange (bucket by day/week/month). Prefer this over searchContent when you need counts or distributions, not the documents themselves.';
730
833
  function buildBaseQuery(params) {
731
834
  const { filters, status, locale, dateFrom, dateTo, dateField = "createdAt" } = params;
732
835
  const query = {};
@@ -878,7 +981,7 @@ async function aggregateContent(strapi, params) {
878
981
  throw new Error(`Unknown operation: ${operation}`);
879
982
  }
880
983
  }
881
- const CONTENT_TYPE$4 = "plugin::ai-sdk.task";
984
+ const CONTENT_TYPE$7 = "plugin::ai-sdk.task";
882
985
  const manageTaskSchema = z.object({
883
986
  action: z.enum(["create", "update", "complete", "list", "summary"]).describe(
884
987
  "Action to perform: create a new task, update an existing task, complete (mark done), list open tasks, or get a summary."
@@ -894,14 +997,14 @@ const manageTaskSchema = z.object({
894
997
  done: z.boolean().optional().describe("Set done status explicitly (for update action)."),
895
998
  filters: z.record(z.string(), z.unknown()).optional().describe("Additional Strapi filters for list action.")
896
999
  });
897
- const manageTaskDescription = "Manage the user's task list. Create, update, complete, list, or summarize tasks. Tasks are scored by consequence × impact to help prioritize what matters most.";
1000
+ const manageTaskDescription = "Manage the user's task list. Actions: create (new task), update (modify fields), complete (mark done), list (open tasks sorted by priority score), summary (overview with overdue/urgent counts). Tasks are scored by consequence × impact to prioritize what matters most. For create: title is required; consequence and impact are collected via UI form if omitted.";
898
1001
  async function resolveTask(strapi, adminUserId, documentId, title) {
899
1002
  if (documentId) {
900
- const task2 = await strapi.documents(CONTENT_TYPE$4).findOne({ documentId });
1003
+ const task2 = await strapi.documents(CONTENT_TYPE$7).findOne({ documentId });
901
1004
  if (task2 && task2.adminUserId === adminUserId) return task2;
902
1005
  }
903
1006
  if (title) {
904
- const tasks = await strapi.documents(CONTENT_TYPE$4).findMany({
1007
+ const tasks = await strapi.documents(CONTENT_TYPE$7).findMany({
905
1008
  filters: {
906
1009
  adminUserId,
907
1010
  title: { $containsi: title }
@@ -954,7 +1057,7 @@ async function manageTask(strapi, params, context) {
954
1057
  if (params.consequence !== void 0) data.consequence = params.consequence;
955
1058
  if (params.impact !== void 0) data.impact = params.impact;
956
1059
  if (params.dueDate !== void 0) data.dueDate = params.dueDate;
957
- const updated = await strapi.documents(CONTENT_TYPE$4).update({
1060
+ const updated = await strapi.documents(CONTENT_TYPE$7).update({
958
1061
  documentId: duplicate.documentId,
959
1062
  data
960
1063
  });
@@ -965,7 +1068,7 @@ async function manageTask(strapi, params, context) {
965
1068
  data: updated
966
1069
  };
967
1070
  }
968
- const task2 = await strapi.documents(CONTENT_TYPE$4).create({
1071
+ const task2 = await strapi.documents(CONTENT_TYPE$7).create({
969
1072
  data: {
970
1073
  title: params.title,
971
1074
  description: params.description,
@@ -994,7 +1097,7 @@ async function manageTask(strapi, params, context) {
994
1097
  for (const key of ["title", "description", "content", "priority", "consequence", "impact", "dueDate", "done"]) {
995
1098
  if (params[key] !== void 0) data[key] = params[key];
996
1099
  }
997
- const updated = await strapi.documents(CONTENT_TYPE$4).update({
1100
+ const updated = await strapi.documents(CONTENT_TYPE$7).update({
998
1101
  documentId: existing.documentId,
999
1102
  data
1000
1103
  });
@@ -1005,7 +1108,7 @@ async function manageTask(strapi, params, context) {
1005
1108
  if (!toComplete) {
1006
1109
  return { success: false, message: "Task not found. Provide a documentId or a title to search by." };
1007
1110
  }
1008
- await strapi.documents(CONTENT_TYPE$4).update({
1111
+ await strapi.documents(CONTENT_TYPE$7).update({
1009
1112
  documentId: toComplete.documentId,
1010
1113
  data: { done: true }
1011
1114
  });
@@ -1017,7 +1120,7 @@ async function manageTask(strapi, params, context) {
1017
1120
  done: false,
1018
1121
  ...params.filters
1019
1122
  };
1020
- const tasks = await strapi.documents(CONTENT_TYPE$4).findMany({
1123
+ const tasks = await strapi.documents(CONTENT_TYPE$7).findMany({
1021
1124
  filters
1022
1125
  });
1023
1126
  const sorted = tasks.sort((a, b) => {
@@ -1046,7 +1149,7 @@ async function manageTask(strapi, params, context) {
1046
1149
  };
1047
1150
  }
1048
1151
  case "summary": {
1049
- const allOpen = await strapi.documents(CONTENT_TYPE$4).findMany({
1152
+ const allOpen = await strapi.documents(CONTENT_TYPE$7).findMany({
1050
1153
  filters: { adminUserId, done: false }
1051
1154
  });
1052
1155
  const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
@@ -1083,6 +1186,82 @@ async function manageTask(strapi, params, context) {
1083
1186
  return { success: false, message: `Task operation failed: ${detail}` };
1084
1187
  }
1085
1188
  }
1189
+ const CONTENT_TYPE$6 = "plugin::ai-sdk.note";
1190
+ const saveNoteSchema = z.object({
1191
+ title: z.string().optional().describe("A short title or label for the note"),
1192
+ content: z.string().describe("The note content in markdown format. Can include code blocks, lists, links, etc."),
1193
+ category: z.string().optional().describe('Category for the note: "research", "snippet", "idea", or "reference". Defaults to "research".'),
1194
+ tags: z.string().optional().describe('Comma-separated tags for filtering (e.g. "strapi, api, tutorial")'),
1195
+ source: z.string().optional().describe('Where this note came from (e.g. "conversation", "web research", a URL)')
1196
+ });
1197
+ const saveNoteDescription = "Save a research note, code snippet, idea, or reference for the user. Use this when the user wants to save part of a conversation, research findings, code examples, or any content they may want to use later for writing blog posts or articles. Content is saved as markdown.";
1198
+ async function saveNote(strapi, params, context) {
1199
+ if (!context?.adminUserId) {
1200
+ return { success: false, message: "Cannot save note: user context not available." };
1201
+ }
1202
+ try {
1203
+ await strapi.documents(CONTENT_TYPE$6).create({
1204
+ data: {
1205
+ title: params.title || "",
1206
+ content: params.content,
1207
+ category: params.category || "research",
1208
+ tags: params.tags || "",
1209
+ source: params.source || "conversation",
1210
+ adminUserId: context.adminUserId
1211
+ }
1212
+ });
1213
+ const label = params.title ? `"${params.title}"` : "Note";
1214
+ return { success: true, message: `${label} saved to research notes.` };
1215
+ } catch (error) {
1216
+ const detail = error instanceof Error ? error.message : String(error);
1217
+ return { success: false, message: `Failed to save note: ${detail}` };
1218
+ }
1219
+ }
1220
+ const CONTENT_TYPE$5 = "plugin::ai-sdk.note";
1221
+ const recallNotesSchema = z.object({
1222
+ query: z.string().optional().describe("Optional search term to filter notes by title or content"),
1223
+ category: z.string().optional().describe('Optional category to filter by: "research", "snippet", "idea", "reference"'),
1224
+ tag: z.string().optional().describe("Optional tag to filter by (matches within comma-separated tags)")
1225
+ });
1226
+ const recallNotesDescription = "Recall saved research notes, snippets, ideas, and references. Use this to look up previously saved notes. Without parameters, returns all notes.";
1227
+ async function recallNotes(strapi, params, context) {
1228
+ if (!context?.adminUserId) {
1229
+ return { success: false, notes: [], count: 0 };
1230
+ }
1231
+ try {
1232
+ const filters = { adminUserId: context.adminUserId };
1233
+ if (params.category) {
1234
+ filters.category = params.category;
1235
+ }
1236
+ if (params.query) {
1237
+ filters.$or = [
1238
+ { title: { $containsi: params.query } },
1239
+ { content: { $containsi: params.query } }
1240
+ ];
1241
+ }
1242
+ if (params.tag) {
1243
+ filters.tags = { $containsi: params.tag };
1244
+ }
1245
+ const notes = await strapi.documents(CONTENT_TYPE$5).findMany({
1246
+ filters,
1247
+ fields: ["title", "content", "category", "tags", "source"],
1248
+ sort: { createdAt: "desc" }
1249
+ });
1250
+ return {
1251
+ success: true,
1252
+ notes: notes.map((n) => ({
1253
+ title: n.title || "",
1254
+ content: n.content,
1255
+ category: n.category,
1256
+ tags: n.tags || "",
1257
+ source: n.source || ""
1258
+ })),
1259
+ count: notes.length
1260
+ };
1261
+ } catch (error) {
1262
+ return { success: false, notes: [], count: 0 };
1263
+ }
1264
+ }
1086
1265
  const listContentTypesTool = {
1087
1266
  name: "listContentTypes",
1088
1267
  description: listContentTypesDescription,
@@ -1213,6 +1392,20 @@ const manageTaskTool = {
1213
1392
  execute: async (args, strapi, context) => manageTask(strapi, args, context),
1214
1393
  internal: true
1215
1394
  };
1395
+ const saveNoteTool = {
1396
+ name: "saveNote",
1397
+ description: saveNoteDescription,
1398
+ schema: saveNoteSchema,
1399
+ execute: async (args, strapi, context) => saveNote(strapi, args, context),
1400
+ internal: true
1401
+ };
1402
+ const recallNotesTool = {
1403
+ name: "recallNotes",
1404
+ description: recallNotesDescription,
1405
+ schema: recallNotesSchema,
1406
+ execute: async (args, strapi, context) => recallNotes(strapi, args, context),
1407
+ internal: true
1408
+ };
1216
1409
  const builtInTools = [
1217
1410
  listContentTypesTool,
1218
1411
  searchContentTool,
@@ -1225,7 +1418,9 @@ const builtInTools = [
1225
1418
  recallMemoriesTool,
1226
1419
  recallPublicMemoriesTool,
1227
1420
  aggregateContentTool,
1228
- manageTaskTool
1421
+ manageTaskTool,
1422
+ saveNoteTool,
1423
+ recallNotesTool
1229
1424
  ];
1230
1425
  const PLUGIN_ID$2 = "ai-sdk";
1231
1426
  const bootstrap = ({ strapi }) => {
@@ -1396,17 +1591,17 @@ const config = {
1396
1591
  }
1397
1592
  }
1398
1593
  };
1399
- const kind$3 = "collectionType";
1400
- const collectionName$3 = "ai_sdk_conversations";
1401
- const info$3 = {
1594
+ const kind$4 = "collectionType";
1595
+ const collectionName$4 = "ai_sdk_conversations";
1596
+ const info$4 = {
1402
1597
  singularName: "conversation",
1403
1598
  pluralName: "conversations",
1404
1599
  displayName: "AI Conversation"
1405
1600
  };
1406
- const options$3 = {
1601
+ const options$4 = {
1407
1602
  draftAndPublish: false
1408
1603
  };
1409
- const pluginOptions$3 = {
1604
+ const pluginOptions$4 = {
1410
1605
  "content-manager": {
1411
1606
  visible: false
1412
1607
  },
@@ -1414,7 +1609,7 @@ const pluginOptions$3 = {
1414
1609
  visible: false
1415
1610
  }
1416
1611
  };
1417
- const attributes$3 = {
1612
+ const attributes$4 = {
1418
1613
  title: {
1419
1614
  type: "string",
1420
1615
  required: true,
@@ -1429,26 +1624,26 @@ const attributes$3 = {
1429
1624
  required: true
1430
1625
  }
1431
1626
  };
1432
- const schema$3 = {
1433
- kind: kind$3,
1434
- collectionName: collectionName$3,
1435
- info: info$3,
1436
- options: options$3,
1437
- pluginOptions: pluginOptions$3,
1438
- attributes: attributes$3
1627
+ const schema$4 = {
1628
+ kind: kind$4,
1629
+ collectionName: collectionName$4,
1630
+ info: info$4,
1631
+ options: options$4,
1632
+ pluginOptions: pluginOptions$4,
1633
+ attributes: attributes$4
1439
1634
  };
1440
- const conversation = { schema: schema$3 };
1441
- const kind$2 = "collectionType";
1442
- const collectionName$2 = "ai_sdk_memories";
1443
- const info$2 = {
1635
+ const conversation = { schema: schema$4 };
1636
+ const kind$3 = "collectionType";
1637
+ const collectionName$3 = "ai_sdk_memories";
1638
+ const info$3 = {
1444
1639
  singularName: "memory",
1445
1640
  pluralName: "memories",
1446
1641
  displayName: "AI Memory"
1447
1642
  };
1448
- const options$2 = {
1643
+ const options$3 = {
1449
1644
  draftAndPublish: false
1450
1645
  };
1451
- const pluginOptions$2 = {
1646
+ const pluginOptions$3 = {
1452
1647
  "content-manager": {
1453
1648
  visible: false
1454
1649
  },
@@ -1456,9 +1651,9 @@ const pluginOptions$2 = {
1456
1651
  visible: false
1457
1652
  }
1458
1653
  };
1459
- const attributes$2 = {
1654
+ const attributes$3 = {
1460
1655
  content: {
1461
- type: "text",
1656
+ type: "richtext",
1462
1657
  required: true
1463
1658
  },
1464
1659
  category: {
@@ -1476,26 +1671,26 @@ const attributes$2 = {
1476
1671
  required: true
1477
1672
  }
1478
1673
  };
1479
- const schema$2 = {
1480
- kind: kind$2,
1481
- collectionName: collectionName$2,
1482
- info: info$2,
1483
- options: options$2,
1484
- pluginOptions: pluginOptions$2,
1485
- attributes: attributes$2
1674
+ const schema$3 = {
1675
+ kind: kind$3,
1676
+ collectionName: collectionName$3,
1677
+ info: info$3,
1678
+ options: options$3,
1679
+ pluginOptions: pluginOptions$3,
1680
+ attributes: attributes$3
1486
1681
  };
1487
- const memory = { schema: schema$2 };
1488
- const kind$1 = "collectionType";
1489
- const collectionName$1 = "ai_sdk_public_memories";
1490
- const info$1 = {
1682
+ const memory = { schema: schema$3 };
1683
+ const kind$2 = "collectionType";
1684
+ const collectionName$2 = "ai_sdk_public_memories";
1685
+ const info$2 = {
1491
1686
  singularName: "public-memory",
1492
1687
  pluralName: "public-memories",
1493
1688
  displayName: "AI Public Memory"
1494
1689
  };
1495
- const options$1 = {
1690
+ const options$2 = {
1496
1691
  draftAndPublish: false
1497
1692
  };
1498
- const pluginOptions$1 = {
1693
+ const pluginOptions$2 = {
1499
1694
  "content-manager": {
1500
1695
  visible: false
1501
1696
  },
@@ -1503,7 +1698,7 @@ const pluginOptions$1 = {
1503
1698
  visible: false
1504
1699
  }
1505
1700
  };
1506
- const attributes$1 = {
1701
+ const attributes$2 = {
1507
1702
  content: {
1508
1703
  type: "text",
1509
1704
  required: true
@@ -1519,26 +1714,26 @@ const attributes$1 = {
1519
1714
  "default": "general"
1520
1715
  }
1521
1716
  };
1522
- const schema$1 = {
1523
- kind: kind$1,
1524
- collectionName: collectionName$1,
1525
- info: info$1,
1526
- options: options$1,
1527
- pluginOptions: pluginOptions$1,
1528
- attributes: attributes$1
1717
+ const schema$2 = {
1718
+ kind: kind$2,
1719
+ collectionName: collectionName$2,
1720
+ info: info$2,
1721
+ options: options$2,
1722
+ pluginOptions: pluginOptions$2,
1723
+ attributes: attributes$2
1529
1724
  };
1530
- const publicMemory = { schema: schema$1 };
1531
- const kind = "collectionType";
1532
- const collectionName = "ai_sdk_tasks";
1533
- const info = {
1725
+ const publicMemory = { schema: schema$2 };
1726
+ const kind$1 = "collectionType";
1727
+ const collectionName$1 = "ai_sdk_tasks";
1728
+ const info$1 = {
1534
1729
  singularName: "task",
1535
1730
  pluralName: "tasks",
1536
1731
  displayName: "AI Task"
1537
1732
  };
1538
- const options = {
1733
+ const options$1 = {
1539
1734
  draftAndPublish: false
1540
1735
  };
1541
- const pluginOptions = {
1736
+ const pluginOptions$1 = {
1542
1737
  "content-manager": {
1543
1738
  visible: true
1544
1739
  },
@@ -1546,7 +1741,7 @@ const pluginOptions = {
1546
1741
  visible: false
1547
1742
  }
1548
1743
  };
1549
- const attributes = {
1744
+ const attributes$1 = {
1550
1745
  title: {
1551
1746
  type: "string",
1552
1747
  required: true
@@ -1591,6 +1786,62 @@ const attributes = {
1591
1786
  required: true
1592
1787
  }
1593
1788
  };
1789
+ const schema$1 = {
1790
+ kind: kind$1,
1791
+ collectionName: collectionName$1,
1792
+ info: info$1,
1793
+ options: options$1,
1794
+ pluginOptions: pluginOptions$1,
1795
+ attributes: attributes$1
1796
+ };
1797
+ const task = { schema: schema$1 };
1798
+ const kind = "collectionType";
1799
+ const collectionName = "ai_sdk_notes";
1800
+ const info = {
1801
+ singularName: "note",
1802
+ pluralName: "notes",
1803
+ displayName: "AI Note"
1804
+ };
1805
+ const options = {
1806
+ draftAndPublish: false
1807
+ };
1808
+ const pluginOptions = {
1809
+ "content-manager": {
1810
+ visible: false
1811
+ },
1812
+ "content-type-builder": {
1813
+ visible: false
1814
+ }
1815
+ };
1816
+ const attributes = {
1817
+ title: {
1818
+ type: "string"
1819
+ },
1820
+ content: {
1821
+ type: "richtext",
1822
+ required: true
1823
+ },
1824
+ category: {
1825
+ type: "enumeration",
1826
+ "enum": [
1827
+ "research",
1828
+ "snippet",
1829
+ "idea",
1830
+ "reference"
1831
+ ],
1832
+ "default": "research"
1833
+ },
1834
+ tags: {
1835
+ type: "text"
1836
+ },
1837
+ source: {
1838
+ type: "string"
1839
+ },
1840
+ adminUserId: {
1841
+ type: "integer",
1842
+ required: true
1843
+ }
1844
+ };
1594
1845
  const schema = {
1595
1846
  kind,
1596
1847
  collectionName,
@@ -1599,8 +1850,8 @@ const schema = {
1599
1850
  pluginOptions,
1600
1851
  attributes
1601
1852
  };
1602
- const task = { schema };
1603
- const contentTypes = { conversation, memory, "public-memory": publicMemory, task };
1853
+ const note = { schema };
1854
+ const contentTypes = { conversation, memory, "public-memory": publicMemory, task, note };
1604
1855
  function getService(strapi, ctx) {
1605
1856
  const service2 = strapi.plugin("ai-sdk").service("service");
1606
1857
  if (!service2.isInitialized()) {
@@ -1916,20 +2167,20 @@ const mcpController = ({ strapi }) => {
1916
2167
  }
1917
2168
  };
1918
2169
  };
1919
- const CONTENT_TYPE$3 = "plugin::ai-sdk.conversation";
1920
- function getAdminUserId$2(ctx) {
2170
+ const CONTENT_TYPE$4 = "plugin::ai-sdk.conversation";
2171
+ function getAdminUserId$3(ctx) {
1921
2172
  const id = ctx.state?.user?.id;
1922
2173
  return typeof id === "number" ? id : null;
1923
2174
  }
1924
2175
  const conversationController = ({ strapi }) => ({
1925
2176
  async find(ctx) {
1926
- const adminUserId = getAdminUserId$2(ctx);
2177
+ const adminUserId = getAdminUserId$3(ctx);
1927
2178
  if (!adminUserId) {
1928
2179
  ctx.status = 401;
1929
2180
  ctx.body = { error: "Unauthorized" };
1930
2181
  return;
1931
2182
  }
1932
- const conversations = await strapi.documents(CONTENT_TYPE$3).findMany({
2183
+ const conversations = await strapi.documents(CONTENT_TYPE$4).findMany({
1933
2184
  filters: { adminUserId },
1934
2185
  fields: ["title", "createdAt", "updatedAt"],
1935
2186
  sort: { updatedAt: "desc" }
@@ -1937,14 +2188,14 @@ const conversationController = ({ strapi }) => ({
1937
2188
  ctx.body = { data: conversations };
1938
2189
  },
1939
2190
  async findOne(ctx) {
1940
- const adminUserId = getAdminUserId$2(ctx);
2191
+ const adminUserId = getAdminUserId$3(ctx);
1941
2192
  if (!adminUserId) {
1942
2193
  ctx.status = 401;
1943
2194
  ctx.body = { error: "Unauthorized" };
1944
2195
  return;
1945
2196
  }
1946
2197
  const { id } = ctx.params;
1947
- const conversation2 = await strapi.documents(CONTENT_TYPE$3).findOne({
2198
+ const conversation2 = await strapi.documents(CONTENT_TYPE$4).findOne({
1948
2199
  documentId: id
1949
2200
  });
1950
2201
  if (!conversation2 || conversation2.adminUserId !== adminUserId) {
@@ -1955,14 +2206,14 @@ const conversationController = ({ strapi }) => ({
1955
2206
  ctx.body = { data: conversation2 };
1956
2207
  },
1957
2208
  async create(ctx) {
1958
- const adminUserId = getAdminUserId$2(ctx);
2209
+ const adminUserId = getAdminUserId$3(ctx);
1959
2210
  if (!adminUserId) {
1960
2211
  ctx.status = 401;
1961
2212
  ctx.body = { error: "Unauthorized" };
1962
2213
  return;
1963
2214
  }
1964
2215
  const { title, messages } = ctx.request.body;
1965
- const conversation2 = await strapi.documents(CONTENT_TYPE$3).create({
2216
+ const conversation2 = await strapi.documents(CONTENT_TYPE$4).create({
1966
2217
  data: {
1967
2218
  title: title || "New conversation",
1968
2219
  messages: messages || [],
@@ -1973,14 +2224,14 @@ const conversationController = ({ strapi }) => ({
1973
2224
  ctx.body = { data: conversation2 };
1974
2225
  },
1975
2226
  async update(ctx) {
1976
- const adminUserId = getAdminUserId$2(ctx);
2227
+ const adminUserId = getAdminUserId$3(ctx);
1977
2228
  if (!adminUserId) {
1978
2229
  ctx.status = 401;
1979
2230
  ctx.body = { error: "Unauthorized" };
1980
2231
  return;
1981
2232
  }
1982
2233
  const { id } = ctx.params;
1983
- const existing = await strapi.documents(CONTENT_TYPE$3).findOne({
2234
+ const existing = await strapi.documents(CONTENT_TYPE$4).findOne({
1984
2235
  documentId: id
1985
2236
  });
1986
2237
  if (!existing || existing.adminUserId !== adminUserId) {
@@ -1992,21 +2243,21 @@ const conversationController = ({ strapi }) => ({
1992
2243
  const data = {};
1993
2244
  if (title !== void 0) data.title = title;
1994
2245
  if (messages !== void 0) data.messages = messages;
1995
- const conversation2 = await strapi.documents(CONTENT_TYPE$3).update({
2246
+ const conversation2 = await strapi.documents(CONTENT_TYPE$4).update({
1996
2247
  documentId: id,
1997
2248
  data
1998
2249
  });
1999
2250
  ctx.body = { data: conversation2 };
2000
2251
  },
2001
2252
  async delete(ctx) {
2002
- const adminUserId = getAdminUserId$2(ctx);
2253
+ const adminUserId = getAdminUserId$3(ctx);
2003
2254
  if (!adminUserId) {
2004
2255
  ctx.status = 401;
2005
2256
  ctx.body = { error: "Unauthorized" };
2006
2257
  return;
2007
2258
  }
2008
2259
  const { id } = ctx.params;
2009
- const existing = await strapi.documents(CONTENT_TYPE$3).findOne({
2260
+ const existing = await strapi.documents(CONTENT_TYPE$4).findOne({
2010
2261
  documentId: id
2011
2262
  });
2012
2263
  if (!existing || existing.adminUserId !== adminUserId) {
@@ -2014,25 +2265,25 @@ const conversationController = ({ strapi }) => ({
2014
2265
  ctx.body = { error: "Conversation not found" };
2015
2266
  return;
2016
2267
  }
2017
- await strapi.documents(CONTENT_TYPE$3).delete({ documentId: id });
2268
+ await strapi.documents(CONTENT_TYPE$4).delete({ documentId: id });
2018
2269
  ctx.status = 200;
2019
2270
  ctx.body = { data: { documentId: id } };
2020
2271
  }
2021
2272
  });
2022
- const CONTENT_TYPE$2 = "plugin::ai-sdk.memory";
2023
- function getAdminUserId$1(ctx) {
2273
+ const CONTENT_TYPE$3 = "plugin::ai-sdk.memory";
2274
+ function getAdminUserId$2(ctx) {
2024
2275
  const id = ctx.state?.user?.id;
2025
2276
  return typeof id === "number" ? id : null;
2026
2277
  }
2027
2278
  const memoryController = ({ strapi }) => ({
2028
2279
  async find(ctx) {
2029
- const adminUserId = getAdminUserId$1(ctx);
2280
+ const adminUserId = getAdminUserId$2(ctx);
2030
2281
  if (!adminUserId) {
2031
2282
  ctx.status = 401;
2032
2283
  ctx.body = { error: "Unauthorized" };
2033
2284
  return;
2034
2285
  }
2035
- const memories = await strapi.documents(CONTENT_TYPE$2).findMany({
2286
+ const memories = await strapi.documents(CONTENT_TYPE$3).findMany({
2036
2287
  filters: { adminUserId },
2037
2288
  fields: ["content", "category", "createdAt"],
2038
2289
  sort: { createdAt: "desc" }
@@ -2040,7 +2291,7 @@ const memoryController = ({ strapi }) => ({
2040
2291
  ctx.body = { data: memories };
2041
2292
  },
2042
2293
  async create(ctx) {
2043
- const adminUserId = getAdminUserId$1(ctx);
2294
+ const adminUserId = getAdminUserId$2(ctx);
2044
2295
  if (!adminUserId) {
2045
2296
  ctx.status = 401;
2046
2297
  ctx.body = { error: "Unauthorized" };
@@ -2052,7 +2303,7 @@ const memoryController = ({ strapi }) => ({
2052
2303
  ctx.body = { error: "content is required" };
2053
2304
  return;
2054
2305
  }
2055
- const memory2 = await strapi.documents(CONTENT_TYPE$2).create({
2306
+ const memory2 = await strapi.documents(CONTENT_TYPE$3).create({
2056
2307
  data: {
2057
2308
  content,
2058
2309
  category: category || "general",
@@ -2063,14 +2314,14 @@ const memoryController = ({ strapi }) => ({
2063
2314
  ctx.body = { data: memory2 };
2064
2315
  },
2065
2316
  async update(ctx) {
2066
- const adminUserId = getAdminUserId$1(ctx);
2317
+ const adminUserId = getAdminUserId$2(ctx);
2067
2318
  if (!adminUserId) {
2068
2319
  ctx.status = 401;
2069
2320
  ctx.body = { error: "Unauthorized" };
2070
2321
  return;
2071
2322
  }
2072
2323
  const { id } = ctx.params;
2073
- const existing = await strapi.documents(CONTENT_TYPE$2).findOne({
2324
+ const existing = await strapi.documents(CONTENT_TYPE$3).findOne({
2074
2325
  documentId: id
2075
2326
  });
2076
2327
  if (!existing || existing.adminUserId !== adminUserId) {
@@ -2082,21 +2333,21 @@ const memoryController = ({ strapi }) => ({
2082
2333
  const data = {};
2083
2334
  if (content !== void 0) data.content = content;
2084
2335
  if (category !== void 0) data.category = category;
2085
- const memory2 = await strapi.documents(CONTENT_TYPE$2).update({
2336
+ const memory2 = await strapi.documents(CONTENT_TYPE$3).update({
2086
2337
  documentId: id,
2087
2338
  data
2088
2339
  });
2089
2340
  ctx.body = { data: memory2 };
2090
2341
  },
2091
2342
  async delete(ctx) {
2092
- const adminUserId = getAdminUserId$1(ctx);
2343
+ const adminUserId = getAdminUserId$2(ctx);
2093
2344
  if (!adminUserId) {
2094
2345
  ctx.status = 401;
2095
2346
  ctx.body = { error: "Unauthorized" };
2096
2347
  return;
2097
2348
  }
2098
2349
  const { id } = ctx.params;
2099
- const existing = await strapi.documents(CONTENT_TYPE$2).findOne({
2350
+ const existing = await strapi.documents(CONTENT_TYPE$3).findOne({
2100
2351
  documentId: id
2101
2352
  });
2102
2353
  if (!existing || existing.adminUserId !== adminUserId) {
@@ -2104,15 +2355,15 @@ const memoryController = ({ strapi }) => ({
2104
2355
  ctx.body = { error: "Memory not found" };
2105
2356
  return;
2106
2357
  }
2107
- await strapi.documents(CONTENT_TYPE$2).delete({ documentId: id });
2358
+ await strapi.documents(CONTENT_TYPE$3).delete({ documentId: id });
2108
2359
  ctx.status = 200;
2109
2360
  ctx.body = { data: { documentId: id } };
2110
2361
  }
2111
2362
  });
2112
- const CONTENT_TYPE$1 = "plugin::ai-sdk.public-memory";
2363
+ const CONTENT_TYPE$2 = "plugin::ai-sdk.public-memory";
2113
2364
  const publicMemoryController = ({ strapi }) => ({
2114
2365
  async find(ctx) {
2115
- const memories = await strapi.documents(CONTENT_TYPE$1).findMany({
2366
+ const memories = await strapi.documents(CONTENT_TYPE$2).findMany({
2116
2367
  fields: ["content", "category", "createdAt"],
2117
2368
  sort: { createdAt: "desc" }
2118
2369
  });
@@ -2125,7 +2376,7 @@ const publicMemoryController = ({ strapi }) => ({
2125
2376
  ctx.body = { error: "content is required" };
2126
2377
  return;
2127
2378
  }
2128
- const memory2 = await strapi.documents(CONTENT_TYPE$1).create({
2379
+ const memory2 = await strapi.documents(CONTENT_TYPE$2).create({
2129
2380
  data: { content, category: category || "general" }
2130
2381
  });
2131
2382
  ctx.status = 201;
@@ -2133,7 +2384,7 @@ const publicMemoryController = ({ strapi }) => ({
2133
2384
  },
2134
2385
  async update(ctx) {
2135
2386
  const { id } = ctx.params;
2136
- const existing = await strapi.documents(CONTENT_TYPE$1).findOne({ documentId: id });
2387
+ const existing = await strapi.documents(CONTENT_TYPE$2).findOne({ documentId: id });
2137
2388
  if (!existing) {
2138
2389
  ctx.status = 404;
2139
2390
  ctx.body = { error: "Public memory not found" };
@@ -2143,7 +2394,7 @@ const publicMemoryController = ({ strapi }) => ({
2143
2394
  const data = {};
2144
2395
  if (content !== void 0) data.content = content;
2145
2396
  if (category !== void 0) data.category = category;
2146
- const memory2 = await strapi.documents(CONTENT_TYPE$1).update({
2397
+ const memory2 = await strapi.documents(CONTENT_TYPE$2).update({
2147
2398
  documentId: id,
2148
2399
  data
2149
2400
  });
@@ -2151,45 +2402,45 @@ const publicMemoryController = ({ strapi }) => ({
2151
2402
  },
2152
2403
  async delete(ctx) {
2153
2404
  const { id } = ctx.params;
2154
- const existing = await strapi.documents(CONTENT_TYPE$1).findOne({ documentId: id });
2405
+ const existing = await strapi.documents(CONTENT_TYPE$2).findOne({ documentId: id });
2155
2406
  if (!existing) {
2156
2407
  ctx.status = 404;
2157
2408
  ctx.body = { error: "Public memory not found" };
2158
2409
  return;
2159
2410
  }
2160
- await strapi.documents(CONTENT_TYPE$1).delete({ documentId: id });
2411
+ await strapi.documents(CONTENT_TYPE$2).delete({ documentId: id });
2161
2412
  ctx.status = 200;
2162
2413
  ctx.body = { data: { documentId: id } };
2163
2414
  }
2164
2415
  });
2165
- const CONTENT_TYPE = "plugin::ai-sdk.task";
2166
- function getAdminUserId(ctx) {
2416
+ const CONTENT_TYPE$1 = "plugin::ai-sdk.task";
2417
+ function getAdminUserId$1(ctx) {
2167
2418
  const id = ctx.state?.user?.id;
2168
2419
  return typeof id === "number" ? id : null;
2169
2420
  }
2170
2421
  const taskController = ({ strapi }) => ({
2171
2422
  async find(ctx) {
2172
- const adminUserId = getAdminUserId(ctx);
2423
+ const adminUserId = getAdminUserId$1(ctx);
2173
2424
  if (!adminUserId) {
2174
2425
  ctx.status = 401;
2175
2426
  ctx.body = { error: "Unauthorized" };
2176
2427
  return;
2177
2428
  }
2178
- const tasks = await strapi.documents(CONTENT_TYPE).findMany({
2429
+ const tasks = await strapi.documents(CONTENT_TYPE$1).findMany({
2179
2430
  filters: { adminUserId },
2180
2431
  sort: { createdAt: "desc" }
2181
2432
  });
2182
2433
  ctx.body = { data: tasks };
2183
2434
  },
2184
2435
  async create(ctx) {
2185
- const adminUserId = getAdminUserId(ctx);
2436
+ const adminUserId = getAdminUserId$1(ctx);
2186
2437
  if (!adminUserId) {
2187
2438
  ctx.status = 401;
2188
2439
  ctx.body = { error: "Unauthorized" };
2189
2440
  return;
2190
2441
  }
2191
2442
  const body = ctx.request.body;
2192
- const task2 = await strapi.documents(CONTENT_TYPE).create({
2443
+ const task2 = await strapi.documents(CONTENT_TYPE$1).create({
2193
2444
  data: {
2194
2445
  title: body.title,
2195
2446
  description: body.description,
@@ -2206,14 +2457,14 @@ const taskController = ({ strapi }) => ({
2206
2457
  ctx.body = { data: task2 };
2207
2458
  },
2208
2459
  async update(ctx) {
2209
- const adminUserId = getAdminUserId(ctx);
2460
+ const adminUserId = getAdminUserId$1(ctx);
2210
2461
  if (!adminUserId) {
2211
2462
  ctx.status = 401;
2212
2463
  ctx.body = { error: "Unauthorized" };
2213
2464
  return;
2214
2465
  }
2215
2466
  const { id } = ctx.params;
2216
- const existing = await strapi.documents(CONTENT_TYPE).findOne({
2467
+ const existing = await strapi.documents(CONTENT_TYPE$1).findOne({
2217
2468
  documentId: id
2218
2469
  });
2219
2470
  if (!existing || existing.adminUserId !== adminUserId) {
@@ -2226,13 +2477,80 @@ const taskController = ({ strapi }) => ({
2226
2477
  for (const key of ["title", "description", "content", "done", "priority", "consequence", "impact", "dueDate"]) {
2227
2478
  if (body[key] !== void 0) data[key] = body[key];
2228
2479
  }
2229
- const task2 = await strapi.documents(CONTENT_TYPE).update({
2480
+ const task2 = await strapi.documents(CONTENT_TYPE$1).update({
2230
2481
  documentId: id,
2231
2482
  data
2232
2483
  });
2233
2484
  ctx.body = { data: task2 };
2234
2485
  },
2235
2486
  async delete(ctx) {
2487
+ const adminUserId = getAdminUserId$1(ctx);
2488
+ if (!adminUserId) {
2489
+ ctx.status = 401;
2490
+ ctx.body = { error: "Unauthorized" };
2491
+ return;
2492
+ }
2493
+ const { id } = ctx.params;
2494
+ const existing = await strapi.documents(CONTENT_TYPE$1).findOne({
2495
+ documentId: id
2496
+ });
2497
+ if (!existing || existing.adminUserId !== adminUserId) {
2498
+ ctx.status = 404;
2499
+ ctx.body = { error: "Task not found" };
2500
+ return;
2501
+ }
2502
+ await strapi.documents(CONTENT_TYPE$1).delete({ documentId: id });
2503
+ ctx.status = 200;
2504
+ ctx.body = { data: { documentId: id } };
2505
+ }
2506
+ });
2507
+ const CONTENT_TYPE = "plugin::ai-sdk.note";
2508
+ function getAdminUserId(ctx) {
2509
+ const id = ctx.state?.user?.id;
2510
+ return typeof id === "number" ? id : null;
2511
+ }
2512
+ const noteController = ({ strapi }) => ({
2513
+ async find(ctx) {
2514
+ const adminUserId = getAdminUserId(ctx);
2515
+ if (!adminUserId) {
2516
+ ctx.status = 401;
2517
+ ctx.body = { error: "Unauthorized" };
2518
+ return;
2519
+ }
2520
+ const notes = await strapi.documents(CONTENT_TYPE).findMany({
2521
+ filters: { adminUserId },
2522
+ fields: ["title", "content", "category", "tags", "source", "createdAt"],
2523
+ sort: { createdAt: "desc" }
2524
+ });
2525
+ ctx.body = { data: notes };
2526
+ },
2527
+ async create(ctx) {
2528
+ const adminUserId = getAdminUserId(ctx);
2529
+ if (!adminUserId) {
2530
+ ctx.status = 401;
2531
+ ctx.body = { error: "Unauthorized" };
2532
+ return;
2533
+ }
2534
+ const { title, content, category, tags, source } = ctx.request.body;
2535
+ if (!content || typeof content !== "string") {
2536
+ ctx.status = 400;
2537
+ ctx.body = { error: "content is required" };
2538
+ return;
2539
+ }
2540
+ const note2 = await strapi.documents(CONTENT_TYPE).create({
2541
+ data: {
2542
+ title: title || "",
2543
+ content,
2544
+ category: category || "research",
2545
+ tags: tags || "",
2546
+ source: source || "",
2547
+ adminUserId
2548
+ }
2549
+ });
2550
+ ctx.status = 201;
2551
+ ctx.body = { data: note2 };
2552
+ },
2553
+ async update(ctx) {
2236
2554
  const adminUserId = getAdminUserId(ctx);
2237
2555
  if (!adminUserId) {
2238
2556
  ctx.status = 401;
@@ -2245,12 +2563,58 @@ const taskController = ({ strapi }) => ({
2245
2563
  });
2246
2564
  if (!existing || existing.adminUserId !== adminUserId) {
2247
2565
  ctx.status = 404;
2248
- ctx.body = { error: "Task not found" };
2566
+ ctx.body = { error: "Note not found" };
2567
+ return;
2568
+ }
2569
+ const { title, content, category, tags, source } = ctx.request.body;
2570
+ const data = {};
2571
+ if (title !== void 0) data.title = title;
2572
+ if (content !== void 0) data.content = content;
2573
+ if (category !== void 0) data.category = category;
2574
+ if (tags !== void 0) data.tags = tags;
2575
+ if (source !== void 0) data.source = source;
2576
+ const note2 = await strapi.documents(CONTENT_TYPE).update({
2577
+ documentId: id,
2578
+ data
2579
+ });
2580
+ ctx.body = { data: note2 };
2581
+ },
2582
+ async delete(ctx) {
2583
+ const adminUserId = getAdminUserId(ctx);
2584
+ if (!adminUserId) {
2585
+ ctx.status = 401;
2586
+ ctx.body = { error: "Unauthorized" };
2587
+ return;
2588
+ }
2589
+ const { id } = ctx.params;
2590
+ const existing = await strapi.documents(CONTENT_TYPE).findOne({
2591
+ documentId: id
2592
+ });
2593
+ if (!existing || existing.adminUserId !== adminUserId) {
2594
+ ctx.status = 404;
2595
+ ctx.body = { error: "Note not found" };
2249
2596
  return;
2250
2597
  }
2251
2598
  await strapi.documents(CONTENT_TYPE).delete({ documentId: id });
2252
2599
  ctx.status = 200;
2253
2600
  ctx.body = { data: { documentId: id } };
2601
+ },
2602
+ async clearAll(ctx) {
2603
+ const adminUserId = getAdminUserId(ctx);
2604
+ if (!adminUserId) {
2605
+ ctx.status = 401;
2606
+ ctx.body = { error: "Unauthorized" };
2607
+ return;
2608
+ }
2609
+ const notes = await strapi.documents(CONTENT_TYPE).findMany({
2610
+ filters: { adminUserId },
2611
+ fields: ["documentId"]
2612
+ });
2613
+ for (const note2 of notes) {
2614
+ await strapi.documents(CONTENT_TYPE).delete({ documentId: note2.documentId });
2615
+ }
2616
+ ctx.status = 200;
2617
+ ctx.body = { data: { deleted: notes.length } };
2254
2618
  }
2255
2619
  });
2256
2620
  const controllers = {
@@ -2259,7 +2623,8 @@ const controllers = {
2259
2623
  conversation: conversationController,
2260
2624
  memory: memoryController,
2261
2625
  "public-memory": publicMemoryController,
2262
- task: taskController
2626
+ task: taskController,
2627
+ note: noteController
2263
2628
  };
2264
2629
  const promptInjection = [
2265
2630
  "ignore (all |any )?(previous|prior|above) (instructions|prompts|rules)",
@@ -2629,6 +2994,36 @@ const adminAPIRoutes = {
2629
2994
  path: "/tasks/:id",
2630
2995
  handler: "task.delete",
2631
2996
  config: { policies: [] }
2997
+ },
2998
+ {
2999
+ method: "GET",
3000
+ path: "/notes",
3001
+ handler: "note.find",
3002
+ config: { policies: [] }
3003
+ },
3004
+ {
3005
+ method: "POST",
3006
+ path: "/notes",
3007
+ handler: "note.create",
3008
+ config: { policies: [] }
3009
+ },
3010
+ {
3011
+ method: "PUT",
3012
+ path: "/notes/:id",
3013
+ handler: "note.update",
3014
+ config: { policies: [] }
3015
+ },
3016
+ {
3017
+ method: "DELETE",
3018
+ path: "/notes/clear",
3019
+ handler: "note.clearAll",
3020
+ config: { policies: [] }
3021
+ },
3022
+ {
3023
+ method: "DELETE",
3024
+ path: "/notes/:id",
3025
+ handler: "note.delete",
3026
+ config: { policies: [] }
2632
3027
  }
2633
3028
  ]
2634
3029
  };
@@ -2716,6 +3111,26 @@ function describeTools(tools) {
2716
3111
  return `Available tools:
2717
3112
  ${lines.join("\n")}`;
2718
3113
  }
3114
+ function trimMessages(messages, max) {
3115
+ if (messages.length <= max) return messages;
3116
+ const sliced = messages.slice(-max);
3117
+ while (sliced.length > 0 && hasOrphanedToolCalls(sliced[0])) {
3118
+ sliced.shift();
3119
+ }
3120
+ return sliced;
3121
+ }
3122
+ function hasOrphanedToolCalls(message) {
3123
+ if (message.role !== "assistant") return false;
3124
+ if (message.parts) {
3125
+ return message.parts.some(
3126
+ (part) => part.type === "tool-invocation"
3127
+ );
3128
+ }
3129
+ if (message.toolInvocations?.length) {
3130
+ return true;
3131
+ }
3132
+ return false;
3133
+ }
2719
3134
  const DEFAULT_PREAMBLE = `You are a Strapi CMS assistant. Use your tools to fulfill user requests. When asked to create or update content, use the appropriate tool — do not tell the user you cannot. When performing bulk operations (e.g. publish multiple items), call multiple tools in parallel in a single step rather than one at a time.
2720
3135
 
2721
3136
  For analytics and counting questions (e.g. "how many", "count", "distribution", "trends", "breakdown"), use the aggregateContent tool instead of searchContent — it is faster and purpose-built for these queries. Present analytics results as markdown tables. After showing analytics results, suggest 2-3 follow-up questions the user might find useful under a "**You might also want to know:**" heading.
@@ -2779,7 +3194,7 @@ const service = ({ strapi }) => {
2779
3194
  const maxMessages = config2?.maxConversationMessages ?? DEFAULT_MAX_CONVERSATION_MESSAGES;
2780
3195
  const maxOutputTokens = config2?.maxOutputTokens ?? DEFAULT_MAX_OUTPUT_TOKENS;
2781
3196
  const maxSteps = config2?.maxSteps ?? DEFAULT_MAX_STEPS;
2782
- const trimmedMessages = messages.length > maxMessages ? messages.slice(-maxMessages) : messages;
3197
+ const trimmedMessages = trimMessages(messages, maxMessages);
2783
3198
  const modelMessages = await convertToModelMessages(trimmedMessages);
2784
3199
  const tools = createTools(strapi, { adminUserId: options2?.adminUserId, enabledToolSources: options2?.enabledToolSources });
2785
3200
  const toolsDescription = describeTools(tools);
@@ -2824,7 +3239,7 @@ ${lines.join("\n")}`;
2824
3239
  const publicModel = publicConfig?.chatModel ?? DEFAULT_PUBLIC_CHAT_MODEL;
2825
3240
  const allowedContentTypes = publicConfig?.allowedContentTypes ?? [];
2826
3241
  const publicToolSources = publicConfig?.publicToolSources;
2827
- const trimmedMessages = messages.length > maxMessages ? messages.slice(-maxMessages) : messages;
3242
+ const trimmedMessages = trimMessages(messages, maxMessages);
2828
3243
  const modelMessages = await convertToModelMessages(trimmedMessages);
2829
3244
  const tools = createPublicTools(strapi, allowedContentTypes, publicToolSources);
2830
3245
  const toolsDescription = describeTools(tools);