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
- Role-Based - Design for specific roles
- Efficient Lists - Optimized filters and columns
- Context Panel - Relevant information accessible
- Agent Assist - Enable knowledge/similar records
- Actions - Streamline common tasks
- Performance - Lazy load components
- Mobile Ready - Test responsive layouts
- ES5 Only - No modern JavaScript syntax
Weekly Installs
49
Repository
groeimetai/snow-flowGitHub Stars
53
First Seen
Jan 22, 2026
Security Audits
Installed on
gemini-cli45
claude-code45
github-copilot44
cursor44
opencode44
codex44