| name | agent-workspace |
| description | Build ServiceNow Agent Workspace configurations — workspaces, lists, forms, contextual side panels, Agent Assist similar-record finders, and workspace-specific UI actions on sys_aw_* tables. |
| license | Apache-2.0 |
| compatibility | Designed for Snow-Code and ServiceNow development |
| metadata | {"author":"serac","version":"1.0.0","category":"servicenow"} |
| tools | ["snow_create_workspace","snow_query_table","snow_artifact_manage","snow_execute_script"] |
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
var workspace = new GlideRecord("sys_aw_workspace")
workspace.initialize()
workspace.setValue("name", "IT Service Desk Workspace")
workspace.setValue("title", "IT Service Desk")
workspace.setValue("description", "Workspace for IT service desk agents")
workspace.setValue("primary_table", "incident")
workspace.setValue("url", "it-service-desk")
workspace.setValue("icon", "support")
workspace.setValue("color", "#0056B3")
workspace.setValue("default_list", getListConfig("incident_active"))
workspace.setValue("agent_assist_enabled", true)
workspace.setValue("contextual_side_panel_enabled", true)
workspace.setValue("activity_stream_enabled", true)
workspace.insert()
Workspace List Configuration
var list = new GlideRecord("sys_aw_list")
list.initialize()
list.setValue("name", "My Active Incidents")
list.setValue("table", "incident")
list.setValue("workspace", workspaceSysId)
list.setValue("filter", "active=true^assigned_to=javascript:gs.getUserID()")
list.setValue("columns", "number,short_description,priority,state,caller_id,opened_at")
list.setValue("order_by", "priority")
list.setValue("order_by_desc", false)
list.setValue("show_row_actions", true)
list.setValue("group_by", "priority")
list.insert()
Workspace Form Configuration
var form = new GlideRecord("sys_aw_form")
form.initialize()
form.setValue("name", "Incident Form")
form.setValue("table", "incident")
form.setValue("workspace", workspaceSysId)
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",
},
]
form.setValue("sections", JSON.stringify(sections))
form.setValue("related_lists", "incident.task_sla,incident.sys_attachment")
form.setValue("agent_assist_enabled", true)
form.insert()
Contextual Side Panel (ES5)
Configure Side Panel
var panel = new GlideRecord("sys_aw_contextual_side_panel")
panel.initialize()
panel.setValue("workspace", workspaceSysId)
panel.setValue("table", "incident")
panel.setValue("name", "Incident Context")
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)
;(function () {
var recordSysId = input.sys_id
var tableName = input.table
if (tableName === "incident" && recordSysId) {
var gr = new GlideRecord("incident")
if (gr.get(recordSysId)) {
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",
}
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
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)
config.setValue("show_knowledge", true)
config.setValue("show_similar_incidents", true)
config.setValue("show_solutions", true)
config.setValue("show_macros", true)
config.setValue("knowledge_bases", kbSysIds)
config.setValue("knowledge_search_fields", "short_description,description")
config.insert()
Similar Records Script (ES5)
var SimilarIncidentFinder = Class.create()
SimilarIncidentFinder.prototype = {
initialize: function () {},
findSimilar: function (incidentSysId) {
var current = new GlideRecord("incident")
if (!current.get(incidentSysId)) {
return []
}
var similar = []
var keywords = this._extractKeywords(current.getValue("short_description"))
var gr = new GlideRecord("incident")
gr.addQuery("state", "IN", "6,7")
gr.addQuery("sys_id", "!=", incidentSysId)
if (current.category) {
gr.addQuery("category", current.getValue("category"))
}
if (current.cmdb_ci) {
gr.addOrCondition("cmdb_ci", current.getValue("cmdb_ci"))
}
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),
})
}
}
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
if (source.getValue("category") === target.getValue("category")) {
score += 0.3
}
if (source.getValue("subcategory") === target.getValue("subcategory")) {
score += 0.2
}
if (source.getValue("cmdb_ci") === target.getValue("cmdb_ci")) {
score += 0.3
}
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
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.setValue("action_type", "form")
action.setValue("order", 100)
action.setValue("condition", "current.active == true && current.state != 6")
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" +
"}",
)
action.setValue("icon", "check-circle")
action.setValue("button_class", "btn-success")
action.insert()
MCP Tool Integration
Available Tools
| Tool | Purpose |
|---|
snow_artifact_manage | Find workspace widgets (action: "find") |
snow_query_table | Query workspace tables |
snow_artifact_manage | Deploy workspace widgets (action: "create") |
snow_execute_script | Test workspace scripts |
Example Workflow
await snow_query_table({
table: "sys_aw_workspace",
query: "active=true",
fields: "name,title,primary_table,url",
})
await snow_query_table({
table: "sys_aw_list",
query: "workspace.name=IT Service Desk Workspace",
fields: "name,table,filter,columns",
})
await snow_execute_script({
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