{ "kind": "open-flow-workflow", "version": 1, "name": "Slack Support Tickets to Linear", "content": { "modelVersion": 14, "document": { "bindings": {}, "graph": { "nodes": { "n_9a578baa6a08257f": { "name": "Test support ticket scan", "kind": "start", "inputDefs": [] }, "n_7a7c405fa3ca6a8f": { "name": "Every five minutes UTC", "kind": "cron", "cronTimes": [ { "type": "cron", "expression": "*/5 * * * *", "timezone": "UTC" } ] }, "n_70a54dbc59bb814b": { "inputs": { "query": { "kind": "value", "value": "in:#YOUR_SUPPORT_CHANNEL has::ticket:" }, "count": { "kind": "value", "value": 10 }, "page": { "kind": "value", "value": 1 }, "sort": { "kind": "value", "value": "timestamp" }, "sortDir": { "kind": "value", "value": "desc" }, "includeRaw": { "kind": "value", "value": false } }, "name": "Find latest ten ticket messages", "kind": "task", "task": { "inputs": [ { "description": "Include the complete unmodified Slack message. History and thread requests also request message metadata.", "jsonSchema": { "type": "boolean", "description": "Include the complete unmodified Slack message. History and thread requests also request message metadata." }, "nullable": true, "handle": "includeRaw", "value": null }, { "description": "The Slack search query.", "jsonSchema": { "type": "string", "minLength": 1, "description": "The Slack search query." }, "nullable": false, "handle": "query" }, { "description": "The number of results to return per page.", "jsonSchema": { "type": "integer", "minimum": 1, "maximum": 100, "description": "The number of results to return per page." }, "nullable": true, "handle": "count", "value": null }, { "description": "The Slack page number to fetch.", "jsonSchema": { "type": "integer", "minimum": 1, "maximum": 100, "description": "The Slack page number to fetch." }, "nullable": true, "handle": "page", "value": null }, { "description": "The Slack cursor for cursormark pagination. Use '*' for the first request.", "jsonSchema": { "type": "string", "description": "The Slack cursor for cursormark pagination. Use '*' for the first request." }, "nullable": true, "handle": "cursor", "value": null }, { "description": "Whether Slack should mark query terms in matching text.", "jsonSchema": { "type": "boolean", "description": "Whether Slack should mark query terms in matching text." }, "nullable": true, "handle": "highlight", "value": null }, { "description": "How Slack should sort search results.", "jsonSchema": { "type": "string", "enum": [ "score", "timestamp" ], "description": "How Slack should sort search results." }, "nullable": true, "handle": "sort", "value": null }, { "description": "The sort direction for search results.", "jsonSchema": { "type": "string", "enum": [ "asc", "desc" ], "description": "The sort direction for search results." }, "nullable": true, "handle": "sortDir", "value": null }, { "description": "The encoded team ID to search when using an org-level token.", "jsonSchema": { "type": "string", "description": "The encoded team ID to search when using an org-level token." }, "nullable": true, "handle": "teamId", "value": null } ], "outputs": [ { "description": "The search query Slack executed.", "jsonSchema": { "type": "string", "description": "The search query Slack executed." }, "nullable": false, "handle": "query" }, { "description": "The matching Slack messages.", "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "matchId": { "type": "string", "description": "Slack's search result item identifier." }, "channelId": { "type": "string", "description": "The conversation identifier containing the message." }, "channelName": { "type": [ "string", "null" ], "description": "The conversation name when Slack returns one." }, "ts": { "type": "string", "description": "The message timestamp identifier." }, "userId": { "type": "string", "description": "The user ID of the message author." }, "username": { "type": "string", "description": "The username of the message author when Slack returns one." }, "text": { "type": "string", "description": "The matching message text." }, "permalink": { "type": "string", "description": "A Slack permalink for the matching message." }, "teamId": { "type": "string", "description": "The Slack team ID returned for the match." }, "type": { "type": "string", "description": "The Slack result type." }, "raw": { "type": "object", "properties": {}, "additionalProperties": true, "description": "The untouched Slack record, exactly as the Slack API returned it. Present only when includeRaw is true." } }, "additionalProperties": true, "description": "A normalized Slack message search match." }, "description": "The matching Slack messages." }, "nullable": false, "handle": "matches" }, { "description": "The total number of matches Slack reports.", "jsonSchema": { "type": "integer", "description": "The total number of matches Slack reports." }, "nullable": false, "handle": "total" }, { "description": "Slack pagination metadata when returned.", "jsonSchema": { "type": "object", "properties": {}, "additionalProperties": true, "description": "Slack pagination metadata when returned." }, "nullable": false, "handle": "pagination" }, { "description": "Slack legacy paging metadata when returned.", "jsonSchema": { "type": "object", "properties": {}, "additionalProperties": true, "description": "Slack legacy paging metadata when returned." }, "nullable": false, "handle": "paging" }, { "description": "The cursor for the next page when Slack returns one.", "jsonSchema": { "type": [ "string", "null" ], "description": "The cursor for the next page when Slack returns one." }, "nullable": true, "handle": "nextCursor" } ], "name": "Find latest ten ticket messages", "executor": { "kind": "connector", "action": "slack.search_messages" } } }, "n_424cb71b3f04d8e9": { "inputs": { "matches": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_70a54dbc59bb814b", "output": "matches" } ] } }, "name": "Normalize selected Slack messages", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "matchId": { "type": "string", "description": "Slack's search result item identifier." }, "channelId": { "type": "string", "description": "The conversation identifier containing the message." }, "channelName": { "type": [ "string", "null" ], "description": "The conversation name when Slack returns one." }, "ts": { "type": "string", "description": "The message timestamp identifier." }, "userId": { "type": "string", "description": "The user ID of the message author." }, "username": { "type": "string", "description": "The username of the message author when Slack returns one." }, "text": { "type": "string", "description": "The matching message text." }, "permalink": { "type": "string", "description": "A Slack permalink for the matching message." }, "teamId": { "type": "string", "description": "The Slack team ID returned for the match." }, "type": { "type": "string", "description": "The Slack result type." }, "raw": { "type": "object", "properties": {}, "additionalProperties": true, "description": "The untouched Slack record, exactly as the Slack API returned it. Present only when includeRaw is true." } }, "additionalProperties": true, "description": "A normalized Slack message search match." }, "description": "The matching Slack messages." }, "nullable": false, "handle": "matches" } ], "outputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "messages" } ], "name": "Normalize selected Slack messages", "moduleId": "n_c0b9477c75b55c36", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_2f3b4e31f6082479": { "inputs": { "messages": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_424cb71b3f04d8e9", "output": "messages" } ] }, "previousState": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_2f3b4e31f6082479", "output": "state" } ] }, "issues": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_8120c0fc1c593aee", "output": "issues" } ] }, "pageInfo": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_8120c0fc1c593aee", "output": "page_info" } ] } }, "name": "Collect existing issue hashes", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "messages" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": true, "handle": "previousState" }, { "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string", "description": "issue unique identifier." }, "identifier": { "type": "string", "description": "Issue abbreviation identifier." }, "title": { "type": "string", "description": "issue title." }, "description": { "type": [ "string", "null" ], "description": "issue description." }, "url": { "type": "string", "description": "issue link." }, "createdAt": { "type": "string", "description": "issue creation time." }, "updatedAt": { "type": "string", "description": "issue update time." }, "archivedAt": { "type": [ "string", "null" ], "description": "Issue filing time." }, "dueDate": { "type": [ "string", "null" ], "description": "issue deadline." }, "priority": { "type": [ "number", "null" ], "description": "issue priority." }, "estimate": { "type": [ "number", "null" ], "description": "issue estimate." }, "team": { "type": [ "object", "null" ], "properties": { "id": { "type": "string", "description": "The team's unique identifier." }, "name": { "type": "string", "description": "Team name." }, "key": { "type": "string", "description": "The team shorthand key." } }, "required": [ "id", "name" ], "additionalProperties": false, "description": "Team information." }, "state": { "type": [ "object", "null" ], "properties": { "id": { "type": "string", "description": "The unique identifier of the status." }, "name": { "type": "string", "description": "Status name." }, "type": { "type": "string", "description": "Status type." }, "color": { "type": "string", "description": "Status color." }, "description": { "type": "string", "description": "Status description." } }, "required": [ "id", "name" ], "additionalProperties": false, "description": "Current workflow status." }, "project": { "type": [ "object", "null" ], "properties": { "id": { "type": "string", "description": "The unique identifier of the project." }, "name": { "type": "string", "description": "Project name." }, "slugId": { "type": "string", "description": "Project slug identifier." }, "url": { "type": "string", "description": "Project link." }, "state": { "type": "string", "description": "Project status." }, "health": { "type": [ "string", "null" ], "description": "Project health status." }, "progress": { "type": "number", "description": "Project progress." }, "priority": { "type": "number", "description": "Project priority." }, "priorityLabel": { "type": "string", "description": "Project priority label." }, "scope": { "type": "number", "description": "Project scope value." }, "color": { "type": [ "string", "null" ], "description": "Item color." }, "description": { "type": [ "string", "null" ], "description": "Project description." }, "icon": { "type": [ "string", "null" ], "description": "Project icon." }, "startDate": { "type": [ "string", "null" ], "description": "Project start date." }, "targetDate": { "type": [ "string", "null" ], "description": "Project target date." }, "createdAt": { "type": "string", "description": "Project creation time." }, "updatedAt": { "type": "string", "description": "Project update time." }, "lead": { "type": [ "object", "null" ], "properties": { "id": { "type": "string", "description": "User unique identifier." }, "name": { "type": "string", "description": "User name." }, "displayName": { "type": "string", "description": "User display name." }, "email": { "type": "string", "description": "User email." }, "avatarUrl": { "type": "string", "description": "User avatar address." }, "active": { "type": "boolean", "description": "Whether the user is active." }, "admin": { "type": "boolean", "description": "Whether the user is an administrator." }, "createdAt": { "type": "string", "description": "User creation time." } }, "required": [ "id" ], "additionalProperties": false, "description": "Project leader." }, "creator": { "type": [ "object", "null" ], "properties": { "id": { "type": "string", "description": "User unique identifier." }, "name": { "type": "string", "description": "User name." }, "displayName": { "type": "string", "description": "User display name." }, "email": { "type": "string", "description": "User email." }, "avatarUrl": { "type": "string", "description": "User avatar address." }, "active": { "type": "boolean", "description": "Whether the user is active." }, "admin": { "type": "boolean", "description": "Whether the user is an administrator." }, "createdAt": { "type": "string", "description": "User creation time." } }, "required": [ "id" ], "additionalProperties": false, "description": "Project creator." }, "status": { "type": [ "object", "null" ], "properties": { "id": { "type": "string", "description": "Unique identifier of project status." }, "name": { "type": "string", "description": "Project status name." }, "type": { "type": "string", "description": "Project status type." }, "color": { "type": "string", "description": "Project status color." }, "description": { "type": "string", "description": "Project status description." } }, "required": [ "id", "name" ], "additionalProperties": false, "description": "Project status object." } }, "required": [ "id", "name" ], "additionalProperties": false, "description": "Project information." }, "assignee": { "type": [ "object", "null" ], "properties": { "id": { "type": "string", "description": "User unique identifier." }, "name": { "type": "string", "description": "User name." }, "displayName": { "type": "string", "description": "User display name." }, "email": { "type": "string", "description": "User email." }, "avatarUrl": { "type": "string", "description": "User avatar address." }, "active": { "type": "boolean", "description": "Whether the user is active." }, "admin": { "type": "boolean", "description": "Whether the user is an administrator." }, "createdAt": { "type": "string", "description": "User creation time." } }, "required": [ "id" ], "additionalProperties": false, "description": "Current person in charge information." }, "labels": { "type": "object", "properties": { "nodes": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string", "description": "Tag unique identifier." }, "name": { "type": "string", "description": "Tag name." }, "color": { "type": "string", "description": "Label color." }, "description": { "type": "string", "description": "Tag description." }, "is_group": { "type": "boolean", "description": "Whether to group labels." }, "isGroup": { "type": "boolean", "description": "Whether to group labels." }, "parent": { "type": [ "object", "null" ], "properties": { "id": { "type": "string", "description": "The unique identifier of the parent label group." }, "name": { "type": "string", "description": "The name of the parent label group." } }, "required": [ "id", "name" ], "additionalProperties": false, "description": "Parent label group information." } }, "required": [ "id", "name" ], "additionalProperties": false, "description": "Linear label object." }, "description": "tag list." } }, "required": [ "nodes" ], "additionalProperties": false, "description": "issue tag connection." } }, "required": [ "id", "identifier", "title", "createdAt", "updatedAt" ], "additionalProperties": false, "description": "Linear issue summary object." }, "description": "issue list." }, "nullable": true, "handle": "issues" }, { "jsonSchema": { "type": "object", "properties": { "startCursor": { "type": [ "string", "null" ], "description": "The starting point of the cursor on the previous page." }, "endCursor": { "type": [ "string", "null" ], "description": "The end cursor for the next page." }, "hasPreviousPage": { "type": "boolean", "description": "Whether there is a previous page." }, "hasNextPage": { "type": "boolean", "description": "Whether the next page exists." } }, "required": [ "endCursor", "hasNextPage" ], "additionalProperties": false, "description": "Pagination information." }, "nullable": true, "handle": "pageInfo" } ], "outputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "more" }, { "jsonSchema": { "type": "string" }, "nullable": true, "handle": "after" }, { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "selected" } ], "name": "Collect existing issue hashes", "moduleId": "n_d28fade608f85e3a", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_a1c97941668ccb54": { "inputs": {}, "name": "Another issue page or scan", "kind": "condition", "matchMode": "first", "cases": [ { "output": "more", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_2f3b4e31f6082479", "output": "more" } }, "operator": "==", "right": { "kind": "value", "value": true } } ] } ] } ] }, "n_8120c0fc1c593aee": { "inputs": { "first": { "kind": "value", "value": 50 }, "after": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_2f3b4e31f6082479", "output": "after" } ] } }, "name": "Read one existing issue page", "kind": "task", "task": { "inputs": [ { "description": "Next page cursor.", "jsonSchema": { "type": "string", "description": "Next page cursor." }, "nullable": true, "handle": "after", "value": null }, { "description": "Return quantity per page.", "jsonSchema": { "type": "number", "description": "Return quantity per page." }, "nullable": true, "handle": "first", "value": null }, { "description": "Only issues updated at or after this ISO 8601 timestamp.", "jsonSchema": { "type": "string", "format": "date-time", "description": "Only issues updated at or after this ISO 8601 timestamp." }, "nullable": true, "handle": "updated_after", "value": null }, { "description": "Include archived issues.", "jsonSchema": { "type": "boolean", "description": "Include archived issues." }, "nullable": true, "handle": "include_archived", "value": null }, { "description": "The field used to order results.", "jsonSchema": { "type": "string", "enum": [ "createdAt", "updatedAt" ], "description": "The field used to order results." }, "nullable": true, "handle": "order_by", "value": null }, { "description": "The unique identifier of the project used for filtering.", "jsonSchema": { "type": "string", "description": "The unique identifier of the project used for filtering." }, "nullable": true, "handle": "project_id", "value": null }, { "description": "The unique identifier of the person responsible for filtering.", "jsonSchema": { "type": "string", "description": "The unique identifier of the person responsible for filtering." }, "nullable": true, "handle": "assignee_id", "value": null } ], "outputs": [ { "description": "issue list.", "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string", "description": "issue unique identifier." }, "identifier": { "type": "string", "description": "Issue abbreviation identifier." }, "title": { "type": "string", "description": "issue title." }, "description": { "type": [ "string", "null" ], "description": "issue description." }, "url": { "type": "string", "description": "issue link." }, "createdAt": { "type": "string", "description": "issue creation time." }, "updatedAt": { "type": "string", "description": "issue update time." }, "archivedAt": { "type": [ "string", "null" ], "description": "Issue filing time." }, "dueDate": { "type": [ "string", "null" ], "description": "issue deadline." }, "priority": { "type": [ "number", "null" ], "description": "issue priority." }, "estimate": { "type": [ "number", "null" ], "description": "issue estimate." }, "team": { "type": [ "object", "null" ], "properties": { "id": { "type": "string", "description": "The team's unique identifier." }, "name": { "type": "string", "description": "Team name." }, "key": { "type": "string", "description": "The team shorthand key." } }, "required": [ "id", "name" ], "additionalProperties": false, "description": "Team information." }, "state": { "type": [ "object", "null" ], "properties": { "id": { "type": "string", "description": "The unique identifier of the status." }, "name": { "type": "string", "description": "Status name." }, "type": { "type": "string", "description": "Status type." }, "color": { "type": "string", "description": "Status color." }, "description": { "type": "string", "description": "Status description." } }, "required": [ "id", "name" ], "additionalProperties": false, "description": "Current workflow status." }, "project": { "type": [ "object", "null" ], "properties": { "id": { "type": "string", "description": "The unique identifier of the project." }, "name": { "type": "string", "description": "Project name." }, "slugId": { "type": "string", "description": "Project slug identifier." }, "url": { "type": "string", "description": "Project link." }, "state": { "type": "string", "description": "Project status." }, "health": { "type": [ "string", "null" ], "description": "Project health status." }, "progress": { "type": "number", "description": "Project progress." }, "priority": { "type": "number", "description": "Project priority." }, "priorityLabel": { "type": "string", "description": "Project priority label." }, "scope": { "type": "number", "description": "Project scope value." }, "color": { "type": [ "string", "null" ], "description": "Item color." }, "description": { "type": [ "string", "null" ], "description": "Project description." }, "icon": { "type": [ "string", "null" ], "description": "Project icon." }, "startDate": { "type": [ "string", "null" ], "description": "Project start date." }, "targetDate": { "type": [ "string", "null" ], "description": "Project target date." }, "createdAt": { "type": "string", "description": "Project creation time." }, "updatedAt": { "type": "string", "description": "Project update time." }, "lead": { "type": [ "object", "null" ], "properties": { "id": { "type": "string", "description": "User unique identifier." }, "name": { "type": "string", "description": "User name." }, "displayName": { "type": "string", "description": "User display name." }, "email": { "type": "string", "description": "User email." }, "avatarUrl": { "type": "string", "description": "User avatar address." }, "active": { "type": "boolean", "description": "Whether the user is active." }, "admin": { "type": "boolean", "description": "Whether the user is an administrator." }, "createdAt": { "type": "string", "description": "User creation time." } }, "required": [ "id" ], "additionalProperties": false, "description": "Project leader." }, "creator": { "type": [ "object", "null" ], "properties": { "id": { "type": "string", "description": "User unique identifier." }, "name": { "type": "string", "description": "User name." }, "displayName": { "type": "string", "description": "User display name." }, "email": { "type": "string", "description": "User email." }, "avatarUrl": { "type": "string", "description": "User avatar address." }, "active": { "type": "boolean", "description": "Whether the user is active." }, "admin": { "type": "boolean", "description": "Whether the user is an administrator." }, "createdAt": { "type": "string", "description": "User creation time." } }, "required": [ "id" ], "additionalProperties": false, "description": "Project creator." }, "status": { "type": [ "object", "null" ], "properties": { "id": { "type": "string", "description": "Unique identifier of project status." }, "name": { "type": "string", "description": "Project status name." }, "type": { "type": "string", "description": "Project status type." }, "color": { "type": "string", "description": "Project status color." }, "description": { "type": "string", "description": "Project status description." } }, "required": [ "id", "name" ], "additionalProperties": false, "description": "Project status object." } }, "required": [ "id", "name" ], "additionalProperties": false, "description": "Project information." }, "assignee": { "type": [ "object", "null" ], "properties": { "id": { "type": "string", "description": "User unique identifier." }, "name": { "type": "string", "description": "User name." }, "displayName": { "type": "string", "description": "User display name." }, "email": { "type": "string", "description": "User email." }, "avatarUrl": { "type": "string", "description": "User avatar address." }, "active": { "type": "boolean", "description": "Whether the user is active." }, "admin": { "type": "boolean", "description": "Whether the user is an administrator." }, "createdAt": { "type": "string", "description": "User creation time." } }, "required": [ "id" ], "additionalProperties": false, "description": "Current person in charge information." }, "labels": { "type": "object", "properties": { "nodes": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string", "description": "Tag unique identifier." }, "name": { "type": "string", "description": "Tag name." }, "color": { "type": "string", "description": "Label color." }, "description": { "type": "string", "description": "Tag description." }, "is_group": { "type": "boolean", "description": "Whether to group labels." }, "isGroup": { "type": "boolean", "description": "Whether to group labels." }, "parent": { "type": [ "object", "null" ], "properties": { "id": { "type": "string", "description": "The unique identifier of the parent label group." }, "name": { "type": "string", "description": "The name of the parent label group." } }, "required": [ "id", "name" ], "additionalProperties": false, "description": "Parent label group information." } }, "required": [ "id", "name" ], "additionalProperties": false, "description": "Linear label object." }, "description": "tag list." } }, "required": [ "nodes" ], "additionalProperties": false, "description": "issue tag connection." } }, "required": [ "id", "identifier", "title", "createdAt", "updatedAt" ], "additionalProperties": false, "description": "Linear issue summary object." }, "description": "issue list." }, "nullable": false, "handle": "issues" }, { "description": "Pagination information.", "jsonSchema": { "type": "object", "properties": { "startCursor": { "type": [ "string", "null" ], "description": "The starting point of the cursor on the previous page." }, "endCursor": { "type": [ "string", "null" ], "description": "The end cursor for the next page." }, "hasPreviousPage": { "type": "boolean", "description": "Whether there is a previous page." }, "hasNextPage": { "type": "boolean", "description": "Whether the next page exists." } }, "required": [ "endCursor", "hasNextPage" ], "additionalProperties": false, "description": "Pagination information." }, "nullable": false, "handle": "page_info" } ], "name": "Read one existing issue page", "executor": { "kind": "connector", "action": "linear.list_linear_issues" } } }, "n_176acfece67d552d": { "inputs": { "selected": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_2f3b4e31f6082479", "output": "selected" } ] }, "previousState": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_176acfece67d552d", "output": "state" } ] }, "response": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_364b7f514633caac", "output": "output" } ] } }, "name": "Collect support drafts", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "selected" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": true, "handle": "previousState" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": true, "handle": "response" } ], "outputs": [ { "jsonSchema": { "type": "object", "properties": { "drafts": { "type": "array", "items": { "type": "object", "additionalProperties": true } } }, "required": [ "drafts" ], "additionalProperties": true }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "more" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "prompt" } ], "name": "Collect support drafts", "moduleId": "n_43fc8043e97d1273", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_246124c13e78ce70": { "inputs": {}, "name": "Another support draft to generate", "kind": "condition", "matchMode": "first", "cases": [ { "output": "more", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_176acfece67d552d", "output": "more" } }, "operator": "==", "right": { "kind": "value", "value": true } } ] } ] } ] }, "n_364b7f514633caac": { "inputs": { "prompt": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_176acfece67d552d", "output": "prompt" } ] } }, "name": "Draft title summary and priority", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "prompt" } ], "outputs": [ { "description": "The final result produced by the Agent.", "jsonSchema": { "type": "object", "properties": { "title": { "type": "string" }, "summary": { "type": "string" }, "ideas": { "type": "array", "items": { "type": "string" }, "maxItems": 3 }, "priority": { "type": "string", "enum": [ "low", "medium", "high", "urgent" ] } }, "required": [ "title", "summary", "ideas", "priority" ], "additionalProperties": false }, "nullable": false, "handle": "output" } ], "name": "Draft title summary and priority", "executor": { "kind": "agent", "code": false, "model": "gpt-5-mini", "prompt": "{{prompt}}", "maxRounds": 1, "tools": [] } } }, "n_14dd7c12b05d616f": { "inputs": { "drafts": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_176acfece67d552d", "output": "state", "field": "drafts" } ] }, "previousState": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_14dd7c12b05d616f", "output": "state" } ] }, "id": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_fcb753eabdff2a48", "output": "id" } ] }, "issue_title": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_fcb753eabdff2a48", "output": "issue_title" } ] }, "identifier": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_fcb753eabdff2a48", "output": "identifier" } ] }, "ticket_url": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_fcb753eabdff2a48", "output": "ticket_url" } ] } }, "name": "Collect confirmed Linear tickets", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "drafts" }, { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": true, "handle": "previousState" }, { "jsonSchema": { "type": "string" }, "nullable": true, "handle": "id" }, { "jsonSchema": { "type": "string" }, "nullable": true, "handle": "issue_title" }, { "jsonSchema": { "type": "string" }, "nullable": true, "handle": "identifier" }, { "jsonSchema": { "type": "string" }, "nullable": true, "handle": "ticket_url" } ], "outputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "state" }, { "jsonSchema": { "type": "boolean" }, "nullable": false, "handle": "more" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "title" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "description" }, { "jsonSchema": { "type": "integer" }, "nullable": false, "handle": "priority" }, { "jsonSchema": { "type": "integer" }, "nullable": false, "handle": "count" }, { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "created" } ], "name": "Collect confirmed Linear tickets", "moduleId": "n_374d3e2240d8e68e", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_ba68babd49a1609c": { "inputs": {}, "name": "Another ticket to create", "kind": "condition", "matchMode": "first", "cases": [ { "output": "more", "groups": [ { "expressions": [ { "left": { "kind": "source", "source": { "kind": "node", "nodeId": "n_14dd7c12b05d616f", "output": "more" } }, "operator": "==", "right": { "kind": "value", "value": true } } ] } ] } ] }, "n_fcb753eabdff2a48": { "inputs": { "team_id": { "kind": "value", "value": "YOUR_LINEAR_TEAM_ID" }, "state_id": { "kind": "value", "value": "YOUR_LINEAR_STATE_ID" }, "title": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_14dd7c12b05d616f", "output": "title" } ] }, "description": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_14dd7c12b05d616f", "output": "description" } ] }, "priority": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_14dd7c12b05d616f", "output": "priority" } ] } }, "name": "Create one Linear support ticket", "kind": "task", "task": { "inputs": [ { "description": "issue title.", "jsonSchema": { "type": "string", "description": "issue title." }, "nullable": false, "handle": "title" }, { "description": "The unique identifier of the target team.", "jsonSchema": { "type": "string", "description": "The unique identifier of the target team." }, "nullable": false, "handle": "team_id" }, { "description": "The unique identifier of the target period.", "jsonSchema": { "type": "string", "description": "The unique identifier of the target period." }, "nullable": true, "handle": "cycle_id", "value": null }, { "description": "issue deadline.", "jsonSchema": { "type": "string", "description": "issue deadline." }, "nullable": true, "handle": "due_date", "value": null }, { "description": "issue estimate.", "jsonSchema": { "type": "number", "description": "issue estimate." }, "nullable": true, "handle": "estimate", "value": null }, { "description": "issue priority.", "jsonSchema": { "type": "number", "description": "issue priority." }, "nullable": true, "handle": "priority", "value": null }, { "description": "Unique identifier of workflow status.", "jsonSchema": { "type": "string", "description": "Unique identifier of workflow status." }, "nullable": true, "handle": "state_id", "value": null }, { "description": "List of tag identifiers to bind.", "jsonSchema": { "type": "array", "items": { "type": "string", "description": "An array item." }, "description": "List of tag identifiers to bind." }, "nullable": true, "handle": "label_ids", "value": null }, { "description": "The parent issue identifier.", "jsonSchema": { "type": "string", "description": "The parent issue identifier." }, "nullable": true, "handle": "parent_id", "value": null }, { "description": "The unique identifier of the project.", "jsonSchema": { "type": "string", "description": "The unique identifier of the project." }, "nullable": true, "handle": "project_id", "value": null }, { "description": "The unique identifier of the person in charge.", "jsonSchema": { "type": "string", "description": "The unique identifier of the person in charge." }, "nullable": true, "handle": "assignee_id", "value": null }, { "description": "Issue description, supports Markdown.", "jsonSchema": { "type": "string", "description": "Issue description, supports Markdown." }, "nullable": true, "handle": "description", "value": null } ], "outputs": [ { "description": "The unique identifier of the issue after creation.", "jsonSchema": { "type": "string", "description": "The unique identifier of the issue after creation." }, "nullable": false, "handle": "id" }, { "description": "The abbreviated identifier of the issue after creation.", "jsonSchema": { "type": "string", "description": "The abbreviated identifier of the issue after creation." }, "nullable": true, "handle": "identifier" }, { "description": "The issue title after creation.", "jsonSchema": { "type": "string", "description": "The issue title after creation." }, "nullable": false, "handle": "issue_title" }, { "description": "Issue description after creation.", "jsonSchema": { "type": [ "string", "null" ], "description": "Issue description after creation." }, "nullable": true, "handle": "issue_description" }, { "description": "Issue link after creation.", "jsonSchema": { "type": "string", "description": "Issue link after creation." }, "nullable": true, "handle": "ticket_url" } ], "name": "Create one Linear support ticket", "executor": { "kind": "connector", "action": "linear.create_linear_issue" } } }, "n_6f9f84afc1ce6af3": { "name": "Created support tickets", "kind": "outputs", "outputDefs": [ { "jsonSchema": { "type": "integer" }, "nullable": false, "handle": "count" }, { "jsonSchema": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "nullable": false, "handle": "created" } ], "outputMappings": { "count": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_14dd7c12b05d616f", "output": "count" } ] }, "created": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_14dd7c12b05d616f", "output": "created" } ] } } } }, "edges": [ { "source": "n_9a578baa6a08257f", "target": "n_70a54dbc59bb814b" }, { "source": "n_7a7c405fa3ca6a8f", "target": "n_70a54dbc59bb814b" }, { "source": "n_70a54dbc59bb814b", "target": "n_424cb71b3f04d8e9" }, { "source": "n_424cb71b3f04d8e9", "target": "n_2f3b4e31f6082479" }, { "source": "n_2f3b4e31f6082479", "target": "n_a1c97941668ccb54" }, { "source": "n_a1c97941668ccb54", "target": "n_8120c0fc1c593aee", "sourceHandle": "more" }, { "source": "n_8120c0fc1c593aee", "target": "n_2f3b4e31f6082479" }, { "source": "n_a1c97941668ccb54", "target": "n_176acfece67d552d", "sourceHandle": "otherwise" }, { "source": "n_176acfece67d552d", "target": "n_246124c13e78ce70" }, { "source": "n_246124c13e78ce70", "target": "n_364b7f514633caac", "sourceHandle": "more" }, { "source": "n_364b7f514633caac", "target": "n_176acfece67d552d" }, { "source": "n_246124c13e78ce70", "target": "n_14dd7c12b05d616f", "sourceHandle": "otherwise" }, { "source": "n_14dd7c12b05d616f", "target": "n_ba68babd49a1609c" }, { "source": "n_ba68babd49a1609c", "target": "n_fcb753eabdff2a48", "sourceHandle": "more" }, { "source": "n_fcb753eabdff2a48", "target": "n_14dd7c12b05d616f" }, { "source": "n_ba68babd49a1609c", "target": "n_6f9f84afc1ce6af3", "sourceHandle": "otherwise" } ] } }, "modules": { "n_c0b9477c75b55c36": { "name": "Normalize selected Slack messages", "imports": [], "source": "export function normalizeSlackTickets(matches){\n if(!Array.isArray(matches)||matches.length>10)throw new Error('Slack search must return at most10 source messages.');\n const seen=new Set();\n return matches.map(m=>{const permalink=m.permalink,channel=m.channelName??m.channel?.name,username=m.username??'',userId=m.userId??m.user;\n if(typeof permalink!=='string'||!/^https:\\/\\/[^/]+\\/archives\\/[^/]+\\/p\\d+$/.test(permalink)||typeof m.text!=='string'||typeof m.ts!=='string'||typeof channel!=='string'||typeof userId!=='string')throw new Error('Slack message metadata is incomplete.');\n const id='#'+permalink.split('/').pop();if(seen.has(id))throw new Error('Duplicate Slack message hash.');seen.add(id);\n return {id,type:m.type??'message',channel,user:username+' ('+userId+')',ts:m.ts,permalink,message:m.text};});\n}\nexport function ticketHashes(issues){const hashes=[];for(const issue of issues){if(issue.description==null)continue;if(typeof issue.description!=='string')throw new Error('Linear description is not text.');const match=issue.description.match(/hash\\:\\s([\\w#]+)/i);if(match&&!hashes.includes(match[1]))hashes.push(match[1]);}return hashes;}\nexport default function prepareSlackTickets({matches}){return {messages:normalizeSlackTickets(matches)};}\n" }, "n_d28fade608f85e3a": { "name": "Collect existing issue hashes", "imports": [], "source": "export default function collectTicketReads({messages,previousState,issues,pageInfo}){\n let state=previousState===null?{scan:0,count:0,after:null,cursors:[],hashes:[]}:JSON.parse(JSON.stringify(previousState));\n if(previousState!==null){if(!Array.isArray(issues)||issues.length>50||!pageInfo||typeof pageInfo.hasNextPage!=='boolean')throw new Error('Invalid Linear issue page.');\n for(const issue of issues){if(issue.description==null)continue;if(typeof issue.description!=='string')throw new Error('Linear description is not text.');const m=issue.description.match(/hash\\:\\s([\\w#]+)/i);if(m&&!state.hashes.includes(m[1]))state.hashes.push(m[1]);}\n state.count+=issues.length;\n if(state.count>=50||!pageInfo.hasNextPage){state.scan++;state.count=0;state.after=null;state.cursors=[];}\n else{const cursor=pageInfo.endCursor;if(typeof cursor!=='string'||!cursor||state.cursors.includes(cursor))throw new Error('Linear pagination failed to advance.');state.after=cursor;state.cursors.push(cursor);}\n }\n const selected=state.scan===messages.length?messages.filter(m=>!state.hashes.includes(m.id)):[];\n return {state,more:state.scan!['title','summary','ideas','priority'].includes(k)))throw new Error('Invalid support ticket structure.');for(const k of ['title','summary','priority'])if(typeof output[k]!=='string'||!output[k].trim())throw new Error('Support ticket '+k+' is empty.');if(output.title.trim().split(/\\s+/).length>10||!Array.isArray(output.ideas)||output.ideas.length>3||output.ideas.some(i=>typeof i!=='string'||!i.trim())||!['low','medium','high','urgent'].includes(output.priority))throw new Error('Support ticket constraints were not satisfied.');return output;}\nexport function describeSupportTicket(message,output){return '## '+output.summary+'\\n\\n### Suggestions\\n'+output.ideas.map(i=>'* '+i).join('\\n')+'\\n\\n## Original Message\\n'+message.user+' asks:\\n> '+message.message+'\\n\\n### Metadata\\nchannel: '+message.channel+'\\nts: '+message.ts+'\\npermalink: '+message.permalink+'\\nhash: '+message.id+'\\n';}\nexport default function collectTicketModels({selected,previousState,response}){const state=previousState===null?{index:0,drafts:[]}:JSON.parse(JSON.stringify(previousState));if(previousState!==null){if(state.index>=selected.length)throw new Error('Unexpected ticket generation response.');const output=validateSupportTicket(response);state.drafts.push({message:selected[state.index],output,description:describeSupportTicket(selected[state.index],output),priority:{urgent:1,high:2,medium:3,low:4}[output.priority]});state.index++;}return {state,more:state.index=drafts.length||!response||typeof response.id!=='string'||!response.id||response.issue_title!==drafts[state.index].output.title)throw new Error('Linear did not confirm the expected created ticket.');state.created.push({hash:drafts[state.index].message.id,id:response.id,identifier:response.identifier??null,url:response.ticket_url??null,title:response.issue_title});state.index++;}const next=drafts[state.index];return {state,more:state.index