skills/groeimetai/snow-flow/agent-workspace

agent-workspace

SKILL.md

Agent Workspace for ServiceNow

Agent Workspace provides a modern, configurable interface for fulfiller productivity.

Workspace Architecture

Workspace (sys_aw_workspace)
    ├── Lists (sys_aw_list)
    ├── Forms (sys_aw_form)
    ├── Related Lists
    ├── UI Actions
    └── Contextual Side Panel
        ├── Agent Assist
        ├── Related Records
        └── Activity Stream

Key Tables

Table Purpose
sys_aw_workspace Workspace definitions
sys_aw_list List configurations
sys_aw_form Form configurations
sys_aw_related_list Related list configs
sys_aw_action Workspace UI actions

Workspace Configuration (ES5)

Create Workspace

// Create workspace (ES5 ONLY!)
var workspace = new GlideRecord("sys_aw_workspace")
workspace.initialize()

// Basic info
workspace.setValue("name", "IT Service Desk Workspace")
workspace.setValue("title", "IT Service Desk")
workspace.setValue("description", "Workspace for IT service desk agents")

// Primary table
workspace.setValue("primary_table", "incident")

// URL path
workspace.setValue("url", "it-service-desk")

// Icon and branding
workspace.setValue("icon", "support")
workspace.setValue("color", "#0056B3")

// Default list
workspace.setValue("default_list", getListConfig("incident_active"))

// Enable features
workspace.setValue("agent_assist_enabled", true)
workspace.setValue("contextual_side_panel_enabled", true)
workspace.setValue("activity_stream_enabled", true)

workspace.insert()

Workspace List Configuration

// Create list configuration (ES5 ONLY!)
var list = new GlideRecord("sys_aw_list")
list.initialize()

list.setValue("name", "My Active Incidents")
list.setValue("table", "incident")
list.setValue("workspace", workspaceSysId)

// Filter
list.setValue("filter", "active=true^assigned_to=javascript:gs.getUserID()")

// Columns
list.setValue("columns", "number,short_description,priority,state,caller_id,opened_at")

// Sort
list.setValue("order_by", "priority")
list.setValue("order_by_desc", false)

// Row actions
list.setValue("show_row_actions", true)

// Grouping (optional)
list.setValue("group_by", "priority")

list.insert()

Workspace Form Configuration

// Create form configuration (ES5 ONLY!)
var form = new GlideRecord("sys_aw_form")
form.initialize()

form.setValue("name", "Incident Form")
form.setValue("table", "incident")
form.setValue("workspace", workspaceSysId)

// Form sections
var sections = [
  {
    name: "Details",
    columns: 2,
    fields: ["number", "state", "caller_id", "opened_at", "short_description", "priority"],
  },
  {
    name: "Assignment",
    columns: 2,
    fields: ["assignment_group", "assigned_to", "escalation"],
  },
  {
    name: "Resolution",
    columns: 1,
    fields: ["resolution_code", "close_notes"],
    condition: "state=6^ORstate=7", // Only show for resolved/closed
  },
]

form.setValue("sections", JSON.stringify(sections))

// Related lists
form.setValue("related_lists", "incident.task_sla,incident.sys_attachment")

// Enable Agent Assist
form.setValue("agent_assist_enabled", true)

form.insert()

Contextual Side Panel (ES5)

Configure Side Panel

// Side panel configuration (ES5 ONLY!)
var panel = new GlideRecord("sys_aw_contextual_side_panel")
panel.initialize()

panel.setValue("workspace", workspaceSysId)
panel.setValue("table", "incident")
panel.setValue("name", "Incident Context")

// Tabs
var tabs = [
  {
    id: "agent_assist",
    label: "Agent Assist",
    icon: "lightbulb-outline",
    component: "agent-assist",
  },
  {
    id: "caller_info",
    label: "Caller Info",
    icon: "user",
    component: "custom-caller-info",
  },
  {
    id: "related",
    label: "Related Records",
    icon: "link",
    component: "related-records",
  },
  {
    id: "activity",
    label: "Activity",
    icon: "history",
    component: "activity-stream",
  },
]

