@formio/uag 1.13.2 → 2.0.0-rc.10

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 (115) hide show
  1. package/README.md +237 -535
  2. package/bin/uag-eval.js +99 -0
  3. package/bin/uag.js +116 -0
  4. package/lib/UAGFormInterface.d.ts +72 -2
  5. package/lib/UAGFormInterface.js +226 -57
  6. package/lib/UAGProjectInterface.d.ts +45 -5
  7. package/lib/UAGProjectInterface.js +187 -36
  8. package/lib/actionLogs.d.ts +24 -0
  9. package/lib/actionLogs.js +149 -0
  10. package/lib/actions/runTask.d.ts +43 -0
  11. package/lib/actions/runTask.js +324 -0
  12. package/lib/agent/anonymous.d.ts +34 -0
  13. package/lib/agent/anonymous.js +99 -0
  14. package/lib/agent/chat.d.ts +114 -0
  15. package/lib/agent/chat.js +475 -0
  16. package/lib/agent/engines.d.ts +17 -0
  17. package/lib/agent/engines.js +46 -0
  18. package/lib/agent/errors.d.ts +25 -0
  19. package/lib/agent/errors.js +132 -0
  20. package/lib/agent/index.d.ts +4 -0
  21. package/lib/agent/index.js +19 -0
  22. package/lib/agent/remote.d.ts +71 -0
  23. package/lib/agent/remote.js +252 -0
  24. package/lib/agent/requestId.d.ts +4 -0
  25. package/lib/agent/requestId.js +19 -0
  26. package/lib/agent/router.d.ts +61 -0
  27. package/lib/agent/router.js +307 -0
  28. package/lib/agent/stubs.d.ts +37 -0
  29. package/lib/agent/stubs.js +39 -0
  30. package/lib/agent/tools.d.ts +31 -0
  31. package/lib/agent/tools.js +76 -0
  32. package/lib/config.d.ts +97 -3
  33. package/lib/config.js +19 -0
  34. package/lib/database.d.ts +70 -0
  35. package/lib/database.js +212 -0
  36. package/lib/documents/convert.d.ts +38 -0
  37. package/lib/documents/convert.js +151 -0
  38. package/lib/documents/converters.d.ts +28 -0
  39. package/lib/documents/converters.js +149 -0
  40. package/lib/documents/detect.d.ts +31 -0
  41. package/lib/documents/detect.js +169 -0
  42. package/lib/documents/index.d.ts +9 -0
  43. package/lib/documents/index.js +43 -0
  44. package/lib/documents/mime.d.ts +39 -0
  45. package/lib/documents/mime.js +100 -0
  46. package/lib/documents/plugins.d.ts +18 -0
  47. package/lib/documents/plugins.js +65 -0
  48. package/lib/documents/readers.d.ts +19 -0
  49. package/lib/documents/readers.js +64 -0
  50. package/lib/documents/routes.d.ts +52 -0
  51. package/lib/documents/routes.js +99 -0
  52. package/lib/documents/types.d.ts +126 -0
  53. package/lib/documents/types.js +2 -0
  54. package/lib/documents/uagJs.d.ts +17 -0
  55. package/lib/documents/uagJs.js +157 -0
  56. package/lib/formflow.d.ts +47 -0
  57. package/lib/formflow.js +28 -0
  58. package/lib/identity.d.ts +53 -0
  59. package/lib/identity.js +107 -0
  60. package/lib/index.d.ts +98 -1
  61. package/lib/index.js +382 -8
  62. package/lib/markdown.d.ts +1 -0
  63. package/lib/markdown.js +166 -0
  64. package/lib/modules/api.d.ts +25 -0
  65. package/lib/modules/api.js +98 -0
  66. package/lib/modules/index.d.ts +4 -0
  67. package/lib/modules/index.js +24 -0
  68. package/lib/modules/routes.d.ts +14 -0
  69. package/lib/modules/routes.js +21 -0
  70. package/lib/modules/types.d.ts +68 -0
  71. package/lib/modules/types.js +5 -0
  72. package/lib/pluginFiles.d.ts +28 -0
  73. package/lib/pluginFiles.js +101 -0
  74. package/lib/plugins.d.ts +17 -0
  75. package/lib/plugins.js +93 -0
  76. package/lib/richText.d.ts +20 -0
  77. package/lib/richText.js +99 -0
  78. package/lib/router.d.ts +9 -0
  79. package/lib/router.js +34 -25
  80. package/lib/runner.d.ts +149 -0
  81. package/lib/runner.js +353 -0
  82. package/lib/runs.d.ts +27 -0
  83. package/lib/runs.js +13 -0
  84. package/lib/template.js +7 -9
  85. package/lib/templates/uagComponentNotFound.md +5 -5
  86. package/lib/testing.d.ts +74 -0
  87. package/lib/testing.js +180 -0
  88. package/lib/tools/SchemaBuilder.d.ts +3 -3
  89. package/lib/tools/SchemaBuilder.js +4 -4
  90. package/lib/tools/agentDecide.d.ts +8 -0
  91. package/lib/tools/agentDecide.js +57 -0
  92. package/lib/tools/agentProvide.d.ts +8 -0
  93. package/lib/tools/agentProvide.js +97 -0
  94. package/lib/tools/collectData.js +3 -2
  95. package/lib/tools/confirmSubmission.js +1 -1
  96. package/lib/tools/fetchExternalData.js +8 -17
  97. package/lib/tools/findSubmission.js +62 -56
  98. package/lib/tools/getFieldInfo.js +1 -1
  99. package/lib/tools/getFormFields.js +1 -1
  100. package/lib/tools/getForms.js +1 -1
  101. package/lib/tools/index.d.ts +4 -1
  102. package/lib/tools/index.js +21 -14
  103. package/lib/tools/readDocument.d.ts +17 -0
  104. package/lib/tools/readDocument.js +99 -0
  105. package/lib/tools/submissionUpdate.js +4 -3
  106. package/lib/tools/submitForm.js +1 -1
  107. package/lib/tools/utils.d.ts +40 -0
  108. package/lib/tools/utils.js +16 -1
  109. package/lib/version.d.ts +2 -0
  110. package/lib/version.js +5 -0
  111. package/package.json +49 -19
  112. package/testing.d.ts +1 -0
  113. package/testing.js +2 -0
  114. package/lib/tools/agentProvideData.d.ts +0 -3
  115. package/lib/tools/agentProvideData.js +0 -62
