Compare commits

..

14 Commits

Author SHA1 Message Date
aadamgough
92a08b0a33 removed success from microsoft tools 2025-12-15 19:37:45 -08:00
aadamgough
5d3b216922 fixed jira output 2025-12-14 21:06:28 -08:00
Waleed
431f206930 fix(tools): add validation for ids in tool routes (#2371) 2025-12-13 19:40:33 -08:00
Waleed
7443e28054 feat(i18n): update translations (#2370)
Co-authored-by: waleedlatif1 <waleedlatif1@users.noreply.github.com>
2025-12-13 19:26:31 -08:00
Waleed
c962e3b398 feat(webflow): added collection, item, & site selectors for webflow (#2368)
* feat(webflow): added collection, item, & site selectors for webflow

* ack PR comments

* ack PR comments
2025-12-13 19:14:33 -08:00
Waleed
d5b95cbd33 fix(organizations): move organization better-auth client to conditionally be included based on FF (#2367)
* fix(organizations): move organization better-auth client to conditionally be included based on FF

* ack PR comment
2025-12-13 19:06:12 -08:00
Waleed
0fb084b9e4 fix(subflows): prevent cross-boundary connections on autoconnect drop between subflow blocks and regular blocks (#2366) 2025-12-13 17:38:59 -08:00
Waleed
95b9ca4670 feat(registration): allow self-hosted users to disable registration altogether (#2365)
* feat(registration): allow self-hosted users to disable registration altogether

* updated tests

* fix build
2025-12-13 17:34:53 -08:00
Vikhyath Mondreti
746ff68a2e fix(sub-deletion): subscription deletion handling for pro vs team/enterprise (#2364)
* fix(subscription): deletion should sync pro limits back to free

* consolidate duplicate code
2025-12-13 16:10:47 -08:00
Waleed
75a5b43252 feat(og): update og image (#2362) 2025-12-13 15:52:13 -08:00
Vikhyath Mondreti
8d0e50fd0d improvement(admin-routes): cleanup code that could accidentally desync stripe and DB (#2363)
* remove non-functional admin route

* stripe updates cleanup
2025-12-13 15:11:14 -08:00
Waleed
f7d1b06d75 feat(docs): add opengraph to docs for dynamic link preview (#2360) 2025-12-13 13:51:43 -08:00
Vikhyath Mondreti
73940ab390 fix(deployed-chat): voice mode (#2358)
* fix(deployed-chat): voice mode

* remove redundant check

* consolidate query

* invalidate session on password change + race condition fix
2025-12-13 12:31:03 -08:00
Vikhyath Mondreti
f111dac020 improvement(autolayout): reduce horizontal spacing (#2357) 2025-12-13 11:06:42 -08:00
224 changed files with 1744 additions and 7174 deletions

View File

@@ -243,6 +243,9 @@ export async function generateMetadata(props: {
const baseUrl = 'https://docs.sim.ai'
const fullUrl = `${baseUrl}${page.url}`
const description = page.data.description || ''
const ogImageUrl = `${baseUrl}/api/og?title=${encodeURIComponent(page.data.title)}&category=DOCUMENTATION${description ? `&description=${encodeURIComponent(description)}` : ''}`
return {
title: page.data.title,
description:
@@ -272,12 +275,23 @@ export async function generateMetadata(props: {
alternateLocale: ['en', 'es', 'fr', 'de', 'ja', 'zh']
.filter((lang) => lang !== params.lang)
.map((lang) => (lang === 'en' ? 'en_US' : `${lang}_${lang.toUpperCase()}`)),
images: [
{
url: ogImageUrl,
width: 1200,
height: 630,
alt: page.data.title,
},
],
},
twitter: {
card: 'summary',
card: 'summary_large_image',
title: page.data.title,
description:
page.data.description || 'Sim visual workflow builder for AI applications documentation',
images: [ogImageUrl],
creator: '@simdotai',
site: '@simdotai',
},
robots: {
index: true,

View File

@@ -0,0 +1,173 @@
import { ImageResponse } from 'next/og'
import type { NextRequest } from 'next/server'
export const runtime = 'edge'
const TITLE_FONT_SIZE = {
large: 64,
medium: 56,
small: 48,
} as const
function getTitleFontSize(title: string): number {
if (title.length > 45) return TITLE_FONT_SIZE.small
if (title.length > 30) return TITLE_FONT_SIZE.medium
return TITLE_FONT_SIZE.large
}
/**
* Loads a Google Font dynamically by fetching the CSS and extracting the font URL.
*/
async function loadGoogleFont(font: string, weights: string, text: string): Promise<ArrayBuffer> {
const url = `https://fonts.googleapis.com/css2?family=${font}:wght@${weights}&text=${encodeURIComponent(text)}`
const css = await (await fetch(url)).text()
const resource = css.match(/src: url\((.+)\) format\('(opentype|truetype)'\)/)
if (resource) {
const response = await fetch(resource[1])
if (response.status === 200) {
return await response.arrayBuffer()
}
}
throw new Error('Failed to load font data')
}
/**
* Generates dynamic Open Graph images for documentation pages.
*/
export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url)
const title = searchParams.get('title') || 'Documentation'
const category = searchParams.get('category') || 'DOCUMENTATION'
const description = searchParams.get('description') || ''
const baseUrl = new URL(request.url).origin
const backgroundImageUrl = `${baseUrl}/static/og-background.png`
const allText = `${title}${category}${description}docs.sim.ai`
const fontData = await loadGoogleFont('Geist', '400;500;600', allText)
return new ImageResponse(
<div
style={{
height: '100%',
width: '100%',
display: 'flex',
flexDirection: 'column',
background: 'linear-gradient(315deg, #1e1e3f 0%, #1a1a2e 40%, #0f0f0f 100%)',
position: 'relative',
fontFamily: 'Geist',
}}
>
{/* Background texture */}
<img
src={backgroundImageUrl}
alt=''
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
objectFit: 'cover',
opacity: 0.04,
}}
/>
{/* Subtle purple glow from bottom right */}
<div
style={{
position: 'absolute',
bottom: 0,
right: 0,
width: '50%',
height: '100%',
background:
'radial-gradient(ellipse at bottom right, rgba(112, 31, 252, 0.1) 0%, transparent 50%)',
display: 'flex',
}}
/>
{/* Content */}
<div
style={{
display: 'flex',
flexDirection: 'column',
padding: '56px 72px',
height: '100%',
justifyContent: 'space-between',
}}
>
{/* Logo */}
<img src={`${baseUrl}/static/logo.png`} alt='sim' height={32} />
{/* Category + Title + Description */}
<div
style={{
display: 'flex',
flexDirection: 'column',
gap: 12,
}}
>
<span
style={{
fontSize: 15,
fontWeight: 600,
color: '#802fff',
letterSpacing: '0.02em',
}}
>
{category}
</span>
<span
style={{
fontSize: getTitleFontSize(title),
fontWeight: 600,
color: '#ffffff',
lineHeight: 1.1,
letterSpacing: '-0.02em',
}}
>
{title}
</span>
{description && (
<span
style={{
fontSize: 18,
fontWeight: 400,
color: '#a1a1aa',
lineHeight: 1.4,
marginTop: 4,
}}
>
{description.length > 100 ? `${description.slice(0, 100)}...` : description}
</span>
)}
</div>
{/* Footer */}
<span
style={{
fontSize: 15,
fontWeight: 500,
color: '#52525b',
}}
>
docs.sim.ai
</span>
</div>
</div>,
{
width: 1200,
height: 630,
fonts: [
{
name: 'Geist',
data: fontData,
style: 'normal',
},
],
}
)
}

View File

@@ -56,6 +56,14 @@ export const metadata = {
title: 'Sim Documentation - Visual Workflow Builder for AI Applications',
description:
'Comprehensive documentation for Sim - the visual workflow builder for AI applications. Create powerful AI agents, automation workflows, and data processing pipelines.',
images: [
{
url: 'https://docs.sim.ai/api/og?title=Sim%20Documentation&category=DOCUMENTATION',
width: 1200,
height: 630,
alt: 'Sim Documentation',
},
],
},
twitter: {
card: 'summary_large_image',
@@ -64,7 +72,7 @@ export const metadata = {
'Comprehensive documentation for Sim - the visual workflow builder for AI applications.',
creator: '@simdotai',
site: '@simdotai',
images: ['/og-image.png'],
images: ['https://docs.sim.ai/api/og?title=Sim%20Documentation&category=DOCUMENTATION'],
},
robots: {
index: true,

View File

@@ -39,9 +39,10 @@ Alle Elemente aus einer Webflow CMS-Sammlung auflisten
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `siteId` | string | Ja | ID der Webflow-Website |
| `collectionId` | string | Ja | ID der Sammlung |
| `offset` | number | Nein | Offset für Paginierung \(optional\) |
| `limit` | number | Nein | Maximale Anzahl der zurückzugebenden Elemente \(optional, Standard: 100\) |
| `offset` | number | Nein | Offset für Paginierung (optional) |
| `limit` | number | Nein | Maximale Anzahl der zurückzugebenden Elemente (optional, Standard: 100) |
#### Ausgabe
@@ -58,6 +59,7 @@ Ein einzelnes Element aus einer Webflow CMS-Sammlung abrufen
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `siteId` | string | Ja | ID der Webflow-Website |
| `collectionId` | string | Ja | ID der Sammlung |
| `itemId` | string | Ja | ID des abzurufenden Elements |
@@ -76,8 +78,9 @@ Ein neues Element in einer Webflow CMS-Sammlung erstellen
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `siteId` | string | Ja | ID der Webflow-Website |
| `collectionId` | string | Ja | ID der Sammlung |
| `fieldData` | json | Ja | Felddaten für das neue Element als JSON-Objekt. Die Schlüssel sollten mit den Feldnamen der Sammlung übereinstimmen. |
| `fieldData` | json | Ja | Felddaten für das neue Element als JSON-Objekt. Schlüssel sollten mit den Sammlungsfeldnamen übereinstimmen. |
#### Ausgabe
@@ -94,6 +97,7 @@ Ein vorhandenes Element in einer Webflow CMS-Sammlung aktualisieren
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `siteId` | string | Ja | ID der Webflow-Website |
| `collectionId` | string | Ja | ID der Sammlung |
| `itemId` | string | Ja | ID des zu aktualisierenden Elements |
| `fieldData` | json | Ja | Zu aktualisierende Felddaten als JSON-Objekt. Nur Felder einschließen, die geändert werden sollen. |
@@ -113,6 +117,7 @@ Ein Element aus einer Webflow CMS-Sammlung löschen
| Parameter | Typ | Erforderlich | Beschreibung |
| --------- | ---- | -------- | ----------- |
| `siteId` | string | Ja | ID der Webflow-Website |
| `collectionId` | string | Ja | ID der Sammlung |
| `itemId` | string | Ja | ID des zu löschenden Elements |

View File

@@ -42,6 +42,7 @@ List all items from a Webflow CMS collection
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `siteId` | string | Yes | ID of the Webflow site |
| `collectionId` | string | Yes | ID of the collection |
| `offset` | number | No | Offset for pagination \(optional\) |
| `limit` | number | No | Maximum number of items to return \(optional, default: 100\) |
@@ -61,6 +62,7 @@ Get a single item from a Webflow CMS collection
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `siteId` | string | Yes | ID of the Webflow site |
| `collectionId` | string | Yes | ID of the collection |
| `itemId` | string | Yes | ID of the item to retrieve |
@@ -79,6 +81,7 @@ Create a new item in a Webflow CMS collection
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `siteId` | string | Yes | ID of the Webflow site |
| `collectionId` | string | Yes | ID of the collection |
| `fieldData` | json | Yes | Field data for the new item as a JSON object. Keys should match collection field names. |
@@ -97,6 +100,7 @@ Update an existing item in a Webflow CMS collection
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `siteId` | string | Yes | ID of the Webflow site |
| `collectionId` | string | Yes | ID of the collection |
| `itemId` | string | Yes | ID of the item to update |
| `fieldData` | json | Yes | Field data to update as a JSON object. Only include fields you want to change. |
@@ -116,6 +120,7 @@ Delete an item from a Webflow CMS collection
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `siteId` | string | Yes | ID of the Webflow site |
| `collectionId` | string | Yes | ID of the collection |
| `itemId` | string | Yes | ID of the item to delete |

View File

@@ -39,6 +39,7 @@ Listar todos los elementos de una colección del CMS de Webflow
| Parámetro | Tipo | Obligatorio | Descripción |
| --------- | ---- | -------- | ----------- |
| `siteId` | string | Sí | ID del sitio de Webflow |
| `collectionId` | string | Sí | ID de la colección |
| `offset` | number | No | Desplazamiento para paginación \(opcional\) |
| `limit` | number | No | Número máximo de elementos a devolver \(opcional, predeterminado: 100\) |
@@ -58,6 +59,7 @@ Obtener un solo elemento de una colección del CMS de Webflow
| Parámetro | Tipo | Obligatorio | Descripción |
| --------- | ---- | -------- | ----------- |
| `siteId` | string | Sí | ID del sitio de Webflow |
| `collectionId` | string | Sí | ID de la colección |
| `itemId` | string | Sí | ID del elemento a recuperar |
@@ -76,6 +78,7 @@ Crear un nuevo elemento en una colección del CMS de Webflow
| Parámetro | Tipo | Obligatorio | Descripción |
| --------- | ---- | -------- | ----------- |
| `siteId` | string | Sí | ID del sitio de Webflow |
| `collectionId` | string | Sí | ID de la colección |
| `fieldData` | json | Sí | Datos de campo para el nuevo elemento como objeto JSON. Las claves deben coincidir con los nombres de campo de la colección. |
@@ -94,6 +97,7 @@ Actualizar un elemento existente en una colección CMS de Webflow
| Parámetro | Tipo | Obligatorio | Descripción |
| --------- | ---- | -------- | ----------- |
| `siteId` | string | Sí | ID del sitio de Webflow |
| `collectionId` | string | Sí | ID de la colección |
| `itemId` | string | Sí | ID del elemento a actualizar |
| `fieldData` | json | Sí | Datos de campo para actualizar como objeto JSON. Solo incluye los campos que quieres cambiar. |
@@ -113,6 +117,7 @@ Eliminar un elemento de una colección CMS de Webflow
| Parámetro | Tipo | Obligatorio | Descripción |
| --------- | ---- | -------- | ----------- |
| `siteId` | string | Sí | ID del sitio de Webflow |
| `collectionId` | string | Sí | ID de la colección |
| `itemId` | string | Sí | ID del elemento a eliminar |

View File

@@ -38,7 +38,8 @@ Lister tous les éléments d'une collection CMS Webflow
#### Entrée
| Paramètre | Type | Obligatoire | Description |
| --------- | ---- | -------- | ----------- |
| --------- | ---- | ---------- | ----------- |
| `siteId` | string | Oui | ID du site Webflow |
| `collectionId` | string | Oui | ID de la collection |
| `offset` | number | Non | Décalage pour la pagination \(facultatif\) |
| `limit` | number | Non | Nombre maximum d'éléments à retourner \(facultatif, par défaut : 100\) |
@@ -57,7 +58,8 @@ Obtenir un seul élément d'une collection CMS Webflow
#### Entrée
| Paramètre | Type | Obligatoire | Description |
| --------- | ---- | -------- | ----------- |
| --------- | ---- | ---------- | ----------- |
| `siteId` | string | Oui | ID du site Webflow |
| `collectionId` | string | Oui | ID de la collection |
| `itemId` | string | Oui | ID de l'élément à récupérer |
@@ -76,8 +78,9 @@ Créer un nouvel élément dans une collection CMS Webflow
| Paramètre | Type | Obligatoire | Description |
| --------- | ---- | ---------- | ----------- |
| `siteId` | string | Oui | ID du site Webflow |
| `collectionId` | string | Oui | ID de la collection |
| `fieldData` | json | Oui | Données de champ pour le nouvel élément sous forme d'objet JSON. Les clés doivent correspondre aux noms des champs de la collection. |
| `fieldData` | json | Oui | Données des champs pour le nouvel élément sous forme d'objet JSON. Les clés doivent correspondre aux noms des champs de la collection. |
#### Sortie
@@ -94,9 +97,10 @@ Mettre à jour un élément existant dans une collection CMS Webflow
| Paramètre | Type | Obligatoire | Description |
| --------- | ---- | ---------- | ----------- |
| `siteId` | string | Oui | ID du site Webflow |
| `collectionId` | string | Oui | ID de la collection |
| `itemId` | string | Oui | ID de l'élément à mettre à jour |
| `fieldData` | json | Oui | Données de champ à mettre à jour sous forme d'objet JSON. N'incluez que les champs que vous souhaitez modifier. |
| `fieldData` | json | Oui | Données des champs à mettre à jour sous forme d'objet JSON. N'incluez que les champs que vous souhaitez modifier. |
#### Sortie
@@ -113,6 +117,7 @@ Supprimer un élément d'une collection CMS Webflow
| Paramètre | Type | Obligatoire | Description |
| --------- | ---- | ---------- | ----------- |
| `siteId` | string | Oui | ID du site Webflow |
| `collectionId` | string | Oui | ID de la collection |
| `itemId` | string | Oui | ID de l'élément à supprimer |

View File

@@ -39,9 +39,10 @@ Webflow CMSコレクションからすべてのアイテムを一覧表示する
| パラメータ | 型 | 必須 | 説明 |
| --------- | ---- | -------- | ----------- |
| `siteId` | string | はい | WebflowサイトのID |
| `collectionId` | string | はい | コレクションのID |
| `offset` | number | いいえ | ページネーション用のオフセット(オプション) |
| `limit` | number | いいえ | 返すアイテムの最大オプション、デフォルト100 |
| `limit` | number | いいえ | 返す最大アイテム数オプション、デフォルト100 |
#### 出力
@@ -58,6 +59,7 @@ Webflow CMSコレクションから単一のアイテムを取得する
| パラメータ | 型 | 必須 | 説明 |
| --------- | ---- | -------- | ----------- |
| `siteId` | string | はい | WebflowサイトのID |
| `collectionId` | string | はい | コレクションのID |
| `itemId` | string | はい | 取得するアイテムのID |
@@ -76,8 +78,9 @@ Webflow CMSコレクションに新しいアイテムを作成する
| パラメータ | 型 | 必須 | 説明 |
| --------- | ---- | -------- | ----------- |
| `siteId` | string | はい | WebflowサイトのID |
| `collectionId` | string | はい | コレクションのID |
| `fieldData` | json | はい | 新しいアイテムのフィールドデータJSONオブジェクト形式)。キーはコレクションフィールド名と一致する必要があります。 |
| `fieldData` | json | はい | 新しいアイテムのフィールドデータJSONオブジェクト。キーはコレクションフィールド名と一致する必要があります。 |
#### 出力
@@ -94,9 +97,10 @@ Webflow CMSコレクション内の既存アイテムを更新する
| パラメータ | 型 | 必須 | 説明 |
| --------- | ---- | -------- | ----------- |
| `siteId` | string | はい | WebflowサイトのID |
| `collectionId` | string | はい | コレクションのID |
| `itemId` | string | はい | 更新するアイテムのID |
| `fieldData` | json | はい | 更新するフィールドデータJSONオブジェクト形式)。変更したいフィールドのみを含めてください。 |
| `fieldData` | json | はい | 更新するフィールドデータJSONオブジェクト。変更したいフィールドのみを含めてください。 |
#### 出力
@@ -113,6 +117,7 @@ Webflow CMSコレクションからアイテムを削除する
| パラメータ | 型 | 必須 | 説明 |
| --------- | ---- | -------- | ----------- |
| `siteId` | string | はい | WebflowサイトのID |
| `collectionId` | string | はい | コレクションのID |
| `itemId` | string | はい | 削除するアイテムのID |

View File

@@ -38,9 +38,10 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
| 参数 | 类型 | 必需 | 描述 |
| --------- | ---- | -------- | ----------- |
| `siteId` | string | 是 | Webflow 网站的 ID |
| `collectionId` | string | 是 | 集合的 ID |
| `offset` | number | 否 | 分页偏移量(可选) |
| `limit` | number | 否 | 返回的最大项目数可选默认值100 |
| `offset` | number | 否 | 分页偏移量(可选) |
| `limit` | number | 否 | 返回的最大项目数可选默认值100 |
#### 输出
@@ -57,8 +58,9 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
| 参数 | 类型 | 必需 | 描述 |
| --------- | ---- | -------- | ----------- |
| `siteId` | string | 是 | Webflow 网站的 ID |
| `collectionId` | string | 是 | 集合的 ID |
| `itemId` | string | 是 | 要检索项目 ID |
| `itemId` | string | 是 | 要检索项目 ID |
#### 输出
@@ -75,8 +77,9 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
| 参数 | 类型 | 必需 | 描述 |
| --------- | ---- | -------- | ----------- |
| `siteId` | string | 是 | Webflow 网站的 ID |
| `collectionId` | string | 是 | 集合的 ID |
| `fieldData` | json | 是 | 新项目的字段数据,格式为 JSON 对象。键名应与集合字段名匹配。 |
| `fieldData` | json | 是 | 新项目的字段数据,格式为 JSON 对象。键名应与集合字段名匹配。 |
#### 输出
@@ -93,9 +96,10 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
| 参数 | 类型 | 必需 | 描述 |
| --------- | ---- | -------- | ----------- |
| `siteId` | string | 是 | Webflow 网站的 ID |
| `collectionId` | string | 是 | 集合的 ID |
| `itemId` | string | 是 | 要更新项目的 ID |
| `fieldData` | json | 是 | 要更新的字段数据,格式为 JSON 对象。仅包含您想更改的字段。 |
| `fieldData` | json | 是 | 要更新的字段数据,格式为 JSON 对象。仅包含需要更改的字段。 |
#### 输出
@@ -112,6 +116,7 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
| 参数 | 类型 | 必需 | 描述 |
| --------- | ---- | -------- | ----------- |
| `siteId` | string | 是 | Webflow 网站的 ID |
| `collectionId` | string | 是 | 集合的 ID |
| `itemId` | string | 是 | 要删除项目的 ID |

View File

@@ -5973,31 +5973,31 @@ checksums:
content/9: 5914baadfaf2ca26d54130a36dd5ed29
content/10: 25507380ac7d9c7f8cf9f5256c6a0dbb
content/11: 371d0e46b4bd2c23f559b8bc112f6955
content/12: e7fb612c3323c1e6b05eacfcea360d34
content/12: e034523b05e8c7bd1723ef0ba96c5332
content/13: bcadfc362b69078beee0088e5936c98b
content/14: e5f830d6049ff79a318110098e5e0130
content/15: 711e90714806b91f93923018e82ad2e9
content/16: 0f3f7d9699d7397cb3a094c3229329ee
content/17: 371d0e46b4bd2c23f559b8bc112f6955
content/18: c53b5b8f901066e63fe159ad2fa5e6e0
content/18: 4b0c581b30f4449b0bfa3cdd4af69e02
content/19: bcadfc362b69078beee0088e5936c98b
content/20: 5f2afdd49c3ac13381401c69d1eca22a
content/21: cc4baa9096fafa4c6276f6136412ba66
content/22: 676f76e8a7154a576d7fa20b245cef70
content/23: 371d0e46b4bd2c23f559b8bc112f6955
content/24: c67c387eb7e274ee7c07b7e1748afce1
content/24: d26dd24c5398fd036d1f464ba3789002
content/25: bcadfc362b69078beee0088e5936c98b
content/26: a6ffebda549ad5b903a66c7d9ac03a20
content/27: 0dadd51cde48d6ea75b29ec3ee4ade56
content/28: cdc74f6483a0b4e9933ecdd92ed7480f
content/29: 371d0e46b4bd2c23f559b8bc112f6955
content/30: 4cda10aa374e1a46d60ad14eeaa79100
content/30: cec3953ee52d1d3c8b1a495f9684d35b
content/31: bcadfc362b69078beee0088e5936c98b
content/32: 5f221421953a0e760ead7388cbf66561
content/33: a3c0372590cef72d5d983dbc8dbbc2cb
content/34: 1402e53c08bdd8a741f44b2d66fcd003
content/35: 371d0e46b4bd2c23f559b8bc112f6955
content/36: 028e579a28e55def4fbc59f39f4610b7
content/36: db921b05a9e5ddceb28a4f3f1af2a377
content/37: bcadfc362b69078beee0088e5936c98b
content/38: 4fe4260da2f137679ce2fa42cffcf56a
content/39: b3f310d5ef115bea5a8b75bf25d7ea9a

Binary file not shown.

After

Width:  |  Height:  |  Size: 583 KiB

View File

@@ -4,10 +4,13 @@ DATABASE_URL="postgresql://postgres:password@localhost:5432/postgres"
# PostgreSQL Port (Optional) - defaults to 5432 if not specified
# POSTGRES_PORT=5432
# Authentication (Required)
# Authentication (Required unless DISABLE_AUTH=true)
BETTER_AUTH_SECRET=your_secret_key # Use `openssl rand -hex 32` to generate, or visit https://www.better-auth.com/docs/installation
BETTER_AUTH_URL=http://localhost:3000
# Authentication Bypass (Optional - for self-hosted deployments behind private networks)
# DISABLE_AUTH=true # Uncomment to bypass authentication entirely. Creates an anonymous session for all requests.
# NextJS (Required)
NEXT_PUBLIC_APP_URL=http://localhost:3000

View File

@@ -1,7 +1,7 @@
'use server'
import { env } from '@/lib/core/config/env'
import { isProd } from '@/lib/core/config/environment'
import { isProd } from '@/lib/core/config/feature-flags'
export async function getOAuthProviderStatus() {
const githubAvailable = !!(env.GITHUB_CLIENT_ID && env.GITHUB_CLIENT_SECRET)

View File

@@ -1,7 +1,6 @@
import { getOAuthProviderStatus } from '@/app/(auth)/components/oauth-provider-checker'
import LoginForm from '@/app/(auth)/login/login-form'
// Force dynamic rendering to avoid prerender errors with search params
export const dynamic = 'force-dynamic'
export default async function LoginPage() {

View File

@@ -1,16 +1,16 @@
import { env, isTruthy } from '@/lib/core/config/env'
import { isRegistrationDisabled } from '@/lib/core/config/feature-flags'
import { getOAuthProviderStatus } from '@/app/(auth)/components/oauth-provider-checker'
import SignupForm from '@/app/(auth)/signup/signup-form'
export const dynamic = 'force-dynamic'
export default async function SignupPage() {
const { githubAvailable, googleAvailable, isProduction } = await getOAuthProviderStatus()
if (isTruthy(env.DISABLE_REGISTRATION)) {
if (isRegistrationDisabled) {
return <div>Registration is disabled, please contact your admin.</div>
}
const { githubAvailable, googleAvailable, isProduction } = await getOAuthProviderStatus()
return (
<SignupForm
githubAvailable={githubAvailable}

View File

@@ -1,4 +1,4 @@
import { isEmailVerificationEnabled, isProd } from '@/lib/core/config/environment'
import { isEmailVerificationEnabled, isProd } from '@/lib/core/config/feature-flags'
import { hasEmailService } from '@/lib/messaging/email/mailer'
import { VerifyContent } from '@/app/(auth)/verify/verify-content'

View File

@@ -1,6 +1,6 @@
import { createLogger } from '@/lib/logs/console/logger'
const DEFAULT_STARS = '18.6k'
const DEFAULT_STARS = '19.4k'
const logger = createLogger('GitHubStars')

View File

@@ -13,7 +13,7 @@ import {
SelectValue,
} from '@/components/ui/select'
import { Textarea } from '@/components/ui/textarea'
import { isHosted } from '@/lib/core/config/environment'
import { isHosted } from '@/lib/core/config/feature-flags'
import { cn } from '@/lib/core/utils/cn'
import { createLogger } from '@/lib/logs/console/logger'
import { quickValidateEmail } from '@/lib/messaging/email/validation'

View File

@@ -1,6 +1,6 @@
'use client'
import { isHosted } from '@/lib/core/config/environment'
import { isHosted } from '@/lib/core/config/feature-flags'
import { soehne } from '@/app/_styles/fonts/soehne/soehne'
import Footer from '@/app/(landing)/components/footer/footer'
import Nav from '@/app/(landing)/components/nav/nav'

View File

@@ -7,7 +7,7 @@ import Link from 'next/link'
import { useRouter } from 'next/navigation'
import { GithubIcon } from '@/components/icons'
import { useBrandConfig } from '@/lib/branding/branding'
import { isHosted } from '@/lib/core/config/environment'
import { isHosted } from '@/lib/core/config/feature-flags'
import { createLogger } from '@/lib/logs/console/logger'
import { soehne } from '@/app/_styles/fonts/soehne/soehne'
import { getFormattedGitHubStars } from '@/app/(landing)/actions/github'

View File

@@ -1,6 +1,23 @@
import { toNextJsHandler } from 'better-auth/next-js'
import { type NextRequest, NextResponse } from 'next/server'
import { auth } from '@/lib/auth'
import { createAnonymousSession, ensureAnonymousUserExists } from '@/lib/auth/anonymous'
import { isAuthDisabled } from '@/lib/core/config/feature-flags'
export const dynamic = 'force-dynamic'
export const { GET, POST } = toNextJsHandler(auth.handler)
const { GET: betterAuthGET, POST: betterAuthPOST } = toNextJsHandler(auth.handler)
export async function GET(request: NextRequest) {
const url = new URL(request.url)
const path = url.pathname.replace('/api/auth/', '')
if (path === 'get-session' && isAuthDisabled) {
await ensureAnonymousUserExists()
return NextResponse.json(createAnonymousSession())
}
return betterAuthGET(request)
}
export const POST = betterAuthPOST

View File

@@ -1,9 +1,14 @@
import { headers } from 'next/headers'
import { NextResponse } from 'next/server'
import { auth } from '@/lib/auth'
import { isAuthDisabled } from '@/lib/core/config/feature-flags'
export async function POST() {
try {
if (isAuthDisabled) {
return NextResponse.json({ token: 'anonymous-socket-token' })
}
const hdrs = await headers()
const response = await auth.api.generateOneTimeToken({
headers: hdrs,

View File

@@ -1,14 +1,14 @@
import { db, ssoProvider } from '@sim/db'
import { eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { auth } from '@/lib/auth'
import { NextResponse } from 'next/server'
import { getSession } from '@/lib/auth'
import { createLogger } from '@/lib/logs/console/logger'
const logger = createLogger('SSO-Providers')
export async function GET(req: NextRequest) {
export async function GET() {
try {
const session = await auth.api.getSession({ headers: req.headers })
const session = await getSession()
let providers
if (session?.user?.id) {
@@ -38,8 +38,6 @@ export async function GET(req: NextRequest) {
: ('oidc' as 'oidc' | 'saml'),
}))
} else {
// Unauthenticated users can only see basic info (domain only)
// This is needed for SSO login flow to check if a domain has SSO enabled
const results = await db
.select({
domain: ssoProvider.domain,

View File

@@ -5,7 +5,7 @@ import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { checkAndBillOverageThreshold } from '@/lib/billing/threshold-billing'
import { checkInternalApiKey } from '@/lib/copilot/utils'
import { isBillingEnabled } from '@/lib/core/config/environment'
import { isBillingEnabled } from '@/lib/core/config/feature-flags'
import { generateRequestId } from '@/lib/core/utils/request'
import { createLogger } from '@/lib/logs/console/logger'

View File

@@ -132,7 +132,7 @@ export async function POST(
if ((password || email) && !input) {
const response = addCorsHeaders(createSuccessResponse({ authenticated: true }), request)
setChatAuthCookie(response, deployment.id, deployment.authType)
setChatAuthCookie(response, deployment.id, deployment.authType, deployment.password)
return response
}
@@ -315,7 +315,7 @@ export async function GET(
if (
deployment.authType !== 'public' &&
authCookie &&
validateAuthToken(authCookie.value, deployment.id)
validateAuthToken(authCookie.value, deployment.id, deployment.password)
) {
return addCorsHeaders(
createSuccessResponse({

View File

@@ -6,6 +6,12 @@ import { NextRequest } from 'next/server'
*/
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
vi.mock('@/lib/core/config/feature-flags', () => ({
isDev: true,
isHosted: false,
isProd: false,
}))
describe('Chat Edit API Route', () => {
const mockSelect = vi.fn()
const mockFrom = vi.fn()
@@ -24,7 +30,6 @@ describe('Chat Edit API Route', () => {
beforeEach(() => {
vi.resetModules()
// Set default return values
mockLimit.mockResolvedValue([])
mockSelect.mockReturnValue({ from: mockFrom })
mockFrom.mockReturnValue({ where: mockWhere })
@@ -77,10 +82,6 @@ describe('Chat Edit API Route', () => {
getEmailDomain: vi.fn().mockReturnValue('localhost:3000'),
}))
vi.doMock('@/lib/core/config/environment', () => ({
isDev: true,
}))
vi.doMock('@/app/api/chat/utils', () => ({
checkChatAccess: mockCheckChatAccess,
}))
@@ -254,7 +255,6 @@ describe('Chat Edit API Route', () => {
mockCheckChatAccess.mockResolvedValue({ hasAccess: true, chat: mockChat })
// Reset and reconfigure mockLimit to return the conflict
mockLimit.mockReset()
mockLimit.mockResolvedValue([{ id: 'other-chat-id', identifier: 'new-identifier' }])
mockWhere.mockReturnValue({ limit: mockLimit })
@@ -291,7 +291,7 @@ describe('Chat Edit API Route', () => {
const req = new NextRequest('http://localhost:3000/api/chat/manage/chat-123', {
method: 'PATCH',
body: JSON.stringify({ authType: 'password' }), // No password provided
body: JSON.stringify({ authType: 'password' }),
})
const { PATCH } = await import('@/app/api/chat/manage/[id]/route')
const response = await PATCH(req, { params: Promise.resolve({ id: 'chat-123' }) })
@@ -316,9 +316,8 @@ describe('Chat Edit API Route', () => {
workflowId: 'workflow-123',
}
// User doesn't own chat but has workspace admin access
mockCheckChatAccess.mockResolvedValue({ hasAccess: true, chat: mockChat })
mockLimit.mockResolvedValueOnce([]) // No identifier conflict
mockLimit.mockResolvedValueOnce([])
const req = new NextRequest('http://localhost:3000/api/chat/manage/chat-123', {
method: 'PATCH',
@@ -399,7 +398,6 @@ describe('Chat Edit API Route', () => {
}),
}))
// User doesn't own chat but has workspace admin access
mockCheckChatAccess.mockResolvedValue({ hasAccess: true })
mockWhere.mockResolvedValue(undefined)

View File

@@ -4,7 +4,7 @@ import { eq } from 'drizzle-orm'
import type { NextRequest } from 'next/server'
import { z } from 'zod'
import { getSession } from '@/lib/auth'
import { isDev } from '@/lib/core/config/environment'
import { isDev } from '@/lib/core/config/feature-flags'
import { encryptSecret } from '@/lib/core/security/encryption'
import { getEmailDomain } from '@/lib/core/utils/urls'
import { createLogger } from '@/lib/logs/console/logger'

View File

@@ -5,7 +5,7 @@ import type { NextRequest } from 'next/server'
import { v4 as uuidv4 } from 'uuid'
import { z } from 'zod'
import { getSession } from '@/lib/auth'
import { isDev } from '@/lib/core/config/environment'
import { isDev } from '@/lib/core/config/feature-flags'
import { encryptSecret } from '@/lib/core/security/encryption'
import { getBaseUrl } from '@/lib/core/utils/urls'
import { createLogger } from '@/lib/logs/console/logger'

View File

@@ -44,6 +44,12 @@ vi.mock('@/lib/core/utils/request', () => ({
generateRequestId: vi.fn(),
}))
vi.mock('@/lib/core/config/feature-flags', () => ({
isDev: true,
isHosted: false,
isProd: false,
}))
describe('Chat API Utils', () => {
beforeEach(() => {
vi.doMock('@/lib/logs/console/logger', () => ({
@@ -62,11 +68,6 @@ describe('Chat API Utils', () => {
NODE_ENV: 'development',
},
})
vi.doMock('@/lib/core/config/environment', () => ({
isDev: true,
isHosted: false,
}))
})
afterEach(() => {

View File

@@ -1,14 +1,19 @@
import { createHash } from 'crypto'
import { db } from '@sim/db'
import { chat, workflow } from '@sim/db/schema'
import { eq } from 'drizzle-orm'
import type { NextRequest, NextResponse } from 'next/server'
import { isDev } from '@/lib/core/config/environment'
import { isDev } from '@/lib/core/config/feature-flags'
import { decryptSecret } from '@/lib/core/security/encryption'
import { createLogger } from '@/lib/logs/console/logger'
import { hasAdminPermission } from '@/lib/workspaces/permissions/utils'
const logger = createLogger('ChatAuthUtils')
function hashPassword(encryptedPassword: string): string {
return createHash('sha256').update(encryptedPassword).digest('hex').substring(0, 8)
}
/**
* Check if user has permission to create a chat for a specific workflow
* Either the user owns the workflow directly OR has admin permission for the workflow's workspace
@@ -77,14 +82,20 @@ export async function checkChatAccess(
return { hasAccess: false }
}
const encryptAuthToken = (chatId: string, type: string): string => {
return Buffer.from(`${chatId}:${type}:${Date.now()}`).toString('base64')
function encryptAuthToken(chatId: string, type: string, encryptedPassword?: string | null): string {
const pwHash = encryptedPassword ? hashPassword(encryptedPassword) : ''
return Buffer.from(`${chatId}:${type}:${Date.now()}:${pwHash}`).toString('base64')
}
export const validateAuthToken = (token: string, chatId: string): boolean => {
export function validateAuthToken(
token: string,
chatId: string,
encryptedPassword?: string | null
): boolean {
try {
const decoded = Buffer.from(token, 'base64').toString()
const [storedId, _type, timestamp] = decoded.split(':')
const parts = decoded.split(':')
const [storedId, _type, timestamp, storedPwHash] = parts
if (storedId !== chatId) {
return false
@@ -92,20 +103,32 @@ export const validateAuthToken = (token: string, chatId: string): boolean => {
const createdAt = Number.parseInt(timestamp)
const now = Date.now()
const expireTime = 24 * 60 * 60 * 1000 // 24 hours
const expireTime = 24 * 60 * 60 * 1000
if (now - createdAt > expireTime) {
return false
}
if (encryptedPassword) {
const currentPwHash = hashPassword(encryptedPassword)
if (storedPwHash !== currentPwHash) {
return false
}
}
return true
} catch (_e) {
return false
}
}
export const setChatAuthCookie = (response: NextResponse, chatId: string, type: string): void => {
const token = encryptAuthToken(chatId, type)
export function setChatAuthCookie(
response: NextResponse,
chatId: string,
type: string,
encryptedPassword?: string | null
): void {
const token = encryptAuthToken(chatId, type, encryptedPassword)
response.cookies.set({
name: `chat_auth_${chatId}`,
value: token,
@@ -113,7 +136,7 @@ export const setChatAuthCookie = (response: NextResponse, chatId: string, type:
secure: !isDev,
sameSite: 'lax',
path: '/',
maxAge: 60 * 60 * 24, // 24 hours
maxAge: 60 * 60 * 24,
})
}
@@ -145,7 +168,7 @@ export async function validateChatAuth(
const cookieName = `chat_auth_${deployment.id}`
const authCookie = request.cookies.get(cookieName)
if (authCookie && validateAuthToken(authCookie.value, deployment.id)) {
if (authCookie && validateAuthToken(authCookie.value, deployment.id, deployment.password)) {
return { authorized: true }
}
@@ -259,8 +282,8 @@ export async function validateChatAuth(
return { authorized: false, error: 'Email not authorized for SSO access' }
}
const { auth } = await import('@/lib/auth')
const session = await auth.api.getSession({ headers: request.headers })
const { getSession } = await import('@/lib/auth')
const session = await getSession()
if (!session || !session.user) {
return { authorized: false, error: 'auth_required_sso' }

View File

@@ -2,7 +2,7 @@ import { db } from '@sim/db'
import { settings } from '@sim/db/schema'
import { eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { auth } from '@/lib/auth'
import { getSession } from '@/lib/auth'
import { createLogger } from '@/lib/logs/console/logger'
const logger = createLogger('CopilotAutoAllowedToolsAPI')
@@ -10,9 +10,9 @@ const logger = createLogger('CopilotAutoAllowedToolsAPI')
/**
* GET - Fetch user's auto-allowed integration tools
*/
export async function GET(request: NextRequest) {
export async function GET() {
try {
const session = await auth.api.getSession({ headers: request.headers })
const session = await getSession()
if (!session?.user?.id) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
@@ -31,7 +31,6 @@ export async function GET(request: NextRequest) {
return NextResponse.json({ autoAllowedTools })
}
// If no settings record exists, create one with empty array
await db.insert(settings).values({
id: userId,
userId,
@@ -50,7 +49,7 @@ export async function GET(request: NextRequest) {
*/
export async function POST(request: NextRequest) {
try {
const session = await auth.api.getSession({ headers: request.headers })
const session = await getSession()
if (!session?.user?.id) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
@@ -65,13 +64,11 @@ export async function POST(request: NextRequest) {
const toolId = body.toolId
// Get existing settings
const [existing] = await db.select().from(settings).where(eq(settings.userId, userId)).limit(1)
if (existing) {
const currentTools = (existing.copilotAutoAllowedTools as string[]) || []
// Add tool if not already present
if (!currentTools.includes(toolId)) {
const updatedTools = [...currentTools, toolId]
await db
@@ -89,7 +86,6 @@ export async function POST(request: NextRequest) {
return NextResponse.json({ success: true, autoAllowedTools: currentTools })
}
// Create new settings record with the tool
await db.insert(settings).values({
id: userId,
userId,
@@ -109,7 +105,7 @@ export async function POST(request: NextRequest) {
*/
export async function DELETE(request: NextRequest) {
try {
const session = await auth.api.getSession({ headers: request.headers })
const session = await getSession()
if (!session?.user?.id) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
@@ -123,7 +119,6 @@ export async function DELETE(request: NextRequest) {
return NextResponse.json({ error: 'toolId query parameter is required' }, { status: 400 })
}
// Get existing settings
const [existing] = await db.select().from(settings).where(eq(settings.userId, userId)).limit(1)
if (existing) {

View File

@@ -1,6 +1,6 @@
import { eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { auth } from '@/lib/auth'
import { getSession } from '@/lib/auth'
import { createLogger } from '@/lib/logs/console/logger'
import { db } from '@/../../packages/db'
import { settings } from '@/../../packages/db/schema'
@@ -32,7 +32,7 @@ const DEFAULT_ENABLED_MODELS: Record<string, boolean> = {
// GET - Fetch user's enabled models
export async function GET(request: NextRequest) {
try {
const session = await auth.api.getSession({ headers: request.headers })
const session = await getSession()
if (!session?.user?.id) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
@@ -40,7 +40,6 @@ export async function GET(request: NextRequest) {
const userId = session.user.id
// Try to fetch existing settings record
const [userSettings] = await db
.select()
.from(settings)
@@ -50,13 +49,11 @@ export async function GET(request: NextRequest) {
if (userSettings) {
const userModelsMap = (userSettings.copilotEnabledModels as Record<string, boolean>) || {}
// Merge: start with defaults, then override with user's existing preferences
const mergedModels = { ...DEFAULT_ENABLED_MODELS }
for (const [modelId, enabled] of Object.entries(userModelsMap)) {
mergedModels[modelId] = enabled
}
// If we added any new models, update the database
const hasNewModels = Object.keys(DEFAULT_ENABLED_MODELS).some(
(key) => !(key in userModelsMap)
)
@@ -76,7 +73,6 @@ export async function GET(request: NextRequest) {
})
}
// If no settings record exists, create one with defaults
await db.insert(settings).values({
id: userId,
userId,
@@ -97,7 +93,7 @@ export async function GET(request: NextRequest) {
// PUT - Update user's enabled models
export async function PUT(request: NextRequest) {
try {
const session = await auth.api.getSession({ headers: request.headers })
const session = await getSession()
if (!session?.user?.id) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
@@ -110,11 +106,9 @@ export async function PUT(request: NextRequest) {
return NextResponse.json({ error: 'enabledModels must be an object' }, { status: 400 })
}
// Check if settings record exists
const [existing] = await db.select().from(settings).where(eq(settings.userId, userId)).limit(1)
if (existing) {
// Update existing record
await db
.update(settings)
.set({
@@ -123,7 +117,6 @@ export async function PUT(request: NextRequest) {
})
.where(eq(settings.userId, userId))
} else {
// Create new settings record
await db.insert(settings).values({
id: userId,
userId,

View File

@@ -1,6 +1,6 @@
import { createContext, Script } from 'vm'
import { type NextRequest, NextResponse } from 'next/server'
import { env, isTruthy } from '@/lib/core/config/env'
import { isE2bEnabled } from '@/lib/core/config/feature-flags'
import { validateProxyUrl } from '@/lib/core/security/input-validation'
import { generateRequestId } from '@/lib/core/utils/request'
import { executeInE2B } from '@/lib/execution/e2b'
@@ -701,7 +701,6 @@ export async function POST(req: NextRequest) {
resolvedCode = codeResolution.resolvedCode
const contextVariables = codeResolution.contextVariables
const e2bEnabled = isTruthy(env.E2B_ENABLED)
const lang = isValidCodeLanguage(language) ? language : DEFAULT_CODE_LANGUAGE
// Extract imports once for JavaScript code (reuse later to avoid double extraction)
@@ -722,14 +721,14 @@ export async function POST(req: NextRequest) {
}
// Python always requires E2B
if (lang === CodeLanguage.Python && !e2bEnabled) {
if (lang === CodeLanguage.Python && !isE2bEnabled) {
throw new Error(
'Python execution requires E2B to be enabled. Please contact your administrator to enable E2B, or use JavaScript instead.'
)
}
// JavaScript with imports requires E2B
if (lang === CodeLanguage.JavaScript && hasImports && !e2bEnabled) {
if (lang === CodeLanguage.JavaScript && hasImports && !isE2bEnabled) {
throw new Error(
'JavaScript code with import statements requires E2B to be enabled. Please remove the import statements, or contact your administrator to enable E2B.'
)
@@ -740,13 +739,13 @@ export async function POST(req: NextRequest) {
// - Not a custom tool AND
// - (Python OR JavaScript with imports)
const useE2B =
e2bEnabled &&
isE2bEnabled &&
!isCustomTool &&
(lang === CodeLanguage.Python || (lang === CodeLanguage.JavaScript && hasImports))
if (useE2B) {
logger.info(`[${requestId}] E2B status`, {
enabled: e2bEnabled,
enabled: isE2bEnabled,
hasApiKey: Boolean(process.env.E2B_API_KEY),
language: lang,
})

View File

@@ -6,7 +6,7 @@ import { z } from 'zod'
import { getSession } from '@/lib/auth'
import { getPlanPricing } from '@/lib/billing/core/billing'
import { requireStripeClient } from '@/lib/billing/stripe-client'
import { isBillingEnabled } from '@/lib/core/config/environment'
import { isBillingEnabled } from '@/lib/core/config/feature-flags'
import { createLogger } from '@/lib/logs/console/logger'
const logger = createLogger('OrganizationSeatsAPI')

View File

@@ -3,7 +3,7 @@ import { NextResponse } from 'next/server'
import { z } from 'zod'
import { checkHybridAuth } from '@/lib/auth/hybrid'
import { generateInternalToken } from '@/lib/auth/internal'
import { isDev } from '@/lib/core/config/environment'
import { isDev } from '@/lib/core/config/feature-flags'
import { createPinnedUrl, validateUrlWithDNS } from '@/lib/core/security/input-validation'
import { generateRequestId } from '@/lib/core/utils/request'
import { getBaseUrl } from '@/lib/core/utils/urls'

View File

@@ -1,26 +1,81 @@
import { db } from '@sim/db'
import { chat } from '@sim/db/schema'
import { eq } from 'drizzle-orm'
import type { NextRequest } from 'next/server'
import { checkHybridAuth } from '@/lib/auth/hybrid'
import { env } from '@/lib/core/config/env'
import { validateAlphanumericId } from '@/lib/core/security/input-validation'
import { createLogger } from '@/lib/logs/console/logger'
import { validateAuthToken } from '@/app/api/chat/utils'
const logger = createLogger('ProxyTTSStreamAPI')
export async function POST(request: NextRequest) {
/**
* Validates chat-based authentication for deployed chat voice mode
* Checks if the user has a valid chat auth cookie for the given chatId
*/
async function validateChatAuth(request: NextRequest, chatId: string): Promise<boolean> {
try {
const authResult = await checkHybridAuth(request, { requireWorkflowId: false })
if (!authResult.success) {
logger.error('Authentication failed for TTS stream proxy:', authResult.error)
return new Response('Unauthorized', { status: 401 })
const chatResult = await db
.select({
id: chat.id,
isActive: chat.isActive,
authType: chat.authType,
password: chat.password,
})
.from(chat)
.where(eq(chat.id, chatId))
.limit(1)
if (chatResult.length === 0 || !chatResult[0].isActive) {
logger.warn('Chat not found or inactive for TTS auth:', chatId)
return false
}
const body = await request.json()
const { text, voiceId, modelId = 'eleven_turbo_v2_5' } = body
const chatData = chatResult[0]
if (chatData.authType === 'public') {
return true
}
const cookieName = `chat_auth_${chatId}`
const authCookie = request.cookies.get(cookieName)
if (authCookie && validateAuthToken(authCookie.value, chatId, chatData.password)) {
return true
}
return false
} catch (error) {
logger.error('Error validating chat auth for TTS:', error)
return false
}
}
export async function POST(request: NextRequest) {
try {
let body: any
try {
body = await request.json()
} catch {
return new Response('Invalid request body', { status: 400 })
}
const { text, voiceId, modelId = 'eleven_turbo_v2_5', chatId } = body
if (!chatId) {
return new Response('chatId is required', { status: 400 })
}
if (!text || !voiceId) {
return new Response('Missing required parameters', { status: 400 })
}
const isChatAuthed = await validateChatAuth(request, chatId)
if (!isChatAuthed) {
logger.warn('Chat authentication failed for TTS, chatId:', chatId)
return new Response('Unauthorized', { status: 401 })
}
const voiceIdValidation = validateAlphanumericId(voiceId, 'voiceId', 255)
if (!voiceIdValidation.isValid) {
logger.error(`Invalid voice ID: ${voiceIdValidation.error}`)

View File

@@ -1,6 +1,7 @@
import type { NextRequest } from 'next/server'
import { NextResponse } from 'next/server'
import { checkHybridAuth } from '@/lib/auth/hybrid'
import { validateAlphanumericId } from '@/lib/core/security/input-validation'
import { getBaseUrl } from '@/lib/core/utils/urls'
import { createLogger } from '@/lib/logs/console/logger'
import { StorageService } from '@/lib/uploads'
@@ -147,6 +148,10 @@ export async function POST(request: NextRequest) {
{ status: 400 }
)
}
const voiceIdValidation = validateAlphanumericId(body.voiceId, 'voiceId')
if (!voiceIdValidation.isValid) {
return NextResponse.json({ error: voiceIdValidation.error }, { status: 400 })
}
const result = await synthesizeWithElevenLabs({
text,
apiKey,

View File

@@ -42,11 +42,11 @@ describe('Scheduled Workflow Execution API Route', () => {
executeScheduleJob: mockExecuteScheduleJob,
}))
vi.doMock('@/lib/core/config/env', () => ({
env: {
TRIGGER_DEV_ENABLED: false,
},
isTruthy: vi.fn(() => false),
vi.doMock('@/lib/core/config/feature-flags', () => ({
isTriggerDevEnabled: false,
isHosted: false,
isProd: false,
isDev: true,
}))
vi.doMock('drizzle-orm', () => ({
@@ -119,11 +119,11 @@ describe('Scheduled Workflow Execution API Route', () => {
},
}))
vi.doMock('@/lib/core/config/env', () => ({
env: {
TRIGGER_DEV_ENABLED: true,
},
isTruthy: vi.fn(() => true),
vi.doMock('@/lib/core/config/feature-flags', () => ({
isTriggerDevEnabled: true,
isHosted: false,
isProd: false,
isDev: true,
}))
vi.doMock('drizzle-orm', () => ({
@@ -191,11 +191,11 @@ describe('Scheduled Workflow Execution API Route', () => {
executeScheduleJob: vi.fn().mockResolvedValue(undefined),
}))
vi.doMock('@/lib/core/config/env', () => ({
env: {
TRIGGER_DEV_ENABLED: false,
},
isTruthy: vi.fn(() => false),
vi.doMock('@/lib/core/config/feature-flags', () => ({
isTriggerDevEnabled: false,
isHosted: false,
isProd: false,
isDev: true,
}))
vi.doMock('drizzle-orm', () => ({
@@ -250,11 +250,11 @@ describe('Scheduled Workflow Execution API Route', () => {
executeScheduleJob: vi.fn().mockResolvedValue(undefined),
}))
vi.doMock('@/lib/core/config/env', () => ({
env: {
TRIGGER_DEV_ENABLED: false,
},
isTruthy: vi.fn(() => false),
vi.doMock('@/lib/core/config/feature-flags', () => ({
isTriggerDevEnabled: false,
isHosted: false,
isProd: false,
isDev: true,
}))
vi.doMock('drizzle-orm', () => ({

View File

@@ -3,7 +3,7 @@ import { tasks } from '@trigger.dev/sdk'
import { and, eq, isNull, lt, lte, not, or } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { verifyCronAuth } from '@/lib/auth/internal'
import { env, isTruthy } from '@/lib/core/config/env'
import { isTriggerDevEnabled } from '@/lib/core/config/feature-flags'
import { generateRequestId } from '@/lib/core/utils/request'
import { createLogger } from '@/lib/logs/console/logger'
import { executeScheduleJob } from '@/background/schedule-execution'
@@ -54,9 +54,7 @@ export async function GET(request: NextRequest) {
logger.debug(`[${requestId}] Successfully queried schedules: ${dueSchedules.length} found`)
logger.info(`[${requestId}] Processing ${dueSchedules.length} due scheduled workflows`)
const useTrigger = isTruthy(env.TRIGGER_DEV_ENABLED)
if (useTrigger) {
if (isTriggerDevEnabled) {
const triggerPromises = dueSchedules.map(async (schedule) => {
const queueTime = schedule.lastQueuedAt ?? queuedAt

View File

@@ -23,13 +23,13 @@ export async function GET() {
if (!response.ok) {
console.warn('GitHub API request failed:', response.status)
return NextResponse.json({ stars: formatStarCount(14500) })
return NextResponse.json({ stars: formatStarCount(19400) })
}
const data = await response.json()
return NextResponse.json({ stars: formatStarCount(Number(data?.stargazers_count ?? 14500)) })
return NextResponse.json({ stars: formatStarCount(Number(data?.stargazers_count ?? 19400)) })
} catch (error) {
console.warn('Error fetching GitHub stars:', error)
return NextResponse.json({ stars: formatStarCount(14500) })
return NextResponse.json({ stars: formatStarCount(19400) })
}
}

View File

@@ -1,6 +1,6 @@
import { type NextRequest, NextResponse } from 'next/server'
import { env } from '@/lib/core/config/env'
import { isProd } from '@/lib/core/config/environment'
import { isProd } from '@/lib/core/config/feature-flags'
import { createLogger } from '@/lib/logs/console/logger'
const logger = createLogger('TelemetryAPI')

View File

@@ -1,6 +1,7 @@
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { checkHybridAuth } from '@/lib/auth/hybrid'
import { validateNumericId } from '@/lib/core/security/input-validation'
import { generateRequestId } from '@/lib/core/utils/request'
import { createLogger } from '@/lib/logs/console/logger'
import { processFilesToUserFiles } from '@/lib/uploads/utils/file-utils'
@@ -41,6 +42,17 @@ export async function POST(request: NextRequest) {
const body = await request.json()
const validatedData = DiscordSendMessageSchema.parse(body)
const channelIdValidation = validateNumericId(validatedData.channelId, 'channelId')
if (!channelIdValidation.isValid) {
logger.warn(`[${requestId}] Invalid channelId format`, {
error: channelIdValidation.error,
})
return NextResponse.json(
{ success: false, error: channelIdValidation.error },
{ status: 400 }
)
}
logger.info(`[${requestId}] Sending Discord message`, {
channelId: validatedData.channelId,
hasFiles: !!(validatedData.files && validatedData.files.length > 0),

View File

@@ -1,32 +1,55 @@
import { type NextRequest, NextResponse } from 'next/server'
import { getSession } from '@/lib/auth'
import { NextResponse } from 'next/server'
import { authorizeCredentialUse } from '@/lib/auth/credential-access'
import { validateAlphanumericId } from '@/lib/core/security/input-validation'
import { generateRequestId } from '@/lib/core/utils/request'
import { createLogger } from '@/lib/logs/console/logger'
import { getOAuthToken } from '@/app/api/auth/oauth/utils'
import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils'
const logger = createLogger('WebflowCollectionsAPI')
export const dynamic = 'force-dynamic'
export async function GET(request: NextRequest) {
export async function POST(request: Request) {
try {
const session = await getSession()
if (!session?.user?.id) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
const requestId = generateRequestId()
const body = await request.json()
const { credential, workflowId, siteId } = body
if (!credential) {
logger.error('Missing credential in request')
return NextResponse.json({ error: 'Credential is required' }, { status: 400 })
}
const { searchParams } = new URL(request.url)
const siteId = searchParams.get('siteId')
if (!siteId) {
return NextResponse.json({ error: 'Missing siteId parameter' }, { status: 400 })
const siteIdValidation = validateAlphanumericId(siteId, 'siteId')
if (!siteIdValidation.isValid) {
logger.error('Invalid siteId', { error: siteIdValidation.error })
return NextResponse.json({ error: siteIdValidation.error }, { status: 400 })
}
const accessToken = await getOAuthToken(session.user.id, 'webflow')
const authz = await authorizeCredentialUse(request as any, {
credentialId: credential,
workflowId,
})
if (!authz.ok || !authz.credentialOwnerUserId) {
return NextResponse.json({ error: authz.error || 'Unauthorized' }, { status: 403 })
}
const accessToken = await refreshAccessTokenIfNeeded(
credential,
authz.credentialOwnerUserId,
requestId
)
if (!accessToken) {
logger.error('Failed to get access token', {
credentialId: credential,
userId: authz.credentialOwnerUserId,
})
return NextResponse.json(
{ error: 'No Webflow access token found. Please connect your Webflow account.' },
{ status: 404 }
{
error: 'Could not retrieve access token',
authRequired: true,
},
{ status: 401 }
)
}
@@ -58,11 +81,11 @@ export async function GET(request: NextRequest) {
name: collection.displayName || collection.slug || collection.id,
}))
return NextResponse.json({ collections: formattedCollections }, { status: 200 })
} catch (error: any) {
logger.error('Error fetching Webflow collections', error)
return NextResponse.json({ collections: formattedCollections })
} catch (error) {
logger.error('Error processing Webflow collections request:', error)
return NextResponse.json(
{ error: 'Internal server error', details: error.message },
{ error: 'Failed to retrieve Webflow collections', details: (error as Error).message },
{ status: 500 }
)
}

View File

@@ -0,0 +1,106 @@
import { NextResponse } from 'next/server'
import { authorizeCredentialUse } from '@/lib/auth/credential-access'
import { validateAlphanumericId } from '@/lib/core/security/input-validation'
import { generateRequestId } from '@/lib/core/utils/request'
import { createLogger } from '@/lib/logs/console/logger'
import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils'
const logger = createLogger('WebflowItemsAPI')
export const dynamic = 'force-dynamic'
export async function POST(request: Request) {
try {
const requestId = generateRequestId()
const body = await request.json()
const { credential, workflowId, collectionId, search } = body
if (!credential) {
logger.error('Missing credential in request')
return NextResponse.json({ error: 'Credential is required' }, { status: 400 })
}
const collectionIdValidation = validateAlphanumericId(collectionId, 'collectionId')
if (!collectionIdValidation.isValid) {
logger.error('Invalid collectionId', { error: collectionIdValidation.error })
return NextResponse.json({ error: collectionIdValidation.error }, { status: 400 })
}
const authz = await authorizeCredentialUse(request as any, {
credentialId: credential,
workflowId,
})
if (!authz.ok || !authz.credentialOwnerUserId) {
return NextResponse.json({ error: authz.error || 'Unauthorized' }, { status: 403 })
}
const accessToken = await refreshAccessTokenIfNeeded(
credential,
authz.credentialOwnerUserId,
requestId
)
if (!accessToken) {
logger.error('Failed to get access token', {
credentialId: credential,
userId: authz.credentialOwnerUserId,
})
return NextResponse.json(
{
error: 'Could not retrieve access token',
authRequired: true,
},
{ status: 401 }
)
}
const response = await fetch(
`https://api.webflow.com/v2/collections/${collectionId}/items?limit=100`,
{
headers: {
Authorization: `Bearer ${accessToken}`,
accept: 'application/json',
},
}
)
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
logger.error('Failed to fetch Webflow items', {
status: response.status,
error: errorData,
collectionId,
})
return NextResponse.json(
{ error: 'Failed to fetch Webflow items', details: errorData },
{ status: response.status }
)
}
const data = await response.json()
const items = data.items || []
let formattedItems = items.map((item: any) => {
const fieldData = item.fieldData || {}
const name = fieldData.name || fieldData.title || fieldData.slug || item.id
return {
id: item.id,
name,
}
})
if (search) {
const searchLower = search.toLowerCase()
formattedItems = formattedItems.filter((item: { id: string; name: string }) =>
item.name.toLowerCase().includes(searchLower)
)
}
return NextResponse.json({ items: formattedItems })
} catch (error) {
logger.error('Error processing Webflow items request:', error)
return NextResponse.json(
{ error: 'Failed to retrieve Webflow items', details: (error as Error).message },
{ status: 500 }
)
}
}

View File

@@ -1,25 +1,48 @@
import { type NextRequest, NextResponse } from 'next/server'
import { getSession } from '@/lib/auth'
import { NextResponse } from 'next/server'
import { authorizeCredentialUse } from '@/lib/auth/credential-access'
import { generateRequestId } from '@/lib/core/utils/request'
import { createLogger } from '@/lib/logs/console/logger'
import { getOAuthToken } from '@/app/api/auth/oauth/utils'
import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils'
const logger = createLogger('WebflowSitesAPI')
export const dynamic = 'force-dynamic'
export async function GET(request: NextRequest) {
export async function POST(request: Request) {
try {
const session = await getSession()
if (!session?.user?.id) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
const requestId = generateRequestId()
const body = await request.json()
const { credential, workflowId } = body
if (!credential) {
logger.error('Missing credential in request')
return NextResponse.json({ error: 'Credential is required' }, { status: 400 })
}
const accessToken = await getOAuthToken(session.user.id, 'webflow')
const authz = await authorizeCredentialUse(request as any, {
credentialId: credential,
workflowId,
})
if (!authz.ok || !authz.credentialOwnerUserId) {
return NextResponse.json({ error: authz.error || 'Unauthorized' }, { status: 403 })
}
const accessToken = await refreshAccessTokenIfNeeded(
credential,
authz.credentialOwnerUserId,
requestId
)
if (!accessToken) {
logger.error('Failed to get access token', {
credentialId: credential,
userId: authz.credentialOwnerUserId,
})
return NextResponse.json(
{ error: 'No Webflow access token found. Please connect your Webflow account.' },
{ status: 404 }
{
error: 'Could not retrieve access token',
authRequired: true,
},
{ status: 401 }
)
}
@@ -50,11 +73,11 @@ export async function GET(request: NextRequest) {
name: site.displayName || site.shortName || site.id,
}))
return NextResponse.json({ sites: formattedSites }, { status: 200 })
} catch (error: any) {
logger.error('Error fetching Webflow sites', error)
return NextResponse.json({ sites: formattedSites })
} catch (error) {
logger.error('Error processing Webflow sites request:', error)
return NextResponse.json(
{ error: 'Internal server error', details: error.message },
{ error: 'Failed to retrieve Webflow sites', details: (error as Error).message },
{ status: 500 }
)
}

View File

@@ -35,19 +35,18 @@
* GET /api/v1/admin/organizations/:id - Get organization details
* PATCH /api/v1/admin/organizations/:id - Update organization
* GET /api/v1/admin/organizations/:id/members - List organization members
* POST /api/v1/admin/organizations/:id/members - Add/update member in organization
* POST /api/v1/admin/organizations/:id/members - Add/update member (validates seat availability)
* GET /api/v1/admin/organizations/:id/members/:mid - Get member details
* PATCH /api/v1/admin/organizations/:id/members/:mid - Update member role
* DELETE /api/v1/admin/organizations/:id/members/:mid - Remove member
* GET /api/v1/admin/organizations/:id/billing - Get org billing summary
* PATCH /api/v1/admin/organizations/:id/billing - Update org usage limit
* GET /api/v1/admin/organizations/:id/seats - Get seat analytics
* PATCH /api/v1/admin/organizations/:id/seats - Update seat count
*
* Subscriptions:
* GET /api/v1/admin/subscriptions - List all subscriptions
* GET /api/v1/admin/subscriptions/:id - Get subscription details
* PATCH /api/v1/admin/subscriptions/:id - Update subscription
* DELETE /api/v1/admin/subscriptions/:id - Cancel subscription (?atPeriodEnd=true for scheduled)
*/
export type { AdminAuthFailure, AdminAuthResult, AdminAuthSuccess } from '@/app/api/v1/admin/auth'

View File

@@ -12,6 +12,9 @@
* POST /api/v1/admin/organizations/[id]/members
*
* Add a user to an organization with full billing logic.
* Validates seat availability before adding (uses same logic as invitation flow):
* - Team plans: checks seats column
* - Enterprise plans: checks metadata.seats
* Handles Pro usage snapshot and subscription cancellation like the invitation flow.
* If user is already a member, updates their role if different.
*
@@ -29,6 +32,7 @@ import { db } from '@sim/db'
import { member, organization, user, userStats } from '@sim/db/schema'
import { count, eq } from 'drizzle-orm'
import { addUserToOrganization } from '@/lib/billing/organizations/membership'
import { requireStripeClient } from '@/lib/billing/stripe-client'
import { createLogger } from '@/lib/logs/console/logger'
import { withAdminAuthParams } from '@/app/api/v1/admin/middleware'
import {
@@ -223,6 +227,29 @@ export const POST = withAdminAuthParams<RouteParams>(async (request, context) =>
return badRequestResponse(result.error || 'Failed to add member')
}
// Sync Pro subscription cancellation with Stripe (same as invitation flow)
if (result.billingActions.proSubscriptionToCancel?.stripeSubscriptionId) {
try {
const stripe = requireStripeClient()
await stripe.subscriptions.update(
result.billingActions.proSubscriptionToCancel.stripeSubscriptionId,
{ cancel_at_period_end: true }
)
logger.info('Admin API: Synced Pro cancellation with Stripe', {
userId: body.userId,
subscriptionId: result.billingActions.proSubscriptionToCancel.subscriptionId,
stripeSubscriptionId: result.billingActions.proSubscriptionToCancel.stripeSubscriptionId,
})
} catch (stripeError) {
logger.error('Admin API: Failed to sync Pro cancellation with Stripe', {
userId: body.userId,
subscriptionId: result.billingActions.proSubscriptionToCancel.subscriptionId,
stripeSubscriptionId: result.billingActions.proSubscriptionToCancel.stripeSubscriptionId,
error: stripeError,
})
}
}
const data: AdminMember = {
id: result.memberId!,
userId: body.userId,

View File

@@ -4,26 +4,12 @@
* Get organization seat analytics including member activity.
*
* Response: AdminSingleResponse<AdminSeatAnalytics>
*
* PATCH /api/v1/admin/organizations/[id]/seats
*
* Update organization seat count with Stripe sync (matches user flow).
*
* Body:
* - seats: number - New seat count (positive integer)
*
* Response: AdminSingleResponse<{ success: true, seats: number, plan: string, stripeUpdated?: boolean }>
*/
import { db } from '@sim/db'
import { organization, subscription } from '@sim/db/schema'
import { and, eq } from 'drizzle-orm'
import { requireStripeClient } from '@/lib/billing/stripe-client'
import { getOrganizationSeatAnalytics } from '@/lib/billing/validation/seat-management'
import { createLogger } from '@/lib/logs/console/logger'
import { withAdminAuthParams } from '@/app/api/v1/admin/middleware'
import {
badRequestResponse,
internalErrorResponse,
notFoundResponse,
singleResponse,
@@ -75,122 +61,3 @@ export const GET = withAdminAuthParams<RouteParams>(async (_, context) => {
return internalErrorResponse('Failed to get organization seats')
}
})
export const PATCH = withAdminAuthParams<RouteParams>(async (request, context) => {
const { id: organizationId } = await context.params
try {
const body = await request.json()
if (typeof body.seats !== 'number' || body.seats < 1 || !Number.isInteger(body.seats)) {
return badRequestResponse('seats must be a positive integer')
}
const [orgData] = await db
.select({ id: organization.id })
.from(organization)
.where(eq(organization.id, organizationId))
.limit(1)
if (!orgData) {
return notFoundResponse('Organization')
}
const [subData] = await db
.select()
.from(subscription)
.where(and(eq(subscription.referenceId, organizationId), eq(subscription.status, 'active')))
.limit(1)
if (!subData) {
return notFoundResponse('Subscription')
}
const newSeatCount = body.seats
let stripeUpdated = false
if (subData.plan === 'enterprise') {
const currentMetadata = (subData.metadata as Record<string, unknown>) || {}
const newMetadata = {
...currentMetadata,
seats: newSeatCount,
}
await db
.update(subscription)
.set({ metadata: newMetadata })
.where(eq(subscription.id, subData.id))
logger.info(`Admin API: Updated enterprise seats for organization ${organizationId}`, {
seats: newSeatCount,
})
} else if (subData.plan === 'team') {
if (subData.stripeSubscriptionId) {
const stripe = requireStripeClient()
const stripeSubscription = await stripe.subscriptions.retrieve(subData.stripeSubscriptionId)
if (stripeSubscription.status !== 'active') {
return badRequestResponse('Stripe subscription is not active')
}
const subscriptionItem = stripeSubscription.items.data[0]
if (!subscriptionItem) {
return internalErrorResponse('No subscription item found in Stripe subscription')
}
const currentSeats = subData.seats || 1
logger.info('Admin API: Updating Stripe subscription quantity', {
organizationId,
stripeSubscriptionId: subData.stripeSubscriptionId,
subscriptionItemId: subscriptionItem.id,
currentSeats,
newSeatCount,
})
await stripe.subscriptions.update(subData.stripeSubscriptionId, {
items: [
{
id: subscriptionItem.id,
quantity: newSeatCount,
},
],
proration_behavior: 'create_prorations',
})
stripeUpdated = true
}
await db
.update(subscription)
.set({ seats: newSeatCount })
.where(eq(subscription.id, subData.id))
logger.info(`Admin API: Updated team seats for organization ${organizationId}`, {
seats: newSeatCount,
stripeUpdated,
})
} else {
await db
.update(subscription)
.set({ seats: newSeatCount })
.where(eq(subscription.id, subData.id))
logger.info(`Admin API: Updated seats for organization ${organizationId}`, {
seats: newSeatCount,
plan: subData.plan,
})
}
return singleResponse({
success: true,
seats: newSeatCount,
plan: subData.plan,
stripeUpdated,
})
} catch (error) {
logger.error('Admin API: Failed to update organization seats', { error, organizationId })
return internalErrorResponse('Failed to update organization seats')
}
})

View File

@@ -5,28 +5,28 @@
*
* Response: AdminSingleResponse<AdminSubscription>
*
* PATCH /api/v1/admin/subscriptions/[id]
* DELETE /api/v1/admin/subscriptions/[id]
*
* Update subscription details with optional side effects.
* Cancel a subscription by triggering Stripe cancellation.
* The Stripe webhook handles all cleanup (same as platform cancellation):
* - Updates subscription status to canceled
* - Bills final period overages
* - Resets usage
* - Restores member Pro subscriptions (for team/enterprise)
* - Deletes organization (for team/enterprise)
* - Syncs usage limits to free tier
*
* Body:
* - plan?: string - New plan (free, pro, team, enterprise)
* - status?: string - New status (active, canceled, etc.)
* - seats?: number - Seat count (for team plans)
* - metadata?: object - Subscription metadata (for enterprise)
* - periodStart?: string - Period start (ISO date)
* - periodEnd?: string - Period end (ISO date)
* - cancelAtPeriodEnd?: boolean - Cancel at period end flag
* - syncLimits?: boolean - Sync usage limits for affected users (default: false)
* - reason?: string - Reason for the change (for audit logging)
* Query Parameters:
* - atPeriodEnd?: boolean - Schedule cancellation at period end instead of immediate (default: false)
* - reason?: string - Reason for cancellation (for audit logging)
*
* Response: AdminSingleResponse<AdminSubscription & { sideEffects }>
* Response: { success: true, message: string, subscriptionId: string, atPeriodEnd: boolean }
*/
import { db } from '@sim/db'
import { member, subscription } from '@sim/db/schema'
import { subscription } from '@sim/db/schema'
import { eq } from 'drizzle-orm'
import { syncUsageLimitsFromSubscription } from '@/lib/billing/core/usage'
import { requireStripeClient } from '@/lib/billing/stripe-client'
import { createLogger } from '@/lib/logs/console/logger'
import { withAdminAuthParams } from '@/app/api/v1/admin/middleware'
import {
@@ -43,9 +43,6 @@ interface RouteParams {
id: string
}
const VALID_PLANS = ['free', 'pro', 'team', 'enterprise']
const VALID_STATUSES = ['active', 'canceled', 'past_due', 'unpaid', 'trialing', 'incomplete']
export const GET = withAdminAuthParams<RouteParams>(async (_, context) => {
const { id: subscriptionId } = await context.params
@@ -69,14 +66,13 @@ export const GET = withAdminAuthParams<RouteParams>(async (_, context) => {
}
})
export const PATCH = withAdminAuthParams<RouteParams>(async (request, context) => {
export const DELETE = withAdminAuthParams<RouteParams>(async (request, context) => {
const { id: subscriptionId } = await context.params
const url = new URL(request.url)
const atPeriodEnd = url.searchParams.get('atPeriodEnd') === 'true'
const reason = url.searchParams.get('reason') || 'Admin cancellation (no reason provided)'
try {
const body = await request.json()
const syncLimits = body.syncLimits === true
const reason = body.reason || 'Admin update (no reason provided)'
const [existing] = await db
.select()
.from(subscription)
@@ -87,150 +83,70 @@ export const PATCH = withAdminAuthParams<RouteParams>(async (request, context) =
return notFoundResponse('Subscription')
}
const updateData: Record<string, unknown> = {}
const warnings: string[] = []
if (body.plan !== undefined) {
if (!VALID_PLANS.includes(body.plan)) {
return badRequestResponse(`plan must be one of: ${VALID_PLANS.join(', ')}`)
}
if (body.plan !== existing.plan) {
warnings.push(
`Plan change from ${existing.plan} to ${body.plan}. This does NOT update Stripe - manual sync required.`
)
}
updateData.plan = body.plan
if (existing.status === 'canceled') {
return badRequestResponse('Subscription is already canceled')
}
if (body.status !== undefined) {
if (!VALID_STATUSES.includes(body.status)) {
return badRequestResponse(`status must be one of: ${VALID_STATUSES.join(', ')}`)
}
if (body.status !== existing.status) {
warnings.push(
`Status change from ${existing.status} to ${body.status}. This does NOT update Stripe - manual sync required.`
)
}
updateData.status = body.status
if (!existing.stripeSubscriptionId) {
return badRequestResponse('Subscription has no Stripe subscription ID')
}
if (body.seats !== undefined) {
if (typeof body.seats !== 'number' || body.seats < 1 || !Number.isInteger(body.seats)) {
return badRequestResponse('seats must be a positive integer')
}
updateData.seats = body.seats
const stripe = requireStripeClient()
if (atPeriodEnd) {
// Schedule cancellation at period end
await stripe.subscriptions.update(existing.stripeSubscriptionId, {
cancel_at_period_end: true,
})
// Update DB (webhooks don't sync cancelAtPeriodEnd)
await db
.update(subscription)
.set({ cancelAtPeriodEnd: true })
.where(eq(subscription.id, subscriptionId))
logger.info('Admin API: Scheduled subscription cancellation at period end', {
subscriptionId,
stripeSubscriptionId: existing.stripeSubscriptionId,
plan: existing.plan,
referenceId: existing.referenceId,
periodEnd: existing.periodEnd,
reason,
})
return singleResponse({
success: true,
message: 'Subscription scheduled to cancel at period end.',
subscriptionId,
stripeSubscriptionId: existing.stripeSubscriptionId,
atPeriodEnd: true,
periodEnd: existing.periodEnd?.toISOString() ?? null,
})
}
if (body.metadata !== undefined) {
if (typeof body.metadata !== 'object' || body.metadata === null) {
return badRequestResponse('metadata must be an object')
}
updateData.metadata = {
...((existing.metadata as Record<string, unknown>) || {}),
...body.metadata,
}
}
// Immediate cancellation
await stripe.subscriptions.cancel(existing.stripeSubscriptionId, {
prorate: true,
invoice_now: true,
})
if (body.periodStart !== undefined) {
const date = new Date(body.periodStart)
if (Number.isNaN(date.getTime())) {
return badRequestResponse('periodStart must be a valid ISO date')
}
updateData.periodStart = date
}
if (body.periodEnd !== undefined) {
const date = new Date(body.periodEnd)
if (Number.isNaN(date.getTime())) {
return badRequestResponse('periodEnd must be a valid ISO date')
}
updateData.periodEnd = date
}
if (body.cancelAtPeriodEnd !== undefined) {
if (typeof body.cancelAtPeriodEnd !== 'boolean') {
return badRequestResponse('cancelAtPeriodEnd must be a boolean')
}
updateData.cancelAtPeriodEnd = body.cancelAtPeriodEnd
}
if (Object.keys(updateData).length === 0) {
return badRequestResponse('No valid fields to update')
}
const [updated] = await db
.update(subscription)
.set(updateData)
.where(eq(subscription.id, subscriptionId))
.returning()
const sideEffects: {
limitsSynced: boolean
usersAffected: string[]
errors: string[]
} = {
limitsSynced: false,
usersAffected: [],
errors: [],
}
if (syncLimits) {
try {
const referenceId = updated.referenceId
if (['free', 'pro'].includes(updated.plan)) {
await syncUsageLimitsFromSubscription(referenceId)
sideEffects.usersAffected.push(referenceId)
sideEffects.limitsSynced = true
} else if (['team', 'enterprise'].includes(updated.plan)) {
const members = await db
.select({ userId: member.userId })
.from(member)
.where(eq(member.organizationId, referenceId))
for (const m of members) {
try {
await syncUsageLimitsFromSubscription(m.userId)
sideEffects.usersAffected.push(m.userId)
} catch (memberError) {
sideEffects.errors.push(`Failed to sync limits for user ${m.userId}`)
logger.error('Admin API: Failed to sync limits for member', {
userId: m.userId,
error: memberError,
})
}
}
sideEffects.limitsSynced = members.length > 0
}
logger.info('Admin API: Synced usage limits after subscription update', {
subscriptionId,
usersAffected: sideEffects.usersAffected.length,
})
} catch (syncError) {
sideEffects.errors.push('Failed to sync usage limits')
logger.error('Admin API: Failed to sync usage limits', {
subscriptionId,
error: syncError,
})
}
}
logger.info(`Admin API: Updated subscription ${subscriptionId}`, {
fields: Object.keys(updateData),
previousPlan: existing.plan,
previousStatus: existing.status,
syncLimits,
logger.info('Admin API: Triggered immediate subscription cancellation on Stripe', {
subscriptionId,
stripeSubscriptionId: existing.stripeSubscriptionId,
plan: existing.plan,
referenceId: existing.referenceId,
reason,
})
return singleResponse({
...toAdminSubscription(updated),
sideEffects,
warnings,
success: true,
message: 'Subscription cancellation triggered. Webhook will complete cleanup.',
subscriptionId,
stripeSubscriptionId: existing.stripeSubscriptionId,
atPeriodEnd: false,
})
} catch (error) {
logger.error('Admin API: Failed to update subscription', { error, subscriptionId })
return internalErrorResponse('Failed to update subscription')
logger.error('Admin API: Failed to cancel subscription', { error, subscriptionId })
return internalErrorResponse('Failed to cancel subscription')
}
})

View File

@@ -1,5 +1,7 @@
import type { NextRequest } from 'next/server'
import { authenticateApiKeyFromHeader, updateApiKeyLastUsed } from '@/lib/api-key/service'
import { ANONYMOUS_USER_ID } from '@/lib/auth/constants'
import { isAuthDisabled } from '@/lib/core/config/feature-flags'
import { createLogger } from '@/lib/logs/console/logger'
const logger = createLogger('V1Auth')
@@ -13,6 +15,14 @@ export interface AuthResult {
}
export async function authenticateV1Request(request: NextRequest): Promise<AuthResult> {
if (isAuthDisabled) {
return {
authenticated: true,
userId: ANONYMOUS_USER_ID,
keyType: 'personal',
}
}
const apiKey = request.headers.get('x-api-key')
if (!apiKey) {

View File

@@ -5,7 +5,7 @@ import { type NextRequest, NextResponse } from 'next/server'
import OpenAI, { AzureOpenAI } from 'openai'
import { checkAndBillOverageThreshold } from '@/lib/billing/threshold-billing'
import { env } from '@/lib/core/config/env'
import { getCostMultiplier, isBillingEnabled } from '@/lib/core/config/environment'
import { getCostMultiplier, isBillingEnabled } from '@/lib/core/config/feature-flags'
import { generateRequestId } from '@/lib/core/utils/request'
import { createLogger } from '@/lib/logs/console/logger'
import { getModelPricing } from '@/providers/utils'

View File

@@ -3,7 +3,7 @@ import { type NextRequest, NextResponse } from 'next/server'
import { validate as uuidValidate, v4 as uuidv4 } from 'uuid'
import { z } from 'zod'
import { checkHybridAuth } from '@/lib/auth/hybrid'
import { env, isTruthy } from '@/lib/core/config/env'
import { isTriggerDevEnabled } from '@/lib/core/config/feature-flags'
import { generateRequestId } from '@/lib/core/utils/request'
import { SSE_HEADERS } from '@/lib/core/utils/sse'
import { getBaseUrl } from '@/lib/core/utils/urls'
@@ -236,9 +236,8 @@ type AsyncExecutionParams = {
*/
async function handleAsyncExecution(params: AsyncExecutionParams): Promise<NextResponse> {
const { requestId, workflowId, userId, input, triggerType } = params
const useTrigger = isTruthy(env.TRIGGER_DEV_ENABLED)
if (!useTrigger) {
if (!isTriggerDevEnabled) {
logger.warn(`[${requestId}] Async mode requested but TRIGGER_DEV_ENABLED is false`)
return NextResponse.json(
{ error: 'Async execution is not enabled. Set TRIGGER_DEV_ENABLED=true to use async mode.' },

View File

@@ -39,6 +39,7 @@ interface ChatConfig {
interface AudioStreamingOptions {
voiceId: string
chatId?: string
onError: (error: Error) => void
}
@@ -62,16 +63,19 @@ function fileToBase64(file: File): Promise<string> {
* Creates an audio stream handler for text-to-speech conversion
* @param streamTextToAudio - Function to stream text to audio
* @param voiceId - The voice ID to use for TTS
* @param chatId - Optional chat ID for deployed chat authentication
* @returns Audio stream handler function or undefined
*/
function createAudioStreamHandler(
streamTextToAudio: (text: string, options: AudioStreamingOptions) => Promise<void>,
voiceId: string
voiceId: string,
chatId?: string
) {
return async (text: string) => {
try {
await streamTextToAudio(text, {
voiceId,
chatId,
onError: (error: Error) => {
logger.error('Audio streaming error:', error)
},
@@ -113,7 +117,7 @@ export default function ChatClient({ identifier }: { identifier: string }) {
const [error, setError] = useState<string | null>(null)
const messagesEndRef = useRef<HTMLDivElement>(null)
const messagesContainerRef = useRef<HTMLDivElement>(null)
const [starCount, setStarCount] = useState('3.4k')
const [starCount, setStarCount] = useState('19.4k')
const [conversationId, setConversationId] = useState('')
const [showScrollButton, setShowScrollButton] = useState(false)
@@ -391,7 +395,11 @@ export default function ChatClient({ identifier }: { identifier: string }) {
// Use the streaming hook with audio support
const shouldPlayAudio = isVoiceInput || isVoiceFirstMode
const audioHandler = shouldPlayAudio
? createAudioStreamHandler(streamTextToAudio, DEFAULT_VOICE_SETTINGS.voiceId)
? createAudioStreamHandler(
streamTextToAudio,
DEFAULT_VOICE_SETTINGS.voiceId,
chatConfig?.id
)
: undefined
logger.info('Starting to handle streamed response:', { shouldPlayAudio })

View File

@@ -68,7 +68,6 @@ export function VoiceInterface({
messages = [],
className,
}: VoiceInterfaceProps) {
// Simple state machine
const [state, setState] = useState<'idle' | 'listening' | 'agent_speaking'>('idle')
const [isInitialized, setIsInitialized] = useState(false)
const [isMuted, setIsMuted] = useState(false)
@@ -76,12 +75,10 @@ export function VoiceInterface({
const [permissionStatus, setPermissionStatus] = useState<'prompt' | 'granted' | 'denied'>(
'prompt'
)
// Current turn transcript (subtitle)
const [currentTranscript, setCurrentTranscript] = useState('')
// State tracking
const currentStateRef = useRef<'idle' | 'listening' | 'agent_speaking'>('idle')
const isCallEndedRef = useRef(false)
useEffect(() => {
currentStateRef.current = state
@@ -98,12 +95,10 @@ export function VoiceInterface({
const isSupported =
typeof window !== 'undefined' && !!(window.SpeechRecognition || window.webkitSpeechRecognition)
// Update muted ref
useEffect(() => {
isMutedRef.current = isMuted
}, [isMuted])
// Timeout to handle cases where agent doesn't provide audio response
const setResponseTimeout = useCallback(() => {
if (responseTimeoutRef.current) {
clearTimeout(responseTimeoutRef.current)
@@ -113,7 +108,7 @@ export function VoiceInterface({
if (currentStateRef.current === 'listening') {
setState('idle')
}
}, 5000) // 5 second timeout (increased from 3)
}, 5000)
}, [])
const clearResponseTimeout = useCallback(() => {
@@ -123,14 +118,12 @@ export function VoiceInterface({
}
}, [])
// Sync with external state
useEffect(() => {
if (isPlayingAudio && state !== 'agent_speaking') {
clearResponseTimeout() // Clear timeout since agent is responding
clearResponseTimeout()
setState('agent_speaking')
setCurrentTranscript('')
// Mute microphone immediately
setIsMuted(true)
if (mediaStreamRef.current) {
mediaStreamRef.current.getAudioTracks().forEach((track) => {
@@ -138,7 +131,6 @@ export function VoiceInterface({
})
}
// Stop speech recognition completely
if (recognitionRef.current) {
try {
recognitionRef.current.abort()
@@ -150,7 +142,6 @@ export function VoiceInterface({
setState('idle')
setCurrentTranscript('')
// Re-enable microphone
setIsMuted(false)
if (mediaStreamRef.current) {
mediaStreamRef.current.getAudioTracks().forEach((track) => {
@@ -160,7 +151,6 @@ export function VoiceInterface({
}
}, [isPlayingAudio, state, clearResponseTimeout])
// Audio setup
const setupAudio = useCallback(async () => {
try {
const stream = await navigator.mediaDevices.getUserMedia({
@@ -175,7 +165,6 @@ export function VoiceInterface({
setPermissionStatus('granted')
mediaStreamRef.current = stream
// Setup audio context for visualization
if (!audioContextRef.current) {
const AudioContext = window.AudioContext || window.webkitAudioContext
audioContextRef.current = new AudioContext()
@@ -194,7 +183,6 @@ export function VoiceInterface({
source.connect(analyser)
analyserRef.current = analyser
// Start visualization
const updateVisualization = () => {
if (!analyserRef.current) return
@@ -223,7 +211,6 @@ export function VoiceInterface({
}
}, [])
// Speech recognition setup
const setupSpeechRecognition = useCallback(() => {
if (!isSupported) return
@@ -259,14 +246,11 @@ export function VoiceInterface({
}
}
// Update live transcript
setCurrentTranscript(interimTranscript || finalTranscript)
// Send final transcript (but keep listening state until agent responds)
if (finalTranscript.trim()) {
setCurrentTranscript('') // Clear transcript
setCurrentTranscript('')
// Stop recognition to avoid interference while waiting for response
if (recognitionRef.current) {
try {
recognitionRef.current.stop()
@@ -275,7 +259,6 @@ export function VoiceInterface({
}
}
// Start timeout in case agent doesn't provide audio response
setResponseTimeout()
onVoiceTranscript?.(finalTranscript)
@@ -283,13 +266,14 @@ export function VoiceInterface({
}
recognition.onend = () => {
if (isCallEndedRef.current) return
const currentState = currentStateRef.current
// Only restart recognition if we're in listening state and not muted
if (currentState === 'listening' && !isMutedRef.current) {
// Add a delay to avoid immediate restart after sending transcript
setTimeout(() => {
// Double-check state hasn't changed during delay
if (isCallEndedRef.current) return
if (
recognitionRef.current &&
currentStateRef.current === 'listening' &&
@@ -301,14 +285,12 @@ export function VoiceInterface({
logger.debug('Error restarting speech recognition:', error)
}
}
}, 1000) // Longer delay to give agent time to respond
}, 1000)
}
}
recognition.onerror = (event: SpeechRecognitionErrorEvent) => {
// Filter out "aborted" errors - these are expected when we intentionally stop recognition
if (event.error === 'aborted') {
// Ignore
return
}
@@ -320,7 +302,6 @@ export function VoiceInterface({
recognitionRef.current = recognition
}, [isSupported, onVoiceTranscript, setResponseTimeout])
// Start/stop listening
const startListening = useCallback(() => {
if (!isInitialized || isMuted || state !== 'idle') {
return
@@ -351,17 +332,12 @@ export function VoiceInterface({
}
}, [])
// Handle interrupt
const handleInterrupt = useCallback(() => {
if (state === 'agent_speaking') {
// Clear any subtitle timeouts and text
// (No longer needed after removing subtitle system)
onInterrupt?.()
setState('listening')
setCurrentTranscript('')
// Unmute microphone for user input
setIsMuted(false)
if (mediaStreamRef.current) {
mediaStreamRef.current.getAudioTracks().forEach((track) => {
@@ -369,7 +345,6 @@ export function VoiceInterface({
})
}
// Start listening immediately
if (recognitionRef.current) {
try {
recognitionRef.current.start()
@@ -380,14 +355,13 @@ export function VoiceInterface({
}
}, [state, onInterrupt])
// Handle call end with proper cleanup
const handleCallEnd = useCallback(() => {
// Stop everything immediately
isCallEndedRef.current = true
setState('idle')
setCurrentTranscript('')
setIsMuted(false)
// Stop speech recognition
if (recognitionRef.current) {
try {
recognitionRef.current.abort()
@@ -396,17 +370,11 @@ export function VoiceInterface({
}
}
// Clear timeouts
clearResponseTimeout()
// Stop audio playback and streaming immediately
onInterrupt?.()
// Call the original onCallEnd
onCallEnd?.()
}, [onCallEnd, onInterrupt, clearResponseTimeout])
// Keyboard handler
useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
if (event.code === 'Space') {
@@ -419,7 +387,6 @@ export function VoiceInterface({
return () => document.removeEventListener('keydown', handleKeyDown)
}, [handleInterrupt])
// Mute toggle
const toggleMute = useCallback(() => {
if (state === 'agent_speaking') {
handleInterrupt()
@@ -442,7 +409,6 @@ export function VoiceInterface({
}
}, [isMuted, state, handleInterrupt, stopListening, startListening])
// Initialize
useEffect(() => {
if (isSupported) {
setupSpeechRecognition()
@@ -450,47 +416,40 @@ export function VoiceInterface({
}
}, [isSupported, setupSpeechRecognition, setupAudio])
// Auto-start listening when ready
useEffect(() => {
if (isInitialized && !isMuted && state === 'idle') {
startListening()
}
}, [isInitialized, isMuted, state, startListening])
// Cleanup when call ends or component unmounts
useEffect(() => {
return () => {
// Stop speech recognition
isCallEndedRef.current = true
if (recognitionRef.current) {
try {
recognitionRef.current.abort()
} catch (error) {
} catch (_e) {
// Ignore
}
recognitionRef.current = null
}
// Stop media stream
if (mediaStreamRef.current) {
mediaStreamRef.current.getTracks().forEach((track) => {
track.stop()
})
mediaStreamRef.current.getTracks().forEach((track) => track.stop())
mediaStreamRef.current = null
}
// Stop audio context
if (audioContextRef.current) {
audioContextRef.current.close()
audioContextRef.current = null
}
// Cancel animation frame
if (animationFrameRef.current) {
cancelAnimationFrame(animationFrameRef.current)
animationFrameRef.current = null
}
// Clear timeouts
if (responseTimeoutRef.current) {
clearTimeout(responseTimeoutRef.current)
responseTimeoutRef.current = null
@@ -498,7 +457,6 @@ export function VoiceInterface({
}
}, [])
// Get status text
const getStatusText = () => {
switch (state) {
case 'listening':
@@ -510,7 +468,6 @@ export function VoiceInterface({
}
}
// Get button content
const getButtonContent = () => {
if (state === 'agent_speaking') {
return (
@@ -524,9 +481,7 @@ export function VoiceInterface({
return (
<div className={cn('fixed inset-0 z-[100] flex flex-col bg-white text-gray-900', className)}>
{/* Main content */}
<div className='flex flex-1 flex-col items-center justify-center px-8'>
{/* Voice visualization */}
<div className='relative mb-16'>
<ParticlesVisualization
audioLevels={audioLevels}
@@ -538,7 +493,6 @@ export function VoiceInterface({
/>
</div>
{/* Live transcript - subtitle style */}
<div className='mb-16 flex h-24 items-center justify-center'>
{currentTranscript && (
<div className='max-w-2xl px-8'>
@@ -549,17 +503,14 @@ export function VoiceInterface({
)}
</div>
{/* Status */}
<p className='mb-8 text-center text-gray-600 text-lg'>
{getStatusText()}
{isMuted && <span className='ml-2 text-gray-400 text-sm'>(Muted)</span>}
</p>
</div>
{/* Controls */}
<div className='px-8 pb-12'>
<div className='flex items-center justify-center space-x-12'>
{/* End call */}
<Button
onClick={handleCallEnd}
variant='outline'
@@ -569,7 +520,6 @@ export function VoiceInterface({
<Phone className='h-6 w-6 rotate-[135deg]' />
</Button>
{/* Mic/Stop button */}
<Button
onClick={toggleMute}
variant='outline'

View File

@@ -14,6 +14,7 @@ declare global {
interface AudioStreamingOptions {
voiceId: string
modelId?: string
chatId?: string
onAudioStart?: () => void
onAudioEnd?: () => void
onError?: (error: Error) => void
@@ -76,7 +77,14 @@ export function useAudioStreaming(sharedAudioContextRef?: RefObject<AudioContext
}
const { text, options } = item
const { voiceId, modelId = 'eleven_turbo_v2_5', onAudioStart, onAudioEnd, onError } = options
const {
voiceId,
modelId = 'eleven_turbo_v2_5',
chatId,
onAudioStart,
onAudioEnd,
onError,
} = options
try {
const audioContext = getAudioContext()
@@ -93,6 +101,7 @@ export function useAudioStreaming(sharedAudioContextRef?: RefObject<AudioContext
text,
voiceId,
modelId,
chatId,
}),
signal: abortControllerRef.current?.signal,
})

View File

@@ -47,12 +47,16 @@ export function FileSelectorInput({
const [projectIdValueFromStore] = useSubBlockValue(blockId, 'projectId')
const [planIdValueFromStore] = useSubBlockValue(blockId, 'planId')
const [teamIdValueFromStore] = useSubBlockValue(blockId, 'teamId')
const [siteIdValueFromStore] = useSubBlockValue(blockId, 'siteId')
const [collectionIdValueFromStore] = useSubBlockValue(blockId, 'collectionId')
const connectedCredential = previewContextValues?.credential ?? connectedCredentialFromStore
const domainValue = previewContextValues?.domain ?? domainValueFromStore
const projectIdValue = previewContextValues?.projectId ?? projectIdValueFromStore
const planIdValue = previewContextValues?.planId ?? planIdValueFromStore
const teamIdValue = previewContextValues?.teamId ?? teamIdValueFromStore
const siteIdValue = previewContextValues?.siteId ?? siteIdValueFromStore
const collectionIdValue = previewContextValues?.collectionId ?? collectionIdValueFromStore
const normalizedCredentialId =
typeof connectedCredential === 'string'
@@ -75,6 +79,8 @@ export function FileSelectorInput({
projectId: (projectIdValue as string) || undefined,
planId: (planIdValue as string) || undefined,
teamId: (teamIdValue as string) || undefined,
siteId: (siteIdValue as string) || undefined,
collectionId: (collectionIdValue as string) || undefined,
})
}, [
subBlock,
@@ -84,6 +90,8 @@ export function FileSelectorInput({
projectIdValue,
planIdValue,
teamIdValue,
siteIdValue,
collectionIdValue,
])
const missingCredential = !normalizedCredentialId
@@ -97,6 +105,10 @@ export function FileSelectorInput({
!selectorResolution.context.projectId
const missingPlan =
selectorResolution?.key === 'microsoft.planner' && !selectorResolution.context.planId
const missingSite =
selectorResolution?.key === 'webflow.collections' && !selectorResolution.context.siteId
const missingCollection =
selectorResolution?.key === 'webflow.items' && !selectorResolution.context.collectionId
const disabledReason =
finalDisabled ||
@@ -105,6 +117,8 @@ export function FileSelectorInput({
missingDomain ||
missingProject ||
missingPlan ||
missingSite ||
missingCollection ||
!selectorResolution?.key
if (!selectorResolution?.key) {

View File

@@ -43,14 +43,12 @@ export function ProjectSelectorInput({
// Use previewContextValues if provided (for tools inside agent blocks), otherwise use store values
const connectedCredential = previewContextValues?.credential ?? connectedCredentialFromStore
const linearCredential = previewContextValues?.credential ?? connectedCredentialFromStore
const linearTeamId = previewContextValues?.teamId ?? linearTeamIdFromStore
const jiraDomain = previewContextValues?.domain ?? jiraDomainFromStore
// Derive provider from serviceId using OAuth config
const serviceId = subBlock.serviceId || ''
const effectiveProviderId = useMemo(() => getProviderIdFromServiceId(serviceId), [serviceId])
const isLinear = serviceId === 'linear'
const { isForeignCredential } = useForeignCredential(
effectiveProviderId,
@@ -65,7 +63,6 @@ export function ProjectSelectorInput({
})
// Jira/Discord upstream fields - use values from previewContextValues or store
const jiraCredential = connectedCredential
const domain = (jiraDomain as string) || ''
// Verify Jira credential belongs to current user; if not, treat as absent
@@ -84,19 +81,11 @@ export function ProjectSelectorInput({
const selectorResolution = useMemo(() => {
return resolveSelectorForSubBlock(subBlock, {
workflowId: workflowIdFromUrl || undefined,
credentialId: (isLinear ? linearCredential : jiraCredential) as string | undefined,
credentialId: (connectedCredential as string) || undefined,
domain,
teamId: (linearTeamId as string) || undefined,
})
}, [
subBlock,
workflowIdFromUrl,
isLinear,
linearCredential,
jiraCredential,
domain,
linearTeamId,
])
}, [subBlock, workflowIdFromUrl, connectedCredential, domain, linearTeamId])
const missingCredential = !selectorResolution?.context.credentialId

View File

@@ -14,7 +14,10 @@ import {
usePopoverContext,
} from '@/components/emcn'
import { cn } from '@/lib/core/utils/cn'
import { extractFieldsFromSchema, findActiveOutputSchema } from '@/lib/core/utils/response-format'
import {
extractFieldsFromSchema,
parseResponseFormatSafely,
} from '@/lib/core/utils/response-format'
import { createLogger } from '@/lib/logs/console/logger'
import { getBlockOutputPaths, getBlockOutputType } from '@/lib/workflows/blocks/block-outputs'
import { TRIGGER_TYPES } from '@/lib/workflows/triggers/triggers'
@@ -542,9 +545,8 @@ export const TagDropdown: React.FC<TagDropdownProps> = ({
const normalizedBlockName = normalizeBlockName(blockName)
const mergedSubBlocks = getMergedSubBlocks(activeSourceBlockId)
// Use generalized schema detection to find active output schema
const activeOutputSchema = findActiveOutputSchema(blockConfig, mergedSubBlocks)
const responseFormatValue = mergedSubBlocks?.responseFormat?.value
const responseFormat = parseResponseFormatSafely(responseFormatValue, activeSourceBlockId)
let blockTags: string[]
@@ -574,8 +576,8 @@ export const TagDropdown: React.FC<TagDropdownProps> = ({
} else {
blockTags = [normalizedBlockName]
}
} else if (activeOutputSchema) {
const schemaFields = extractFieldsFromSchema(activeOutputSchema.schema)
} else if (responseFormat) {
const schemaFields = extractFieldsFromSchema(responseFormat)
if (schemaFields.length > 0) {
blockTags = schemaFields.map((field) => `${normalizedBlockName}.${field.name}`)
} else {
@@ -881,8 +883,8 @@ export const TagDropdown: React.FC<TagDropdownProps> = ({
const normalizedBlockName = normalizeBlockName(blockName)
const mergedSubBlocks = getMergedSubBlocks(accessibleBlockId)
const activeOutputSchema = findActiveOutputSchema(blockConfig, mergedSubBlocks)
const responseFormatValue = mergedSubBlocks?.responseFormat?.value
const responseFormat = parseResponseFormatSafely(responseFormatValue, accessibleBlockId)
let blockTags: string[]
@@ -933,8 +935,8 @@ export const TagDropdown: React.FC<TagDropdownProps> = ({
} else {
blockTags = [normalizedBlockName]
}
} else if (activeOutputSchema) {
const schemaFields = extractFieldsFromSchema(activeOutputSchema.schema)
} else if (responseFormat) {
const schemaFields = extractFieldsFromSchema(responseFormat)
if (schemaFields.length > 0) {
blockTags = schemaFields.map((field) => `${normalizedBlockName}.${field.name}`)
} else {

View File

@@ -47,12 +47,16 @@ export function FileSelectorInput({
const [projectIdValueFromStore] = useSubBlockValue(blockId, 'projectId')
const [planIdValueFromStore] = useSubBlockValue(blockId, 'planId')
const [teamIdValueFromStore] = useSubBlockValue(blockId, 'teamId')
const [siteIdValueFromStore] = useSubBlockValue(blockId, 'siteId')
const [collectionIdValueFromStore] = useSubBlockValue(blockId, 'collectionId')
const connectedCredential = previewContextValues?.credential ?? connectedCredentialFromStore
const domainValue = previewContextValues?.domain ?? domainValueFromStore
const projectIdValue = previewContextValues?.projectId ?? projectIdValueFromStore
const planIdValue = previewContextValues?.planId ?? planIdValueFromStore
const teamIdValue = previewContextValues?.teamId ?? teamIdValueFromStore
const siteIdValue = previewContextValues?.siteId ?? siteIdValueFromStore
const collectionIdValue = previewContextValues?.collectionId ?? collectionIdValueFromStore
const normalizedCredentialId =
typeof connectedCredential === 'string'
@@ -75,6 +79,8 @@ export function FileSelectorInput({
projectId: (projectIdValue as string) || undefined,
planId: (planIdValue as string) || undefined,
teamId: (teamIdValue as string) || undefined,
siteId: (siteIdValue as string) || undefined,
collectionId: (collectionIdValue as string) || undefined,
})
}, [
subBlock,
@@ -84,6 +90,8 @@ export function FileSelectorInput({
projectIdValue,
planIdValue,
teamIdValue,
siteIdValue,
collectionIdValue,
])
const missingCredential = !normalizedCredentialId
@@ -97,6 +105,10 @@ export function FileSelectorInput({
!selectorResolution?.context.projectId
const missingPlan =
selectorResolution?.key === 'microsoft.planner' && !selectorResolution?.context.planId
const missingSite =
selectorResolution?.key === 'webflow.collections' && !selectorResolution?.context.siteId
const missingCollection =
selectorResolution?.key === 'webflow.items' && !selectorResolution?.context.collectionId
const disabledReason =
finalDisabled ||
@@ -105,6 +117,8 @@ export function FileSelectorInput({
missingDomain ||
missingProject ||
missingPlan ||
missingSite ||
missingCollection ||
!selectorResolution?.key
if (!selectorResolution?.key) {

View File

@@ -579,8 +579,10 @@ const WorkflowContent = React.memo(() => {
const node = nodeIndex.get(id)
if (!node) return false
// If dropping outside containers, ignore blocks that are inside a container
if (!containerAtPoint && blocks[id]?.data?.parentId) return false
const blockParentId = blocks[id]?.data?.parentId
const dropParentId = containerAtPoint?.loopId
if (dropParentId !== blockParentId) return false
return true
})
.map(([id, block]) => {

View File

@@ -16,6 +16,7 @@ import {
} from '@/components/emcn'
import { Input, Skeleton } from '@/components/ui'
import { signOut, useSession } from '@/lib/auth/auth-client'
import { ANONYMOUS_USER_ID } from '@/lib/auth/constants'
import { useBrandConfig } from '@/lib/branding/branding'
import { getEnv, isTruthy } from '@/lib/core/config/env'
import { getBaseUrl } from '@/lib/core/utils/urls'
@@ -59,6 +60,7 @@ export function General({ onOpenChange }: GeneralProps) {
const isLoading = isProfileLoading || isSettingsLoading
const isTrainingEnabled = isTruthy(getEnv('NEXT_PUBLIC_COPILOT_TRAINING_ENABLED'))
const isAuthDisabled = session?.user?.id === ANONYMOUS_USER_ID
const [isSuperUser, setIsSuperUser] = useState(false)
const [loadingSuperUser, setLoadingSuperUser] = useState(true)
@@ -461,10 +463,12 @@ export function General({ onOpenChange }: GeneralProps) {
</div>
)}
<div className='mt-auto flex items-center gap-[8px]'>
<Button onClick={handleSignOut}>Sign out</Button>
<Button onClick={() => setShowResetPasswordModal(true)}>Reset password</Button>
</div>
{!isAuthDisabled && (
<div className='mt-auto flex items-center gap-[8px]'>
<Button onClick={handleSignOut}>Sign out</Button>
<Button onClick={() => setShowResetPasswordModal(true)}>Reset password</Button>
</div>
)}
{/* Password Reset Confirmation Modal */}
<Modal open={showResetPasswordModal} onOpenChange={setShowResetPasswordModal}>

View File

@@ -6,7 +6,7 @@ import { Button, Combobox, Input, Switch, Textarea } from '@/components/emcn'
import { Skeleton } from '@/components/ui'
import { useSession } from '@/lib/auth/auth-client'
import { getSubscriptionStatus } from '@/lib/billing/client/utils'
import { isBillingEnabled } from '@/lib/core/config/environment'
import { isBillingEnabled } from '@/lib/core/config/feature-flags'
import { cn } from '@/lib/core/utils/cn'
import { getBaseUrl } from '@/lib/core/utils/urls'
import { createLogger } from '@/lib/logs/console/logger'

View File

@@ -26,7 +26,7 @@ import { McpIcon } from '@/components/icons'
import { useSession } from '@/lib/auth/auth-client'
import { getSubscriptionStatus } from '@/lib/billing/client'
import { getEnv, isTruthy } from '@/lib/core/config/env'
import { isHosted } from '@/lib/core/config/environment'
import { isHosted } from '@/lib/core/config/feature-flags'
import { getUserRole } from '@/lib/workspaces/organization'
import {
ApiKeys,

View File

@@ -1,5 +1,5 @@
import { AgentIcon } from '@/components/icons'
import { isHosted } from '@/lib/core/config/environment'
import { isHosted } from '@/lib/core/config/feature-flags'
import { createLogger } from '@/lib/logs/console/logger'
import type { BlockConfig } from '@/blocks/types'
import { AuthMode } from '@/blocks/types'
@@ -290,9 +290,6 @@ The JSON object MUST have the following top-level properties: 'name' (string), '
The 'schema' object must define the structure and MUST contain 'type': 'object', 'properties': {...}, 'additionalProperties': false, and 'required': [...].
Inside 'properties', use standard JSON Schema properties (type, description, enum, items for arrays, etc.).
### VARIABLE RESOLUTION
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax for dynamic schema names or descriptions.
Current schema: {context}
Do not include any explanations, markdown formatting, or other text outside the JSON object.

View File

@@ -87,52 +87,6 @@ export const AirtableBlock: BlockConfig<AirtableResponse> = {
placeholder: 'For Create: `[{ "fields": { ... } }]`\n',
condition: { field: 'operation', value: ['create', 'updateMultiple'] },
required: true,
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert Airtable API developer. Generate Airtable records JSON array for create or bulk update operations.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.name>\`, \`<function1.result.email>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{DEFAULT_STATUS}}\`)
Do NOT wrap variable references in quotes for non-string values.
### CRITICAL INSTRUCTION
Return ONLY a valid JSON array of record objects. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
### RECORD STRUCTURE
For creating records:
- Each record has a **fields** object containing field name/value pairs
- Field names must match exactly with your Airtable base
For updating multiple records:
- Each record needs an **id** field with the record ID
- Plus a **fields** object with fields to update
### EXAMPLES
**Create single record**: "Add a new contact John Doe with email"
→ [{"fields": {"Name": "John Doe", "Email": "john@example.com"}}]
**With variables**: "Create record from previous block data"
→ [{"fields": {"Name": <agent1.name>, "Email": <agent1.email>, "Status": "{{DEFAULT_STATUS}}"}}]
**Bulk update**: "Update status for two records"
→ [
{"id": "recABC123", "fields": {"Status": "Complete"}},
{"id": "recDEF456", "fields": {"Status": "In Progress"}}
]
### REMEMBER
Return ONLY a valid JSON array - no explanations, no markdown, no extra text.`,
placeholder: 'Describe the records you want to create or update...',
generationType: 'json-array',
},
},
{
id: 'fields',
@@ -141,49 +95,6 @@ Return ONLY a valid JSON array - no explanations, no markdown, no extra text.`,
placeholder: 'Fields to update: `{ "Field Name": "New Value" }`',
condition: { field: 'operation', value: 'update' },
required: true,
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert Airtable API developer. Generate Airtable fields JSON object for updating a single record.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.new_status>\`, \`<function1.result.price>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{DEFAULT_CATEGORY}}\`)
Do NOT wrap variable references in quotes for non-string values.
### CRITICAL INSTRUCTION
Return ONLY a valid JSON object with field name/value pairs. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object.
### FIELD VALUES
- **Text fields**: String values
- **Number fields**: Numeric values
- **Checkbox**: true or false
- **Single select**: String matching option name
- **Multi-select**: Array of option names
- **Linked records**: Array of record IDs
- **Date**: ISO date string
### EXAMPLES
**Update text fields**: "Update name and email"
→ {"Name": "Jane Doe", "Email": "jane@example.com"}
**With variables**: "Update fields from previous block"
→ {"Status": <agent1.new_status>, "Updated By": "{{SYSTEM_USER}}"}
**Update status**: "Mark as completed"
→ {"Status": "Completed", "Completed Date": "2024-01-15"}
### REMEMBER
Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
placeholder: 'Describe the fields you want to update...',
generationType: 'json-object',
},
},
...getTrigger('airtable_webhook').subBlocks,
],

View File

@@ -58,35 +58,24 @@ export const ApiBlock: BlockConfig<RequestResponse> = {
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert JSON programmer. Generate ONLY the raw JSON object based on the user's request.
prompt: `You are an expert JSON programmer.
Generate ONLY the raw JSON object based on the user's request.
The output MUST be a single, valid JSON object, starting with { and ending with }.
### CONTEXT
{context}
Current body: {context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.response>\`, \`<function1.result.data>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{API_KEY}}\`, \`{{BASE_URL}}\`)
Do NOT wrap variable references in quotes for non-string values.
### CRITICAL INSTRUCTION
Return ONLY valid JSON. The output MUST be a single, valid JSON object, starting with { and ending with }.
Do not include any explanations, markdown formatting, or other text outside the JSON object.
### EXAMPLES
You have access to the following variables you can use to generate the JSON body:
- 'params' (object): Contains input parameters derived from the JSON schema. Access these directly using the parameter name wrapped in angle brackets, e.g., '<paramName>'. Do NOT use 'params.paramName'.
- 'environmentVariables' (object): Contains environment variables. Reference these using the double curly brace syntax: '{{ENV_VAR_NAME}}'. Do NOT use 'environmentVariables.VAR_NAME' or env.
**Simple request body**: "Send user data"
→ {"name": "John Doe", "email": "john@example.com", "active": true}
**With variables**: "Send data from previous agent"
→ {"name": <agent1.name>, "email": <agent1.email>, "apiKey": "{{API_KEY}}"}
**Nested data**: "Send order with items"
→ {"orderId": <function1.order_id>, "items": <agent1.cart_items>, "total": <function1.total>}
### REMEMBER
Return ONLY valid JSON - no explanations, no markdown.`,
Example:
{
"name": "<block.agent.response.content>",
"age": <block.function.output.age>,
"success": true
}`,
placeholder: 'Describe the API request body you need...',
generationType: 'json-object',
},

View File

@@ -46,62 +46,6 @@ export const ApifyBlock: BlockConfig<RunActorResult> = {
language: 'json',
placeholder: '{\n "startUrl": "https://example.com",\n "maxPages": 10\n}',
required: false,
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert Apify developer. Generate JSON input for Apify actors based on the user's request.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.url>\`, \`<function1.result.query>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{APIFY_PROXY_PASSWORD}}\`)
Do NOT wrap variable references in quotes for non-string values.
### CRITICAL INSTRUCTION
Return ONLY valid JSON input for the Apify actor. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object.
### COMMON APIFY ACTOR INPUT PATTERNS
**Web Scraping Actors**:
- startUrls: Array of URLs to start scraping from
- maxRequestsPerCrawl: Maximum number of pages to scrape
- proxyConfiguration: Proxy settings
**E-commerce Scrapers**:
- searchTerms: Array of search queries
- maxItems: Maximum products to return
- categoryUrls: URLs of categories to scrape
**Social Media Scrapers**:
- handles: Array of usernames/handles
- resultsLimit: Maximum posts/items to return
- startDate/endDate: Date range filters
### EXAMPLES
**Web scraper**: "Scrape product pages from example.com, max 50 pages"
→ {
"startUrls": [{"url": "https://example.com/products"}],
"maxRequestsPerCrawl": 50,
"proxyConfiguration": {"useApifyProxy": true}
}
**With variables**: "Scrape URL from previous block"
→ {
"startUrls": [{"url": <agent1.target_url>}],
"maxRequestsPerCrawl": <function1.max_pages>,
"proxyConfiguration": {"useApifyProxy": true}
}
### REMEMBER
Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
placeholder: 'Describe the input for the Apify actor...',
generationType: 'json-object',
},
},
{
id: 'timeout',

View File

@@ -64,30 +64,6 @@ export const ApolloBlock: BlockConfig<ApolloResponse> = {
type: 'code',
placeholder: '["CEO", "VP of Sales"]',
condition: { field: 'operation', value: 'people_search' },
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `Generate a JSON array of job titles for Apollo people search.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax (e.g., \`<agent1.job_title>\`).
Environment variables use \`{{ENV_VAR_NAME}}\` syntax.
### CRITICAL INSTRUCTION
Return ONLY a valid JSON array. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
### EXAMPLES
["CEO", "VP of Sales"]
["Software Engineer", "Senior Developer"]
[<agent1.target_titles>]
Return ONLY valid JSON array - no explanations.`,
placeholder: 'Describe the job titles to search for...',
generationType: 'json-array',
},
},
{
id: 'person_locations',
@@ -95,30 +71,6 @@ Return ONLY valid JSON array - no explanations.`,
type: 'code',
placeholder: '["San Francisco, CA", "New York, NY"]',
condition: { field: 'operation', value: 'people_search' },
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `Generate a JSON array of locations for Apollo people search. Use "City, State" or "City, Country" format.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax (e.g., \`<agent1.location>\`).
Environment variables use \`{{ENV_VAR_NAME}}\` syntax.
### CRITICAL INSTRUCTION
Return ONLY a valid JSON array. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
### EXAMPLES
["San Francisco, CA", "New York, NY"]
["London, UK", "Paris, France"]
[<agent1.target_location>]
Return ONLY valid JSON array - no explanations.`,
placeholder: 'Describe the locations to search for...',
generationType: 'json-array',
},
},
{
id: 'organization_names',
@@ -126,30 +78,6 @@ Return ONLY valid JSON array - no explanations.`,
type: 'code',
placeholder: '["Company A", "Company B"]',
condition: { field: 'operation', value: 'people_search' },
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `Generate a JSON array of company names for Apollo people search.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax (e.g., \`<agent1.company>\`).
Environment variables use \`{{ENV_VAR_NAME}}\` syntax.
### CRITICAL INSTRUCTION
Return ONLY a valid JSON array. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
### EXAMPLES
["Acme Corp", "Tech Solutions Inc"]
["Google", "Microsoft", "Apple"]
[<agent1.target_companies>]
Return ONLY valid JSON array - no explanations.`,
placeholder: 'Describe the companies to search for...',
generationType: 'json-array',
},
},
{
id: 'person_seniorities',
@@ -157,30 +85,6 @@ Return ONLY valid JSON array - no explanations.`,
type: 'code',
placeholder: '["senior", "manager", "director"]',
condition: { field: 'operation', value: 'people_search' },
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `Generate a JSON array of seniority levels for Apollo people search. Common values: "senior", "manager", "director", "vp", "c_level", "owner", "founder".
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax (e.g., \`<agent1.seniority>\`).
Environment variables use \`{{ENV_VAR_NAME}}\` syntax.
### CRITICAL INSTRUCTION
Return ONLY a valid JSON array. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
### EXAMPLES
["senior", "manager", "director"]
["vp", "c_level"]
["founder", "owner"]
Return ONLY valid JSON array - no explanations.`,
placeholder: 'Describe the seniority levels to search for...',
generationType: 'json-array',
},
},
{
id: 'contact_stage_ids',
@@ -188,28 +92,6 @@ Return ONLY valid JSON array - no explanations.`,
type: 'code',
placeholder: '["stage_id_1", "stage_id_2"]',
condition: { field: 'operation', value: 'contact_search' },
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `Generate a JSON array of Apollo contact stage IDs for filtering contacts by their pipeline stage.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax (e.g., \`<agent1.stage_id>\`).
### CRITICAL INSTRUCTION
Return ONLY a valid JSON array of stage ID strings. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
### EXAMPLES
["stage_id_1", "stage_id_2"]
[<agent1.selected_stage>]
Return ONLY valid JSON array - no explanations.`,
placeholder: 'Enter or describe the contact stage IDs...',
generationType: 'json-array',
},
},
// People Enrich Fields
@@ -298,29 +180,6 @@ Return ONLY valid JSON array - no explanations.`,
placeholder: '[{"first_name": "John", "last_name": "Doe", "email": "john@example.com"}]',
condition: { field: 'operation', value: 'people_bulk_enrich' },
required: true,
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `Generate a JSON array of people objects for Apollo bulk enrich. Each object should have first_name, last_name, and optionally email, organization_name, or domain.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax (e.g., \`<agent1.first_name>\`).
Environment variables use \`{{ENV_VAR_NAME}}\` syntax.
### CRITICAL INSTRUCTION
Return ONLY a valid JSON array. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
### EXAMPLES
[{"first_name": "John", "last_name": "Doe", "email": "john@example.com"}]
[{"first_name": <agent1.first_name>, "last_name": <agent1.last_name>, "email": <agent1.email>}]
Return ONLY valid JSON array - no explanations.`,
placeholder: 'Describe the people you want to enrich...',
generationType: 'json-array',
},
},
{
id: 'organizations',
@@ -329,29 +188,6 @@ Return ONLY valid JSON array - no explanations.`,
placeholder: '[{"organization_name": "Company A", "domain": "companya.com"}]',
condition: { field: 'operation', value: 'organization_bulk_enrich' },
required: true,
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `Generate a JSON array of organization objects for Apollo bulk enrich. Each object should have organization_name and/or domain.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax (e.g., \`<agent1.company_name>\`).
Environment variables use \`{{ENV_VAR_NAME}}\` syntax.
### CRITICAL INSTRUCTION
Return ONLY a valid JSON array. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
### EXAMPLES
[{"organization_name": "Acme Corp", "domain": "acme.com"}]
[{"organization_name": <agent1.company>, "domain": <agent1.domain>}]
Return ONLY valid JSON array - no explanations.`,
placeholder: 'Describe the organizations you want to enrich...',
generationType: 'json-array',
},
},
// Organization Search Fields
@@ -361,30 +197,6 @@ Return ONLY valid JSON array - no explanations.`,
type: 'code',
placeholder: '["San Francisco, CA"]',
condition: { field: 'operation', value: 'organization_search' },
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `Generate a JSON array of location strings for Apollo organization search.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax (e.g., \`<agent1.location>\`).
### CRITICAL INSTRUCTION
Return ONLY a valid JSON array of location strings. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
### EXAMPLES
["San Francisco, CA"]
["New York, NY", "Los Angeles, CA", "Chicago, IL"]
["United States", "Canada"]
[<agent1.target_location>]
Return ONLY valid JSON array - no explanations.`,
placeholder: 'Describe the locations to search for...',
generationType: 'json-array',
},
},
{
id: 'organization_num_employees_ranges',
@@ -392,40 +204,6 @@ Return ONLY valid JSON array - no explanations.`,
type: 'code',
placeholder: '["1-10", "11-50", "51-200"]',
condition: { field: 'operation', value: 'organization_search' },
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `Generate a JSON array of employee count range strings for Apollo organization search.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax (e.g., \`<agent1.company_size>\`).
### CRITICAL INSTRUCTION
Return ONLY a valid JSON array of employee range strings. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
### AVAILABLE RANGES
Apollo uses these standard employee count ranges:
- "1-10" (Micro)
- "11-50" (Small)
- "51-200" (Medium)
- "201-500" (Mid-Market)
- "501-1000" (Large)
- "1001-5000" (Enterprise)
- "5001-10000" (Large Enterprise)
- "10001+" (Global Enterprise)
### EXAMPLES
["1-10", "11-50"]
["51-200", "201-500", "501-1000"]
["1001-5000", "5001-10000", "10001+"]
Return ONLY valid JSON array - no explanations.`,
placeholder: 'Describe the company sizes to search for...',
generationType: 'json-array',
},
},
{
id: 'q_organization_keyword_tags',
@@ -433,30 +211,6 @@ Return ONLY valid JSON array - no explanations.`,
type: 'code',
placeholder: '["saas", "b2b", "enterprise"]',
condition: { field: 'operation', value: 'organization_search' },
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `Generate a JSON array of keyword/industry tag strings for Apollo organization search.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax (e.g., \`<agent1.industry>\`).
### CRITICAL INSTRUCTION
Return ONLY a valid JSON array of keyword strings. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
### EXAMPLES
["saas", "b2b", "enterprise"]
["fintech", "healthcare", "ai"]
["e-commerce", "retail", "marketplace"]
[<agent1.target_industry>]
Return ONLY valid JSON array - no explanations.`,
placeholder: 'Describe the industry keywords to search for...',
generationType: 'json-array',
},
},
{
id: 'q_organization_name',
@@ -533,25 +287,6 @@ Return ONLY valid JSON array - no explanations.`,
'[{"first_name": "John", "last_name": "Doe", "email": "john@example.com", "title": "CEO"}]',
condition: { field: 'operation', value: 'contact_bulk_create' },
required: true,
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `Generate a JSON array of contact objects for Apollo bulk create. Each object must have first_name and last_name, and optionally email, title, account_id, owner_id.
### CONTEXT
{context}
### CRITICAL INSTRUCTION
Return ONLY a valid JSON array. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
### EXAMPLES
[{"first_name": "John", "last_name": "Doe", "email": "john@example.com", "title": "CEO"}]
[{"first_name": "Jane", "last_name": "Smith", "title": "Manager", "account_id": "123"}]
Return ONLY valid JSON array - no explanations.`,
placeholder: 'Describe the contacts you want to create...',
generationType: 'json-array',
},
},
{
id: 'contacts',
@@ -560,25 +295,6 @@ Return ONLY valid JSON array - no explanations.`,
placeholder: '[{"id": "contact_id_1", "first_name": "John", "last_name": "Doe"}]',
condition: { field: 'operation', value: 'contact_bulk_update' },
required: true,
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `Generate a JSON array of contact objects for Apollo bulk update. Each object must have id, and fields to update like first_name, last_name, email, title.
### CONTEXT
{context}
### CRITICAL INSTRUCTION
Return ONLY a valid JSON array. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
### EXAMPLES
[{"id": "contact_id_1", "first_name": "John", "last_name": "Doe"}]
[{"id": "contact_id_2", "email": "newemail@example.com", "title": "Senior Manager"}]
Return ONLY valid JSON array - no explanations.`,
placeholder: 'Describe the contacts you want to update...',
generationType: 'json-array',
},
},
{
id: 'run_dedupe',
@@ -640,28 +356,6 @@ Return ONLY valid JSON array - no explanations.`,
type: 'code',
placeholder: '["stage_id_1", "stage_id_2"]',
condition: { field: 'operation', value: 'account_search' },
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `Generate a JSON array of Apollo account stage IDs for filtering accounts by their pipeline stage.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax (e.g., \`<agent1.stage_id>\`).
### CRITICAL INSTRUCTION
Return ONLY a valid JSON array of stage ID strings. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
### EXAMPLES
["stage_id_1", "stage_id_2"]
[<agent1.selected_stage>]
Return ONLY valid JSON array - no explanations.`,
placeholder: 'Enter or describe the account stage IDs...',
generationType: 'json-array',
},
},
// Account Bulk Operations
@@ -673,29 +367,6 @@ Return ONLY valid JSON array - no explanations.`,
'[{"name": "Company A", "website_url": "https://companya.com", "phone": "+1234567890"}]',
condition: { field: 'operation', value: 'account_bulk_create' },
required: true,
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `Generate a JSON array of account objects for Apollo bulk create. Each object must have name, and optionally website_url, phone, owner_id.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax (e.g., \`<agent1.company_name>\`).
Environment variables use \`{{ENV_VAR_NAME}}\` syntax.
### CRITICAL INSTRUCTION
Return ONLY a valid JSON array. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
### EXAMPLES
[{"name": "Acme Corp", "website_url": "https://acme.com", "phone": "+1234567890"}]
[{"name": <agent1.company>, "website_url": <agent1.website>}]
Return ONLY valid JSON array - no explanations.`,
placeholder: 'Describe the accounts you want to create...',
generationType: 'json-array',
},
},
{
id: 'accounts',
@@ -704,29 +375,6 @@ Return ONLY valid JSON array - no explanations.`,
placeholder: '[{"id": "account_id_1", "name": "Updated Company Name"}]',
condition: { field: 'operation', value: 'account_bulk_update' },
required: true,
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `Generate a JSON array of account objects for Apollo bulk update. Each object must have id, and fields to update like name, website_url, phone.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax (e.g., \`<agent1.account_id>\`).
Environment variables use \`{{ENV_VAR_NAME}}\` syntax.
### CRITICAL INSTRUCTION
Return ONLY a valid JSON array. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
### EXAMPLES
[{"id": "account_id_1", "name": "Updated Company Name"}]
[{"id": <agent1.account_id>, "name": <agent1.new_name>}]
Return ONLY valid JSON array - no explanations.`,
placeholder: 'Describe the accounts you want to update...',
generationType: 'json-array',
},
},
// Opportunity Fields
@@ -805,28 +453,6 @@ Return ONLY valid JSON array - no explanations.`,
type: 'code',
placeholder: '["account_id_1", "account_id_2"]',
condition: { field: 'operation', value: 'opportunity_search' },
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `Generate a JSON array of Apollo account IDs for filtering opportunities.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax (e.g., \`<agent1.account_id>\`).
### CRITICAL INSTRUCTION
Return ONLY a valid JSON array of account ID strings. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
### EXAMPLES
["account_id_1", "account_id_2"]
[<agent1.account_id>]
Return ONLY valid JSON array - no explanations.`,
placeholder: 'Enter or describe the account IDs...',
generationType: 'json-array',
},
},
{
id: 'stage_ids',
@@ -834,28 +460,6 @@ Return ONLY valid JSON array - no explanations.`,
type: 'code',
placeholder: '["stage_id_1", "stage_id_2"]',
condition: { field: 'operation', value: 'opportunity_search' },
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `Generate a JSON array of Apollo opportunity stage IDs for filtering opportunities.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax (e.g., \`<agent1.stage_id>\`).
### CRITICAL INSTRUCTION
Return ONLY a valid JSON array of stage ID strings. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
### EXAMPLES
["stage_id_1", "stage_id_2"]
[<agent1.selected_stages>]
Return ONLY valid JSON array - no explanations.`,
placeholder: 'Enter or describe the stage IDs...',
generationType: 'json-array',
},
},
{
id: 'owner_ids',
@@ -863,28 +467,6 @@ Return ONLY valid JSON array - no explanations.`,
type: 'code',
placeholder: '["user_id_1", "user_id_2"]',
condition: { field: 'operation', value: 'opportunity_search' },
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `Generate a JSON array of Apollo user/owner IDs for filtering opportunities by owner.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax (e.g., \`<agent1.owner_id>\`).
### CRITICAL INSTRUCTION
Return ONLY a valid JSON array of user ID strings. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
### EXAMPLES
["user_id_1", "user_id_2"]
[<agent1.assigned_owner>]
Return ONLY valid JSON array - no explanations.`,
placeholder: 'Enter or describe the owner IDs...',
generationType: 'json-array',
},
},
// Sequence Search Fields
@@ -918,29 +500,6 @@ Return ONLY valid JSON array - no explanations.`,
placeholder: '["contact_id_1", "contact_id_2"]',
condition: { field: 'operation', value: 'sequence_add' },
required: true,
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `Generate a JSON array of Apollo contact IDs to add to a sequence.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax (e.g., \`<agent1.contact_id>\`).
Environment variables use \`{{ENV_VAR_NAME}}\` syntax.
### CRITICAL INSTRUCTION
Return ONLY a valid JSON array. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
### EXAMPLES
["contact_id_1", "contact_id_2"]
[<agent1.contact_ids>]
Return ONLY valid JSON array - no explanations.`,
placeholder: 'Describe the contact IDs to add...',
generationType: 'json-array',
},
},
// Task Fields

View File

@@ -119,79 +119,12 @@ export const AsanaBlock: BlockConfig<AsanaResponse> = {
id: 'notes',
title: 'Task Notes',
type: 'long-input',
placeholder: 'Enter task notes or description',
condition: {
field: 'operation',
value: ['create_task', 'update_task'],
},
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert at writing Asana task descriptions. Create clear, actionable task notes.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.details>\`, \`<function1.result>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{PROJECT_NAME}}\`)
### ASANA FORMATTING
Asana supports rich text:
- **bold** with double asterisks
- _italic_ with underscores
- Bullet points with -
- Numbered lists with 1.
- Links automatically detected
### GUIDELINES
1. **Clarity**: Be specific about what needs to be done
2. **Context**: Include relevant background
3. **Subtasks**: Break down complex tasks
4. **Dependencies**: Note any blockers or dependencies
5. **Resources**: Link to relevant documents or references
### EXAMPLES
**Task description**: "Write notes for implementing user authentication"
→ **Objective**
Implement user authentication using OAuth 2.0.
**Requirements**
- Support Google and GitHub OAuth providers
- Implement token refresh mechanism
- Add session management
**Acceptance Criteria**
- Users can sign in with Google
- Users can sign in with GitHub
- Sessions persist across browser restarts
- Token refresh works automatically
**Resources**
- Design doc: [link]
- API specs: [link]
**With variables**: "Create task from request"
→ **Request Details**
- Submitted by: <agent1.user_name>
- Priority: <function1.priority>
- Due: <function1.due_date>
**Description**
<agent1.request_description>
**Next Steps**
- Review requirements
- Create implementation plan
- Schedule kickoff meeting
### REMEMBER
Be specific and actionable. Include acceptance criteria when relevant.`,
placeholder: 'Describe the task notes...',
generationType: 'markdown-content',
},
},
{
id: 'assignee',

View File

@@ -30,47 +30,6 @@ export const ClayBlock: BlockConfig<ClayPopulateResponse> = {
JSON: Best for populating multiple columns.
Plain Text: Best for populating a table in free-form style.
`,
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert Clay developer. Generate data to populate a Clay workbook table.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.name>\`, \`<function1.result.email>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{DEFAULT_SOURCE}}\`)
Do NOT wrap variable references in quotes for non-string values.
### CRITICAL INSTRUCTION
Return ONLY valid JSON or plain text data. Do not include any explanations, markdown formatting, or comments.
### DATA GUIDELINES
1. **JSON Format**: Best for structured data with multiple columns
- Use objects for single rows: {"column1": "value1", "column2": "value2"}
- Use arrays for multiple rows: [{"col1": "val1"}, {"col1": "val2"}]
2. **Plain Text**: Best for free-form single column data
3. **Column Names**: Should match your Clay table column names
### EXAMPLES
**Single record**: "Add a lead with name and email"
→ {"name": "John Doe", "email": "john@example.com", "company": "Acme Inc"}
**Multiple records**: "Add multiple leads"
→ [{"name": "John", "email": "john@example.com"}, {"name": "Jane", "email": "jane@example.com"}]
**With variables**: "Populate with data from previous block"
→ {"name": <agent1.name>, "email": <agent1.email>, "source": "{{DEFAULT_SOURCE}}"}
### REMEMBER
Return ONLY the data - no explanations.`,
placeholder: 'Describe the data you want to populate...',
generationType: 'json-object',
},
},
{
id: 'authToken',

View File

@@ -116,85 +116,6 @@ export const ConfluenceBlock: BlockConfig<ConfluenceResponse> = {
type: 'long-input',
placeholder: 'Enter content for the page',
condition: { field: 'operation', value: ['create', 'update'] },
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert at writing Confluence documentation. Create clear, professional documentation content.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.content>\`, \`<function1.result>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{PROJECT_NAME}}\`)
### CONFLUENCE FORMATTING
Confluence uses wiki markup or can accept HTML:
- h1. h2. h3. for headers
- *bold* and _italic_
- * bullet lists
- # numbered lists
- {code}code blocks{code}
- [link text|URL]
- {info}, {warning}, {note} macros
### GUIDELINES
1. **Structure**: Use clear hierarchy with headers
2. **Documentation Style**: Write for clarity and completeness
3. **Tables**: Use tables for structured data
4. **Code Examples**: Include code samples where relevant
5. **Links**: Reference related pages and resources
### EXAMPLES
**Technical documentation**: "Write API endpoint documentation"
→ h1. User API Endpoints
h2. Overview
This document describes the User API endpoints available in the system.
h2. Authentication
All endpoints require a valid API key in the Authorization header.
h2. Endpoints
h3. GET /api/users
Retrieves a list of all users.
*Parameters:*
| Name | Type | Required | Description |
| limit | integer | No | Maximum results (default: 100) |
| offset | integer | No | Pagination offset |
*Response:*
{code:json}
{
"users": [...],
"total": 150
}
{code}
{info}Rate limit: 100 requests per minute{info}
**With variables**: "Create page from template"
→ h1. <agent1.title>
h2. Overview
<agent1.summary>
h2. Details
<function1.content>
h2. Related Pages
* [Related Page 1]
* [Related Page 2]
### REMEMBER
Use Confluence wiki markup. Write clear, professional documentation.`,
placeholder: 'Describe the Confluence content...',
generationType: 'markdown-content',
},
},
{
id: 'parentId',

View File

@@ -54,47 +54,6 @@ export const DatadogBlock: BlockConfig<DatadogResponse> = {
]`,
condition: { field: 'operation', value: 'datadog_submit_metrics' },
required: true,
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert Datadog developer. Generate Datadog metrics series JSON array.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.metric_value>\`, \`<function1.result.tags>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{DD_SERVICE}}\`, \`{{ENVIRONMENT}}\`)
Do NOT wrap variable references in quotes for non-string values.
### CRITICAL INSTRUCTION
Return ONLY the metrics series as a valid JSON array. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
### METRICS GUIDELINES
1. **Array Format**: Always return a JSON array, even for single metric
2. **Metric Types**: gauge, count, rate, histogram, distribution
3. **Points**: Array of {timestamp, value} objects (timestamp in Unix seconds)
4. **Tags**: Array of strings in "key:value" format
5. **Metric Names**: Use dot notation (e.g., "custom.app.response_time")
### EXAMPLES
**Single gauge metric**: "Submit response time metric"
→ [{"metric": "custom.app.response_time", "type": "gauge", "points": [{"timestamp": 1704067200, "value": 0.85}], "tags": ["env:production"]}]
**Multiple metrics**: "Submit CPU and memory metrics"
→ [{"metric": "system.cpu.usage", "type": "gauge", "points": [{"timestamp": 1704067200, "value": 75.5}], "tags": ["env:production"]}, {"metric": "system.memory.used", "type": "gauge", "points": [{"timestamp": 1704067200, "value": 8192}], "tags": ["env:production"]}]
**With multiple points**: "Submit metric with multiple data points"
→ [{"metric": "custom.app.requests", "type": "count", "points": [{"timestamp": 1704067200, "value": 100}, {"timestamp": 1704067300, "value": 150}], "tags": ["env:production", "service:api"]}]
### REMEMBER
Return ONLY valid JSON array - no explanations, no markdown, no extra text.`,
placeholder: 'Describe the metrics you want to submit...',
generationType: 'json-array',
},
},
// ========================
@@ -239,50 +198,6 @@ Return ONLY valid JSON array - no explanations, no markdown, no extra text.`,
type: 'code',
placeholder: '{"notify_no_data": true, "thresholds": {"critical": 90}}',
condition: { field: 'operation', value: 'datadog_create_monitor' },
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert Datadog developer. Generate Datadog monitor options JSON.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.threshold>\`, \`<function1.result.interval>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{ALERT_THRESHOLD}}\`)
Do NOT wrap variable references in quotes for non-string values.
### CRITICAL INSTRUCTION
Return ONLY the options as valid JSON. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object.
### OPTIONS GUIDELINES
1. **Thresholds**: Define warning and critical thresholds
2. **Notify No Data**: Whether to notify when no data is received
3. **Evaluation Delay**: Delay before evaluation (in seconds)
4. **Renotify Interval**: Minutes between renotifications
5. **Timeout**: Evaluation timeout (in seconds)
### EXAMPLES
**Basic thresholds**: "Set critical threshold at 90"
→ {"thresholds": {"critical": 90}}
**With warning**: "Set warning at 70 and critical at 90"
→ {"thresholds": {"warning": 70, "critical": 90}}
**Full options**: "Monitor with thresholds, notify no data, and renotify every 60 minutes"
→ {"notify_no_data": true, "thresholds": {"warning": 70, "critical": 90}, "renotify_interval": 60}
**With evaluation delay**: "Monitor with 5 minute evaluation delay"
→ {"thresholds": {"critical": 90}, "evaluation_delay": 300}
### REMEMBER
Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
placeholder: 'Describe the monitor options you need...',
generationType: 'json-object',
},
},
// ========================
@@ -351,61 +266,6 @@ Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
placeholder: 'service:web-app status:error',
condition: { field: 'operation', value: 'datadog_query_logs' },
required: true,
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert Datadog developer. Generate Datadog log search queries.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax.
Environment variables use \`{{ENV_VAR_NAME}}\` syntax.
### CRITICAL INSTRUCTION
Return ONLY the log search query string. Do not include any explanations, markdown formatting, comments, or additional text.
### LOG QUERY SYNTAX
**Basic Filters**:
- service:my-service
- status:error
- host:my-host
**Text Search**:
- "exact phrase"
- message:*error*
**Comparisons**:
- @duration:>1000
- @status_code:>=400
**Combining**:
- service:api AND status:error
- status:error OR status:warn
- NOT service:healthcheck
**Facets**:
- @http.url:"/api/*"
- @user.id:12345
### EXAMPLES
**Simple**: "Find errors in the API service"
→ service:api status:error
**With text**: "Find logs containing 'timeout'"
→ service:api "timeout"
**Complex**: "Find API errors except healthcheck"
→ service:api status:error NOT @http.url:"/health*"
### REMEMBER
Return ONLY the query string - no explanations.`,
placeholder: 'Describe the logs you want to find...',
generationType: 'sql-query',
},
},
{
id: 'logFrom',
@@ -448,48 +308,6 @@ Return ONLY the query string - no explanations.`,
]`,
condition: { field: 'operation', value: 'datadog_send_logs' },
required: true,
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert Datadog developer. Generate Datadog logs JSON array.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.message>\`, \`<function1.result.user_id>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{DD_SERVICE}}\`, \`{{ENVIRONMENT}}\`)
Do NOT wrap variable references in quotes for non-string values.
### CRITICAL INSTRUCTION
Return ONLY the logs as a valid JSON array. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
### LOGS GUIDELINES
1. **Array Format**: Always return a JSON array, even for single log
2. **Message**: Required field with the log message
3. **Service**: Application/service name
4. **ddsource**: Source identifier (e.g., "custom", "nginx", "python")
5. **ddtags**: Comma-separated tags in "key:value" format
6. **Additional Fields**: Add any custom fields as needed
### EXAMPLES
**Single log**: "Send application start log"
→ [{"message": "Application started successfully", "service": "my-app", "ddsource": "custom", "ddtags": "env:production"}]
**Multiple logs**: "Send error and info logs"
→ [{"message": "Error occurred", "service": "api", "ddsource": "custom", "ddtags": "env:production,level:error"}, {"message": "Request processed", "service": "api", "ddsource": "custom", "ddtags": "env:production,level:info"}]
**With custom fields**: "Send log with user context"
→ [{"message": "User logged in", "service": "auth", "ddsource": "custom", "ddtags": "env:production", "user_id": "123", "ip": "192.168.1.1"}]
### REMEMBER
Return ONLY valid JSON array - no explanations, no markdown, no extra text.`,
placeholder: 'Describe the logs you want to send...',
generationType: 'json-array',
},
},
// ========================

View File

@@ -131,63 +131,6 @@ export const DiscordBlock: BlockConfig<DiscordResponse> = {
field: 'operation',
value: ['discord_send_message', 'discord_edit_message', 'discord_execute_webhook'],
},
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert at writing Discord messages. Compose engaging messages using Discord's markdown formatting.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.user>\`, \`<function1.result>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{SERVER_NAME}}\`)
### DISCORD MARKDOWN FORMATTING
- **bold** with double asterisks
- *italic* with single asterisks
- __underline__ with double underscores
- ~~strikethrough~~ with double tildes
- \`inline code\` with backticks
- \`\`\`code block\`\`\` with triple backticks
- > quote with >
- >>> multi-line quote with >>>
- ||spoiler|| with double pipes
### GUIDELINES
1. **Tone**: Match community/server tone (casual, friendly, or professional)
2. **Formatting**: Use markdown for emphasis
3. **Emojis**: Use Discord emojis where appropriate
4. **Length**: Discord has a 2000 character limit
### EXAMPLES
**Announcement**: "Announce a new feature"
→ 🎉 **New Feature Alert!**
We've just released a brand new feature that you're going to love!
**What's New:**
• Faster performance
• Better UI
• New customization options
Check it out and let us know what you think!
**With variables**: "Send alert message"
→ ⚠️ **Alert**
Status: <function1.status>
Service: <agent1.service_name>
Please check the dashboard for more details.
### REMEMBER
Use Discord markdown. Keep messages engaging and within 2000 characters.`,
placeholder: 'Describe the Discord message you want to send...',
generationType: 'message-content',
},
},
// Emoji - for reaction operations
{

View File

@@ -65,49 +65,6 @@ export const DynamoDBBlock: BlockConfig<DynamoDBResponse> = {
placeholder: '{\n "pk": "user#123"\n}',
condition: { field: 'operation', value: 'get' },
required: true,
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert DynamoDB developer. Generate DynamoDB primary key JSON based on the user's request.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.user_id>\`, \`<function1.result.order_id>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{TABLE_PREFIX}}\`)
Do NOT wrap variable references in quotes for non-string values.
### CRITICAL INSTRUCTION
Return ONLY the key as valid JSON. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object.
### KEY GUIDELINES
1. **Primary Key**: Must include partition key (pk) and optionally sort key (sk)
2. **Data Types**: Use strings, numbers, or binary for key values
3. **Format**: Simple key-value pairs matching your table schema
4. **Required**: Partition key is always required, sort key only if table has composite key
### EXAMPLES
**Simple partition key**: "Get item with partition key user#123"
→ {"pk": "user#123"}
**Composite key**: "Get item with pk user#123 and sk order#456"
→ {"pk": "user#123", "sk": "order#456"}
**Numeric key**: "Get item with id 12345"
→ {"id": 12345}
**String key**: "Get user by email"
→ {"email": "user@example.com"}
### REMEMBER
Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
placeholder: 'Describe the key you want to use...',
generationType: 'json-object',
},
},
{
id: 'key',
@@ -116,43 +73,6 @@ Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
placeholder: '{\n "pk": "user#123"\n}',
condition: { field: 'operation', value: 'update' },
required: true,
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert DynamoDB developer. Generate DynamoDB primary key JSON for UPDATE operations.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.user_id>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax
Do NOT wrap variable references in quotes for non-string values.
### CRITICAL INSTRUCTION
Return ONLY the key as valid JSON. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object.
### KEY GUIDELINES
1. **Primary Key**: Must include partition key (pk) and optionally sort key (sk)
2. **Data Types**: Use strings, numbers, or binary for key values
3. **Format**: Simple key-value pairs matching your table schema
4. **Required**: Partition key is always required, sort key only if table has composite key
### EXAMPLES
**Simple partition key**: "Update item with partition key user#123"
→ {"pk": "user#123"}
**Composite key**: "Update item with pk user#123 and sk order#456"
→ {"pk": "user#123", "sk": "order#456"}
### REMEMBER
Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
placeholder: 'Describe the key you want to use...',
generationType: 'json-object',
},
},
{
id: 'key',
@@ -161,46 +81,6 @@ Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
placeholder: '{\n "pk": "user#123"\n}',
condition: { field: 'operation', value: 'delete' },
required: true,
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert DynamoDB developer. Generate DynamoDB primary key JSON for DELETE operations.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.item_id>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax
Do NOT wrap variable references in quotes for non-string values.
### CRITICAL INSTRUCTION
Return ONLY the key as valid JSON. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object.
### ⚠️ DELETION WARNING ⚠️
DELETIONS ARE PERMANENT! Be extremely careful and specific with your key criteria.
### KEY GUIDELINES
1. **Primary Key**: Must include partition key (pk) and optionally sort key (sk)
2. **Data Types**: Use strings, numbers, or binary for key values
3. **Format**: Simple key-value pairs matching your table schema
4. **Required**: Partition key is always required, sort key only if table has composite key
### EXAMPLES
**Simple partition key**: "Delete item with partition key user#123"
→ {"pk": "user#123"}
**Composite key**: "Delete item with pk user#123 and sk order#456"
→ {"pk": "user#123", "sk": "order#456"}
### REMEMBER
Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
placeholder: 'Describe the key you want to use...',
generationType: 'json-object',
},
},
// Consistent read for get
{
@@ -223,49 +103,6 @@ Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
'{\n "pk": "user#123",\n "name": "John Doe",\n "email": "john@example.com"\n}',
condition: { field: 'operation', value: 'put' },
required: true,
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert DynamoDB developer. Generate DynamoDB item JSON for PUT operations.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.name>\`, \`<function1.result.email>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{DEFAULT_STATUS}}\`)
Do NOT wrap variable references in quotes for non-string values.
### CRITICAL INSTRUCTION
Return ONLY the item as valid JSON. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object.
### ITEM GUIDELINES
1. **Primary Key**: Must include partition key (pk) and optionally sort key (sk)
2. **Attributes**: Include all attributes you want to store
3. **Data Types**: Use strings, numbers, booleans, lists, maps, sets, null
4. **Nested Data**: Use maps (objects) for nested structures
### EXAMPLES
**Simple item**: "Create user with pk, name, and email"
→ {"pk": "user#123", "name": "John Doe", "email": "john@example.com"}
**With composite key**: "Create order with pk, sk, total, and status"
→ {"pk": "user#123", "sk": "order#456", "total": 99.99, "status": "pending"}
**Complex nested**: "Create user with profile and preferences"
→ {"pk": "user#123", "name": "John", "profile": {"age": 30, "city": "NYC"}, "preferences": {"theme": "dark", "notifications": true}}
**With lists**: "Create product with tags and categories"
→ {"pk": "product#123", "name": "Widget", "tags": ["electronics", "gadget"], "categories": ["tech", "home"]}
### REMEMBER
Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
placeholder: 'Describe the item you want to create...',
generationType: 'json-object',
},
},
// Key condition expression for query
{
@@ -315,42 +152,6 @@ Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
type: 'code',
placeholder: '{\n "#name": "name"\n}',
condition: { field: 'operation', value: 'query' },
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert DynamoDB developer. Generate DynamoDB Expression Attribute Names JSON.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax for dynamic field names.
### CRITICAL INSTRUCTION
Return ONLY the expression attribute names as valid JSON. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object.
### EXPRESSION ATTRIBUTE NAMES GUIDELINES
1. **Purpose**: Maps placeholder names (starting with #) to actual attribute names
2. **Reserved Words**: Use when attribute names are DynamoDB reserved words (name, status, data, etc.)
3. **Format**: Keys start with #, values are actual attribute names
4. **Query/Scan/Update**: Used in KeyConditionExpression, FilterExpression, UpdateExpression
### EXAMPLES
**Single attribute**: "Map #name to name attribute"
→ {"#name": "name"}
**Multiple attributes**: "Map #status and #date to status and created_date"
→ {"#status": "status", "#date": "created_date"}
**Reserved words**: "Map #data to data attribute (reserved word)"
→ {"#data": "data"}
### REMEMBER
Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
placeholder: 'Describe the attribute name mappings...',
generationType: 'json-object',
},
},
{
id: 'expressionAttributeNames',
@@ -358,39 +159,6 @@ Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
type: 'code',
placeholder: '{\n "#name": "name"\n}',
condition: { field: 'operation', value: 'scan' },
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert DynamoDB developer. Generate DynamoDB Expression Attribute Names JSON for SCAN operations.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax for dynamic field names.
### CRITICAL INSTRUCTION
Return ONLY the expression attribute names as valid JSON. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object.
### EXPRESSION ATTRIBUTE NAMES GUIDELINES
1. **Purpose**: Maps placeholder names (starting with #) to actual attribute names
2. **Reserved Words**: Use when attribute names are DynamoDB reserved words
3. **Format**: Keys start with #, values are actual attribute names
4. **Scan**: Used in FilterExpression and ProjectionExpression
### EXAMPLES
**Single attribute**: "Map #name to name attribute"
→ {"#name": "name"}
**Multiple attributes**: "Map #status and #date"
→ {"#status": "status", "#date": "created_date"}
### REMEMBER
Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
placeholder: 'Describe the attribute name mappings...',
generationType: 'json-object',
},
},
{
id: 'expressionAttributeNames',
@@ -398,39 +166,6 @@ Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
type: 'code',
placeholder: '{\n "#name": "name"\n}',
condition: { field: 'operation', value: 'update' },
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert DynamoDB developer. Generate DynamoDB Expression Attribute Names JSON for UPDATE operations.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax for dynamic field names.
### CRITICAL INSTRUCTION
Return ONLY the expression attribute names as valid JSON. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object.
### EXPRESSION ATTRIBUTE NAMES GUIDELINES
1. **Purpose**: Maps placeholder names (starting with #) to actual attribute names
2. **Reserved Words**: Use when attribute names are DynamoDB reserved words
3. **Format**: Keys start with #, values are actual attribute names
4. **Update**: Used in UpdateExpression and ConditionExpression
### EXAMPLES
**Single attribute**: "Map #name to name attribute"
→ {"#name": "name"}
**Multiple attributes**: "Map #status and #date"
→ {"#status": "status", "#date": "created_date"}
### REMEMBER
Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
placeholder: 'Describe the attribute name mappings...',
generationType: 'json-object',
},
},
// Expression attribute values for query, scan, update
{
@@ -439,49 +174,6 @@ Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
type: 'code',
placeholder: '{\n ":pk": "user#123",\n ":name": "Jane"\n}',
condition: { field: 'operation', value: 'query' },
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert DynamoDB developer. Generate DynamoDB Expression Attribute Values JSON for QUERY operations.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.user_id>\`, \`<function1.result.status>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax
Do NOT wrap variable references in quotes for non-string values.
### CRITICAL INSTRUCTION
Return ONLY the expression attribute values as valid JSON. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object.
### EXPRESSION ATTRIBUTE VALUES GUIDELINES
1. **Purpose**: Provides values for placeholders (starting with :) in expressions
2. **Format**: Keys start with :, values are the actual values
3. **Data Types**: Use strings, numbers, booleans, lists, maps, sets, null
4. **Query**: Used in KeyConditionExpression and FilterExpression
### EXAMPLES
**Simple values**: "Values for pk and status"
→ {":pk": "user#123", ":status": "active"}
**Numeric values**: "Values for min and max"
→ {":min": 18, ":max": 65}
**Mixed types**: "Values for pk, count, and active"
→ {":pk": "user#123", ":count": 10, ":active": true}
**List values**: "Values for tags"
→ {":tags": ["tag1", "tag2"]}
### REMEMBER
Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
placeholder: 'Describe the attribute values...',
generationType: 'json-object',
},
},
{
id: 'expressionAttributeValues',
@@ -489,39 +181,6 @@ Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
type: 'code',
placeholder: '{\n ":status": "active"\n}',
condition: { field: 'operation', value: 'scan' },
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert DynamoDB developer. Generate DynamoDB Expression Attribute Values JSON for SCAN operations.
### CONTEXT
{context}
### CRITICAL INSTRUCTION
Return ONLY the expression attribute values as valid JSON. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object.
### EXPRESSION ATTRIBUTE VALUES GUIDELINES
1. **Purpose**: Provides values for placeholders (starting with :) in expressions
2. **Format**: Keys start with :, values are the actual values
3. **Data Types**: Use strings, numbers, booleans, lists, maps, sets, null
4. **Scan**: Used in FilterExpression
### EXAMPLES
**Simple values**: "Values for status"
→ {":status": "active"}
**Numeric values**: "Values for min and max"
→ {":min": 18, ":max": 65}
**Mixed types**: "Values for status and count"
→ {":status": "active", ":count": 10}
### REMEMBER
Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
placeholder: 'Describe the attribute values...',
generationType: 'json-object',
},
},
{
id: 'expressionAttributeValues',
@@ -529,39 +188,6 @@ Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
type: 'code',
placeholder: '{\n ":name": "Jane Doe"\n}',
condition: { field: 'operation', value: 'update' },
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert DynamoDB developer. Generate DynamoDB Expression Attribute Values JSON for UPDATE operations.
### CONTEXT
{context}
### CRITICAL INSTRUCTION
Return ONLY the expression attribute values as valid JSON. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object.
### EXPRESSION ATTRIBUTE VALUES GUIDELINES
1. **Purpose**: Provides values for placeholders (starting with :) in expressions
2. **Format**: Keys start with :, values are the actual values
3. **Data Types**: Use strings, numbers, booleans, lists, maps, sets, null
4. **Update**: Used in UpdateExpression and ConditionExpression
### EXAMPLES
**Simple values**: "Values for name and status"
→ {":name": "Jane Doe", ":status": "active"}
**Numeric values**: "Values for increment"
→ {":inc": 1}
**Mixed types**: "Values for name, count, and active"
→ {":name": "Jane", ":count": 5, ":active": true}
### REMEMBER
Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
placeholder: 'Describe the attribute values...',
generationType: 'json-object',
},
},
// Index name for query
{

View File

@@ -174,45 +174,6 @@ export const ElasticsearchBlock: BlockConfig<ElasticsearchResponse> = {
placeholder: '{ "field": "value", "another_field": 123 }',
required: true,
condition: { field: 'operation', value: 'elasticsearch_index_document' },
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert Elasticsearch developer. Generate Elasticsearch document JSON based on the user's request.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.name>\`, \`<function1.result.email>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{DEFAULT_STATUS}}\`)
Do NOT wrap variable references in quotes for non-string values.
### CRITICAL INSTRUCTION
Return ONLY the document as valid JSON. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object.
### DOCUMENT GUIDELINES
1. **Structure**: Use proper JSON object structure with field-value pairs
2. **Data Types**: Use appropriate types (strings, numbers, booleans, arrays, nested objects)
3. **Naming**: Use lowercase field names with underscores (snake_case) following Elasticsearch conventions
### EXAMPLES
**Simple document**: "Create a user document with name, email, and age"
→ {"name": "John Doe", "email": "john@example.com", "age": 30}
**With variables**: "Create a document from previous block"
→ {"name": <agent1.name>, "email": <agent1.email>, "source": "{{DATA_SOURCE}}"}
**With nested data**: "Create a product with name, price, and category details"
→ {"name": "Laptop", "price": 999.99, "category": {"name": "Electronics", "id": "cat-123"}}
### REMEMBER
Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
placeholder: 'Describe the document you want to create...',
generationType: 'json-object',
},
},
// Document body - for update (partial)
@@ -223,43 +184,6 @@ Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
placeholder: '{ "field_to_update": "new_value" }',
required: true,
condition: { field: 'operation', value: 'elasticsearch_update_document' },
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert Elasticsearch developer. Generate a partial document JSON for updating an existing document.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.new_status>\`, \`<function1.result.price>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{DEFAULT_STATUS}}\`)
Do NOT wrap variable references in quotes for non-string values.
### CRITICAL INSTRUCTION
Return ONLY the partial document as valid JSON. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object with only the fields you want to update.
### UPDATE GUIDELINES
1. **Partial Updates**: Only include fields that need to be updated
2. **Nested Updates**: Use dot notation for nested fields or include the full nested object
3. **Arrays**: Replace entire arrays, or use scripts for array manipulation
4. **Data Types**: Maintain consistent data types with existing document
### EXAMPLES
**Simple update**: "Update the email and status fields"
→ {"email": "newemail@example.com", "status": "active"}
**With variables**: "Update fields from previous block"
→ {"status": <agent1.new_status>, "updated_by": "{{SYSTEM_USER}}"}
### REMEMBER
Return ONLY valid JSON with fields to update - no explanations, no markdown, no extra text.`,
placeholder: 'Describe what fields you want to update...',
generationType: 'json-object',
},
},
// Search query
@@ -269,72 +193,6 @@ Return ONLY valid JSON with fields to update - no explanations, no markdown, no
type: 'code',
placeholder: '{ "match": { "field": "search term" } }',
condition: { field: 'operation', value: 'elasticsearch_search' },
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert Elasticsearch developer. Generate Elasticsearch Query DSL queries based on the user's request.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.search_term>\`, \`<function1.result.status>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{DEFAULT_STATUS}}\`)
Do NOT wrap variable references in quotes for non-string values.
### CRITICAL INSTRUCTION
Return ONLY the query as valid JSON. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON query object.
### QUERY GUIDELINES
1. **Query DSL**: Use Elasticsearch Query DSL syntax
2. **Performance**: Structure queries efficiently
3. **Relevance**: Use appropriate query types for the use case
4. **Combining**: Use bool queries to combine multiple conditions
### COMMON QUERY TYPES
**Match Query** (full-text search):
{"match": {"field": "search text"}}
**Term Query** (exact match):
{"term": {"status": "active"}}
**Range Query**:
{"range": {"age": {"gte": 18, "lte": 65}}}
**Bool Query** (combine queries):
{"bool": {"must": [{"match": {"title": "elasticsearch"}}], "filter": [{"term": {"status": "published"}}]}}
**Multi-Match** (search multiple fields):
{"multi_match": {"query": "search text", "fields": ["title", "content"]}}
**Match Phrase** (exact phrase):
{"match_phrase": {"description": "exact phrase here"}}
### EXAMPLES
**Simple search**: "Find documents with 'elasticsearch' in the title"
→ {"match": {"title": "elasticsearch"}}
**Multiple conditions**: "Find active users over 18"
→ {"bool": {"must": [{"term": {"status": "active"}}, {"range": {"age": {"gt": 18}}}]}}
**Text search with filters**: "Search for 'laptop' in products that are in stock and priced under 1000"
→ {"bool": {"must": [{"match": {"name": "laptop"}}], "filter": [{"term": {"in_stock": true}}, {"range": {"price": {"lt": 1000}}]}]}}
**Complex bool query**: "Find published articles about 'python' or 'javascript' written in 2024"
→ {"bool": {"must": [{"term": {"status": "published"}}, {"range": {"published_date": {"gte": "2024-01-01", "lte": "2024-12-31"}}], "should": [{"match": {"tags": "python"}}, {"match": {"tags": "javascript"}}], "minimum_should_match": 1}}
**Fuzzy search**: "Find documents similar to 'elastiksearch'"
→ {"fuzzy": {"title": {"value": "elastiksearch", "fuzziness": "AUTO"}}}
### REMEMBER
Return ONLY valid JSON query - no explanations, no markdown, no extra text.`,
placeholder: 'Describe what you want to search for...',
generationType: 'elasticsearch-query',
},
},
// Count query
@@ -344,45 +202,6 @@ Return ONLY valid JSON query - no explanations, no markdown, no extra text.`,
type: 'code',
placeholder: '{ "match": { "field": "value" } }',
condition: { field: 'operation', value: 'elasticsearch_count' },
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert Elasticsearch developer. Generate Elasticsearch Query DSL queries for counting documents.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax.
Environment variables use \`{{ENV_VAR_NAME}}\` syntax.
### CRITICAL INSTRUCTION
Return ONLY the query as valid JSON. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON query object.
### COUNT QUERY GUIDELINES
1. **Query DSL**: Use Elasticsearch Query DSL syntax (same as search queries)
2. **Efficiency**: Use filters instead of queries when possible for better performance
3. **Structure**: Same query structure as search queries
### EXAMPLES
**Count by term**: "Count active documents"
→ {"term": {"status": "active"}}
**Count with range**: "Count users between 18 and 65"
→ {"range": {"age": {"gte": 18, "lte": 65}}}
**Count with multiple conditions**: "Count published articles from 2024"
→ {"bool": {"must": [{"term": {"status": "published"}}, {"range": {"published_date": {"gte": "2024-01-01", "lte": "2024-12-31"}}}]}}
**Count matching text**: "Count documents containing 'error'"
→ {"match": {"message": "error"}}
### REMEMBER
Return ONLY valid JSON query - no explanations, no markdown, no extra text.`,
placeholder: 'Describe what documents you want to count...',
generationType: 'elasticsearch-query',
},
},
// Search size
@@ -410,45 +229,6 @@ Return ONLY valid JSON query - no explanations, no markdown, no extra text.`,
type: 'code',
placeholder: '[{ "field": { "order": "asc" } }]',
condition: { field: 'operation', value: 'elasticsearch_search' },
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert Elasticsearch developer. Generate Elasticsearch sort specifications as JSON arrays.
### CONTEXT
{context}
### CRITICAL INSTRUCTION
Return ONLY the sort specification as a valid JSON array. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
### SORT GUIDELINES
1. **Array Format**: Always return a JSON array, even for single field sorts
2. **Order**: Use "asc" for ascending, "desc" for descending
3. **Multiple Fields**: Array order determines sort priority
4. **Nested Fields**: Use dot notation for nested fields
### EXAMPLES
**Single field ascending**: "Sort by name"
→ [{"name": {"order": "asc"}}]
**Single field descending**: "Sort by date newest first"
→ [{"created_at": {"order": "desc"}}]
**Multiple fields**: "Sort by category then price"
→ [{"category": {"order": "asc"}}, {"price": {"order": "asc"}}]
**Complex sort**: "Sort by status, then by date descending, then by score"
→ [{"status": {"order": "asc"}}, {"date": {"order": "desc"}}, {"score": {"order": "desc"}}]
**Nested field**: "Sort by customer name"
→ [{"customer.name": {"order": "asc"}}]
### REMEMBER
Return ONLY valid JSON array - no explanations, no markdown, no extra text.`,
placeholder: 'Describe how you want to sort the results...',
generationType: 'elasticsearch-sort',
},
},
// Source includes
@@ -484,72 +264,6 @@ Return ONLY valid JSON array - no explanations, no markdown, no extra text.`,
'{ "index": { "_index": "my-index", "_id": "1" } }\n{ "field": "value" }\n{ "delete": { "_index": "my-index", "_id": "2" } }',
required: true,
condition: { field: 'operation', value: 'elasticsearch_bulk' },
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert Elasticsearch developer. Generate Elasticsearch bulk operations in NDJSON (Newline Delimited JSON) format.
### CONTEXT
{context}
### CRITICAL INSTRUCTION
Return ONLY the bulk operations as NDJSON (each JSON object on a new line). Do not include any explanations, markdown formatting, comments, or additional text. Just the raw NDJSON.
### BULK OPERATION FORMAT
Bulk operations use NDJSON format where:
- Each operation action is on one line (index, create, update, delete)
- Each document follows on the next line (for index/create/update)
- Each pair is separated by a newline
### OPERATION TYPES
**Index** (create or replace):
{"index": {"_index": "my-index", "_id": "1"}}
{"field": "value", "another": 123}
**Create** (only if doesn't exist):
{"create": {"_index": "my-index", "_id": "2"}}
{"field": "value"}
**Update** (partial update):
{"update": {"_index": "my-index", "_id": "3"}}
{"doc": {"field": "new_value"}}
**Delete**:
{"delete": {"_index": "my-index", "_id": "4"}}
### EXAMPLES
**Index multiple documents**: "Index two user documents"
→ {"index": {"_index": "users", "_id": "1"}}
{"name": "John", "email": "john@example.com"}
{"index": {"_index": "users", "_id": "2"}}
{"name": "Jane", "email": "jane@example.com"}
**Mixed operations**: "Index one document and delete another"
→ {"index": {"_index": "products", "_id": "new-1"}}
{"name": "Widget", "price": 19.99}
{"delete": {"_index": "products", "_id": "old-1"}}
**Update operations**: "Update two documents"
→ {"update": {"_index": "users", "_id": "1"}}
{"doc": {"status": "active"}}
{"update": {"_index": "users", "_id": "2"}}
{"doc": {"status": "inactive"}}
**Bulk create**: "Create three products"
→ {"create": {"_index": "products"}}
{"name": "Product A", "price": 10}
{"create": {"_index": "products"}}
{"name": "Product B", "price": 20}
{"create": {"_index": "products"}}
{"name": "Product C", "price": 30}
### REMEMBER
Return ONLY NDJSON format (each JSON object on its own line) - no explanations, no markdown, no extra text.`,
placeholder: 'Describe the bulk operations you want to perform...',
generationType: 'elasticsearch-bulk',
},
},
// Index settings
@@ -559,50 +273,6 @@ Return ONLY NDJSON format (each JSON object on its own line) - no explanations,
type: 'code',
placeholder: '{ "number_of_shards": 1, "number_of_replicas": 1 }',
condition: { field: 'operation', value: 'elasticsearch_create_index' },
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert Elasticsearch developer. Generate Elasticsearch index settings as JSON.
### CONTEXT
{context}
### CRITICAL INSTRUCTION
Return ONLY the settings as valid JSON. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object.
### SETTINGS GUIDELINES
1. **Shards**: number_of_shards determines how data is split (typically 1-5 for small indices)
2. **Replicas**: number_of_replicas for redundancy (0 for development, 1+ for production)
3. **Analysis**: Custom analyzers, tokenizers, filters
4. **Refresh**: Control when changes become searchable
### COMMON SETTINGS
**Basic settings**:
{"number_of_shards": 1, "number_of_replicas": 1}
**With refresh interval**:
{"number_of_shards": 1, "number_of_replicas": 1, "refresh_interval": "30s"}
**With analysis**:
{"number_of_shards": 1, "number_of_replicas": 1, "analysis": {"analyzer": {"custom": {"type": "standard"}}}}
### EXAMPLES
**Simple settings**: "Create index with 1 shard and 1 replica"
→ {"number_of_shards": 1, "number_of_replicas": 1}
**Production settings**: "Create index with 3 shards, 2 replicas, and 10s refresh"
→ {"number_of_shards": 3, "number_of_replicas": 2, "refresh_interval": "10s"}
**Development settings**: "Create index with no replicas for testing"
→ {"number_of_shards": 1, "number_of_replicas": 0}
### REMEMBER
Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
placeholder: 'Describe the index settings you need...',
generationType: 'json-object',
},
},
// Index mappings
@@ -612,65 +282,6 @@ Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
type: 'code',
placeholder: '{ "properties": { "field": { "type": "text" } } }',
condition: { field: 'operation', value: 'elasticsearch_create_index' },
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert Elasticsearch developer. Generate Elasticsearch index mappings as JSON.
### CONTEXT
{context}
### CRITICAL INSTRUCTION
Return ONLY the mappings as valid JSON. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object.
### MAPPING GUIDELINES
1. **Properties**: Define field types and behaviors
2. **Field Types**: text (full-text), keyword (exact), long/integer (numbers), date, boolean, object, nested
3. **Analysis**: text fields are analyzed, keyword fields are not
4. **Nested**: Use nested type for arrays of objects that need independent querying
### COMMON FIELD TYPES
**Text** (full-text search):
{"properties": {"title": {"type": "text"}}}
**Keyword** (exact match, sorting):
{"properties": {"status": {"type": "keyword"}}}
**Number**:
{"properties": {"age": {"type": "integer"}, "price": {"type": "float"}}}
**Date**:
{"properties": {"created_at": {"type": "date"}}}
**Boolean**:
{"properties": {"active": {"type": "boolean"}}}
**Object** (nested structure):
{"properties": {"user": {"type": "object", "properties": {"name": {"type": "text"}, "email": {"type": "keyword"}}}}}
**Nested** (array of objects):
{"properties": {"tags": {"type": "nested", "properties": {"name": {"type": "keyword"}, "value": {"type": "text"}}}}}
### EXAMPLES
**Simple mapping**: "Create mapping for user with name, email, and age"
→ {"properties": {"name": {"type": "text"}, "email": {"type": "keyword"}, "age": {"type": "integer"}}}
**Product mapping**: "Create mapping for product with name, price, description, and tags"
→ {"properties": {"name": {"type": "text"}, "price": {"type": "float"}, "description": {"type": "text"}, "tags": {"type": "keyword"}}}
**Complex nested**: "Create mapping for order with customer and items"
→ {"properties": {"order_id": {"type": "keyword"}, "customer": {"type": "object", "properties": {"name": {"type": "text"}, "email": {"type": "keyword"}}}, "items": {"type": "nested", "properties": {"product": {"type": "text"}, "quantity": {"type": "integer"}, "price": {"type": "float"}}}, "created_at": {"type": "date"}}}
**With analyzers**: "Create text field with custom analyzer"
→ {"properties": {"content": {"type": "text", "analyzer": "standard"}}}
### REMEMBER
Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
placeholder: 'Describe the index mapping you need...',
generationType: 'elasticsearch-mapping',
},
},
// Refresh option

View File

@@ -1,5 +1,5 @@
import { ChartBarIcon } from '@/components/icons'
import { isHosted } from '@/lib/core/config/environment'
import { isHosted } from '@/lib/core/config/feature-flags'
import { createLogger } from '@/lib/logs/console/logger'
import type { BlockConfig, ParamType } from '@/blocks/types'
import type { ProviderId } from '@/providers/types'

View File

@@ -551,46 +551,9 @@ export const GitHubBlock: BlockConfig<GitHubResponse> = {
{
id: 'required_status_checks',
title: 'Required Status Checks',
type: 'long-input',
type: 'short-input',
placeholder: 'JSON: {"strict":true,"contexts":["ci/test"]}',
condition: { field: 'operation', value: 'github_update_branch_protection' },
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert GitHub developer. Generate required status checks JSON for branch protection.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax.
Environment variables use \`{{ENV_VAR_NAME}}\` syntax.
### CRITICAL INSTRUCTION
Return ONLY valid JSON. Do not include any explanations, markdown formatting, or comments.
### STATUS CHECKS STRUCTURE
{
"strict": boolean, // Require branches to be up to date before merging
"contexts": string[] // Array of required status check names
}
### EXAMPLES
**CI required**: "Require CI to pass"
→ {"strict": true, "contexts": ["ci/test"]}
**Multiple checks**: "Require tests and linting"
→ {"strict": true, "contexts": ["ci/test", "ci/lint", "ci/build"]}
**No strict**: "Just require checks, not up-to-date"
→ {"strict": false, "contexts": ["ci/test"]}
### REMEMBER
Return ONLY valid JSON - no explanations.`,
placeholder: 'Describe the required status checks...',
generationType: 'json-object',
},
},
{
id: 'enforce_admins',
@@ -605,47 +568,9 @@ Return ONLY valid JSON - no explanations.`,
{
id: 'required_pull_request_reviews',
title: 'Required PR Reviews',
type: 'long-input',
type: 'short-input',
placeholder: 'JSON: {"required_approving_review_count":1}',
condition: { field: 'operation', value: 'github_update_branch_protection' },
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert GitHub developer. Generate required PR reviews JSON for branch protection.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax.
### CRITICAL INSTRUCTION
Return ONLY valid JSON. Do not include any explanations, markdown formatting, or comments.
### PR REVIEWS STRUCTURE
{
"required_approving_review_count": number, // Number of approvals needed (1-6)
"dismiss_stale_reviews": boolean, // Dismiss approvals when new commits pushed
"require_code_owner_reviews": boolean, // Require review from code owners
"require_last_push_approval": boolean // Require approval of last push
}
### EXAMPLES
**Single approval**: "Require one approval"
→ {"required_approving_review_count": 1}
**Strict reviews**: "Require 2 approvals, dismiss stale"
→ {"required_approving_review_count": 2, "dismiss_stale_reviews": true}
**Code owners**: "Require code owner review"
→ {"required_approving_review_count": 1, "require_code_owner_reviews": true, "dismiss_stale_reviews": true}
### REMEMBER
Return ONLY valid JSON - no explanations.`,
placeholder: 'Describe the PR review requirements...',
generationType: 'json-object',
},
},
// Issue operations parameters
{
@@ -662,91 +587,6 @@ Return ONLY valid JSON - no explanations.`,
type: 'long-input',
placeholder: 'Enter issue description (optional)',
condition: { field: 'operation', value: 'github_create_issue' },
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert at writing GitHub issue descriptions. Create clear, well-structured issues using GitHub markdown.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.error>\`, \`<function1.result>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{REPO_URL}}\`)
### GITHUB MARKDOWN
- **bold** with double asterisks
- *italic* with asterisks
- \`inline code\` with backticks
- \`\`\`language code block\`\`\`
- - [ ] task lists
- > blockquotes
- [link text](url)
- @mentions for users
- #123 for issue references
### GUIDELINES
1. **Problem Statement**: Clearly describe the issue
2. **Reproduction Steps**: For bugs, include steps to reproduce
3. **Expected vs Actual**: What should happen vs what happens
4. **Environment**: Include relevant version/environment info
5. **Screenshots/Logs**: Reference any attachments
### EXAMPLES
**Bug report**: "Create a bug report for API rate limiting"
→ ## Description
API requests are being rate limited incorrectly, causing valid requests to fail.
## Steps to Reproduce
1. Make 10 API requests within 1 minute
2. Observe that requests start failing after 5 requests
3. Check response headers for rate limit info
## Expected Behavior
Rate limit should be 100 requests per minute as documented.
## Actual Behavior
Rate limit appears to be 5 requests per minute.
## Environment
- API Version: v2.1
- Client: Node.js SDK v3.0.0
- Region: US-East
## Logs
\`\`\`
Error: Rate limit exceeded (5/5)
X-RateLimit-Remaining: 0
\`\`\`
**With variables**: "Create issue from error"
→ ## Automated Issue Report
### Error Details
- Service: <agent1.service>
- Error: <function1.error_message>
- Timestamp: <function1.timestamp>
### Stack Trace
\`\`\`
<agent1.stack_trace>
\`\`\`
### Impact
<agent1.impact_description>
### Suggested Actions
- [ ] Investigate root cause
- [ ] Implement fix
- [ ] Add tests
### REMEMBER
Use GitHub markdown. Include all relevant technical details.`,
placeholder: 'Describe the GitHub issue...',
generationType: 'markdown-content',
},
},
{
id: 'labels',
@@ -1009,45 +849,6 @@ Use GitHub markdown. Include all relevant technical details.`,
type: 'long-input',
placeholder: 'JSON: {"key":"value"}',
condition: { field: 'operation', value: 'github_trigger_workflow' },
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert GitHub Actions developer. Generate workflow inputs JSON for triggering a workflow dispatch.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.version>\`, \`<function1.result.tag>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{DEPLOY_ENV}}\`)
Do NOT wrap variable references in quotes for non-string values.
### CRITICAL INSTRUCTION
Return ONLY valid JSON. Do not include any explanations, markdown formatting, or comments.
### WORKFLOW INPUTS GUIDELINES
1. Keys must match the input names defined in the workflow's workflow_dispatch trigger
2. Values can be strings, numbers, or booleans depending on input type
3. Required inputs must be provided
### EXAMPLES
**Simple deploy**: "Deploy to production"
→ {"environment": "production", "dry_run": false}
**Version release**: "Release version 1.2.3"
→ {"version": "1.2.3", "create_tag": true}
**With variables**: "Deploy the version from previous block"
→ {"version": <agent1.version>, "environment": "{{DEPLOY_ENV}}"}
### REMEMBER
Return ONLY valid JSON - no explanations.`,
placeholder: 'Describe the workflow inputs...',
generationType: 'json-object',
},
},
{
id: 'workflow_id',

View File

@@ -76,52 +76,6 @@ export const GmailBlock: BlockConfig<GmailToolResponse> = {
placeholder: 'Email content',
condition: { field: 'operation', value: ['send_gmail', 'draft_gmail'] },
required: true,
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert email writer. Compose professional, clear, and effective email content.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.customer_name>\`, \`<function1.result.order_id>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{COMPANY_NAME}}\`, \`{{SUPPORT_EMAIL}}\`)
### GUIDELINES
1. **Tone**: Match the appropriate tone (formal, friendly, urgent) based on context
2. **Structure**: Use clear paragraphs, bullet points where helpful
3. **Brevity**: Be concise but complete
4. **Call to Action**: Include clear next steps when appropriate
5. **Personalization**: Use variable references for dynamic content
### EXAMPLES
**Professional inquiry**: "Ask about project status"
→ Hi [Name],
I hope this email finds you well. I wanted to follow up on the project we discussed last week.
Could you provide an update on the current status and expected timeline? Please let me know if there are any blockers I can help address.
Best regards
**With variables**: "Send order confirmation"
→ Dear <agent1.customer_name>,
Thank you for your order #<function1.order_id>!
Your order has been confirmed and will be shipped within 2-3 business days.
Best,
{{COMPANY_NAME}}
### REMEMBER
Write the email body only - no subject line unless specifically requested.`,
placeholder: 'Describe the email you want to write...',
generationType: 'email-content',
},
},
{
id: 'contentType',

View File

@@ -74,60 +74,6 @@ export const GoogleCalendarBlock: BlockConfig<GoogleCalendarResponse> = {
type: 'long-input',
placeholder: 'Event description',
condition: { field: 'operation', value: 'create' },
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert at writing calendar event descriptions. Create clear, informative event details.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.agenda>\`, \`<function1.result>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{MEETING_LINK}}\`)
### GUIDELINES
1. **Purpose**: Clearly state the meeting purpose
2. **Agenda**: Include key discussion points
3. **Preparation**: Note any required preparation
4. **Links**: Include relevant meeting links and documents
5. **Contact**: Add organizer contact if needed
### EXAMPLES
**Team meeting**: "Write description for weekly team sync"
→ Weekly Team Sync
Agenda:
• Project status updates
• Blockers and dependencies
• Upcoming deadlines
• Open discussion
Meeting Link: {{MEETING_LINK}}
Please come prepared with your updates for the week.
**With variables**: "Create event from request"
→ <agent1.meeting_title>
Purpose: <agent1.purpose>
Attendees: <function1.attendees>
Agenda:
<agent1.agenda_items>
Documents: <function1.doc_links>
Questions? Contact <agent1.organizer_email>
### REMEMBER
Be concise. Include all necessary details for attendees.`,
placeholder: 'Describe the event details...',
generationType: 'markdown-content',
},
},
{
id: 'location',

View File

@@ -107,73 +107,6 @@ export const GoogleDocsBlock: BlockConfig<GoogleDocsResponse> = {
placeholder: 'Enter document content',
condition: { field: 'operation', value: 'write' },
required: true,
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert document writer. Create clear, professional content for Google Docs.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.data>\`, \`<function1.result>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{COMPANY_NAME}}\`)
### GUIDELINES
1. **Structure**: Use clear headings and sections
2. **Professional Tone**: Write clearly and professionally
3. **Formatting**: Use paragraphs, lists, and emphasis
4. **Completeness**: Include all relevant information
5. **Readability**: Keep sentences and paragraphs concise
### EXAMPLES
**Report**: "Write a weekly status report"
→ Weekly Status Report
Week of January 15, 2024
Executive Summary
This week saw significant progress on our key initiatives with completion of Phase 1 development.
Accomplishments
• Completed user authentication module
• Deployed staging environment
• Conducted security review
In Progress
• Database optimization (75% complete)
• API documentation updates
• Performance testing
Blockers
• Awaiting design approval for dashboard redesign
Next Week's Priorities
1. Complete database optimization
2. Begin Phase 2 development
3. Stakeholder review meeting
**With variables**: "Create document from data"
→ <agent1.document_title>
Date: <function1.date>
Author: {{AUTHOR_NAME}}
Overview
<agent1.summary>
Details
<function1.content>
Next Steps
<agent1.action_items>
### REMEMBER
Write clear, professional content. Structure with headings and lists.`,
placeholder: 'Describe the document content...',
generationType: 'markdown-content',
},
},
// Content Field for create operation
{
@@ -182,30 +115,6 @@ Write clear, professional content. Structure with headings and lists.`,
type: 'long-input',
placeholder: 'Enter document content',
condition: { field: 'operation', value: 'create' },
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert document writer. Create clear, professional content for Google Docs.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax
### GUIDELINES
1. Use clear headings and sections
2. Write professionally and concisely
3. Use bullet points and numbered lists
4. Include all relevant information
### REMEMBER
Write clear, professional content. Structure with headings and lists.`,
placeholder: 'Describe the document content...',
generationType: 'markdown-content',
},
},
],
tools: {

View File

@@ -83,48 +83,6 @@ export const GoogleSheetsBlock: BlockConfig<GoogleSheetsResponse> = {
'Enter values as JSON array of arrays (e.g., [["A1", "B1"], ["A2", "B2"]]) or an array of objects (e.g., [{"name":"John", "age":30}, {"name":"Jane", "age":25}])',
condition: { field: 'operation', value: 'write' },
required: true,
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert at Google Sheets data formatting. Generate JSON data for writing to Google Sheets.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.data>\`, \`<function1.result.rows>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax
Do NOT wrap variable references in quotes for non-string values.
### CRITICAL INSTRUCTION
Return ONLY a valid JSON array. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
### DATA FORMATS
**Array of Arrays** (Row-based):
[["Header1", "Header2"], ["Value1", "Value2"]]
**Array of Objects** (Column-mapped):
[{"name": "John", "age": 30}, {"name": "Jane", "age": 25}]
### EXAMPLES
**Simple write**: "Write headers and two rows of data"
→ [["Name", "Email", "Age"], ["John Doe", "john@example.com", 30], ["Jane Smith", "jane@example.com", 25]]
**With variables**: "Write data from previous block"
→ [["Name", "Status"], [<agent1.name>, <agent1.status>]]
**Object format**: "Write user records"
→ [{"name": "John", "email": "john@example.com"}, {"name": "Jane", "email": "jane@example.com"}]
### REMEMBER
Return ONLY valid JSON array - no explanations, no markdown, no extra text.`,
placeholder: 'Describe the data you want to write...',
generationType: 'json-array',
},
},
{
id: 'valueInputOption',
@@ -145,37 +103,6 @@ Return ONLY valid JSON array - no explanations, no markdown, no extra text.`,
'Enter values as JSON array of arrays (e.g., [["A1", "B1"], ["A2", "B2"]]) or an array of objects (e.g., [{"name":"John", "age":30}, {"name":"Jane", "age":25}])',
condition: { field: 'operation', value: 'update' },
required: true,
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert at Google Sheets data formatting. Generate JSON data for updating Google Sheets.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax.
Environment variables use \`{{ENV_VAR_NAME}}\` syntax.
### CRITICAL INSTRUCTION
Return ONLY a valid JSON array. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
### DATA FORMATS
**Array of Arrays** (Row-based): [["Value1", "Value2"], ["Value3", "Value4"]]
**Array of Objects** (Column-mapped): [{"col1": "val1"}, {"col1": "val2"}]
### EXAMPLES
**Update cells**: "Update with new values"
→ [["Updated Name", "Updated Email"], ["John Doe", "john.new@example.com"]]
**With variables**: "Update with data from previous block"
→ [[<agent1.name>, <agent1.email>]]
### REMEMBER
Return ONLY valid JSON array - no explanations.`,
placeholder: 'Describe the data you want to update...',
generationType: 'json-array',
},
},
{
id: 'valueInputOption',
@@ -196,37 +123,6 @@ Return ONLY valid JSON array - no explanations.`,
'Enter values as JSON array of arrays (e.g., [["A1", "B1"], ["A2", "B2"]]) or an array of objects (e.g., [{"name":"John", "age":30}, {"name":"Jane", "age":25}])',
condition: { field: 'operation', value: 'append' },
required: true,
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert at Google Sheets data formatting. Generate JSON data for appending to Google Sheets.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax.
Environment variables use \`{{ENV_VAR_NAME}}\` syntax.
### CRITICAL INSTRUCTION
Return ONLY a valid JSON array. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
### DATA FORMATS
**Array of Arrays** (Row-based): [["Value1", "Value2"], ["Value3", "Value4"]]
**Array of Objects** (Column-mapped): [{"col1": "val1"}, {"col1": "val2"}]
### EXAMPLES
**Append rows**: "Add new rows to the sheet"
→ [["New Entry", "new@email.com", "Active"]]
**With variables**: "Append data from previous block"
→ [[<agent1.name>, <agent1.email>, <agent1.status>]]
### REMEMBER
Return ONLY valid JSON array - no explanations.`,
placeholder: 'Describe the data you want to append...',
generationType: 'json-array',
},
},
{
id: 'valueInputOption',

View File

@@ -201,44 +201,6 @@ export const GoogleSlidesBlock: BlockConfig<GoogleSlidesResponse> = {
placeholder: 'JSON array: [{"layoutPlaceholder":{"type":"TITLE"},"objectId":"my_title"}]',
condition: { field: 'operation', value: 'add_slide' },
mode: 'advanced',
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert Google Slides API developer. Generate placeholder ID mappings JSON for adding slides.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax for dynamic object IDs.
### CRITICAL INSTRUCTION
Return ONLY a valid JSON array. Do not include any explanations, markdown formatting, or comments.
### PLACEHOLDER TYPES
- TITLE: Main title placeholder
- SUBTITLE: Subtitle placeholder
- BODY: Main body text placeholder
- CENTERED_TITLE: Centered title
- HEADER: Header area
- FOOTER: Footer area
### FORMAT
Each mapping needs: "layoutPlaceholder" with "type", and "objectId" (your custom ID)
### EXAMPLES
**Title and body**: "Map title and body placeholders"
→ [{"layoutPlaceholder": {"type": "TITLE"}, "objectId": "my_title_1"}, {"layoutPlaceholder": {"type": "BODY"}, "objectId": "my_body_1"}]
**Just title**: "Map only the title placeholder"
→ [{"layoutPlaceholder": {"type": "TITLE"}, "objectId": "slide_title"}]
### REMEMBER
Return ONLY valid JSON array - no explanations.`,
placeholder: 'Describe the placeholder mappings...',
generationType: 'json-array',
},
},
// ========== Add Image Operation Fields ==========

View File

@@ -154,58 +154,6 @@ export const GrafanaBlock: BlockConfig<GrafanaResponse> = {
field: 'operation',
value: ['grafana_create_dashboard', 'grafana_update_dashboard'],
},
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert Grafana developer. Generate Grafana panel configuration JSON array.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.metric_name>\`, \`<function1.result.query>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{DATASOURCE_UID}}\`, \`{{DASHBOARD_TITLE}}\`)
Do NOT wrap variable references in quotes for non-string values.
### CRITICAL INSTRUCTION
Return ONLY a valid JSON array of panel objects. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
### PANEL STRUCTURE
Each panel typically has:
- **id**: Unique panel ID (number)
- **type**: Panel type (graph, stat, gauge, table, text, etc.)
- **title**: Panel title
- **gridPos**: Position {x, y, w, h}
- **targets**: Array of data source queries
- **options**: Panel-specific options
### EXAMPLES
**Simple stat panel**: "Create a panel showing total users"
→ [{
"id": 1,
"type": "stat",
"title": "Total Users",
"gridPos": {"x": 0, "y": 0, "w": 6, "h": 4},
"targets": [{"refId": "A", "expr": "sum(users_total)"}]
}]
**With variables**: "Create panel with dynamic title and query"
→ [{
"id": 1,
"type": "timeseries",
"title": <agent1.panel_title>,
"gridPos": {"x": 0, "y": 0, "w": 12, "h": 8},
"targets": [{"refId": "A", "expr": <agent1.prometheus_query>}]
}]
### REMEMBER
Return ONLY a valid JSON array - no explanations, no markdown, no extra text.`,
placeholder: 'Describe the panels you want to create...',
generationType: 'json-array',
},
},
{
id: 'message',
@@ -279,60 +227,6 @@ Return ONLY a valid JSON array - no explanations, no markdown, no extra text.`,
field: 'operation',
value: ['grafana_create_alert_rule', 'grafana_update_alert_rule'],
},
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert Grafana developer. Generate Grafana alert rule query data JSON array.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.query>\`, \`<function1.result.threshold>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{PROMETHEUS_UID}}\`, \`{{ALERT_THRESHOLD}}\`)
Do NOT wrap variable references in quotes for non-string values.
### CRITICAL INSTRUCTION
Return ONLY a valid JSON array of query/expression data objects. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
### QUERY DATA STRUCTURE
Each query/expression object has:
- **refId**: Reference ID (A, B, C, etc.)
- **queryType**: Type of query
- **relativeTimeRange**: Time range {from, to} in seconds
- **datasourceUid**: Data source UID
- **model**: Query model specific to the data source
### EXAMPLES
**Prometheus query**: "Alert when CPU is above 80%"
→ [
{
"refId": "A",
"queryType": "instant",
"relativeTimeRange": {"from": 600, "to": 0},
"datasourceUid": "prometheus",
"model": {"expr": "avg(cpu_usage_percent) > 80", "intervalMs": 1000}
}
]
**With variables**: "Alert with dynamic threshold"
→ [
{
"refId": "A",
"relativeTimeRange": {"from": 600, "to": 0},
"datasourceUid": "{{PROMETHEUS_UID}}",
"model": {"expr": <agent1.prometheus_query>, "intervalMs": 1000}
}
]
### REMEMBER
Return ONLY a valid JSON array - no explanations, no markdown, no extra text.`,
placeholder: 'Describe the alert query data...',
generationType: 'json-array',
},
},
{
id: 'forDuration',

View File

@@ -1,5 +1,5 @@
import { ShieldCheckIcon } from '@/components/icons'
import { isHosted } from '@/lib/core/config/environment'
import { isHosted } from '@/lib/core/config/feature-flags'
import type { BlockConfig } from '@/blocks/types'
import { getHostedModels, getProviderIcon } from '@/providers/utils'
import { useProvidersStore } from '@/stores/providers/store'

View File

@@ -126,13 +126,6 @@ export const HubSpotBlock: BlockConfig<HubSpotResponse> = {
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.name>\`, \`<function1.result.email>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{DEFAULT_OWNER_ID}}\`)
Do NOT wrap variable references in quotes for non-string values.
### CRITICAL INSTRUCTION
Return ONLY the JSON object with HubSpot properties. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object that can be used directly in HubSpot API create/update operations.
@@ -319,13 +312,6 @@ Return ONLY the JSON object with properties - no explanations, no markdown, no e
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.email>\`, \`<function1.result.status>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{DEFAULT_LIFECYCLE_STAGE}}\`)
Do NOT wrap variable references in quotes for non-string values.
### CRITICAL INSTRUCTION
Return ONLY the JSON array of filter groups. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array that can be used directly in HubSpot API search operations.

View File

@@ -160,46 +160,6 @@ export const IntercomBlock: BlockConfig = {
field: 'operation',
value: ['create_contact', 'update_contact'],
},
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert Intercom developer. Generate custom attributes JSON for Intercom contacts.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.plan_type>\`, \`<function1.result.company>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{DEFAULT_PLAN}}\`)
Do NOT wrap variable references in quotes for non-string values.
### CRITICAL INSTRUCTION
Return ONLY valid JSON. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object.
### CUSTOM ATTRIBUTES GUIDELINES
1. **Types**: Strings, numbers, booleans, dates (as Unix timestamps)
2. **Naming**: Use snake_case or lowercase with underscores
3. **Limits**: String values max 255 characters
4. **Pre-defined**: Must be created in Intercom first if strict mode is enabled
### EXAMPLES
**User attributes**: "Set plan and company info"
→ {"plan_type": "enterprise", "company_name": "Acme Corp", "seats": 50}
**With variables**: "Use data from previous block"
→ {"subscription_id": <agent1.sub_id>, "mrr": <function1.monthly_revenue>, "industry": <agent1.company_industry>}
**Boolean flags**: "Set feature flags"
→ {"has_completed_onboarding": true, "beta_tester": false, "vip_customer": true}
### REMEMBER
Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
placeholder: 'Describe the custom attributes...',
generationType: 'json-object',
},
},
{
id: 'query',
@@ -211,46 +171,6 @@ Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
field: 'operation',
value: ['search_contacts', 'search_conversations'],
},
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert Intercom developer. Generate Intercom search queries.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax.
Environment variables use \`{{ENV_VAR_NAME}}\` syntax.
### CRITICAL INSTRUCTION
Return ONLY the search query (JSON object for advanced search, or text for simple search). Do not include any explanations.
### INTERCOM SEARCH QUERY GUIDELINES
1. **Simple Text**: Just enter text for basic search
2. **Advanced**: JSON object with "query" containing filter/operator structure
3. **Operators**: field, operator (=, !=, IN, NIN, <, >, ~, !~, ^, $), value
4. **Combining**: Use AND/OR operators to combine conditions
### EXAMPLES
**Simple search**: "Find contacts with example.com email"
→ example.com
**By email**: "Find contact with specific email"
→ {"query": {"field": "email", "operator": "=", "value": "user@example.com"}}
**Multiple conditions**: "Find VIP customers from Acme"
→ {"query": {"operator": "AND", "value": [{"field": "custom_attributes.vip_customer", "operator": "=", "value": true}, {"field": "custom_attributes.company_name", "operator": "=", "value": "Acme"}]}}
**With variables**: "Search for contact by email from previous block"
→ {"query": {"field": "email", "operator": "=", "value": <agent1.email>}}
### REMEMBER
Return ONLY the query - no explanations.`,
placeholder: 'Describe what you want to search for...',
generationType: 'json-object',
},
},
// Company fields
{

View File

@@ -193,81 +193,6 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
placeholder: 'Enter new description for the issue',
dependsOn: ['projectId'],
condition: { field: 'operation', value: ['update', 'write'] },
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert at writing Jira issue descriptions. Create clear, well-structured descriptions.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.error>\`, \`<function1.result>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{ENVIRONMENT}}\`)
### JIRA FORMATTING
Jira uses its own wiki-style markup:
- *bold* or **bold**
- _italic_
- h1. Header 1, h2. Header 2, etc.
- * bullet points
- # numbered lists
- {code}code block{code}
- [link text|URL]
### GUIDELINES
1. **Structure**: Use headers and bullet points
2. **Context**: Include background and impact
3. **Acceptance Criteria**: Define completion criteria
4. **Technical Details**: Include relevant technical info
5. **Reproducibility**: For bugs, include steps to reproduce
### EXAMPLES
**Bug ticket**: "Write a bug description for API timeout"
→ h2. Description
API requests to the user service are timing out under high load.
h2. Steps to Reproduce
# Send 100+ concurrent requests to /api/users
# Observe response times
# Note timeout errors after ~30 seconds
h2. Expected Behavior
All requests should complete within 5 seconds.
h2. Actual Behavior
~20% of requests timeout with 504 errors.
h2. Impact
* Affects user registration flow
* Estimated 500 users impacted daily
h2. Technical Details
* Endpoint: /api/users
* Environment: Production
* Load balancer logs attached
**With variables**: "Create ticket from alert"
→ h2. Description
Automated ticket from monitoring alert.
h2. Alert Details
* Service: <agent1.service>
* Error: <function1.error_message>
* Time: <function1.timestamp>
h2. Actions Required
* Investigate root cause
* Implement fix
* Update monitoring
### REMEMBER
Use Jira wiki markup. Be thorough and structured.`,
placeholder: 'Describe the Jira issue...',
generationType: 'markdown-content',
},
},
// Delete Issue fields
{
@@ -314,77 +239,6 @@ Use Jira wiki markup. Be thorough and structured.`,
required: true,
placeholder: 'Enter JQL query (e.g., project = PROJ AND status = "In Progress")',
condition: { field: 'operation', value: 'search' },
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert Jira administrator. Generate JQL (Jira Query Language) queries based on the user's request.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.project_key>\`, \`<function1.result.assignee>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{DEFAULT_PROJECT}}\`)
### CRITICAL INSTRUCTION
Return ONLY the JQL query. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JQL query string.
### JQL SYNTAX GUIDE
**Field Operators**:
- **=** : Equals - \`status = "In Progress"\`
- **!=** : Not equals - \`status != Done\`
- **~** : Contains - \`summary ~ "bug"\`
- **!~** : Does not contain - \`summary !~ "test"\`
- **>**, **>=**, **<**, **<=** : Comparisons - \`created > -7d\`
- **IN** : In list - \`status IN ("Open", "In Progress")\`
- **NOT IN** : Not in list - \`priority NOT IN (Low, Lowest)\`
- **IS** : Is empty/null - \`assignee IS EMPTY\`
- **IS NOT** : Is not empty - \`resolution IS NOT EMPTY\`
- **WAS** : Historical value - \`status WAS "Open"\`
- **CHANGED** : Field changed - \`status CHANGED\`
**Logical Operators**:
- **AND** : Both conditions - \`project = PROJ AND status = Open\`
- **OR** : Either condition - \`priority = High OR priority = Critical\`
- **NOT** : Negate - \`NOT status = Closed\`
**Common Fields**:
- project, issuetype, status, priority, resolution
- assignee, reporter, creator, watcher
- created, updated, resolved, due, duedate
- summary, description, labels, components
- fixVersion, affectedVersion, sprint
**Date Functions**:
- \`startOfDay()\`, \`endOfDay()\`
- \`startOfWeek()\`, \`endOfWeek()\`
- \`startOfMonth()\`, \`endOfMonth()\`
- Relative: \`-7d\` (7 days ago), \`-2w\` (2 weeks ago)
### EXAMPLES
**Simple query**: "Find all open bugs in project PROJ"
→ project = PROJ AND issuetype = Bug AND status = Open
**With assignee**: "Find my tasks in progress"
→ assignee = currentUser() AND status = "In Progress"
**Date range**: "Find issues created this week"
→ created >= startOfWeek() AND created <= endOfWeek()
**Complex query**: "Find high priority unresolved issues assigned to me or unassigned"
→ priority IN (High, Critical) AND resolution IS EMPTY AND (assignee = currentUser() OR assignee IS EMPTY)
**With variables**: "Search for issues in a specific project"
→ project = <agent1.project_key> AND status != Done
### REMEMBER
Return ONLY the JQL query - no explanations, no markdown, no extra text.`,
placeholder: 'Describe the issues you want to search for...',
generationType: 'sql-query',
},
},
{
id: 'maxResults',
@@ -908,7 +762,6 @@ Return ONLY the JQL query - no explanations, no markdown, no extra text.`,
outputs: {
// Common outputs across all Jira operations
ts: { type: 'string', description: 'Timestamp of the operation' },
success: { type: 'boolean', description: 'Whether the operation was successful' },
// jira_retrieve (read) outputs
issueKey: { type: 'string', description: 'Issue key (e.g., PROJ-123)' },

View File

@@ -333,73 +333,6 @@ export const LinearBlock: BlockConfig<LinearResponse> = {
'linear_update_project',
],
},
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert at writing Linear issue and project descriptions. Create clear, well-structured descriptions using markdown.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.error_message>\`, \`<function1.result>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{REPO_URL}}\`)
### GUIDELINES
1. **Structure**: Use headers, bullet points, and sections
2. **Context**: Include relevant background information
3. **Acceptance Criteria**: For issues, define what "done" looks like
4. **Technical Details**: Include relevant technical information
5. **Links**: Reference related issues, PRs, or documentation
### EXAMPLES
**Bug report**: "Create a bug report for login issues"
→ ## Description
Users are experiencing intermittent login failures when using SSO authentication.
## Steps to Reproduce
1. Navigate to login page
2. Click "Sign in with SSO"
3. Complete authentication
4. Observe error message
## Expected Behavior
User should be logged in and redirected to dashboard.
## Actual Behavior
Error message "Authentication failed" appears approximately 30% of the time.
## Technical Details
- Browser: Chrome 120+
- Environment: Production
- Error code: AUTH_SSO_TIMEOUT
**With variables**: "Create issue from error"
→ ## Description
Automated issue created from error detection.
## Error Details
- Message: <agent1.error_message>
- Timestamp: <function1.timestamp>
- Service: <function1.service_name>
## Stack Trace
\`\`\`
<agent1.stack_trace>
\`\`\`
## Next Steps
- [ ] Investigate root cause
- [ ] Implement fix
- [ ] Add monitoring
### REMEMBER
Use markdown formatting. Be thorough but concise.`,
placeholder: 'Describe the issue or project...',
generationType: 'markdown-content',
},
},
// Comment body
{

View File

@@ -49,79 +49,6 @@ export const LinkedInBlock: BlockConfig<LinkedInResponse> = {
value: 'share_post',
},
required: true,
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert LinkedIn content creator. Write engaging, professional posts optimized for LinkedIn.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.topic>\`, \`<function1.result>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{COMPANY_NAME}}\`)
### LINKEDIN BEST PRACTICES
1. **Hook**: Start with an attention-grabbing first line
2. **Formatting**: Use line breaks for readability
3. **Length**: 1,300 characters max, but 150-300 often performs best
4. **Hashtags**: Use 3-5 relevant hashtags at the end
5. **CTA**: Include a call-to-action or question
6. **Emojis**: Use sparingly for visual interest
### GUIDELINES
- Write in first person for authenticity
- Share insights, not just promotions
- Ask questions to drive engagement
- Use numbers and data when relevant
- Break up text with white space
### EXAMPLES
**Thought leadership**: "Write a post about AI in business"
→ AI isn't replacing jobs.
It's replacing tasks.
The professionals who thrive in 2024 will be those who:
→ Learn to work WITH AI, not against it
→ Focus on skills AI can't replicate (creativity, empathy, leadership)
→ Continuously adapt and upskill
I've seen teams 10x their productivity by embracing AI tools.
But here's what nobody talks about: the human skills matter MORE now, not less.
What's your take? Is your company embracing AI or resisting it?
#AI #FutureOfWork #Leadership #Technology #Innovation
**With variables**: "Announce a product launch"
→ Exciting news! 🚀
We just launched <agent1.product_name> - and I couldn't be more proud of our team.
What makes it special:
✅ <agent1.feature_1>
✅ <agent1.feature_2>
✅ <agent1.feature_3>
This has been months in the making, and seeing it live is incredible.
Check it out: <function1.product_url>
What features would you like to see next? 👇
#ProductLaunch #{{COMPANY_NAME}} #Innovation
### REMEMBER
Write professional but personable content. Use line breaks and keep it scannable.`,
placeholder: 'Describe the LinkedIn post you want to create...',
generationType: 'social-post',
},
},
{
id: 'visibility',

View File

@@ -395,45 +395,6 @@ export const MailchimpBlock: BlockConfig = {
field: 'operation',
value: ['add_member', 'add_or_update_member', 'update_member'],
},
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert Mailchimp developer. Generate merge fields JSON for Mailchimp members.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.first_name>\`, \`<function1.result.phone>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{DEFAULT_SOURCE}}\`)
Do NOT wrap variable references in quotes for non-string values.
### CRITICAL INSTRUCTION
Return ONLY valid JSON. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object.
### MERGE FIELDS GUIDELINES
1. **Standard Fields**: FNAME, LNAME, PHONE, ADDRESS, BIRTHDAY
2. **Custom Fields**: Use your audience's custom merge field tags
3. **Format**: Key is merge tag (uppercase), value is the data
### EXAMPLES
**Basic info**: "Add first name and last name"
→ {"FNAME": "John", "LNAME": "Doe"}
**With address**: "Add full contact details"
→ {"FNAME": "John", "LNAME": "Doe", "PHONE": "555-1234", "ADDRESS": {"addr1": "123 Main St", "city": "Boston", "state": "MA", "zip": "02101"}}
**With variables**: "Use data from previous block"
→ {"FNAME": <agent1.first_name>, "LNAME": <agent1.last_name>, "EMAIL": <agent1.email>}
### REMEMBER
Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
placeholder: 'Describe the merge fields you want to set...',
generationType: 'json-object',
},
},
{
id: 'interests',
@@ -458,42 +419,6 @@ Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
field: 'operation',
value: ['add_member_tags', 'remove_member_tags'],
},
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert Mailchimp developer. Generate tags JSON for Mailchimp members.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax.
Environment variables use \`{{ENV_VAR_NAME}}\` syntax.
### CRITICAL INSTRUCTION
Return ONLY a valid JSON array of tag objects. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
### TAGS GUIDELINES
1. **Format**: Array of objects with "name" and "status" fields
2. **Status**: "active" to add tag, "inactive" to remove tag
3. **Name**: The tag name string
### EXAMPLES
**Add tags**: "Add VIP and Newsletter tags"
→ [{"name": "VIP", "status": "active"}, {"name": "Newsletter", "status": "active"}]
**Remove tags**: "Remove Inactive tag"
→ [{"name": "Inactive", "status": "inactive"}]
**With variables**: "Add tag from previous block"
→ [{"name": <agent1.tag_name>, "status": "active"}]
### REMEMBER
Return ONLY valid JSON array - no explanations.`,
placeholder: 'Describe the tags you want to add or remove...',
generationType: 'json-array',
},
},
// Campaign fields
{
@@ -976,50 +901,6 @@ Return ONLY valid JSON array - no explanations.`,
field: 'operation',
value: ['create_batch_operation'],
},
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert Mailchimp developer. Generate batch operations JSON for Mailchimp API.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.email>\`, \`<function1.result.list_id>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{LIST_ID}}\`)
### CRITICAL INSTRUCTION
Return ONLY a valid JSON array of operation objects. Do not include any explanations, markdown formatting, or comments.
### BATCH OPERATION STRUCTURE
Each operation object should have:
- **method**: HTTP method (GET, POST, PUT, PATCH, DELETE)
- **path**: API endpoint path (e.g., /lists/{list_id}/members)
- **operation_id**: Unique identifier for the operation (optional)
- **params**: Query parameters (optional)
- **body**: Request body as JSON string (for POST/PUT/PATCH)
### EXAMPLES
**Add multiple members**: "Add 3 members to a list"
→ [
{"method": "POST", "path": "/lists/abc123/members", "body": "{\\"email_address\\":\\"john@example.com\\",\\"status\\":\\"subscribed\\"}"},
{"method": "POST", "path": "/lists/abc123/members", "body": "{\\"email_address\\":\\"jane@example.com\\",\\"status\\":\\"subscribed\\"}"},
{"method": "POST", "path": "/lists/abc123/members", "body": "{\\"email_address\\":\\"bob@example.com\\",\\"status\\":\\"subscribed\\"}"}
]
**Update multiple members**: "Update member statuses"
→ [
{"method": "PATCH", "path": "/lists/abc123/members/hash1", "body": "{\\"status\\":\\"unsubscribed\\"}"},
{"method": "PATCH", "path": "/lists/abc123/members/hash2", "body": "{\\"status\\":\\"unsubscribed\\"}"}
]
### REMEMBER
Return ONLY valid JSON array - no explanations.`,
placeholder: 'Describe the batch operations you want to perform...',
generationType: 'json-array',
},
},
// Pagination and filtering
{

View File

@@ -44,51 +44,6 @@ export const Mem0Block: BlockConfig<Mem0Response> = {
value: 'add',
},
required: true,
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert at creating Mem0 memory messages. Generate a JSON array of message objects for storing memories.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.content>\`, \`<function1.result>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{USER_NAME}}\`)
Do NOT wrap variable references in quotes for non-string values.
### CRITICAL INSTRUCTION
Return ONLY a valid JSON array of message objects. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
### MESSAGE STRUCTURE
Each message must have:
- **role**: Either "user" or "assistant"
- **content**: The message text/content
### EXAMPLES
**Single user memory**: "Remember that the user's favorite color is blue"
→ [{"role": "user", "content": "My favorite color is blue"}]
**With variables**: "Store the conversation from previous agent"
→ [
{"role": "user", "content": <agent1.user_message>},
{"role": "assistant", "content": <agent1.response>}
]
**Multiple facts**: "Remember user likes coffee and works remotely"
→ [
{"role": "user", "content": "I love drinking coffee every morning"},
{"role": "user", "content": "I work remotely from home"}
]
### REMEMBER
Return ONLY a valid JSON array of message objects - no explanations, no markdown, no extra text.`,
placeholder: 'Describe the memories you want to store...',
generationType: 'json-array',
},
},
{
id: 'query',

View File

@@ -95,45 +95,6 @@ export const MicrosoftExcelBlock: BlockConfig<MicrosoftExcelResponse> = {
'Enter values as JSON array of arrays (e.g., [["A1", "B1"], ["A2", "B2"]]) or an array of objects (e.g., [{"name":"John", "age":30}, {"name":"Jane", "age":25}])',
condition: { field: 'operation', value: 'write' },
required: true,
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert at Microsoft Excel data formatting. Generate JSON data for writing to Excel.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.data>\`, \`<function1.result.rows>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax
Do NOT wrap variable references in quotes for non-string values.
### CRITICAL INSTRUCTION
Return ONLY a valid JSON array. Do not include any explanations, markdown formatting, or comments.
### DATA FORMATS
**Array of Arrays** (Row-based):
[["Header1", "Header2"], ["Value1", "Value2"]]
**Array of Objects** (Column-mapped):
[{"name": "John", "age": 30}, {"name": "Jane", "age": 25}]
### EXAMPLES
**Simple write**: "Write headers and data rows"
→ [["Name", "Email", "Age"], ["John Doe", "john@example.com", 30], ["Jane Smith", "jane@example.com", 25]]
**With variables**: "Write data from previous block"
→ [["Name", "Status"], [<agent1.name>, <agent1.status>]]
### REMEMBER
Return ONLY valid JSON array - no explanations.`,
placeholder: 'Describe the data you want to write...',
generationType: 'json-array',
},
},
{
id: 'valueInputOption',
@@ -153,33 +114,6 @@ Return ONLY valid JSON array - no explanations.`,
'Enter values as JSON array of arrays (e.g., [["A1", "B1"], ["A2", "B2"]]) or an array of objects (e.g., [{"name":"John", "age":30}, {"name":"Jane", "age":25}])',
condition: { field: 'operation', value: 'update' },
required: true,
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert at Microsoft Excel data formatting. Generate JSON data for updating Excel.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax.
Environment variables use \`{{ENV_VAR_NAME}}\` syntax.
### CRITICAL INSTRUCTION
Return ONLY a valid JSON array. Do not include any explanations, markdown formatting, or comments.
### EXAMPLES
**Update cells**: "Update with new values"
→ [["Updated Name", "Updated Email"], ["John Doe", "john.new@example.com"]]
**With variables**: "Update with data from previous block"
→ [[<agent1.name>, <agent1.email>]]
### REMEMBER
Return ONLY valid JSON array - no explanations.`,
placeholder: 'Describe the data you want to update...',
generationType: 'json-array',
},
},
{
id: 'valueInputOption',
@@ -199,33 +133,6 @@ Return ONLY valid JSON array - no explanations.`,
'Enter values as JSON array of arrays (e.g., [["A1", "B1"], ["A2", "B2"]]) or an array of objects (e.g., [{"name":"John", "age":30}, {"name":"Jane", "age":25}])',
condition: { field: 'operation', value: 'table_add' },
required: true,
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert at Microsoft Excel data formatting. Generate JSON data for adding rows to an Excel table.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax.
Environment variables use \`{{ENV_VAR_NAME}}\` syntax.
### CRITICAL INSTRUCTION
Return ONLY a valid JSON array. Do not include any explanations, markdown formatting, or comments.
### EXAMPLES
**Add rows**: "Add new data rows to the table"
→ [["John Doe", "john@example.com", "Active"], ["Jane Smith", "jane@example.com", "Active"]]
**With variables**: "Add data from previous block"
→ [[<agent1.name>, <agent1.email>, <agent1.status>]]
### REMEMBER
Return ONLY valid JSON array - no explanations.`,
placeholder: 'Describe the data you want to add...',
generationType: 'json-array',
},
},
],
tools: {

View File

@@ -245,44 +245,6 @@ export const MicrosoftPlannerBlock: BlockConfig<MicrosoftPlannerResponse> = {
type: 'long-input',
placeholder: 'Enter checklist as JSON object (optional)',
condition: { field: 'operation', value: ['update_task_details'] },
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert Microsoft Planner developer. Generate checklist JSON for Planner task details.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.item_title>\`, \`<function1.result.status>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax
Do NOT wrap variable references in quotes for non-string values.
### CRITICAL INSTRUCTION
Return ONLY valid JSON. Do not include any explanations, markdown formatting, or comments.
### CHECKLIST FORMAT
Microsoft Planner checklists use a specific JSON format where each item has a unique key (typically a GUID):
{
"item-id-1": {"@odata.type": "#microsoft.graph.plannerChecklistItem", "title": "Item 1", "isChecked": false},
"item-id-2": {"@odata.type": "#microsoft.graph.plannerChecklistItem", "title": "Item 2", "isChecked": true}
}
### EXAMPLES
**Simple checklist**: "Add two items - Review code and Write tests"
→ {"item1": {"@odata.type": "#microsoft.graph.plannerChecklistItem", "title": "Review code", "isChecked": false}, "item2": {"@odata.type": "#microsoft.graph.plannerChecklistItem", "title": "Write tests", "isChecked": false}}
**With variables**: "Add items from previous block"
→ {"item1": {"@odata.type": "#microsoft.graph.plannerChecklistItem", "title": <agent1.task_item>, "isChecked": false}}
### REMEMBER
Return ONLY valid JSON - no explanations.`,
placeholder: 'Describe the checklist items...',
generationType: 'json-object',
},
},
// References for task details
@@ -292,41 +254,6 @@ Return ONLY valid JSON - no explanations.`,
type: 'long-input',
placeholder: 'Enter references as JSON object (optional)',
condition: { field: 'operation', value: ['update_task_details'] },
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert Microsoft Planner developer. Generate references JSON for Planner task details.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.url>\`, \`<function1.result.link>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{DOCS_BASE_URL}}\`)
### CRITICAL INSTRUCTION
Return ONLY valid JSON. Do not include any explanations, markdown formatting, or comments.
### REFERENCES FORMAT
Microsoft Planner references use URL-encoded keys pointing to the reference URL:
{
"https%3A//example%2Ecom/doc": {"@odata.type": "#microsoft.graph.plannerExternalReference", "alias": "Doc Name", "type": "Other"},
"https%3A//github%2Ecom/repo": {"@odata.type": "#microsoft.graph.plannerExternalReference", "alias": "GitHub Repo", "type": "Other"}
}
Note: The key must be URL-encoded (: becomes %3A, . becomes %2E, / becomes %2F)
### EXAMPLES
**Add document link**: "Link to documentation"
→ {"https%3A%2F%2Fdocs%2Eexample%2Ecom%2Fguide": {"@odata.type": "#microsoft.graph.plannerExternalReference", "alias": "User Guide", "type": "Other"}}
### REMEMBER
Return ONLY valid JSON with URL-encoded keys - no explanations.`,
placeholder: 'Describe the references to add...',
generationType: 'json-object',
},
},
// Preview Type

View File

@@ -216,62 +216,6 @@ export const MicrosoftTeamsBlock: BlockConfig<MicrosoftTeamsResponse> = {
],
},
required: true,
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert at writing Microsoft Teams messages. Compose professional, clear messages for workplace communication.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.update>\`, \`<function1.result>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{TEAM_NAME}}\`)
### GUIDELINES
1. **Tone**: Professional workplace communication
2. **Structure**: Use clear paragraphs and bullet points
3. **Mentions**: Use @mentions when needed to notify team members
4. **Brevity**: Be concise but informative
5. **Action Items**: Clearly state any required actions
### EXAMPLES
**Team update**: "Share project status update"
→ 📊 Project Status Update
Hi team,
Here's our weekly progress update:
✅ Completed:
• Feature A development
• Code review for PR #123
🔄 In Progress:
• Testing phase
• Documentation updates
📅 Next Steps:
• Release scheduled for Friday
Let me know if you have questions!
**With variables**: "Share automated alert"
→ ⚠️ System Alert
Service: <agent1.service_name>
Status: <function1.status>
Time: <function1.timestamp>
Please review and take action if needed.
### REMEMBER
Write professional workplace messages. Be clear and actionable.`,
placeholder: 'Describe the Teams message you want to send...',
generationType: 'message-content',
},
},
{
id: 'reactionType',

View File

@@ -101,13 +101,6 @@ export const MongoDBBlock: BlockConfig<MongoDBResponse> = {
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.user_id>\`, \`<function1.result.status>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{DEFAULT_STATUS}}\`)
Do NOT wrap variable references in quotes for non-string values.
### CRITICAL INSTRUCTION
Return ONLY the MongoDB query filter as valid JSON. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object that can be used directly in a MongoDB find() operation.
@@ -231,13 +224,6 @@ Return ONLY the MongoDB query filter as valid JSON - no explanations, no markdow
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.status>\`, \`<function1.result.limit>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{DEFAULT_LIMIT}}\`)
Do NOT wrap variable references in quotes for non-string values.
### CRITICAL INSTRUCTION
Return ONLY the aggregation pipeline as a valid JSON array. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
@@ -478,13 +464,10 @@ Return ONLY the JSON array pipeline - no explanations, no markdown, no extra tex
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax.
### EXAMPLES
Newest first: {"createdAt": -1}
Alphabetical: {"name": 1}
Dynamic field: {<agent1.sort_field>: <agent1.sort_order>}
Multiple fields: {"category": 1, "price": -1}
Use 1 for ascending, -1 for descending. Return ONLY valid JSON.`,
placeholder: 'Describe how you want to sort the results...',
@@ -506,13 +489,9 @@ Use 1 for ascending, -1 for descending. Return ONLY valid JSON.`,
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks using \`<block_name.field_name>\` syntax.
Environment variables use \`{{ENV_VAR_NAME}}\` syntax.
### EXAMPLES
Simple user: [{"name": "John Doe", "email": "john@example.com", "active": true}]
With variables: [{"name": <agent1.name>, "email": <agent1.email>, "source": "{{DATA_SOURCE}}"}]
With nested data: [{"user": {"name": "Jane", "profile": {"age": 25, "city": "NYC"}}, "status": "active"}]
Multiple docs: [{"name": "User1", "type": "admin"}, {"name": "User2", "type": "user"}]
Return ONLY valid JSON array - no explanations.`,
@@ -535,11 +514,6 @@ Return ONLY valid JSON array - no explanations.`,
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.user_id>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax
### CRITICAL INSTRUCTION
Return ONLY the MongoDB query filter as valid JSON. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object that will identify which documents to update.
@@ -731,11 +705,6 @@ Generate the MongoDB update operation that safely and accurately fulfills the us
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.user_id>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax
### CRITICAL INSTRUCTION
Return ONLY the MongoDB query filter as valid JSON. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object that will identify which documents to delete.

View File

@@ -256,47 +256,6 @@ Return ONLY the SQL query - no explanations, no markdown, no extra text.`,
placeholder: '{\n "name": "John Doe",\n "email": "john@example.com",\n "active": true\n}',
condition: { field: 'operation', value: 'insert' },
required: true,
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert MySQL developer. Generate JSON data for INSERT operations.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.content>\`, \`<function1.result.id>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{API_KEY}}\`, \`{{DATABASE_NAME}}\`)
Do NOT wrap variable references in quotes - they will be resolved before execution.
### CRITICAL INSTRUCTION
Return ONLY the data as valid JSON. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object.
### DATA GUIDELINES
1. **Structure**: Use field-value pairs matching your table columns
2. **Data Types**: Use appropriate types (strings, numbers, booleans, null)
3. **Naming**: Use column names exactly as they appear in your table
4. **Required Fields**: Include all required (NOT NULL) columns
5. **MySQL Types**: Use 1/0 or true/false for booleans, ISO date strings for dates
### EXAMPLES
**Simple insert**: "Insert user with name, email, and active status"
→ {"name": "John Doe", "email": "john@example.com", "active": true}
**With variables**: "Insert user data from previous agent block"
→ {"name": <agent1.name>, "email": <agent1.email>, "created_by": "{{SYSTEM_USER}}"}
**With dates**: "Insert order with customer_id, total, and order_date"
→ {"customer_id": 123, "total": 99.99, "order_date": "2024-01-15"}
### REMEMBER
Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
placeholder: 'Describe the data you want to insert...',
generationType: 'json-object',
},
},
// Set clause for updates
{
@@ -306,46 +265,6 @@ Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
placeholder: '{\n "name": "Jane Doe",\n "email": "jane@example.com"\n}',
condition: { field: 'operation', value: 'update' },
required: true,
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert MySQL developer. Generate JSON data for UPDATE operations.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.content>\`, \`<function1.result.id>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{API_KEY}}\`, \`{{DEFAULT_STATUS}}\`)
Do NOT wrap variable references in quotes - they will be resolved before execution.
### CRITICAL INSTRUCTION
Return ONLY the data as valid JSON. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object containing only the fields to update.
### DATA GUIDELINES
1. **Structure**: Include only the fields you want to update
2. **Data Types**: Use appropriate types (strings, numbers, booleans, null)
3. **Naming**: Use column names exactly as they appear in your table
4. **Partial Updates**: You don't need to include all columns, only those being changed
### EXAMPLES
**Update single field**: "Update user's email"
→ {"email": "newemail@example.com"}
**With variables**: "Update user with data from previous block"
→ {"email": <agent1.new_email>, "updated_at": <function1.timestamp>}
**Set to null**: "Clear the user's phone number"
→ {"phone": null}
### REMEMBER
Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
placeholder: 'Describe the fields you want to update...',
generationType: 'json-object',
},
},
// Where clause for update/delete
{

View File

@@ -92,13 +92,6 @@ export const Neo4jBlock: BlockConfig<Neo4jResponse> = {
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.user_id>\`, \`<function1.result.name>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{DEFAULT_LIMIT}}\`)
Use parameters ($paramName) for values in Cypher queries and define them in the Parameters field.
### CRITICAL INSTRUCTION
Return ONLY the Cypher query. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw Cypher query.

View File

@@ -103,73 +103,6 @@ export const NotionBlock: BlockConfig<NotionResponse> = {
value: 'notion_write',
},
required: true,
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert at writing Notion content. Create well-structured, readable content using markdown.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.data>\`, \`<function1.result>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{TEAM_NAME}}\`)
### NOTION FORMATTING
Notion supports markdown:
- **bold** and *italic*
- # ## ### headers
- - bullet lists
- 1. numbered lists
- - [ ] todo items
- > callouts/quotes
- \`code\` and \`\`\`code blocks\`\`\`
- [links](url)
- --- dividers
### GUIDELINES
1. **Structure**: Use headers to organize content
2. **Scannable**: Use bullet points and short paragraphs
3. **Visual**: Use callouts and dividers for emphasis
4. **Actionable**: Include todo items where relevant
5. **Links**: Reference related pages or resources
### EXAMPLES
**Meeting notes**: "Write meeting notes template"
→ # Meeting Notes
## Date
<function1.date>
## Attendees
- <agent1.attendees>
## Agenda
1. Review previous action items
2. Project updates
3. New business
## Discussion Points
- Point 1
- Point 2
## Action Items
- [ ] Action 1 - @owner
- [ ] Action 2 - @owner
## Next Steps
Schedule follow-up for next week.
---
*Notes taken by {{USER_NAME}}*
### REMEMBER
Use markdown formatting. Create scannable, well-organized content.`,
placeholder: 'Describe the content you want to write...',
generationType: 'markdown-content',
},
},
{
id: 'content',
@@ -181,38 +114,6 @@ Use markdown formatting. Create scannable, well-organized content.`,
value: 'notion_create_page',
},
required: true,
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert at writing Notion page content. Create well-structured, readable content using markdown.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax
### NOTION FORMATTING
- **bold** and *italic*
- # ## ### headers
- - bullet lists and 1. numbered lists
- - [ ] todo items
- > callouts/quotes
- \`code\` and \`\`\`code blocks\`\`\`
### GUIDELINES
1. Use headers to organize content
2. Keep paragraphs short and scannable
3. Use bullet points for lists
4. Include action items where relevant
### REMEMBER
Use markdown. Create well-organized, readable content.`,
placeholder: 'Describe the page content...',
generationType: 'markdown-content',
},
},
// Query Database Fields
{
@@ -230,55 +131,6 @@ Use markdown. Create well-organized, readable content.`,
placeholder: 'Enter filter conditions as JSON (optional)',
condition: { field: 'operation', value: 'notion_query_database' },
required: true,
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert Notion API developer. Generate Notion database filter JSON based on the user's request.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.status>\`, \`<function1.result.date>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{DEFAULT_STATUS}}\`)
Do NOT wrap variable references in quotes for non-string values.
### CRITICAL INSTRUCTION
Return ONLY valid JSON filter. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object.
### NOTION FILTER SYNTAX
Filters use property-based conditions with operators:
- **equals, does_not_equal**: Exact match
- **contains, does_not_contain**: Text contains
- **starts_with, ends_with**: Text patterns
- **is_empty, is_not_empty**: Existence check
- **greater_than, less_than, etc.**: Comparisons for numbers/dates
Compound filters use:
- **and**: Array of conditions (all must match)
- **or**: Array of conditions (any must match)
### EXAMPLES
**Simple filter**: "Filter where Status is Done"
→ {"property": "Status", "status": {"equals": "Done"}}
**With variables**: "Filter by status from previous block"
→ {"property": "Status", "status": {"equals": <agent1.selected_status>}}
**AND conditions**: "Status is Active and Priority is High"
→ {"and": [
{"property": "Status", "status": {"equals": "Active"}},
{"property": "Priority", "select": {"equals": "High"}}
]}
### REMEMBER
Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
placeholder: 'Describe the filter conditions...',
generationType: 'json-object',
},
},
{
id: 'sorts',
@@ -286,49 +138,6 @@ Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
type: 'long-input',
placeholder: 'Enter sort criteria as JSON array (optional)',
condition: { field: 'operation', value: 'notion_query_database' },
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert Notion API developer. Generate Notion database sort criteria JSON array.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.sort_field>\`, \`<function1.result.direction>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{DEFAULT_SORT_FIELD}}\`)
Do NOT wrap variable references in quotes for non-string values.
### CRITICAL INSTRUCTION
Return ONLY a valid JSON array of sort objects. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON array.
### SORT STRUCTURE
Each sort object has:
- **property**: Property name to sort by
- **direction**: "ascending" or "descending"
Or for timestamp sorts:
- **timestamp**: "created_time" or "last_edited_time"
- **direction**: "ascending" or "descending"
### EXAMPLES
**Sort by property**: "Sort by Name ascending"
→ [{"property": "Name", "direction": "ascending"}]
**With variables**: "Sort by field from previous block"
→ [{"property": <agent1.sort_field>, "direction": <agent1.sort_direction>}]
**Sort by date**: "Sort by created time, newest first"
→ [{"timestamp": "created_time", "direction": "descending"}]
### REMEMBER
Return ONLY a valid JSON array - no explanations, no markdown, no extra text.`,
placeholder: 'Describe how you want to sort...',
generationType: 'json-array',
},
},
{
id: 'pageSize',
@@ -379,63 +188,6 @@ Return ONLY a valid JSON array - no explanations, no markdown, no extra text.`,
type: 'long-input',
placeholder: 'Enter database properties as JSON object',
condition: { field: 'operation', value: 'notion_create_database' },
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert Notion API developer. Generate Notion database properties schema JSON.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.property_name>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{DEFAULT_STATUS_OPTIONS}}\`)
Do NOT wrap variable references in quotes for non-string values.
### CRITICAL INSTRUCTION
Return ONLY valid JSON properties object. Do not include any explanations, markdown formatting, comments, or additional text. Just the raw JSON object.
### PROPERTY TYPES
Common Notion property types:
- **title**: Primary name field
- **rich_text**: Multi-line text
- **number**: Numeric value
- **select**: Single select dropdown
- **multi_select**: Multiple select tags
- **date**: Date/datetime
- **checkbox**: Boolean checkbox
- **url**: URL link
- **email**: Email address
- **phone_number**: Phone number
- **status**: Status field with groups
### EXAMPLES
**Basic task database**: "Create properties for a task tracker"
→ {
"Name": {"title": {}},
"Status": {"status": {}},
"Due Date": {"date": {}},
"Priority": {"select": {"options": [{"name": "High"}, {"name": "Medium"}, {"name": "Low"}]}},
"Completed": {"checkbox": {}}
}
**Contact database**: "Create properties for contacts"
→ {
"Name": {"title": {}},
"Email": {"email": {}},
"Phone": {"phone_number": {}},
"Company": {"rich_text": {}},
"Website": {"url": {}}
}
### REMEMBER
Return ONLY valid JSON - no explanations, no markdown, no extra text.`,
placeholder: 'Describe the database properties...',
generationType: 'json-object',
},
},
],
tools: {

View File

@@ -92,38 +92,10 @@ export const OneDriveBlock: BlockConfig<OneDriveResponse> = {
},
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert at Excel data formatting. Generate JSON data for creating an Excel file in OneDrive.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.data>\`, \`<function1.result.rows>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax
Do NOT wrap variable references in quotes for non-string values.
### CRITICAL INSTRUCTION
Return ONLY a valid JSON array of arrays. Do not include any explanations, markdown formatting, or comments.
### DATA FORMAT
Use array of arrays format where each inner array represents a row:
[["Header1", "Header2"], ["Value1", "Value2"], ["Value3", "Value4"]]
### EXAMPLES
**Simple table**: "Create a table with names and emails"
→ [["Name", "Email"], ["John Doe", "john@example.com"], ["Jane Smith", "jane@example.com"]]
**With variables**: "Create table from previous block data"
→ [["Name", "Status"], [<agent1.name>, <agent1.status>]]
### REMEMBER
Return ONLY valid JSON array of arrays - no explanations.`,
prompt:
'Generate a JSON array of arrays that can be written directly into an Excel worksheet.',
placeholder: 'Describe the table you want to generate...',
generationType: 'json-array',
generationType: 'json-object',
},
required: false,
},

View File

@@ -94,53 +94,6 @@ export const OutlookBlock: BlockConfig<OutlookResponse> = {
placeholder: 'Email content',
condition: { field: 'operation', value: ['send_outlook', 'draft_outlook'] },
required: true,
wandConfig: {
enabled: true,
maintainHistory: true,
prompt: `You are an expert email writer. Compose professional, clear, and effective email content.
### CONTEXT
{context}
### VARIABLE RESOLUTION
You can reference variables from previous blocks and environment variables:
- **Block variables**: Use \`<block_name.field_name>\` syntax (e.g., \`<agent1.customer_name>\`, \`<function1.result.details>\`)
- **Environment variables**: Use \`{{ENV_VAR_NAME}}\` syntax (e.g., \`{{COMPANY_NAME}}\`)
### GUIDELINES
1. **Tone**: Match the appropriate tone (formal, friendly, urgent) based on context
2. **Structure**: Use clear paragraphs, bullet points where helpful
3. **Brevity**: Be concise but complete
4. **Call to Action**: Include clear next steps when appropriate
### EXAMPLES
**Meeting request**: "Request a meeting to discuss Q4 planning"
→ Hi,
I'd like to schedule a meeting to discuss our Q4 planning and goals.
Would you be available this week for a 30-minute call? I'm flexible on timing and can adjust to your schedule.
Please let me know what works best for you.
Thanks
**With variables**: "Follow up on support ticket"
→ Dear <agent1.customer_name>,
Thank you for contacting us regarding ticket #<function1.ticket_id>.
Our team has reviewed your request and we're working on a resolution. You can expect an update within 24 hours.
Best regards,
{{COMPANY_NAME}} Support
### REMEMBER
Write the email body only - no subject line unless specifically requested.`,
placeholder: 'Describe the email you want to write...',
generationType: 'email-content',
},
},
{
id: 'contentType',

Some files were not shown because too many files have changed in this diff Show More