strapi-plugin-ai-sdk 0.7.4 → 0.7.9

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 (34) hide show
  1. package/dist/_chunks/{App-CEEsJsKL.js → App-DTbAnq7K.js} +712 -53
  2. package/dist/_chunks/{App-DCV7o6Hc.mjs → App-DZVBmpvs.mjs} +714 -55
  3. package/dist/_chunks/{index-Cw2aiQ8K.js → index-C3xn2IND.js} +1 -1
  4. package/dist/_chunks/{index-BMrDQVQl.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 +603 -171
  12. package/dist/server/index.mjs +603 -171
  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/mcp/resources/tool-guide.d.ts +5 -0
  19. package/dist/server/src/tool-logic/aggregate-content.d.ts +1 -1
  20. package/dist/server/src/tool-logic/create-content.d.ts +27 -0
  21. package/dist/server/src/tool-logic/find-one-content.d.ts +1 -1
  22. package/dist/server/src/tool-logic/index.d.ts +8 -2
  23. package/dist/server/src/tool-logic/list-content-types.d.ts +1 -1
  24. package/dist/server/src/tool-logic/manage-task.d.ts +1 -1
  25. package/dist/server/src/tool-logic/recall-notes.d.ts +22 -0
  26. package/dist/server/src/tool-logic/save-note.d.ts +17 -0
  27. package/dist/server/src/tool-logic/update-content.d.ts +29 -0
  28. package/dist/server/src/tool-logic/upload-media.d.ts +1 -1
  29. package/dist/server/src/tools/definitions/create-content.d.ts +2 -0
  30. package/dist/server/src/tools/definitions/{write-content.d.ts → recall-notes.d.ts} +1 -1
  31. package/dist/server/src/tools/definitions/save-note.d.ts +2 -0
  32. package/dist/server/src/tools/definitions/update-content.d.ts +2 -0
  33. package/package.json +1 -1
  34. package/dist/server/src/tool-logic/write-content.d.ts +0 -34
@@ -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",
@@ -367,33 +470,41 @@ async function searchContent(strapi, params) {
367
470
  }
368
471
  };
369
472
  }
