mirror of
https://github.com/simstudioai/sim.git
synced 2026-02-18 02:11:59 -05:00
v0.5.92: shortlinks, copilot scrolling stickiness, pagination
This commit is contained in:
@@ -59,12 +59,6 @@ body {
|
||||
--content-gap: 1.75rem;
|
||||
}
|
||||
|
||||
/* Remove custom layout variable overrides to fallback to fumadocs defaults */
|
||||
|
||||
/* ============================================
|
||||
Navbar Light Mode Styling
|
||||
============================================ */
|
||||
|
||||
/* Light mode navbar and search styling */
|
||||
:root:not(.dark) nav {
|
||||
background-color: hsla(0, 0%, 96%, 0.85) !important;
|
||||
@@ -88,10 +82,6 @@ body {
|
||||
-webkit-backdrop-filter: blur(25px) saturate(180%) brightness(0.6) !important;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
Custom Sidebar Styling (Turborepo-inspired)
|
||||
============================================ */
|
||||
|
||||
/* Floating sidebar appearance - remove background */
|
||||
[data-sidebar-container],
|
||||
#nd-sidebar {
|
||||
@@ -468,10 +458,6 @@ aside[data-sidebar],
|
||||
writing-mode: horizontal-tb !important;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
Code Block Styling (Improved)
|
||||
============================================ */
|
||||
|
||||
/* Apply Geist Mono to code elements */
|
||||
code,
|
||||
pre,
|
||||
@@ -532,10 +518,6 @@ pre code .line {
|
||||
color: var(--color-fd-primary);
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
TOC (Table of Contents) Styling
|
||||
============================================ */
|
||||
|
||||
/* Remove the thin border-left on nested TOC items (keeps main indicator only) */
|
||||
#nd-toc a[style*="padding-inline-start"] {
|
||||
border-left: none !important;
|
||||
@@ -554,10 +536,6 @@ main article,
|
||||
padding-bottom: 4rem;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
Center and Constrain Main Content Width
|
||||
============================================ */
|
||||
|
||||
/* Main content area - center and constrain like turborepo/raindrop */
|
||||
/* Note: --sidebar-offset and --toc-offset are now applied at #nd-docs-layout level */
|
||||
main[data-main] {
|
||||
|
||||
@@ -234,7 +234,6 @@ List actions from incident.io. Optionally filter by incident ID.
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | incident.io API Key |
|
||||
| `incident_id` | string | No | Filter actions by incident ID \(e.g., "01FCNDV6P870EA6S7TK1DSYDG0"\) |
|
||||
| `page_size` | number | No | Number of actions to return per page \(e.g., 10, 25, 50\) |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -309,7 +308,6 @@ List follow-ups from incident.io. Optionally filter by incident ID.
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | incident.io API Key |
|
||||
| `incident_id` | string | No | Filter follow-ups by incident ID \(e.g., "01FCNDV6P870EA6S7TK1DSYDG0"\) |
|
||||
| `page_size` | number | No | Number of follow-ups to return per page \(e.g., 10, 25, 50\) |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -396,6 +394,7 @@ List all users in your Incident.io workspace. Returns user details including id,
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Incident.io API Key |
|
||||
| `page_size` | number | No | Number of results to return per page \(e.g., 10, 25, 50\). Default: 25 |
|
||||
| `after` | string | No | Pagination cursor to fetch the next page of results |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -406,6 +405,10 @@ List all users in your Incident.io workspace. Returns user details including id,
|
||||
| ↳ `name` | string | Full name of the user |
|
||||
| ↳ `email` | string | Email address of the user |
|
||||
| ↳ `role` | string | Role of the user in the workspace |
|
||||
| `pagination_meta` | object | Pagination metadata |
|
||||
| ↳ `after` | string | Cursor for next page |
|
||||
| ↳ `page_size` | number | Number of items per page |
|
||||
| ↳ `total_record_count` | number | Total number of records |
|
||||
|
||||
### `incidentio_users_show`
|
||||
|
||||
@@ -644,7 +647,6 @@ List all escalation policies in incident.io
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | incident.io API Key |
|
||||
| `page_size` | number | No | Number of results per page \(e.g., 10, 25, 50\). Default: 25 |
|
||||
|
||||
#### Output
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ Retrieve all deals from Pipedrive with optional filters
|
||||
| `pipeline_id` | string | No | If supplied, only deals in the specified pipeline are returned \(e.g., "1"\) |
|
||||
| `updated_since` | string | No | If set, only deals updated after this time are returned. Format: 2025-01-01T10:20:00Z |
|
||||
| `limit` | string | No | Number of results to return \(e.g., "50", default: 100, max: 500\) |
|
||||
| `cursor` | string | No | For pagination, the marker representing the first item on the next page |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -74,6 +75,8 @@ Retrieve all deals from Pipedrive with optional filters
|
||||
| `metadata` | object | Pagination metadata for the response |
|
||||
| ↳ `total_items` | number | Total number of items |
|
||||
| ↳ `has_more` | boolean | Whether more items are available |
|
||||
| ↳ `next_cursor` | string | Cursor for fetching the next page \(v2 endpoints\) |
|
||||
| ↳ `next_start` | number | Offset for fetching the next page \(v1 endpoints\) |
|
||||
| `success` | boolean | Operation success status |
|
||||
|
||||
### `pipedrive_get_deal`
|
||||
@@ -148,10 +151,9 @@ Retrieve files from Pipedrive with optional filters
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `deal_id` | string | No | Filter files by deal ID \(e.g., "123"\) |
|
||||
| `person_id` | string | No | Filter files by person ID \(e.g., "456"\) |
|
||||
| `org_id` | string | No | Filter files by organization ID \(e.g., "789"\) |
|
||||
| `limit` | string | No | Number of results to return \(e.g., "50", default: 100, max: 500\) |
|
||||
| `sort` | string | No | Sort files by field \(supported: "id", "update_time"\) |
|
||||
| `limit` | string | No | Number of results to return \(e.g., "50", default: 100, max: 100\) |
|
||||
| `start` | string | No | Pagination start offset \(0-based index of the first item to return\) |
|
||||
| `downloadFiles` | boolean | No | Download file contents into file outputs |
|
||||
|
||||
#### Output
|
||||
@@ -171,6 +173,8 @@ Retrieve files from Pipedrive with optional filters
|
||||
| ↳ `url` | string | File download URL |
|
||||
| `downloadedFiles` | file[] | Downloaded files from Pipedrive |
|
||||
| `total_items` | number | Total number of files returned |
|
||||
| `has_more` | boolean | Whether more files are available |
|
||||
| `next_start` | number | Offset for fetching the next page |
|
||||
| `success` | boolean | Operation success status |
|
||||
|
||||
### `pipedrive_get_mail_messages`
|
||||
@@ -183,6 +187,7 @@ Retrieve mail threads from Pipedrive mailbox
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `folder` | string | No | Filter by folder: inbox, drafts, sent, archive \(default: inbox\) |
|
||||
| `limit` | string | No | Number of results to return \(e.g., "25", default: 50\) |
|
||||
| `start` | string | No | Pagination start offset \(0-based index of the first item to return\) |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -190,6 +195,8 @@ Retrieve mail threads from Pipedrive mailbox
|
||||
| --------- | ---- | ----------- |
|
||||
| `messages` | array | Array of mail thread objects from Pipedrive mailbox |
|
||||
| `total_items` | number | Total number of mail threads returned |
|
||||
| `has_more` | boolean | Whether more messages are available |
|
||||
| `next_start` | number | Offset for fetching the next page |
|
||||
| `success` | boolean | Operation success status |
|
||||
|
||||
### `pipedrive_get_mail_thread`
|
||||
@@ -221,7 +228,7 @@ Retrieve all pipelines from Pipedrive
|
||||
| `sort_by` | string | No | Field to sort by: id, update_time, add_time \(default: id\) |
|
||||
| `sort_direction` | string | No | Sorting direction: asc, desc \(default: asc\) |
|
||||
| `limit` | string | No | Number of results to return \(e.g., "50", default: 100, max: 500\) |
|
||||
| `cursor` | string | No | For pagination, the marker representing the first item on the next page |
|
||||
| `start` | string | No | Pagination start offset \(0-based index of the first item to return\) |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -237,6 +244,8 @@ Retrieve all pipelines from Pipedrive
|
||||
| ↳ `add_time` | string | When the pipeline was created |
|
||||
| ↳ `update_time` | string | When the pipeline was last updated |
|
||||
| `total_items` | number | Total number of pipelines returned |
|
||||
| `has_more` | boolean | Whether more pipelines are available |
|
||||
| `next_start` | number | Offset for fetching the next page |
|
||||
| `success` | boolean | Operation success status |
|
||||
|
||||
### `pipedrive_get_pipeline_deals`
|
||||
@@ -249,8 +258,8 @@ Retrieve all deals in a specific pipeline
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `pipeline_id` | string | Yes | The ID of the pipeline \(e.g., "1"\) |
|
||||
| `stage_id` | string | No | Filter by specific stage within the pipeline \(e.g., "2"\) |
|
||||
| `status` | string | No | Filter by deal status: open, won, lost |
|
||||
| `limit` | string | No | Number of results to return \(e.g., "50", default: 100, max: 500\) |
|
||||
| `start` | string | No | Pagination start offset \(0-based index of the first item to return\) |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -271,6 +280,7 @@ Retrieve all projects or a specific project from Pipedrive
|
||||
| `project_id` | string | No | Optional: ID of a specific project to retrieve \(e.g., "123"\) |
|
||||
| `status` | string | No | Filter by project status: open, completed, deleted \(only for listing all\) |
|
||||
| `limit` | string | No | Number of results to return \(e.g., "50", default: 100, max: 500, only for listing all\) |
|
||||
| `cursor` | string | No | For pagination, the marker representing the first item on the next page |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -279,6 +289,8 @@ Retrieve all projects or a specific project from Pipedrive
|
||||
| `projects` | array | Array of project objects \(when listing all\) |
|
||||
| `project` | object | Single project object \(when project_id is provided\) |
|
||||
| `total_items` | number | Total number of projects returned |
|
||||
| `has_more` | boolean | Whether more projects are available |
|
||||
| `next_cursor` | string | Cursor for fetching the next page |
|
||||
| `success` | boolean | Operation success status |
|
||||
|
||||
### `pipedrive_create_project`
|
||||
@@ -309,12 +321,11 @@ Retrieve activities (tasks) from Pipedrive with optional filters
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `deal_id` | string | No | Filter activities by deal ID \(e.g., "123"\) |
|
||||
| `person_id` | string | No | Filter activities by person ID \(e.g., "456"\) |
|
||||
| `org_id` | string | No | Filter activities by organization ID \(e.g., "789"\) |
|
||||
| `user_id` | string | No | Filter activities by user ID \(e.g., "123"\) |
|
||||
| `type` | string | No | Filter by activity type \(call, meeting, task, deadline, email, lunch\) |
|
||||
| `done` | string | No | Filter by completion status: 0 for not done, 1 for done |
|
||||
| `limit` | string | No | Number of results to return \(e.g., "50", default: 100, max: 500\) |
|
||||
| `start` | string | No | Pagination start offset \(0-based index of the first item to return\) |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -335,6 +346,8 @@ Retrieve activities (tasks) from Pipedrive with optional filters
|
||||
| ↳ `add_time` | string | When the activity was created |
|
||||
| ↳ `update_time` | string | When the activity was last updated |
|
||||
| `total_items` | number | Total number of activities returned |
|
||||
| `has_more` | boolean | Whether more activities are available |
|
||||
| `next_start` | number | Offset for fetching the next page |
|
||||
| `success` | boolean | Operation success status |
|
||||
|
||||
### `pipedrive_create_activity`
|
||||
@@ -399,6 +412,7 @@ Retrieve all leads or a specific lead from Pipedrive
|
||||
| `person_id` | string | No | Filter by person ID \(e.g., "456"\) |
|
||||
| `organization_id` | string | No | Filter by organization ID \(e.g., "789"\) |
|
||||
| `limit` | string | No | Number of results to return \(e.g., "50", default: 100, max: 500\) |
|
||||
| `start` | string | No | Pagination start offset \(0-based index of the first item to return\) |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -433,6 +447,8 @@ Retrieve all leads or a specific lead from Pipedrive
|
||||
| ↳ `add_time` | string | When the lead was created \(ISO 8601\) |
|
||||
| ↳ `update_time` | string | When the lead was last updated \(ISO 8601\) |
|
||||
| `total_items` | number | Total number of leads returned |
|
||||
| `has_more` | boolean | Whether more leads are available |
|
||||
| `next_start` | number | Offset for fetching the next page |
|
||||
| `success` | boolean | Operation success status |
|
||||
|
||||
### `pipedrive_create_lead`
|
||||
|
||||
@@ -57,6 +57,7 @@ Query data from a Supabase table
|
||||
| `filter` | string | No | PostgREST filter \(e.g., "id=eq.123"\) |
|
||||
| `orderBy` | string | No | Column to order by \(add DESC for descending\) |
|
||||
| `limit` | number | No | Maximum number of rows to return |
|
||||
| `offset` | number | No | Number of rows to skip \(for pagination\) |
|
||||
| `apiKey` | string | Yes | Your Supabase service role secret key |
|
||||
|
||||
#### Output
|
||||
@@ -211,6 +212,7 @@ Perform full-text search on a Supabase table
|
||||
| `searchType` | string | No | Search type: plain, phrase, or websearch \(default: websearch\) |
|
||||
| `language` | string | No | Language for text search configuration \(default: english\) |
|
||||
| `limit` | number | No | Maximum number of rows to return |
|
||||
| `offset` | number | No | Number of rows to skip \(for pagination\) |
|
||||
| `apiKey` | string | Yes | Your Supabase service role secret key |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -43,6 +43,8 @@ Retrieve form responses from Typeform
|
||||
| `formId` | string | Yes | Typeform form ID \(e.g., "abc123XYZ"\) |
|
||||
| `apiKey` | string | Yes | Typeform Personal Access Token |
|
||||
| `pageSize` | number | No | Number of responses to retrieve \(e.g., 10, 25, 50\) |
|
||||
| `before` | string | No | Cursor token for fetching the next page of older responses |
|
||||
| `after` | string | No | Cursor token for fetching the next page of newer responses |
|
||||
| `since` | string | No | Retrieve responses submitted after this date \(e.g., "2024-01-01T00:00:00Z"\) |
|
||||
| `until` | string | No | Retrieve responses submitted before this date \(e.g., "2024-12-31T23:59:59Z"\) |
|
||||
| `completed` | string | No | Filter by completion status \(e.g., "true", "false", "all"\) |
|
||||
|
||||
@@ -67,10 +67,9 @@ Retrieve a list of tickets from Zendesk with optional filtering
|
||||
| `type` | string | No | Filter by type: "problem", "incident", "question", or "task" |
|
||||
| `assigneeId` | string | No | Filter by assignee user ID as a numeric string \(e.g., "12345"\) |
|
||||
| `organizationId` | string | No | Filter by organization ID as a numeric string \(e.g., "67890"\) |
|
||||
| `sortBy` | string | No | Sort field: "created_at", "updated_at", "priority", or "status" |
|
||||
| `sortOrder` | string | No | Sort order: "asc" or "desc" |
|
||||
| `sort` | string | No | Sort field for ticket listing \(only applies without filters\): "updated_at", "id", or "status". Prefix with "-" for descending \(e.g., "-updated_at"\) |
|
||||
| `perPage` | string | No | Results per page as a number string \(default: "100", max: "100"\) |
|
||||
| `page` | string | No | Page number as a string \(e.g., "1", "2"\) |
|
||||
| `pageAfter` | string | No | Cursor from a previous response to fetch the next page of results |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -129,10 +128,10 @@ Retrieve a list of tickets from Zendesk with optional filtering
|
||||
| ↳ `from_messaging_channel` | boolean | Whether the ticket originated from a messaging channel |
|
||||
| ↳ `ticket_form_id` | number | Ticket form ID |
|
||||
| ↳ `generated_timestamp` | number | Unix timestamp of the ticket generation |
|
||||
| `paging` | object | Pagination information |
|
||||
| `paging` | object | Cursor-based pagination information |
|
||||
| ↳ `after_cursor` | string | Cursor for fetching the next page of results |
|
||||
| ↳ `has_more` | boolean | Whether more results are available |
|
||||
| ↳ `next_page` | string | URL for next page of results |
|
||||
| ↳ `previous_page` | string | URL for previous page of results |
|
||||
| ↳ `count` | number | Total count of items |
|
||||
| `metadata` | object | Response metadata |
|
||||
| ↳ `total_returned` | number | Number of items returned in this response |
|
||||
| ↳ `has_more` | boolean | Whether more items are available |
|
||||
@@ -515,7 +514,7 @@ Retrieve a list of users from Zendesk with optional filtering
|
||||
| `role` | string | No | Filter by role: "end-user", "agent", or "admin" |
|
||||
| `permissionSet` | string | No | Filter by permission set ID as a numeric string \(e.g., "12345"\) |
|
||||
| `perPage` | string | No | Results per page as a number string \(default: "100", max: "100"\) |
|
||||
| `page` | string | No | Page number as a string \(e.g., "1", "2"\) |
|
||||
| `pageAfter` | string | No | Cursor from a previous response to fetch the next page of results |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -563,10 +562,10 @@ Retrieve a list of users from Zendesk with optional filtering
|
||||
| ↳ `shared` | boolean | Whether the user is shared from a different Zendesk |
|
||||
| ↳ `shared_agent` | boolean | Whether the agent is shared from a different Zendesk |
|
||||
| ↳ `remote_photo_url` | string | URL to a remote photo |
|
||||
| `paging` | object | Pagination information |
|
||||
| `paging` | object | Cursor-based pagination information |
|
||||
| ↳ `after_cursor` | string | Cursor for fetching the next page of results |
|
||||
| ↳ `has_more` | boolean | Whether more results are available |
|
||||
| ↳ `next_page` | string | URL for next page of results |
|
||||
| ↳ `previous_page` | string | URL for previous page of results |
|
||||
| ↳ `count` | number | Total count of items |
|
||||
| `metadata` | object | Response metadata |
|
||||
| ↳ `total_returned` | number | Number of items returned in this response |
|
||||
| ↳ `has_more` | boolean | Whether more items are available |
|
||||
@@ -706,7 +705,7 @@ Search for users in Zendesk using a query string
|
||||
| `query` | string | No | Search query string \(e.g., user name or email\) |
|
||||
| `externalId` | string | No | External ID to search by \(your system identifier\) |
|
||||
| `perPage` | string | No | Results per page as a number string \(default: "100", max: "100"\) |
|
||||
| `page` | string | No | Page number as a string \(e.g., "1", "2"\) |
|
||||
| `page` | string | No | Page number for pagination \(1-based\) |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -754,10 +753,10 @@ Search for users in Zendesk using a query string
|
||||
| ↳ `shared` | boolean | Whether the user is shared from a different Zendesk |
|
||||
| ↳ `shared_agent` | boolean | Whether the agent is shared from a different Zendesk |
|
||||
| ↳ `remote_photo_url` | string | URL to a remote photo |
|
||||
| `paging` | object | Pagination information |
|
||||
| `paging` | object | Cursor-based pagination information |
|
||||
| ↳ `after_cursor` | string | Cursor for fetching the next page of results |
|
||||
| ↳ `has_more` | boolean | Whether more results are available |
|
||||
| ↳ `next_page` | string | URL for next page of results |
|
||||
| ↳ `previous_page` | string | URL for previous page of results |
|
||||
| ↳ `count` | number | Total count of items |
|
||||
| `metadata` | object | Response metadata |
|
||||
| ↳ `total_returned` | number | Number of items returned in this response |
|
||||
| ↳ `has_more` | boolean | Whether more items are available |
|
||||
@@ -999,7 +998,7 @@ Retrieve a list of organizations from Zendesk
|
||||
| `apiToken` | string | Yes | Zendesk API token |
|
||||
| `subdomain` | string | Yes | Your Zendesk subdomain \(e.g., "mycompany" for mycompany.zendesk.com\) |
|
||||
| `perPage` | string | No | Results per page as a number string \(default: "100", max: "100"\) |
|
||||
| `page` | string | No | Page number as a string \(e.g., "1", "2"\) |
|
||||
| `pageAfter` | string | No | Cursor from a previous response to fetch the next page of results |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -1020,10 +1019,10 @@ Retrieve a list of organizations from Zendesk
|
||||
| ↳ `created_at` | string | When the organization was created \(ISO 8601 format\) |
|
||||
| ↳ `updated_at` | string | When the organization was last updated \(ISO 8601 format\) |
|
||||
| ↳ `external_id` | string | External ID for linking to external records |
|
||||
| `paging` | object | Pagination information |
|
||||
| `paging` | object | Cursor-based pagination information |
|
||||
| ↳ `after_cursor` | string | Cursor for fetching the next page of results |
|
||||
| ↳ `has_more` | boolean | Whether more results are available |
|
||||
| ↳ `next_page` | string | URL for next page of results |
|
||||
| ↳ `previous_page` | string | URL for previous page of results |
|
||||
| ↳ `count` | number | Total count of items |
|
||||
| `metadata` | object | Response metadata |
|
||||
| ↳ `total_returned` | number | Number of items returned in this response |
|
||||
| ↳ `has_more` | boolean | Whether more items are available |
|
||||
@@ -1075,7 +1074,7 @@ Autocomplete organizations in Zendesk by name prefix (for name matching/autocomp
|
||||
| `subdomain` | string | Yes | Your Zendesk subdomain |
|
||||
| `name` | string | Yes | Organization name prefix to search for \(e.g., "Acme"\) |
|
||||
| `perPage` | string | No | Results per page as a number string \(default: "100", max: "100"\) |
|
||||
| `page` | string | No | Page number as a string \(e.g., "1", "2"\) |
|
||||
| `page` | string | No | Page number for pagination \(1-based\) |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -1096,10 +1095,10 @@ Autocomplete organizations in Zendesk by name prefix (for name matching/autocomp
|
||||
| ↳ `created_at` | string | When the organization was created \(ISO 8601 format\) |
|
||||
| ↳ `updated_at` | string | When the organization was last updated \(ISO 8601 format\) |
|
||||
| ↳ `external_id` | string | External ID for linking to external records |
|
||||
| `paging` | object | Pagination information |
|
||||
| `paging` | object | Cursor-based pagination information |
|
||||
| ↳ `after_cursor` | string | Cursor for fetching the next page of results |
|
||||
| ↳ `has_more` | boolean | Whether more results are available |
|
||||
| ↳ `next_page` | string | URL for next page of results |
|
||||
| ↳ `previous_page` | string | URL for previous page of results |
|
||||
| ↳ `count` | number | Total count of items |
|
||||
| `metadata` | object | Response metadata |
|
||||
| ↳ `total_returned` | number | Number of items returned in this response |
|
||||
| ↳ `has_more` | boolean | Whether more items are available |
|
||||
@@ -1249,19 +1248,18 @@ Unified search across tickets, users, and organizations in Zendesk
|
||||
| `apiToken` | string | Yes | Zendesk API token |
|
||||
| `subdomain` | string | Yes | Your Zendesk subdomain |
|
||||
| `query` | string | Yes | Search query string using Zendesk search syntax \(e.g., "type:ticket status:open"\) |
|
||||
| `sortBy` | string | No | Sort field: "relevance", "created_at", "updated_at", "priority", "status", or "ticket_type" |
|
||||
| `sortOrder` | string | No | Sort order: "asc" or "desc" |
|
||||
| `filterType` | string | Yes | Resource type to search for: "ticket", "user", "organization", or "group" |
|
||||
| `perPage` | string | No | Results per page as a number string \(default: "100", max: "100"\) |
|
||||
| `page` | string | No | Page number as a string \(e.g., "1", "2"\) |
|
||||
| `pageAfter` | string | No | Cursor from a previous response to fetch the next page of results |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `paging` | object | Pagination information |
|
||||
| `paging` | object | Cursor-based pagination information |
|
||||
| ↳ `after_cursor` | string | Cursor for fetching the next page of results |
|
||||
| ↳ `has_more` | boolean | Whether more results are available |
|
||||
| ↳ `next_page` | string | URL for next page of results |
|
||||
| ↳ `previous_page` | string | URL for previous page of results |
|
||||
| ↳ `count` | number | Total count of items |
|
||||
| `metadata` | object | Response metadata |
|
||||
| ↳ `total_returned` | number | Number of items returned in this response |
|
||||
| ↳ `has_more` | boolean | Whether more items are available |
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
'use server'
|
||||
|
||||
import { env } from '@/lib/core/config/env'
|
||||
import { isProd } from '@/lib/core/config/feature-flags'
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ export const LandingNode = React.memo(function LandingNode({ data }: { data: Lan
|
||||
transform: isAnimated ? 'translateY(0) scale(1)' : 'translateY(8px) scale(0.98)',
|
||||
transition:
|
||||
'opacity 0.6s cubic-bezier(0.22, 1, 0.36, 1), transform 0.6s cubic-bezier(0.22, 1, 0.36, 1)',
|
||||
willChange: 'transform, opacity',
|
||||
willChange: isAnimated ? 'auto' : 'transform, opacity',
|
||||
}}
|
||||
>
|
||||
<LandingBlock icon={data.icon} color={data.color} name={data.name} tags={data.tags} />
|
||||
|
||||
@@ -67,7 +67,6 @@ export const LandingEdge = React.memo(function LandingEdge(props: EdgeProps) {
|
||||
strokeLinejoin: 'round',
|
||||
pointerEvents: 'none',
|
||||
animation: `landing-edge-dash-${id} 1s linear infinite`,
|
||||
willChange: 'stroke-dashoffset',
|
||||
...style,
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -754,3 +754,100 @@ input[type="search"]::-ms-clear {
|
||||
text-decoration: none !important;
|
||||
color: inherit !important;
|
||||
}
|
||||
|
||||
/**
|
||||
* Respect user's prefers-reduced-motion setting (WCAG 2.3.3)
|
||||
* Disables animations and transitions for users who prefer reduced motion.
|
||||
*/
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
scroll-behavior: auto !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* WandPromptBar status indicator */
|
||||
@keyframes smoke-pulse {
|
||||
0%,
|
||||
100% {
|
||||
transform: scale(0.8);
|
||||
opacity: 0.4;
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.1);
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
.status-indicator {
|
||||
position: relative;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
background-color: hsl(var(--muted-foreground) / 0.5);
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
|
||||
.status-indicator.streaming {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.status-indicator.streaming::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(
|
||||
circle,
|
||||
hsl(var(--primary) / 0.9) 0%,
|
||||
hsl(var(--primary) / 0.4) 60%,
|
||||
transparent 80%
|
||||
);
|
||||
animation: smoke-pulse 1.8s ease-in-out infinite;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.dark .status-indicator.streaming::before {
|
||||
background: #6b7280;
|
||||
opacity: 0.9;
|
||||
animation: smoke-pulse 1.8s ease-in-out infinite;
|
||||
}
|
||||
|
||||
/* MessageContainer loading dot */
|
||||
@keyframes growShrink {
|
||||
0%,
|
||||
100% {
|
||||
transform: scale(0.9);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
}
|
||||
|
||||
.loading-dot {
|
||||
animation: growShrink 1.5s infinite ease-in-out;
|
||||
}
|
||||
|
||||
/* Subflow node z-index and drag-over styles */
|
||||
.workflow-container .react-flow__node-subflowNode {
|
||||
z-index: -1 !important;
|
||||
}
|
||||
|
||||
.workflow-container .react-flow__node-subflowNode:has([data-subflow-selected="true"]) {
|
||||
z-index: 10 !important;
|
||||
}
|
||||
|
||||
.loop-node-drag-over,
|
||||
.parallel-node-drag-over {
|
||||
box-shadow: 0 0 0 1.75px var(--brand-secondary) !important;
|
||||
border-radius: 8px !important;
|
||||
}
|
||||
|
||||
.react-flow__node[data-parent-node-id] .react-flow__handle {
|
||||
z-index: 30;
|
||||
}
|
||||
|
||||
@@ -22,15 +22,20 @@ interface PipedriveFile {
|
||||
interface PipedriveApiResponse {
|
||||
success: boolean
|
||||
data?: PipedriveFile[]
|
||||
additional_data?: {
|
||||
pagination?: {
|
||||
more_items_in_collection: boolean
|
||||
next_start: number
|
||||
}
|
||||
}
|
||||
error?: string
|
||||
}
|
||||
|
||||
const PipedriveGetFilesSchema = z.object({
|
||||
accessToken: z.string().min(1, 'Access token is required'),
|
||||
deal_id: z.string().optional().nullable(),
|
||||
person_id: z.string().optional().nullable(),
|
||||
org_id: z.string().optional().nullable(),
|
||||
sort: z.enum(['id', 'update_time']).optional().nullable(),
|
||||
limit: z.string().optional().nullable(),
|
||||
start: z.string().optional().nullable(),
|
||||
downloadFiles: z.boolean().optional().default(false),
|
||||
})
|
||||
|
||||
@@ -54,20 +59,19 @@ export async function POST(request: NextRequest) {
|
||||
const body = await request.json()
|
||||
const validatedData = PipedriveGetFilesSchema.parse(body)
|
||||
|
||||
const { accessToken, deal_id, person_id, org_id, limit, downloadFiles } = validatedData
|
||||
const { accessToken, sort, limit, start, downloadFiles } = validatedData
|
||||
|
||||
const baseUrl = 'https://api.pipedrive.com/v1/files'
|
||||
const queryParams = new URLSearchParams()
|
||||
|
||||
if (deal_id) queryParams.append('deal_id', deal_id)
|
||||
if (person_id) queryParams.append('person_id', person_id)
|
||||
if (org_id) queryParams.append('org_id', org_id)
|
||||
if (sort) queryParams.append('sort', sort)
|
||||
if (limit) queryParams.append('limit', limit)
|
||||
if (start) queryParams.append('start', start)
|
||||
|
||||
const queryString = queryParams.toString()
|
||||
const apiUrl = queryString ? `${baseUrl}?${queryString}` : baseUrl
|
||||
|
||||
logger.info(`[${requestId}] Fetching files from Pipedrive`, { deal_id, person_id, org_id })
|
||||
logger.info(`[${requestId}] Fetching files from Pipedrive`)
|
||||
|
||||
const urlValidation = await validateUrlWithDNS(apiUrl, 'apiUrl')
|
||||
if (!urlValidation.isValid) {
|
||||
@@ -93,6 +97,8 @@ export async function POST(request: NextRequest) {
|
||||
}
|
||||
|
||||
const files = data.data || []
|
||||
const hasMore = data.additional_data?.pagination?.more_items_in_collection || false
|
||||
const nextStart = data.additional_data?.pagination?.next_start ?? null
|
||||
const downloadedFiles: Array<{
|
||||
name: string
|
||||
mimeType: string
|
||||
@@ -149,6 +155,8 @@ export async function POST(request: NextRequest) {
|
||||
files,
|
||||
downloadedFiles: downloadedFiles.length > 0 ? downloadedFiles : undefined,
|
||||
total_items: files.length,
|
||||
has_more: hasMore,
|
||||
next_start: nextStart,
|
||||
success: true,
|
||||
},
|
||||
})
|
||||
|
||||
@@ -30,21 +30,6 @@ export const ChatMessageContainer = memo(function ChatMessageContainer({
|
||||
}: ChatMessageContainerProps) {
|
||||
return (
|
||||
<div className='relative flex flex-1 flex-col overflow-hidden bg-white'>
|
||||
<style jsx>{`
|
||||
@keyframes growShrink {
|
||||
0%,
|
||||
100% {
|
||||
transform: scale(0.9);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
}
|
||||
.loading-dot {
|
||||
animation: growShrink 1.5s infinite ease-in-out;
|
||||
}
|
||||
`}</style>
|
||||
|
||||
{/* Scrollable Messages Area */}
|
||||
<div
|
||||
ref={messagesContainerRef}
|
||||
|
||||
@@ -71,7 +71,7 @@ export function VoiceInterface({
|
||||
const [state, setState] = useState<'idle' | 'listening' | 'agent_speaking'>('idle')
|
||||
const [isInitialized, setIsInitialized] = useState(false)
|
||||
const [isMuted, setIsMuted] = useState(false)
|
||||
const [audioLevels, setAudioLevels] = useState<number[]>(new Array(200).fill(0))
|
||||
const [audioLevels, setAudioLevels] = useState<number[]>(() => new Array(200).fill(0))
|
||||
const [permissionStatus, setPermissionStatus] = useState<'prompt' | 'granted' | 'denied'>(
|
||||
'prompt'
|
||||
)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { redirect } from 'next/navigation'
|
||||
import { redirect, unstable_rethrow } from 'next/navigation'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { getWorkspaceFile } from '@/lib/uploads/contexts/workspace'
|
||||
import { verifyWorkspaceMembership } from '@/app/api/workflows/utils'
|
||||
@@ -14,24 +14,27 @@ interface FileViewerPageProps {
|
||||
export default async function FileViewerPage({ params }: FileViewerPageProps) {
|
||||
const { workspaceId, fileId } = await params
|
||||
|
||||
try {
|
||||
const session = await getSession()
|
||||
if (!session?.user?.id) {
|
||||
redirect('/')
|
||||
}
|
||||
const session = await getSession()
|
||||
if (!session?.user?.id) {
|
||||
redirect('/')
|
||||
}
|
||||
|
||||
const hasPermission = await verifyWorkspaceMembership(session.user.id, workspaceId)
|
||||
if (!hasPermission) {
|
||||
redirect(`/workspace/${workspaceId}`)
|
||||
}
|
||||
|
||||
const fileRecord = await getWorkspaceFile(workspaceId, fileId)
|
||||
if (!fileRecord) {
|
||||
redirect(`/workspace/${workspaceId}`)
|
||||
}
|
||||
|
||||
return <FileViewer file={fileRecord} />
|
||||
} catch (error) {
|
||||
const hasPermission = await verifyWorkspaceMembership(session.user.id, workspaceId)
|
||||
if (!hasPermission) {
|
||||
redirect(`/workspace/${workspaceId}`)
|
||||
}
|
||||
|
||||
let fileRecord: Awaited<ReturnType<typeof getWorkspaceFile>>
|
||||
try {
|
||||
fileRecord = await getWorkspaceFile(workspaceId, fileId)
|
||||
} catch (error) {
|
||||
unstable_rethrow(error)
|
||||
redirect(`/workspace/${workspaceId}`)
|
||||
}
|
||||
|
||||
if (!fileRecord) {
|
||||
redirect(`/workspace/${workspaceId}`)
|
||||
}
|
||||
|
||||
return <FileViewer file={fileRecord} />
|
||||
}
|
||||
|
||||
@@ -131,10 +131,8 @@ export const Copilot = forwardRef<CopilotRef, CopilotProps>(({ panelWidth }, ref
|
||||
resumeActiveStream,
|
||||
})
|
||||
|
||||
// Handle scroll management (80px stickiness for copilot)
|
||||
const { scrollAreaRef, scrollToBottom } = useScrollManagement(messages, isSendingMessage, {
|
||||
stickinessThreshold: 40,
|
||||
})
|
||||
// Handle scroll management
|
||||
const { scrollAreaRef, scrollToBottom } = useScrollManagement(messages, isSendingMessage)
|
||||
|
||||
// Handle chat history grouping
|
||||
const { groupedChats, handleHistoryDropdownOpen: handleHistoryDropdownOpenHook } = useChatHistory(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { isEqual } from 'lodash'
|
||||
import isEqual from 'lodash/isEqual'
|
||||
import { useReactFlow } from 'reactflow'
|
||||
import { useStoreWithEqualityFn } from 'zustand/traditional'
|
||||
import { Combobox, type ComboboxOption } from '@/components/emcn/components'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { isEqual } from 'lodash'
|
||||
import isEqual from 'lodash/isEqual'
|
||||
import { useStoreWithEqualityFn } from 'zustand/traditional'
|
||||
import { Badge } from '@/components/emcn'
|
||||
import { Combobox, type ComboboxOption } from '@/components/emcn/components'
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react'
|
||||
import { isEqual } from 'lodash'
|
||||
import isEqual from 'lodash/isEqual'
|
||||
import { ChevronDown, ChevronsUpDown, ChevronUp, Plus } from 'lucide-react'
|
||||
import { Button, Popover, PopoverContent, PopoverItem, PopoverTrigger } from '@/components/emcn'
|
||||
import { Trash } from '@/components/emcn/icons/trash'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { type JSX, type MouseEvent, memo, useCallback, useRef, useState } from 'react'
|
||||
import { isEqual } from 'lodash'
|
||||
import isEqual from 'lodash/isEqual'
|
||||
import { AlertTriangle, ArrowLeftRight, ArrowUp, Check, Clipboard } from 'lucide-react'
|
||||
import { Button, Input, Label, Tooltip } from '@/components/emcn/components'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { isEqual } from 'lodash'
|
||||
import isEqual from 'lodash/isEqual'
|
||||
import {
|
||||
BookOpen,
|
||||
Check,
|
||||
|
||||
@@ -10,40 +10,6 @@ import { ActionBar } from '@/app/workspace/[workspaceId]/w/[workflowId]/componen
|
||||
import { useCurrentWorkflow } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks'
|
||||
import { usePanelEditorStore } from '@/stores/panel'
|
||||
|
||||
/**
|
||||
* Global styles for subflow nodes (loop and parallel containers).
|
||||
* Includes animations for drag-over states and hover effects.
|
||||
*
|
||||
* @returns Style component with global CSS
|
||||
*/
|
||||
const SubflowNodeStyles: React.FC = () => {
|
||||
return (
|
||||
<style jsx global>{`
|
||||
/* Z-index management for subflow nodes - default behind blocks */
|
||||
.workflow-container .react-flow__node-subflowNode {
|
||||
z-index: -1 !important;
|
||||
}
|
||||
|
||||
/* Selected subflows appear above other subflows but below blocks (z-21) */
|
||||
.workflow-container .react-flow__node-subflowNode:has([data-subflow-selected='true']) {
|
||||
z-index: 10 !important;
|
||||
}
|
||||
|
||||
/* Drag-over states */
|
||||
.loop-node-drag-over,
|
||||
.parallel-node-drag-over {
|
||||
box-shadow: 0 0 0 1.75px var(--brand-secondary) !important;
|
||||
border-radius: 8px !important;
|
||||
}
|
||||
|
||||
/* Handle z-index for nested nodes */
|
||||
.react-flow__node[data-parent-node-id] .react-flow__handle {
|
||||
z-index: 30;
|
||||
}
|
||||
`}</style>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Data structure for subflow nodes (loop and parallel containers)
|
||||
*/
|
||||
@@ -151,133 +117,130 @@ export const SubflowNodeComponent = memo(({ data, id, selected }: NodeProps<Subf
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<SubflowNodeStyles />
|
||||
<div className='group relative'>
|
||||
<div className='group relative'>
|
||||
<div
|
||||
ref={blockRef}
|
||||
onClick={() => setCurrentBlockId(id)}
|
||||
className={cn(
|
||||
'workflow-drag-handle relative cursor-grab select-none rounded-[8px] border border-[var(--border-1)] [&:active]:cursor-grabbing',
|
||||
'transition-block-bg transition-ring',
|
||||
'z-[20]'
|
||||
)}
|
||||
style={{
|
||||
width: data.width || 500,
|
||||
height: data.height || 300,
|
||||
position: 'relative',
|
||||
overflow: 'visible',
|
||||
pointerEvents: isPreview ? 'none' : 'all',
|
||||
}}
|
||||
data-node-id={id}
|
||||
data-type='subflowNode'
|
||||
data-nesting-level={nestingLevel}
|
||||
data-subflow-selected={isFocused || isSelected || isPreviewSelected}
|
||||
>
|
||||
{!isPreview && (
|
||||
<ActionBar blockId={id} blockType={data.kind} disabled={!userPermissions.canEdit} />
|
||||
)}
|
||||
|
||||
{/* Header Section */}
|
||||
<div
|
||||
ref={blockRef}
|
||||
onClick={() => setCurrentBlockId(id)}
|
||||
className={cn(
|
||||
'workflow-drag-handle relative cursor-grab select-none rounded-[8px] border border-[var(--border-1)] [&:active]:cursor-grabbing',
|
||||
'transition-block-bg transition-ring',
|
||||
'z-[20]'
|
||||
'flex items-center justify-between rounded-t-[8px] border-[var(--border)] border-b bg-[var(--surface-2)] py-[8px] pr-[12px] pl-[8px]'
|
||||
)}
|
||||
style={{
|
||||
width: data.width || 500,
|
||||
height: data.height || 300,
|
||||
position: 'relative',
|
||||
overflow: 'visible',
|
||||
pointerEvents: isPreview ? 'none' : 'all',
|
||||
}}
|
||||
data-node-id={id}
|
||||
data-type='subflowNode'
|
||||
data-nesting-level={nestingLevel}
|
||||
data-subflow-selected={isFocused || isSelected || isPreviewSelected}
|
||||
>
|
||||
{!isPreview && (
|
||||
<ActionBar blockId={id} blockType={data.kind} disabled={!userPermissions.canEdit} />
|
||||
)}
|
||||
|
||||
{/* Header Section */}
|
||||
<div
|
||||
className={cn(
|
||||
'flex items-center justify-between rounded-t-[8px] border-[var(--border)] border-b bg-[var(--surface-2)] py-[8px] pr-[12px] pl-[8px]'
|
||||
)}
|
||||
>
|
||||
<div className='flex min-w-0 flex-1 items-center gap-[10px]'>
|
||||
<div
|
||||
className='flex h-[24px] w-[24px] flex-shrink-0 items-center justify-center rounded-[6px]'
|
||||
style={{ backgroundColor: isEnabled ? blockIconBg : 'gray' }}
|
||||
>
|
||||
<BlockIcon className='h-[16px] w-[16px] text-white' />
|
||||
</div>
|
||||
<span
|
||||
className={cn(
|
||||
'truncate font-medium text-[16px]',
|
||||
!isEnabled && 'text-[var(--text-muted)]'
|
||||
)}
|
||||
title={blockName}
|
||||
>
|
||||
{blockName}
|
||||
</span>
|
||||
</div>
|
||||
<div className='flex items-center gap-1'>
|
||||
{!isEnabled && <Badge variant='gray-secondary'>disabled</Badge>}
|
||||
{isLocked && <Badge variant='gray-secondary'>locked</Badge>}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{!isPreview && (
|
||||
<div className='flex min-w-0 flex-1 items-center gap-[10px]'>
|
||||
<div
|
||||
className='absolute right-[8px] bottom-[8px] z-20 flex h-[32px] w-[32px] cursor-se-resize items-center justify-center text-muted-foreground'
|
||||
style={{ pointerEvents: 'auto' }}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div
|
||||
className='h-[calc(100%-50px)] pt-[16px] pr-[80px] pb-[16px] pl-[16px]'
|
||||
data-dragarea='true'
|
||||
style={{
|
||||
position: 'relative',
|
||||
pointerEvents: isPreview ? 'none' : 'auto',
|
||||
}}
|
||||
>
|
||||
{/* Subflow Start */}
|
||||
<div
|
||||
className='absolute top-[16px] left-[16px] flex items-center justify-center rounded-[8px] border border-[var(--border-1)] bg-[var(--surface-2)] px-[12px] py-[6px]'
|
||||
style={{ pointerEvents: isPreview ? 'none' : 'auto' }}
|
||||
data-parent-id={id}
|
||||
data-node-role={`${data.kind}-start`}
|
||||
data-extent='parent'
|
||||
className='flex h-[24px] w-[24px] flex-shrink-0 items-center justify-center rounded-[6px]'
|
||||
style={{ backgroundColor: isEnabled ? blockIconBg : 'gray' }}
|
||||
>
|
||||
<span className='font-medium text-[14px] text-[var(--text-primary)]'>Start</span>
|
||||
|
||||
<Handle
|
||||
type='source'
|
||||
position={Position.Right}
|
||||
id={startHandleId}
|
||||
className={getHandleClasses('right')}
|
||||
style={{
|
||||
top: '50%',
|
||||
transform: 'translateY(-50%)',
|
||||
pointerEvents: 'auto',
|
||||
}}
|
||||
data-parent-id={id}
|
||||
/>
|
||||
<BlockIcon className='h-[16px] w-[16px] text-white' />
|
||||
</div>
|
||||
<span
|
||||
className={cn(
|
||||
'truncate font-medium text-[16px]',
|
||||
!isEnabled && 'text-[var(--text-muted)]'
|
||||
)}
|
||||
title={blockName}
|
||||
>
|
||||
{blockName}
|
||||
</span>
|
||||
</div>
|
||||
<div className='flex items-center gap-1'>
|
||||
{!isEnabled && <Badge variant='gray-secondary'>disabled</Badge>}
|
||||
{isLocked && <Badge variant='gray-secondary'>locked</Badge>}
|
||||
</div>
|
||||
|
||||
{/* Input handle on left middle */}
|
||||
<Handle
|
||||
type='target'
|
||||
position={Position.Left}
|
||||
className={getHandleClasses('left')}
|
||||
style={{
|
||||
...getHandleStyle(),
|
||||
pointerEvents: 'auto',
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Output handle on right middle */}
|
||||
<Handle
|
||||
type='source'
|
||||
position={Position.Right}
|
||||
className={getHandleClasses('right')}
|
||||
style={{
|
||||
...getHandleStyle(),
|
||||
pointerEvents: 'auto',
|
||||
}}
|
||||
id={endHandleId}
|
||||
/>
|
||||
|
||||
{hasRing && (
|
||||
<div
|
||||
className={cn('pointer-events-none absolute inset-0 z-40 rounded-[8px]', ringStyles)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{!isPreview && (
|
||||
<div
|
||||
className='absolute right-[8px] bottom-[8px] z-20 flex h-[32px] w-[32px] cursor-se-resize items-center justify-center text-muted-foreground'
|
||||
style={{ pointerEvents: 'auto' }}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div
|
||||
className='h-[calc(100%-50px)] pt-[16px] pr-[80px] pb-[16px] pl-[16px]'
|
||||
data-dragarea='true'
|
||||
style={{
|
||||
position: 'relative',
|
||||
pointerEvents: isPreview ? 'none' : 'auto',
|
||||
}}
|
||||
>
|
||||
{/* Subflow Start */}
|
||||
<div
|
||||
className='absolute top-[16px] left-[16px] flex items-center justify-center rounded-[8px] border border-[var(--border-1)] bg-[var(--surface-2)] px-[12px] py-[6px]'
|
||||
style={{ pointerEvents: isPreview ? 'none' : 'auto' }}
|
||||
data-parent-id={id}
|
||||
data-node-role={`${data.kind}-start`}
|
||||
data-extent='parent'
|
||||
>
|
||||
<span className='font-medium text-[14px] text-[var(--text-primary)]'>Start</span>
|
||||
|
||||
<Handle
|
||||
type='source'
|
||||
position={Position.Right}
|
||||
id={startHandleId}
|
||||
className={getHandleClasses('right')}
|
||||
style={{
|
||||
top: '50%',
|
||||
transform: 'translateY(-50%)',
|
||||
pointerEvents: 'auto',
|
||||
}}
|
||||
data-parent-id={id}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Input handle on left middle */}
|
||||
<Handle
|
||||
type='target'
|
||||
position={Position.Left}
|
||||
className={getHandleClasses('left')}
|
||||
style={{
|
||||
...getHandleStyle(),
|
||||
pointerEvents: 'auto',
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Output handle on right middle */}
|
||||
<Handle
|
||||
type='source'
|
||||
position={Position.Right}
|
||||
className={getHandleClasses('right')}
|
||||
style={{
|
||||
...getHandleStyle(),
|
||||
pointerEvents: 'auto',
|
||||
}}
|
||||
id={endHandleId}
|
||||
/>
|
||||
|
||||
{hasRing && (
|
||||
<div
|
||||
className={cn('pointer-events-none absolute inset-0 z-40 rounded-[8px]', ringStyles)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
|
||||
@@ -134,57 +134,6 @@ export function WandPromptBar({
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<style jsx global>{`
|
||||
|
||||
@keyframes smoke-pulse {
|
||||
0%,
|
||||
100% {
|
||||
transform: scale(0.8);
|
||||
opacity: 0.4;
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.1);
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
.status-indicator {
|
||||
position: relative;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
background-color: hsl(var(--muted-foreground) / 0.5);
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
|
||||
.status-indicator.streaming {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.status-indicator.streaming::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(
|
||||
circle,
|
||||
hsl(var(--primary) / 0.9) 0%,
|
||||
hsl(var(--primary) / 0.4) 60%,
|
||||
transparent 80%
|
||||
);
|
||||
animation: smoke-pulse 1.8s ease-in-out infinite;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.dark .status-indicator.streaming::before {
|
||||
background: #6b7280;
|
||||
opacity: 0.9;
|
||||
animation: smoke-pulse 1.8s ease-in-out infinite;
|
||||
}
|
||||
|
||||
`}</style>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { memo, useCallback, useEffect, useMemo, useRef } from 'react'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { isEqual } from 'lodash'
|
||||
import isEqual from 'lodash/isEqual'
|
||||
import { useParams } from 'next/navigation'
|
||||
import { Handle, type NodeProps, Position, useUpdateNodeInternals } from 'reactflow'
|
||||
import { useStoreWithEqualityFn } from 'zustand/traditional'
|
||||
|
||||
@@ -16,7 +16,7 @@ interface UseScrollManagementOptions {
|
||||
/**
|
||||
* Distance from bottom (in pixels) within which auto-scroll stays active
|
||||
* @remarks Lower values = less sticky (user can scroll away easier)
|
||||
* @defaultValue 100
|
||||
* @defaultValue 30
|
||||
*/
|
||||
stickinessThreshold?: number
|
||||
}
|
||||
@@ -41,7 +41,7 @@ export function useScrollManagement(
|
||||
const lastScrollTopRef = useRef(0)
|
||||
|
||||
const scrollBehavior = options?.behavior ?? 'smooth'
|
||||
const stickinessThreshold = options?.stickinessThreshold ?? 100
|
||||
const stickinessThreshold = options?.stickinessThreshold ?? 30
|
||||
|
||||
/** Scrolls the container to the bottom */
|
||||
const scrollToBottom = useCallback(() => {
|
||||
|
||||
@@ -514,6 +514,7 @@ export function HelpModal({ open, onOpenChange, workflowId, workspaceId }: HelpM
|
||||
alt={`Preview ${index + 1}`}
|
||||
fill
|
||||
unoptimized
|
||||
sizes='(max-width: 768px) 100vw, 50vw'
|
||||
className='object-contain'
|
||||
/>
|
||||
<button
|
||||
|
||||
@@ -165,12 +165,16 @@ export function CancelSubscription({ subscription, subscriptionData }: CancelSub
|
||||
logger.info('Subscription restored successfully', result)
|
||||
}
|
||||
|
||||
await queryClient.invalidateQueries({ queryKey: subscriptionKeys.all })
|
||||
if (activeOrgId) {
|
||||
await queryClient.invalidateQueries({ queryKey: organizationKeys.detail(activeOrgId) })
|
||||
await queryClient.invalidateQueries({ queryKey: organizationKeys.billing(activeOrgId) })
|
||||
await queryClient.invalidateQueries({ queryKey: organizationKeys.lists() })
|
||||
}
|
||||
await Promise.all([
|
||||
queryClient.invalidateQueries({ queryKey: subscriptionKeys.all }),
|
||||
...(activeOrgId
|
||||
? [
|
||||
queryClient.invalidateQueries({ queryKey: organizationKeys.detail(activeOrgId) }),
|
||||
queryClient.invalidateQueries({ queryKey: organizationKeys.billing(activeOrgId) }),
|
||||
queryClient.invalidateQueries({ queryKey: organizationKeys.lists() }),
|
||||
]
|
||||
: []),
|
||||
])
|
||||
|
||||
setIsDialogOpen(false)
|
||||
} catch (err) {
|
||||
|
||||
@@ -37,7 +37,7 @@ export const UsageLimit = forwardRef<UsageLimitRef, UsageLimitProps>(
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
const [inputValue, setInputValue] = useState(currentLimit.toString())
|
||||
const [inputValue, setInputValue] = useState(() => currentLimit.toString())
|
||||
const [hasError, setHasError] = useState(false)
|
||||
const [errorType, setErrorType] = useState<'general' | 'belowUsage' | null>(null)
|
||||
const [isEditing, setIsEditing] = useState(false)
|
||||
|
||||
@@ -92,12 +92,9 @@ export const IncidentioBlock: BlockConfig<IncidentioResponse> = {
|
||||
field: 'operation',
|
||||
value: [
|
||||
'incidentio_incidents_list',
|
||||
'incidentio_actions_list',
|
||||
'incidentio_follow_ups_list',
|
||||
'incidentio_users_list',
|
||||
'incidentio_workflows_list',
|
||||
'incidentio_schedules_list',
|
||||
'incidentio_escalations_list',
|
||||
'incidentio_incident_updates_list',
|
||||
'incidentio_schedule_entries_list',
|
||||
],
|
||||
@@ -113,6 +110,7 @@ export const IncidentioBlock: BlockConfig<IncidentioResponse> = {
|
||||
field: 'operation',
|
||||
value: [
|
||||
'incidentio_incidents_list',
|
||||
'incidentio_users_list',
|
||||
'incidentio_workflows_list',
|
||||
'incidentio_schedules_list',
|
||||
'incidentio_incident_updates_list',
|
||||
|
||||
@@ -216,31 +216,21 @@ Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, n
|
||||
condition: { field: 'operation', value: ['update_deal'] },
|
||||
},
|
||||
{
|
||||
id: 'deal_id',
|
||||
title: 'Deal ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Filter by deal ID ',
|
||||
condition: { field: 'operation', value: ['get_files'] },
|
||||
},
|
||||
{
|
||||
id: 'person_id',
|
||||
title: 'Person ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Filter by person ID ',
|
||||
condition: { field: 'operation', value: ['get_files'] },
|
||||
},
|
||||
{
|
||||
id: 'org_id',
|
||||
title: 'Organization ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Filter by organization ID ',
|
||||
id: 'sort',
|
||||
title: 'Sort By',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'ID', id: 'id' },
|
||||
{ label: 'Update Time', id: 'update_time' },
|
||||
],
|
||||
value: () => 'id',
|
||||
condition: { field: 'operation', value: ['get_files'] },
|
||||
},
|
||||
{
|
||||
id: 'limit',
|
||||
title: 'Limit',
|
||||
type: 'short-input',
|
||||
placeholder: 'Number of results (default 100, max 500)',
|
||||
placeholder: 'Number of results (default 100, max 100)',
|
||||
condition: { field: 'operation', value: ['get_files'] },
|
||||
},
|
||||
{
|
||||
@@ -305,8 +295,28 @@ Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, n
|
||||
id: 'cursor',
|
||||
title: 'Cursor',
|
||||
type: 'short-input',
|
||||
placeholder: 'Pagination cursor (optional)',
|
||||
condition: { field: 'operation', value: ['get_pipelines'] },
|
||||
placeholder: 'Pagination cursor from previous response',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['get_all_deals', 'get_projects'],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'start',
|
||||
title: 'Start (Offset)',
|
||||
type: 'short-input',
|
||||
placeholder: 'Pagination offset (e.g., 0, 100, 200)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: [
|
||||
'get_activities',
|
||||
'get_leads',
|
||||
'get_files',
|
||||
'get_pipeline_deals',
|
||||
'get_mail_messages',
|
||||
'get_pipelines',
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'pipeline_id',
|
||||
@@ -323,19 +333,6 @@ Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, n
|
||||
placeholder: 'Filter by stage ID ',
|
||||
condition: { field: 'operation', value: ['get_pipeline_deals'] },
|
||||
},
|
||||
{
|
||||
id: 'status',
|
||||
title: 'Status',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'All', id: '' },
|
||||
{ label: 'Open', id: 'open' },
|
||||
{ label: 'Won', id: 'won' },
|
||||
{ label: 'Lost', id: 'lost' },
|
||||
],
|
||||
value: () => '',
|
||||
condition: { field: 'operation', value: ['get_pipeline_deals'] },
|
||||
},
|
||||
{
|
||||
id: 'limit',
|
||||
title: 'Limit',
|
||||
@@ -426,22 +423,29 @@ Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, n
|
||||
id: 'deal_id',
|
||||
title: 'Deal ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Filter by deal ID ',
|
||||
condition: { field: 'operation', value: ['get_activities', 'create_activity'] },
|
||||
placeholder: 'Associated deal ID ',
|
||||
condition: { field: 'operation', value: ['create_activity'] },
|
||||
},
|
||||
{
|
||||
id: 'person_id',
|
||||
title: 'Person ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Filter by person ID ',
|
||||
condition: { field: 'operation', value: ['get_activities', 'create_activity'] },
|
||||
placeholder: 'Associated person ID ',
|
||||
condition: { field: 'operation', value: ['create_activity'] },
|
||||
},
|
||||
{
|
||||
id: 'org_id',
|
||||
title: 'Organization ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Filter by organization ID ',
|
||||
condition: { field: 'operation', value: ['get_activities', 'create_activity'] },
|
||||
placeholder: 'Associated organization ID ',
|
||||
condition: { field: 'operation', value: ['create_activity'] },
|
||||
},
|
||||
{
|
||||
id: 'user_id',
|
||||
title: 'User ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Filter by user ID',
|
||||
condition: { field: 'operation', value: ['get_activities'] },
|
||||
},
|
||||
{
|
||||
id: 'type',
|
||||
@@ -781,7 +785,8 @@ Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, n
|
||||
thread_id: { type: 'string', description: 'Mail thread ID' },
|
||||
sort_by: { type: 'string', description: 'Field to sort by' },
|
||||
sort_direction: { type: 'string', description: 'Sorting direction' },
|
||||
cursor: { type: 'string', description: 'Pagination cursor' },
|
||||
cursor: { type: 'string', description: 'Pagination cursor (v2 endpoints)' },
|
||||
start: { type: 'string', description: 'Pagination start offset (v1 endpoints)' },
|
||||
project_id: { type: 'string', description: 'Project ID' },
|
||||
description: { type: 'string', description: 'Description' },
|
||||
start_date: { type: 'string', description: 'Start date' },
|
||||
@@ -793,12 +798,15 @@ Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, n
|
||||
due_time: { type: 'string', description: 'Due time' },
|
||||
duration: { type: 'string', description: 'Duration' },
|
||||
done: { type: 'string', description: 'Completion status' },
|
||||
user_id: { type: 'string', description: 'User ID' },
|
||||
note: { type: 'string', description: 'Notes' },
|
||||
lead_id: { type: 'string', description: 'Lead ID' },
|
||||
archived: { type: 'string', description: 'Archived status' },
|
||||
value_amount: { type: 'string', description: 'Value amount' },
|
||||
value_currency: { type: 'string', description: 'Value currency' },
|
||||
is_archived: { type: 'string', description: 'Archive status' },
|
||||
organization_id: { type: 'string', description: 'Organization ID' },
|
||||
owner_id: { type: 'string', description: 'Owner user ID' },
|
||||
},
|
||||
outputs: {
|
||||
deals: { type: 'json', description: 'Array of deal objects' },
|
||||
|
||||
@@ -445,6 +445,13 @@ Return ONLY the order by expression - no explanations, no extra text.`,
|
||||
placeholder: '100',
|
||||
condition: { field: 'operation', value: 'query' },
|
||||
},
|
||||
{
|
||||
id: 'offset',
|
||||
title: 'Offset',
|
||||
type: 'short-input',
|
||||
placeholder: '0',
|
||||
condition: { field: 'operation', value: 'query' },
|
||||
},
|
||||
// Vector search operation fields
|
||||
{
|
||||
id: 'functionName',
|
||||
@@ -543,6 +550,13 @@ Return ONLY the order by expression - no explanations, no extra text.`,
|
||||
placeholder: '100',
|
||||
condition: { field: 'operation', value: 'text_search' },
|
||||
},
|
||||
{
|
||||
id: 'offset',
|
||||
title: 'Offset',
|
||||
type: 'short-input',
|
||||
placeholder: '0',
|
||||
condition: { field: 'operation', value: 'text_search' },
|
||||
},
|
||||
// Count operation fields
|
||||
{
|
||||
id: 'filter',
|
||||
|
||||
@@ -66,6 +66,20 @@ export const TypeformBlock: BlockConfig<TypeformResponse> = {
|
||||
placeholder: 'Number of responses per page (default: 25)',
|
||||
condition: { field: 'operation', value: 'typeform_responses' },
|
||||
},
|
||||
{
|
||||
id: 'before',
|
||||
title: 'Before (Cursor)',
|
||||
type: 'short-input',
|
||||
placeholder: 'Cursor token from previous response for pagination',
|
||||
condition: { field: 'operation', value: 'typeform_responses' },
|
||||
},
|
||||
{
|
||||
id: 'after',
|
||||
title: 'After (Cursor)',
|
||||
type: 'short-input',
|
||||
placeholder: 'Cursor token from previous response for newer results',
|
||||
condition: { field: 'operation', value: 'typeform_responses' },
|
||||
},
|
||||
{
|
||||
id: 'since',
|
||||
title: 'Since',
|
||||
@@ -380,6 +394,8 @@ Do not include any explanations, markdown formatting, or other text outside the
|
||||
apiKey: { type: 'string', description: 'Personal access token' },
|
||||
// Response operation params
|
||||
pageSize: { type: 'number', description: 'Responses per page' },
|
||||
before: { type: 'string', description: 'Cursor token for fetching the next page' },
|
||||
after: { type: 'string', description: 'Cursor token for fetching newer results' },
|
||||
since: { type: 'string', description: 'Start date filter' },
|
||||
until: { type: 'string', description: 'End date filter' },
|
||||
completed: { type: 'string', description: 'Completion status filter' },
|
||||
|
||||
@@ -444,33 +444,36 @@ Return ONLY the search query - no explanations.`,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'sortBy',
|
||||
title: 'Sort By',
|
||||
id: 'filterType',
|
||||
title: 'Resource Type',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Relevance', id: 'relevance' },
|
||||
{ label: 'Created At', id: 'created_at' },
|
||||
{ label: 'Updated At', id: 'updated_at' },
|
||||
{ label: 'Priority', id: 'priority' },
|
||||
{ label: 'Status', id: 'status' },
|
||||
{ label: 'Ticket Type', id: 'ticket_type' },
|
||||
{ label: 'Ticket', id: 'ticket' },
|
||||
{ label: 'User', id: 'user' },
|
||||
{ label: 'Organization', id: 'organization' },
|
||||
{ label: 'Group', id: 'group' },
|
||||
],
|
||||
required: true,
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['search'],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'sortOrder',
|
||||
title: 'Sort Order',
|
||||
id: 'sort',
|
||||
title: 'Sort',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Ascending', id: 'asc' },
|
||||
{ label: 'Descending', id: 'desc' },
|
||||
{ label: 'Updated At (Asc)', id: 'updated_at' },
|
||||
{ label: 'Updated At (Desc)', id: '-updated_at' },
|
||||
{ label: 'ID (Asc)', id: 'id' },
|
||||
{ label: 'ID (Desc)', id: '-id' },
|
||||
{ label: 'Status (Asc)', id: 'status' },
|
||||
{ label: 'Status (Desc)', id: '-status' },
|
||||
],
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['search'],
|
||||
value: ['get_tickets'],
|
||||
},
|
||||
},
|
||||
// Pagination fields
|
||||
@@ -492,20 +495,25 @@ Return ONLY the search query - no explanations.`,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'page',
|
||||
title: 'Page',
|
||||
id: 'pageAfter',
|
||||
title: 'Page After (Cursor)',
|
||||
type: 'short-input',
|
||||
placeholder: 'Page number',
|
||||
placeholder: 'Cursor from previous response (after_cursor)',
|
||||
description: 'Cursor value from a previous response to fetch the next page of results',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: [
|
||||
'get_tickets',
|
||||
'get_users',
|
||||
'get_organizations',
|
||||
'search_users',
|
||||
'autocomplete_organizations',
|
||||
'search',
|
||||
],
|
||||
value: ['get_tickets', 'get_users', 'get_organizations', 'search'],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'page',
|
||||
title: 'Page Number',
|
||||
type: 'short-input',
|
||||
placeholder: 'Page number (default: 1)',
|
||||
description: 'Page number for offset-based pagination',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['search_users', 'autocomplete_organizations'],
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -624,6 +632,7 @@ Return ONLY the search query - no explanations.`,
|
||||
email: { type: 'string', description: 'Zendesk email address' },
|
||||
apiToken: { type: 'string', description: 'Zendesk API token' },
|
||||
subdomain: { type: 'string', description: 'Zendesk subdomain' },
|
||||
sort: { type: 'string', description: 'Sort field for ticket listing' },
|
||||
},
|
||||
outputs: {
|
||||
// Ticket operations - list
|
||||
@@ -665,8 +674,11 @@ Return ONLY the search query - no explanations.`,
|
||||
type: 'boolean',
|
||||
description: 'Deletion confirmation (delete_ticket, delete_user, delete_organization)',
|
||||
},
|
||||
// Pagination (shared across list operations)
|
||||
paging: { type: 'json', description: 'Pagination information for list operations' },
|
||||
// Cursor-based pagination (shared across list operations)
|
||||
paging: {
|
||||
type: 'json',
|
||||
description: 'Cursor-based pagination information (after_cursor, has_more)',
|
||||
},
|
||||
// Metadata (shared across all operations)
|
||||
metadata: { type: 'json', description: 'Operation metadata including operation type' },
|
||||
},
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
type ReactNode,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useId,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
@@ -170,6 +171,7 @@ const Combobox = memo(
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
const listboxId = useId()
|
||||
const [open, setOpen] = useState(false)
|
||||
const [highlightedIndex, setHighlightedIndex] = useState(-1)
|
||||
const [searchQuery, setSearchQuery] = useState('')
|
||||
@@ -513,6 +515,7 @@ const Combobox = memo(
|
||||
role='combobox'
|
||||
aria-expanded={open}
|
||||
aria-haspopup='listbox'
|
||||
aria-controls={listboxId}
|
||||
aria-disabled={disabled}
|
||||
tabIndex={disabled ? -1 : 0}
|
||||
className={cn(
|
||||
@@ -616,7 +619,7 @@ const Combobox = memo(
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div ref={dropdownRef} role='listbox'>
|
||||
<div ref={dropdownRef} role='listbox' id={listboxId}>
|
||||
{isLoading ? (
|
||||
<div className='flex items-center justify-center py-[14px]'>
|
||||
<Loader2 className='h-[16px] w-[16px] animate-spin text-[var(--text-muted)]' />
|
||||
|
||||
@@ -27,12 +27,14 @@ const Alert = React.forwardRef<
|
||||
Alert.displayName = 'Alert'
|
||||
|
||||
const AlertTitle = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLHeadingElement>>(
|
||||
({ className, ...props }, ref) => (
|
||||
({ className, children, ...props }, ref) => (
|
||||
<h5
|
||||
ref={ref}
|
||||
className={cn('mb-1 font-medium leading-none tracking-tight', className)}
|
||||
{...props}
|
||||
/>
|
||||
>
|
||||
{children}
|
||||
</h5>
|
||||
)
|
||||
)
|
||||
AlertTitle.displayName = 'AlertTitle'
|
||||
|
||||
@@ -16,26 +16,32 @@ export const mdxComponents: MDXRemoteProps['components'] = {
|
||||
unoptimized
|
||||
/>
|
||||
),
|
||||
h2: (props: any) => (
|
||||
h2: ({ children, className, ...props }: any) => (
|
||||
<h2
|
||||
{...props}
|
||||
style={{ fontSize: '30px', marginTop: '3rem', marginBottom: '1.5rem' }}
|
||||
className={clsx('font-medium text-black leading-tight', props.className)}
|
||||
/>
|
||||
className={clsx('font-medium text-black leading-tight', className)}
|
||||
>
|
||||
{children}
|
||||
</h2>
|
||||
),
|
||||
h3: (props: any) => (
|
||||
h3: ({ children, className, ...props }: any) => (
|
||||
<h3
|
||||
{...props}
|
||||
style={{ fontSize: '24px', marginTop: '1.5rem', marginBottom: '0.75rem' }}
|
||||
className={clsx('font-medium leading-tight', props.className)}
|
||||
/>
|
||||
className={clsx('font-medium leading-tight', className)}
|
||||
>
|
||||
{children}
|
||||
</h3>
|
||||
),
|
||||
h4: (props: any) => (
|
||||
h4: ({ children, className, ...props }: any) => (
|
||||
<h4
|
||||
{...props}
|
||||
style={{ fontSize: '19px', marginTop: '1.5rem', marginBottom: '0.75rem' }}
|
||||
className={clsx('font-medium leading-tight', props.className)}
|
||||
/>
|
||||
className={clsx('font-medium leading-tight', className)}
|
||||
>
|
||||
{children}
|
||||
</h4>
|
||||
),
|
||||
p: (props: any) => (
|
||||
<p
|
||||
|
||||
@@ -107,5 +107,3 @@ if (typeof process !== 'undefined') {
|
||||
logger.info(`S3 copilot bucket: ${env.S3_COPILOT_BUCKET_NAME}`)
|
||||
}
|
||||
}
|
||||
|
||||
export default ensureUploadsDirectory
|
||||
|
||||
@@ -326,6 +326,18 @@ const nextConfig: NextConfig = {
|
||||
|
||||
return redirects
|
||||
},
|
||||
async rewrites() {
|
||||
return [
|
||||
...(isHosted
|
||||
? [
|
||||
{
|
||||
source: '/r/:shortCode',
|
||||
destination: 'https://go.trybeluga.ai/:shortCode',
|
||||
},
|
||||
]
|
||||
: []),
|
||||
]
|
||||
},
|
||||
}
|
||||
|
||||
export default nextConfig
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
supportsNativeStructuredOutputs,
|
||||
} from '@/providers/models'
|
||||
import type { ProviderRequest, ProviderResponse, TimeSegment } from '@/providers/types'
|
||||
import { ProviderError } from '@/providers/types'
|
||||
import {
|
||||
calculateCost,
|
||||
prepareToolExecution,
|
||||
@@ -842,15 +843,11 @@ export async function executeAnthropicProviderRequest(
|
||||
duration: totalDuration,
|
||||
})
|
||||
|
||||
const enhancedError = new Error(error instanceof Error ? error.message : String(error))
|
||||
// @ts-ignore
|
||||
enhancedError.timing = {
|
||||
throw new ProviderError(error instanceof Error ? error.message : String(error), {
|
||||
startTime: providerStartTimeISO,
|
||||
endTime: providerEndTimeISO,
|
||||
duration: totalDuration,
|
||||
}
|
||||
|
||||
throw enhancedError
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1299,14 +1296,10 @@ export async function executeAnthropicProviderRequest(
|
||||
duration: totalDuration,
|
||||
})
|
||||
|
||||
const enhancedError = new Error(error instanceof Error ? error.message : String(error))
|
||||
// @ts-ignore
|
||||
enhancedError.timing = {
|
||||
throw new ProviderError(error instanceof Error ? error.message : String(error), {
|
||||
startTime: providerStartTimeISO,
|
||||
endTime: providerEndTimeISO,
|
||||
duration: totalDuration,
|
||||
}
|
||||
|
||||
throw enhancedError
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import type {
|
||||
ProviderResponse,
|
||||
TimeSegment,
|
||||
} from '@/providers/types'
|
||||
import { ProviderError } from '@/providers/types'
|
||||
import {
|
||||
calculateCost,
|
||||
prepareToolExecution,
|
||||
@@ -251,7 +252,7 @@ async function executeChatCompletionsRequest(
|
||||
output: currentResponse.usage?.completion_tokens || 0,
|
||||
total: currentResponse.usage?.total_tokens || 0,
|
||||
}
|
||||
const toolCalls: (FunctionCallResponse & { success: boolean })[] = []
|
||||
const toolCalls: FunctionCallResponse[] = []
|
||||
const toolResults: Record<string, unknown>[] = []
|
||||
const currentMessages = [...allMessages]
|
||||
let iterationCount = 0
|
||||
@@ -577,15 +578,11 @@ async function executeChatCompletionsRequest(
|
||||
duration: totalDuration,
|
||||
})
|
||||
|
||||
const enhancedError = new Error(error instanceof Error ? error.message : String(error))
|
||||
// @ts-ignore - Adding timing property to the error
|
||||
enhancedError.timing = {
|
||||
throw new ProviderError(error instanceof Error ? error.message : String(error), {
|
||||
startTime: providerStartTimeISO,
|
||||
endTime: providerEndTimeISO,
|
||||
duration: totalDuration,
|
||||
}
|
||||
|
||||
throw enhancedError
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,11 +22,13 @@ import {
|
||||
} from '@/providers/bedrock/utils'
|
||||
import { getProviderDefaultModel, getProviderModels } from '@/providers/models'
|
||||
import type {
|
||||
FunctionCallResponse,
|
||||
ProviderConfig,
|
||||
ProviderRequest,
|
||||
ProviderResponse,
|
||||
TimeSegment,
|
||||
} from '@/providers/types'
|
||||
import { ProviderError } from '@/providers/types'
|
||||
import {
|
||||
calculateCost,
|
||||
prepareToolExecution,
|
||||
@@ -419,8 +421,8 @@ export const bedrockProvider: ProviderConfig = {
|
||||
pricing: initialCost.pricing,
|
||||
}
|
||||
|
||||
const toolCalls: any[] = []
|
||||
const toolResults: any[] = []
|
||||
const toolCalls: FunctionCallResponse[] = []
|
||||
const toolResults: Record<string, unknown>[] = []
|
||||
const currentMessages = [...messages]
|
||||
let iterationCount = 0
|
||||
let hasUsedForcedTool = false
|
||||
@@ -561,7 +563,7 @@ export const bedrockProvider: ProviderConfig = {
|
||||
|
||||
let resultContent: any
|
||||
if (result.success) {
|
||||
toolResults.push(result.output)
|
||||
toolResults.push(result.output!)
|
||||
resultContent = result.output
|
||||
} else {
|
||||
resultContent = {
|
||||
@@ -903,15 +905,11 @@ export const bedrockProvider: ProviderConfig = {
|
||||
duration: totalDuration,
|
||||
})
|
||||
|
||||
const enhancedError = new Error(error instanceof Error ? error.message : String(error))
|
||||
// @ts-ignore
|
||||
enhancedError.timing = {
|
||||
throw new ProviderError(error instanceof Error ? error.message : String(error), {
|
||||
startTime: providerStartTimeISO,
|
||||
endTime: providerEndTimeISO,
|
||||
duration: totalDuration,
|
||||
}
|
||||
|
||||
throw enhancedError
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import type {
|
||||
ProviderResponse,
|
||||
TimeSegment,
|
||||
} from '@/providers/types'
|
||||
import { ProviderError } from '@/providers/types'
|
||||
import {
|
||||
calculateCost,
|
||||
prepareToolExecution,
|
||||
@@ -539,15 +540,11 @@ export const cerebrasProvider: ProviderConfig = {
|
||||
duration: totalDuration,
|
||||
})
|
||||
|
||||
const enhancedError = new Error(error instanceof Error ? error.message : String(error))
|
||||
// @ts-ignore - Adding timing property to error for debugging
|
||||
enhancedError.timing = {
|
||||
throw new ProviderError(error instanceof Error ? error.message : String(error), {
|
||||
startTime: providerStartTimeISO,
|
||||
endTime: providerEndTimeISO,
|
||||
duration: totalDuration,
|
||||
}
|
||||
|
||||
throw enhancedError
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import type {
|
||||
ProviderResponse,
|
||||
TimeSegment,
|
||||
} from '@/providers/types'
|
||||
import { ProviderError } from '@/providers/types'
|
||||
import {
|
||||
calculateCost,
|
||||
prepareToolExecution,
|
||||
@@ -538,15 +539,11 @@ export const deepseekProvider: ProviderConfig = {
|
||||
duration: totalDuration,
|
||||
})
|
||||
|
||||
const enhancedError = new Error(error instanceof Error ? error.message : String(error))
|
||||
// @ts-ignore
|
||||
enhancedError.timing = {
|
||||
throw new ProviderError(error instanceof Error ? error.message : String(error), {
|
||||
startTime: providerStartTimeISO,
|
||||
endTime: providerEndTimeISO,
|
||||
duration: totalDuration,
|
||||
}
|
||||
|
||||
throw enhancedError
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import type {
|
||||
ProviderResponse,
|
||||
TimeSegment,
|
||||
} from '@/providers/types'
|
||||
import { ProviderError } from '@/providers/types'
|
||||
import {
|
||||
calculateCost,
|
||||
prepareToolExecution,
|
||||
@@ -496,15 +497,11 @@ export const groqProvider: ProviderConfig = {
|
||||
duration: totalDuration,
|
||||
})
|
||||
|
||||
const enhancedError = new Error(error instanceof Error ? error.message : String(error))
|
||||
// @ts-ignore
|
||||
enhancedError.timing = {
|
||||
throw new ProviderError(error instanceof Error ? error.message : String(error), {
|
||||
startTime: providerStartTimeISO,
|
||||
endTime: providerEndTimeISO,
|
||||
duration: totalDuration,
|
||||
}
|
||||
|
||||
throw enhancedError
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import type {
|
||||
ProviderResponse,
|
||||
TimeSegment,
|
||||
} from '@/providers/types'
|
||||
import { ProviderError } from '@/providers/types'
|
||||
import {
|
||||
calculateCost,
|
||||
prepareToolExecution,
|
||||
@@ -551,15 +552,11 @@ export const mistralProvider: ProviderConfig = {
|
||||
duration: totalDuration,
|
||||
})
|
||||
|
||||
const enhancedError = new Error(error instanceof Error ? error.message : String(error))
|
||||
// @ts-ignore - Adding timing property to error for debugging
|
||||
enhancedError.timing = {
|
||||
throw new ProviderError(error instanceof Error ? error.message : String(error), {
|
||||
startTime: providerStartTimeISO,
|
||||
endTime: providerEndTimeISO,
|
||||
duration: totalDuration,
|
||||
}
|
||||
|
||||
throw enhancedError
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import type {
|
||||
ProviderResponse,
|
||||
TimeSegment,
|
||||
} from '@/providers/types'
|
||||
import { ProviderError } from '@/providers/types'
|
||||
import { calculateCost, prepareToolExecution } from '@/providers/utils'
|
||||
import { useProvidersStore } from '@/stores/providers'
|
||||
import { executeTool } from '@/tools'
|
||||
@@ -554,15 +555,11 @@ export const ollamaProvider: ProviderConfig = {
|
||||
duration: totalDuration,
|
||||
})
|
||||
|
||||
const enhancedError = new Error(error instanceof Error ? error.message : String(error))
|
||||
// @ts-ignore
|
||||
enhancedError.timing = {
|
||||
throw new ProviderError(error instanceof Error ? error.message : String(error), {
|
||||
startTime: providerStartTimeISO,
|
||||
endTime: providerEndTimeISO,
|
||||
duration: totalDuration,
|
||||
}
|
||||
|
||||
throw enhancedError
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import type OpenAI from 'openai'
|
||||
import type { StreamingExecution } from '@/executor/types'
|
||||
import { MAX_TOOL_ITERATIONS } from '@/providers'
|
||||
import type { Message, ProviderRequest, ProviderResponse, TimeSegment } from '@/providers/types'
|
||||
import { ProviderError } from '@/providers/types'
|
||||
import {
|
||||
calculateCost,
|
||||
prepareToolExecution,
|
||||
@@ -806,14 +807,10 @@ export async function executeResponsesProviderRequest(
|
||||
duration: totalDuration,
|
||||
})
|
||||
|
||||
const enhancedError = new Error(error instanceof Error ? error.message : String(error))
|
||||
// @ts-ignore - Adding timing property to the error
|
||||
enhancedError.timing = {
|
||||
throw new ProviderError(error instanceof Error ? error.message : String(error), {
|
||||
startTime: providerStartTimeISO,
|
||||
endTime: providerEndTimeISO,
|
||||
duration: totalDuration,
|
||||
}
|
||||
|
||||
throw enhancedError
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,11 +10,14 @@ import {
|
||||
supportsNativeStructuredOutputs,
|
||||
} from '@/providers/openrouter/utils'
|
||||
import type {
|
||||
FunctionCallResponse,
|
||||
Message,
|
||||
ProviderConfig,
|
||||
ProviderRequest,
|
||||
ProviderResponse,
|
||||
TimeSegment,
|
||||
} from '@/providers/types'
|
||||
import { ProviderError } from '@/providers/types'
|
||||
import {
|
||||
calculateCost,
|
||||
generateSchemaInstructions,
|
||||
@@ -90,7 +93,7 @@ export const openRouterProvider: ProviderConfig = {
|
||||
stream: !!request.stream,
|
||||
})
|
||||
|
||||
const allMessages = [] as any[]
|
||||
const allMessages: Message[] = []
|
||||
|
||||
if (request.systemPrompt) {
|
||||
allMessages.push({ role: 'system', content: request.systemPrompt })
|
||||
@@ -237,8 +240,8 @@ export const openRouterProvider: ProviderConfig = {
|
||||
output: currentResponse.usage?.completion_tokens || 0,
|
||||
total: currentResponse.usage?.total_tokens || 0,
|
||||
}
|
||||
const toolCalls = [] as any[]
|
||||
const toolResults = [] as any[]
|
||||
const toolCalls: FunctionCallResponse[] = []
|
||||
const toolResults: Record<string, unknown>[] = []
|
||||
const currentMessages = [...allMessages]
|
||||
let iterationCount = 0
|
||||
let modelTime = firstResponseTime
|
||||
@@ -352,7 +355,7 @@ export const openRouterProvider: ProviderConfig = {
|
||||
|
||||
let resultContent: any
|
||||
if (result.success) {
|
||||
toolResults.push(result.output)
|
||||
toolResults.push(result.output!)
|
||||
resultContent = result.output
|
||||
} else {
|
||||
resultContent = {
|
||||
@@ -593,14 +596,11 @@ export const openRouterProvider: ProviderConfig = {
|
||||
}
|
||||
|
||||
logger.error('Error in OpenRouter request:', errorDetails)
|
||||
const enhancedError = new Error(error instanceof Error ? error.message : String(error))
|
||||
// @ts-ignore
|
||||
enhancedError.timing = {
|
||||
throw new ProviderError(error instanceof Error ? error.message : String(error), {
|
||||
startTime: providerStartTimeISO,
|
||||
endTime: providerEndTimeISO,
|
||||
duration: totalDuration,
|
||||
}
|
||||
throw enhancedError
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ export interface FunctionCallResponse {
|
||||
result?: Record<string, any>
|
||||
output?: Record<string, any>
|
||||
input?: Record<string, any>
|
||||
success?: boolean
|
||||
}
|
||||
|
||||
export interface TimeSegment {
|
||||
@@ -177,4 +178,21 @@ export interface ProviderRequest {
|
||||
previousInteractionId?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Typed error class for provider failures that includes timing information.
|
||||
*/
|
||||
export class ProviderError extends Error {
|
||||
timing: {
|
||||
startTime: string
|
||||
endTime: string
|
||||
duration: number
|
||||
}
|
||||
|
||||
constructor(message: string, timing: { startTime: string; endTime: string; duration: number }) {
|
||||
super(message)
|
||||
this.name = 'ProviderError'
|
||||
this.timing = timing
|
||||
}
|
||||
}
|
||||
|
||||
export const providers: Record<string, ProviderConfig> = {}
|
||||
|
||||
@@ -6,11 +6,13 @@ import type { StreamingExecution } from '@/executor/types'
|
||||
import { MAX_TOOL_ITERATIONS } from '@/providers'
|
||||
import { getProviderDefaultModel, getProviderModels } from '@/providers/models'
|
||||
import type {
|
||||
Message,
|
||||
ProviderConfig,
|
||||
ProviderRequest,
|
||||
ProviderResponse,
|
||||
TimeSegment,
|
||||
} from '@/providers/types'
|
||||
import { ProviderError } from '@/providers/types'
|
||||
import {
|
||||
calculateCost,
|
||||
prepareToolExecution,
|
||||
@@ -98,7 +100,7 @@ export const vllmProvider: ProviderConfig = {
|
||||
baseURL: `${baseUrl}/v1`,
|
||||
})
|
||||
|
||||
const allMessages = [] as any[]
|
||||
const allMessages: Message[] = []
|
||||
|
||||
if (request.systemPrompt) {
|
||||
allMessages.push({
|
||||
@@ -635,23 +637,11 @@ export const vllmProvider: ProviderConfig = {
|
||||
duration: totalDuration,
|
||||
})
|
||||
|
||||
const enhancedError = new Error(errorMessage)
|
||||
// @ts-ignore
|
||||
enhancedError.timing = {
|
||||
throw new ProviderError(errorMessage, {
|
||||
startTime: providerStartTimeISO,
|
||||
endTime: providerEndTimeISO,
|
||||
duration: totalDuration,
|
||||
}
|
||||
if (errorType) {
|
||||
// @ts-ignore
|
||||
enhancedError.vllmErrorType = errorType
|
||||
}
|
||||
if (errorCode) {
|
||||
// @ts-ignore
|
||||
enhancedError.vllmErrorCode = errorCode
|
||||
}
|
||||
|
||||
throw enhancedError
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@@ -5,11 +5,13 @@ import type { StreamingExecution } from '@/executor/types'
|
||||
import { MAX_TOOL_ITERATIONS } from '@/providers'
|
||||
import { getProviderDefaultModel, getProviderModels } from '@/providers/models'
|
||||
import type {
|
||||
Message,
|
||||
ProviderConfig,
|
||||
ProviderRequest,
|
||||
ProviderResponse,
|
||||
TimeSegment,
|
||||
} from '@/providers/types'
|
||||
import { ProviderError } from '@/providers/types'
|
||||
import {
|
||||
calculateCost,
|
||||
prepareToolExecution,
|
||||
@@ -52,7 +54,7 @@ export const xAIProvider: ProviderConfig = {
|
||||
streaming: !!request.stream,
|
||||
})
|
||||
|
||||
const allMessages: any[] = []
|
||||
const allMessages: Message[] = []
|
||||
|
||||
if (request.systemPrompt) {
|
||||
allMessages.push({
|
||||
@@ -587,15 +589,11 @@ export const xAIProvider: ProviderConfig = {
|
||||
hasResponseFormat: !!request.responseFormat,
|
||||
})
|
||||
|
||||
const enhancedError = new Error(error instanceof Error ? error.message : String(error))
|
||||
// @ts-ignore - Adding timing property to error for debugging
|
||||
enhancedError.timing = {
|
||||
throw new ProviderError(error instanceof Error ? error.message : String(error), {
|
||||
startTime: providerStartTimeISO,
|
||||
endTime: providerEndTimeISO,
|
||||
duration: totalDuration,
|
||||
}
|
||||
|
||||
throw enhancedError
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@@ -26,12 +26,6 @@ export const actionsListTool: ToolConfig<
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter actions by incident ID (e.g., "01FCNDV6P870EA6S7TK1DSYDG0")',
|
||||
},
|
||||
page_size: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of actions to return per page (e.g., 10, 25, 50)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
@@ -42,10 +36,6 @@ export const actionsListTool: ToolConfig<
|
||||
url.searchParams.append('incident_id', params.incident_id)
|
||||
}
|
||||
|
||||
if (params.page_size) {
|
||||
url.searchParams.append('page_size', params.page_size.toString())
|
||||
}
|
||||
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
|
||||
@@ -20,22 +20,10 @@ export const escalationsListTool: ToolConfig<
|
||||
visibility: 'user-only',
|
||||
description: 'incident.io API Key',
|
||||
},
|
||||
page_size: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of results per page (e.g., 10, 25, 50). Default: 25',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL('https://api.incident.io/v2/escalations')
|
||||
if (params.page_size) {
|
||||
url.searchParams.append('page_size', params.page_size.toString())
|
||||
}
|
||||
return url.toString()
|
||||
},
|
||||
url: () => 'https://api.incident.io/v2/escalations',
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/json',
|
||||
|
||||
@@ -26,12 +26,6 @@ export const followUpsListTool: ToolConfig<
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter follow-ups by incident ID (e.g., "01FCNDV6P870EA6S7TK1DSYDG0")',
|
||||
},
|
||||
page_size: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of follow-ups to return per page (e.g., 10, 25, 50)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
@@ -42,10 +36,6 @@ export const followUpsListTool: ToolConfig<
|
||||
url.searchParams.append('incident_id', params.incident_id)
|
||||
}
|
||||
|
||||
if (params.page_size) {
|
||||
url.searchParams.append('page_size', params.page_size.toString())
|
||||
}
|
||||
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
|
||||
@@ -396,7 +396,6 @@ export interface IncidentioIncidentsUpdateResponse extends ToolResponse {
|
||||
// Action types
|
||||
export interface IncidentioActionsListParams extends IncidentioBaseParams {
|
||||
incident_id?: string
|
||||
page_size?: number
|
||||
}
|
||||
|
||||
export interface IncidentioAction {
|
||||
@@ -446,7 +445,6 @@ export interface IncidentioActionsShowResponse extends ToolResponse {
|
||||
// Follow-up types
|
||||
export interface IncidentioFollowUpsListParams extends IncidentioBaseParams {
|
||||
incident_id?: string
|
||||
page_size?: number
|
||||
}
|
||||
|
||||
export interface IncidentioFollowUp {
|
||||
@@ -664,6 +662,7 @@ export interface CustomFieldsDeleteResponse extends ToolResponse {
|
||||
// Users list tool types
|
||||
export interface IncidentioUsersListParams extends IncidentioBaseParams {
|
||||
page_size?: number
|
||||
after?: string
|
||||
}
|
||||
|
||||
export interface IncidentioUser {
|
||||
@@ -676,6 +675,11 @@ export interface IncidentioUser {
|
||||
export interface IncidentioUsersListResponse extends ToolResponse {
|
||||
output: {
|
||||
users: IncidentioUser[]
|
||||
pagination_meta?: {
|
||||
after: string
|
||||
page_size: number
|
||||
total_record_count?: number
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -786,9 +790,7 @@ export type IncidentioResponse =
|
||||
| IncidentioEscalationPathsDeleteResponse
|
||||
|
||||
// Escalations types
|
||||
export interface IncidentioEscalationsListParams extends IncidentioBaseParams {
|
||||
page_size?: number
|
||||
}
|
||||
export interface IncidentioEscalationsListParams extends IncidentioBaseParams {}
|
||||
|
||||
export interface IncidentioEscalation {
|
||||
id: string
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type {
|
||||
IncidentioUsersListParams,
|
||||
IncidentioUsersListResponse,
|
||||
import {
|
||||
INCIDENTIO_PAGINATION_OUTPUT_PROPERTIES,
|
||||
type IncidentioUsersListParams,
|
||||
type IncidentioUsersListResponse,
|
||||
} from '@/tools/incidentio/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
@@ -24,15 +25,27 @@ export const usersListTool: ToolConfig<IncidentioUsersListParams, IncidentioUser
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of results to return per page (e.g., 10, 25, 50). Default: 25',
|
||||
},
|
||||
after: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Pagination cursor to fetch the next page of results',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const baseUrl = 'https://api.incident.io/v2/users'
|
||||
const url = new URL('https://api.incident.io/v2/users')
|
||||
|
||||
if (params.page_size) {
|
||||
return `${baseUrl}?page_size=${params.page_size}`
|
||||
url.searchParams.append('page_size', params.page_size.toString())
|
||||
}
|
||||
return baseUrl
|
||||
|
||||
if (params.after) {
|
||||
url.searchParams.append('after', params.after)
|
||||
}
|
||||
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
@@ -53,6 +66,13 @@ export const usersListTool: ToolConfig<IncidentioUsersListParams, IncidentioUser
|
||||
email: user.email,
|
||||
role: user.role,
|
||||
})),
|
||||
pagination_meta: data.pagination_meta
|
||||
? {
|
||||
after: data.pagination_meta.after,
|
||||
page_size: data.pagination_meta.page_size,
|
||||
total_record_count: data.pagination_meta.total_record_count,
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
}
|
||||
},
|
||||
@@ -71,5 +91,11 @@ export const usersListTool: ToolConfig<IncidentioUsersListParams, IncidentioUser
|
||||
},
|
||||
},
|
||||
},
|
||||
pagination_meta: {
|
||||
type: 'object',
|
||||
description: 'Pagination metadata',
|
||||
optional: true,
|
||||
properties: INCIDENTIO_PAGINATION_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -24,23 +24,11 @@ export const pipedriveGetActivitiesTool: ToolConfig<
|
||||
visibility: 'hidden',
|
||||
description: 'The access token for the Pipedrive API',
|
||||
},
|
||||
deal_id: {
|
||||
user_id: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter activities by deal ID (e.g., "123")',
|
||||
},
|
||||
person_id: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter activities by person ID (e.g., "456")',
|
||||
},
|
||||
org_id: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter activities by organization ID (e.g., "789")',
|
||||
description: 'Filter activities by user ID (e.g., "123")',
|
||||
},
|
||||
type: {
|
||||
type: 'string',
|
||||
@@ -60,6 +48,12 @@ export const pipedriveGetActivitiesTool: ToolConfig<
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of results to return (e.g., "50", default: 100, max: 500)',
|
||||
},
|
||||
start: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Pagination start offset (0-based index of the first item to return)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
@@ -67,12 +61,11 @@ export const pipedriveGetActivitiesTool: ToolConfig<
|
||||
const baseUrl = 'https://api.pipedrive.com/v1/activities'
|
||||
const queryParams = new URLSearchParams()
|
||||
|
||||
if (params.deal_id) queryParams.append('deal_id', params.deal_id)
|
||||
if (params.person_id) queryParams.append('person_id', params.person_id)
|
||||
if (params.org_id) queryParams.append('org_id', params.org_id)
|
||||
if (params.user_id) queryParams.append('user_id', params.user_id)
|
||||
if (params.type) queryParams.append('type', params.type)
|
||||
if (params.done) queryParams.append('done', params.done)
|
||||
if (params.limit) queryParams.append('limit', params.limit)
|
||||
if (params.start) queryParams.append('start', params.start)
|
||||
|
||||
const queryString = queryParams.toString()
|
||||
return queryString ? `${baseUrl}?${queryString}` : baseUrl
|
||||
@@ -99,12 +92,16 @@ export const pipedriveGetActivitiesTool: ToolConfig<
|
||||
}
|
||||
|
||||
const activities = data.data || []
|
||||
const hasMore = data.additional_data?.pagination?.more_items_in_collection || false
|
||||
const nextStart = data.additional_data?.pagination?.next_start ?? null
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
activities,
|
||||
total_items: activities.length,
|
||||
has_more: hasMore,
|
||||
next_start: nextStart,
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
@@ -120,6 +117,16 @@ export const pipedriveGetActivitiesTool: ToolConfig<
|
||||
},
|
||||
},
|
||||
total_items: { type: 'number', description: 'Total number of activities returned' },
|
||||
has_more: {
|
||||
type: 'boolean',
|
||||
description: 'Whether more activities are available',
|
||||
optional: true,
|
||||
},
|
||||
next_start: {
|
||||
type: 'number',
|
||||
description: 'Offset for fetching the next page',
|
||||
optional: true,
|
||||
},
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -67,6 +67,12 @@ export const pipedriveGetAllDealsTool: ToolConfig<
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of results to return (e.g., "50", default: 100, max: 500)',
|
||||
},
|
||||
cursor: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'For pagination, the marker representing the first item on the next page',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
@@ -81,6 +87,7 @@ export const pipedriveGetAllDealsTool: ToolConfig<
|
||||
if (params.pipeline_id) queryParams.append('pipeline_id', params.pipeline_id)
|
||||
if (params.updated_since) queryParams.append('updated_since', params.updated_since)
|
||||
if (params.limit) queryParams.append('limit', params.limit)
|
||||
if (params.cursor) queryParams.append('cursor', params.cursor)
|
||||
|
||||
const queryString = queryParams.toString()
|
||||
return queryString ? `${baseUrl}?${queryString}` : baseUrl
|
||||
@@ -107,7 +114,8 @@ export const pipedriveGetAllDealsTool: ToolConfig<
|
||||
}
|
||||
|
||||
const deals = data.data || []
|
||||
const hasMore = data.additional_data?.pagination?.more_items_in_collection || false
|
||||
const nextCursor = data.additional_data?.next_cursor ?? null
|
||||
const hasMore = nextCursor !== null
|
||||
|
||||
return {
|
||||
success: true,
|
||||
@@ -116,6 +124,7 @@ export const pipedriveGetAllDealsTool: ToolConfig<
|
||||
metadata: {
|
||||
total_items: deals.length,
|
||||
has_more: hasMore,
|
||||
next_cursor: nextCursor,
|
||||
},
|
||||
success: true,
|
||||
},
|
||||
|
||||
@@ -16,29 +16,23 @@ export const pipedriveGetFilesTool: ToolConfig<PipedriveGetFilesParams, Pipedriv
|
||||
visibility: 'hidden',
|
||||
description: 'The access token for the Pipedrive API',
|
||||
},
|
||||
deal_id: {
|
||||
sort: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter files by deal ID (e.g., "123")',
|
||||
},
|
||||
person_id: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter files by person ID (e.g., "456")',
|
||||
},
|
||||
org_id: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter files by organization ID (e.g., "789")',
|
||||
description: 'Sort files by field (supported: "id", "update_time")',
|
||||
},
|
||||
limit: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of results to return (e.g., "50", default: 100, max: 500)',
|
||||
description: 'Number of results to return (e.g., "50", default: 100, max: 100)',
|
||||
},
|
||||
start: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Pagination start offset (0-based index of the first item to return)',
|
||||
},
|
||||
downloadFiles: {
|
||||
type: 'boolean',
|
||||
@@ -56,10 +50,9 @@ export const pipedriveGetFilesTool: ToolConfig<PipedriveGetFilesParams, Pipedriv
|
||||
}),
|
||||
body: (params) => ({
|
||||
accessToken: params.accessToken,
|
||||
deal_id: params.deal_id,
|
||||
person_id: params.person_id,
|
||||
org_id: params.org_id,
|
||||
sort: params.sort,
|
||||
limit: params.limit,
|
||||
start: params.start,
|
||||
downloadFiles: params.downloadFiles,
|
||||
}),
|
||||
},
|
||||
@@ -79,6 +72,16 @@ export const pipedriveGetFilesTool: ToolConfig<PipedriveGetFilesParams, Pipedriv
|
||||
optional: true,
|
||||
},
|
||||
total_items: { type: 'number', description: 'Total number of files returned' },
|
||||
has_more: {
|
||||
type: 'boolean',
|
||||
description: 'Whether more files are available',
|
||||
optional: true,
|
||||
},
|
||||
next_start: {
|
||||
type: 'number',
|
||||
description: 'Offset for fetching the next page',
|
||||
optional: true,
|
||||
},
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -60,6 +60,12 @@ export const pipedriveGetLeadsTool: ToolConfig<PipedriveGetLeadsParams, Pipedriv
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of results to return (e.g., "50", default: 100, max: 500)',
|
||||
},
|
||||
start: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Pagination start offset (0-based index of the first item to return)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
@@ -81,6 +87,7 @@ export const pipedriveGetLeadsTool: ToolConfig<PipedriveGetLeadsParams, Pipedriv
|
||||
if (params.person_id) queryParams.append('person_id', params.person_id)
|
||||
if (params.organization_id) queryParams.append('organization_id', params.organization_id)
|
||||
if (params.limit) queryParams.append('limit', params.limit)
|
||||
if (params.start) queryParams.append('start', params.start)
|
||||
|
||||
const queryString = queryParams.toString()
|
||||
return queryString ? `${baseUrl}?${queryString}` : baseUrl
|
||||
@@ -119,12 +126,19 @@ export const pipedriveGetLeadsTool: ToolConfig<PipedriveGetLeadsParams, Pipedriv
|
||||
|
||||
// Otherwise, return list of leads
|
||||
const leads = data.data || []
|
||||
// Leads endpoint puts pagination fields directly on additional_data (no .pagination wrapper)
|
||||
const hasMore = data.additional_data?.more_items_in_collection || false
|
||||
const currentStart = data.additional_data?.start ?? 0
|
||||
const currentLimit = data.additional_data?.limit ?? leads.length
|
||||
const nextStart = hasMore ? currentStart + currentLimit : null
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
leads,
|
||||
total_items: leads.length,
|
||||
has_more: hasMore,
|
||||
next_start: nextStart,
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
@@ -151,6 +165,16 @@ export const pipedriveGetLeadsTool: ToolConfig<PipedriveGetLeadsParams, Pipedriv
|
||||
description: 'Total number of leads returned',
|
||||
optional: true,
|
||||
},
|
||||
has_more: {
|
||||
type: 'boolean',
|
||||
description: 'Whether more leads are available',
|
||||
optional: true,
|
||||
},
|
||||
next_start: {
|
||||
type: 'number',
|
||||
description: 'Offset for fetching the next page',
|
||||
optional: true,
|
||||
},
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -40,6 +40,12 @@ export const pipedriveGetMailMessagesTool: ToolConfig<
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of results to return (e.g., "25", default: 50)',
|
||||
},
|
||||
start: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Pagination start offset (0-based index of the first item to return)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
@@ -49,6 +55,7 @@ export const pipedriveGetMailMessagesTool: ToolConfig<
|
||||
|
||||
if (params.folder) queryParams.append('folder', params.folder)
|
||||
if (params.limit) queryParams.append('limit', params.limit)
|
||||
if (params.start) queryParams.append('start', params.start)
|
||||
|
||||
const queryString = queryParams.toString()
|
||||
return queryString ? `${baseUrl}?${queryString}` : baseUrl
|
||||
@@ -75,12 +82,16 @@ export const pipedriveGetMailMessagesTool: ToolConfig<
|
||||
}
|
||||
|
||||
const threads = data.data || []
|
||||
const hasMore = data.additional_data?.pagination?.more_items_in_collection || false
|
||||
const nextStart = data.additional_data?.pagination?.next_start ?? null
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
messages: threads,
|
||||
total_items: threads.length,
|
||||
has_more: hasMore,
|
||||
next_start: nextStart,
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
@@ -89,6 +100,16 @@ export const pipedriveGetMailMessagesTool: ToolConfig<
|
||||
outputs: {
|
||||
messages: { type: 'array', description: 'Array of mail thread objects from Pipedrive mailbox' },
|
||||
total_items: { type: 'number', description: 'Total number of mail threads returned' },
|
||||
has_more: {
|
||||
type: 'boolean',
|
||||
description: 'Whether more messages are available',
|
||||
optional: true,
|
||||
},
|
||||
next_start: {
|
||||
type: 'number',
|
||||
description: 'Offset for fetching the next page',
|
||||
optional: true,
|
||||
},
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -35,18 +35,18 @@ export const pipedriveGetPipelineDealsTool: ToolConfig<
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by specific stage within the pipeline (e.g., "2")',
|
||||
},
|
||||
status: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by deal status: open, won, lost',
|
||||
},
|
||||
limit: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of results to return (e.g., "50", default: 100, max: 500)',
|
||||
},
|
||||
start: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Pagination start offset (0-based index of the first item to return)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
@@ -55,8 +55,8 @@ export const pipedriveGetPipelineDealsTool: ToolConfig<
|
||||
const queryParams = new URLSearchParams()
|
||||
|
||||
if (params.stage_id) queryParams.append('stage_id', params.stage_id)
|
||||
if (params.status) queryParams.append('status', params.status)
|
||||
if (params.limit) queryParams.append('limit', params.limit)
|
||||
if (params.start) queryParams.append('start', params.start)
|
||||
|
||||
const queryString = queryParams.toString()
|
||||
return queryString ? `${baseUrl}?${queryString}` : baseUrl
|
||||
@@ -83,6 +83,8 @@ export const pipedriveGetPipelineDealsTool: ToolConfig<
|
||||
}
|
||||
|
||||
const deals = data.data || []
|
||||
const hasMore = data.additional_data?.pagination?.more_items_in_collection || false
|
||||
const nextStart = data.additional_data?.pagination?.next_start ?? null
|
||||
|
||||
return {
|
||||
success: true,
|
||||
@@ -91,6 +93,8 @@ export const pipedriveGetPipelineDealsTool: ToolConfig<
|
||||
metadata: {
|
||||
pipeline_id: params?.pipeline_id || '',
|
||||
total_items: deals.length,
|
||||
has_more: hasMore,
|
||||
next_start: nextStart,
|
||||
},
|
||||
success: true,
|
||||
},
|
||||
|
||||
@@ -42,11 +42,11 @@ export const pipedriveGetPipelinesTool: ToolConfig<
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of results to return (e.g., "50", default: 100, max: 500)',
|
||||
},
|
||||
cursor: {
|
||||
start: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'For pagination, the marker representing the first item on the next page',
|
||||
description: 'Pagination start offset (0-based index of the first item to return)',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -58,7 +58,7 @@ export const pipedriveGetPipelinesTool: ToolConfig<
|
||||
if (params.sort_by) queryParams.append('sort_by', params.sort_by)
|
||||
if (params.sort_direction) queryParams.append('sort_direction', params.sort_direction)
|
||||
if (params.limit) queryParams.append('limit', params.limit)
|
||||
if (params.cursor) queryParams.append('cursor', params.cursor)
|
||||
if (params.start) queryParams.append('start', params.start)
|
||||
|
||||
const queryString = queryParams.toString()
|
||||
return queryString ? `${baseUrl}?${queryString}` : baseUrl
|
||||
@@ -85,12 +85,16 @@ export const pipedriveGetPipelinesTool: ToolConfig<
|
||||
}
|
||||
|
||||
const pipelines = data.data || []
|
||||
const hasMore = data.additional_data?.pagination?.more_items_in_collection || false
|
||||
const nextStart = data.additional_data?.pagination?.next_start ?? null
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
pipelines,
|
||||
total_items: pipelines.length,
|
||||
has_more: hasMore,
|
||||
next_start: nextStart,
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
@@ -106,6 +110,16 @@ export const pipedriveGetPipelinesTool: ToolConfig<
|
||||
},
|
||||
},
|
||||
total_items: { type: 'number', description: 'Total number of pipelines returned' },
|
||||
has_more: {
|
||||
type: 'boolean',
|
||||
description: 'Whether more pipelines are available',
|
||||
optional: true,
|
||||
},
|
||||
next_start: {
|
||||
type: 'number',
|
||||
description: 'Offset for fetching the next page',
|
||||
optional: true,
|
||||
},
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -42,6 +42,12 @@ export const pipedriveGetProjectsTool: ToolConfig<
|
||||
description:
|
||||
'Number of results to return (e.g., "50", default: 100, max: 500, only for listing all)',
|
||||
},
|
||||
cursor: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'For pagination, the marker representing the first item on the next page',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
@@ -57,6 +63,7 @@ export const pipedriveGetProjectsTool: ToolConfig<
|
||||
|
||||
if (params.status) queryParams.append('status', params.status)
|
||||
if (params.limit) queryParams.append('limit', params.limit)
|
||||
if (params.cursor) queryParams.append('cursor', params.cursor)
|
||||
|
||||
const queryString = queryParams.toString()
|
||||
return queryString ? `${baseUrl}?${queryString}` : baseUrl
|
||||
@@ -95,12 +102,16 @@ export const pipedriveGetProjectsTool: ToolConfig<
|
||||
|
||||
// Otherwise, return list of projects
|
||||
const projects = data.data || []
|
||||
const nextCursor = data.additional_data?.next_cursor ?? null
|
||||
const hasMore = nextCursor !== null
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
projects,
|
||||
total_items: projects.length,
|
||||
has_more: hasMore,
|
||||
next_cursor: nextCursor,
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
@@ -122,6 +133,16 @@ export const pipedriveGetProjectsTool: ToolConfig<
|
||||
description: 'Total number of projects returned',
|
||||
optional: true,
|
||||
},
|
||||
has_more: {
|
||||
type: 'boolean',
|
||||
description: 'Whether more projects are available',
|
||||
optional: true,
|
||||
},
|
||||
next_cursor: {
|
||||
type: 'string',
|
||||
description: 'Cursor for fetching the next page',
|
||||
optional: true,
|
||||
},
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -239,6 +239,16 @@ export const PIPEDRIVE_MAIL_MESSAGE_OUTPUT: OutputProperty = {
|
||||
export const PIPEDRIVE_METADATA_OUTPUT_PROPERTIES = {
|
||||
total_items: { type: 'number', description: 'Total number of items' },
|
||||
has_more: { type: 'boolean', description: 'Whether more items are available', optional: true },
|
||||
next_cursor: {
|
||||
type: 'string',
|
||||
description: 'Cursor for fetching the next page (v2 endpoints)',
|
||||
optional: true,
|
||||
},
|
||||
next_start: {
|
||||
type: 'number',
|
||||
description: 'Offset for fetching the next page (v1 endpoints)',
|
||||
optional: true,
|
||||
},
|
||||
} as const satisfies Record<string, OutputProperty>
|
||||
|
||||
// Common Pipedrive types
|
||||
@@ -355,6 +365,7 @@ export interface PipedriveGetAllDealsParams {
|
||||
pipeline_id?: string
|
||||
updated_since?: string
|
||||
limit?: string
|
||||
cursor?: string
|
||||
}
|
||||
|
||||
export interface PipedriveGetAllDealsOutput {
|
||||
@@ -362,6 +373,7 @@ export interface PipedriveGetAllDealsOutput {
|
||||
metadata: {
|
||||
total_items: number
|
||||
has_more: boolean
|
||||
next_cursor?: string
|
||||
}
|
||||
success: boolean
|
||||
}
|
||||
@@ -431,10 +443,9 @@ export interface PipedriveUpdateDealResponse extends ToolResponse {
|
||||
// GET Files
|
||||
export interface PipedriveGetFilesParams {
|
||||
accessToken: string
|
||||
deal_id?: string
|
||||
person_id?: string
|
||||
org_id?: string
|
||||
sort?: string
|
||||
limit?: string
|
||||
start?: string
|
||||
downloadFiles?: boolean
|
||||
}
|
||||
|
||||
@@ -442,6 +453,8 @@ export interface PipedriveGetFilesOutput {
|
||||
files: PipedriveFile[]
|
||||
downloadedFiles?: ToolFileData[]
|
||||
total_items: number
|
||||
has_more?: boolean
|
||||
next_start?: number
|
||||
success: boolean
|
||||
}
|
||||
|
||||
@@ -453,11 +466,14 @@ export interface PipedriveGetMailMessagesParams {
|
||||
accessToken: string
|
||||
folder?: string
|
||||
limit?: string
|
||||
start?: string
|
||||
}
|
||||
|
||||
export interface PipedriveGetMailMessagesOutput {
|
||||
messages: PipedriveMailMessage[]
|
||||
total_items: number
|
||||
has_more?: boolean
|
||||
next_start?: number
|
||||
success: boolean
|
||||
}
|
||||
|
||||
@@ -490,12 +506,14 @@ export interface PipedriveGetPipelinesParams {
|
||||
sort_by?: string
|
||||
sort_direction?: string
|
||||
limit?: string
|
||||
cursor?: string
|
||||
start?: string
|
||||
}
|
||||
|
||||
export interface PipedriveGetPipelinesOutput {
|
||||
pipelines: PipedrivePipeline[]
|
||||
total_items: number
|
||||
has_more?: boolean
|
||||
next_start?: number
|
||||
success: boolean
|
||||
}
|
||||
|
||||
@@ -508,8 +526,8 @@ export interface PipedriveGetPipelineDealsParams {
|
||||
accessToken: string
|
||||
pipeline_id: string
|
||||
stage_id?: string
|
||||
status?: string
|
||||
limit?: string
|
||||
start?: string
|
||||
}
|
||||
|
||||
export interface PipedriveGetPipelineDealsOutput {
|
||||
@@ -517,6 +535,8 @@ export interface PipedriveGetPipelineDealsOutput {
|
||||
metadata: {
|
||||
pipeline_id: string
|
||||
total_items: number
|
||||
has_more?: boolean
|
||||
next_start?: number
|
||||
}
|
||||
success: boolean
|
||||
}
|
||||
@@ -531,12 +551,15 @@ export interface PipedriveGetProjectsParams {
|
||||
project_id?: string
|
||||
status?: string
|
||||
limit?: string
|
||||
cursor?: string
|
||||
}
|
||||
|
||||
export interface PipedriveGetProjectsOutput {
|
||||
projects?: PipedriveProject[]
|
||||
project?: PipedriveProject
|
||||
total_items?: number
|
||||
has_more?: boolean
|
||||
next_cursor?: string
|
||||
success: boolean
|
||||
}
|
||||
|
||||
@@ -565,17 +588,18 @@ export interface PipedriveCreateProjectResponse extends ToolResponse {
|
||||
// GET All Activities
|
||||
export interface PipedriveGetActivitiesParams {
|
||||
accessToken: string
|
||||
deal_id?: string
|
||||
person_id?: string
|
||||
org_id?: string
|
||||
user_id?: string
|
||||
type?: string
|
||||
done?: string
|
||||
limit?: string
|
||||
start?: string
|
||||
}
|
||||
|
||||
export interface PipedriveGetActivitiesOutput {
|
||||
activities: PipedriveActivity[]
|
||||
total_items: number
|
||||
has_more?: boolean
|
||||
next_start?: number
|
||||
success: boolean
|
||||
}
|
||||
|
||||
@@ -636,12 +660,15 @@ export interface PipedriveGetLeadsParams {
|
||||
person_id?: string
|
||||
organization_id?: string
|
||||
limit?: string
|
||||
start?: string
|
||||
}
|
||||
|
||||
export interface PipedriveGetLeadsOutput {
|
||||
leads?: PipedriveLead[]
|
||||
lead?: PipedriveLead
|
||||
total_items?: number
|
||||
has_more?: boolean
|
||||
next_start?: number
|
||||
success: boolean
|
||||
}
|
||||
|
||||
|
||||
@@ -51,6 +51,12 @@ export const queryTool: ToolConfig<SupabaseQueryParams, SupabaseQueryResponse> =
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of rows to return',
|
||||
},
|
||||
offset: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of rows to skip (for pagination)',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
@@ -91,10 +97,15 @@ export const queryTool: ToolConfig<SupabaseQueryParams, SupabaseQueryResponse> =
|
||||
}
|
||||
|
||||
// Add limit if provided
|
||||
if (params.limit) {
|
||||
if (params.limit !== undefined && params.limit !== null) {
|
||||
url += `&limit=${Number(params.limit)}`
|
||||
}
|
||||
|
||||
// Add offset if provided
|
||||
if (params.offset !== undefined && params.offset !== null) {
|
||||
url += `&offset=${Number(params.offset)}`
|
||||
}
|
||||
|
||||
return url
|
||||
},
|
||||
method: 'GET',
|
||||
|
||||
@@ -57,6 +57,12 @@ export const textSearchTool: ToolConfig<SupabaseTextSearchParams, SupabaseTextSe
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of rows to return',
|
||||
},
|
||||
offset: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of rows to skip (for pagination)',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
@@ -74,8 +80,9 @@ export const textSearchTool: ToolConfig<SupabaseTextSearchParams, SupabaseTextSe
|
||||
let url = `https://${params.projectId}.supabase.co/rest/v1/${params.table}?select=*`
|
||||
|
||||
// Map search types to PostgREST operators
|
||||
// plfts = plainto_tsquery (natural language), phfts = phraseto_tsquery, wfts = websearch_to_tsquery
|
||||
const operatorMap: Record<string, string> = {
|
||||
plain: 'fts',
|
||||
plain: 'plfts',
|
||||
phrase: 'phfts',
|
||||
websearch: 'wfts',
|
||||
}
|
||||
@@ -86,10 +93,15 @@ export const textSearchTool: ToolConfig<SupabaseTextSearchParams, SupabaseTextSe
|
||||
url += `&${params.column}=${operator}(${language}).${encodeURIComponent(params.query)}`
|
||||
|
||||
// Add limit if provided
|
||||
if (params.limit) {
|
||||
if (params.limit !== undefined && params.limit !== null) {
|
||||
url += `&limit=${Number(params.limit)}`
|
||||
}
|
||||
|
||||
// Add offset if provided
|
||||
if (params.offset !== undefined && params.offset !== null) {
|
||||
url += `&offset=${Number(params.offset)}`
|
||||
}
|
||||
|
||||
return url
|
||||
},
|
||||
method: 'GET',
|
||||
|
||||
@@ -315,6 +315,7 @@ export interface SupabaseQueryParams {
|
||||
filter?: string
|
||||
orderBy?: string
|
||||
limit?: number
|
||||
offset?: number
|
||||
}
|
||||
|
||||
export interface SupabaseInsertParams {
|
||||
@@ -413,6 +414,7 @@ export interface SupabaseTextSearchParams {
|
||||
searchType?: string
|
||||
language?: string
|
||||
limit?: number
|
||||
offset?: number
|
||||
}
|
||||
|
||||
export interface SupabaseTextSearchResponse extends SupabaseBaseResponse {}
|
||||
|
||||
@@ -26,6 +26,18 @@ export const responsesTool: ToolConfig<TypeformResponsesParams, TypeformResponse
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of responses to retrieve (e.g., 10, 25, 50)',
|
||||
},
|
||||
before: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Cursor token for fetching the next page of older responses',
|
||||
},
|
||||
after: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Cursor token for fetching the next page of newer responses',
|
||||
},
|
||||
since: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
@@ -56,6 +68,14 @@ export const responsesTool: ToolConfig<TypeformResponsesParams, TypeformResponse
|
||||
queryParams.push(`page_size=${Number(params.pageSize)}`)
|
||||
}
|
||||
|
||||
if (params.before) {
|
||||
queryParams.push(`before=${encodeURIComponent(params.before)}`)
|
||||
}
|
||||
|
||||
if (params.after) {
|
||||
queryParams.push(`after=${encodeURIComponent(params.after)}`)
|
||||
}
|
||||
|
||||
if (params.since) {
|
||||
queryParams.push(`since=${encodeURIComponent(params.since)}`)
|
||||
}
|
||||
|
||||
@@ -62,6 +62,8 @@ export interface TypeformResponsesParams {
|
||||
formId: string
|
||||
apiKey: string
|
||||
pageSize?: number
|
||||
before?: string
|
||||
after?: string
|
||||
since?: string
|
||||
until?: string
|
||||
completed?: string
|
||||
|
||||
@@ -21,9 +21,9 @@ export interface ZendeskAutocompleteOrganizationsResponse {
|
||||
output: {
|
||||
organizations: any[]
|
||||
paging?: {
|
||||
after_cursor: string | null
|
||||
has_more: boolean
|
||||
next_page?: string | null
|
||||
previous_page?: string | null
|
||||
count: number
|
||||
}
|
||||
metadata: {
|
||||
total_returned: number
|
||||
@@ -78,7 +78,7 @@ export const zendeskAutocompleteOrganizationsTool: ToolConfig<
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number as a string (e.g., "1", "2")',
|
||||
description: 'Page number for pagination (1-based)',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -86,8 +86,8 @@ export const zendeskAutocompleteOrganizationsTool: ToolConfig<
|
||||
url: (params) => {
|
||||
const queryParams = new URLSearchParams()
|
||||
queryParams.append('name', params.name)
|
||||
if (params.page) queryParams.append('page', params.page)
|
||||
if (params.perPage) queryParams.append('per_page', params.perPage)
|
||||
if (params.page) queryParams.append('page', params.page)
|
||||
|
||||
const query = queryParams.toString()
|
||||
const url = buildZendeskUrl(params.subdomain, '/organizations/autocomplete')
|
||||
@@ -112,19 +112,22 @@ export const zendeskAutocompleteOrganizationsTool: ToolConfig<
|
||||
|
||||
const data = await response.json()
|
||||
const organizations = data.organizations || []
|
||||
const hasMore = data.next_page !== null && data.next_page !== undefined
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
organizations,
|
||||
// /organizations/autocomplete uses offset pagination (page/per_page), not cursor pagination.
|
||||
// after_cursor is always null; use next_page URL or page param for subsequent pages.
|
||||
paging: {
|
||||
after_cursor: null,
|
||||
has_more: hasMore,
|
||||
next_page: data.next_page ?? null,
|
||||
previous_page: data.previous_page ?? null,
|
||||
count: data.count || organizations.length,
|
||||
},
|
||||
metadata: {
|
||||
total_returned: organizations.length,
|
||||
has_more: !!data.next_page,
|
||||
has_more: hasMore,
|
||||
},
|
||||
success: true,
|
||||
},
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import {
|
||||
appendCursorPaginationParams,
|
||||
buildZendeskUrl,
|
||||
extractCursorPagingInfo,
|
||||
handleZendeskError,
|
||||
METADATA_OUTPUT,
|
||||
ORGANIZATIONS_ARRAY_OUTPUT,
|
||||
@@ -12,7 +14,7 @@ export interface ZendeskGetOrganizationsParams {
|
||||
apiToken: string
|
||||
subdomain: string
|
||||
perPage?: string
|
||||
page?: string
|
||||
pageAfter?: string
|
||||
}
|
||||
|
||||
export interface ZendeskGetOrganizationsResponse {
|
||||
@@ -20,9 +22,8 @@ export interface ZendeskGetOrganizationsResponse {
|
||||
output: {
|
||||
organizations: any[]
|
||||
paging?: {
|
||||
next_page?: string | null
|
||||
previous_page?: string | null
|
||||
count: number
|
||||
after_cursor: string | null
|
||||
has_more: boolean
|
||||
}
|
||||
metadata: {
|
||||
total_returned: number
|
||||
@@ -66,19 +67,18 @@ export const zendeskGetOrganizationsTool: ToolConfig<
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Results per page as a number string (default: "100", max: "100")',
|
||||
},
|
||||
page: {
|
||||
pageAfter: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number as a string (e.g., "1", "2")',
|
||||
description: 'Cursor from a previous response to fetch the next page of results',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const queryParams = new URLSearchParams()
|
||||
if (params.page) queryParams.append('page', params.page)
|
||||
if (params.perPage) queryParams.append('per_page', params.perPage)
|
||||
appendCursorPaginationParams(queryParams, params)
|
||||
|
||||
const query = queryParams.toString()
|
||||
const url = buildZendeskUrl(params.subdomain, '/organizations')
|
||||
@@ -103,19 +103,16 @@ export const zendeskGetOrganizationsTool: ToolConfig<
|
||||
|
||||
const data = await response.json()
|
||||
const organizations = data.organizations || []
|
||||
const paging = extractCursorPagingInfo(data)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
organizations,
|
||||
paging: {
|
||||
next_page: data.next_page ?? null,
|
||||
previous_page: data.previous_page ?? null,
|
||||
count: data.count || organizations.length,
|
||||
},
|
||||
paging,
|
||||
metadata: {
|
||||
total_returned: organizations.length,
|
||||
has_more: !!data.next_page,
|
||||
has_more: paging.has_more,
|
||||
},
|
||||
success: true,
|
||||
},
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import {
|
||||
appendCursorPaginationParams,
|
||||
buildZendeskUrl,
|
||||
extractCursorPagingInfo,
|
||||
handleZendeskError,
|
||||
METADATA_OUTPUT,
|
||||
PAGING_OUTPUT,
|
||||
@@ -16,10 +18,9 @@ export interface ZendeskGetTicketsParams {
|
||||
type?: string
|
||||
assigneeId?: string
|
||||
organizationId?: string
|
||||
sortBy?: string
|
||||
sortOrder?: string
|
||||
sort?: string
|
||||
perPage?: string
|
||||
page?: string
|
||||
pageAfter?: string
|
||||
}
|
||||
|
||||
export interface ZendeskGetTicketsResponse {
|
||||
@@ -27,9 +28,8 @@ export interface ZendeskGetTicketsResponse {
|
||||
output: {
|
||||
tickets: any[]
|
||||
paging?: {
|
||||
next_page?: string | null
|
||||
previous_page?: string | null
|
||||
count: number
|
||||
after_cursor: string | null
|
||||
has_more: boolean
|
||||
}
|
||||
metadata: {
|
||||
total_returned: number
|
||||
@@ -95,17 +95,12 @@ export const zendeskGetTicketsTool: ToolConfig<ZendeskGetTicketsParams, ZendeskG
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by organization ID as a numeric string (e.g., "67890")',
|
||||
},
|
||||
sortBy: {
|
||||
sort: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sort field: "created_at", "updated_at", "priority", or "status"',
|
||||
},
|
||||
sortOrder: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sort order: "asc" or "desc"',
|
||||
description:
|
||||
'Sort field for ticket listing (only applies without filters): "updated_at", "id", or "status". Prefix with "-" for descending (e.g., "-updated_at")',
|
||||
},
|
||||
perPage: {
|
||||
type: 'string',
|
||||
@@ -113,11 +108,11 @@ export const zendeskGetTicketsTool: ToolConfig<ZendeskGetTicketsParams, ZendeskG
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Results per page as a number string (default: "100", max: "100")',
|
||||
},
|
||||
page: {
|
||||
pageAfter: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number as a string (e.g., "1", "2")',
|
||||
description: 'Cursor from a previous response to fetch the next page of results',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -142,20 +137,16 @@ export const zendeskGetTicketsTool: ToolConfig<ZendeskGetTicketsParams, ZendeskG
|
||||
|
||||
const queryParams = new URLSearchParams()
|
||||
queryParams.append('query', searchTerms.join(' '))
|
||||
if (params.sortBy) queryParams.append('sort_by', params.sortBy)
|
||||
if (params.sortOrder) queryParams.append('sort_order', params.sortOrder)
|
||||
if (params.page) queryParams.append('page', params.page)
|
||||
if (params.perPage) queryParams.append('per_page', params.perPage)
|
||||
queryParams.append('filter[type]', 'ticket')
|
||||
appendCursorPaginationParams(queryParams, params)
|
||||
|
||||
return `${buildZendeskUrl(params.subdomain, '/search')}?${queryParams.toString()}`
|
||||
return `${buildZendeskUrl(params.subdomain, '/search/export')}?${queryParams.toString()}`
|
||||
}
|
||||
|
||||
// No filters - use the simple /tickets endpoint
|
||||
// No filters - use the simple /tickets endpoint with cursor-based pagination
|
||||
const queryParams = new URLSearchParams()
|
||||
if (params.sortBy) queryParams.append('sort_by', params.sortBy)
|
||||
if (params.sortOrder) queryParams.append('sort_order', params.sortOrder)
|
||||
if (params.page) queryParams.append('page', params.page)
|
||||
if (params.perPage) queryParams.append('per_page', params.perPage)
|
||||
if (params.sort) queryParams.append('sort', params.sort)
|
||||
appendCursorPaginationParams(queryParams, params)
|
||||
|
||||
const query = queryParams.toString()
|
||||
const url = buildZendeskUrl(params.subdomain, '/tickets')
|
||||
@@ -182,19 +173,16 @@ export const zendeskGetTicketsTool: ToolConfig<ZendeskGetTicketsParams, ZendeskG
|
||||
const data = await response.json()
|
||||
// Handle both /tickets response (data.tickets) and /search response (data.results)
|
||||
const tickets = data.tickets || data.results || []
|
||||
const paging = extractCursorPagingInfo(data)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
tickets,
|
||||
paging: {
|
||||
next_page: data.next_page ?? null,
|
||||
previous_page: data.previous_page ?? null,
|
||||
count: data.count || tickets.length,
|
||||
},
|
||||
paging,
|
||||
metadata: {
|
||||
total_returned: tickets.length,
|
||||
has_more: !!data.next_page,
|
||||
has_more: paging.has_more,
|
||||
},
|
||||
success: true,
|
||||
},
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import {
|
||||
appendCursorPaginationParams,
|
||||
buildZendeskUrl,
|
||||
extractCursorPagingInfo,
|
||||
handleZendeskError,
|
||||
METADATA_OUTPUT,
|
||||
PAGING_OUTPUT,
|
||||
@@ -14,7 +16,7 @@ export interface ZendeskGetUsersParams {
|
||||
role?: string
|
||||
permissionSet?: string
|
||||
perPage?: string
|
||||
page?: string
|
||||
pageAfter?: string
|
||||
}
|
||||
|
||||
export interface ZendeskGetUsersResponse {
|
||||
@@ -22,9 +24,8 @@ export interface ZendeskGetUsersResponse {
|
||||
output: {
|
||||
users: any[]
|
||||
paging?: {
|
||||
next_page?: string | null
|
||||
previous_page?: string | null
|
||||
count: number
|
||||
after_cursor: string | null
|
||||
has_more: boolean
|
||||
}
|
||||
metadata: {
|
||||
total_returned: number
|
||||
@@ -77,11 +78,11 @@ export const zendeskGetUsersTool: ToolConfig<ZendeskGetUsersParams, ZendeskGetUs
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Results per page as a number string (default: "100", max: "100")',
|
||||
},
|
||||
page: {
|
||||
pageAfter: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number as a string (e.g., "1", "2")',
|
||||
description: 'Cursor from a previous response to fetch the next page of results',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -90,8 +91,7 @@ export const zendeskGetUsersTool: ToolConfig<ZendeskGetUsersParams, ZendeskGetUs
|
||||
const queryParams = new URLSearchParams()
|
||||
if (params.role) queryParams.append('role', params.role)
|
||||
if (params.permissionSet) queryParams.append('permission_set', params.permissionSet)
|
||||
if (params.page) queryParams.append('page', params.page)
|
||||
if (params.perPage) queryParams.append('per_page', params.perPage)
|
||||
appendCursorPaginationParams(queryParams, params)
|
||||
|
||||
const query = queryParams.toString()
|
||||
const url = buildZendeskUrl(params.subdomain, '/users')
|
||||
@@ -116,19 +116,16 @@ export const zendeskGetUsersTool: ToolConfig<ZendeskGetUsersParams, ZendeskGetUs
|
||||
|
||||
const data = await response.json()
|
||||
const users = data.users || []
|
||||
const paging = extractCursorPagingInfo(data)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
users,
|
||||
paging: {
|
||||
next_page: data.next_page ?? null,
|
||||
previous_page: data.previous_page ?? null,
|
||||
count: data.count || users.length,
|
||||
},
|
||||
paging,
|
||||
metadata: {
|
||||
total_returned: users.length,
|
||||
has_more: !!data.next_page,
|
||||
has_more: paging.has_more,
|
||||
},
|
||||
success: true,
|
||||
},
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import {
|
||||
appendCursorPaginationParams,
|
||||
buildZendeskUrl,
|
||||
extractCursorPagingInfo,
|
||||
handleZendeskError,
|
||||
METADATA_OUTPUT,
|
||||
PAGING_OUTPUT,
|
||||
@@ -11,10 +13,9 @@ export interface ZendeskSearchParams {
|
||||
apiToken: string
|
||||
subdomain: string
|
||||
query: string
|
||||
sortBy?: string
|
||||
sortOrder?: string
|
||||
filterType: string
|
||||
perPage?: string
|
||||
page?: string
|
||||
pageAfter?: string
|
||||
}
|
||||
|
||||
export interface ZendeskSearchResponse {
|
||||
@@ -22,9 +23,8 @@ export interface ZendeskSearchResponse {
|
||||
output: {
|
||||
results: any[]
|
||||
paging?: {
|
||||
next_page?: string | null
|
||||
previous_page?: string | null
|
||||
count: number
|
||||
after_cursor: string | null
|
||||
has_more: boolean
|
||||
}
|
||||
metadata: {
|
||||
total_returned: number
|
||||
@@ -66,18 +66,11 @@ export const zendeskSearchTool: ToolConfig<ZendeskSearchParams, ZendeskSearchRes
|
||||
description:
|
||||
'Search query string using Zendesk search syntax (e.g., "type:ticket status:open")',
|
||||
},
|
||||
sortBy: {
|
||||
filterType: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Sort field: "relevance", "created_at", "updated_at", "priority", "status", or "ticket_type"',
|
||||
},
|
||||
sortOrder: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sort order: "asc" or "desc"',
|
||||
description: 'Resource type to search for: "ticket", "user", "organization", or "group"',
|
||||
},
|
||||
perPage: {
|
||||
type: 'string',
|
||||
@@ -85,11 +78,11 @@ export const zendeskSearchTool: ToolConfig<ZendeskSearchParams, ZendeskSearchRes
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Results per page as a number string (default: "100", max: "100")',
|
||||
},
|
||||
page: {
|
||||
pageAfter: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number as a string (e.g., "1", "2")',
|
||||
description: 'Cursor from a previous response to fetch the next page of results',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -97,13 +90,11 @@ export const zendeskSearchTool: ToolConfig<ZendeskSearchParams, ZendeskSearchRes
|
||||
url: (params) => {
|
||||
const queryParams = new URLSearchParams()
|
||||
queryParams.append('query', params.query)
|
||||
if (params.sortBy) queryParams.append('sort_by', params.sortBy)
|
||||
if (params.sortOrder) queryParams.append('sort_order', params.sortOrder)
|
||||
if (params.page) queryParams.append('page', params.page)
|
||||
if (params.perPage) queryParams.append('per_page', params.perPage)
|
||||
queryParams.append('filter[type]', params.filterType)
|
||||
appendCursorPaginationParams(queryParams, params)
|
||||
|
||||
const query = queryParams.toString()
|
||||
const url = buildZendeskUrl(params.subdomain, '/search')
|
||||
const url = buildZendeskUrl(params.subdomain, '/search/export')
|
||||
return `${url}?${query}`
|
||||
},
|
||||
method: 'GET',
|
||||
@@ -125,19 +116,16 @@ export const zendeskSearchTool: ToolConfig<ZendeskSearchParams, ZendeskSearchRes
|
||||
|
||||
const data = await response.json()
|
||||
const results = data.results || []
|
||||
const paging = extractCursorPagingInfo(data)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
results,
|
||||
paging: {
|
||||
next_page: data.next_page ?? null,
|
||||
previous_page: data.previous_page ?? null,
|
||||
count: data.count || results.length,
|
||||
},
|
||||
paging,
|
||||
metadata: {
|
||||
total_returned: results.length,
|
||||
has_more: !!data.next_page,
|
||||
has_more: paging.has_more,
|
||||
},
|
||||
success: true,
|
||||
},
|
||||
|
||||
@@ -22,9 +22,9 @@ export interface ZendeskSearchUsersResponse {
|
||||
output: {
|
||||
users: any[]
|
||||
paging?: {
|
||||
after_cursor: string | null
|
||||
has_more: boolean
|
||||
next_page?: string | null
|
||||
previous_page?: string | null
|
||||
count: number
|
||||
}
|
||||
metadata: {
|
||||
total_returned: number
|
||||
@@ -84,7 +84,7 @@ export const zendeskSearchUsersTool: ToolConfig<
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number as a string (e.g., "1", "2")',
|
||||
description: 'Page number for pagination (1-based)',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -93,8 +93,8 @@ export const zendeskSearchUsersTool: ToolConfig<
|
||||
const queryParams = new URLSearchParams()
|
||||
if (params.query) queryParams.append('query', params.query)
|
||||
if (params.externalId) queryParams.append('external_id', params.externalId)
|
||||
if (params.page) queryParams.append('page', params.page)
|
||||
if (params.perPage) queryParams.append('per_page', params.perPage)
|
||||
if (params.page) queryParams.append('page', params.page)
|
||||
|
||||
const query = queryParams.toString()
|
||||
const url = buildZendeskUrl(params.subdomain, '/users/search')
|
||||
@@ -119,19 +119,22 @@ export const zendeskSearchUsersTool: ToolConfig<
|
||||
|
||||
const data = await response.json()
|
||||
const users = data.users || []
|
||||
const hasMore = data.next_page !== null && data.next_page !== undefined
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
users,
|
||||
// /users/search uses offset pagination (page/per_page), not cursor pagination.
|
||||
// after_cursor is always null; use next_page URL or page param for subsequent pages.
|
||||
paging: {
|
||||
after_cursor: null,
|
||||
has_more: hasMore,
|
||||
next_page: data.next_page ?? null,
|
||||
previous_page: data.previous_page ?? null,
|
||||
count: data.count || users.length,
|
||||
},
|
||||
metadata: {
|
||||
total_returned: users.length,
|
||||
has_more: !!data.next_page,
|
||||
has_more: hasMore,
|
||||
},
|
||||
success: true,
|
||||
},
|
||||
|
||||
@@ -11,14 +11,14 @@ export interface ZendeskBaseParams {
|
||||
}
|
||||
|
||||
export interface ZendeskPaginationParams {
|
||||
page?: string
|
||||
perPage?: string
|
||||
pageAfter?: string
|
||||
}
|
||||
|
||||
export interface ZendeskPagingInfo {
|
||||
after_cursor: string | null
|
||||
has_more: boolean
|
||||
next_page?: string | null
|
||||
previous_page?: string | null
|
||||
count: number
|
||||
}
|
||||
|
||||
export interface ZendeskListMetadata {
|
||||
@@ -50,6 +50,32 @@ export function handleZendeskError(data: any, status: number, operation: string)
|
||||
throw new Error(`Zendesk ${operation} failed: ${errorMessage}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends cursor-based pagination query params.
|
||||
* Zendesk uses bracket notation: `page[size]` and `page[after]`.
|
||||
*/
|
||||
export function appendCursorPaginationParams(
|
||||
queryParams: URLSearchParams,
|
||||
params: ZendeskPaginationParams
|
||||
): void {
|
||||
if (params.perPage) queryParams.append('page[size]', params.perPage)
|
||||
if (params.pageAfter) queryParams.append('page[after]', params.pageAfter)
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts cursor-based pagination info from Zendesk API response.
|
||||
* Zendesk cursor-based responses include `meta.after_cursor`, `meta.has_more`, and `links.next`.
|
||||
*/
|
||||
export function extractCursorPagingInfo(data: Record<string, unknown>): ZendeskPagingInfo {
|
||||
const meta = (data.meta as Record<string, unknown>) || {}
|
||||
const links = (data.links as Record<string, unknown>) || {}
|
||||
return {
|
||||
after_cursor: (meta.after_cursor as string) ?? null,
|
||||
has_more: Boolean(meta.has_more),
|
||||
next_page: (links.next as string) ?? null,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Output definition for the "via" object in ticket responses.
|
||||
* Contains information about how the ticket was created.
|
||||
@@ -377,13 +403,13 @@ export const ORGANIZATION_OUTPUT_PROPERTIES = {
|
||||
* Pagination output properties for list endpoints
|
||||
*/
|
||||
export const PAGING_OUTPUT_PROPERTIES = {
|
||||
next_page: { type: 'string', description: 'URL for next page of results', optional: true },
|
||||
previous_page: {
|
||||
after_cursor: {
|
||||
type: 'string',
|
||||
description: 'URL for previous page of results',
|
||||
description: 'Cursor for fetching the next page of results',
|
||||
optional: true,
|
||||
},
|
||||
count: { type: 'number', description: 'Total count of items' },
|
||||
has_more: { type: 'boolean', description: 'Whether more results are available' },
|
||||
next_page: { type: 'string', description: 'URL for next page of results', optional: true },
|
||||
} as const satisfies Record<string, OutputProperty>
|
||||
|
||||
/**
|
||||
@@ -391,7 +417,7 @@ export const PAGING_OUTPUT_PROPERTIES = {
|
||||
*/
|
||||
export const PAGING_OUTPUT: OutputProperty = {
|
||||
type: 'object',
|
||||
description: 'Pagination information',
|
||||
description: 'Cursor-based pagination information',
|
||||
properties: PAGING_OUTPUT_PROPERTIES,
|
||||
}
|
||||
|
||||
|
||||
16
bun.lock
16
bun.lock
@@ -13,7 +13,7 @@
|
||||
"glob": "13.0.0",
|
||||
"husky": "9.1.7",
|
||||
"lint-staged": "16.0.0",
|
||||
"turbo": "2.8.3",
|
||||
"turbo": "2.8.9",
|
||||
},
|
||||
},
|
||||
"apps/docs": {
|
||||
@@ -3437,19 +3437,19 @@
|
||||
|
||||
"tunnel-agent": ["tunnel-agent@0.6.0", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w=="],
|
||||
|
||||
"turbo": ["turbo@2.8.3", "", { "optionalDependencies": { "turbo-darwin-64": "2.8.3", "turbo-darwin-arm64": "2.8.3", "turbo-linux-64": "2.8.3", "turbo-linux-arm64": "2.8.3", "turbo-windows-64": "2.8.3", "turbo-windows-arm64": "2.8.3" }, "bin": { "turbo": "bin/turbo" } }, "sha512-8Osxz5Tu/Dw2kb31EAY+nhq/YZ3wzmQSmYa1nIArqxgCAldxv9TPlrAiaBUDVnKA4aiPn0OFBD1ACcpc5VFOAQ=="],
|
||||
"turbo": ["turbo@2.8.9", "", { "optionalDependencies": { "turbo-darwin-64": "2.8.9", "turbo-darwin-arm64": "2.8.9", "turbo-linux-64": "2.8.9", "turbo-linux-arm64": "2.8.9", "turbo-windows-64": "2.8.9", "turbo-windows-arm64": "2.8.9" }, "bin": { "turbo": "bin/turbo" } }, "sha512-G+Mq8VVQAlpz/0HTsxiNNk/xywaHGl+dk1oiBREgOEVCCDjXInDlONWUn5srRnC9s5tdHTFD1bx1N19eR4hI+g=="],
|
||||
|
||||
"turbo-darwin-64": ["turbo-darwin-64@2.8.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-4kXRLfcygLOeNcP6JquqRLmGB/ATjjfehiojL2dJkL7GFm3SPSXbq7oNj8UbD8XriYQ5hPaSuz59iF1ijPHkTw=="],
|
||||
"turbo-darwin-64": ["turbo-darwin-64@2.8.9", "", { "os": "darwin", "cpu": "x64" }, "sha512-KnCw1ZI9KTnEAhdI9avZrnZ/z4wsM++flMA1w8s8PKOqi5daGpFV36qoPafg4S8TmYMe52JPWEoFr0L+lQ5JIw=="],
|
||||
|
||||
"turbo-darwin-arm64": ["turbo-darwin-arm64@2.8.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-xF7uCeC0UY0Hrv/tqax0BMbFlVP1J/aRyeGQPZT4NjvIPj8gSPDgFhfkfz06DhUwDg5NgMo04uiSkAWE8WB/QQ=="],
|
||||
"turbo-darwin-arm64": ["turbo-darwin-arm64@2.8.9", "", { "os": "darwin", "cpu": "arm64" }, "sha512-CbD5Y2NKJKBXTOZ7z7Cc7vGlFPZkYjApA7ri9lH4iFwKV1X7MoZswh9gyRLetXYWImVX1BqIvP8KftulJg/wIA=="],
|
||||
|
||||
"turbo-linux-64": ["turbo-linux-64@2.8.3", "", { "os": "linux", "cpu": "x64" }, "sha512-vxMDXwaOjweW/4etY7BxrXCSkvtwh0PbwVafyfT1Ww659SedUxd5rM3V2ZCmbwG8NiCfY7d6VtxyHx3Wh1GoZA=="],
|
||||
"turbo-linux-64": ["turbo-linux-64@2.8.9", "", { "os": "linux", "cpu": "x64" }, "sha512-OXC9HdCtsHvyH+5KUoH8ds+p5WU13vdif0OPbsFzZca4cUXMwKA3HWwUuCgQetk0iAE4cscXpi/t8A263n3VTg=="],
|
||||
|
||||
"turbo-linux-arm64": ["turbo-linux-arm64@2.8.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-mQX7uYBZFkuPLLlKaNe9IjR1JIef4YvY8f21xFocvttXvdPebnq3PK1Zjzl9A1zun2BEuWNUwQIL8lgvN9Pm3Q=="],
|
||||
"turbo-linux-arm64": ["turbo-linux-arm64@2.8.9", "", { "os": "linux", "cpu": "arm64" }, "sha512-yI5n8jNXiFA6+CxnXG0gO7h5ZF1+19K8uO3/kXPQmyl37AdiA7ehKJQOvf9OPAnmkGDHcF2HSCPltabERNRmug=="],
|
||||
|
||||
"turbo-windows-64": ["turbo-windows-64@2.8.3", "", { "os": "win32", "cpu": "x64" }, "sha512-YLGEfppGxZj3VWcNOVa08h6ISsVKiG85aCAWosOKNUjb6yErWEuydv6/qImRJUI+tDLvDvW7BxopAkujRnWCrw=="],
|
||||
"turbo-windows-64": ["turbo-windows-64@2.8.9", "", { "os": "win32", "cpu": "x64" }, "sha512-/OztzeGftJAg258M/9vK2ZCkUKUzqrWXJIikiD2pm8TlqHcIYUmepDbyZSDfOiUjMy6NzrLFahpNLnY7b5vNgg=="],
|
||||
|
||||
"turbo-windows-arm64": ["turbo-windows-arm64@2.8.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-afTUGKBRmOJU1smQSBnFGcbq0iabAPwh1uXu2BVk7BREg30/1gMnJh9DFEQTah+UD3n3ru8V55J83RQNFfqoyw=="],
|
||||
"turbo-windows-arm64": ["turbo-windows-arm64@2.8.9", "", { "os": "win32", "cpu": "arm64" }, "sha512-xZ2VTwVTjIqpFZKN4UBxDHCPM3oJ2J5cpRzCBSmRpJ/Pn33wpiYjs+9FB2E03svKaD04/lSSLlEUej0UYsugfg=="],
|
||||
|
||||
"tweetnacl": ["tweetnacl@0.14.5", "", {}, "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA=="],
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
"glob": "13.0.0",
|
||||
"husky": "9.1.7",
|
||||
"lint-staged": "16.0.0",
|
||||
"turbo": "2.8.3"
|
||||
"turbo": "2.8.9"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,jsx,ts,tsx,json,css,scss}": [
|
||||
|
||||
Reference in New Issue
Block a user