panel.setValue("tabs", JSON.stringify(tabs))
panel.setValue("default_tab", "agent_assist")

panel.insert()

Custom Panel Component (ES5)

// Widget for side panel (ES5 ONLY!)
// Server Script
;(function () {
  // Get current record from context
  var recordSysId = input.sys_id
  var tableName = input.table

  if (tableName === "incident" && recordSysId) {
    var gr = new GlideRecord("incident")
    if (gr.get(recordSysId)) {
      // Get caller information
      data.caller = {
        name: gr.caller_id.getDisplayValue(),
        email: gr.caller_id.email.getDisplayValue(),
        phone: gr.caller_id.phone.getDisplayValue(),
        location: gr.caller_id.location.getDisplayValue(),
        vip: gr.caller_id.vip.getDisplayValue() === "true",
      }

      // Get caller's open incidents
      data.openIncidents = []
      var incidents = new GlideRecord("incident")
      incidents.addQuery("caller_id", gr.getValue("caller_id"))
      incidents.addQuery("active", true)
      incidents.addQuery("sys_id", "!=", recordSysId)
      incidents.orderByDesc("opened_at")
      incidents.setLimit(5)
      incidents.query()

      while (incidents.next()) {
        data.openIncidents.push({
          sys_id: incidents.getUniqueValue(),
          number: incidents.getValue("number"),
          short_description: incidents.getValue("short_description"),
          state: incidents.state.getDisplayValue(),
        })
      }
    }
  }
})()

Agent Assist (ES5)

Configure Agent Assist

// Agent Assist configuration (ES5 ONLY!)
var config = new GlideRecord("sys_aw_agent_assist_config")
config.initialize()

config.setValue("workspace", workspaceSysId)
config.setValue("table", "incident")
config.setValue("name", "Incident Agent Assist")
config.setValue("active", true)

// Recommendations sources
config.setValue("show_knowledge", true)
config.setValue("show_similar_incidents", true)
config.setValue("show_solutions", true)
config.setValue("show_macros", true)

// Knowledge search configuration
config.setValue("knowledge_bases", kbSysIds) // Comma-separated
config.setValue("knowledge_search_fields", "short_description,description")

config.insert()

Similar Records Script (ES5)

// Find similar incidents for Agent Assist (ES5 ONLY!)
var SimilarIncidentFinder = Class.create()
SimilarIncidentFinder.prototype = {
  initialize: function () {},

  /**
   * Find similar resolved incidents
   */
  findSimilar: function (incidentSysId) {
    var current = new GlideRecord("incident")
    if (!current.get(incidentSysId)) {
      return []
    }

    var similar = []
    var keywords = this._extractKeywords(current.getValue("short_description"))

    // Search resolved incidents
    var gr = new GlideRecord("incident")
    gr.addQuery("state", "IN", "6,7") // Resolved or Closed
    gr.addQuery("sys_id", "!=", incidentSysId)

    // Match by category
    if (current.category) {
      gr.addQuery("category", current.getValue("category"))
    }

    // Match by CI
    if (current.cmdb_ci) {
      gr.addOrCondition("cmdb_ci", current.getValue("cmdb_ci"))
    }

    // Keyword matching
    for (var i = 0; i < keywords.length && i < 3; i++) {
      gr.addOrCondition("short_description", "CONTAINS", keywords[i])
    }

    gr.setLimit(10)
    gr.orderByDesc("resolved_at")
    gr.query()

    while (gr.next()) {
      var score = this._calculateSimilarity(current, gr)
      if (score > 0.3) {
        similar.push({
          sys_id: gr.getUniqueValue(),
          number: gr.getValue("number"),
          short_description: gr.getValue("short_description"),
          resolution_code: gr.resolution_code.getDisplayValue(),
          close_notes: gr.getValue("close_notes"),
          score: Math.round(score * 100),
        })
      }
    }

    // Sort by similarity score
    similar.sort(function (a, b) {
      return b.score - a.score
    })

    return similar.slice(0, 5)
  },

  _extractKeywords: function (text) {
    var stopWords = ["the", "is", "at", "which", "on", "a", "an", "and", "or", "not", "to", "for"]
    var words = text.toLowerCase().split(/\s+/)
    var keywords = []

    for (var i = 0; i < words.length; i++) {
      var word = words[i].replace(/[^a-z0-9]/g, "")
      if (word.length > 3 && stopWords.indexOf(word) === -1) {
        keywords.push(word)
      }
    }

    return keywords
  },

  _calculateSimilarity: function (source, target) {
    var score = 0

    // Category match
    if (source.getValue("category") === target.getValue("category")) {
      score += 0.3
    }

    // Subcategory match
    if (source.getValue("subcategory") === target.getValue("subcategory")) {
      score += 0.2
    }

    // CI match
    if (source.getValue("cmdb_ci") === target.getValue("cmdb_ci")) {
      score += 0.3
    }

    // Keyword overlap
    var sourceKeywords = this._extractKeywords(source.getValue("short_description"))
    var targetKeywords = this._extractKeywords(target.getValue("short_description"))
    var overlap = 0

    for (var i = 0; i < sourceKeywords.length; i++) {
      if (targetKeywords.indexOf(sourceKeywords[i]) !== -1) {
        overlap++
      }
    }

    if (sourceKeywords.length > 0) {
      score += 0.2 * (overlap / sourceKeywords.length)
    }

    return score
  },

  type: "SimilarIncidentFinder",
}

