@mindstudio-ai/agent 0.1.106 → 0.1.107

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -2972,9 +2972,12 @@ interface SchemaValidationError {
2972
2972
  * - `{ method }`: SDK method name with default input overrides.
2973
2973
  * - `{ appMethod }`: one of your own app's methods, called as the invoking
2974
2974
  * user with their roles.
2975
+ * - `{ name, execute }`: an inline function tool, executed right here in your
2976
+ * process. No platform dispatch, no billing — just your code.
2975
2977
  *
2976
2978
  * Defaults win over whatever the model passes for the same field, including
2977
2979
  * nested fields — the model decides what to do, you pin how it's done.
2980
+ * Function tools take no defaults: close over what you need instead.
2978
2981
  */
2979
2982
  type TaskToolConfig = string | {
2980
2983
  method: string;
@@ -2989,6 +2992,22 @@ type TaskToolConfig = string | {
2989
2992
  */
2990
2993
  description?: string;
2991
2994
  defaults?: Record<string, unknown>;
2995
+ } | {
2996
+ /** Tool name the model sees. Letters, digits, `_`, `-`; max 64 chars. */
2997
+ name: string;
2998
+ /** What the tool does and when to reach for it — all the model gets. */
2999
+ description: string;
3000
+ /** JSON Schema for the input (tool-definition dialect). Omitted means
3001
+ * the tool takes no arguments. */
3002
+ inputSchema?: JsonObjectSchema;
3003
+ /**
3004
+ * Runs in this process when the model calls the tool. A thrown error is
3005
+ * fed back to the model as `{ error }` tool output to work around — it
3006
+ * never fails the task. Note: calling `runTask` from inside a function
3007
+ * tool is not depth-capped (the server cap rides the method's token,
3008
+ * which a function tool reuses) — recursion is yours to bound.
3009
+ */
3010
+ execute: (input: Record<string, unknown>) => Promise<unknown> | unknown;
2992
3011
  };
2993
3012
  /** Options shared by both output modes of {@link MindStudioAgent.runTask}. */
2994
3013
  interface RunTaskOptionsBase {
@@ -2996,7 +3015,8 @@ interface RunTaskOptionsBase {
2996
3015
  prompt: string;
2997
3016
  /** Structured input for this task instance. Passed as the user message. */
2998
3017
  input: Record<string, unknown>;
2999
- /** SDK actions and/or app methods to make available as tools. */
3018
+ /** SDK actions, app methods, and/or inline function tools to make
3019
+ * available to the model. */
3000
3020
  tools: TaskToolConfig[];
3001
3021
  /**
3002
3022
  * Model ID for the task agent. Must support tool use. Tuned model lines
@@ -3214,8 +3234,12 @@ declare class MindStudioAgent$1 {
3214
3234
  * to produce structured output. The model receives the prompt and tools,
3215
3235
  * calls actions as needed, and returns structured JSON.
3216
3236
  *
3217
- * Tools can be SDK actions, your own app's methods, or both. App methods run
3218
- * as the user who invoked the method that started the task, with their roles.
3237
+ * Tools can be SDK actions, your own app's methods, and/or inline functions
3238
+ * defined right in your code, in any combination. App methods run as the
3239
+ * user who invoked the method that started the task, with their roles.
3240
+ * Function tools run in this process — a thrown error is fed back to the
3241
+ * model as tool output, and there are no `defaults` (close over what you
3242
+ * need instead).
3219
3243
  *
3220
3244
  * Prefer `outputSchema` (plain JSON Schema, tool-definition dialect):
3221
3245
  * output is validated every turn with automatic repair, `result.output` is
@@ -3231,6 +3255,12 @@ declare class MindStudioAgent$1 {
3231
3255
  * 'searchGoogle',
3232
3256
  * 'fetchUrl',
3233
3257
  * { appMethod: 'saveRestaurant', description: 'Persist the researched restaurant.' },
3258
+ * {
3259
+ * name: 'checkExisting',
3260
+ * description: 'Look up whether we already track this restaurant.',
3261
+ * inputSchema: { type: 'object', properties: { name: { type: 'string' } }, required: ['name'] },
3262
+ * execute: async (input) => Restaurants.findByName(String(input.name)),
3263
+ * },
3234
3264
  * ],
3235
3265
  * outputSchema: {
3236
3266
  * type: 'object',
@@ -10073,6 +10103,7 @@ interface StepMethods {
10073
10103
  * - The special connectionId "trigger_email" uses the email address that triggered the workflow.
10074
10104
  * - Sends default to category "marketing": every message carries a one-click unsubscribe header pointing at the app's own domain, and recipients who previously unsubscribed from this app are skipped and returned in "suppressed". Because the unsubscribe link is per-recipient, a marketing send delivers one separate message per recipient and cc/bcc are folded into that list.
10075
10105
  * - Set category to "transactional" for receipts, alerts, password resets and anything a recipient must not be able to opt out of. Transactional sends carry no unsubscribe header, ignore the unsubscribe list, and keep cc/bcc on a single message — use it whenever cc/bcc semantics matter.
10106
+ * - The subject is stored verbatim in the app's delivery log, which every workspace member can read for 90 days. Never put a secret in the subject — no one-time codes, no magic-link tokens, no password-reset tokens. Put those in the body, which is never stored.
10076
10107
  *
10077
10108
  * @example
10078
10109
  * ```typescript
package/dist/index.js CHANGED
@@ -3797,7 +3797,8 @@ var stepMetadata = {
3797
3797
  - connectionId can be a comma-separated list to send to multiple recipients.
3798
3798
  - The special connectionId "trigger_email" uses the email address that triggered the workflow.
3799
3799
  - Sends default to category "marketing": every message carries a one-click unsubscribe header pointing at the app's own domain, and recipients who previously unsubscribed from this app are skipped and returned in "suppressed". Because the unsubscribe link is per-recipient, a marketing send delivers one separate message per recipient and cc/bcc are folded into that list.
3800
- - Set category to "transactional" for receipts, alerts, password resets and anything a recipient must not be able to opt out of. Transactional sends carry no unsubscribe header, ignore the unsubscribe list, and keep cc/bcc on a single message \u2014 use it whenever cc/bcc semantics matter.`,
3800
+ - Set category to "transactional" for receipts, alerts, password resets and anything a recipient must not be able to opt out of. Transactional sends carry no unsubscribe header, ignore the unsubscribe list, and keep cc/bcc on a single message \u2014 use it whenever cc/bcc semantics matter.
3801
+ - The subject is stored verbatim in the app's delivery log, which every workspace member can read for 90 days. Never put a secret in the subject \u2014 no one-time codes, no magic-link tokens, no password-reset tokens. Put those in the body, which is never stored.`,
3801
3802
  inputSchema: { "type": "object", "properties": { "subject": { "type": "string", "description": "Email subject line" }, "body": { "type": "string", "description": "Email body content (plain text, markdown, HTML, or a CDN URL to an HTML file)" }, "to": { "anyOf": [{ "type": "string" }, { "type": "array", "items": { "type": "string" } }] }, "cc": { "anyOf": [{ "type": "string" }, { "type": "array", "items": { "type": "string" } }] }, "bcc": { "anyOf": [{ "type": "string" }, { "type": "array", "items": { "type": "string" } }] }, "connectionId": { "type": "string", "description": "OAuth connection ID(s) for the recipient(s), comma-separated for multiple" }, "generateHtml": { "type": "boolean", "description": "When true, auto-convert the body text into a styled HTML email using AI" }, "generateHtmlInstructions": { "type": "string", "description": "Natural language instructions for the HTML generation style" }, "generateHtmlModelOverride": { "type": "object", "properties": { "model": { "type": "string", "description": 'Model identifier (e.g. "gpt-4", "claude-3-opus")' }, "temperature": { "type": "number", "description": "Sampling temperature for the model (0-2)" }, "maxResponseTokens": { "type": "number", "description": "Maximum number of tokens in the model's response" }, "ignorePreamble": { "type": "boolean", "description": "Whether to skip the system preamble/instructions" }, "userMessagePreprocessor": { "type": "object", "properties": { "dataSource": { "type": "string", "description": "Data source identifier for the preprocessor" }, "messageTemplate": { "type": "string", "description": "Template string applied to user messages before sending to the model" }, "maxResults": { "type": "number", "description": "Maximum number of results to include from the data source" }, "enabled": { "type": "boolean", "description": "Whether the preprocessor is active" }, "shouldInherit": { "type": "boolean", "description": "Whether child steps should inherit this preprocessor configuration" } }, "description": "Preprocessor applied to user messages before sending to the model" }, "preamble": { "type": "string", "description": "System preamble/instructions for the model" }, "multiModelEnabled": { "type": "boolean", "description": "Whether multi-model candidate generation is enabled" }, "editResponseEnabled": { "type": "boolean", "description": "Whether the user can edit the model's response" }, "config": { "type": "object", "properties": {}, "required": [], "description": "Additional model-specific configuration" } }, "required": ["model", "temperature", "maxResponseTokens"], "description": "Model settings override for HTML generation" }, "attachments": { "type": "array", "items": { "anyOf": [{ "type": "string" }, { "type": "object", "properties": { "url": { "type": "string" }, "filename": { "type": "string" }, "contentType": { "type": "string" } }, "required": ["url"] }] }, "description": "Files to attach: each entry is a URL string, or an object `{ url, filename?, contentType? }` to control the displayed filename and MIME type." }, "from": { "type": "string", "description": "Custom sender handle \u2014 only for apps with a custom domain or subdomain. Bare handle (`support`), full `support@your-domain.com`, or `Name <support@your-domain.com>`. Must resolve to a domain the app owns." }, "bodyType": { "enum": ["auto", "html", "markdown", "text"], "type": "string", "description": "How to interpret `body`: `auto` (default \u2014 detect HTML, else render markdown), `html` (send as-is), `markdown` (render to HTML), or `text` (send as plain text only, no HTML part). Every send includes a text/plain alternative." }, "text": { "type": "string", "description": "Explicit text/plain alternative body. Auto-derived from `body` if omitted." }, "replyTo": { "type": "string", "description": "Reply-To address for the email." }, "inReplyTo": { "type": "string", "description": "Message-ID this email replies to, for inbox threading (In-Reply-To header)." }, "references": { "type": "array", "items": { "type": "string" }, "description": "Prior Message-IDs in the thread, for inbox threading (References header)." }, "category": { "enum": ["transactional", "marketing"], "type": "string", "description": "`marketing` (default) adds one-click unsubscribe headers and skips recipients who unsubscribed from this app. Use `transactional` for receipts, alerts and password resets: no unsubscribe header, never suppressed, and cc/bcc preserved on a single message." } }, "required": ["subject", "body"] },
3802
3803
  outputSchema: { "type": "object", "properties": { "recipients": { "type": "array", "items": { "type": "string" }, "description": "To addresses the message was sent to." }, "cc": { "type": "array", "items": { "type": "string" }, "description": "Cc addresses on the message (empty if none)." }, "bcc": { "type": "array", "items": { "type": "string" }, "description": "Bcc addresses on the message (empty if none)." }, "from": { "type": "string", "description": "The resolved sender address the message went out as (auto-selected when no `from` is given)." }, "suppressed": { "type": "array", "items": { "type": "string" }, "description": "Recipients skipped because they unsubscribed from this app. Always empty for a `transactional` send." } }, "required": ["recipients", "cc", "bcc", "from", "suppressed"] }
3803
3804
  },
@@ -4245,6 +4246,13 @@ function resolveStepType(name) {
4245
4246
  }
4246
4247
  function mapTools(tools) {
4247
4248
  return tools.map((t) => {
4249
+ if (typeof t === "object" && "execute" in t) {
4250
+ return {
4251
+ name: t.name,
4252
+ description: t.description,
4253
+ inputSchema: t.inputSchema ?? { type: "object", properties: {} }
4254
+ };
4255
+ }
4248
4256
  if (typeof t === "object" && "appMethod" in t) {
4249
4257
  return {
4250
4258
  appMethod: t.appMethod,
@@ -4690,10 +4698,18 @@ ${structuredOutputExample}
4690
4698
  const wireTools = mapTools(options.tools);
4691
4699
  const toolKinds = /* @__PURE__ */ new Map();
4692
4700
  const toolDefaults = /* @__PURE__ */ new Map();
4701
+ const functionTools = /* @__PURE__ */ new Map();
4702
+ for (const t of options.tools) {
4703
+ if (typeof t === "object" && "execute" in t) {
4704
+ functionTools.set(t.name, t.execute);
4705
+ }
4706
+ }
4693
4707
  for (const t of wireTools) {
4694
4708
  if ("appMethod" in t) {
4695
4709
  toolKinds.set(t.appMethod, "method");
4696
4710
  if (t.defaults) toolDefaults.set(t.appMethod, t.defaults);
4711
+ } else if ("name" in t) {
4712
+ toolKinds.set(t.name, "function");
4697
4713
  } else {
4698
4714
  toolKinds.set(t.stepType, "step");
4699
4715
  if (t.defaults) toolDefaults.set(t.stepType, t.defaults);
@@ -4822,8 +4838,27 @@ ${structuredOutputExample}
4822
4838
  const startTime = Date.now();
4823
4839
  const defaults = toolDefaults.get(toolCall.name) || {};
4824
4840
  const mergedInput = mergeToolInput(toolCall.input, defaults);
4825
- const execute = toolKinds.get(toolCall.name) === "method" ? deps.executeMethodTool : deps.executeStepTool;
4826
- const result = await execute(toolCall.name, mergedInput);
4841
+ const kind = toolKinds.get(toolCall.name);
4842
+ let result;
4843
+ if (kind === "function") {
4844
+ try {
4845
+ const output3 = await functionTools.get(toolCall.name)(
4846
+ mergedInput
4847
+ );
4848
+ result = { output: output3 ?? null, billingCost: 0, isError: false };
4849
+ } catch (err) {
4850
+ result = {
4851
+ output: {
4852
+ error: err instanceof Error ? err.message : "Function tool execution failed"
4853
+ },
4854
+ billingCost: 0,
4855
+ isError: true
4856
+ };
4857
+ }
4858
+ } else {
4859
+ const execute = kind === "method" ? deps.executeMethodTool : deps.executeStepTool;
4860
+ result = await execute(toolCall.name, mergedInput);
4861
+ }
4827
4862
  toolCallLog.push({
4828
4863
  name: toolCall.name,
4829
4864
  success: !result.isError,
@@ -6074,6 +6109,13 @@ var MindStudioAgent = class {
6074
6109
  );
6075
6110
  } catch (err) {
6076
6111
  if (err instanceof MindStudioError && err.code === TURN_UNAVAILABLE_CODE) {
6112
+ if (options.tools.some((t) => typeof t === "object" && "execute" in t)) {
6113
+ throw new MindStudioError(
6114
+ "[task] This server does not support the per-turn task endpoint required for function tools.",
6115
+ "task_function_tools_unsupported",
6116
+ 404
6117
+ );
6118
+ }
6077
6119
  const body = buildTaskRequestBody(options);
6078
6120
  const result = options.onEvent ? await runTaskStream(httpConfig, body, options.onEvent) : await runTaskPoll(httpConfig, body);
6079
6121
  if (options.outputSchema) {