370
- const writeContentSchema = z.object({
473
+ const createContentSchema = z.object({
371
474
  contentType: z.string().describe('Content type UID, e.g. "api::article.article"'),
372
- action: z.enum(["create", "update"]).describe("Whether to create a new document or update an existing one"),
373
- documentId: z.string().optional().describe("Required for update — the document ID to update"),
374
475
  data: z.record(z.string(), z.unknown()).describe("The field values to set. Must match the content type schema."),
375
476
  status: z.enum(["draft", "published"]).optional().describe("Document status. Defaults to draft."),
376
477
  locale: z.string().optional().describe('Locale code for i18n content, e.g. "en" or "fr"')
377
478
  });
378
- const writeContentDescription = "Create or update a document in any Strapi content type. Use listContentTypes first to discover the schema, and searchContent to find existing documents for updates.";
379
- async function writeContent(strapi, params) {
380
- const { contentType, action, documentId, data, status, locale } = params;
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.";
480
+ async function createContent(strapi, params) {
481
+ const { contentType, data, status, locale } = params;
381
482
  if (!strapi.contentTypes[contentType]) {
382
483
  throw new Error(`Content type "${contentType}" does not exist.`);
383
484
  }
384
- if (action === "update" && !documentId) {
385
- throw new Error("documentId is required for update actions.");
386
- }
387
485
  const docs = strapi.documents(contentType);
388
- if (action === "create") {
389
- const document2 = await docs.create({
390
- data,
391
- ...status ? { status } : {},
392
- ...locale ? { locale } : {},
393
- populate: "*"
394
- });
395
- return { action: "create", document: document2 };
486
+ const document = await docs.create({
487
+ data,
488
+ ...status ? { status } : {},
489
+ ...locale ? { locale } : {},
490
+ populate: "*"
491
+ });
492
+ return { action: "create", document };
493
+ }
494
+ const updateContentSchema = z.object({
495
+ contentType: z.string().describe('Content type UID, e.g. "api::article.article"'),
496
+ documentId: z.string().describe("The document ID to update"),
497
+ data: z.record(z.string(), z.unknown()).describe("The field values to set. Must match the content type schema."),
498
+ status: z.enum(["draft", "published"]).optional().describe("Document status. Defaults to draft."),
499
+ locale: z.string().optional().describe('Locale code for i18n content, e.g. "en" or "fr"')
500
+ });
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.";
502
+ async function updateContent(strapi, params) {
503
+ const { contentType, documentId, data, status, locale } = params;
504
+ if (!strapi.contentTypes[contentType]) {
505
+ throw new Error(`Content type "${contentType}" does not exist.`);
396
506
  }
507
+ const docs = strapi.documents(contentType);
397
508
  const existing = await docs.findOne({
398
509
  documentId,
399
510
  ...locale ? { locale } : {}
@@ -460,7 +571,7 @@ async function sendEmail(strapi, params) {
460
571
  subject: params.subject
461
572
  };
462
573
  }
463
- const CONTENT_TYPE$7 = "plugin::ai-sdk.memory";
574
+ const CONTENT_TYPE$a = "plugin::ai-sdk.memory";
464
575
  const saveMemorySchema = z.object({
465
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")'),
466
577
  category: z.string().optional().describe('Category for the memory (e.g. "preference", "project", "personal", "general"). Defaults to "general".')
@@ -471,7 +582,7 @@ async function saveMemory(strapi, params, context) {
471
582
  return { success: false, message: "Cannot save memory: user context not available." };
472
583
  }
473
584
  try {
474
- await strapi.documents(CONTENT_TYPE$7).create({
585
+ await strapi.documents(CONTENT_TYPE$a).create({
475
586
  data: {
476
587
  content: params.content,
477
588
  category: params.category || "general",
@@ -484,7 +595,7 @@ async function saveMemory(strapi, params, context) {
484
595
  return { success: false, message: `Failed to save memory: ${detail}` };
485
596
  }
486
597
  }
487
- const CONTENT_TYPE$6 = "plugin::ai-sdk.memory";
598
+ const CONTENT_TYPE$9 = "plugin::ai-sdk.memory";
488
599
  const recallMemoriesSchema = z.object({
489
600
  query: z.string().optional().describe("Optional search term to filter memories by content"),
490
601
  category: z.string().optional().describe('Optional category to filter by (e.g. "preference", "project", "personal")')
@@ -502,7 +613,7 @@ async function recallMemories(strapi, params, context) {
502
613
  if (params.query) {
503
614
  filters.content = { $containsi: params.query };
504
615
  }
505
- const memories = await strapi.documents(CONTENT_TYPE$6).findMany({
616
+ const memories = await strapi.documents(CONTENT_TYPE$9).findMany({
506
617
  filters,
507
618
  fields: ["content", "category"],
508
619
  sort: { createdAt: "desc" }
@@ -528,7 +639,7 @@ const findOneContentSchema = z.object({
528
639
  status: z.enum(["draft", "published"]).optional().describe("Document status filter."),
529
640
  locale: z.string().optional().describe('Locale code for i18n content, e.g. "en" or "fr"')
530
641
  });
531
- 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.";
532
643
  async function findOneContent(strapi, params) {
533
644
  const { contentType, documentId, populate = "*", fields, status, locale } = params;
534
645
  if (!strapi.contentTypes[contentType]) {
@@ -554,7 +665,7 @@ const uploadMediaSchema = z.object({
554
665
  caption: z.string().optional().describe("Caption for the media file"),
555
666
  alternativeText: z.string().optional().describe("Alternative text for accessibility")
556
667
  });
557
- 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 writeContent 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.";
558
669
  async function uploadMedia(strapi, params) {
559
670
  const { url, name, caption, alternativeText } = params;
560
671
  let parsedUrl;
@@ -622,10 +733,10 @@ async function uploadMedia(strapi, params) {
622
733
  return {
623
734
  file: uploadedFile,
624
735
  message: `File "${uploadedFile.name}" uploaded successfully (ID: ${uploadedFile.id}).`,
625
- usage: `To link this file to a content type field, use writeContent with: { "fieldName": ${uploadedFile.id} }`
736
+ usage: `To link this file to a content type field, use createContent or updateContent with: { "fieldName": ${uploadedFile.id} }`
626
737
  };
627
738
  }
628
- const CONTENT_TYPE$5 = "plugin::ai-sdk.public-memory";
739
+ const CONTENT_TYPE$8 = "plugin::ai-sdk.public-memory";
629
740
  const recallPublicMemoriesSchema = z.object({
630
741
  query: z.string().optional().describe("Optional search term to filter public memories by content"),
631
742
  category: z.string().optional().describe('Optional category to filter by (e.g. "faq", "product", "policy")')
@@ -636,7 +747,7 @@ async function recallPublicMemories(strapi, params) {
636
747
  const filters = {};
637
748
  if (params.category) filters.category = params.category;
638
749
  if (params.query) filters.content = { $containsi: params.query };
639
- const memories = await strapi.documents(CONTENT_TYPE$5).findMany({
750
+ const memories = await strapi.documents(CONTENT_TYPE$8).findMany({
640
751
  filters,
641
752
  fields: ["content", "category"],
642
753
  sort: { createdAt: "desc" }
@@ -718,7 +829,7 @@ const aggregateContentSchema = z.object({
718
829
  status: z.enum(["draft", "published"]).optional().describe("Filter by document status"),
719
830
  locale: z.string().optional().describe("Locale code for i18n content")
720
831
  });
721
- 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.';
722
833
  function buildBaseQuery(params) {
723
834
  const { filters, status, locale, dateFrom, dateTo, dateField = "createdAt" } = params;
724
835
  const query = {};
@@ -870,7 +981,7 @@ async function aggregateContent(strapi, params) {
870
981
  throw new Error(`Unknown operation: ${operation}`);
871
982
  }
872
983
  }
873
- const CONTENT_TYPE$4 = "plugin::ai-sdk.task";
984
+ const CONTENT_TYPE$7 = "plugin::ai-sdk.task";
874
985
  const manageTaskSchema = z.object({
875
986
  action: z.enum(["create", "update", "complete", "list", "summary"]).describe(
876
987
  "Action to perform: create a new task, update an existing task, complete (mark done), list open tasks, or get a summary."
@@ -886,14 +997,14 @@ const manageTaskSchema = z.object({
886
997
  done: z.boolean().optional().describe("Set done status explicitly (for update action)."),
887
998
  filters: z.record(z.string(), z.unknown()).optional().describe("Additional Strapi filters for list action.")
888
999
  });
889
- 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.";
890
1001
  async function resolveTask(strapi, adminUserId, documentId, title) {
891
1002
  if (documentId) {
892
- const task2 = await strapi.documents(CONTENT_TYPE$4).findOne({ documentId });
1003
+ const task2 = await strapi.documents(CONTENT_TYPE$7).findOne({ documentId });
893
1004
  if (task2 && task2.adminUserId === adminUserId) return task2;
894
1005
  }
895
1006
  if (title) {
896
- const tasks = await strapi.documents(CONTENT_TYPE$4).findMany({
1007
+ const tasks = await strapi.documents(CONTENT_TYPE$7).findMany({
897
1008
  filters: {
898
1009
  adminUserId,
899
1010
  title: { $containsi: title }
@@ -946,7 +1057,7 @@ async function manageTask(strapi, params, context) {
946
1057
  if (params.consequence !== void 0) data.consequence = params.consequence;
947
1058
  if (params.impact !== void 0) data.impact = params.impact;
948
1059
  if (params.dueDate !== void 0) data.dueDate = params.dueDate;
949
- const updated = await strapi.documents(CONTENT_TYPE$4).update({
1060
+ const updated = await strapi.documents(CONTENT_TYPE$7).update({
950
1061
  documentId: duplicate.documentId,
951
1062
  data
952
1063
  });
@@ -957,7 +1068,7 @@ async function manageTask(strapi, params, context) {
957
1068
  data: updated
958
1069
  };
959
1070
  }
960
- const task2 = await strapi.documents(CONTENT_TYPE$4).create({
1071
+ const task2 = await strapi.documents(CONTENT_TYPE$7).create({
961
1072
  data: {
962
1073
  title: params.title,
963
1074
  description: params.description,
@@ -986,7 +1097,7 @@ async function manageTask(strapi, params, context) {
986
1097
  for (const key of ["title", "description", "content", "priority", "consequence", "impact", "dueDate", "done"]) {
987
1098
  if (params[key] !== void 0) data[key] = params[key];
988
1099
  }
989
- const updated = await strapi.documents(CONTENT_TYPE$4).update({
1100
+ const updated = await strapi.documents(CONTENT_TYPE$7).update({
990
1101
  documentId: existing.documentId,
991
1102
  data
992
1103
  });
@@ -997,7 +1108,7 @@ async function manageTask(strapi, params, context) {
997
1108
  if (!toComplete) {
998
1109
  return { success: false, message: "Task not found. Provide a documentId or a title to search by." };
999
1110
  }
1000
- await strapi.documents(CONTENT_TYPE$4).update({
1111
+ await strapi.documents(CONTENT_TYPE$7).update({
1001
1112
  documentId: toComplete.documentId,
1002
1113
  data: { done: true }
1003
1114
  });
@@ -1009,7 +1120,7 @@ async function manageTask(strapi, params, context) {
1009
1120
  done: false,
1010
1121
  ...params.filters
1011
1122
  };
1012
- const tasks = await strapi.documents(CONTENT_TYPE$4).findMany({
1123
+ const tasks = await strapi.documents(CONTENT_TYPE$7).findMany({
1013
1124
  filters
1014
1125
  });
1015
1126
  const sorted = tasks.sort((a, b) => {
@@ -1038,7 +1149,7 @@ async function manageTask(strapi, params, context) {
1038
1149
  };
1039
1150
  }
1040
1151
  case "summary": {
1041
- const allOpen = await strapi.documents(CONTENT_TYPE$4).findMany({
1152
+ const allOpen = await strapi.documents(CONTENT_TYPE$7).findMany({
1042
1153
  filters: { adminUserId, done: false }
1043
1154
  });
1044
1155
  const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
@@ -1075,6 +1186,82 @@ async function manageTask(strapi, params, context) {
1075
1186
  return { success: false, message: `Task operation failed: ${detail}` };
1076
1187
  }
1077
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
+ }
1078
1265
  const listContentTypesTool = {
1079
1266
  name: "listContentTypes",
1080
1267
  description: listContentTypesDescription,
@@ -1123,13 +1310,24 @@ async function sanitizeInput(strapi, uid, data, auth) {
1123
1310
  throw error;
1124
1311
  }
1125
1312
  }
1126
- const writeContentTool = {
1127
- name: "writeContent",
1128
- description: writeContentDescription,
1129
- schema: writeContentSchema,
1313
+ const createContentTool = {
1314
+ name: "createContent",
1315
+ description: createContentDescription,
1316
+ schema: createContentSchema,
1317
+ execute: async (args, strapi) => {
1318
+ const sanitizedData = await sanitizeInput(strapi, args.contentType, args.data);
1319
+ const result = await createContent(strapi, { ...args, data: sanitizedData });
1320
+ const sanitizedDoc = await sanitizeOutput(strapi, args.contentType, result.document);
1321
+ return { ...result, document: sanitizedDoc };
1322
+ }
1323
+ };
1324
+ const updateContentTool = {
1325
+ name: "updateContent",
1326
+ description: updateContentDescription,
1327
+ schema: updateContentSchema,
1130
1328
  execute: async (args, strapi) => {
1131
1329
  const sanitizedData = await sanitizeInput(strapi, args.contentType, args.data);
1132
- const result = await writeContent(strapi, { ...args, data: sanitizedData });
1330
+ const result = await updateContent(strapi, { ...args, data: sanitizedData });
1133
1331
  const sanitizedDoc = await sanitizeOutput(strapi, args.contentType, result.document);
1134
1332
  return { ...result, document: sanitizedDoc };
1135
1333
  }
@@ -1194,10 +1392,25 @@ const manageTaskTool = {
1194
1392
  execute: async (args, strapi, context) => manageTask(strapi, args, context),
1195
1393
  internal: true
1196
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
+ };
1197
1409
  const builtInTools = [
1198
1410
  listContentTypesTool,
1199
1411
  searchContentTool,
1200
- writeContentTool,
1412
+ createContentTool,
1413
+ updateContentTool,
1201
1414
  findOneContentTool,
1202
1415
  uploadMediaTool,
1203
1416
  sendEmailTool,
@@ -1205,12 +1418,22 @@ const builtInTools = [
1205
1418
  recallMemoriesTool,
1206
1419
  recallPublicMemoriesTool,
1207
1420
  aggregateContentTool,
1208
- manageTaskTool
1421
+ manageTaskTool,
1422
+ saveNoteTool,
1423
+ recallNotesTool
1209
1424
  ];
1210
1425
  const PLUGIN_ID$2 = "ai-sdk";
1211
1426
  const bootstrap = ({ strapi }) => {
1212
1427
  const plugin = strapi.plugin(PLUGIN_ID$2);
1213
1428
  const config2 = strapi.config.get(`plugin::${PLUGIN_ID$2}`);
1429
+ initializeProvider(strapi, plugin, config2);
1430
+ const registry = initializeToolRegistry(plugin);
1431
+ discoverPluginTools(strapi, registry);
1432
+ plugin.createMcpServer = () => createMcpServer(strapi);
1433
+ plugin.mcpSessions = /* @__PURE__ */ new Map();
1434
+ strapi.log.info(`[${PLUGIN_ID$2}] MCP endpoint available at: /api/${PLUGIN_ID$2}/mcp`);
1435
+ };
1436
+ function initializeProvider(strapi, plugin, config2) {
1214
1437
  AIProvider.registerProvider("anthropic", ({ apiKey, baseURL }) => {
1215
1438
  const provider = createAnthropic({ apiKey, baseURL });
1216
1439
  return (modelId) => provider(modelId);
@@ -1223,27 +1446,22 @@ const bootstrap = ({ strapi }) => {
1223
1446
  } else {
1224
1447
  strapi.log.warn(`[${PLUGIN_ID$2}] anthropicApiKey not configured, AI provider will not be available`);
1225
1448
  }
1449
+ }
1450
+ function initializeToolRegistry(plugin) {
1226
1451
  const toolRegistry = new ToolRegistry();
1227
1452
  for (const tool2 of builtInTools) {
1228
1453
  toolRegistry.register(tool2);
1229
1454
  }
1230
1455
  plugin.toolRegistry = toolRegistry;
1456
+ return toolRegistry;
1457
+ }
1458
+ function discoverPluginTools(strapi, registry) {
1231
1459
  const pluginNames = Object.keys(strapi.plugins).filter((n) => n !== PLUGIN_ID$2);
1232
1460
  strapi.log.info(`[${PLUGIN_ID$2}] Scanning ${pluginNames.length} plugins for ai-tools: [${pluginNames.join(", ")}]`);
1233
1461
  for (const [pluginName, pluginInstance] of Object.entries(strapi.plugins)) {
1234
1462
  if (pluginName === PLUGIN_ID$2) continue;
1235
1463
  try {
1236
- let aiToolsService = null;
1237
- try {
1238
- aiToolsService = strapi.plugin(pluginName)?.service?.("ai-tools");
1239
- } catch {
1240
- }
1241
- if (!aiToolsService) {
1242
- try {
1243
- aiToolsService = pluginInstance.service?.("ai-tools");
1244
- } catch {
1245
- }
1246
- }
1464
+ const aiToolsService = resolveAiToolsService(strapi, pluginName, pluginInstance);
1247
1465
  if (!aiToolsService?.getTools) {
1248
1466
  strapi.log.debug(`[${PLUGIN_ID$2}] No ai-tools service on plugin: ${pluginName}`);
1249
1467
  continue;
@@ -1251,21 +1469,7 @@ const bootstrap = ({ strapi }) => {
1251
1469
  strapi.log.info(`[${PLUGIN_ID$2}] Found ai-tools service on plugin: ${pluginName}`);
1252
1470
  const contributed = aiToolsService.getTools();
1253
1471
  if (!Array.isArray(contributed)) continue;
1254
- let count = 0;
1255
- for (const tool2 of contributed) {
1256
- if (!tool2.name || !tool2.execute || !tool2.schema) {
1257
- strapi.log.warn(`[${PLUGIN_ID$2}] Invalid tool from ${pluginName}: ${tool2.name || "unnamed"}`);
1258
- continue;
1259
- }
1260
- const safeName = pluginName.replace(/[^a-zA-Z0-9_-]/g, "_");
1261
- const namespacedName = `${safeName}__${tool2.name}`;
1262
- if (toolRegistry.has(namespacedName)) {
1263
- strapi.log.warn(`[${PLUGIN_ID$2}] Duplicate tool: ${namespacedName}`);
1264
- continue;
1265
- }
1266
- toolRegistry.register({ ...tool2, name: namespacedName });
1267
- count++;
1268
- }
1472
+ const count = registerContributedTools(strapi, registry, pluginName, contributed);
1269
1473
  if (count > 0) {
1270
1474
  strapi.log.info(`[${PLUGIN_ID$2}] Registered ${count} tools from plugin: ${pluginName}`);
1271
1475
  }
@@ -1273,10 +1477,38 @@ const bootstrap = ({ strapi }) => {
1273
1477
  strapi.log.warn(`[${PLUGIN_ID$2}] Tool discovery failed for ${pluginName}: ${err}`);
1274
1478
  }
1275
1479
  }
1276
- plugin.createMcpServer = () => createMcpServer(strapi);
1277
- plugin.mcpSessions = /* @__PURE__ */ new Map();
1278
- strapi.log.info(`[${PLUGIN_ID$2}] MCP endpoint available at: /api/${PLUGIN_ID$2}/mcp`);
1279
- };
1480
+ }
1481
+ function registerContributedTools(strapi, registry, pluginName, tools) {
1482
+ const safeName = pluginName.replace(/[^a-zA-Z0-9_-]/g, "_");
1483
+ let count = 0;
1484
+ for (const tool2 of tools) {
1485
+ if (!tool2.name || !tool2.execute || !tool2.schema) {
1486
+ strapi.log.warn(`[${PLUGIN_ID$2}] Invalid tool from ${pluginName}: ${tool2.name || "unnamed"}`);
1487
+ continue;
1488
+ }
1489
+ const namespacedName = `${safeName}__${tool2.name}`;
1490
+ if (registry.has(namespacedName)) {
1491
+ strapi.log.warn(`[${PLUGIN_ID$2}] Duplicate tool: ${namespacedName}`);
1492
+ continue;
1493
+ }
1494
+ registry.register({ ...tool2, name: namespacedName });
1495
+ count++;
1496
+ }
1497
+ return count;
1498
+ }
1499
+ function resolveAiToolsService(strapi, pluginName, pluginInstance) {
1500
+ try {
1501
+ const svc = strapi.plugin(pluginName)?.service?.("ai-tools");
1502
+ if (svc) return svc;
1503
+ } catch {
1504
+ }
1505
+ try {
1506
+ const svc = pluginInstance.service?.("ai-tools");
1507
+ if (svc) return svc;
1508
+ } catch {
1509
+ }
1510
+ return null;
1511
+ }
1280
1512
  const PLUGIN_ID$1 = "ai-sdk";
1281
1513
  async function closeSession(strapi, sessionId, session) {
1282
1514
  try {
@@ -1359,17 +1591,17 @@ const config = {
1359
1591
  }
1360
1592
  }
1361
1593
  };
1362
- const kind$3 = "collectionType";
1363
- const collectionName$3 = "ai_sdk_conversations";
1364
- const info$3 = {
1594
+ const kind$4 = "collectionType";
1595
+ const collectionName$4 = "ai_sdk_conversations";
1596
+ const info$4 = {
1365
1597
  singularName: "conversation",
1366
1598
  pluralName: "conversations",
1367
1599
  displayName: "AI Conversation"
1368
1600
  };
1369
- const options$3 = {
1601
+ const options$4 = {
1370
1602
  draftAndPublish: false
1371
1603
  };
1372
- const pluginOptions$3 = {
1604
+ const pluginOptions$4 = {
1373
1605
  "content-manager": {
1374
1606
  visible: false
1375
1607
  },
@@ -1377,7 +1609,7 @@ const pluginOptions$3 = {
1377
1609
  visible: false
1378
1610
  }
1379
1611
  };
1380
- const attributes$3 = {
1612
+ const attributes$4 = {
1381
1613
  title: {
1382
1614
  type: "string",
1383
1615
  required: true,
@@ -1392,26 +1624,26 @@ const attributes$3 = {
1392
1624
  required: true
1393
1625
  }
1394
1626
  };
1395
- const schema$3 = {
1396
- kind: kind$3,
1397
- collectionName: collectionName$3,
1398
- info: info$3,
1399
- options: options$3,
1400
- pluginOptions: pluginOptions$3,
1401
- 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
1402
1634
  };
1403
- const conversation = { schema: schema$3 };
1404
- const kind$2 = "collectionType";
1405
- const collectionName$2 = "ai_sdk_memories";
1406
- 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 = {
1407
1639
  singularName: "memory",
1408
1640
  pluralName: "memories",
1409
1641
  displayName: "AI Memory"
1410
1642
  };
1411
- const options$2 = {
1643
+ const options$3 = {
1412
1644
  draftAndPublish: false
1413
1645
  };
1414
- const pluginOptions$2 = {
1646
+ const pluginOptions$3 = {
1415
1647
  "content-manager": {
1416
1648
  visible: false
1417
1649
  },
@@ -1419,9 +1651,9 @@ const pluginOptions$2 = {
1419
1651
  visible: false
1420
1652
  }
1421
1653
  };
1422
- const attributes$2 = {
1654
+ const attributes$3 = {
1423
1655
  content: {
1424
- type: "text",
1656
+ type: "richtext",
1425
1657
  required: true
1426
1658
  },
1427
1659
  category: {
@@ -1439,26 +1671,26 @@ const attributes$2 = {
1439
1671
  required: true
1440
1672
  }
1441
1673
  };
1442
- const schema$2 = {
1443
- kind: kind$2,
1444
- collectionName: collectionName$2,
1445
- info: info$2,
1446
- options: options$2,
1447
- pluginOptions: pluginOptions$2,
1448
- 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
1449
1681
  };
1450
- const memory = { schema: schema$2 };
1451
- const kind$1 = "collectionType";
1452
- const collectionName$1 = "ai_sdk_public_memories";
1453
- 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 = {
1454
1686
  singularName: "public-memory",
1455
1687
  pluralName: "public-memories",
1456
1688
  displayName: "AI Public Memory"
1457
1689
  };
1458
- const options$1 = {
1690
+ const options$2 = {
1459
1691
  draftAndPublish: false
1460
1692
  };
1461
- const pluginOptions$1 = {
1693
+ const pluginOptions$2 = {
1462
1694
  "content-manager": {
1463
1695
  visible: false
1464
1696
  },
@@ -1466,7 +1698,7 @@ const pluginOptions$1 = {
1466
1698
  visible: false
1467
1699
  }
1468
1700
  };
1469
- const attributes$1 = {
1701
+ const attributes$2 = {
1470
1702
  content: {
1471
1703
  type: "text",
1472
1704
  required: true
@@ -1482,26 +1714,26 @@ const attributes$1 = {
1482
1714
  "default": "general"
1483
1715
  }
1484
1716
  };
1485
- const schema$1 = {
1486
- kind: kind$1,
1487
- collectionName: collectionName$1,
1488
- info: info$1,
1489
- options: options$1,
1490
- pluginOptions: pluginOptions$1,
1491
- 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
1492
1724
  };
1493
- const publicMemory = { schema: schema$1 };
1494
- const kind = "collectionType";
1495
- const collectionName = "ai_sdk_tasks";
1496
- const info = {
1725
+ const publicMemory = { schema: schema$2 };
1726
+ const kind$1 = "collectionType";
1727
+ const collectionName$1 = "ai_sdk_tasks";
1728
+ const info$1 = {
1497
1729
  singularName: "task",
1498
1730
  pluralName: "tasks",
1499
1731
  displayName: "AI Task"
1500
1732
  };
1501
- const options = {
1733
+ const options$1 = {
1502
1734
  draftAndPublish: false
1503
1735
  };
1504
- const pluginOptions = {
1736
+ const pluginOptions$1 = {
1505
1737
  "content-manager": {
1506
1738
  visible: true
1507
1739
  },
@@ -1509,7 +1741,7 @@ const pluginOptions = {
1509
1741
  visible: false
1510
1742
  }
1511
1743
  };
1512
- const attributes = {
1744
+ const attributes$1 = {
1513
1745
  title: {
1514
1746
  type: "string",
1515
1747
  required: true
@@ -1554,6 +1786,62 @@ const attributes = {
1554
1786
  required: true
1555
1787
  }
1556
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
+ };
1557
1845
  const schema = {
1558
1846
  kind,
1559
1847
  collectionName,
@@ -1562,8 +1850,8 @@ const schema = {
1562
1850
  pluginOptions,
1563
1851
  attributes
1564
1852
  };
1565
- const task = { schema };
1566
- const contentTypes = { conversation, memory, "public-memory": publicMemory, task };
1853
+ const note = { schema };
1854
+ const contentTypes = { conversation, memory, "public-memory": publicMemory, task, note };
1567
1855
  function getService(strapi, ctx) {
1568
1856
  const service2 = strapi.plugin("ai-sdk").service("service");
1569
1857
  if (!service2.isInitialized()) {
@@ -1879,20 +2167,20 @@ const mcpController = ({ strapi }) => {
1879
2167
  }
1880
2168
  };
1881
2169
  };
1882
- const CONTENT_TYPE$3 = "plugin::ai-sdk.conversation";
1883
- function getAdminUserId$2(ctx) {
2170
+ const CONTENT_TYPE$4 = "plugin::ai-sdk.conversation";
2171
+ function getAdminUserId$3(ctx) {
1884
2172
  const id = ctx.state?.user?.id;
1885
2173
  return typeof id === "number" ? id : null;
1886
2174
  }
1887
2175
  const conversationController = ({ strapi }) => ({
1888
2176
  async find(ctx) {
1889
- const adminUserId = getAdminUserId$2(ctx);
2177
+ const adminUserId = getAdminUserId$3(ctx);
1890
2178
  if (!adminUserId) {
1891
2179
  ctx.status = 401;
1892
2180
  ctx.body = { error: "Unauthorized" };
1893
2181
  return;
1894
2182
  }
1895
- const conversations = await strapi.documents(CONTENT_TYPE$3).findMany({
2183
+ const conversations = await strapi.documents(CONTENT_TYPE$4).findMany({
1896
2184
  filters: { adminUserId },
1897
2185
  fields: ["title", "createdAt", "updatedAt"],
1898
2186
  sort: { updatedAt: "desc" }
@@ -1900,14 +2188,14 @@ const conversationController = ({ strapi }) => ({
1900
2188
  ctx.body = { data: conversations };
1901
2189
  },
1902
2190
  async findOne(ctx) {
1903
- const adminUserId = getAdminUserId$2(ctx);
2191
+ const adminUserId = getAdminUserId$3(ctx);
1904
2192
  if (!adminUserId) {
1905
2193
  ctx.status = 401;
1906
2194
  ctx.body = { error: "Unauthorized" };
1907
2195
  return;
1908
2196
  }
1909
2197
  const { id } = ctx.params;
1910
- const conversation2 = await strapi.documents(CONTENT_TYPE$3).findOne({
2198
+ const conversation2 = await strapi.documents(CONTENT_TYPE$4).findOne({
1911
2199
  documentId: id
1912
2200
  });
1913
2201
  if (!conversation2 || conversation2.adminUserId !== adminUserId) {
@@ -1918,14 +2206,14 @@ const conversationController = ({ strapi }) => ({
1918
2206
  ctx.body = { data: conversation2 };
1919
2207
  },
1920
2208
  async create(ctx) {
1921
- const adminUserId = getAdminUserId$2(ctx);
2209
+ const adminUserId = getAdminUserId$3(ctx);
1922
2210
  if (!adminUserId) {
1923
2211
  ctx.status = 401;
1924
2212
  ctx.body = { error: "Unauthorized" };
1925
2213
  return;
1926
2214
  }
1927
2215
  const { title, messages } = ctx.request.body;
1928
- const conversation2 = await strapi.documents(CONTENT_TYPE$3).create({
2216
+ const conversation2 = await strapi.documents(CONTENT_TYPE$4).create({
1929
2217
  data: {
1930
2218
  title: title || "New conversation",
1931
2219
  messages: messages || [],
@@ -1936,14 +2224,14 @@ const conversationController = ({ strapi }) => ({
1936
2224
  ctx.body = { data: conversation2 };
1937
2225
  },
1938
2226
  async update(ctx) {
1939
- const adminUserId = getAdminUserId$2(ctx);
2227
+ const adminUserId = getAdminUserId$3(ctx);
1940
2228
  if (!adminUserId) {
1941
2229
  ctx.status = 401;
1942
2230
  ctx.body = { error: "Unauthorized" };
1943
2231
  return;
1944
2232
  }
1945
2233
  const { id } = ctx.params;
1946
- const existing = await strapi.documents(CONTENT_TYPE$3).findOne({
2234
+ const existing = await strapi.documents(CONTENT_TYPE$4).findOne({
1947
2235
  documentId: id
1948
2236
  });
1949
2237
  if (!existing || existing.adminUserId !== adminUserId) {
@@ -1955,21 +2243,21 @@ const conversationController = ({ strapi }) => ({
1955
2243
  const data = {};
1956
2244
  if (title !== void 0) data.title = title;
1957
2245
  if (messages !== void 0) data.messages = messages;
1958
- const conversation2 = await strapi.documents(CONTENT_TYPE$3).update({
2246
+ const conversation2 = await strapi.documents(CONTENT_TYPE$4).update({
1959
2247
  documentId: id,
1960
2248
  data
1961
2249
  });
1962
2250
  ctx.body = { data: conversation2 };
1963
2251
  },
1964
2252
  async delete(ctx) {
1965
- const adminUserId = getAdminUserId$2(ctx);
2253
+ const adminUserId = getAdminUserId$3(ctx);
1966
2254
  if (!adminUserId) {
1967
2255
  ctx.status = 401;
1968
2256
  ctx.body = { error: "Unauthorized" };
1969
2257
  return;
1970
2258
  }
1971
2259
  const { id } = ctx.params;
1972
- const existing = await strapi.documents(CONTENT_TYPE$3).findOne({
2260
+ const existing = await strapi.documents(CONTENT_TYPE$4).findOne({
1973
2261
  documentId: id
1974
2262
  });
1975
2263
  if (!existing || existing.adminUserId !== adminUserId) {
@@ -1977,25 +2265,25 @@ const conversationController = ({ strapi }) => ({
1977
2265
  ctx.body = { error: "Conversation not found" };
1978
2266
  return;
1979
2267
  }
1980
- await strapi.documents(CONTENT_TYPE$3).delete({ documentId: id });
2268
+ await strapi.documents(CONTENT_TYPE$4).delete({ documentId: id });
1981
2269
  ctx.status = 200;
1982
2270
  ctx.body = { data: { documentId: id } };
1983
2271
  }
1984
2272
  });
1985
- const CONTENT_TYPE$2 = "plugin::ai-sdk.memory";
1986
- function getAdminUserId$1(ctx) {
2273
+ const CONTENT_TYPE$3 = "plugin::ai-sdk.memory";
2274
+ function getAdminUserId$2(ctx) {
1987
2275
  const id = ctx.state?.user?.id;
1988
2276
  return typeof id === "number" ? id : null;
1989
2277
  }
1990
2278
  const memoryController = ({ strapi }) => ({
1991
2279
  async find(ctx) {
1992
- const adminUserId = getAdminUserId$1(ctx);
2280
+ const adminUserId = getAdminUserId$2(ctx);
1993
2281
  if (!adminUserId) {
1994
2282
  ctx.status = 401;
1995
2283
  ctx.body = { error: "Unauthorized" };
1996
2284
  return;
1997
2285
  }
1998
- const memories = await strapi.documents(CONTENT_TYPE$2).findMany({
2286
+ const memories = await strapi.documents(CONTENT_TYPE$3).findMany({
1999
2287
  filters: { adminUserId },
2000
2288
  fields: ["content", "category", "createdAt"],
2001
2289
  sort: { createdAt: "desc" }
@@ -2003,7 +2291,7 @@ const memoryController = ({ strapi }) => ({
2003
2291
  ctx.body = { data: memories };
2004
2292
  },
2005
2293
  async create(ctx) {
2006
- const adminUserId = getAdminUserId$1(ctx);
2294
+ const adminUserId = getAdminUserId$2(ctx);
2007
2295
  if (!adminUserId) {
2008
2296
  ctx.status = 401;
2009
2297
  ctx.body = { error: "Unauthorized" };
@@ -2015,7 +2303,7 @@ const memoryController = ({ strapi }) => ({
2015
2303
  ctx.body = { error: "content is required" };
2016
2304
  return;
2017
2305
  }
2018
- const memory2 = await strapi.documents(CONTENT_TYPE$2).create({
2306
+ const memory2 = await strapi.documents(CONTENT_TYPE$3).create({
2019
2307
  data: {
2020
2308
  content,
2021
2309
  category: category || "general",
@@ -2026,14 +2314,14 @@ const memoryController = ({ strapi }) => ({
2026
2314
  ctx.body = { data: memory2 };
2027
2315
  },
2028
2316
  async update(ctx) {
2029
- const adminUserId = getAdminUserId$1(ctx);
2317
+ const adminUserId = getAdminUserId$2(ctx);
2030
2318
  if (!adminUserId) {
2031
2319
  ctx.status = 401;
2032
2320
  ctx.body = { error: "Unauthorized" };
2033
2321
  return;
2034
2322
  }
2035
2323
  const { id } = ctx.params;
2036
- const existing = await strapi.documents(CONTENT_TYPE$2).findOne({
2324
+ const existing = await strapi.documents(CONTENT_TYPE$3).findOne({
2037
2325
  documentId: id
2038
2326
  });
2039
2327
  if (!existing || existing.adminUserId !== adminUserId) {
@@ -2045,21 +2333,21 @@ const memoryController = ({ strapi }) => ({
2045
2333
  const data = {};
2046
2334
  if (content !== void 0) data.content = content;
2047
2335
  if (category !== void 0) data.category = category;
2048
- const memory2 = await strapi.documents(CONTENT_TYPE$2).update({
2336
+ const memory2 = await strapi.documents(CONTENT_TYPE$3).update({
2049
2337
  documentId: id,
2050
2338
  data
2051
2339
  });
2052
2340
  ctx.body = { data: memory2 };
2053
2341
  },
2054
2342
  async delete(ctx) {
2055
- const adminUserId = getAdminUserId$1(ctx);
2343
+ const adminUserId = getAdminUserId$2(ctx);
2056
2344
  if (!adminUserId) {
2057
2345
  ctx.status = 401;
2058
2346
  ctx.body = { error: "Unauthorized" };
2059
2347
  return;
2060
2348
  }
2061
2349
  const { id } = ctx.params;
2062
- const existing = await strapi.documents(CONTENT_TYPE$2).findOne({
2350
+ const existing = await strapi.documents(CONTENT_TYPE$3).findOne({
2063
2351
  documentId: id
2064
2352
  });
2065
2353
  if (!existing || existing.adminUserId !== adminUserId) {
@@ -2067,15 +2355,15 @@ const memoryController = ({ strapi }) => ({
2067
2355
  ctx.body = { error: "Memory not found" };
2068
2356
  return;
2069
2357
  }
2070
- await strapi.documents(CONTENT_TYPE$2).delete({ documentId: id });
2358
+ await strapi.documents(CONTENT_TYPE$3).delete({ documentId: id });
2071
2359
  ctx.status = 200;
2072
2360
  ctx.body = { data: { documentId: id } };
2073
2361
  }
2074
2362
  });
2075
- const CONTENT_TYPE$1 = "plugin::ai-sdk.public-memory";
2363
+ const CONTENT_TYPE$2 = "plugin::ai-sdk.public-memory";
2076
2364
  const publicMemoryController = ({ strapi }) => ({
2077
2365
  async find(ctx) {
2078
- const memories = await strapi.documents(CONTENT_TYPE$1).findMany({
2366
+ const memories = await strapi.documents(CONTENT_TYPE$2).findMany({
2079
2367
  fields: ["content", "category", "createdAt"],
2080
2368
  sort: { createdAt: "desc" }
2081
2369
  });
@@ -2088,7 +2376,7 @@ const publicMemoryController = ({ strapi }) => ({
2088
2376
  ctx.body = { error: "content is required" };
2089
2377
  return;
2090
2378
  }
2091
- const memory2 = await strapi.documents(CONTENT_TYPE$1).create({
2379
+ const memory2 = await strapi.documents(CONTENT_TYPE$2).create({
2092
2380
  data: { content, category: category || "general" }
2093
2381
  });
2094
2382
  ctx.status = 201;
@@ -2096,7 +2384,7 @@ const publicMemoryController = ({ strapi }) => ({
2096
2384
  },
2097
2385
  async update(ctx) {
2098
2386
  const { id } = ctx.params;
2099
- const existing = await strapi.documents(CONTENT_TYPE$1).findOne({ documentId: id });
2387
+ const existing = await strapi.documents(CONTENT_TYPE$2).findOne({ documentId: id });
2100
2388
  if (!existing) {
2101
2389
  ctx.status = 404;
2102
2390
  ctx.body = { error: "Public memory not found" };
@@ -2106,7 +2394,7 @@ const publicMemoryController = ({ strapi }) => ({
2106
2394
  const data = {};
2107
2395
  if (content !== void 0) data.content = content;
2108
2396
  if (category !== void 0) data.category = category;
2109
- const memory2 = await strapi.documents(CONTENT_TYPE$1).update({
2397
+ const memory2 = await strapi.documents(CONTENT_TYPE$2).update({
2110
2398
  documentId: id,
2111
2399
  data
2112
2400
  });
@@ -2114,45 +2402,45 @@ const publicMemoryController = ({ strapi }) => ({
2114
2402
  },
2115
2403
  async delete(ctx) {
2116
2404
  const { id } = ctx.params;
2117
- const existing = await strapi.documents(CONTENT_TYPE$1).findOne({ documentId: id });
2405
+ const existing = await strapi.documents(CONTENT_TYPE$2).findOne({ documentId: id });
2118
2406
  if (!existing) {
2119
2407
  ctx.status = 404;
2120
2408
  ctx.body = { error: "Public memory not found" };
2121
2409
  return;
2122
2410
  }
2123
- await strapi.documents(CONTENT_TYPE$1).delete({ documentId: id });
2411
+ await strapi.documents(CONTENT_TYPE$2).delete({ documentId: id });
2124
2412
  ctx.status = 200;
2125
2413
  ctx.body = { data: { documentId: id } };
2126
2414
  }
2127
2415
  });
2128
- const CONTENT_TYPE = "plugin::ai-sdk.task";
2129
- function getAdminUserId(ctx) {
2416
+ const CONTENT_TYPE$1 = "plugin::ai-sdk.task";
2417
+ function getAdminUserId$1(ctx) {
2130
2418
  const id = ctx.state?.user?.id;
2131
2419
  return typeof id === "number" ? id : null;
2132
2420
  }
2133
2421
  const taskController = ({ strapi }) => ({
2134
2422
  async find(ctx) {
2135
- const adminUserId = getAdminUserId(ctx);
2423
+ const adminUserId = getAdminUserId$1(ctx);
2136
2424
  if (!adminUserId) {
2137
2425
  ctx.status = 401;
2138
2426
  ctx.body = { error: "Unauthorized" };
2139
2427
  return;
2140
2428
  }
2141
- const tasks = await strapi.documents(CONTENT_TYPE).findMany({
2429
+ const tasks = await strapi.documents(CONTENT_TYPE$1).findMany({
2142
2430
  filters: { adminUserId },
2143
2431
  sort: { createdAt: "desc" }
2144
2432
  });
2145
2433
  ctx.body = { data: tasks };
2146
2434
  },
2147
2435
  async create(ctx) {
2148
- const adminUserId = getAdminUserId(ctx);
2436
+ const adminUserId = getAdminUserId$1(ctx);
2149
2437
  if (!adminUserId) {
2150
2438
  ctx.status = 401;
2151
2439
  ctx.body = { error: "Unauthorized" };
2152
2440
  return;
2153
2441
  }
2154
2442
  const body = ctx.request.body;
2155
- const task2 = await strapi.documents(CONTENT_TYPE).create({
2443
+ const task2 = await strapi.documents(CONTENT_TYPE$1).create({
2156
2444
  data: {
2157
2445
  title: body.title,
2158
2446
  description: body.description,
@@ -2169,14 +2457,14 @@ const taskController = ({ strapi }) => ({
2169
2457
  ctx.body = { data: task2 };
2170
2458
  },
2171
2459
  async update(ctx) {
2172
- const adminUserId = getAdminUserId(ctx);
2460
+ const adminUserId = getAdminUserId$1(ctx);
2173
2461
  if (!adminUserId) {
2174
2462
  ctx.status = 401;
2175
2463
  ctx.body = { error: "Unauthorized" };
2176
2464
  return;
2177
2465
  }
2178
2466
  const { id } = ctx.params;
2179
- const existing = await strapi.documents(CONTENT_TYPE).findOne({
2467
+ const existing = await strapi.documents(CONTENT_TYPE$1).findOne({
2180
2468
  documentId: id
2181
2469
  });
2182
2470
  if (!existing || existing.adminUserId !== adminUserId) {
@@ -2189,13 +2477,80 @@ const taskController = ({ strapi }) => ({
2189
2477
  for (const key of ["title", "description", "content", "done", "priority", "consequence", "impact", "dueDate"]) {
2190
2478
  if (body[key] !== void 0) data[key] = body[key];
2191
2479
  }
2192
- const task2 = await strapi.documents(CONTENT_TYPE).update({
2480
+ const task2 = await strapi.documents(CONTENT_TYPE$1).update({
2193
2481
  documentId: id,
2194
2482
  data
2195
2483
  });
2196
2484
  ctx.body = { data: task2 };
2197
2485
  },
2198
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) {
2199
2554
  const adminUserId = getAdminUserId(ctx);
2200
2555
  if (!adminUserId) {
2201
2556
  ctx.status = 401;
@@ -2208,12 +2563,58 @@ const taskController = ({ strapi }) => ({
2208
2563
  });
2209
2564
  if (!existing || existing.adminUserId !== adminUserId) {
2210
2565
  ctx.status = 404;
2211
- 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" };
2212
2596
  return;
2213
2597
  }
2214
2598
  await strapi.documents(CONTENT_TYPE).delete({ documentId: id });
2215
2599
  ctx.status = 200;
2216
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 } };
2217
2618
  }
2218
2619
  });
2219
2620
  const controllers = {
@@ -2222,7 +2623,8 @@ const controllers = {
2222
2623
  conversation: conversationController,
2223
2624
  memory: memoryController,
2224
2625
  "public-memory": publicMemoryController,
2225
- task: taskController
2626
+ task: taskController,
2627
+ note: noteController
2226
2628
  };
2227
2629
  const promptInjection = [
2228
2630
  "ignore (all |any )?(previous|prior|above) (instructions|prompts|rules)",
@@ -2592,6 +2994,36 @@ const adminAPIRoutes = {
2592
2994
  path: "/tasks/:id",
2593
2995
  handler: "task.delete",
2594
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: [] }
2595
3027
  }
2596
3028
  ]
2597
3029
  };