@@ -7,7 +7,7 @@ const SchemaBuilder_1 = require("./SchemaBuilder");
7
7
  const annotations_1 = require("./annotations");
8
8
  const error = require('debug')('formio:uag:getFieldInfo:error');
9
9
  const getFieldInfo = async (project) => {
10
- return (0, lodash_1.defaultsDeep)(project.config?.toolOverrides?.get_field_info || {}, {
10
+ return (0, lodash_1.defaultsDeep)({}, project.config?.toolOverrides?.get_field_info, {
11
11
  name: 'get_field_info',
12
12
  title: 'Get Field(s) Info',
13
13
  description: 'Get detailed information about a specific field(s) that have been provided from the user. It is used to understand the properties, validation rules, and options of the specific field(s) the user has provided values for. This purpose of this tool is to provide detailed information for any field(s) (provided using the `field_paths` parameter) to help understand the structure and requirements of a form. PREREQUISITE: This tool should be called after the `get_form_fields` tool has been called AND once the user has provided some values for the provided fields.',
@@ -7,7 +7,7 @@ const SchemaBuilder_1 = require("./SchemaBuilder");
7
7
  const annotations_1 = require("./annotations");
8
8
  const error = require('debug')('formio:uag:getFormFields:error');
9
9
  const getFormFields = async (project) => {
10
- return (0, lodash_1.defaultsDeep)(project.config?.toolOverrides?.get_form_fields || {}, {
10
+ return (0, lodash_1.defaultsDeep)({}, project.config?.toolOverrides?.get_form_fields, {
11
11
  name: 'get_form_fields',
12
12
  title: 'Get Form Fields',
13
13
  description: 'Get high level overview of the fields that are present in a form, and to understand the "rules" on how the data for each field type should be collected. The purpose of this tool is to determine what fields the user is requesting (and to understand how to format that values for that data), and use that to create a list of field data_path\'s that the user is providing context for. This list can then be provided to the `get_field_info` tool to determine the specific field level information for those fields (validation, required, collection rules, etc). PREREQUISITE: You must call the `get_forms` tool first to understand what forms are available to submit and the permissions associated with those forms.',
@@ -5,7 +5,7 @@ const lodash_1 = require("lodash");
5
5
  const template_1 = require("../template");
6
6
  const annotations_1 = require("./annotations");
7
7
  const getForms = async (project) => {
8
- return (0, lodash_1.defaultsDeep)(project.config?.toolOverrides?.get_forms || {}, {
8
+ return (0, lodash_1.defaultsDeep)({}, project.config?.toolOverrides?.get_forms, {
9
9
  name: 'get_forms',
10
10
  title: 'Get Available Forms',
11
11
  description: 'Get a list of all available forms with their descriptions. Use this tool when a user expresses intent to add, create, submit, fill out, register for something (e.g., "I want to add a new Contact", "I need to submit a User Registration", "I met a contact who was the CMO", "What was the email of the contact"). This tool can also be used when they ask what forms are available.',
@@ -8,8 +8,11 @@ import { findSubmission } from "./findSubmission";
8
8
  import { submissionUpdate } from "./submissionUpdate";
9
9
  import { ToolInfo } from "./utils";
10
10
  import { UAGProjectInterface } from "../UAGProjectInterface";
11
+ import { agentProvide } from "./agentProvide";
12
+ import { agentDecide } from "./agentDecide";
11
13
  import { fetchExternalData } from "./fetchExternalData";
14
+ import { readDocument } from "./readDocument";
12
15
  export * from './utils';
13
16
  export * from './annotations';
14
- export { getForms, getFormFields, getFieldInfo, collectData, confirmSubmission, submitCompletedForm, findSubmission, submissionUpdate, fetchExternalData };
17
+ export { getForms, getFormFields, getFieldInfo, collectData, confirmSubmission, submitCompletedForm, findSubmission, submissionUpdate, fetchExternalData, agentProvide, agentDecide, readDocument };
15
18
  export declare const getTools: (project: UAGProjectInterface) => Promise<ToolInfo[]>;
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.getTools = exports.fetchExternalData = exports.submissionUpdate = exports.findSubmission = exports.submitCompletedForm = exports.confirmSubmission = exports.collectData = exports.getFieldInfo = exports.getFormFields = exports.getForms = void 0;
17
+ exports.getTools = exports.readDocument = exports.agentDecide = exports.agentProvide = exports.fetchExternalData = exports.submissionUpdate = exports.findSubmission = exports.submitCompletedForm = exports.confirmSubmission = exports.collectData = exports.getFieldInfo = exports.getFormFields = exports.getForms = void 0;
18
18
  const getForms_1 = require("./getForms");
19
19
  Object.defineProperty(exports, "getForms", { enumerable: true, get: function () { return getForms_1.getForms; } });
20
20
  const getFormFields_1 = require("./getFormFields");
@@ -31,23 +31,30 @@ const findSubmission_1 = require("./findSubmission");
31
31
  Object.defineProperty(exports, "findSubmission", { enumerable: true, get: function () { return findSubmission_1.findSubmission; } });
32
32
  const submissionUpdate_1 = require("./submissionUpdate");
33
33
  Object.defineProperty(exports, "submissionUpdate", { enumerable: true, get: function () { return submissionUpdate_1.submissionUpdate; } });
34
- const agentProvideData_1 = require("./agentProvideData");
34
+ const agentProvide_1 = require("./agentProvide");
35
+ Object.defineProperty(exports, "agentProvide", { enumerable: true, get: function () { return agentProvide_1.agentProvide; } });
36
+ const agentDecide_1 = require("./agentDecide");
37
+ Object.defineProperty(exports, "agentDecide", { enumerable: true, get: function () { return agentDecide_1.agentDecide; } });
35
38
  const fetchExternalData_1 = require("./fetchExternalData");
36
39
  Object.defineProperty(exports, "fetchExternalData", { enumerable: true, get: function () { return fetchExternalData_1.fetchExternalData; } });
40
+ const readDocument_1 = require("./readDocument");
41
+ Object.defineProperty(exports, "readDocument", { enumerable: true, get: function () { return readDocument_1.readDocument; } });
37
42
  __exportStar(require("./utils"), exports);
38
43
  __exportStar(require("./annotations"), exports);
39
44
  const getTools = async (project) => {
40
- return [
41
- await (0, getForms_1.getForms)(project),
42
- await (0, getFormFields_1.getFormFields)(project),
43
- await (0, getFieldInfo_1.getFieldInfo)(project),
44
- await (0, collectData_1.collectData)(project),
45
- await (0, confirmSubmission_1.confirmSubmission)(project),
46
- await (0, submitForm_1.submitCompletedForm)(project),
47
- await (0, findSubmission_1.findSubmission)(project),
48
- await (0, submissionUpdate_1.submissionUpdate)(project),
49
- await (0, agentProvideData_1.agentProvideData)(project),
50
- await (0, fetchExternalData_1.fetchExternalData)(project)
51
- ];
45
+ return Promise.all([
46
+ (0, getForms_1.getForms)(project),
47
+ (0, getFormFields_1.getFormFields)(project),
48
+ (0, getFieldInfo_1.getFieldInfo)(project),
49
+ (0, collectData_1.collectData)(project),
50
+ (0, confirmSubmission_1.confirmSubmission)(project),
51
+ (0, submitForm_1.submitCompletedForm)(project),
52
+ (0, findSubmission_1.findSubmission)(project),
53
+ (0, submissionUpdate_1.submissionUpdate)(project),
54
+ (0, agentProvide_1.agentProvide)(project),
55
+ (0, agentDecide_1.agentDecide)(project),
56
+ (0, fetchExternalData_1.fetchExternalData)(project),
57
+ (0, readDocument_1.readDocument)(project)
58
+ ]);
52
59
  };
53
60
  exports.getTools = getTools;
@@ -0,0 +1,17 @@
1
+ import type { UAGProjectInterface } from '../UAGProjectInterface';
2
+ import { ToolInfo } from './utils';
3
+ /**
4
+ * read_document's limits: the largest file it reads (`documents.maxFileBytes`,
5
+ * else DOCUMENTS_MAX_FILE_BYTES, 25 MB), and the most characters of text it
6
+ * returns (`agentApi.files.maxChars`, as task inputs are cut, 200000).
7
+ */
8
+ export declare function readDocumentLimits(config: any, env?: NodeJS.ProcessEnv): {
9
+ maxBytes: number;
10
+ maxChars: number;
11
+ };
12
+ /**
13
+ * read_document: the text of an uploaded document in a submission (a file in a
14
+ * file component, or in an AI Document's files), read through the installed
15
+ * converters with the caller's identity. Read-only.
16
+ */
17
+ export declare const readDocument: (project: UAGProjectInterface) => Promise<ToolInfo>;
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.readDocument = void 0;
7
+ exports.readDocumentLimits = readDocumentLimits;
8
+ const zod_1 = __importDefault(require("zod"));
9
+ const lodash_1 = require("lodash");
10
+ const core_1 = require("@formio/core");
11
+ const template_1 = require("../template");
12
+ const annotations_1 = require("./annotations");
13
+ const readers_1 = require("../documents/readers");
14
+ const DEFAULT_MAX_FILE_BYTES = 25 * 1024 * 1024;
15
+ const DEFAULT_MAX_CHARS = 200000;
16
+ const positive = (value) => (Number(value) > 0 ? Number(value) : undefined);
17
+ /**
18
+ * read_document's limits: the largest file it reads (`documents.maxFileBytes`,
19
+ * else DOCUMENTS_MAX_FILE_BYTES, 25 MB), and the most characters of text it
20
+ * returns (`agentApi.files.maxChars`, as task inputs are cut, 200000).
21
+ */
22
+ function readDocumentLimits(config, env = process.env) {
23
+ return {
24
+ maxBytes: positive(config?.documents?.maxFileBytes) || positive(env.DOCUMENTS_MAX_FILE_BYTES) || DEFAULT_MAX_FILE_BYTES,
25
+ maxChars: positive(config?.agentApi?.files?.maxChars) || DEFAULT_MAX_CHARS
26
+ };
27
+ }
28
+ /** The component at a data path, row indices removed (`rows[2].contract` is `rows.contract`). */
29
+ function componentAt(components, dataPath) {
30
+ const path = dataPath.replace(/\[\d+\]/g, '');
31
+ let found;
32
+ // The data path: with every component included, the second argument also names layout components (panels, columns).
33
+ core_1.Utils.eachComponent(components, (component, componentPath, _components, _parent, paths) => {
34
+ if (!found && (paths?.path ?? componentPath) === path)
35
+ found = component;
36
+ }, true);
37
+ return found;
38
+ }
39
+ /**
40
+ * read_document: the text of an uploaded document in a submission (a file in a
41
+ * file component, or in an AI Document's files), read through the installed
42
+ * converters with the caller's identity. Read-only.
43
+ */
44
+ const readDocument = async (project) => {
45
+ return (0, lodash_1.defaultsDeep)({}, project.config?.toolOverrides?.read_document, {
46
+ name: 'read_document',
47
+ title: 'Read Uploaded Document',
48
+ description: 'Read the text of a document uploaded to a submission: a file in a file field, or in an AI Document field. ' +
49
+ 'Give the form, the submission id (from `find_submissions`) and the data path of the field (such as "contract" or "rows[0].attachment"; ' +
50
+ 'add an index, such as "contract[1]", for another file than the first). Returns the file name, its type, its page count when known, and its text; ' +
51
+ 'a long text is cut, and `truncated` is true.',
52
+ annotations: (0, annotations_1.reads)('Read an uploaded document'),
53
+ inputSchema: {
54
+ form_name: zod_1.default.string().describe('The name of the form, as `get_forms` lists it.'),
55
+ submission_id: zod_1.default.string().describe('The full ID of the submission, as `find_submissions` gives it.'),
56
+ data_path: zod_1.default.string().describe('The data path of the file field or AI Document field.')
57
+ },
58
+ execute: async ({ form_name, submission_id, data_path }, extra) => {
59
+ const form = await project.getForm(form_name);
60
+ if (!form) {
61
+ return project.mcpResponse(template_1.ResponseTemplate.formNotFound, { formName: form_name }, true);
62
+ }
63
+ const submission = await form.loadSubmission(submission_id, extra.authInfo);
64
+ if (!submission) {
65
+ return project.mcpResponse(template_1.ResponseTemplate.submissionNotFound, { submissionId: submission_id, form: form.form }, true);
66
+ }
67
+ // An index on the last segment picks a file of a multiple field: contract[1].
68
+ const index = /\[(\d+)\]$/.exec(data_path);
69
+ const fieldPath = index ? data_path.slice(0, index.index) : data_path;
70
+ const value = (0, lodash_1.get)(submission.data, fieldPath);
71
+ const isDocument = value && typeof value === 'object' && !Array.isArray(value) && Array.isArray(value.files);
72
+ const files = isDocument ? value.files : Array.isArray(value) ? value : (value && typeof value === 'object' ? [value] : []);
73
+ const file = files[index ? Number(index[1]) : 0];
74
+ const component = componentAt(form.form?.components || [], isDocument ? `${fieldPath}.files` : fieldPath);
75
+ if (!file || !component || component.type !== 'file') {
76
+ return project.mcpJSONResponse({ error: `No uploaded file at "${data_path}" in that submission. Give the data path of a file field or an AI Document field.` }, true);
77
+ }
78
+ if (!project.plugins) {
79
+ return project.mcpJSONResponse({ error: 'Documents cannot be read until the server has started.' }, true);
80
+ }
81
+ const limits = readDocumentLimits(project.config);
82
+ try {
83
+ const buffer = await form.readFile(file, extra.authInfo, { maxBytes: limits.maxBytes, component });
84
+ if (!buffer) {
85
+ return project.mcpJSONResponse({ error: `The file is stored as ${file.storage || 'unknown'}, which cannot be read here.` }, true);
86
+ }
87
+ const name = String(file.originalName || file.name || 'file');
88
+ const read = await (0, readers_1.readAsText)(project.plugins, buffer, { name, type: typeof file.type === 'string' ? file.type : undefined }, { signal: extra?.signal });
89
+ const truncated = read.text.length > limits.maxChars;
90
+ return project.mcpJSONResponse({ file_name: name, type: read.type, pages: read.pages ?? null, text: truncated ? read.text.slice(0, limits.maxChars) : read.text, truncated });
91
+ }
92
+ catch (err) {
93
+ // The message names the file and the reason, never its content.
94
+ return project.mcpJSONResponse({ error: err?.message || 'The file could not be read.' }, true);
95
+ }
96
+ }
97
+ });
98
+ };
99
+ exports.readDocument = readDocument;
@@ -7,7 +7,7 @@ const SchemaBuilder_1 = require("./SchemaBuilder");
7
7
  const annotations_1 = require("./annotations");
8
8
  const error = require('debug')('formio:uag:submissionUpdate:error');
9
9
  const submissionUpdate = async (project) => {
10
- return (0, lodash_1.defaultsDeep)(project.config?.toolOverrides?.submission_update || {}, {
10
+ return (0, lodash_1.defaultsDeep)({}, project.config?.toolOverrides?.submission_update, {
11
11
  name: 'submission_update',
12
12
  title: 'Submission Update',
13
13
  description: 'Apply multiple planned field updates to a selected submission after the user confirmed they wish to update the record.',
@@ -35,10 +35,11 @@ const submissionUpdate = async (project) => {
35
35
  const updateSummary = [];
36
36
  for (const update of updates) {
37
37
  const previousValue = (0, lodash_1.get)(updatedData, update.data_path);
38
- (0, lodash_1.set)(updatedData, update.data_path, update.new_value);
38
+ const newValue = form.normalizeWrittenValue(update.data_path, update.new_value);
39
+ (0, lodash_1.set)(updatedData, update.data_path, newValue);
39
40
  updateSummary.push({
40
41
  data_path: update.data_path,
41
- new_value: update.new_value,
42
+ new_value: newValue,
42
43
  previous_value: previousValue || ''
43
44
  });
44
45
  }
@@ -7,7 +7,7 @@ const SchemaBuilder_1 = require("./SchemaBuilder");
7
7
  const annotations_1 = require("./annotations");
8
8
  const debug = require('debug')('formio:uag:submitForm');
9
9
  const submitCompletedForm = async (project) => {
10
- return (0, lodash_1.defaultsDeep)(project.config?.toolOverrides?.submit_completed_form || {}, {
10
+ return (0, lodash_1.defaultsDeep)({}, project.config?.toolOverrides?.submit_completed_form, {
11
11
  name: 'submit_completed_form',
12
12
  title: 'Submit Completed Form',
13
13
  description: 'Submit the completed form data. Should only be used once all the required fields have been collected, and the user has explicitly confirmed submission (e.g. has said "submit", "send", "done", etc)',
@@ -1,5 +1,7 @@
1
1
  import { ZodRawShape } from "zod";
2
2
  import { ToolAnnotations } from "./annotations";
3
+ import type { AuthRequest } from "@formio/appserver";
4
+ import type { UAGProjectInterface } from "../UAGProjectInterface";
3
5
  /**
4
6
  * Escape a literal value so it can be carried in a `__regex` query parameter.
5
7
  *
@@ -20,14 +22,52 @@ import { ToolAnnotations } from "./annotations";
20
22
  * @returns The value as a regular expression pattern that matches it literally.
21
23
  */
22
24
  export declare const escapeSearchPattern: (value: string) => string;
25
+ /**
26
+ * What a tool's `execute` receives besides its arguments, on every path that
27
+ * runs it: MCP clients on `/mcp`, and Agent API research in process.
28
+ */
29
+ export type ToolExtra = {
30
+ /** The caller's identity: who the work runs as. Pass it to form methods so Form.io applies its permissions. */
31
+ authInfo: AuthRequest;
32
+ /** The project serving the request. */
33
+ project: UAGProjectInterface;
34
+ /** For a tool from a module with a name and an apiVersion: the module's API (its settings, engines, ...). */
35
+ module?: import('../modules').UAGModuleApi;
36
+ /** On `/mcp`, the rest of the MCP SDK's request context (signal, request id, ...). */
37
+ [key: string]: any;
38
+ };
23
39
  export type ToolInfo = {
24
40
  name?: string;
25
41
  title?: string;
26
42
  description?: string;
27
43
  inputSchema?: ZodRawShape | undefined;
28
44
  annotations?: ToolAnnotations;
45
+ /**
46
+ * Runs the tool: always called with its arguments (an empty object when it
47
+ * takes none), then a `ToolExtra`. Typed loosely so existing tools and their
48
+ * callers keep compiling; annotate `extra` as `ToolExtra` in your own tools.
49
+ */
29
50
  execute?: any;
51
+ /**
52
+ * Builds the project-specific part of the tool (`inputSchema`, `annotations`,
53
+ * `execute`, ...) for a project, as the built-in tools are built. The static
54
+ * `name`, `title` and `description` are used unless `bind` returns its own,
55
+ * so the tool's name is known without a project. Called whenever the tool
56
+ * list is built (every `/mcp` request and every research plan): keep it
57
+ * cheap, and load libraries and other slow state once, outside it.
58
+ */
59
+ bind?: (project: UAGProjectInterface) => Partial<ToolInfo> | Promise<Partial<ToolInfo>>;
30
60
  };
61
+ /** An entry of `config.tools`: a tool, optionally bound to the project with `bind`. */
62
+ export type ToolEntry = ToolInfo;
63
+ /** The shape a `config.tools` entry takes, shown when one is a bare function. */
64
+ export declare const BOUND_TOOL_SHAPE = "{ name: 'my_tool', title: 'My Tool', description: '...', bind(project) { return { inputSchema, annotations, execute }; } }";
65
+ /**
66
+ * Refuse `config.tools` entries that are bare functions (the unreleased factory
67
+ * form): a tool is an object, whose `bind(project)` builds its project-specific
68
+ * part. Throws naming the entry and showing the shape.
69
+ */
70
+ export declare function assertToolEntries(entries: unknown[] | undefined): void;
31
71
  export type ParentInfo = {
32
72
  type: string;
33
73
  label?: string;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.escapeSearchPattern = void 0;
3
+ exports.BOUND_TOOL_SHAPE = exports.escapeSearchPattern = void 0;
4
+ exports.assertToolEntries = assertToolEntries;
4
5
  const lodash_1 = require("lodash");
5
6
  /**
6
7
  * Escape a literal value so it can be carried in a `__regex` query parameter.
@@ -23,3 +24,17 @@ const lodash_1 = require("lodash");
23
24
  */
24
25
  const escapeSearchPattern = (value) => (0, lodash_1.escapeRegExp)(value).replace(/\//g, '\\x2f');
25
26
  exports.escapeSearchPattern = escapeSearchPattern;
27
+ /** The shape a `config.tools` entry takes, shown when one is a bare function. */
28
+ exports.BOUND_TOOL_SHAPE = "{ name: 'my_tool', title: 'My Tool', description: '...', bind(project) { return { inputSchema, annotations, execute }; } }";
29
+ /**
30
+ * Refuse `config.tools` entries that are bare functions (the unreleased factory
31
+ * form): a tool is an object, whose `bind(project)` builds its project-specific
32
+ * part. Throws naming the entry and showing the shape.
33
+ */
34
+ function assertToolEntries(entries) {
35
+ (entries || []).forEach((entry, index) => {
36
+ if (typeof entry === 'function') {
37
+ throw new Error(`config.tools[${index}] is a function. A custom tool is an object; build its project-specific part with bind(project): ${exports.BOUND_TOOL_SHAPE}`);
38
+ }
39
+ });
40
+ }
@@ -0,0 +1,2 @@
1
+ /** The UAG's own version, from its package.json (src/ and lib/ both sit one level below it). */
2
+ export declare const UAG_VERSION: string;
package/lib/version.js ADDED
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.UAG_VERSION = void 0;
4
+ /** The UAG's own version, from its package.json (src/ and lib/ both sit one level below it). */
5
+ exports.UAG_VERSION = require('../package.json').version;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@formio/uag",
3
- "version": "1.13.2",
3
+ "version": "2.0.0-rc.10",
4
4
  "mcpName": "io.form/formio-uag",
5
5
  "description": "The Form.io Universal Agent Gateway (UAG).",
6
6
  "main": "lib/index.js",
@@ -9,19 +9,35 @@
9
9
  "node": ">=22.0.0"
10
10
  },
11
11
  "files": [
12
- "lib"
12
+ "lib",
13
+ "bin",
14
+ "testing.js",
15
+ "testing.d.ts"
16
+ ],
17
+ "workspaces": [
18
+ "engines/*",
19
+ "packages/*",
20
+ "demo"
13
21
  ],
14
22
  "scripts": {
15
23
  "clean": "rm -rf lib",
16
24
  "build:ts": "tsc",
17
25
  "build:copy-templates": "mkdir -p lib/templates && cp src/templates/*.md lib/templates/",
18
26
  "build": "npm run clean && npm run build:ts && npm run build:copy-templates",
27
+ "prepublishOnly": "npm run build && node scripts/check-release.mjs",
19
28
  "build:docker": "docker build -t formio/uag$(node -p \"require('./package.json').version.includes('-rc.') ? ':rc' : ''\") --platform=linux/amd64 .",
20
29
  "test": "tsc --noEmit -p tsconfig.test.json && mocha --no-node-snapshot",
21
30
  "test:e2e": "mocha --config .mocharc.e2e.json",
31
+ "test:getting-started": "mocha --no-config --node-option no-node-snapshot --require dotenv/config --timeout 1800000 test/e2e/layer3.getting-started.e2e.mjs",
32
+ "release": "node scripts/release.mjs",
33
+ "release:version": "node scripts/set-version.mjs",
22
34
  "dev": "tsx --no-node-snapshot index.js",
23
- "start": "node --no-node-snapshot index.js",
24
- "claude": "node --no-node-snapshot ./integrations/claude/index.js"
35
+ "demo:build": "node demo/scripts/build.mjs",
36
+ "demo:dev": "node --no-node-snapshot demo/scripts/dev.mjs",
37
+ "demo:local": "node --no-node-snapshot demo/scripts/local.mjs",
38
+ "demo:start": "node --no-node-snapshot demo/scripts/start.mjs",
39
+ "demo:record": "node --no-node-snapshot demo/recordings/record.mjs",
40
+ "start": "node --no-node-snapshot index.js"
25
41
  },
26
42
  "repository": {
27
43
  "type": "git",
@@ -30,18 +46,24 @@
30
46
  "author": "Form.io Developers",
31
47
  "license": "MIT",
32
48
  "dependencies": {
33
- "@formio/appserver": "^2.14.1",
34
- "@formio/core": "2.8.0",
35
- "@modelcontextprotocol/sdk": "1.30.0",
49
+ "@formio/appserver": "^2.26.4",
50
+ "@formio/core": "2.9.0",
51
+ "@formio/uag-sdk": "^2.0.0-rc.10",
52
+ "@modelcontextprotocol/sdk": "1.32.2",
36
53
  "cors": "^2.8.6",
37
54
  "debug": "^4.4.3",
38
- "dotenv": "^17.4.2",
39
- "express": "^5.2.1",
55
+ "dotenv": "^18.0.9",
56
+ "express": "^5.3.0",
40
57
  "jsonwebtoken": "^9.0.3",
41
58
  "lodash": "^4.18.1",
59
+ "markdown-it": "^15.0.3",
60
+ "mongodb": "^7.7.0",
42
61
  "node-cache": "^5.1.2",
43
62
  "node-html-markdown": "^2.0.0",
44
- "zod": "^4.4.3"
63
+ "node-html-parser": "^9.0.5",
64
+ "pdf-lib": "^1.17.1",
65
+ "unpdf": "^2.1.0",
66
+ "zod": "^4.6.7"
45
67
  },
46
68
  "resolutions": {
47
69
  "tar": ">=7.5.12",
@@ -52,22 +74,26 @@
52
74
  "dompurify": ">=3.3.3"
53
75
  },
54
76
  "devDependencies": {
55
- "@anthropic-ai/sdk": "^0.115.0",
77
+ "@anthropic-ai/sdk": "^1.3.0",
78
+ "@formio/js": "5.7.0",
56
79
  "@types/chai": "^5.2.3",
57
80
  "@types/cors": "^2.8.19",
58
81
  "@types/debug": "^4.1.13",
59
- "@types/dotenv": "^8.2.3",
60
82
  "@types/express": "^5.0.6",
83
+ "@types/jsdom": "^30.0.0",
61
84
  "@types/jsonwebtoken": "^9.0.10",
62
- "@types/lodash": "^4.17.25",
85
+ "@types/lodash": "^4.17.27",
63
86
  "@types/mocha": "^10.0.10",
64
- "@types/node": "^26.1.2",
87
+ "@types/node": "^26.6.6",
65
88
  "@types/supertest": "^7.2.1",
66
- "chai": "^6.2.2",
67
- "mocha": "^11.8.0",
68
- "supertest": "^7.2.2",
69
- "terser": "^5.49.1",
70
- "tsx": "^4.23.5",
89
+ "chai": "^6.3.0",
90
+ "jsdom": "30.1.3",
91
+ "mocha": "^12.0.3",
92
+ "mongodb-memory-server": "^11.3.0",
93
+ "playwright": "1.64.1",
94
+ "supertest": "^7.3.1",
95
+ "terser": "^5.51.2",
96
+ "tsx": "^4.23.15",
71
97
  "typescript": "^7.0.2"
72
98
  },
73
99
  "packageManager": "yarn@4.17.1",
@@ -78,5 +104,9 @@
78
104
  "isolated-vm": {
79
105
  "built": true
80
106
  }
107
+ },
108
+ "bin": {
109
+ "uag": "bin/uag.js",
110
+ "uag-eval": "bin/uag-eval.js"
81
111
  }
82
112
  }
package/testing.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './lib/testing';
package/testing.js ADDED
@@ -0,0 +1,2 @@
1
+ // @formio/uag/testing: test a module without a Form.io project or a running UAG.
2
+ module.exports = require('./lib/testing');
@@ -1,3 +0,0 @@
1
- import { UAGProjectInterface } from "../UAGProjectInterface";
2
- import { ToolInfo } from "./utils";
3
- export declare const agentProvideData: (project: UAGProjectInterface) => Promise<ToolInfo>;
@@ -1,62 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.agentProvideData = void 0;
4
- const template_1 = require("../template");
5
- const lodash_1 = require("lodash");
6
- const SchemaBuilder_1 = require("./SchemaBuilder");
7
- const annotations_1 = require("./annotations");
8
- const agentProvideData = async (project) => {
9
- return (0, lodash_1.defaultsDeep)(project.config?.toolOverrides?.agent_provide_data || {}, {
10
- name: 'agent_provide_data',
11
- title: 'Agent Provide Data',
12
- description: 'Provides a mechanism for agents to process existing submission data, understand it, and then to provide additional data values provided a criteria and form context.',
13
- annotations: (0, annotations_1.reads)('Process submission data'),
14
- inputSchema: (new SchemaBuilder_1.SchemaBuilder(project))
15
- .form_name()
16
- .submission_id()
17
- .persona().schema,
18
- execute: async ({ form_name, submission_id, persona }, extra) => {
19
- const form = await project.getForm(form_name);
20
- if (!form) {
21
- return project.mcpResponse(template_1.ResponseTemplate.formNotFound, { formName: form_name }, true);
22
- }
23
- const uag = persona ? form.uagFields[persona] : Object.values(form.uagFields)[0];
24
- if (!uag) {
25
- return project.mcpResponse(template_1.ResponseTemplate.uagComponentNotFound, {
26
- persona: persona || 'default',
27
- error: 'no_uag'
28
- }, true);
29
- }
30
- const submission = await form.loadSubmission(submission_id, extra.authInfo);
31
- if (!submission) {
32
- return project.mcpResponse(template_1.ResponseTemplate.submissionNotFound, {
33
- form: form.form,
34
- submissionId: submission_id
35
- }, true);
36
- }
37
- if (!uag.criteria) {
38
- return project.mcpResponse(template_1.ResponseTemplate.uagComponentNotFound, {
39
- persona: persona || 'default',
40
- error: 'no_criteria'
41
- }, true);
42
- }
43
- const agentFields = Object.values(uag.components);
44
- if (agentFields.length === 0) {
45
- return project.mcpResponse(template_1.ResponseTemplate.uagComponentNotFound, {
46
- persona: persona || 'default',
47
- error: 'no_fields'
48
- }, true);
49
- }
50
- // Collect more required fields.
51
- return project.mcpResponse(template_1.ResponseTemplate.agentProcessData, {
52
- persona: persona || 'default',
53
- criteria: uag.criteria,
54
- values: project.uagTemplate?.renderTemplate(template_1.ResponseTemplate.fieldValues, {
55
- data: form.formatData(submission.data)
56
- }),
57
- fields: project.uagTemplate?.renderTemplate(template_1.ResponseTemplate.fieldList, { fields: agentFields })
58
- });
59
- }
60
- });
61
- };
62
- exports.agentProvideData = agentProvideData;