rhombus-node-mcp 0.1.56 → 0.1.57
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/access-control-tool-api.js +231 -32
- package/dist/api/alarm-monitoring-tool-api.js +50 -1
- package/dist/api/automated-prompts-tool-api.js +9 -17
- package/dist/api/camera-policy-tool-api.js +122 -0
- package/dist/api/camera-tool-api.js +3 -5
- package/dist/api/camera-uptime-tool-api.js +62 -25
- package/dist/api/clips-tool-api.js +45 -9
- package/dist/api/create-tool-api.js +55 -7
- package/dist/api/door-tool-api.js +74 -13
- package/dist/api/entity-lookup-tool-api.js +8 -1
- package/dist/api/events-tool-api.js +79 -6
- package/dist/api/faces-tool-api.js +112 -7
- package/dist/api/get-accessible-apps.js +68 -6
- package/dist/api/get-entity-tool-api.js +12 -12
- package/dist/api/get-org-information-tool-api.js +2 -2
- package/dist/api/guest-management-tool-api.js +75 -1
- package/dist/api/location-tool-api.js +120 -28
- package/dist/api/lpr-tool-api.js +71 -16
- package/dist/api/policy-alerts-tool-api.js +7 -3
- package/dist/api/report-tool-api.js +4 -1
- package/dist/api/rules-tool-api.js +6 -16
- package/dist/api/schedule-tool-api.js +107 -0
- package/dist/api/search-tool-api.js +5 -13
- package/dist/api/update-tool-api.js +96 -1
- package/dist/api/user-tool-api.js +58 -13
- package/dist/createServer.js +18 -6
- package/dist/filtering-utils.js +421 -29
- package/dist/index.js +1 -1
- package/dist/network/network.js +122 -10
- package/dist/network/org-reference-cache.js +74 -0
- package/dist/telemetry/tracingProxy.js +4 -0
- package/dist/tools/entity-lookup-tool.js +6 -4
- package/dist/tools/get-entity-tool.js +40 -20
- package/dist/tools/get-org-information-tool.js +30 -2
- package/dist/tools/time-conversion-tool.js +59 -36
- package/dist/tools/time-tool.js +12 -3
- package/dist/tools/user-tool.js +100 -2
- package/dist/tools-console/access-anomaly-tool.js +2 -19
- package/dist/tools-console/access-control-tool.js +328 -2
- package/dist/tools-console/alarm-monitoring-tool.js +72 -2
- package/dist/tools-console/analytics-tool.js +2 -2
- package/dist/tools-console/badge-timeline-tool.js +2 -21
- package/dist/tools-console/camera-policy-tool.js +252 -0
- package/dist/tools-console/camera-tool.js +111 -89
- package/dist/tools-console/clips-tool.js +21 -31
- package/dist/tools-console/count-tool.js +8 -0
- package/dist/tools-console/create-camera-policy-tool.js +302 -122
- package/dist/tools-console/door-schedule-exception-tool.js +2 -33
- package/dist/tools-console/door-tool.js +130 -2
- package/dist/tools-console/elements-access-anomaly-tool.js +2 -19
- package/dist/tools-console/elements-badge-timeline-tool.js +2 -21
- package/dist/tools-console/elements-lost-badge-tool.js +2 -17
- package/dist/tools-console/elements-tool.js +2 -29
- package/dist/tools-console/events-tool.js +22 -101
- package/dist/tools-console/faces-tool.js +254 -28
- package/dist/tools-console/guest-management-tool.js +127 -2
- package/dist/tools-console/location-tool.js +156 -35
- package/dist/tools-console/lost-badge-tool.js +2 -17
- package/dist/tools-console/lpr-tool.js +89 -23
- package/dist/tools-console/netbox-access-anomaly-tool.js +2 -19
- package/dist/tools-console/netbox-badge-timeline-tool.js +2 -21
- package/dist/tools-console/netbox-lost-badge-tool.js +2 -17
- package/dist/tools-console/netbox-tool.js +2 -29
- package/dist/tools-console/onguard-tool.js +2 -30
- package/dist/tools-console/person-tracking-tool.js +2 -2
- package/dist/tools-console/policy-alerts-tool.js +5 -29
- package/dist/tools-console/reboot-cameras-tool.js +17 -2
- package/dist/tools-console/report-tool.js +10 -40
- package/dist/tools-console/schedule-tool.js +190 -0
- package/dist/tools-console/update-tool.js +298 -168
- package/dist/tools-console/video-walls-tool.js +93 -21
- package/dist/types/access-anomaly-tool-types.js +22 -2
- package/dist/types/access-control-tool-types.js +116 -1
- package/dist/types/alarm-monitoring-tool-types.js +42 -1
- package/dist/types/badge-timeline-tool-types.js +18 -3
- package/dist/types/camera-policy-tool-types.js +104 -0
- package/dist/types/camera-tool-types.js +80 -1
- package/dist/types/clips-tool-types.js +27 -3
- package/dist/types/create-camera-policy-tool-types.js +275 -26
- package/dist/types/door-schedule-exception-tool-types.js +20 -6
- package/dist/types/door-tool-types.js +36 -4
- package/dist/types/entity-lookup-tool-types.js +6 -1
- package/dist/types/events-tools-types.js +24 -16
- package/dist/types/faces-tools-types.js +106 -3
- package/dist/types/get-entity-tool-types.js +76 -12
- package/dist/types/guest-management-tool-types.js +103 -1
- package/dist/types/location-tool-types.js +100 -14
- package/dist/types/lost-badge-tool-types.js +17 -2
- package/dist/types/lpr-tool-types.js +47 -8
- package/dist/types/onguard-tool-types.js +21 -1
- package/dist/types/policy-alerts-tool-types.js +1 -1
- package/dist/types/report-tool-types.js +29 -1
- package/dist/types/schedule-tool-types.js +370 -0
- package/dist/types/schema.js +479 -9
- package/dist/types/time-conversion-tool-types.js +3 -4
- package/dist/types/update-tool-types.js +40 -18
- package/dist/types/user-tool-types.js +44 -1
- package/dist/types/video-walls-tool-types.js +30 -2
- package/dist/types/zod-schemas.js +5128 -996
- package/dist/util.js +62 -3
- package/dist/utils/entity-name-match.js +11 -0
- package/package.json +3 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getDoorControllerRules, createDoorControllerRule, getDoorPolicies, createDoorPolicy, } from "../api/door-tool-api.js";
|
|
1
|
+
import { getDoorControllerRules, createDoorControllerRule, getDoorControllerRule, updateDoorControllerRule, deleteDoorControllerRule, getDoorPolicies, createDoorPolicy, updateDoorPolicy, deleteDoorPolicy, } from "../api/door-tool-api.js";
|
|
2
2
|
import { DoorToolRequestType, OUTPUT_SCHEMA, TOOL_ARGS, } from "../types/door-tool-types.js";
|
|
3
3
|
import { createToolStructuredContent, createToolTextContent, extractFromToolExtra, } from "../util.js";
|
|
4
4
|
const TOOL_NAME = "door-tool";
|
|
@@ -8,8 +8,14 @@ This tool manages Rhombus door controller rules and door policies.
|
|
|
8
8
|
It has the following modes of operation, determined by the "requestType" parameter:
|
|
9
9
|
- ${DoorToolRequestType.GET_DOOR_CONTROLLER_RULES}: Get all rules for a specific door controller. Requires doorControllerUuid.
|
|
10
10
|
- ${DoorToolRequestType.CREATE_DOOR_CONTROLLER_RULE}: Create a new rule for a door controller. Requires doorControllerUuid and ruleConfig (JSON string).
|
|
11
|
+
- ${DoorToolRequestType.UPDATE_DOOR_CONTROLLER_RULE}: Change an existing rule. Requires doorControllerUuid, ruleUuid and ruleConfig; ruleConfig is merged over the rule's current configuration.
|
|
12
|
+
- ${DoorToolRequestType.DELETE_DOOR_CONTROLLER_RULE}: Delete a rule. Requires doorControllerUuid, ruleUuid and confirmDelete.
|
|
11
13
|
- ${DoorToolRequestType.GET_DOOR_POLICIES}: Get all door alert policies in the organization.
|
|
12
14
|
- ${DoorToolRequestType.CREATE_DOOR_POLICY}: Create a new door policy. Requires policyName and policyConfig (JSON string).
|
|
15
|
+
- ${DoorToolRequestType.UPDATE_DOOR_POLICY}: Rename a door policy and/or change its configuration. Requires policyUuid plus policyName and/or policyConfig.
|
|
16
|
+
- ${DoorToolRequestType.DELETE_DOOR_POLICY}: Delete a door policy. Requires policyUuid and confirmDelete.
|
|
17
|
+
|
|
18
|
+
Door rules and policies govern physical access. Deleting or disabling one can leave a door permanently unlocked or permanently locked, so state what will change and get explicit confirmation before any update or delete.
|
|
13
19
|
|
|
14
20
|
Use the get-entity-tool to find door controller and access controlled door UUIDs.
|
|
15
21
|
`;
|
|
@@ -47,6 +53,128 @@ const TOOL_HANDLER = async (args, _extra) => {
|
|
|
47
53
|
const createdPolicy = await createDoorPolicy(config, requestModifiers, sessionId);
|
|
48
54
|
return createToolStructuredContent({ createdPolicy });
|
|
49
55
|
}
|
|
56
|
+
case DoorToolRequestType.UPDATE_DOOR_POLICY: {
|
|
57
|
+
if (!args.policyUuid) {
|
|
58
|
+
return createToolTextContent("policyUuid is required for update-door-policy.");
|
|
59
|
+
}
|
|
60
|
+
if (!args.policyName?.trim() && !args.policyConfig) {
|
|
61
|
+
return createToolTextContent("update-door-policy needs at least one of policyName or policyConfig — both were empty, so there is nothing to change.");
|
|
62
|
+
}
|
|
63
|
+
// Parse before reading anything, so malformed JSON costs no calls and is
|
|
64
|
+
// unambiguously safe to retry.
|
|
65
|
+
let configPatch = {};
|
|
66
|
+
if (args.policyConfig) {
|
|
67
|
+
try {
|
|
68
|
+
const parsed = JSON.parse(args.policyConfig);
|
|
69
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
70
|
+
return createToolTextContent(`RETRYABLE — nothing was changed. policyConfig must be a JSON object describing the door policy; received ${Array.isArray(parsed) ? "an array" : typeof parsed}.`);
|
|
71
|
+
}
|
|
72
|
+
configPatch = parsed;
|
|
73
|
+
}
|
|
74
|
+
catch (parseError) {
|
|
75
|
+
return createToolTextContent(`RETRYABLE — nothing was changed. policyConfig is not valid JSON (${parseError instanceof Error ? parseError.message : "parse error"}).`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// updateDoorPolicy REPLACES the policy object, so merge over the current
|
|
79
|
+
// one rather than sending the patch alone.
|
|
80
|
+
const policies = await getDoorPolicies(requestModifiers, sessionId);
|
|
81
|
+
const existing = policies.find(policy => policy.uuid === args.policyUuid);
|
|
82
|
+
if (!existing) {
|
|
83
|
+
return createToolTextContent(`No door policy in this organization has the uuid "${args.policyUuid}". Use get-door-policies to see the policies that exist — do not guess a uuid.`);
|
|
84
|
+
}
|
|
85
|
+
const { success, warningMsg } = await updateDoorPolicy({
|
|
86
|
+
...existing,
|
|
87
|
+
...configPatch,
|
|
88
|
+
uuid: args.policyUuid,
|
|
89
|
+
name: args.policyName?.trim() || existing.name,
|
|
90
|
+
}, requestModifiers, sessionId);
|
|
91
|
+
return createToolStructuredContent({
|
|
92
|
+
updated: { success, uuid: args.policyUuid },
|
|
93
|
+
warningMsg,
|
|
94
|
+
note: `Updated the door policy "${args.policyName?.trim() || existing.name || args.policyUuid}". Fields not included in policyConfig kept their previous values. This changes how every door on this policy behaves.`,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
case DoorToolRequestType.DELETE_DOOR_POLICY: {
|
|
98
|
+
if (!args.policyUuid) {
|
|
99
|
+
return createToolTextContent("policyUuid is required for delete-door-policy.");
|
|
100
|
+
}
|
|
101
|
+
const policies = await getDoorPolicies(requestModifiers, sessionId);
|
|
102
|
+
const existing = policies.find(policy => policy.uuid === args.policyUuid);
|
|
103
|
+
if (!existing) {
|
|
104
|
+
return createToolTextContent(`No door policy in this organization has the uuid "${args.policyUuid}". Use get-door-policies to see the policies that exist.`);
|
|
105
|
+
}
|
|
106
|
+
if (!args.confirmDelete) {
|
|
107
|
+
return createToolStructuredContent({
|
|
108
|
+
doorPolicies: [existing],
|
|
109
|
+
note: `NOT DELETED — nothing was changed. Deleting the door policy "${existing.name ?? args.policyUuid}" is irreversible and ` +
|
|
110
|
+
`removes the alerting rules from every door using it. Tell the user what will stop being monitored and get explicit ` +
|
|
111
|
+
`confirmation, then call this tool again with confirmDelete: true.`,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
const { success, uuid, warningMsg } = await deleteDoorPolicy(args.policyUuid, requestModifiers, sessionId);
|
|
115
|
+
return createToolStructuredContent({
|
|
116
|
+
deleted: { success, uuid },
|
|
117
|
+
warningMsg,
|
|
118
|
+
note: `Deleted the door policy "${existing.name ?? args.policyUuid}". Doors that used it no longer have its alerting rules.`,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
case DoorToolRequestType.UPDATE_DOOR_CONTROLLER_RULE: {
|
|
122
|
+
if (!args.doorControllerUuid || !args.ruleUuid) {
|
|
123
|
+
return createToolTextContent("doorControllerUuid and ruleUuid are both required for update-door-controller-rule — a rule is addressed by controller AND rule uuid.");
|
|
124
|
+
}
|
|
125
|
+
if (!args.ruleConfig) {
|
|
126
|
+
return createToolTextContent("ruleConfig is required for update-door-controller-rule.");
|
|
127
|
+
}
|
|
128
|
+
let configPatch;
|
|
129
|
+
try {
|
|
130
|
+
const parsed = JSON.parse(args.ruleConfig);
|
|
131
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
132
|
+
return createToolTextContent(`RETRYABLE — nothing was changed. ruleConfig must be a JSON object describing the rule; received ${Array.isArray(parsed) ? "an array" : typeof parsed}.`);
|
|
133
|
+
}
|
|
134
|
+
configPatch = parsed;
|
|
135
|
+
}
|
|
136
|
+
catch (parseError) {
|
|
137
|
+
return createToolTextContent(`RETRYABLE — nothing was changed. ruleConfig is not valid JSON (${parseError instanceof Error ? parseError.message : "parse error"}).`);
|
|
138
|
+
}
|
|
139
|
+
// updateDoorControllerRule REPLACES the rule, so merge over the current one.
|
|
140
|
+
const existing = await getDoorControllerRule(args.doorControllerUuid, args.ruleUuid, requestModifiers, sessionId);
|
|
141
|
+
if (!existing) {
|
|
142
|
+
return createToolTextContent(`Door controller ${args.doorControllerUuid} has no rule with the uuid "${args.ruleUuid}". Use get-door-controller-rules for that controller to see its rules.`);
|
|
143
|
+
}
|
|
144
|
+
const { success } = await updateDoorControllerRule(args.doorControllerUuid, { ...existing, ...configPatch, uuid: args.ruleUuid }, requestModifiers, sessionId);
|
|
145
|
+
return createToolStructuredContent({
|
|
146
|
+
updated: { success, uuid: args.ruleUuid },
|
|
147
|
+
note: `Updated rule "${existing.name ?? args.ruleUuid}" on door controller ${args.doorControllerUuid}. Fields not included in ruleConfig kept their previous values. This changes the physical behaviour of the door.`,
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
case DoorToolRequestType.DELETE_DOOR_CONTROLLER_RULE: {
|
|
151
|
+
if (!args.doorControllerUuid || !args.ruleUuid) {
|
|
152
|
+
return createToolTextContent("doorControllerUuid and ruleUuid are both required for delete-door-controller-rule.");
|
|
153
|
+
}
|
|
154
|
+
const existing = await getDoorControllerRule(args.doorControllerUuid, args.ruleUuid, requestModifiers, sessionId);
|
|
155
|
+
if (!existing) {
|
|
156
|
+
return createToolTextContent(`Door controller ${args.doorControllerUuid} has no rule with the uuid "${args.ruleUuid}", so there is nothing to delete.`);
|
|
157
|
+
}
|
|
158
|
+
if (!args.confirmDelete) {
|
|
159
|
+
return createToolStructuredContent({
|
|
160
|
+
doorControllerRules: [
|
|
161
|
+
{
|
|
162
|
+
uuid: existing.uuid ?? undefined,
|
|
163
|
+
name: existing.name ?? undefined,
|
|
164
|
+
deviceUuid: args.doorControllerUuid,
|
|
165
|
+
},
|
|
166
|
+
],
|
|
167
|
+
note: `NOT DELETED — nothing was changed. Deleting rule "${existing.name ?? args.ruleUuid}" from door controller ` +
|
|
168
|
+
`${args.doorControllerUuid} is irreversible and changes how that door physically behaves — it can leave the door ` +
|
|
169
|
+
`unlocked or locked outside its schedule. Confirm with the user, then call this tool again with confirmDelete: true.`,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
const { success, uuid } = await deleteDoorControllerRule(args.doorControllerUuid, args.ruleUuid, requestModifiers, sessionId);
|
|
173
|
+
return createToolStructuredContent({
|
|
174
|
+
deleted: { success, uuid },
|
|
175
|
+
note: `Deleted rule "${existing.name ?? args.ruleUuid}" from door controller ${args.doorControllerUuid}.`,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
50
178
|
}
|
|
51
179
|
}
|
|
52
180
|
catch (error) {
|
|
@@ -63,6 +191,6 @@ export function createTool(server) {
|
|
|
63
191
|
description: TOOL_DESCRIPTION,
|
|
64
192
|
inputSchema: TOOL_ARGS,
|
|
65
193
|
outputSchema: OUTPUT_SCHEMA.shape,
|
|
66
|
-
annotations: { readOnlyHint: false, destructiveHint:
|
|
194
|
+
annotations: { readOnlyHint: false, destructiveHint: true },
|
|
67
195
|
}, TOOL_HANDLER);
|
|
68
196
|
}
|
|
@@ -1,26 +1,9 @@
|
|
|
1
1
|
import { getElementsAccessAnomalies } from "../api/elements-access-anomaly-tool-api.js";
|
|
2
2
|
import { OUTPUT_SCHEMA, TOOL_ARGS } from "../types/elements-access-anomaly-tool-types.js";
|
|
3
3
|
import { createToolStructuredContent, extractFromToolExtra } from "../util.js";
|
|
4
|
+
import { buildAccessAnomalyToolDescription } from "../types/access-anomaly-tool-types.js";
|
|
4
5
|
const TOOL_NAME = "elements-access-anomaly-tool";
|
|
5
|
-
const TOOL_DESCRIPTION =
|
|
6
|
-
Scans Honeywell Elements (LenelS2 Elements) access events over a time window and flags anomalous badge activity. Use for
|
|
7
|
-
"find unusual badge activity", "anything suspicious in access this week", or proactive access review.
|
|
8
|
-
|
|
9
|
-
Runs deterministic rules and returns ranked findings (high severity first):
|
|
10
|
-
- lost_or_inactive_badge: a non-active badge (lost/suspended) was used
|
|
11
|
-
- entry_not_made: access granted but no entry made (possible tailgating)
|
|
12
|
-
- off_hours: entry outside business hours (configurable)
|
|
13
|
-
- impossible_travel: one cardholder at two different areas seconds apart
|
|
14
|
-
- area_novelty: a cardholder's first-ever entry to an area vs their prior history (needs a baseline window)
|
|
15
|
-
|
|
16
|
-
Each finding includes cardholderName, the rule, severity, datetime, area, the camera deviceUuid, a plain-language
|
|
17
|
-
rationale, and clip/still hints. Resolve relative times (e.g. "this week") to ISO 8601 first via the timestamp tool.
|
|
18
|
-
|
|
19
|
-
This is a triage aid: present findings grouped by severity, and for the notable ones call the camera-tool
|
|
20
|
-
(requestType "image", cameraUuid = finding.deviceUuid, timestamp = finding.timestampMs) and/or clips-tool
|
|
21
|
-
("createClip" using finding.clipHint) — in PARALLEL — so a human can confirm. Don't assert wrongdoing; surface the
|
|
22
|
-
evidence.
|
|
23
|
-
`;
|
|
6
|
+
const TOOL_DESCRIPTION = buildAccessAnomalyToolDescription("Honeywell Elements (LenelS2 Elements)");
|
|
24
7
|
const TOOL_HANDLER = async (args, _extra) => {
|
|
25
8
|
const { requestModifiers, sessionId } = extractFromToolExtra(_extra);
|
|
26
9
|
try {
|
|
@@ -1,28 +1,9 @@
|
|
|
1
1
|
import { getElementsBadgeTimeline } from "../api/elements-badge-timeline-tool-api.js";
|
|
2
2
|
import { OUTPUT_SCHEMA, TOOL_ARGS } from "../types/elements-badge-timeline-tool-types.js";
|
|
3
3
|
import { createToolStructuredContent, extractFromToolExtra } from "../util.js";
|
|
4
|
+
import { buildBadgeTimelineToolDescription } from "../types/badge-timeline-tool-types.js";
|
|
4
5
|
const TOOL_NAME = "elements-badge-timeline-tool";
|
|
5
|
-
const TOOL_DESCRIPTION =
|
|
6
|
-
Reconstructs one person's movements through a building from their Honeywell Elements (LenelS2 Elements) badge taps.
|
|
7
|
-
Use this for incident reconstruction / "follow the badge" requests, e.g. "reconstruct Eve's movements
|
|
8
|
-
yesterday" or "where did this cardholder go".
|
|
9
|
-
|
|
10
|
-
Returns the cardholder's badge taps in CHRONOLOGICAL order (oldest first), each with:
|
|
11
|
-
- datetime / timestampMs and the area entered
|
|
12
|
-
- deviceUuid: the camera at that door
|
|
13
|
-
- clipHint (camera + start/end window) and stillHint (camera + timestamp)
|
|
14
|
-
- gapToNextSeconds: time until the next tap (a large gap = unobserved movement between doors)
|
|
15
|
-
plus a "path" array summarizing the areas traversed in order.
|
|
16
|
-
|
|
17
|
-
Resolve relative times like "yesterday" to ISO 8601 first (use the timestamp tool), then pass
|
|
18
|
-
startTime/endTime. cardholderQuery is a full-text name match; if "ambiguousCardholders" is returned the
|
|
19
|
-
query matched more than one person — ask the user which one before trusting the timeline.
|
|
20
|
-
|
|
21
|
-
IMPORTANT — to show the movement visually: for each stop (or the key transitions), call the camera-tool
|
|
22
|
-
(requestType "image", cameraUuid = stop.deviceUuid, timestamp = stop.timestampMs) for a still you can see,
|
|
23
|
-
and/or the clips-tool (requestType "createClip", using stop.clipHint) for video. Issue those per-stop
|
|
24
|
-
media calls in PARALLEL, then present the timeline as a chronological narrative.
|
|
25
|
-
`;
|
|
6
|
+
const TOOL_DESCRIPTION = buildBadgeTimelineToolDescription("Honeywell Elements (LenelS2 Elements)");
|
|
26
7
|
const TOOL_HANDLER = async (args, _extra) => {
|
|
27
8
|
const { requestModifiers, sessionId } = extractFromToolExtra(_extra);
|
|
28
9
|
try {
|
|
@@ -1,24 +1,9 @@
|
|
|
1
1
|
import { getElementsLostBadgeResponse } from "../api/elements-lost-badge-tool-api.js";
|
|
2
2
|
import { OUTPUT_SCHEMA, TOOL_ARGS } from "../types/elements-lost-badge-tool-types.js";
|
|
3
3
|
import { createToolStructuredContent, extractFromToolExtra } from "../util.js";
|
|
4
|
+
import { buildLostBadgeToolDescription } from "../types/lost-badge-tool-types.js";
|
|
4
5
|
const TOOL_NAME = "elements-lost-badge-tool";
|
|
5
|
-
const TOOL_DESCRIPTION =
|
|
6
|
-
Lost / stolen-badge live response for Honeywell Elements (LenelS2 Elements) access. Use for "a lost badge was just used —
|
|
7
|
-
who is it and where did they go?", or to review lost/inactive-badge use over a window.
|
|
8
|
-
|
|
9
|
-
For each lost/inactive-badge use it returns:
|
|
10
|
-
- cardholderOfRecord (who the badge belongs to — may NOT be who used it) and badgeStatus
|
|
11
|
-
- the door deviceUuid + time, plus clipHint / stillHint for the door footage
|
|
12
|
-
- facesAtDoor: the face(s) captured at the door at that moment — a recognized name, or UNIDENTIFIED (an unknown
|
|
13
|
-
face on a valid badge is the strongest stolen/shared-badge signal), with a thumbnail
|
|
14
|
-
- sightings: that same face tracked across cameras, ordered in time, ending in lastKnownSighting (last-known location)
|
|
15
|
-
|
|
16
|
-
Resolve relative times (e.g. "in the last hour") to ISO 8601 first via the timestamp tool.
|
|
17
|
-
|
|
18
|
-
To present: show the door still/clip (camera-tool image / clips-tool createClip with the hints), the face at the
|
|
19
|
-
door, and the cross-camera track to last-known location. Detection + door evidence are reliable; the face track
|
|
20
|
-
depends on face-recognition coverage, so treat it as investigative, not proof.
|
|
21
|
-
`;
|
|
6
|
+
const TOOL_DESCRIPTION = buildLostBadgeToolDescription("Honeywell Elements (LenelS2 Elements)");
|
|
22
7
|
const TOOL_HANDLER = async (args, _extra) => {
|
|
23
8
|
const { requestModifiers, sessionId } = extractFromToolExtra(_extra);
|
|
24
9
|
try {
|
|
@@ -1,36 +1,9 @@
|
|
|
1
1
|
import { searchElementsEvents } from "../api/elements-tool-api.js";
|
|
2
2
|
import { OUTPUT_SCHEMA, TOOL_ARGS } from "../types/elements-tool-types.js";
|
|
3
|
+
import { buildBadgeEventsToolDescription } from "../types/onguard-tool-types.js";
|
|
3
4
|
import { createToolStructuredContent, extractFromToolExtra } from "../util.js";
|
|
4
5
|
const TOOL_NAME = "elements-events-tool";
|
|
5
|
-
const TOOL_DESCRIPTION =
|
|
6
|
-
Searches Honeywell Elements (LenelS2 Elements) badge / access-control events for the organization. Use this to answer
|
|
7
|
-
"who entered WHERE and WHEN" questions, e.g. "who entered the back office yesterday".
|
|
8
|
-
|
|
9
|
-
NOTE: an organization may run any combination of Honeywell OnGuard (Lenel), Honeywell Elements (LenelS2
|
|
10
|
-
Elements), and Lenel S2 NetBox badge integrations — each searched by its own sibling tool
|
|
11
|
-
(onguard-events-tool / elements-events-tool / netbox-events-tool), all taking identical arguments and
|
|
12
|
-
returning the same shape. For a general "who badged in / did anyone enter" question you usually do NOT
|
|
13
|
-
know which integration recorded the event, so call ALL THREE sibling tools (in parallel) and combine the
|
|
14
|
-
results — each returns an empty list when its integration isn't configured. Restrict to one vendor only
|
|
15
|
-
when the user explicitly names it.
|
|
16
|
-
|
|
17
|
-
Each returned event includes:
|
|
18
|
-
- cardholderName: the person's name
|
|
19
|
-
- deviceUuid: the camera that saw the event
|
|
20
|
-
- timestampMs / datetime: when it happened
|
|
21
|
-
- label: e.g. "Elements: Badge Authorized" (a grant) or an anomaly label
|
|
22
|
-
- badgeStatus, badgeType, areaEntering, areaExiting, entryMade, isAnomaly
|
|
23
|
-
|
|
24
|
-
Filters (all optional): area, locationUuids, deviceUuids, cardholderQuery, badgeStatus, badgeType,
|
|
25
|
-
anomalyOnly, entryMade, startTime, endTime, limit. Resolve relative times like "yesterday" to ISO 8601
|
|
26
|
-
first (use the timestamp tool), then pass startTime/endTime.
|
|
27
|
-
|
|
28
|
-
IMPORTANT — to show pictures and video of each person so the user can visually identify them: after this
|
|
29
|
-
returns, for each event (or the most relevant ones) call the camera-tool (requestType "image",
|
|
30
|
-
cameraUuid = the event's deviceUuid, timestamp = the event's time) to get a still you can see, and/or the
|
|
31
|
-
clips-tool (requestType "createClip") with a short window around the timestamp for video. Issue those
|
|
32
|
-
per-event media calls in PARALLEL.
|
|
33
|
-
`;
|
|
6
|
+
const TOOL_DESCRIPTION = buildBadgeEventsToolDescription("Honeywell Elements (LenelS2 Elements)", "Elements");
|
|
34
7
|
const TOOL_HANDLER = async (args, _extra) => {
|
|
35
8
|
const { requestModifiers, sessionId } = extractFromToolExtra(_extra);
|
|
36
9
|
try {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getAccessControlEvents, getBrivoAccessControlEvents, getEventsForEnvironmentalGateway, getClimateEventsForSensor, getComponentEventsByLocation, getCameraFootageSeekpointEvents, getButtonPressEvents, getOccupancyEvents, getProximityEvents, getDoorbellEvents, } from "../api/events-tool-api.js";
|
|
1
|
+
import { getAccessControlEvents, getBrivoAccessControlEvents, getEventsForEnvironmentalGateway, getClimateEventsForSensor, getComponentEventsByLocation, describeEmptyComponentEventResult, getCameraFootageSeekpointEvents, getButtonPressEvents, getOccupancyEvents, getProximityEvents, getDoorbellEvents, } from "../api/events-tool-api.js";
|
|
2
2
|
import { EventsToolRequestType, OUTPUT_SCHEMA, TOOL_ARGS, } from "../types/events-tools-types.js";
|
|
3
3
|
import { createToolStructuredContent } from "../util.js";
|
|
4
4
|
import { getLogger } from "../logger.js";
|
|
@@ -6,109 +6,21 @@ import { TempUnit } from "../utils/temp.js";
|
|
|
6
6
|
const logger = getLogger("events-tool");
|
|
7
7
|
const TOOL_NAME = "events-tool";
|
|
8
8
|
// "faces" | "people" | "human" | "access-control"
|
|
9
|
+
//
|
|
10
|
+
// Tool descriptions are billed on EVERY LLM call even while the tool is
|
|
11
|
+
// deferred behind hosted tool_search, so this string carries only what the
|
|
12
|
+
// model needs to CHOOSE this tool. Per-mode arguments, field semantics, and
|
|
13
|
+
// enum catalogs live on the input-parameter descriptions (unbilled until the
|
|
14
|
+
// tool is loaded, still in front of the model when it builds the call).
|
|
15
|
+
// See PERF_MASTER_PLAN P2 #4a.
|
|
9
16
|
const TOOL_DESCRIPTION = `
|
|
10
|
-
**
|
|
17
|
+
**Raw, event-level records** — individual events, each with a timestamp. Modes are set by "eventType": access-control, brivo-access-control, environmental-gateway, climate-sensor, component-events, camera, button-press, occupancy, proximity, doorbell.
|
|
11
18
|
|
|
12
|
-
|
|
19
|
+
Use it when the user asks for specific events: unlocks, badge ins, credentials, arrivals, Brivo access control, door state changes, panic or doorbell button presses, environmental gateway readings, climate data (temperature, humidity, air quality, vape), occupancy counts, proximity tags, or a camera's timeline activity. For maximum flexibility across event types at a location, use eventType "component-events".
|
|
13
20
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
---
|
|
17
|
-
|
|
18
|
-
When eventType is "brivo-access-control":
|
|
19
|
-
|
|
20
|
-
Retrieves badge/credential events from Brivo-integrated doors. Automatically fetches the Brivo integration configuration to determine which locations have Brivo doors mapped. No door UUIDs are required.
|
|
21
|
-
|
|
22
|
-
Use this when the user asks specifically about Brivo events, Brivo badge ins, Brivo access control, or events from Brivo doors.
|
|
23
|
-
|
|
24
|
-
Arguments:
|
|
25
|
-
* **startTime (string):** Start of the time range (ISO 8601).
|
|
26
|
-
* **endTime (string):** End of the time range (ISO 8601).
|
|
27
|
-
|
|
28
|
-
Returns:
|
|
29
|
-
* **integrationEnabled:** Whether the Brivo integration is currently enabled.
|
|
30
|
-
* **brivoDoorsConfigured:** Number of Brivo doors configured in the integration.
|
|
31
|
-
* **brivoDoors:** List of Brivo doors with their IDs, names, and associated Rhombus location UUIDs.
|
|
32
|
-
* **events:** Credential received events from all locations that have Brivo doors configured, sorted newest first.
|
|
33
|
-
|
|
34
|
-
Note: Events are fetched at the location level, so results may include events from all access-controlled doors at locations where Brivo is configured.
|
|
35
|
-
|
|
36
|
-
---
|
|
37
|
-
|
|
38
|
-
When eventType is "access-control":
|
|
39
|
-
|
|
40
|
-
Retrieves access control events (arrivals, badge ins, credentials, unlocks) for the given door(s). Can return a lot of data—use a narrow time range.
|
|
41
|
-
|
|
42
|
-
Arguments:
|
|
43
|
-
* **accessControlledDoorUuids (array of strings):** UUIDs of the access-controlled doors.
|
|
44
|
-
* **startTime (string):** Start of the time range (ISO 8601).
|
|
45
|
-
* **endTime (string):** End of the time range (ISO 8601).
|
|
46
|
-
|
|
47
|
-
The \`credSource\` field indicates how the event was triggered:
|
|
48
|
-
* **REMOTE:** Rhombus Key app remote unlock.
|
|
49
|
-
* **REMOTE (Admin):** Unlock via Rhombus console or browser/mobile app.
|
|
50
|
-
* **BLE_WAVE:** User waved hand over the reader.
|
|
51
|
-
* **NFC:** User tapped badge or phone on the reader.
|
|
52
|
-
|
|
53
|
-
---
|
|
54
|
-
|
|
55
|
-
Retrieves environmental gateway events (sensor readings, derived values) for a device in a time range. Timestamps are in the **device** timezone, not necessarily UTC.
|
|
56
|
-
|
|
57
|
-
Arguments:
|
|
58
|
-
* **deviceUuid (string):** UUID of the environmental gateway device.
|
|
59
|
-
* **startTime (string):** Start of range (ISO 8601).
|
|
60
|
-
* **endTime (string):** End of range (ISO 8601).
|
|
61
|
-
|
|
62
|
-
---
|
|
63
|
-
|
|
64
|
-
When eventType is "climate-sensor":
|
|
65
|
-
|
|
66
|
-
Retrieves climate sensor events (temperature, humidity, air quality, etc.) for a sensor in a time range. Timestamps are in the **sensor** timezone, not necessarily UTC.
|
|
67
|
-
|
|
68
|
-
Arguments:
|
|
69
|
-
* **sensorUuid (string):** UUID of the climate sensor.
|
|
70
|
-
* **startTime (string):** Start of range (ISO 8601).
|
|
71
|
-
* **endTime (string):** End of range (ISO 8601).
|
|
72
|
-
* **limit (number, optional):** Max events to return. Default 1000.
|
|
73
|
-
|
|
74
|
-
---
|
|
75
|
-
|
|
76
|
-
When eventType is "component-events":
|
|
77
|
-
|
|
78
|
-
Retrieves all component event types for a location in a time range. Most flexible option; filter by event type via componentEventTypes. Timestamps are in the **location** timezone, not necessarily UTC.
|
|
79
|
-
|
|
80
|
-
Arguments:
|
|
81
|
-
* **locationUuid (string):** UUID of the location.
|
|
82
|
-
* **componentEventTypes (array, optional):** Event types to include. If empty or omitted, returns all types.
|
|
83
|
-
* **startTime (string):** Start of range (ISO 8601).
|
|
84
|
-
* **endTime (string):** End of range (ISO 8601).
|
|
85
|
-
|
|
86
|
-
Valid event types include:
|
|
87
|
-
* **DoorbellEvent:** Doorbell button press events
|
|
88
|
-
* **CredentialReceivedEvent:** Badge/credential scans (NFC, BLE_WAVE, REMOTE unlocks)
|
|
89
|
-
* **DoorStateChangeEvent:** Door state changes (locked/unlocked)
|
|
90
|
-
* **ButtonEvent:** Generic button press events
|
|
91
|
-
* **PanicButtonEvent:** Panic/emergency button activations
|
|
92
|
-
* **DoorReaderStateChangeEvent:** Changes in door reader state
|
|
93
|
-
* **DoorRelayStateChangeEvent:** Changes in door relay state
|
|
94
|
-
* **AccessControlUnitTamperEvent:** Tamper detection events
|
|
95
|
-
* **AccessControlUnitBatteryStateChangeEvent:** Battery state changes
|
|
96
|
-
* **WaveToUnlockIntentExpiredEvent:** Wave-to-unlock timeout events
|
|
97
|
-
* **DoorAuthFirstInStateEvent:** First-in authentication state events
|
|
98
|
-
* **DoorScheduleFirstInStateEvent:** First-in schedule state events
|
|
99
|
-
* And more (see input schema for full list).
|
|
100
|
-
|
|
101
|
-
---
|
|
102
|
-
|
|
103
|
-
When eventType is "camera":
|
|
104
|
-
|
|
105
|
-
Retrieves **footage seekpoints** for one camera: **all activity types** returned for the search window (not limited to human motion). Each item includes an **activity** string plus **timestamp**; plate/vehicle/face fields appear when the API provides them. Use **lpr-tool** for org LPR saved vehicles, labels, and dedicated plate search.
|
|
106
|
-
|
|
107
|
-
Arguments:
|
|
108
|
-
* **cameraUuid (string):** UUID of the camera.
|
|
109
|
-
* **startTime (string):** Start of range (ISO 8601).
|
|
110
|
-
* **duration (number):** Search window in seconds. Default 3600 (1 hour).
|
|
21
|
+
**Not this tool:** use **report-tool** for aggregated counts, time-series summaries, or analytics over intervals — including any range spanning more than ~24 hours. Use **lpr-tool** for org LPR workflows: saved vehicles, vehicle labels, fuzzy plate search, and vehicle event APIs. eventType "camera" returns that camera's own **footage seekpoints** — **every activity type** on its recording timeline (human motion, vehicle motion, and others depending on camera and analytics), sometimes carrying plate or vehicle metadata — which is not a replacement for lpr-tool's plate search.
|
|
111
22
|
|
|
23
|
+
Result sets can be large: keep time ranges narrow. Per-mode required arguments, field semantics, and the full component-event-type list are documented on the input parameters.
|
|
112
24
|
`;
|
|
113
25
|
const TOOL_HANDLER = async (args, extra) => {
|
|
114
26
|
const { eventType, accessControlledDoorUuids, deviceUuid, sensorUuid, locationUuid, componentEventTypes, startTime, endTime, limit, timeZone, tempUnit, cameraUuid, duration, buttonSensorUuid, occupancySensorUuid, proximityTagUuids, doorbellCameraUuid, } = args;
|
|
@@ -166,7 +78,16 @@ const TOOL_HANDLER = async (args, extra) => {
|
|
|
166
78
|
}
|
|
167
79
|
else {
|
|
168
80
|
const events = await getComponentEventsByLocation(locationUuid, componentEventTypes || [], startTime ? new Date(startTime).getTime() : undefined, endTime ? new Date(endTime).getTime() : undefined, timeZone, extra._meta?.requestModifiers, extra.sessionId);
|
|
169
|
-
|
|
81
|
+
// An empty result from a doorless location reads identically to "nothing
|
|
82
|
+
// happened" — say which it is rather than letting the model guess.
|
|
83
|
+
const note = events.length === 0
|
|
84
|
+
? await describeEmptyComponentEventResult(locationUuid, componentEventTypes || [], extra._meta?.requestModifiers, extra.sessionId)
|
|
85
|
+
: undefined;
|
|
86
|
+
return createToolStructuredContent({
|
|
87
|
+
eventType: "component-events",
|
|
88
|
+
componentEvents: events,
|
|
89
|
+
...(note ? { note } : {}),
|
|
90
|
+
});
|
|
170
91
|
}
|
|
171
92
|
}
|
|
172
93
|
case EventsToolRequestType.CAMERA: {
|