struere 0.12.3 → 0.12.5

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.
@@ -181,6 +181,7 @@ async function syncViaHttp(apiKey, payload) {
181
181
  },
182
182
  body: JSON.stringify({
183
183
  agents: payload.agents,
184
+ tools: payload.tools,
184
185
  entityTypes: payload.entityTypes,
185
186
  roles: payload.roles,
186
187
  evalSuites: payload.evalSuites,
@@ -1682,8 +1683,13 @@ function defineTrigger(config) {
1682
1683
  if (!config.name) throw new Error('Trigger name is required')
1683
1684
  if (!config.slug) throw new Error('Trigger slug is required')
1684
1685
  if (!config.on) throw new Error('Trigger "on" configuration is required')
1685
- if (!config.on.entityType) throw new Error('Trigger entityType is required')
1686
- if (!config.on.action || !VALID_ACTIONS.includes(config.on.action)) throw new Error('Trigger action must be one of: ' + VALID_ACTIONS.join(', '))
1686
+ if (config.on.schedule) {
1687
+ const parts = config.on.schedule.trim().split(/s+/)
1688
+ if (parts.length !== 5) throw new Error('Invalid cron expression: expected 5 fields, got ' + parts.length)
1689
+ } else {
1690
+ if (!config.on.entityType) throw new Error('Trigger entityType is required')
1691
+ if (!config.on.action || !VALID_ACTIONS.includes(config.on.action)) throw new Error('Trigger action must be one of: ' + VALID_ACTIONS.join(', '))
1692
+ }
1687
1693
  if (!config.actions || config.actions.length === 0) throw new Error('Trigger must have at least one action')
1688
1694
  for (const action of config.actions) {
1689
1695
  if (!action.tool) throw new Error('Trigger action tool is required')
@@ -2195,7 +2201,8 @@ function buildProjectContext(orgName, resources) {
2195
2201
  lines.push("");
2196
2202
  lines.push(`### Triggers (${resources.triggers.length})`);
2197
2203
  for (const trigger of resources.triggers) {
2198
- lines.push(`- **${trigger.name}** (\`${trigger.slug}\`) \u2014 on \`${trigger.on.entityType}.${trigger.on.action}\``);
2204
+ const onDesc = "schedule" in trigger.on ? `cron \`${trigger.on.schedule}\`` : `\`${trigger.on.entityType}.${trigger.on.action}\``;
2205
+ lines.push(`- **${trigger.name}** (\`${trigger.slug}\`) \u2014 on ${onDesc}`);
2199
2206
  }
2200
2207
  }
2201
2208
  if (resources.customTools.length > 0) {
@@ -2722,6 +2729,13 @@ function extractSyncPayload(resources) {
2722
2729
  customToolsMap.set(tool.name, tool);
2723
2730
  }
2724
2731
  const agents = resources.agents.map((agent) => extractAgentPayload(agent, customToolsMap));
2732
+ const tools = resources.customTools.length > 0 ? resources.customTools.map((tool) => ({
2733
+ name: tool.name,
2734
+ description: tool.description,
2735
+ parameters: tool.parameters || { type: "object", properties: {} },
2736
+ handlerCode: extractHandlerCode(tool._originalHandler || tool.handler),
2737
+ ...tool.templateOnly && { templateOnly: true }
2738
+ })) : undefined;
2725
2739
  const entityTypes = resources.entityTypes.map((et) => ({
2726
2740
  name: et.name,
2727
2741
  slug: et.slug,
@@ -2775,21 +2789,26 @@ function extractSyncPayload(resources) {
2775
2789
  contextParams: c.contextParams
2776
2790
  }))
2777
2791
  })) : undefined;
2778
- const triggers = resources.triggers.length > 0 ? resources.triggers.map((t) => ({
2779
- name: t.name,
2780
- slug: t.slug,
2781
- description: t.description,
2782
- entityType: t.on.entityType,
2783
- action: t.on.action,
2784
- condition: t.on.condition,
2785
- actions: t.actions.map((a) => ({
2786
- tool: a.tool,
2787
- args: a.args,
2788
- as: a.as
2789
- })),
2790
- schedule: t.schedule,
2791
- retry: t.retry
2792
- })) : undefined;
2792
+ const triggers = resources.triggers.length > 0 ? resources.triggers.map((t) => {
2793
+ const isScheduled = "schedule" in t.on && typeof t.on.schedule === "string";
2794
+ return {
2795
+ name: t.name,
2796
+ slug: t.slug,
2797
+ description: t.description,
2798
+ entityType: isScheduled ? undefined : t.on.entityType,
2799
+ action: isScheduled ? undefined : t.on.action,
2800
+ condition: isScheduled ? undefined : t.on.condition,
2801
+ cronSchedule: isScheduled ? t.on.schedule : undefined,
2802
+ cronTimezone: isScheduled ? t.on.timezone : undefined,
2803
+ actions: t.actions.map((a) => ({
2804
+ tool: a.tool,
2805
+ args: a.args,
2806
+ as: a.as
2807
+ })),
2808
+ schedule: t.schedule,
2809
+ retry: t.retry
2810
+ };
2811
+ }) : undefined;
2793
2812
  const routers = resources.routers.length > 0 ? resources.routers.map((r) => ({
2794
2813
  name: r.name,
2795
2814
  slug: r.slug,
@@ -2829,7 +2848,7 @@ function extractSyncPayload(resources) {
2829
2848
  metadata: r.metadata
2830
2849
  }))
2831
2850
  })) : undefined;
2832
- return { agents, entityTypes, roles, evalSuites, triggers, routers, fixtures };
2851
+ return { agents, entityTypes, roles, tools, evalSuites, triggers, routers, fixtures };
2833
2852
  }
