@base44-preview/sdk 0.8.32-pr.203.8313142 → 0.8.32-pr.205.168e407

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/client.js CHANGED
@@ -7,6 +7,7 @@ import { createConnectorsModule, createUserConnectorsModule, } from "./modules/c
7
7
  import { getAccessToken } from "./utils/auth-utils.js";
8
8
  import { createFunctionsModule } from "./modules/functions.js";
9
9
  import { createAgentsModule } from "./modules/agents.js";
10
+ import { createDynamicAgentsModule } from "./modules/dynamic-agents.js";
10
11
  import { createAppLogsModule } from "./modules/app-logs.js";
11
12
  import { createUsersModule } from "./modules/users.js";
12
13
  import { RoomsSocket } from "./utils/socket-utils.js";
@@ -150,6 +151,10 @@ export function createClient(config) {
150
151
  serverUrl,
151
152
  token,
152
153
  }),
154
+ dynamicAgents: createDynamicAgentsModule({
155
+ serverUrl,
156
+ getToken: () => token || getAccessToken() || undefined,
157
+ }),
153
158
  appLogs: createAppLogsModule(axiosClient, appId),
154
159
  users: createUsersModule(axiosClient, appId),
155
160
  analytics: createAnalyticsModule({
@@ -192,6 +197,10 @@ export function createClient(config) {
192
197
  serverUrl,
193
198
  token,
194
199
  }),
200
+ dynamicAgents: createDynamicAgentsModule({
201
+ serverUrl,
202
+ getToken: () => serviceToken,
203
+ }),
195
204
  appLogs: createAppLogsModule(serviceRoleAxiosClient, appId),
196
205
  cleanup: () => {
197
206
  if (socket) {
@@ -7,6 +7,7 @@ import type { FunctionsModule } from "./modules/functions.types.js";
7
7
  import type { AgentsModule } from "./modules/agents.types.js";
8
8
  import type { AppLogsModule } from "./modules/app-logs.types.js";
9
9
  import type { AnalyticsModule } from "./modules/analytics.types.js";
10
+ import type { DynamicAgentsModule } from "./modules/dynamic-agents.types.js";
10
11
  /**
11
12
  * Options for creating a Base44 client.
12
13
  */
@@ -85,6 +86,8 @@ export interface Base44Client {
85
86
  analytics: AnalyticsModule;
86
87
  /** {@link AppLogsModule | App logs module} for tracking app usage. */
87
88
  appLogs: AppLogsModule;
89
+ /** {@link DynamicAgentsModule | Dynamic agents module} for code-defined AI agents and tool loops. */
90
+ dynamicAgents: DynamicAgentsModule;
88
91
  /** {@link AuthModule | Auth module} for user authentication and management. */
89
92
  auth: AuthModule;
90
93
  /** {@link UserConnectorsModule | Connectors module} for app-user OAuth flows. */
@@ -126,6 +129,8 @@ export interface Base44Client {
126
129
  agents: AgentsModule;
127
130
  /** {@link AppLogsModule | App logs module} with elevated permissions. */
128
131
  appLogs: AppLogsModule;
132
+ /** {@link DynamicAgentsModule | Dynamic agents module} with elevated permissions. */
133
+ dynamicAgents: DynamicAgentsModule;
129
134
  /** {@link ConnectorsModule | Connectors module} for OAuth token retrieval. */
130
135
  connectors: ConnectorsModule;
131
136
  /** {@link EntitiesModule | Entities module} with elevated permissions. */
package/dist/index.d.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  import { createClient, createClientFromRequest, type Base44Client, type CreateClientConfig, type CreateClientOptions } from "./client.js";
2
2
  import { Base44Error, type Base44ErrorJSON } from "./utils/axios-client.js";
3
3
  import { getAccessToken, saveAccessToken, removeAccessToken, getLoginUrl } from "./utils/auth-utils.js";
4
- export { createClient, createClientFromRequest, Base44Error, getAccessToken, saveAccessToken, removeAccessToken, getLoginUrl, };
4
+ import { tool } from "./modules/dynamic-agents.js";
5
+ export { createClient, createClientFromRequest, Base44Error, getAccessToken, saveAccessToken, removeAccessToken, getLoginUrl, tool, };
5
6
  export type { Base44Client, CreateClientConfig, CreateClientOptions, Base44ErrorJSON, };
6
7
  export * from "./types.js";
7
8
  export type { DeleteManyResult, DeleteResult, EntitiesModule, EntityFilterOperators, EntityFilterQuery, EntityFilterValue, EntityHandler, EntityRecord, EntityTypeRegistry, ImportResult, RealtimeEventType, RealtimeEvent, RealtimeCallback, SortField, UpdateManyResult, } from "./modules/entities.types.js";
@@ -13,4 +14,5 @@ export type { AppLogsModule } from "./modules/app-logs.types.js";
13
14
  export type { SsoModule, SsoAccessTokenResponse } from "./modules/sso.types.js";
14
15
  export type { ConnectorsModule, UserConnectorsModule, } from "./modules/connectors.types.js";
15
16
  export type { CustomIntegrationsModule, CustomIntegrationCallParams, CustomIntegrationCallResponse, } from "./modules/custom-integrations.types.js";
17
+ export type { Tool, JSONSchema, ChatMessage, Step, RunUsage, RunResult, RunInput, RunOptions, ToolChoice, AgentConfig, Agent, DynamicAgentsModule, } from "./modules/dynamic-agents.types.js";
16
18
  export type { GetAccessTokenOptions, SaveAccessTokenOptions, RemoveAccessTokenOptions, GetLoginUrlOptions, } from "./utils/auth-utils.types.js";
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { createClient, createClientFromRequest, } from "./client.js";
2
2
  import { Base44Error } from "./utils/axios-client.js";
3
3
  import { getAccessToken, saveAccessToken, removeAccessToken, getLoginUrl, } from "./utils/auth-utils.js";
4
- export { createClient, createClientFromRequest, Base44Error, getAccessToken, saveAccessToken, removeAccessToken, getLoginUrl, };
4
+ import { tool } from "./modules/dynamic-agents.js";
5
+ export { createClient, createClientFromRequest, Base44Error, getAccessToken, saveAccessToken, removeAccessToken, getLoginUrl, tool, };
5
6
  export * from "./types.js";
@@ -0,0 +1,20 @@
1
+ import type { DynamicAgentsModuleConfig } from "./dynamic-agents.types.js";
2
+ /**
3
+ * Resolves the AI Gateway connection from a client config.
4
+ * @internal
5
+ */
6
+ export declare function resolveConnection(config: DynamicAgentsModuleConfig): {
7
+ baseURL: string;
8
+ apiKey: string;
9
+ };
10
+ /**
11
+ * Creates the gateway transport. Owns the single HTTP call to the OpenAI-compatible
12
+ * `/chat/completions` endpoint. Shaped so a streaming `.stream()` method can be added
13
+ * later without changing callers of `.complete()`.
14
+ * @internal
15
+ */
16
+ export declare function createGatewayTransport(config: DynamicAgentsModuleConfig): {
17
+ complete(body: Record<string, unknown>, opts?: {
18
+ signal?: AbortSignal;
19
+ }): Promise<any>;
20
+ };
@@ -0,0 +1,42 @@
1
+ import { Base44Error } from "../utils/axios-client.js";
2
+ /**
3
+ * Resolves the AI Gateway connection from a client config.
4
+ * @internal
5
+ */
6
+ export function resolveConnection(config) {
7
+ var _a;
8
+ const { serverUrl, getToken } = config;
9
+ // No appId in the path: the gateway resolves the app by request Host.
10
+ return {
11
+ baseURL: `${serverUrl}/api/ai/unified/v1`,
12
+ apiKey: (_a = getToken()) !== null && _a !== void 0 ? _a : "",
13
+ };
14
+ }
15
+ /**
16
+ * Creates the gateway transport. Owns the single HTTP call to the OpenAI-compatible
17
+ * `/chat/completions` endpoint. Shaped so a streaming `.stream()` method can be added
18
+ * later without changing callers of `.complete()`.
19
+ * @internal
20
+ */
21
+ export function createGatewayTransport(config) {
22
+ return {
23
+ async complete(body, opts = {}) {
24
+ const { baseURL, apiKey } = resolveConnection(config);
25
+ const res = await fetch(`${baseURL}/chat/completions`, {
26
+ method: "POST",
27
+ headers: {
28
+ "Content-Type": "application/json",
29
+ Authorization: `Bearer ${apiKey}`,
30
+ },
31
+ body: JSON.stringify(body),
32
+ signal: opts.signal,
33
+ });
34
+ const json = await res.json().catch(() => null);
35
+ if (!res.ok) {
36
+ const err = (json && json.error) || {};
37
+ throw new Base44Error(err.message || `AI Gateway request failed with status ${res.status}`, res.status, err.code || err.type || "ai_gateway_error", json, null);
38
+ }
39
+ return json;
40
+ },
41
+ };
42
+ }
@@ -0,0 +1,32 @@
1
+ import type { AgentConfig, ChatMessage, DynamicAgentsModule, DynamicAgentsModuleConfig, Tool } from "./dynamic-agents.types.js";
2
+ /**
3
+ * Defines a tool an agent can call.
4
+ *
5
+ * @example
6
+ * ```typescript
7
+ * const getWeather = tool({
8
+ * description: "Get the current weather for a city.",
9
+ * parameters: { type: "object", properties: { city: { type: "string" } }, required: ["city"] },
10
+ * execute: async ({ city }) => ({ city, tempC: 28 }),
11
+ * });
12
+ * ```
13
+ */
14
+ export declare function tool(t: Tool): Tool;
15
+ /**
16
+ * Maps a `{ name: Tool }` map to the OpenAI `tools[]` array. Returns `undefined`
17
+ * when empty so the param is omitted from the request body.
18
+ * @internal
19
+ */
20
+ export declare function serializeTools(tools?: Record<string, Tool>): any[] | undefined;
21
+ /**
22
+ * Builds the gateway request body from config + messages using an explicit whitelist.
23
+ * Rejected params (max_tokens, stop, top_p, penalties, logit_bias, seed, n) can never
24
+ * appear because only the supported keys are ever written.
25
+ * @internal
26
+ */
27
+ export declare function buildRequestBody(config: AgentConfig, messages: ChatMessage[]): Record<string, unknown>;
28
+ /**
29
+ * Creates the `base44.dynamicAgents` module.
30
+ * @internal
31
+ */
32
+ export declare function createDynamicAgentsModule(config: DynamicAgentsModuleConfig): DynamicAgentsModule;
@@ -0,0 +1,176 @@
1
+ import { createGatewayTransport } from "./ai-gateway.js";
2
+ /**
3
+ * Defines a tool an agent can call.
4
+ *
5
+ * @example
6
+ * ```typescript
7
+ * const getWeather = tool({
8
+ * description: "Get the current weather for a city.",
9
+ * parameters: { type: "object", properties: { city: { type: "string" } }, required: ["city"] },
10
+ * execute: async ({ city }) => ({ city, tempC: 28 }),
11
+ * });
12
+ * ```
13
+ */
14
+ export function tool(t) {
15
+ return t;
16
+ }
17
+ /**
18
+ * Maps a `{ name: Tool }` map to the OpenAI `tools[]` array. Returns `undefined`
19
+ * when empty so the param is omitted from the request body.
20
+ * @internal
21
+ */
22
+ export function serializeTools(tools) {
23
+ if (!tools)
24
+ return undefined;
25
+ const entries = Object.entries(tools);
26
+ if (entries.length === 0)
27
+ return undefined;
28
+ return entries.map(([name, t]) => ({
29
+ type: "function",
30
+ function: { name, description: t.description, parameters: t.parameters },
31
+ }));
32
+ }
33
+ /**
34
+ * Builds the gateway request body from config + messages using an explicit whitelist.
35
+ * Rejected params (max_tokens, stop, top_p, penalties, logit_bias, seed, n) can never
36
+ * appear because only the supported keys are ever written.
37
+ * @internal
38
+ */
39
+ export function buildRequestBody(config, messages) {
40
+ const body = {
41
+ model: config.model,
42
+ messages,
43
+ };
44
+ if (config.temperature !== undefined)
45
+ body.temperature = config.temperature;
46
+ if (config.toolChoice !== undefined)
47
+ body.tool_choice = config.toolChoice;
48
+ if (config.responseFormat !== undefined) {
49
+ body.response_format = {
50
+ type: "json_schema",
51
+ json_schema: { name: "response", schema: config.responseFormat, strict: true },
52
+ };
53
+ }
54
+ const tools = serializeTools(config.tools);
55
+ if (tools)
56
+ body.tools = tools;
57
+ return body;
58
+ }
59
+ const DEFAULT_MAX_STEPS = 8;
60
+ function inputToMessages(input) {
61
+ if ("messages" in input)
62
+ return input.messages;
63
+ return [{ role: "user", content: input.prompt }];
64
+ }
65
+ function stringifyResult(out) {
66
+ return typeof out === "string" ? out : JSON.stringify(out);
67
+ }
68
+ function mapUsage(raw) {
69
+ const u = (raw && raw.usage) || {};
70
+ return {
71
+ promptTokens: u.prompt_tokens,
72
+ completionTokens: u.completion_tokens,
73
+ totalTokens: u.total_tokens,
74
+ credits: u.base44_credits,
75
+ };
76
+ }
77
+ /**
78
+ * Creates the `base44.dynamicAgents` module.
79
+ * @internal
80
+ */
81
+ export function createDynamicAgentsModule(config) {
82
+ const transport = createGatewayTransport(config);
83
+ function create(agentConfig) {
84
+ var _a;
85
+ const maxSteps = (_a = agentConfig.maxSteps) !== null && _a !== void 0 ? _a : DEFAULT_MAX_STEPS;
86
+ const tools = agentConfig.tools;
87
+ const agent = {
88
+ async run(input, options = {}) {
89
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
90
+ const messages = [];
91
+ if (agentConfig.system)
92
+ messages.push({ role: "system", content: agentConfig.system });
93
+ messages.push(...inputToMessages(input));
94
+ const steps = [];
95
+ let raw = null;
96
+ for (let i = 0; i < maxSteps; i++) {
97
+ const body = buildRequestBody(agentConfig, messages);
98
+ raw = await transport.complete(body, { signal: options.abortSignal });
99
+ const choice = (_a = raw === null || raw === void 0 ? void 0 : raw.choices) === null || _a === void 0 ? void 0 : _a[0];
100
+ const message = (_b = choice === null || choice === void 0 ? void 0 : choice.message) !== null && _b !== void 0 ? _b : { role: "assistant", content: "" };
101
+ messages.push(message);
102
+ const toolCalls = message.tool_calls;
103
+ if (!toolCalls || toolCalls.length === 0) {
104
+ return {
105
+ text: (_c = message.content) !== null && _c !== void 0 ? _c : "",
106
+ steps,
107
+ finishReason: (_d = choice === null || choice === void 0 ? void 0 : choice.finish_reason) !== null && _d !== void 0 ? _d : "stop",
108
+ usage: mapUsage(raw),
109
+ raw,
110
+ };
111
+ }
112
+ const toolResults = [];
113
+ for (const call of toolCalls) {
114
+ const name = (_e = call.function) === null || _e === void 0 ? void 0 : _e.name;
115
+ const t = tools === null || tools === void 0 ? void 0 : tools[name];
116
+ let args = {};
117
+ try {
118
+ args = JSON.parse(((_f = call.function) === null || _f === void 0 ? void 0 : _f.arguments) || "{}");
119
+ }
120
+ catch (_l) {
121
+ args = {};
122
+ }
123
+ let resultContent;
124
+ if (!t) {
125
+ resultContent = `Error: tool "${name}" is not available.`;
126
+ }
127
+ else {
128
+ try {
129
+ resultContent = stringifyResult(await t.execute(args));
130
+ }
131
+ catch (e) {
132
+ resultContent = `Error: ${(_g = e === null || e === void 0 ? void 0 : e.message) !== null && _g !== void 0 ? _g : String(e)}`;
133
+ }
134
+ }
135
+ messages.push({ role: "tool", tool_call_id: call.id, content: resultContent });
136
+ toolResults.push({ toolCallId: call.id, toolName: name, args, result: resultContent });
137
+ }
138
+ steps.push({ toolResults });
139
+ }
140
+ // maxSteps exhausted
141
+ const lastMessage = (_j = (_h = raw === null || raw === void 0 ? void 0 : raw.choices) === null || _h === void 0 ? void 0 : _h[0]) === null || _j === void 0 ? void 0 : _j.message;
142
+ return {
143
+ text: (_k = lastMessage === null || lastMessage === void 0 ? void 0 : lastMessage.content) !== null && _k !== void 0 ? _k : "",
144
+ steps,
145
+ finishReason: "max_steps",
146
+ usage: mapUsage(raw),
147
+ raw,
148
+ };
149
+ },
150
+ asTool(toolOpts) {
151
+ return {
152
+ description: toolOpts.description,
153
+ parameters: {
154
+ type: "object",
155
+ properties: { prompt: { type: "string", description: "What to ask the sub-agent." } },
156
+ required: ["prompt"],
157
+ },
158
+ execute: async (args) => {
159
+ const result = await agent.run({ prompt: args.prompt });
160
+ return result.text;
161
+ },
162
+ };
163
+ },
164
+ };
165
+ return agent;
166
+ }
167
+ function run(runConfig, options) {
168
+ if ("messages" in runConfig) {
169
+ const { messages, ...agentConfig } = runConfig;
170
+ return create(agentConfig).run({ messages }, options);
171
+ }
172
+ const { prompt, ...agentConfig } = runConfig;
173
+ return create(agentConfig).run({ prompt }, options);
174
+ }
175
+ return { create, run };
176
+ }
@@ -0,0 +1,127 @@
1
+ /**
2
+ * A JSON Schema object describing a tool's input parameters.
3
+ * Use the standard JSON Schema `object` shape: `{ type: "object", properties: {...}, required: [...] }`.
4
+ */
5
+ export type JSONSchema = Record<string, unknown>;
6
+ /**
7
+ * A tool an agent can call. Create one with {@linkcode tool | tool()}, or derive it from a
8
+ * resource with `.asTool()`.
9
+ */
10
+ export interface Tool {
11
+ /** Natural-language description the model uses to decide when to call the tool. */
12
+ description: string;
13
+ /** JSON Schema for the tool's arguments. */
14
+ parameters: JSONSchema;
15
+ /** Runs the tool. Receives parsed arguments; returns any JSON-serializable value (or a string). */
16
+ execute: (args: any) => Promise<unknown> | unknown;
17
+ }
18
+ /** An OpenAI-shaped chat message used internally and accepted by {@linkcode Agent.run}. */
19
+ export interface ChatMessage {
20
+ role: "system" | "user" | "assistant" | "tool";
21
+ content?: string | null;
22
+ tool_calls?: Array<{
23
+ id: string;
24
+ type: "function";
25
+ function: {
26
+ name: string;
27
+ arguments: string;
28
+ };
29
+ }>;
30
+ tool_call_id?: string;
31
+ }
32
+ /** One iteration of the agent loop: the tool calls the model made and their results. */
33
+ export interface Step {
34
+ toolResults: Array<{
35
+ toolCallId: string;
36
+ toolName: string;
37
+ args: unknown;
38
+ result: string;
39
+ }>;
40
+ }
41
+ /** Token/credit usage for a run. `credits` is the Base44 gateway's `base44_credits`. */
42
+ export interface RunUsage {
43
+ promptTokens?: number;
44
+ completionTokens?: number;
45
+ totalTokens?: number;
46
+ credits?: number;
47
+ }
48
+ /** Result of {@linkcode Agent.run} / {@linkcode DynamicAgentsModule.run}. */
49
+ export interface RunResult {
50
+ /** The model's final text output. */
51
+ text: string;
52
+ /** The loop history (one entry per step that made tool calls). */
53
+ steps: Step[];
54
+ /** Why the run ended: `"stop"` (model finished), `"tool_calls"`, or `"max_steps"`. */
55
+ finishReason: string;
56
+ /** Token and credit usage from the final completion. */
57
+ usage: RunUsage;
58
+ /** The raw final completion body, for advanced use. */
59
+ raw: unknown;
60
+ }
61
+ /** Input to {@linkcode Agent.run}: either a single prompt or a full message list. */
62
+ export type RunInput = {
63
+ prompt: string;
64
+ } | {
65
+ messages: ChatMessage[];
66
+ };
67
+ /** Per-run options. */
68
+ export interface RunOptions {
69
+ /** Abort the run (and the in-flight gateway request). */
70
+ abortSignal?: AbortSignal;
71
+ }
72
+ /** OpenAI-compatible tool choice. */
73
+ export type ToolChoice = "auto" | "none" | "required" | {
74
+ type: "function";
75
+ function: {
76
+ name: string;
77
+ };
78
+ };
79
+ /** Configuration for a dynamic agent. */
80
+ export interface AgentConfig {
81
+ /** Model alias (e.g. `"claude_sonnet_4_6"`, `"gpt_5_mini"`) or vendor id. */
82
+ model: string;
83
+ /** System prompt. */
84
+ system?: string;
85
+ /** Tools the agent may call, keyed by name. */
86
+ tools?: Record<string, Tool>;
87
+ /** Max loop iterations before stopping. Default `8`. */
88
+ maxSteps?: number;
89
+ /** Sampling temperature. Omitted unless set. Note: GPT-5 models only accept `1`. */
90
+ temperature?: number;
91
+ /** A JSON Schema to constrain output to structured JSON (`response_format: json_schema`). */
92
+ responseFormat?: JSONSchema;
93
+ /** Controls whether/which tool the model must call. */
94
+ toolChoice?: ToolChoice;
95
+ }
96
+ /** A reusable dynamic agent. */
97
+ export interface Agent {
98
+ /** Run the agent's tool-calling loop to completion. */
99
+ run(input: RunInput, options?: RunOptions): Promise<RunResult>;
100
+ /**
101
+ * Turn this agent into a {@linkcode Tool} so another agent can call it as a sub-agent.
102
+ * (Implemented in Effort 2.)
103
+ */
104
+ asTool(opts: {
105
+ name?: string;
106
+ description: string;
107
+ }): Tool;
108
+ }
109
+ /** The `base44.dynamicAgents` module. */
110
+ export interface DynamicAgentsModule {
111
+ /** Define a reusable agent. */
112
+ create(config: AgentConfig): Agent;
113
+ /** One-shot: `create(config).run({ prompt })`. */
114
+ run(config: AgentConfig & RunInput, options?: RunOptions): Promise<RunResult>;
115
+ }
116
+ /**
117
+ * Configuration for the dynamic-agents module.
118
+ *
119
+ * Note: the gateway resolves the app by request Host, so no `appId` is needed here —
120
+ * `serverUrl` must be an app-resolving domain.
121
+ * @internal
122
+ */
123
+ export interface DynamicAgentsModuleConfig {
124
+ serverUrl: string;
125
+ /** Returns the current bearer token at call time (thunk — never a captured string). */
126
+ getToken: () => string | undefined;
127
+ }
@@ -0,0 +1,2 @@
1
+ // src/modules/dynamic-agents.types.ts
2
+ export {};
@@ -31,38 +31,40 @@ export function createFunctionsModule(axios, appId, config) {
31
31
  }
32
32
  return headers;
33
33
  };
34
+ // Hoisted so both the returned `invoke` property and `asTool.execute` can reference it
35
+ // without relying on `this` (which breaks when the object is spread into the client).
36
+ const invoke = async (functionName, data) => {
37
+ // Validate input
38
+ if (typeof data === "string") {
39
+ throw new Error(`Function ${functionName} must receive an object with named parameters, received: ${data}`);
40
+ }
41
+ let formData;
42
+ let contentType;
43
+ // Handle file uploads with FormData
44
+ if (data instanceof FormData ||
45
+ (data && Object.values(data).some((value) => value instanceof File))) {
46
+ formData = new FormData();
47
+ Object.keys(data).forEach((key) => {
48
+ if (data[key] instanceof File) {
49
+ formData.append(key, data[key], data[key].name);
50
+ }
51
+ else if (typeof data[key] === "object" && data[key] !== null) {
52
+ formData.append(key, JSON.stringify(data[key]));
53
+ }
54
+ else {
55
+ formData.append(key, data[key]);
56
+ }
57
+ });
58
+ contentType = "multipart/form-data";
59
+ }
60
+ else {
61
+ formData = data;
62
+ contentType = "application/json";
63
+ }
64
+ return axios.post(`/apps/${appId}/functions/${functionName}`, formData || data, { headers: { "Content-Type": contentType } });
65
+ };
34
66
  return {
35
- // Invoke a custom backend function by name
36
- async invoke(functionName, data) {
37
- // Validate input
38
- if (typeof data === "string") {
39
- throw new Error(`Function ${functionName} must receive an object with named parameters, received: ${data}`);
40
- }
41
- let formData;
42
- let contentType;
43
- // Handle file uploads with FormData
44
- if (data instanceof FormData ||
45
- (data && Object.values(data).some((value) => value instanceof File))) {
46
- formData = new FormData();
47
- Object.keys(data).forEach((key) => {
48
- if (data[key] instanceof File) {
49
- formData.append(key, data[key], data[key].name);
50
- }
51
- else if (typeof data[key] === "object" && data[key] !== null) {
52
- formData.append(key, JSON.stringify(data[key]));
53
- }
54
- else {
55
- formData.append(key, data[key]);
56
- }
57
- });
58
- contentType = "multipart/form-data";
59
- }
60
- else {
61
- formData = data;
62
- contentType = "application/json";
63
- }
64
- return axios.post(`/apps/${appId}/functions/${functionName}`, formData || data, { headers: { "Content-Type": contentType } });
65
- },
67
+ invoke,
66
68
  // Fetch a backend function endpoint directly.
67
69
  async fetch(path, init = {}) {
68
70
  const normalizedPath = path.startsWith("/") ? path : `/${path}`;
@@ -75,5 +77,17 @@ export function createFunctionsModule(axios, appId, config) {
75
77
  const response = await fetch(joinBaseUrl(config === null || config === void 0 ? void 0 : config.baseURL, primaryPath), requestInit);
76
78
  return response;
77
79
  },
80
+ // Turn a backend function into an agent tool.
81
+ asTool(name, opts) {
82
+ var _a;
83
+ return {
84
+ description: opts.description,
85
+ parameters: (_a = opts.parameters) !== null && _a !== void 0 ? _a : { type: "object", properties: {}, additionalProperties: true },
86
+ execute: async (args) => {
87
+ const res = await invoke(name, args !== null && args !== void 0 ? args : {});
88
+ return res === null || res === void 0 ? void 0 : res.data;
89
+ },
90
+ };
91
+ },
78
92
  };
79
93
  }
@@ -86,6 +86,19 @@ export interface FunctionsModule {
86
86
  * ```
87
87
  */
88
88
  invoke(functionName: FunctionName, data?: Record<string, any>): Promise<any>;
89
+ /**
90
+ * Turns a backend function into a {@linkcode Tool} an agent can call.
91
+ *
92
+ * Functions are invoked by name with no server-known input schema, so you supply a
93
+ * `description` and (optionally) JSON Schema `parameters` for the model.
94
+ *
95
+ * @param name - The backend function name.
96
+ * @param opts - `description` (required) and optional JSON Schema `parameters`.
97
+ */
98
+ asTool(name: FunctionName, opts: {
99
+ description: string;
100
+ parameters?: Record<string, unknown>;
101
+ }): import("./dynamic-agents.types.js").Tool;
89
102
  /**
90
103
  * Performs a direct HTTP request to a backend function path and returns the native `Response`.
91
104
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@base44-preview/sdk",
3
- "version": "0.8.32-pr.203.8313142",
3
+ "version": "0.8.32-pr.205.168e407",
4
4
  "description": "JavaScript SDK for Base44 API",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",