diff --git a/apps/docs/content/docs/en/tools/cursor.mdx b/apps/docs/content/docs/en/tools/cursor.mdx
index 4e6f165c014..e182f7ddd5c 100644
--- a/apps/docs/content/docs/en/tools/cursor.mdx
+++ b/apps/docs/content/docs/en/tools/cursor.mdx
@@ -211,4 +211,57 @@ Download a generated artifact file from a cloud agent. Returns the file for exec
| --------- | ---- | ----------- |
| `file` | file | Downloaded artifact file stored in execution files |
+### `cursor_list_models`
+
+List the models available for launching cloud agents. Returns API-aligned fields only.
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `apiKey` | string | Yes | Cursor API key |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `models` | array | Array of available model names |
+
+### `cursor_list_repositories`
+
+List the GitHub repositories accessible to the authenticated user. Returns API-aligned fields only.
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `apiKey` | string | Yes | Cursor API key |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `repositories` | array | Array of accessible repositories |
+| ↳ `owner` | string | Repository owner |
+| ↳ `name` | string | Repository name |
+| ↳ `repository` | string | Repository URL |
+
+### `cursor_get_api_key_info`
+
+Retrieve details about the API key currently in use. Returns API-aligned fields only.
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `apiKey` | string | Yes | Cursor API key |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `apiKeyName` | string | Name of the API key |
+| `createdAt` | string | API key creation timestamp |
+| `userEmail` | string | Email of the key owner |
+
diff --git a/apps/docs/content/docs/en/tools/devin.mdx b/apps/docs/content/docs/en/tools/devin.mdx
index 4ea369fcccb..eb38d6a03c1 100644
--- a/apps/docs/content/docs/en/tools/devin.mdx
+++ b/apps/docs/content/docs/en/tools/devin.mdx
@@ -50,6 +50,7 @@ Create a new Devin session with a prompt. Devin will autonomously work on the ta
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Devin API key \(service user credential starting with cog_\) |
+| `orgId` | string | Yes | Devin organization ID \(prefixed with org-\) |
| `prompt` | string | Yes | The task prompt for Devin to work on |
| `playbookId` | string | No | Optional playbook ID to guide the session |
| `maxAcuLimit` | number | No | Maximum ACU limit for the session |
@@ -67,10 +68,11 @@ Create a new Devin session with a prompt. Devin will autonomously work on the ta
| `createdAt` | number | Unix timestamp when the session was created |
| `updatedAt` | number | Unix timestamp when the session was last updated |
| `acusConsumed` | number | ACUs consumed by the session |
-| `tags` | json | Tags associated with the session |
-| `pullRequests` | json | Pull requests created during the session |
+| `tags` | json | Tags associated with the session \(array of strings\) |
+| `pullRequests` | json | Pull requests created during the session \(\[\{pr_url, pr_state\}\]\) |
| `structuredOutput` | json | Structured output from the session |
| `playbookId` | string | Associated playbook ID |
+| `isArchived` | boolean | Whether the session is archived |
### `devin_get_session`
@@ -81,6 +83,7 @@ Retrieve details of an existing Devin session including status, tags, pull reque
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Devin API key \(service user credential starting with cog_\) |
+| `orgId` | string | Yes | Devin organization ID \(prefixed with org-\) |
| `sessionId` | string | Yes | The session ID to retrieve |
#### Output
@@ -95,10 +98,11 @@ Retrieve details of an existing Devin session including status, tags, pull reque
| `createdAt` | number | Unix timestamp when the session was created |
| `updatedAt` | number | Unix timestamp when the session was last updated |
| `acusConsumed` | number | ACUs consumed by the session |
-| `tags` | json | Tags associated with the session |
-| `pullRequests` | json | Pull requests created during the session |
+| `tags` | json | Tags associated with the session \(array of strings\) |
+| `pullRequests` | json | Pull requests created during the session \(\[\{pr_url, pr_state\}\]\) |
| `structuredOutput` | json | Structured output from the session |
| `playbookId` | string | Associated playbook ID |
+| `isArchived` | boolean | Whether the session is archived |
### `devin_list_sessions`
@@ -109,7 +113,9 @@ List Devin sessions in the organization. Returns up to 100 sessions by default.
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Devin API key \(service user credential starting with cog_\) |
+| `orgId` | string | Yes | Devin organization ID \(prefixed with org-\) |
| `limit` | number | No | Maximum number of sessions to return \(1-200, default: 100\) |
+| `after` | string | No | Pagination cursor \(endCursor from a previous response\) to fetch the next page |
#### Output
@@ -123,7 +129,14 @@ List Devin sessions in the organization. Returns up to 100 sessions by default.
| ↳ `title` | string | Session title |
| ↳ `createdAt` | number | Creation timestamp \(Unix\) |
| ↳ `updatedAt` | number | Last updated timestamp \(Unix\) |
-| ↳ `tags` | json | Session tags |
+| ↳ `tags` | json | Session tags \(array of strings\) |
+| ↳ `acusConsumed` | number | ACUs consumed by the session |
+| ↳ `pullRequests` | json | Pull requests created during the session \(\[\{pr_url, pr_state\}\]\) |
+| ↳ `playbookId` | string | Associated playbook ID |
+| ↳ `isArchived` | boolean | Whether the session is archived |
+| `endCursor` | string | Pagination cursor for the next page, or null if last page |
+| `hasNextPage` | boolean | Whether more sessions are available |
+| `total` | number | Total number of sessions, if provided |
### `devin_send_message`
@@ -134,6 +147,7 @@ Send a message to a Devin session. If the session is suspended, it will be autom
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `apiKey` | string | Yes | Devin API key \(service user credential starting with cog_\) |
+| `orgId` | string | Yes | Devin organization ID \(prefixed with org-\) |
| `sessionId` | string | Yes | The session ID to send the message to |
| `message` | string | Yes | The message to send to Devin |
@@ -149,9 +163,177 @@ Send a message to a Devin session. If the session is suspended, it will be autom
| `createdAt` | number | Unix timestamp when the session was created |
| `updatedAt` | number | Unix timestamp when the session was last updated |
| `acusConsumed` | number | ACUs consumed by the session |
-| `tags` | json | Tags associated with the session |
-| `pullRequests` | json | Pull requests created during the session |
+| `tags` | json | Tags associated with the session \(array of strings\) |
+| `pullRequests` | json | Pull requests created during the session \(\[\{pr_url, pr_state\}\]\) |
| `structuredOutput` | json | Structured output from the session |
| `playbookId` | string | Associated playbook ID |
+| `isArchived` | boolean | Whether the session is archived |
+
+### `devin_list_session_messages`
+
+List the messages exchanged in a Devin session, including messages from both the user and Devin.
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `apiKey` | string | Yes | Devin API key \(service user credential starting with cog_\) |
+| `orgId` | string | Yes | Devin organization ID \(prefixed with org-\) |
+| `sessionId` | string | Yes | The session ID to list messages for |
+| `limit` | number | No | Maximum number of messages to return \(1-200, default: 100\) |
+| `after` | string | No | Pagination cursor \(endCursor from a previous response\) to fetch the next page |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `messages` | array | Messages exchanged in the session |
+| ↳ `eventId` | string | Unique identifier for the message event |
+| ↳ `source` | string | Origin of the message \(devin or user\) |
+| ↳ `message` | string | The message content |
+| ↳ `createdAt` | number | Unix timestamp when the message was created |
+| `endCursor` | string | Pagination cursor for the next page, or null if last page |
+| `hasNextPage` | boolean | Whether more messages are available |
+| `total` | number | Total number of messages, if provided |
+
+### `devin_list_session_attachments`
+
+List the files uploaded to or produced by a Devin session.
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `apiKey` | string | Yes | Devin API key \(service user credential starting with cog_\) |
+| `orgId` | string | Yes | Devin organization ID \(prefixed with org-\) |
+| `sessionId` | string | Yes | The session ID to list attachments for |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `attachments` | array | Attachments associated with the session |
+| ↳ `attachmentId` | string | Unique identifier for the attachment |
+| ↳ `name` | string | Attachment file name |
+| ↳ `url` | string | URL to download the attachment |
+| ↳ `source` | string | Origin of the attachment \(devin or user\) |
+| ↳ `contentType` | string | MIME type of the attachment |
+
+### `devin_get_session_tags`
+
+Retrieve the tags currently applied to a Devin session.
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `apiKey` | string | Yes | Devin API key \(service user credential starting with cog_\) |
+| `orgId` | string | Yes | Devin organization ID \(prefixed with org-\) |
+| `sessionId` | string | Yes | The session ID to retrieve tags for |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `tags` | json | Tags applied to the session \(array of strings\) |
+
+### `devin_append_session_tags`
+
+Add tags to a Devin session without removing existing tags (max 50 tags total).
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `apiKey` | string | Yes | Devin API key \(service user credential starting with cog_\) |
+| `orgId` | string | Yes | Devin organization ID \(prefixed with org-\) |
+| `sessionId` | string | Yes | The session ID to add tags to |
+| `tags` | string | Yes | Tags to append to the session \(comma-separated string or array of strings\) |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `tags` | json | Updated list of tags on the session \(array of strings\) |
+
+### `devin_replace_session_tags`
+
+Replace all tags on a Devin session with a new set of tags (max 50 tags).
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `apiKey` | string | Yes | Devin API key \(service user credential starting with cog_\) |
+| `orgId` | string | Yes | Devin organization ID \(prefixed with org-\) |
+| `sessionId` | string | Yes | The session ID to replace tags on |
+| `tags` | string | Yes | Tags that will overwrite the existing tags \(comma-separated string or array of strings\) |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `tags` | json | Updated list of tags on the session \(array of strings\) |
+
+### `devin_archive_session`
+
+Archive a Devin session. Archived sessions can still be viewed but cannot be modified or resumed.
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `apiKey` | string | Yes | Devin API key \(service user credential starting with cog_\) |
+| `orgId` | string | Yes | Devin organization ID \(prefixed with org-\) |
+| `sessionId` | string | Yes | The session ID to archive |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `sessionId` | string | Unique identifier for the session |
+| `url` | string | URL to view the session in the Devin UI |
+| `status` | string | Session status \(new, claimed, running, exit, error, suspended, resuming\) |
+| `statusDetail` | string | Detailed status \(working, waiting_for_user, waiting_for_approval, finished, inactivity, etc.\) |
+| `title` | string | Session title |
+| `createdAt` | number | Unix timestamp when the session was created |
+| `updatedAt` | number | Unix timestamp when the session was last updated |
+| `acusConsumed` | number | ACUs consumed by the session |
+| `tags` | json | Tags associated with the session \(array of strings\) |
+| `pullRequests` | json | Pull requests created during the session \(\[\{pr_url, pr_state\}\]\) |
+| `structuredOutput` | json | Structured output from the session |
+| `playbookId` | string | Associated playbook ID |
+| `isArchived` | boolean | Whether the session is archived |
+
+### `devin_terminate_session`
+
+Terminate a Devin session. Optionally archive the session instead of permanently terminating it.
+
+#### Input
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `apiKey` | string | Yes | Devin API key \(service user credential starting with cog_\) |
+| `orgId` | string | Yes | Devin organization ID \(prefixed with org-\) |
+| `sessionId` | string | Yes | The session ID to terminate |
+| `archive` | boolean | No | Archive the session instead of permanently terminating it \(default: false\) |
+
+#### Output
+
+| Parameter | Type | Description |
+| --------- | ---- | ----------- |
+| `sessionId` | string | Unique identifier for the session |
+| `url` | string | URL to view the session in the Devin UI |
+| `status` | string | Session status \(new, claimed, running, exit, error, suspended, resuming\) |
+| `statusDetail` | string | Detailed status \(working, waiting_for_user, waiting_for_approval, finished, inactivity, etc.\) |
+| `title` | string | Session title |
+| `createdAt` | number | Unix timestamp when the session was created |
+| `updatedAt` | number | Unix timestamp when the session was last updated |
+| `acusConsumed` | number | ACUs consumed by the session |
+| `tags` | json | Tags associated with the session \(array of strings\) |
+| `pullRequests` | json | Pull requests created during the session \(\[\{pr_url, pr_state\}\]\) |
+| `structuredOutput` | json | Structured output from the session |
+| `playbookId` | string | Associated playbook ID |
+| `isArchived` | boolean | Whether the session is archived |
diff --git a/apps/docs/content/docs/en/tools/greptile.mdx b/apps/docs/content/docs/en/tools/greptile.mdx
index 7132bbb131c..1e3aed3929a 100644
--- a/apps/docs/content/docs/en/tools/greptile.mdx
+++ b/apps/docs/content/docs/en/tools/greptile.mdx
@@ -7,7 +7,7 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
{/* MANUAL-CONTENT-START:intro */}
diff --git a/apps/sim/app/(landing)/integrations/data/integrations.json b/apps/sim/app/(landing)/integrations/data/integrations.json
index fc8e6b61cb1..8d7bec0c5ed 100644
--- a/apps/sim/app/(landing)/integrations/data/integrations.json
+++ b/apps/sim/app/(landing)/integrations/data/integrations.json
@@ -3052,9 +3052,21 @@
{
"name": "Download Artifact",
"description": "Download a generated artifact file from a cloud agent."
+ },
+ {
+ "name": "List Models",
+ "description": "List the models available for launching cloud agents."
+ },
+ {
+ "name": "List Repositories",
+ "description": "List the GitHub repositories accessible to the authenticated user."
+ },
+ {
+ "name": "Get API Key Info",
+ "description": "Retrieve details about the API key currently in use."
}
],
- "operationCount": 9,
+ "operationCount": 12,
"triggers": [],
"triggerCount": 0,
"authType": "api-key",
@@ -3298,9 +3310,37 @@
{
"name": "Send Message",
"description": "Send a message to a Devin session. If the session is suspended, it will be automatically resumed. Returns the updated session state."
+ },
+ {
+ "name": "List Session Messages",
+ "description": "List the messages exchanged in a Devin session, including messages from both the user and Devin."
+ },
+ {
+ "name": "List Session Attachments",
+ "description": "List the files uploaded to or produced by a Devin session."
+ },
+ {
+ "name": "Get Session Tags",
+ "description": "Retrieve the tags currently applied to a Devin session."
+ },
+ {
+ "name": "Append Session Tags",
+ "description": "Add tags to a Devin session without removing existing tags (max 50 tags total)."
+ },
+ {
+ "name": "Replace Session Tags",
+ "description": "Replace all tags on a Devin session with a new set of tags (max 50 tags)."
+ },
+ {
+ "name": "Archive Session",
+ "description": "Archive a Devin session. Archived sessions can still be viewed but cannot be modified or resumed."
+ },
+ {
+ "name": "Terminate Session",
+ "description": "Terminate a Devin session. Optionally archive the session instead of permanently terminating it."
}
],
- "operationCount": 4,
+ "operationCount": 11,
"triggers": [],
"triggerCount": 0,
"authType": "api-key",
@@ -6470,7 +6510,7 @@
"name": "Greptile",
"description": "AI-powered codebase search and Q&A",
"longDescription": "Query and search codebases using natural language with Greptile. Get AI-generated answers about your code, find relevant files, and understand complex codebases.",
- "bgColor": "#e5e5e5",
+ "bgColor": "#5DE195",
"iconName": "GreptileIcon",
"docsUrl": "https://docs.sim.ai/tools/greptile",
"operations": [
diff --git a/apps/sim/blocks/blocks/cursor.ts b/apps/sim/blocks/blocks/cursor.ts
index b6ef6a52411..c4ef939e766 100644
--- a/apps/sim/blocks/blocks/cursor.ts
+++ b/apps/sim/blocks/blocks/cursor.ts
@@ -33,6 +33,9 @@ export const CursorBlock: BlockConfig = {
{ label: 'Delete Agent', id: 'cursor_delete_agent' },
{ label: 'List Artifacts', id: 'cursor_list_artifacts' },
{ label: 'Download Artifact', id: 'cursor_download_artifact' },
+ { label: 'List Models', id: 'cursor_list_models' },
+ { label: 'List Repositories', id: 'cursor_list_repositories' },
+ { label: 'Get API Key Info', id: 'cursor_get_api_key_info' },
],
value: () => 'cursor_launch_agent',
},
@@ -183,6 +186,9 @@ export const CursorBlock: BlockConfig = {
'cursor_delete_agent',
'cursor_list_artifacts',
'cursor_download_artifact',
+ 'cursor_list_models',
+ 'cursor_list_repositories',
+ 'cursor_get_api_key_info',
],
config: {
tool: (params) => params.operation || 'cursor_launch_agent',
@@ -236,6 +242,9 @@ export const CursorV2Block: BlockConfig = {
'cursor_delete_agent_v2',
'cursor_list_artifacts_v2',
'cursor_download_artifact_v2',
+ 'cursor_list_models_v2',
+ 'cursor_list_repositories_v2',
+ 'cursor_get_api_key_info_v2',
],
config: {
tool: createVersionedToolSelector({
@@ -253,11 +262,19 @@ export const CursorV2Block: BlockConfig = {
source: { type: 'json', description: 'Agent source repository info' },
target: { type: 'json', description: 'Agent target branch/PR info' },
summary: { type: 'string', description: 'Agent summary' },
- createdAt: { type: 'string', description: 'Agent creation timestamp' },
+ createdAt: { type: 'string', description: 'Creation timestamp (agent or API key)' },
agents: { type: 'json', description: 'Array of agent objects (list operation)' },
nextCursor: { type: 'string', description: 'Pagination cursor (list operation)' },
messages: { type: 'json', description: 'Conversation messages (get conversation operation)' },
artifacts: { type: 'json', description: 'List of artifact files (list artifacts operation)' },
file: { type: 'file', description: 'Downloaded artifact file (download artifact operation)' },
+ models: { type: 'json', description: 'Available model names (list models operation)' },
+ repositories: {
+ type: 'json',
+ description:
+ 'Accessible repositories [{owner, name, repository}] (list repositories operation)',
+ },
+ apiKeyName: { type: 'string', description: 'API key name (api key info operation)' },
+ userEmail: { type: 'string', description: 'Key owner email (api key info operation)' },
},
}
diff --git a/apps/sim/blocks/blocks/devin.ts b/apps/sim/blocks/blocks/devin.ts
index 291722e7d7d..7460d293155 100644
--- a/apps/sim/blocks/blocks/devin.ts
+++ b/apps/sim/blocks/blocks/devin.ts
@@ -2,6 +2,14 @@ import { DevinIcon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import { AuthMode, IntegrationType } from '@/blocks/types'
+const SESSION_OBJECT_OPERATIONS = [
+ 'create_session',
+ 'get_session',
+ 'send_message',
+ 'archive_session',
+ 'terminate_session',
+] as const
+
export const DevinBlock: BlockConfig = {
type: 'devin',
name: 'Devin',
@@ -32,6 +40,13 @@ export const DevinBlock: BlockConfig = {
{ label: 'Get Session', id: 'get_session' },
{ label: 'List Sessions', id: 'list_sessions' },
{ label: 'Send Message', id: 'send_message' },
+ { label: 'List Session Messages', id: 'list_session_messages' },
+ { label: 'List Session Attachments', id: 'list_session_attachments' },
+ { label: 'Get Session Tags', id: 'get_session_tags' },
+ { label: 'Append Session Tags', id: 'append_session_tags' },
+ { label: 'Replace Session Tags', id: 'replace_session_tags' },
+ { label: 'Archive Session', id: 'archive_session' },
+ { label: 'Terminate Session', id: 'terminate_session' },
],
value: () => 'create_session',
},
@@ -43,6 +58,13 @@ export const DevinBlock: BlockConfig = {
password: true,
required: true,
},
+ {
+ id: 'orgId',
+ title: 'Organization ID',
+ type: 'short-input',
+ placeholder: 'Enter your Devin organization ID (org-...)',
+ required: true,
+ },
{
id: 'prompt',
title: 'Prompt',
@@ -86,16 +108,19 @@ RULES:
title: 'Tags',
type: 'short-input',
placeholder: 'Comma-separated tags',
- condition: { field: 'operation', value: 'create_session' },
- mode: 'advanced',
+ required: { field: 'operation', value: ['append_session_tags', 'replace_session_tags'] },
+ condition: {
+ field: 'operation',
+ value: ['create_session', 'append_session_tags', 'replace_session_tags'],
+ },
},
{
id: 'sessionId',
title: 'Session ID',
type: 'short-input',
placeholder: 'Enter session ID',
- required: { field: 'operation', value: ['get_session', 'send_message'] },
- condition: { field: 'operation', value: ['get_session', 'send_message'] },
+ required: { field: 'operation', value: ['create_session', 'list_sessions'], not: true },
+ condition: { field: 'operation', value: ['create_session', 'list_sessions'], not: true },
},
{
id: 'message',
@@ -109,8 +134,28 @@ RULES:
id: 'limit',
title: 'Limit',
type: 'short-input',
- placeholder: 'Number of sessions (1-200, default: 100)',
- condition: { field: 'operation', value: 'list_sessions' },
+ placeholder: 'Max results (1-200, default: 100)',
+ condition: { field: 'operation', value: ['list_sessions', 'list_session_messages'] },
+ mode: 'advanced',
+ },
+ {
+ id: 'after',
+ title: 'After Cursor',
+ type: 'short-input',
+ placeholder: 'Pagination cursor from a previous response',
+ condition: { field: 'operation', value: ['list_sessions', 'list_session_messages'] },
+ mode: 'advanced',
+ },
+ {
+ id: 'terminateArchive',
+ title: 'Archive Instead of Terminate',
+ type: 'dropdown',
+ options: [
+ { label: 'No', id: 'false' },
+ { label: 'Yes', id: 'true' },
+ ],
+ value: () => 'false',
+ condition: { field: 'operation', value: 'terminate_session' },
mode: 'advanced',
},
],
@@ -120,15 +165,30 @@ RULES:
'devin_get_session',
'devin_list_sessions',
'devin_send_message',
+ 'devin_list_session_messages',
+ 'devin_list_session_attachments',
+ 'devin_get_session_tags',
+ 'devin_append_session_tags',
+ 'devin_replace_session_tags',
+ 'devin_archive_session',
+ 'devin_terminate_session',
],
config: {
tool: (params) => `devin_${params.operation}`,
params: (params) => {
if (params.maxAcuLimit != null && params.maxAcuLimit !== '') {
- params.maxAcuLimit = Number(params.maxAcuLimit)
+ const parsed = Number(params.maxAcuLimit)
+ params.maxAcuLimit = Number.isFinite(parsed) ? parsed : undefined
}
if (params.limit != null && params.limit !== '') {
- params.limit = Number(params.limit)
+ const parsed = Number(params.limit)
+ params.limit = Number.isFinite(parsed) ? parsed : undefined
+ }
+ if (params.terminateArchive != null && params.terminateArchive !== '') {
+ params.archive =
+ typeof params.terminateArchive === 'boolean'
+ ? params.terminateArchive
+ : params.terminateArchive === 'true'
}
return params
},
@@ -139,51 +199,121 @@ RULES:
sessionId: { type: 'string', description: 'Session ID' },
message: { type: 'string', description: 'Message to send to the session' },
apiKey: { type: 'string', description: 'Devin API key' },
+ orgId: { type: 'string', description: 'Devin organization ID' },
playbookId: { type: 'string', description: 'Playbook ID to guide the session' },
maxAcuLimit: { type: 'number', description: 'Maximum ACU limit' },
- tags: { type: 'string', description: 'Comma-separated tags' },
- limit: { type: 'number', description: 'Number of sessions to return' },
+ tags: { type: 'string', description: 'Tags (comma-separated string or array of strings)' },
+ limit: { type: 'number', description: 'Maximum number of results to return' },
+ after: { type: 'string', description: 'Pagination cursor for the next page' },
+ terminateArchive: {
+ type: 'string',
+ description: 'Whether to archive instead of terminate the session',
+ },
},
outputs: {
- sessionId: { type: 'string', description: 'Session identifier' },
- url: { type: 'string', description: 'URL to view the session in Devin UI' },
+ sessionId: {
+ type: 'string',
+ description: 'Session identifier',
+ condition: { field: 'operation', value: [...SESSION_OBJECT_OPERATIONS] },
+ },
+ url: {
+ type: 'string',
+ description: 'URL to view the session in Devin UI',
+ condition: { field: 'operation', value: [...SESSION_OBJECT_OPERATIONS] },
+ },
status: {
type: 'string',
description: 'Session status (new, claimed, running, exit, error, suspended, resuming)',
+ condition: { field: 'operation', value: [...SESSION_OBJECT_OPERATIONS] },
},
statusDetail: {
type: 'string',
description: 'Detailed status (working, waiting_for_user, finished, etc.)',
- condition: { field: 'operation', value: 'list_sessions', not: true },
+ condition: { field: 'operation', value: [...SESSION_OBJECT_OPERATIONS] },
+ },
+ title: {
+ type: 'string',
+ description: 'Session title',
+ condition: { field: 'operation', value: [...SESSION_OBJECT_OPERATIONS] },
+ },
+ createdAt: {
+ type: 'number',
+ description: 'Creation timestamp (Unix)',
+ condition: { field: 'operation', value: [...SESSION_OBJECT_OPERATIONS] },
+ },
+ updatedAt: {
+ type: 'number',
+ description: 'Last updated timestamp (Unix)',
+ condition: { field: 'operation', value: [...SESSION_OBJECT_OPERATIONS] },
},
- title: { type: 'string', description: 'Session title' },
- createdAt: { type: 'number', description: 'Creation timestamp (Unix)' },
- updatedAt: { type: 'number', description: 'Last updated timestamp (Unix)' },
acusConsumed: {
type: 'number',
description: 'ACUs consumed',
- condition: { field: 'operation', value: 'list_sessions', not: true },
+ condition: { field: 'operation', value: [...SESSION_OBJECT_OPERATIONS] },
+ },
+ tags: {
+ type: 'json',
+ description: 'Session tags (array of strings)',
+ condition: {
+ field: 'operation',
+ value: [
+ ...SESSION_OBJECT_OPERATIONS,
+ 'get_session_tags',
+ 'append_session_tags',
+ 'replace_session_tags',
+ ],
+ },
},
- tags: { type: 'json', description: 'Session tags' },
pullRequests: {
type: 'json',
- description: 'Pull requests created during the session',
- condition: { field: 'operation', value: 'list_sessions', not: true },
+ description: 'Pull requests created during the session ([{pr_url, pr_state}])',
+ condition: { field: 'operation', value: [...SESSION_OBJECT_OPERATIONS] },
},
structuredOutput: {
type: 'json',
description: 'Structured output from the session',
- condition: { field: 'operation', value: 'list_sessions', not: true },
+ condition: { field: 'operation', value: [...SESSION_OBJECT_OPERATIONS] },
},
playbookId: {
type: 'string',
description: 'Associated playbook ID',
- condition: { field: 'operation', value: 'list_sessions', not: true },
+ condition: { field: 'operation', value: [...SESSION_OBJECT_OPERATIONS] },
+ },
+ isArchived: {
+ type: 'boolean',
+ description: 'Whether the session is archived',
+ condition: { field: 'operation', value: [...SESSION_OBJECT_OPERATIONS] },
},
sessions: {
type: 'json',
- description: 'List of sessions',
+ description:
+ 'List of sessions ([{sessionId, url, status, statusDetail, title, tags, acusConsumed, pullRequests, playbookId, isArchived, ...}])',
condition: { field: 'operation', value: 'list_sessions' },
},
+ messages: {
+ type: 'json',
+ description: 'Messages in the session ([{eventId, source, message, createdAt}])',
+ condition: { field: 'operation', value: 'list_session_messages' },
+ },
+ attachments: {
+ type: 'json',
+ description: 'Session attachments ([{attachmentId, name, url, source, contentType}])',
+ condition: { field: 'operation', value: 'list_session_attachments' },
+ },
+ endCursor: {
+ type: 'string',
+ description: 'Pagination cursor for the next page, or null if last page',
+ condition: { field: 'operation', value: ['list_sessions', 'list_session_messages'] },
+ },
+ hasNextPage: {
+ type: 'boolean',
+ description: 'Whether more results are available',
+ condition: { field: 'operation', value: ['list_sessions', 'list_session_messages'] },
+ },
+ total: {
+ type: 'number',
+ description: 'Total number of results, if provided',
+ condition: { field: 'operation', value: ['list_sessions', 'list_session_messages'] },
+ },
},
}
diff --git a/apps/sim/blocks/blocks/greptile.ts b/apps/sim/blocks/blocks/greptile.ts
index 76cb142cf3c..83de267de4d 100644
--- a/apps/sim/blocks/blocks/greptile.ts
+++ b/apps/sim/blocks/blocks/greptile.ts
@@ -14,7 +14,7 @@ export const GreptileBlock: BlockConfig = {
category: 'tools',
integrationType: IntegrationType.DeveloperTools,
tags: ['version-control', 'knowledge-base'],
- bgColor: '#e5e5e5',
+ bgColor: '#5DE195',
icon: GreptileIcon,
subBlocks: [
{
@@ -89,7 +89,7 @@ export const GreptileBlock: BlockConfig = {
// type: 'switch',
// condition: { field: 'operation', value: 'greptile_search' },
// },
- // Index operation inputs
+ // Index & Status shared inputs
{
id: 'remote',
title: 'Git Remote',
@@ -99,14 +99,14 @@ export const GreptileBlock: BlockConfig = {
{ label: 'GitLab', id: 'gitlab' },
],
value: () => 'github',
- condition: { field: 'operation', value: 'greptile_index_repo' },
+ condition: { field: 'operation', value: ['greptile_index_repo', 'greptile_status'] },
},
{
id: 'repository',
title: 'Repository',
type: 'short-input',
placeholder: 'owner/repo',
- condition: { field: 'operation', value: 'greptile_index_repo' },
+ condition: { field: 'operation', value: ['greptile_index_repo', 'greptile_status'] },
required: true,
},
{
@@ -114,9 +114,10 @@ export const GreptileBlock: BlockConfig = {
title: 'Branch',
type: 'short-input',
placeholder: 'main',
- condition: { field: 'operation', value: 'greptile_index_repo' },
+ condition: { field: 'operation', value: ['greptile_index_repo', 'greptile_status'] },
required: true,
},
+ // Index-only inputs
{
id: 'reload',
title: 'Force Re-index',
@@ -129,34 +130,6 @@ export const GreptileBlock: BlockConfig = {
type: 'switch',
condition: { field: 'operation', value: 'greptile_index_repo' },
},
- // Status operation inputs
- {
- id: 'remote',
- title: 'Git Remote',
- type: 'dropdown',
- options: [
- { label: 'GitHub', id: 'github' },
- { label: 'GitLab', id: 'gitlab' },
- ],
- value: () => 'github',
- condition: { field: 'operation', value: 'greptile_status' },
- },
- {
- id: 'repository',
- title: 'Repository',
- type: 'short-input',
- placeholder: 'owner/repo',
- condition: { field: 'operation', value: 'greptile_status' },
- required: true,
- },
- {
- id: 'branch',
- title: 'Branch',
- type: 'short-input',
- placeholder: 'main',
- condition: { field: 'operation', value: 'greptile_status' },
- required: true,
- },
// API Keys (common)
{
id: 'apiKey',
diff --git a/apps/sim/tools/cursor/download_artifact.ts b/apps/sim/tools/cursor/download_artifact.ts
index 5b891688410..39d3da66928 100644
--- a/apps/sim/tools/cursor/download_artifact.ts
+++ b/apps/sim/tools/cursor/download_artifact.ts
@@ -59,7 +59,12 @@ export const downloadArtifactTool: ToolConfig 'https://api.cursor.com/v0/me',
+ method: 'GET',
+ headers: (params: GetApiKeyInfoParams) => ({
+ Authorization: `Basic ${Buffer.from(`${params.apiKey}:`).toString('base64')}`,
+ }),
+ },
+} satisfies Pick, 'params' | 'request'>
+
+export const getApiKeyInfoTool: ToolConfig = {
+ id: 'cursor_get_api_key_info',
+ name: 'Cursor Get API Key Info',
+ description: 'Retrieve details about the API key currently in use.',
+ version: '1.0.0',
+
+ ...getApiKeyInfoBase,
+
+ transformResponse: async (response) => {
+ const data = await response.json()
+
+ return {
+ success: true,
+ output: {
+ content: `API key "${data.apiKeyName}" for ${data.userEmail}`,
+ metadata: {
+ apiKeyName: data.apiKeyName,
+ createdAt: data.createdAt,
+ userEmail: data.userEmail,
+ },
+ },
+ }
+ },
+
+ outputs: {
+ content: { type: 'string', description: 'Human-readable API key summary' },
+ metadata: {
+ type: 'object',
+ description: 'API key metadata',
+ properties: {
+ apiKeyName: { type: 'string', description: 'Name of the API key' },
+ createdAt: { type: 'string', description: 'API key creation timestamp' },
+ userEmail: { type: 'string', description: 'Email of the key owner' },
+ },
+ },
+ },
+}
+
+interface GetApiKeyInfoV2Response {
+ success: boolean
+ output: {
+ apiKeyName: string
+ createdAt: string
+ userEmail: string
+ }
+}
+
+export const getApiKeyInfoV2Tool: ToolConfig = {
+ ...getApiKeyInfoBase,
+ id: 'cursor_get_api_key_info_v2',
+ name: 'Cursor Get API Key Info',
+ description:
+ 'Retrieve details about the API key currently in use. Returns API-aligned fields only.',
+ version: '2.0.0',
+ transformResponse: async (response) => {
+ const data = await response.json()
+
+ return {
+ success: true,
+ output: {
+ apiKeyName: data.apiKeyName,
+ createdAt: data.createdAt,
+ userEmail: data.userEmail,
+ },
+ }
+ },
+ outputs: {
+ apiKeyName: { type: 'string', description: 'Name of the API key' },
+ createdAt: { type: 'string', description: 'API key creation timestamp' },
+ userEmail: { type: 'string', description: 'Email of the key owner' },
+ },
+}
diff --git a/apps/sim/tools/cursor/index.ts b/apps/sim/tools/cursor/index.ts
index 80573009eec..5427b8de00e 100644
--- a/apps/sim/tools/cursor/index.ts
+++ b/apps/sim/tools/cursor/index.ts
@@ -2,10 +2,13 @@ import { addFollowupTool, addFollowupV2Tool } from '@/tools/cursor/add_followup'
import { deleteAgentTool, deleteAgentV2Tool } from '@/tools/cursor/delete_agent'
import { downloadArtifactTool, downloadArtifactV2Tool } from '@/tools/cursor/download_artifact'
import { getAgentTool, getAgentV2Tool } from '@/tools/cursor/get_agent'
+import { getApiKeyInfoTool, getApiKeyInfoV2Tool } from '@/tools/cursor/get_api_key_info'
import { getConversationTool, getConversationV2Tool } from '@/tools/cursor/get_conversation'
import { launchAgentTool, launchAgentV2Tool } from '@/tools/cursor/launch_agent'
import { listAgentsTool, listAgentsV2Tool } from '@/tools/cursor/list_agents'
import { listArtifactsTool, listArtifactsV2Tool } from '@/tools/cursor/list_artifacts'
+import { listModelsTool, listModelsV2Tool } from '@/tools/cursor/list_models'
+import { listRepositoriesTool, listRepositoriesV2Tool } from '@/tools/cursor/list_repositories'
import { stopAgentTool, stopAgentV2Tool } from '@/tools/cursor/stop_agent'
export const cursorListAgentsTool = listAgentsTool
@@ -17,6 +20,9 @@ export const cursorStopAgentTool = stopAgentTool
export const cursorDeleteAgentTool = deleteAgentTool
export const cursorDownloadArtifactTool = downloadArtifactTool
export const cursorListArtifactsTool = listArtifactsTool
+export const cursorListModelsTool = listModelsTool
+export const cursorListRepositoriesTool = listRepositoriesTool
+export const cursorGetApiKeyInfoTool = getApiKeyInfoTool
export const cursorListAgentsV2Tool = listAgentsV2Tool
export const cursorGetAgentV2Tool = getAgentV2Tool
@@ -27,3 +33,6 @@ export const cursorStopAgentV2Tool = stopAgentV2Tool
export const cursorDeleteAgentV2Tool = deleteAgentV2Tool
export const cursorDownloadArtifactV2Tool = downloadArtifactV2Tool
export const cursorListArtifactsV2Tool = listArtifactsV2Tool
+export const cursorListModelsV2Tool = listModelsV2Tool
+export const cursorListRepositoriesV2Tool = listRepositoriesV2Tool
+export const cursorGetApiKeyInfoV2Tool = getApiKeyInfoV2Tool
diff --git a/apps/sim/tools/cursor/list_models.ts b/apps/sim/tools/cursor/list_models.ts
new file mode 100644
index 00000000000..48012d429be
--- /dev/null
+++ b/apps/sim/tools/cursor/list_models.ts
@@ -0,0 +1,92 @@
+import type { ListModelsParams, ListModelsResponse } from '@/tools/cursor/types'
+import type { ToolConfig } from '@/tools/types'
+
+const listModelsBase = {
+ params: {
+ apiKey: {
+ type: 'string',
+ required: true,
+ visibility: 'user-only',
+ description: 'Cursor API key',
+ },
+ },
+ request: {
+ url: () => 'https://api.cursor.com/v0/models',
+ method: 'GET',
+ headers: (params: ListModelsParams) => ({
+ Authorization: `Basic ${Buffer.from(`${params.apiKey}:`).toString('base64')}`,
+ }),
+ },
+} satisfies Pick, 'params' | 'request'>
+
+export const listModelsTool: ToolConfig = {
+ id: 'cursor_list_models',
+ name: 'Cursor List Models',
+ description: 'List the models available for launching cloud agents.',
+ version: '1.0.0',
+
+ ...listModelsBase,
+
+ transformResponse: async (response) => {
+ const data = await response.json()
+ const models = data.models ?? []
+
+ return {
+ success: true,
+ output: {
+ content: `Found ${models.length} model(s)`,
+ metadata: {
+ models,
+ },
+ },
+ }
+ },
+
+ outputs: {
+ content: { type: 'string', description: 'Human-readable model count' },
+ metadata: {
+ type: 'object',
+ description: 'Models metadata',
+ properties: {
+ models: {
+ type: 'array',
+ description: 'Array of available model names',
+ items: { type: 'string', description: 'Model name' },
+ },
+ },
+ },
+ },
+}
+
+interface ListModelsV2Response {
+ success: boolean
+ output: {
+ models: string[]
+ }
+}
+
+export const listModelsV2Tool: ToolConfig = {
+ ...listModelsBase,
+ id: 'cursor_list_models_v2',
+ name: 'Cursor List Models',
+ description:
+ 'List the models available for launching cloud agents. Returns API-aligned fields only.',
+ version: '2.0.0',
+ transformResponse: async (response) => {
+ const data = await response.json()
+
+ return {
+ success: true,
+ output: {
+ models: Array.isArray(data.models) ? data.models : [],
+ },
+ }
+ },
+ outputs: {
+ models: {
+ type: 'array',
+ description: 'Array of available model names',
+ items: { type: 'string', description: 'Model name' },
+ },
+ },
+}
diff --git a/apps/sim/tools/cursor/list_repositories.ts b/apps/sim/tools/cursor/list_repositories.ts
new file mode 100644
index 00000000000..97ebdb8e5c6
--- /dev/null
+++ b/apps/sim/tools/cursor/list_repositories.ts
@@ -0,0 +1,109 @@
+import type { ListRepositoriesParams, ListRepositoriesResponse } from '@/tools/cursor/types'
+import type { ToolConfig } from '@/tools/types'
+
+const listRepositoriesBase = {
+ params: {
+ apiKey: {
+ type: 'string',
+ required: true,
+ visibility: 'user-only',
+ description: 'Cursor API key',
+ },
+ },
+ request: {
+ url: () => 'https://api.cursor.com/v0/repositories',
+ method: 'GET',
+ headers: (params: ListRepositoriesParams) => ({
+ Authorization: `Basic ${Buffer.from(`${params.apiKey}:`).toString('base64')}`,
+ }),
+ },
+} satisfies Pick, 'params' | 'request'>
+
+export const listRepositoriesTool: ToolConfig = {
+ id: 'cursor_list_repositories',
+ name: 'Cursor List Repositories',
+ description: 'List the GitHub repositories accessible to the authenticated user.',
+ version: '1.0.0',
+
+ ...listRepositoriesBase,
+
+ transformResponse: async (response) => {
+ const data = await response.json()
+ const repositories = data.repositories ?? []
+
+ return {
+ success: true,
+ output: {
+ content: `Found ${repositories.length} repository(ies)`,
+ metadata: {
+ repositories,
+ },
+ },
+ }
+ },
+
+ outputs: {
+ content: { type: 'string', description: 'Human-readable repository count' },
+ metadata: {
+ type: 'object',
+ description: 'Repositories metadata',
+ properties: {
+ repositories: {
+ type: 'array',
+ description: 'Array of accessible repositories',
+ items: {
+ type: 'object',
+ properties: {
+ owner: { type: 'string', description: 'Repository owner' },
+ name: { type: 'string', description: 'Repository name' },
+ repository: { type: 'string', description: 'Repository URL' },
+ },
+ },
+ },
+ },
+ },
+ },
+}
+
+interface ListRepositoriesV2Response {
+ success: boolean
+ output: {
+ repositories: Array<{ owner: string; name: string; repository: string }>
+ }
+}
+
+export const listRepositoriesV2Tool: ToolConfig<
+ ListRepositoriesParams,
+ ListRepositoriesV2Response
+> = {
+ ...listRepositoriesBase,
+ id: 'cursor_list_repositories_v2',
+ name: 'Cursor List Repositories',
+ description:
+ 'List the GitHub repositories accessible to the authenticated user. Returns API-aligned fields only.',
+ version: '2.0.0',
+ transformResponse: async (response) => {
+ const data = await response.json()
+
+ return {
+ success: true,
+ output: {
+ repositories: Array.isArray(data.repositories) ? data.repositories : [],
+ },
+ }
+ },
+ outputs: {
+ repositories: {
+ type: 'array',
+ description: 'Array of accessible repositories',
+ items: {
+ type: 'object',
+ properties: {
+ owner: { type: 'string', description: 'Repository owner' },
+ name: { type: 'string', description: 'Repository name' },
+ repository: { type: 'string', description: 'Repository URL' },
+ },
+ },
+ },
+ },
+}
diff --git a/apps/sim/tools/cursor/types.ts b/apps/sim/tools/cursor/types.ts
index f4117e21ccd..2dfcf38fa20 100644
--- a/apps/sim/tools/cursor/types.ts
+++ b/apps/sim/tools/cursor/types.ts
@@ -44,6 +44,12 @@ export interface DeleteAgentParams extends BaseCursorParams {
agentId: string
}
+export type ListModelsParams = BaseCursorParams
+
+export type ListRepositoriesParams = BaseCursorParams
+
+export type GetApiKeyInfoParams = BaseCursorParams
+
interface AgentSource {
repository: string
ref: string
@@ -54,14 +60,14 @@ interface AgentTarget {
url: string
prUrl?: string
autoCreatePr: boolean
- openAsCursorGithubApp: boolean
- skipReviewerRequest: boolean
+ openAsCursorGithubApp?: boolean
+ skipReviewerRequest?: boolean
}
interface AgentMetadata {
id: string
name: string
- status: 'CREATING' | 'RUNNING' | 'FINISHED' | 'STOPPED' | 'FAILED'
+ status: string
source: AgentSource
target: AgentTarget
summary?: string
@@ -150,14 +156,14 @@ export interface DeleteAgentResponse extends ToolResponse {
}
}
-interface GetApiKeyInfoResponse extends ToolResponse {
+export interface GetApiKeyInfoResponse extends ToolResponse {
output: {
content: string
metadata: ApiKeyInfoMetadata
}
}
-interface ListModelsResponse extends ToolResponse {
+export interface ListModelsResponse extends ToolResponse {
output: {
content: string
metadata: {
@@ -166,7 +172,7 @@ interface ListModelsResponse extends ToolResponse {
}
}
-interface ListRepositoriesResponse extends ToolResponse {
+export interface ListRepositoriesResponse extends ToolResponse {
output: {
content: string
metadata: {
@@ -201,7 +207,12 @@ export interface DownloadArtifactParams extends BaseCursorParams {
export interface DownloadArtifactResponse extends ToolResponse {
output: {
content: string
- metadata: Record
+ metadata: {
+ name: string
+ mimeType: string
+ data: string
+ size: number
+ }
}
}
diff --git a/apps/sim/tools/devin/append_session_tags.ts b/apps/sim/tools/devin/append_session_tags.ts
new file mode 100644
index 00000000000..ad50cdb83d5
--- /dev/null
+++ b/apps/sim/tools/devin/append_session_tags.ts
@@ -0,0 +1,70 @@
+import type { ToolConfig } from '@/tools/types'
+import type { DevinAppendSessionTagsParams, DevinSessionTagsResponse } from './types'
+import { normalizeTags } from './utils'
+
+export const devinAppendSessionTagsTool: ToolConfig<
+ DevinAppendSessionTagsParams,
+ DevinSessionTagsResponse
+> = {
+ id: 'devin_append_session_tags',
+ name: 'append_session_tags',
+ description: 'Add tags to a Devin session without removing existing tags (max 50 tags total).',
+ version: '1.0.0',
+
+ params: {
+ apiKey: {
+ type: 'string',
+ required: true,
+ visibility: 'user-only',
+ description: 'Devin API key (service user credential starting with cog_)',
+ },
+ orgId: {
+ type: 'string',
+ required: true,
+ visibility: 'user-only',
+ description: 'Devin organization ID (prefixed with org-)',
+ },
+ sessionId: {
+ type: 'string',
+ required: true,
+ visibility: 'user-or-llm',
+ description: 'The session ID to add tags to',
+ },
+ tags: {
+ type: 'string',
+ required: true,
+ visibility: 'user-or-llm',
+ description: 'Tags to append to the session (comma-separated string or array of strings)',
+ },
+ },
+
+ request: {
+ url: (params) =>
+ `https://api.devin.ai/v3/organizations/${params.orgId.trim()}/sessions/${params.sessionId.trim()}/tags`,
+ method: 'POST',
+ headers: (params) => ({
+ Authorization: `Bearer ${params.apiKey}`,
+ 'Content-Type': 'application/json',
+ }),
+ body: (params) => ({
+ tags: normalizeTags(params.tags),
+ }),
+ },
+
+ transformResponse: async (response: Response) => {
+ const data = await response.json()
+ return {
+ success: true,
+ output: {
+ tags: data.tags ?? [],
+ },
+ }
+ },
+
+ outputs: {
+ tags: {
+ type: 'json',
+ description: 'Updated list of tags on the session (array of strings)',
+ },
+ },
+}
diff --git a/apps/sim/tools/devin/archive_session.ts b/apps/sim/tools/devin/archive_session.ts
new file mode 100644
index 00000000000..7a4b13c2656
--- /dev/null
+++ b/apps/sim/tools/devin/archive_session.ts
@@ -0,0 +1,82 @@
+import type { ToolConfig } from '@/tools/types'
+import type { DevinArchiveSessionParams, DevinArchiveSessionResponse } from './types'
+import { DEVIN_SESSION_OUTPUT_PROPERTIES } from './types'
+
+export const devinArchiveSessionTool: ToolConfig<
+ DevinArchiveSessionParams,
+ DevinArchiveSessionResponse
+> = {
+ id: 'devin_archive_session',
+ name: 'archive_session',
+ description:
+ 'Archive a Devin session. Archived sessions can still be viewed but cannot be modified or resumed.',
+ version: '1.0.0',
+
+ params: {
+ apiKey: {
+ type: 'string',
+ required: true,
+ visibility: 'user-only',
+ description: 'Devin API key (service user credential starting with cog_)',
+ },
+ orgId: {
+ type: 'string',
+ required: true,
+ visibility: 'user-only',
+ description: 'Devin organization ID (prefixed with org-)',
+ },
+ sessionId: {
+ type: 'string',
+ required: true,
+ visibility: 'user-or-llm',
+ description: 'The session ID to archive',
+ },
+ },
+
+ request: {
+ url: (params) =>
+ `https://api.devin.ai/v3/organizations/${params.orgId.trim()}/sessions/${params.sessionId.trim()}/archive`,
+ method: 'POST',
+ headers: (params) => ({
+ Authorization: `Bearer ${params.apiKey}`,
+ }),
+ },
+
+ transformResponse: async (response: Response) => {
+ const data = await response.json()
+ return {
+ success: true,
+ output: {
+ sessionId: data.session_id ?? null,
+ url: data.url ?? null,
+ status: data.status ?? null,
+ statusDetail: data.status_detail ?? null,
+ title: data.title ?? null,
+ createdAt: data.created_at ?? null,
+ updatedAt: data.updated_at ?? null,
+ acusConsumed: data.acus_consumed ?? null,
+ tags: data.tags ?? [],
+ pullRequests: data.pull_requests ?? [],
+ structuredOutput: data.structured_output ?? null,
+ playbookId: data.playbook_id ?? null,
+ isArchived: data.is_archived ?? false,
+ },
+ }
+ },
+
+ outputs: {
+ sessionId: DEVIN_SESSION_OUTPUT_PROPERTIES.sessionId,
+ url: DEVIN_SESSION_OUTPUT_PROPERTIES.url,
+ status: DEVIN_SESSION_OUTPUT_PROPERTIES.status,
+ statusDetail: DEVIN_SESSION_OUTPUT_PROPERTIES.statusDetail,
+ title: DEVIN_SESSION_OUTPUT_PROPERTIES.title,
+ createdAt: DEVIN_SESSION_OUTPUT_PROPERTIES.createdAt,
+ updatedAt: DEVIN_SESSION_OUTPUT_PROPERTIES.updatedAt,
+ acusConsumed: DEVIN_SESSION_OUTPUT_PROPERTIES.acusConsumed,
+ tags: DEVIN_SESSION_OUTPUT_PROPERTIES.tags,
+ pullRequests: DEVIN_SESSION_OUTPUT_PROPERTIES.pullRequests,
+ structuredOutput: DEVIN_SESSION_OUTPUT_PROPERTIES.structuredOutput,
+ playbookId: DEVIN_SESSION_OUTPUT_PROPERTIES.playbookId,
+ isArchived: DEVIN_SESSION_OUTPUT_PROPERTIES.isArchived,
+ },
+}
diff --git a/apps/sim/tools/devin/create_session.ts b/apps/sim/tools/devin/create_session.ts
index 1c99bfba774..72febaa6445 100644
--- a/apps/sim/tools/devin/create_session.ts
+++ b/apps/sim/tools/devin/create_session.ts
@@ -1,6 +1,7 @@
import type { ToolConfig } from '@/tools/types'
import type { DevinCreateSessionParams, DevinCreateSessionResponse } from './types'
import { DEVIN_SESSION_OUTPUT_PROPERTIES } from './types'
+import { normalizeTags } from './utils'
export const devinCreateSessionTool: ToolConfig<
DevinCreateSessionParams,
@@ -19,6 +20,12 @@ export const devinCreateSessionTool: ToolConfig<
visibility: 'user-only',
description: 'Devin API key (service user credential starting with cog_)',
},
+ orgId: {
+ type: 'string',
+ required: true,
+ visibility: 'user-only',
+ description: 'Devin organization ID (prefixed with org-)',
+ },
prompt: {
type: 'string',
required: true,
@@ -46,7 +53,7 @@ export const devinCreateSessionTool: ToolConfig<
},
request: {
- url: 'https://api.devin.ai/v3/organizations/sessions',
+ url: (params) => `https://api.devin.ai/v3/organizations/${params.orgId.trim()}/sessions`,
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
@@ -60,9 +67,8 @@ export const devinCreateSessionTool: ToolConfig<
if (params.maxAcuLimit != null) {
body.max_acu_limit = params.maxAcuLimit
}
- if (params.tags) {
- body.tags = params.tags.split(',').map((t: string) => t.trim())
- }
+ const tags = normalizeTags(params.tags)
+ if (tags.length > 0) body.tags = tags
return body
},
},
@@ -80,10 +86,11 @@ export const devinCreateSessionTool: ToolConfig<
createdAt: data.created_at ?? null,
updatedAt: data.updated_at ?? null,
acusConsumed: data.acus_consumed ?? null,
- tags: data.tags ?? null,
- pullRequests: data.pull_requests ?? null,
+ tags: data.tags ?? [],
+ pullRequests: data.pull_requests ?? [],
structuredOutput: data.structured_output ?? null,
playbookId: data.playbook_id ?? null,
+ isArchived: data.is_archived ?? false,
},
}
},
@@ -101,5 +108,6 @@ export const devinCreateSessionTool: ToolConfig<
pullRequests: DEVIN_SESSION_OUTPUT_PROPERTIES.pullRequests,
structuredOutput: DEVIN_SESSION_OUTPUT_PROPERTIES.structuredOutput,
playbookId: DEVIN_SESSION_OUTPUT_PROPERTIES.playbookId,
+ isArchived: DEVIN_SESSION_OUTPUT_PROPERTIES.isArchived,
},
}
diff --git a/apps/sim/tools/devin/get_session.ts b/apps/sim/tools/devin/get_session.ts
index 0af5b309bfb..f25d43c1e97 100644
--- a/apps/sim/tools/devin/get_session.ts
+++ b/apps/sim/tools/devin/get_session.ts
@@ -16,6 +16,12 @@ export const devinGetSessionTool: ToolConfig `https://api.devin.ai/v3/organizations/sessions/${params.sessionId}`,
+ url: (params) =>
+ `https://api.devin.ai/v3/organizations/${params.orgId.trim()}/sessions/${params.sessionId.trim()}`,
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
@@ -45,10 +52,11 @@ export const devinGetSessionTool: ToolConfig = {
+ id: 'devin_get_session_tags',
+ name: 'get_session_tags',
+ description: 'Retrieve the tags currently applied to a Devin session.',
+ version: '1.0.0',
+
+ params: {
+ apiKey: {
+ type: 'string',
+ required: true,
+ visibility: 'user-only',
+ description: 'Devin API key (service user credential starting with cog_)',
+ },
+ orgId: {
+ type: 'string',
+ required: true,
+ visibility: 'user-only',
+ description: 'Devin organization ID (prefixed with org-)',
+ },
+ sessionId: {
+ type: 'string',
+ required: true,
+ visibility: 'user-or-llm',
+ description: 'The session ID to retrieve tags for',
+ },
+ },
+
+ request: {
+ url: (params) =>
+ `https://api.devin.ai/v3/organizations/${params.orgId.trim()}/sessions/${params.sessionId.trim()}/tags`,
+ method: 'GET',
+ headers: (params) => ({
+ Authorization: `Bearer ${params.apiKey}`,
+ }),
+ },
+
+ transformResponse: async (response: Response) => {
+ const data = await response.json()
+ return {
+ success: true,
+ output: {
+ tags: data.tags ?? [],
+ },
+ }
+ },
+
+ outputs: {
+ tags: {
+ type: 'json',
+ description: 'Tags applied to the session (array of strings)',
+ },
+ },
+}
diff --git a/apps/sim/tools/devin/index.ts b/apps/sim/tools/devin/index.ts
index be9d1f91db8..9025778b76d 100644
--- a/apps/sim/tools/devin/index.ts
+++ b/apps/sim/tools/devin/index.ts
@@ -1,4 +1,11 @@
+export { devinAppendSessionTagsTool } from './append_session_tags'
+export { devinArchiveSessionTool } from './archive_session'
export { devinCreateSessionTool } from './create_session'
export { devinGetSessionTool } from './get_session'
+export { devinGetSessionTagsTool } from './get_session_tags'
+export { devinListSessionAttachmentsTool } from './list_session_attachments'
+export { devinListSessionMessagesTool } from './list_session_messages'
export { devinListSessionsTool } from './list_sessions'
+export { devinReplaceSessionTagsTool } from './replace_session_tags'
export { devinSendMessageTool } from './send_message'
+export { devinTerminateSessionTool } from './terminate_session'
diff --git a/apps/sim/tools/devin/list_session_attachments.ts b/apps/sim/tools/devin/list_session_attachments.ts
new file mode 100644
index 00000000000..32b4f0f746a
--- /dev/null
+++ b/apps/sim/tools/devin/list_session_attachments.ts
@@ -0,0 +1,74 @@
+import type { ToolConfig } from '@/tools/types'
+import type {
+ DevinListSessionAttachmentsParams,
+ DevinListSessionAttachmentsResponse,
+} from './types'
+import { DEVIN_SESSION_ATTACHMENT_PROPERTIES } from './types'
+
+export const devinListSessionAttachmentsTool: ToolConfig<
+ DevinListSessionAttachmentsParams,
+ DevinListSessionAttachmentsResponse
+> = {
+ id: 'devin_list_session_attachments',
+ name: 'list_session_attachments',
+ description: 'List the files uploaded to or produced by a Devin session.',
+ version: '1.0.0',
+
+ params: {
+ apiKey: {
+ type: 'string',
+ required: true,
+ visibility: 'user-only',
+ description: 'Devin API key (service user credential starting with cog_)',
+ },
+ orgId: {
+ type: 'string',
+ required: true,
+ visibility: 'user-only',
+ description: 'Devin organization ID (prefixed with org-)',
+ },
+ sessionId: {
+ type: 'string',
+ required: true,
+ visibility: 'user-or-llm',
+ description: 'The session ID to list attachments for',
+ },
+ },
+
+ request: {
+ url: (params) =>
+ `https://api.devin.ai/v3/organizations/${params.orgId.trim()}/sessions/${params.sessionId.trim()}/attachments`,
+ method: 'GET',
+ headers: (params) => ({
+ Authorization: `Bearer ${params.apiKey}`,
+ }),
+ },
+
+ transformResponse: async (response: Response) => {
+ const data = await response.json()
+ const items = Array.isArray(data) ? data : (data.items ?? [])
+ return {
+ success: true,
+ output: {
+ attachments: items.map((item: Record) => ({
+ attachmentId: item.attachment_id ?? null,
+ name: item.name ?? null,
+ url: item.url ?? null,
+ source: item.source ?? null,
+ contentType: item.content_type ?? null,
+ })),
+ },
+ }
+ },
+
+ outputs: {
+ attachments: {
+ type: 'array',
+ description: 'Attachments associated with the session',
+ items: {
+ type: 'object',
+ properties: DEVIN_SESSION_ATTACHMENT_PROPERTIES,
+ },
+ },
+ },
+}
diff --git a/apps/sim/tools/devin/list_session_messages.ts b/apps/sim/tools/devin/list_session_messages.ts
new file mode 100644
index 00000000000..ca50ccd33f1
--- /dev/null
+++ b/apps/sim/tools/devin/list_session_messages.ts
@@ -0,0 +1,105 @@
+import type { ToolConfig } from '@/tools/types'
+import type { DevinListSessionMessagesParams, DevinListSessionMessagesResponse } from './types'
+import { DEVIN_SESSION_MESSAGE_PROPERTIES } from './types'
+
+export const devinListSessionMessagesTool: ToolConfig<
+ DevinListSessionMessagesParams,
+ DevinListSessionMessagesResponse
+> = {
+ id: 'devin_list_session_messages',
+ name: 'list_session_messages',
+ description:
+ 'List the messages exchanged in a Devin session, including messages from both the user and Devin.',
+ version: '1.0.0',
+
+ params: {
+ apiKey: {
+ type: 'string',
+ required: true,
+ visibility: 'user-only',
+ description: 'Devin API key (service user credential starting with cog_)',
+ },
+ orgId: {
+ type: 'string',
+ required: true,
+ visibility: 'user-only',
+ description: 'Devin organization ID (prefixed with org-)',
+ },
+ sessionId: {
+ type: 'string',
+ required: true,
+ visibility: 'user-or-llm',
+ description: 'The session ID to list messages for',
+ },
+ limit: {
+ type: 'number',
+ required: false,
+ visibility: 'user-or-llm',
+ description: 'Maximum number of messages to return (1-200, default: 100)',
+ },
+ after: {
+ type: 'string',
+ required: false,
+ visibility: 'user-or-llm',
+ description: 'Pagination cursor (endCursor from a previous response) to fetch the next page',
+ },
+ },
+
+ request: {
+ url: (params) => {
+ const searchParams = new URLSearchParams()
+ if (params.limit) searchParams.set('first', String(params.limit))
+ if (params.after) searchParams.set('after', params.after.trim())
+ const qs = searchParams.toString()
+ return `https://api.devin.ai/v3/organizations/${params.orgId.trim()}/sessions/${params.sessionId.trim()}/messages${qs ? `?${qs}` : ''}`
+ },
+ method: 'GET',
+ headers: (params) => ({
+ Authorization: `Bearer ${params.apiKey}`,
+ }),
+ },
+
+ transformResponse: async (response: Response) => {
+ const data = await response.json()
+ const items = data.items ?? []
+ return {
+ success: true,
+ output: {
+ messages: items.map((item: Record) => ({
+ eventId: item.event_id ?? null,
+ source: item.source ?? null,
+ message: item.message ?? null,
+ createdAt: item.created_at ?? null,
+ })),
+ endCursor: data.end_cursor ?? null,
+ hasNextPage: data.has_next_page ?? false,
+ total: data.total ?? null,
+ },
+ }
+ },
+
+ outputs: {
+ messages: {
+ type: 'array',
+ description: 'Messages exchanged in the session',
+ items: {
+ type: 'object',
+ properties: DEVIN_SESSION_MESSAGE_PROPERTIES,
+ },
+ },
+ endCursor: {
+ type: 'string',
+ description: 'Pagination cursor for the next page, or null if last page',
+ optional: true,
+ },
+ hasNextPage: {
+ type: 'boolean',
+ description: 'Whether more messages are available',
+ },
+ total: {
+ type: 'number',
+ description: 'Total number of messages, if provided',
+ optional: true,
+ },
+ },
+}
diff --git a/apps/sim/tools/devin/list_sessions.ts b/apps/sim/tools/devin/list_sessions.ts
index 66391e4f34e..90bf6d4a21b 100644
--- a/apps/sim/tools/devin/list_sessions.ts
+++ b/apps/sim/tools/devin/list_sessions.ts
@@ -16,20 +16,34 @@ export const devinListSessionsTool: ToolConfig {
const searchParams = new URLSearchParams()
if (params.limit) searchParams.set('first', String(params.limit))
+ if (params.after) searchParams.set('after', params.after.trim())
const qs = searchParams.toString()
- return `https://api.devin.ai/v3/organizations/sessions${qs ? `?${qs}` : ''}`
+ return `https://api.devin.ai/v3/organizations/${params.orgId.trim()}/sessions${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params) => ({
@@ -51,8 +65,15 @@ export const devinListSessionsTool: ToolConfig = {
+ id: 'devin_replace_session_tags',
+ name: 'replace_session_tags',
+ description: 'Replace all tags on a Devin session with a new set of tags (max 50 tags).',
+ version: '1.0.0',
+
+ params: {
+ apiKey: {
+ type: 'string',
+ required: true,
+ visibility: 'user-only',
+ description: 'Devin API key (service user credential starting with cog_)',
+ },
+ orgId: {
+ type: 'string',
+ required: true,
+ visibility: 'user-only',
+ description: 'Devin organization ID (prefixed with org-)',
+ },
+ sessionId: {
+ type: 'string',
+ required: true,
+ visibility: 'user-or-llm',
+ description: 'The session ID to replace tags on',
+ },
+ tags: {
+ type: 'string',
+ required: true,
+ visibility: 'user-or-llm',
+ description:
+ 'Tags that will overwrite the existing tags (comma-separated string or array of strings)',
+ },
+ },
+
+ request: {
+ url: (params) =>
+ `https://api.devin.ai/v3/organizations/${params.orgId.trim()}/sessions/${params.sessionId.trim()}/tags`,
+ method: 'PUT',
+ headers: (params) => ({
+ Authorization: `Bearer ${params.apiKey}`,
+ 'Content-Type': 'application/json',
+ }),
+ body: (params) => ({
+ tags: normalizeTags(params.tags),
+ }),
+ },
+
+ transformResponse: async (response: Response) => {
+ const data = await response.json()
+ return {
+ success: true,
+ output: {
+ tags: data.tags ?? [],
+ },
+ }
+ },
+
+ outputs: {
+ tags: {
+ type: 'json',
+ description: 'Updated list of tags on the session (array of strings)',
+ },
+ },
+}
diff --git a/apps/sim/tools/devin/send_message.ts b/apps/sim/tools/devin/send_message.ts
index 13844c47032..c6705b020a3 100644
--- a/apps/sim/tools/devin/send_message.ts
+++ b/apps/sim/tools/devin/send_message.ts
@@ -16,6 +16,12 @@ export const devinSendMessageTool: ToolConfig `https://api.devin.ai/v3/organizations/sessions/${params.sessionId}/messages`,
+ url: (params) =>
+ `https://api.devin.ai/v3/organizations/${params.orgId.trim()}/sessions/${params.sessionId.trim()}/messages`,
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
@@ -55,10 +62,11 @@ export const devinSendMessageTool: ToolConfig = {
+ id: 'devin_terminate_session',
+ name: 'terminate_session',
+ description:
+ 'Terminate a Devin session. Optionally archive the session instead of permanently terminating it.',
+ version: '1.0.0',
+
+ params: {
+ apiKey: {
+ type: 'string',
+ required: true,
+ visibility: 'user-only',
+ description: 'Devin API key (service user credential starting with cog_)',
+ },
+ orgId: {
+ type: 'string',
+ required: true,
+ visibility: 'user-only',
+ description: 'Devin organization ID (prefixed with org-)',
+ },
+ sessionId: {
+ type: 'string',
+ required: true,
+ visibility: 'user-or-llm',
+ description: 'The session ID to terminate',
+ },
+ archive: {
+ type: 'boolean',
+ required: false,
+ visibility: 'user-or-llm',
+ description: 'Archive the session instead of permanently terminating it (default: false)',
+ },
+ },
+
+ request: {
+ url: (params) => {
+ const searchParams = new URLSearchParams()
+ if (params.archive) searchParams.set('archive', 'true')
+ const qs = searchParams.toString()
+ return `https://api.devin.ai/v3/organizations/${params.orgId.trim()}/sessions/${params.sessionId.trim()}${qs ? `?${qs}` : ''}`
+ },
+ method: 'DELETE',
+ headers: (params) => ({
+ Authorization: `Bearer ${params.apiKey}`,
+ }),
+ },
+
+ transformResponse: async (response: Response) => {
+ const data = await response.json()
+ return {
+ success: true,
+ output: {
+ sessionId: data.session_id ?? null,
+ url: data.url ?? null,
+ status: data.status ?? null,
+ statusDetail: data.status_detail ?? null,
+ title: data.title ?? null,
+ createdAt: data.created_at ?? null,
+ updatedAt: data.updated_at ?? null,
+ acusConsumed: data.acus_consumed ?? null,
+ tags: data.tags ?? [],
+ pullRequests: data.pull_requests ?? [],
+ structuredOutput: data.structured_output ?? null,
+ playbookId: data.playbook_id ?? null,
+ isArchived: data.is_archived ?? false,
+ },
+ }
+ },
+
+ outputs: {
+ sessionId: DEVIN_SESSION_OUTPUT_PROPERTIES.sessionId,
+ url: DEVIN_SESSION_OUTPUT_PROPERTIES.url,
+ status: DEVIN_SESSION_OUTPUT_PROPERTIES.status,
+ statusDetail: DEVIN_SESSION_OUTPUT_PROPERTIES.statusDetail,
+ title: DEVIN_SESSION_OUTPUT_PROPERTIES.title,
+ createdAt: DEVIN_SESSION_OUTPUT_PROPERTIES.createdAt,
+ updatedAt: DEVIN_SESSION_OUTPUT_PROPERTIES.updatedAt,
+ acusConsumed: DEVIN_SESSION_OUTPUT_PROPERTIES.acusConsumed,
+ tags: DEVIN_SESSION_OUTPUT_PROPERTIES.tags,
+ pullRequests: DEVIN_SESSION_OUTPUT_PROPERTIES.pullRequests,
+ structuredOutput: DEVIN_SESSION_OUTPUT_PROPERTIES.structuredOutput,
+ playbookId: DEVIN_SESSION_OUTPUT_PROPERTIES.playbookId,
+ isArchived: DEVIN_SESSION_OUTPUT_PROPERTIES.isArchived,
+ },
+}
diff --git a/apps/sim/tools/devin/types.ts b/apps/sim/tools/devin/types.ts
index 7db09ae08af..a1b7c338380 100644
--- a/apps/sim/tools/devin/types.ts
+++ b/apps/sim/tools/devin/types.ts
@@ -2,28 +2,80 @@ import type { OutputProperty, ToolResponse } from '@/tools/types'
export interface DevinCreateSessionParams {
apiKey: string
+ orgId: string
prompt: string
playbookId?: string
maxAcuLimit?: number
- tags?: string
+ tags?: string | string[]
}
export interface DevinGetSessionParams {
apiKey: string
+ orgId: string
sessionId: string
}
export interface DevinListSessionsParams {
apiKey: string
+ orgId: string
limit?: number
+ after?: string
}
export interface DevinSendMessageParams {
apiKey: string
+ orgId: string
sessionId: string
message: string
}
+export interface DevinListSessionMessagesParams {
+ apiKey: string
+ orgId: string
+ sessionId: string
+ limit?: number
+ after?: string
+}
+
+export interface DevinListSessionAttachmentsParams {
+ apiKey: string
+ orgId: string
+ sessionId: string
+}
+
+export interface DevinGetSessionTagsParams {
+ apiKey: string
+ orgId: string
+ sessionId: string
+}
+
+export interface DevinAppendSessionTagsParams {
+ apiKey: string
+ orgId: string
+ sessionId: string
+ tags: string | string[]
+}
+
+export interface DevinReplaceSessionTagsParams {
+ apiKey: string
+ orgId: string
+ sessionId: string
+ tags: string | string[]
+}
+
+export interface DevinArchiveSessionParams {
+ apiKey: string
+ orgId: string
+ sessionId: string
+}
+
+export interface DevinTerminateSessionParams {
+ apiKey: string
+ orgId: string
+ sessionId: string
+ archive?: boolean
+}
+
export const DEVIN_SESSION_OUTPUT_PROPERTIES = {
sessionId: {
type: 'string',
@@ -65,13 +117,11 @@ export const DEVIN_SESSION_OUTPUT_PROPERTIES = {
},
tags: {
type: 'json',
- description: 'Tags associated with the session',
- optional: true,
+ description: 'Tags associated with the session (array of strings)',
},
pullRequests: {
type: 'json',
- description: 'Pull requests created during the session',
- optional: true,
+ description: 'Pull requests created during the session ([{pr_url, pr_state}])',
},
structuredOutput: {
type: 'json',
@@ -83,6 +133,11 @@ export const DEVIN_SESSION_OUTPUT_PROPERTIES = {
description: 'Associated playbook ID',
optional: true,
},
+ isArchived: {
+ type: 'boolean',
+ description: 'Whether the session is archived',
+ optional: true,
+ },
} as const satisfies Record
export const DEVIN_SESSION_LIST_ITEM_PROPERTIES = {
@@ -120,7 +175,69 @@ export const DEVIN_SESSION_LIST_ITEM_PROPERTIES = {
},
tags: {
type: 'json',
- description: 'Session tags',
+ description: 'Session tags (array of strings)',
+ },
+ acusConsumed: {
+ type: 'number',
+ description: 'ACUs consumed by the session',
+ optional: true,
+ },
+ pullRequests: {
+ type: 'json',
+ description: 'Pull requests created during the session ([{pr_url, pr_state}])',
+ },
+ playbookId: {
+ type: 'string',
+ description: 'Associated playbook ID',
+ optional: true,
+ },
+ isArchived: {
+ type: 'boolean',
+ description: 'Whether the session is archived',
+ optional: true,
+ },
+} as const satisfies Record
+
+export const DEVIN_SESSION_MESSAGE_PROPERTIES = {
+ eventId: {
+ type: 'string',
+ description: 'Unique identifier for the message event',
+ },
+ source: {
+ type: 'string',
+ description: 'Origin of the message (devin or user)',
+ },
+ message: {
+ type: 'string',
+ description: 'The message content',
+ },
+ createdAt: {
+ type: 'number',
+ description: 'Unix timestamp when the message was created',
+ optional: true,
+ },
+} as const satisfies Record
+
+export const DEVIN_SESSION_ATTACHMENT_PROPERTIES = {
+ attachmentId: {
+ type: 'string',
+ description: 'Unique identifier for the attachment',
+ },
+ name: {
+ type: 'string',
+ description: 'Attachment file name',
+ },
+ url: {
+ type: 'string',
+ description: 'URL to download the attachment',
+ },
+ source: {
+ type: 'string',
+ description: 'Origin of the attachment (devin or user)',
+ },
+ contentType: {
+ type: 'string',
+ description: 'MIME type of the attachment',
optional: true,
},
} as const satisfies Record
@@ -134,10 +251,11 @@ interface DevinSessionOutput {
createdAt: number | null
updatedAt: number | null
acusConsumed: number | null
- tags: string[] | null
- pullRequests: Array<{ pr_url: string; pr_state: string | null }> | null
+ tags: string[]
+ pullRequests: Array<{ pr_url: string; pr_state: string | null }>
structuredOutput: Record | null
playbookId: string | null
+ isArchived: boolean
}
export interface DevinCreateSessionResponse extends ToolResponse {
@@ -158,11 +276,58 @@ export interface DevinListSessionsResponse extends ToolResponse {
title: string | null
createdAt: number | null
updatedAt: number | null
- tags: string[] | null
+ tags: string[]
+ acusConsumed: number | null
+ pullRequests: Array<{ pr_url: string; pr_state: string | null }>
+ playbookId: string | null
+ isArchived: boolean
}>
+ endCursor: string | null
+ hasNextPage: boolean
+ total: number | null
}
}
export interface DevinSendMessageResponse extends ToolResponse {
output: DevinSessionOutput
}
+
+export interface DevinListSessionMessagesResponse extends ToolResponse {
+ output: {
+ messages: Array<{
+ eventId: string
+ source: string
+ message: string
+ createdAt: number | null
+ }>
+ endCursor: string | null
+ hasNextPage: boolean
+ total: number | null
+ }
+}
+
+export interface DevinListSessionAttachmentsResponse extends ToolResponse {
+ output: {
+ attachments: Array<{
+ attachmentId: string
+ name: string
+ url: string
+ source: string
+ contentType: string | null
+ }>
+ }
+}
+
+export interface DevinSessionTagsResponse extends ToolResponse {
+ output: {
+ tags: string[]
+ }
+}
+
+export interface DevinArchiveSessionResponse extends ToolResponse {
+ output: DevinSessionOutput
+}
+
+export interface DevinTerminateSessionResponse extends ToolResponse {
+ output: DevinSessionOutput
+}
diff --git a/apps/sim/tools/devin/utils.ts b/apps/sim/tools/devin/utils.ts
new file mode 100644
index 00000000000..34863b4f8ea
--- /dev/null
+++ b/apps/sim/tools/devin/utils.ts
@@ -0,0 +1,19 @@
+/**
+ * Normalize a tags input into a clean string array.
+ *
+ * Tags can arrive either as a comma-separated string (typed into the block's
+ * text input) or as a string array (when wired from another block's JSON
+ * output, e.g. the tags returned by a get/append/replace tags operation).
+ */
+export function normalizeTags(input: string | string[] | undefined | null): string[] {
+ if (Array.isArray(input)) {
+ return input.map((tag) => String(tag).trim()).filter(Boolean)
+ }
+ if (typeof input === 'string') {
+ return input
+ .split(',')
+ .map((tag) => tag.trim())
+ .filter(Boolean)
+ }
+ return []
+}
diff --git a/apps/sim/tools/registry.ts b/apps/sim/tools/registry.ts
index d6558b49423..2bf90f1e477 100644
--- a/apps/sim/tools/registry.ts
+++ b/apps/sim/tools/registry.ts
@@ -435,6 +435,8 @@ import {
cursorDownloadArtifactV2Tool,
cursorGetAgentTool,
cursorGetAgentV2Tool,
+ cursorGetApiKeyInfoTool,
+ cursorGetApiKeyInfoV2Tool,
cursorGetConversationTool,
cursorGetConversationV2Tool,
cursorLaunchAgentTool,
@@ -443,6 +445,10 @@ import {
cursorListAgentsV2Tool,
cursorListArtifactsTool,
cursorListArtifactsV2Tool,
+ cursorListModelsTool,
+ cursorListModelsV2Tool,
+ cursorListRepositoriesTool,
+ cursorListRepositoriesV2Tool,
cursorStopAgentTool,
cursorStopAgentV2Tool,
} from '@/tools/cursor'
@@ -487,10 +493,17 @@ import {
datadogSubmitMetricsTool,
} from '@/tools/datadog'
import {
+ devinAppendSessionTagsTool,
+ devinArchiveSessionTool,
devinCreateSessionTool,
+ devinGetSessionTagsTool,
devinGetSessionTool,
+ devinListSessionAttachmentsTool,
+ devinListSessionMessagesTool,
devinListSessionsTool,
+ devinReplaceSessionTagsTool,
devinSendMessageTool,
+ devinTerminateSessionTool,
} from '@/tools/devin'
import {
discordAddReactionTool,
@@ -4027,6 +4040,13 @@ export const tools: Record = {
devin_get_session: devinGetSessionTool,
devin_list_sessions: devinListSessionsTool,
devin_send_message: devinSendMessageTool,
+ devin_list_session_messages: devinListSessionMessagesTool,
+ devin_list_session_attachments: devinListSessionAttachmentsTool,
+ devin_get_session_tags: devinGetSessionTagsTool,
+ devin_append_session_tags: devinAppendSessionTagsTool,
+ devin_replace_session_tags: devinReplaceSessionTagsTool,
+ devin_archive_session: devinArchiveSessionTool,
+ devin_terminate_session: devinTerminateSessionTool,
dagster_delete_run: dagsterDeleteRunTool,
dagster_get_run: dagsterGetRunTool,
dagster_get_run_logs: dagsterGetRunLogsTool,
@@ -4822,6 +4842,12 @@ export const tools: Record = {
cursor_download_artifact_v2: cursorDownloadArtifactV2Tool,
cursor_list_artifacts: cursorListArtifactsTool,
cursor_list_artifacts_v2: cursorListArtifactsV2Tool,
+ cursor_list_models: cursorListModelsTool,
+ cursor_list_models_v2: cursorListModelsV2Tool,
+ cursor_list_repositories: cursorListRepositoriesTool,
+ cursor_list_repositories_v2: cursorListRepositoriesV2Tool,
+ cursor_get_api_key_info: cursorGetApiKeyInfoTool,
+ cursor_get_api_key_info_v2: cursorGetApiKeyInfoV2Tool,
trello_list_lists: trelloListListsTool,
trello_list_cards: trelloListCardsTool,
trello_create_card: trelloCreateCardTool,
diff --git a/bun.lock b/bun.lock
index 070992c9a4f..fdee6edfc42 100644
--- a/bun.lock
+++ b/bun.lock
@@ -1,5 +1,6 @@
{
"lockfileVersion": 1,
+ "configVersion": 0,
"workspaces": {
"": {
"name": "simstudio",