Workspace UI Actions (ES5)

Create Workspace Action

// Create workspace-specific UI action (ES5 ONLY!)
var action = new GlideRecord("sys_aw_action")
action.initialize()

action.setValue("name", "Quick Resolve")
action.setValue("label", "Quick Resolve")
action.setValue("workspace", workspaceSysId)
action.setValue("table", "incident")

// Action type
action.setValue("action_type", "form") // form, list, both
action.setValue("order", 100)

// Condition
action.setValue("condition", "current.active == true && current.state != 6")

// Client action (opens modal)
action.setValue(
  "client_script",
  "function onClick() {\n" +
    "    spModal.open({\n" +
    '        title: "Quick Resolve",\n' +
    '        widget: "quick-resolve-modal",\n' +
    "        widgetInput: { table: g_form.getTableName(), sys_id: g_form.getUniqueValue() }\n" +
    "    }).then(function(result) {\n" +
    "        if (result) {\n" +
    '            g_form.setValue("state", 6);\n' +
    '            g_form.setValue("resolution_code", result.code);\n' +
    '            g_form.setValue("close_notes", result.notes);\n' +
    "            g_form.save();\n" +
    "        }\n" +
    "    });\n" +
    "}",
)

// Icon and style
action.setValue("icon", "check-circle")
action.setValue("button_class", "btn-success")

action.insert()

MCP Tool Integration

Available Tools

Tool Purpose
snow_find_artifact Find workspace configs
snow_query_table Query workspace tables
snow_deploy Deploy workspace widgets
snow_execute_script_with_output Test workspace scripts

Example Workflow

// 1. Find workspaces
await snow_query_table({
  table: "sys_aw_workspace",
  query: "active=true",
  fields: "name,title,primary_table,url",
})

// 2. Get list configurations
await snow_query_table({
  table: "sys_aw_list",
  query: "workspace.name=IT Service Desk Workspace",
  fields: "name,table,filter,columns",
})

// 3. Test similar incident finder
await snow_execute_script_with_output({
  script: `
        var finder = new SimilarIncidentFinder();
        var similar = finder.findSimilar('incident_sys_id');
        gs.info('Found: ' + similar.length);
    `,
})

Best Practices

  1. Role-Based - Design for specific roles
  2. Efficient Lists - Optimized filters and columns
  3. Context Panel - Relevant information accessible
  4. Agent Assist - Enable knowledge/similar records
  5. Actions - Streamline common tasks
  6. Performance - Lazy load components
  7. Mobile Ready - Test responsive layouts
  8. ES5 Only - No modern JavaScript syntax
Weekly Installs
49
GitHub Stars
53
First Seen
Jan 22, 2026
Installed on
gemini-cli45
claude-code45
github-copilot44
cursor44
opencode44
codex44