2834
2853
  function extractAgentPayload(agent, customToolsMap) {
2835
2854
  let systemPrompt;
@@ -2844,25 +2863,11 @@ function extractAgentPayload(agent, customToolsMap) {
2844
2863
  }
2845
2864
  const tools = (agent.tools || []).map((toolName) => {
2846
2865
  const isBuiltin = BUILTIN_TOOLS.includes(toolName);
2847
- if (isBuiltin) {
2848
- return {
2849
- name: toolName,
2850
- description: getBuiltinToolDescription(toolName),
2851
- parameters: getBuiltinToolParameters(toolName)
2852
- };
2853
- }
2854
- const customTool = customToolsMap.get(toolName);
2855
- if (!customTool) {
2866
+ if (!isBuiltin && !customToolsMap.has(toolName)) {
2856
2867
  const available = customToolsMap.size > 0 ? `Available custom tools: ${Array.from(customToolsMap.keys()).join(", ")}` : "No custom tools were loaded from tools/index.ts";
2857
2868
  throw new Error(`Agent "${agent.name}" references tool "${toolName}" but it was not found. ${available}`);
2858
2869
  }
2859
- return {
2860
- name: customTool.name,
2861
- description: customTool.description,
2862
- parameters: customTool.parameters || { type: "object", properties: {} },
2863
- handlerCode: extractHandlerCode(customTool._originalHandler || customTool.handler),
2864
- ...customTool.templateOnly && { templateOnly: true }
2865
- };
2870
+ return toolName;
2866
2871
  });
2867
2872
  return {
2868
2873
  name: agent.name,
@@ -2880,352 +2885,6 @@ function extractAgentPayload(agent, customToolsMap) {
2880
2885
  tools
2881
2886
  };
2882
2887
  }
2883
- function getBuiltinToolDescription(name) {
2884
- const descriptions = {
2885
- "entity.create": "Create a new entity of a specified type",
2886
- "entity.get": "Get an entity by its ID",
2887
- "entity.query": "Query entities by type with optional filters",
2888
- "entity.update": "Update an existing entity by ID",
2889
- "entity.delete": "Delete an entity by ID",
2890
- "calendar.list": "List Google Calendar events for a user within a time range",
2891
- "calendar.create": "Create a Google Calendar event on a user's calendar",
2892
- "calendar.update": "Update an existing Google Calendar event",
2893
- "calendar.delete": "Delete a Google Calendar event",
2894
- "calendar.freeBusy": "Check free/busy availability on a user's Google Calendar",
2895
- "whatsapp.send": "Send a text message via WhatsApp",
2896
- "whatsapp.sendTemplate": "Send a pre-approved template message via WhatsApp (works outside 24h window)",
2897
- "whatsapp.sendInteractive": "Send an interactive button message via WhatsApp (max 3 buttons)",
2898
- "whatsapp.sendMedia": "Send an image or audio message via WhatsApp",
2899
- "whatsapp.listTemplates": "List available WhatsApp message templates",
2900
- "whatsapp.getConversation": "Get WhatsApp conversation history with a phone number",
2901
- "whatsapp.getStatus": "Get WhatsApp connection status for this organization",
2902
- "agent.chat": "Send a message to another agent and get its response",
2903
- "airtable.listBases": "List all Airtable bases accessible with the configured token",
2904
- "airtable.listTables": "List all tables in an Airtable base",
2905
- "airtable.listRecords": "List records from an Airtable table with optional filtering and sorting",
2906
- "airtable.getRecord": "Get a single record from an Airtable table by ID",
2907
- "airtable.createRecords": "Create up to 10 records in an Airtable table",
2908
- "airtable.updateRecords": "Update up to 10 records in an Airtable table",
2909
- "airtable.deleteRecords": "Delete up to 10 records from an Airtable table",
2910
- "email.send": "Send an email via Resend",
2911
- "payment.create": "Create a payment link via Flow.cl and return the URL",
2912
- "payment.getStatus": "Check the current status of a payment",
2913
- "web.search": "Search the web and return relevant results with snippets",
2914
- "web.fetch": "Fetch a web page and return its content as clean markdown"
2915
- };
2916
- return descriptions[name] || name;
2917
- }
2918
- function getBuiltinToolParameters(name) {
2919
- const schemas = {
2920
- "entity.create": {
2921
- type: "object",
2922
- properties: {
2923
- type: { type: "string", description: 'The entity type slug (e.g., "customer", "order")' },
2924
- data: { type: "object", description: "The entity data matching the entity type schema" },
2925
- status: { type: "string", description: 'Optional status (defaults to "active")' }
2926
- },
2927
- required: ["type", "data"]
2928
- },
2929
- "entity.get": {
2930
- type: "object",
2931
- properties: {
2932
- id: { type: "string", description: "The entity ID to retrieve" }
2933
- },
2934
- required: ["id"]
2935
- },
2936
- "entity.query": {
2937
- type: "object",
2938
- properties: {
2939
- type: { type: "string", description: 'The entity type slug to query (e.g., "customer", "order")' },
2940
- filters: { type: "object", description: 'Optional filters to apply (e.g., {"data.status": "active"})' },
2941
- status: { type: "string", description: "Filter by entity status" },
2942
- limit: { type: "number", description: "Maximum number of results (default 100)" }
2943
- },
2944
- required: ["type"]
2945
- },
2946
- "entity.update": {
2947
- type: "object",
2948
- properties: {
2949
- id: { type: "string", description: "The entity ID to update" },
2950
- type: { type: "string", description: 'The entity type slug (e.g., "session", "teacher"). Must match the actual type of the entity being updated.' },
2951
- data: { type: "object", description: "The fields to update (merged with existing data)" },
2952
- status: { type: "string", description: "Optional new status" }
2953
- },
2954
- required: ["id", "type", "data"]
2955
- },
2956
- "entity.delete": {
2957
- type: "object",
2958
- properties: {
2959
- id: { type: "string", description: "The entity ID to delete" }
2960
- },
2961
- required: ["id"]
2962
- },
2963
- "calendar.list": {
2964
- type: "object",
2965
- properties: {
2966
- userId: { type: "string", description: "User ID (Convex or Clerk) whose calendar to query" },
2967
- timeMin: { type: "string", description: "Start of time range (ISO 8601 datetime)" },
2968
- timeMax: { type: "string", description: "End of time range (ISO 8601 datetime)" },
2969
- maxResults: { type: "number", description: "Maximum number of events to return" }
2970
- },
2971
- required: ["userId", "timeMin", "timeMax"]
2972
- },
2973
- "calendar.create": {
2974
- type: "object",
2975
- properties: {
2976
- userId: { type: "string", description: "User ID (Convex or Clerk) whose calendar to create the event on" },
2977
- summary: { type: "string", description: "Event title" },
2978
- startTime: { type: "string", description: "Event start time (ISO 8601 datetime)" },
2979
- endTime: { type: "string", description: "Event end time (ISO 8601 datetime). Provide either endTime or durationMinutes" },
2980
- durationMinutes: { type: "number", description: "Duration in minutes. Used to calculate endTime if endTime is not provided" },
2981
- description: { type: "string", description: "Event description" },
2982
- attendees: { type: "array", items: { type: "string" }, description: "List of attendee email addresses" },
2983
- timeZone: { type: "string", description: 'Time zone (e.g., "America/Santiago")' },
2984
- addGoogleMeet: { type: "boolean", description: "Set to true to automatically create a Google Meet video conference link for this event" }
2985
- },
2986
- required: ["userId", "summary", "startTime"]
2987
- },
2988
- "calendar.update": {
2989
- type: "object",
2990
- properties: {
2991
- userId: { type: "string", description: "User ID (Convex or Clerk) whose calendar contains the event" },
2992
- eventId: { type: "string", description: "Google Calendar event ID to update" },
2993
- summary: { type: "string", description: "New event title" },
2994
- startTime: { type: "string", description: "New start time (ISO 8601 datetime)" },
2995
- endTime: { type: "string", description: "New end time (ISO 8601 datetime)" },
2996
- description: { type: "string", description: "New event description" },
2997
- attendees: { type: "array", items: { type: "string" }, description: "Updated list of attendee emails" },
2998
- status: { type: "string", description: "Event status (confirmed, tentative, cancelled)" }
2999
- },
3000
- required: ["userId", "eventId"]
3001
- },
3002
- "calendar.delete": {
3003
- type: "object",
3004
- properties: {
3005
- userId: { type: "string", description: "User ID (Convex or Clerk) whose calendar contains the event" },
3006
- eventId: { type: "string", description: "Google Calendar event ID to delete" }
3007
- },
3008
- required: ["userId", "eventId"]
3009
- },
3010
- "calendar.freeBusy": {
3011
- type: "object",
3012
- properties: {
3013
- userId: { type: "string", description: "User ID (Convex or Clerk) whose availability to check" },
3014
- timeMin: { type: "string", description: "Start of time range (ISO 8601 datetime)" },
3015
- timeMax: { type: "string", description: "End of time range (ISO 8601 datetime)" }
3016
- },
3017
- required: ["userId", "timeMin", "timeMax"]
3018
- },
3019
- "whatsapp.send": {
3020
- type: "object",
3021
- properties: {
3022
- to: { type: "string", description: 'Recipient phone number in E.164 format (e.g., "+15551234567")' },
3023
- text: { type: "string", description: "The text message to send" }
3024
- },
3025
- required: ["to", "text"]
3026
- },
3027
- "whatsapp.sendTemplate": {
3028
- type: "object",
3029
- properties: {
3030
- to: { type: "string", description: 'Recipient phone number in E.164 format (e.g., "+15551234567")' },
3031
- templateName: { type: "string", description: "Name of the approved template to send" },
3032
- language: { type: "string", description: 'Template language code (e.g., "en_US")' },
3033
- components: {
3034
- type: "array",
3035
- description: "Optional template components with parameter values",
3036
- items: {
3037
- type: "object",
3038
- properties: {
3039
- type: { type: "string", description: 'Component type (e.g., "body", "header")' },
3040
- parameters: {
3041
- type: "array",
3042
- items: {
3043
- type: "object",
3044
- properties: {
3045
- type: { type: "string", description: 'Parameter type (e.g., "text")' },
3046
- text: { type: "string", description: "Parameter text value" },
3047
- parameterName: { type: "string", description: "Named parameter name (for NAMED format templates)" }
3048
- },
3049
- required: ["type"]
3050
- }
3051
- }
3052
- },
3053
- required: ["type", "parameters"]
3054
- }
3055
- }
3056
- },
3057
- required: ["to", "templateName", "language"]
3058
- },
3059
- "whatsapp.sendInteractive": {
3060
- type: "object",
3061
- properties: {
3062
- to: { type: "string", description: 'Recipient phone number in E.164 format (e.g., "+15551234567")' },
3063
- bodyText: { type: "string", description: "The message body text" },
3064
- buttons: {
3065
- type: "array",
3066
- description: "Action buttons (1-3 buttons, max 20 chars per title)",
3067
- items: {
3068
- type: "object",
3069
- properties: {
3070
- id: { type: "string", description: "Unique button identifier returned on click" },
3071
- title: { type: "string", description: "Button label shown to user (max 20 characters)" }
3072
- },
3073
- required: ["id", "title"]
3074
- }
3075
- },
3076
- footerText: { type: "string", description: "Optional footer text below the buttons" }
3077
- },
3078
- required: ["to", "bodyText", "buttons"]
3079
- },
3080
- "whatsapp.sendMedia": {
3081
- type: "object",
3082
- properties: {
3083
- to: { type: "string", description: 'Recipient phone number in E.164 format (e.g., "+15551234567")' },
3084
- mediaUrl: { type: "string", description: "Public URL of the media file to send" },
3085
- mediaType: { type: "string", enum: ["image", "audio"], description: "Type of media to send" },
3086
- caption: { type: "string", description: "Optional caption (only supported for images)" }
3087
- },
3088
- required: ["to", "mediaUrl", "mediaType"]
3089
- },
3090
- "whatsapp.listTemplates": {
3091
- type: "object",
3092
- properties: {}
3093
- },
3094
- "whatsapp.getConversation": {
3095
- type: "object",
3096
- properties: {
3097
- phoneNumber: { type: "string", description: "Phone number to get conversation history for" },
3098
- limit: { type: "number", description: "Maximum number of messages to return" }
3099
- },
3100
- required: ["phoneNumber"]
3101
- },
3102
- "whatsapp.getStatus": {
3103
- type: "object",
3104
- properties: {}
3105
- },
3106
- "agent.chat": {
3107
- type: "object",
3108
- properties: {
3109
- agent: { type: "string", description: "Target agent slug to communicate with" },
3110
- message: { type: "string", description: "The message to send to the agent" },
3111
- context: { type: "object", description: "Optional context data to pass to the target agent" }
3112
- },
3113
- required: ["agent", "message"]
3114
- },
3115
- "airtable.listBases": {
3116
- type: "object",
3117
- properties: {}
3118
- },
3119
- "airtable.listTables": {
3120
- type: "object",
3121
- properties: {
3122
- baseId: { type: "string", description: 'Airtable base ID (e.g., "appXXXXXXXXXXXXXX")' }
3123
- },
3124
- required: ["baseId"]
3125
- },
3126
- "airtable.listRecords": {
3127
- type: "object",
3128
- properties: {
3129
- baseId: { type: "string", description: "Airtable base ID" },
3130
- tableIdOrName: { type: "string", description: "Table ID or name" },
3131
- pageSize: { type: "number", description: "Number of records per page (max 100)" },
3132
- offset: { type: "string", description: "Pagination offset from a previous response" },
3133
- filterByFormula: { type: "string", description: `Airtable formula to filter records (e.g., "{Status} = 'Active'")` },
3134
- sort: { type: "array", items: { type: "object", properties: { field: { type: "string" }, direction: { type: "string", enum: ["asc", "desc"] } }, required: ["field"] }, description: "Sort configuration" },
3135
- fields: { type: "array", items: { type: "string" }, description: "Only return specific field names" },
3136
- view: { type: "string", description: "Name or ID of an Airtable view to use" }
3137
- },
3138
- required: ["baseId", "tableIdOrName"]
3139
- },
3140
- "airtable.getRecord": {
3141
- type: "object",
3142
- properties: {
3143
- baseId: { type: "string", description: "Airtable base ID" },
3144
- tableIdOrName: { type: "string", description: "Table ID or name" },
3145
- recordId: { type: "string", description: 'Record ID (e.g., "recXXXXXXXXXXXXXX")' }
3146
- },
3147
- required: ["baseId", "tableIdOrName", "recordId"]
3148
- },
3149
- "airtable.createRecords": {
3150
- type: "object",
3151
- properties: {
3152
- baseId: { type: "string", description: "Airtable base ID" },
3153
- tableIdOrName: { type: "string", description: "Table ID or name" },
3154
- records: { type: "array", items: { type: "object", properties: { fields: { type: "object", description: "Field values for the record" } }, required: ["fields"] }, description: "Array of records to create (max 10)" }
3155
- },
3156
- required: ["baseId", "tableIdOrName", "records"]
3157
- },
3158
- "airtable.updateRecords": {
3159
- type: "object",
3160
- properties: {
3161
- baseId: { type: "string", description: "Airtable base ID" },
3162
- tableIdOrName: { type: "string", description: "Table ID or name" },
3163
- records: { type: "array", items: { type: "object", properties: { id: { type: "string", description: "Record ID to update" }, fields: { type: "object", description: "Field values to update" } }, required: ["id", "fields"] }, description: "Array of records to update (max 10)" }
3164
- },
3165
- required: ["baseId", "tableIdOrName", "records"]
3166
- },
3167
- "airtable.deleteRecords": {
3168
- type: "object",
3169
- properties: {
3170
- baseId: { type: "string", description: "Airtable base ID" },
3171
- tableIdOrName: { type: "string", description: "Table ID or name" },
3172
- recordIds: { type: "array", items: { type: "string" }, description: "Array of record IDs to delete (max 10)" }
3173
- },
3174
- required: ["baseId", "tableIdOrName", "recordIds"]
3175
- },
3176
- "email.send": {
3177
- type: "object",
3178
- properties: {
3179
- to: { type: "string", description: "Recipient email address" },
3180
- subject: { type: "string", description: "Email subject line" },
3181
- html: { type: "string", description: "HTML body content" },
3182
- text: { type: "string", description: "Plain text body content" },
3183
- replyTo: { type: "string", description: "Reply-to email address" }
3184
- },
3185
- required: ["to", "subject"]
3186
- },
3187
- "payment.create": {
3188
- type: "object",
3189
- properties: {
3190
- amount: { type: "number", description: "Payment amount in the smallest currency unit" },
3191
- description: { type: "string", description: "Description of the payment" },
3192
- currency: { type: "string", description: "Currency code (defaults to CLP)" },
3193
- customerEmail: { type: "string", description: "Customer email address" },
3194
- entityId: { type: "string", description: "Optional entity ID to link the payment to" }
3195
- },
3196
- required: ["amount", "description"]
3197
- },
3198
- "payment.getStatus": {
3199
- type: "object",
3200
- properties: {
3201
- entityId: { type: "string", description: "Payment entity ID to check status for" }
3202
- },
3203
- required: ["entityId"]
3204
- },
3205
- "web.search": {
3206
- type: "object",
3207
- properties: {
3208
- query: { type: "string", description: "Search query" },
3209
- maxResults: { type: "number", description: "Maximum number of results (default: 5, max: 20)" },
3210
- site: { type: "array", items: { type: "string" }, description: "Only include results from these domains" },
3211
- gl: { type: "string", description: 'Country code for localized results (e.g., "US", "GB")' },
3212
- hl: { type: "string", description: 'Language code (ISO 639-1, e.g., "en", "es")' }
3213
- },
3214
- required: ["query"]
3215
- },
3216
- "web.fetch": {
3217
- type: "object",
3218
- properties: {
3219
- url: { type: "string", description: "URL of the page to fetch" },
3220
- targetSelector: { type: "string", description: "CSS selector to extract specific content from the page" },
3221
- removeSelector: { type: "string", description: "CSS selector to remove elements from the page before extraction" },
3222
- tokenBudget: { type: "number", description: "Maximum number of tokens in the response content" }
3223
- },
3224
- required: ["url"]
3225
- }
3226
- };
3227
- return schemas[name] || { type: "object", properties: {} };
3228
- }
3229
2888
  function extractHandlerCode(handler) {
3230
2889
  const code = handler.toString();
3231
2890
  const arrowBlockMatch = code.match(/(?:async\s*)?\([^)]*\)\s*=>\s*\{([\s\S]*)\}\s*$/);
@@ -3255,6 +2914,7 @@ ${resources.errors.join(`
3255
2914
  const payload = extractSyncPayload(resources);
3256
2915
  const devResult = await syncOrganization({
3257
2916
  agents: payload.agents,
2917
+ tools: payload.tools,
3258
2918
  entityTypes: payload.entityTypes,
3259
2919
  roles: payload.roles,
3260
2920
  triggers: payload.triggers,
@@ -3269,6 +2929,7 @@ ${resources.errors.join(`
3269
2929
  if (hasEvalContent) {
3270
2930
  const evalResult = await syncOrganization({
3271
2931
  agents: payload.agents,
2932
+ tools: payload.tools,
3272
2933
  entityTypes: payload.entityTypes,
3273
2934
  roles: payload.roles,
3274
2935
  evalSuites: payload.evalSuites,
@@ -3961,6 +3622,7 @@ var deployCommand = new Command7("deploy").description("Deploy all resources to
3961
3622
  try {
3962
3623
  const syncResult = await syncOrganization({
3963
3624
  agents: payload.agents,
3625
+ tools: payload.tools,
3964
3626
  entityTypes: payload.entityTypes,
3965
3627
  roles: payload.roles,
3966
3628
  triggers: payload.triggers,
@@ -4445,6 +4107,8 @@ var statusCommand = new Command11("status").description("Compare local vs remote
4445
4107
  const devEntityTypeSlugs = new Set(devState.entityTypes.map((et) => et.slug));
4446
4108
  const localRoleNames = new Set(localResources.roles.map((r) => r.name));
4447
4109
  const devRoleNames = new Set(devState.roles.map((r) => r.name));
4110
+ const localToolNames = new Set(localResources.customTools.map((t) => t.name));
4111
+ const devToolNames = new Set((devState.tools || []).map((t) => t.name));
4448
4112
  const localRouterSlugs = new Set(localResources.routers.map((r) => r.slug));
4449
4113
  const devRouterSlugs = new Set((devState.routers || []).map((r) => r.slug));
4450
4114
  if (opts.json) {
@@ -4551,6 +4215,26 @@ var statusCommand = new Command11("status").description("Compare local vs remote
4551
4215
  }
4552
4216
  }
4553
4217
  console.log();
4218
+ console.log(chalk12.bold("Custom Tools"));
4219
+ console.log(chalk12.gray("\u2500".repeat(60)));
4220
+ if (localResources.customTools.length === 0 && (devState.tools || []).length === 0) {
4221
+ console.log(chalk12.gray(" No custom tools"));
4222
+ } else {
4223
+ for (const tool of localResources.customTools) {
4224
+ const remote = (devState.tools || []).find((t) => t.name === tool.name);
4225
+ if (remote) {
4226
+ console.log(` ${chalk12.green("\u25CF")} ${chalk12.cyan(tool.name)}`);
4227
+ } else {
4228
+ console.log(` ${chalk12.blue("+")} ${chalk12.cyan(tool.name)} - ${chalk12.blue("new")}`);
4229
+ }
4230
+ }
4231
+ for (const remote of devState.tools || []) {
4232
+ if (!localToolNames.has(remote.name)) {
4233
+ console.log(` ${chalk12.red("-")} ${remote.name} - ${chalk12.red("will be deleted")}`);
4234
+ }
4235
+ }
4236
+ }
4237
+ console.log();
4554
4238
  console.log(chalk12.gray("Legend:"));
4555
4239
  console.log(chalk12.gray(" "), chalk12.green("\u25CF"), "Synced", chalk12.yellow("\u25CB"), "Not in production", chalk12.blue("+"), "New", chalk12.red("-"), "Will be deleted");
4556
4240
  console.log();
@@ -4569,16 +4253,6 @@ import { join as join9 } from "path";
4569
4253
  init_convex();
4570
4254
 
4571
4255
  // src/cli/utils/generator.ts
4572
- var BUILTIN_TOOLS2 = [
4573
- "entity.create",
4574
- "entity.get",
4575
- "entity.query",
4576
- "entity.update",
4577
- "entity.delete",
4578
- "agent.chat",
4579
- "web.search",
4580
- "web.fetch"
4581
- ];
4582
4256
  function escapeTemplateLiteral(str) {
4583
4257
  return str.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
4584
4258
  }
@@ -4614,8 +4288,7 @@ ${" ".repeat(indent)}}`;
4614
4288
  return JSON.stringify(value);
4615
4289
  }
4616
4290
  function generateAgentFile(agent) {
4617
- const toolNames = agent.tools.map((t) => t.name);
4618
- const toolsArray = toolNames.map((n) => ` "${n}"`).join(`,
4291
+ const toolsArray = agent.tools.map((n) => ` "${n}"`).join(`,
4619
4292
  `);
4620
4293
  const modelParts = [
4621
4294
  ` model: "${agent.model.model}"`
@@ -4776,12 +4449,18 @@ ${toolEntries.join(`,
4776
4449
  `;
4777
4450
  }
4778
4451
  function generateTriggerFile(trigger) {
4779
- const onParts = [
4780
- ` entityType: "${trigger.entityType}"`,
4781
- ` action: "${trigger.action}"`
4782
- ];
4783
- if (trigger.condition && Object.keys(trigger.condition).length > 0) {
4784
- onParts.push(` condition: ${stringifyValue(trigger.condition, 4)}`);
4452
+ const onParts = [];
4453
+ if (trigger.cronSchedule) {
4454
+ onParts.push(` schedule: "${trigger.cronSchedule}"`);
4455
+ if (trigger.cronTimezone) {
4456
+ onParts.push(` timezone: "${trigger.cronTimezone}"`);
4457
+ }
4458
+ } else {
4459
+ onParts.push(` entityType: "${trigger.entityType}"`);
4460
+ onParts.push(` action: "${trigger.action}"`);
4461
+ if (trigger.condition && Object.keys(trigger.condition).length > 0) {
4462
+ onParts.push(` condition: ${stringifyValue(trigger.condition, 4)}`);
4463
+ }
4785
4464
  }
4786
4465
  const actionLines = trigger.actions.map((a) => {
4787
4466
  const aParts = [
@@ -4884,22 +4563,6 @@ function generateIndexFile(type, slugs) {
4884
4563
  `) + `
4885
4564
  `;
4886
4565
  }
4887
- function collectCustomTools(agents) {
4888
- const seen = new Map;
4889
- for (const agent of agents) {
4890
- for (const tool of agent.tools) {
4891
- if (!BUILTIN_TOOLS2.includes(tool.name) && !seen.has(tool.name)) {
4892
- seen.set(tool.name, {
4893
- name: tool.name,
4894
- description: tool.description,
4895
- parameters: tool.parameters,
4896
- handlerCode: tool.handlerCode
4897
- });
4898
- }
4899
- }
4900
- }
4901
- return Array.from(seen.values());
4902
- }
4903
4566
 
4904
4567
  // src/cli/commands/pull.ts
4905
4568
  var pullCommand = new Command12("pull").description("Pull remote resources to local files").option("--force", "Overwrite existing local files").option("--env <environment>", "Environment to pull from", "development").option("--dry-run", "Show what would be written without writing").option("--json", "Output raw JSON").action(async (options) => {
@@ -5040,7 +4703,7 @@ var pullCommand = new Command12("pull").description("Pull remote resources to lo
5040
4703
  const content = generateRouterFile(router);
5041
4704
  writeOrSkip(`routers/${router.slug}.ts`, content);
5042
4705
  }
5043
- const customTools = collectCustomTools(state.agents);
4706
+ const customTools = state.tools || [];
5044
4707
  if (customTools.length > 0) {
5045
4708
  const content = generateToolsFile(customTools);
5046
4709
  writeOrSkip("tools/index.ts", content);
@@ -7735,8 +7398,7 @@ triggersCommand.command("list", { isDefault: true }).description("List all trigg
7735
7398
  renderTable([
7736
7399
  { key: "name", label: "Name", width: 20 },
7737
7400
  { key: "slug", label: "Slug", width: 18 },
7738
- { key: "entityType", label: "Entity", width: 14 },
7739
- { key: "action", label: "Action", width: 10 },
7401
+ { key: "type", label: "Type", width: 18 },
7740
7402
  { key: "enabled", label: "Enabled", width: 8 },
7741
7403
  { key: "lastRun", label: "Last Run", width: 12 },
7742
7404
  { key: "lastError", label: "Last Error", width: 25 },
@@ -7744,8 +7406,7 @@ triggersCommand.command("list", { isDefault: true }).description("List all trigg
7744
7406
  ], filtered.map((t) => ({
7745
7407
  name: t.name ?? "",
7746
7408
  slug: t.slug ?? "",
7747
- entityType: t.entityType ?? "",
7748
- action: t.action ?? "",
7409
+ type: t.cronSchedule ? `cron ${t.cronSchedule}` : `${t.entityType ?? ""}.${t.action ?? ""}`,
7749
7410
  enabled: t.enabled ? chalk20.green("\u2713") : chalk20.red("\u2717"),
7750
7411
  lastRun: statuses[t.slug] ? statusColor4(statuses[t.slug].status) : chalk20.gray("-"),
7751
7412
  lastError: statuses[t.slug]?.status === "failed" ? chalk20.red((statuses[t.slug].error ?? "").slice(0, 23)) : "",
@@ -7785,8 +7446,15 @@ triggersCommand.command("get <slug>").description("View trigger details").option
7785
7446
  console.log(` ${chalk20.gray("Name:")} ${chalk20.cyan(trigger.name)}`);
7786
7447
  console.log(` ${chalk20.gray("Slug:")} ${chalk20.cyan(trigger.slug)}`);
7787
7448
  console.log(` ${chalk20.gray("Description:")} ${chalk20.cyan(trigger.description || "-")}`);
7788
- console.log(` ${chalk20.gray("Entity Type:")} ${chalk20.cyan(trigger.entityType)}`);
7789
- console.log(` ${chalk20.gray("Action:")} ${chalk20.cyan(trigger.action)}`);
7449
+ if (trigger.cronSchedule) {
7450
+ console.log(` ${chalk20.gray("Schedule:")} ${chalk20.cyan(trigger.cronSchedule)}`);
7451
+ if (trigger.cronTimezone) {
7452
+ console.log(` ${chalk20.gray("Timezone:")} ${chalk20.cyan(trigger.cronTimezone)}`);
7453
+ }
7454
+ } else {
7455
+ console.log(` ${chalk20.gray("Entity Type:")} ${chalk20.cyan(trigger.entityType)}`);
7456
+ console.log(` ${chalk20.gray("Action:")} ${chalk20.cyan(trigger.action)}`);
7457
+ }
7790
7458
  console.log(` ${chalk20.gray("Enabled:")} ${trigger.enabled ? chalk20.green("\u2713") : chalk20.red("\u2717")}`);
7791
7459
  console.log(` ${chalk20.gray("Created:")} ${chalk20.cyan(relativeTime2(trigger.createdAt ?? Date.now()))}`);
7792
7460
  console.log(` ${chalk20.gray("Updated:")} ${chalk20.cyan(relativeTime2(trigger.updatedAt ?? Date.now()))}`);
@@ -9041,7 +8709,7 @@ whatsappCommand.command("set-agent <connection> <agent-slug>").description("Assi
9041
8709
  // package.json
9042
8710
  var package_default = {
9043
8711
  name: "struere",
9044
- version: "0.12.3",
8712
+ version: "0.12.5",
9045
8713
  description: "Build, test, and deploy AI agents",
9046
8714
  keywords: [
9047
8715
  "ai",
@@ -1 +1 @@
1
- {"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/deploy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAcnC,eAAO,MAAM,aAAa,SAqUtB,CAAA"}
1
+ {"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/deploy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAcnC,eAAO,MAAM,aAAa,SAsUtB,CAAA"}