@wilnertech/halopsa-mcp-server 1.4.0 → 1.5.0

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.
Files changed (38) hide show
  1. package/README.md +1 -1
  2. package/dist/api/client.d.ts +12 -2
  3. package/dist/api/client.d.ts.map +1 -1
  4. package/dist/api/client.js +74 -9
  5. package/dist/api/client.js.map +1 -1
  6. package/dist/api/errors.d.ts +16 -3
  7. package/dist/api/errors.d.ts.map +1 -1
  8. package/dist/api/errors.js +106 -28
  9. package/dist/api/errors.js.map +1 -1
  10. package/dist/cache/memory-cache.d.ts +1 -0
  11. package/dist/cache/memory-cache.d.ts.map +1 -1
  12. package/dist/cache/memory-cache.js +1 -0
  13. package/dist/cache/memory-cache.js.map +1 -1
  14. package/dist/tools/budgets.d.ts.map +1 -1
  15. package/dist/tools/budgets.js +7 -4
  16. package/dist/tools/budgets.js.map +1 -1
  17. package/dist/tools/milestones.d.ts.map +1 -1
  18. package/dist/tools/milestones.js +12 -9
  19. package/dist/tools/milestones.js.map +1 -1
  20. package/dist/tools/registrations.d.ts +4 -3
  21. package/dist/tools/registrations.d.ts.map +1 -1
  22. package/dist/tools/registrations.js +17 -12
  23. package/dist/tools/registrations.js.map +1 -1
  24. package/dist/tools/ticket-actions.d.ts +46 -0
  25. package/dist/tools/ticket-actions.d.ts.map +1 -1
  26. package/dist/tools/ticket-actions.js +102 -1
  27. package/dist/tools/ticket-actions.js.map +1 -1
  28. package/dist/tools/ticket-reference-data.d.ts +42 -1
  29. package/dist/tools/ticket-reference-data.d.ts.map +1 -1
  30. package/dist/tools/ticket-reference-data.js +144 -8
  31. package/dist/tools/ticket-reference-data.js.map +1 -1
  32. package/dist/tools/tickets.d.ts +33 -0
  33. package/dist/tools/tickets.d.ts.map +1 -1
  34. package/dist/tools/tickets.js +141 -6
  35. package/dist/tools/tickets.js.map +1 -1
  36. package/dist/types/tickets.d.ts +68 -0
  37. package/dist/types/tickets.d.ts.map +1 -1
  38. package/package.json +1 -1
@@ -2,7 +2,7 @@
2
2
  * HaloPSA Ticket Reference Data tools.
3
3
  *
4
4
  * Mirrors the existing pattern in `src/tools/reference-data.ts` for asset
5
- * reference data. All five tools default to compact responses (token
5
+ * reference data. All six tools default to compact responses (token
6
6
  * optimization) and use 1-hour cache TTLs because reference data on a
7
7
  * Halo tenant changes infrequently — most reads should hit cache.
8
8
  *
@@ -10,6 +10,13 @@
10
10
  * standard `format_options` block so callers can choose their style:
11
11
  * `{ full: true }` is the path-of-least-surprise for ad-hoc lookups,
12
12
  * while `format_options` keeps parity with the rest of the MCP surface.
13
+ *
14
+ * v1.5.0 changes:
15
+ * - Bug 3 (P0): Added list_halopsa_ticket_outcomes + getOutcomeByName helper.
16
+ * - Bug 6 (P1): list_halopsa_ticket_priorities now exposes slaid and accepts
17
+ * optional tickettype_id for default-SLA scoping.
18
+ * - Bug 7 (P1): list_halopsa_ticket_categories replaced per-tickettype loop
19
+ * with a single GET /Category call; no more hang.
13
20
  */
14
21
  import { z } from 'zod';
15
22
  import { formatResponse } from '../utils/formatter.js';
@@ -34,15 +41,30 @@ const ListTicketStatusesArgsSchema = z.object({
34
41
  format_options: FormatOptionsSchema,
35
42
  });
36
43
  const ListTicketPrioritiesArgsSchema = z.object({
44
+ tickettype_id: z.number().int().optional()
45
+ .describe('Optional. When supplied, fetches the ticket type and filters priorities to the default SLA ' +
46
+ 'for that ticket type. Falls back to all 12 records if the ticket type has no default SLA. ' +
47
+ "Halo priorities are SLA-scoped — pass tickettype_id to narrow to the relevant SLA, or " +
48
+ 'inspect slaid on each record yourself.'),
37
49
  full: z.boolean().optional()
38
50
  .describe('Return full records instead of compact (default false).'),
39
51
  format_options: FormatOptionsSchema,
40
52
  });
41
53
  const ListTicketCategoriesArgsSchema = z.object({
54
+ tickettype_id: z.number().int().optional()
55
+ .describe('Optional. When supplied, filters categories to those whose type_id matches this ticket type ID ' +
56
+ '(client-side filter — Halo returns all categories regardless of server-side params).'),
42
57
  full: z.boolean().optional()
43
58
  .describe('Return full records instead of compact (default false).'),
44
59
  format_options: FormatOptionsSchema,
45
60
  });
61
+ const ListTicketOutcomesArgsSchema = z.object({
62
+ tickettype_id: z.number().int().optional()
63
+ .describe("Optional. When supplied, fetches the ticket type (with includedetails=true) and filters the " +
64
+ "global outcome list to the IDs listed in the tickettype's outcomes[] array. The Halo " +
65
+ '/Outcome endpoint does not support server-side tickettype filtering — scoping is client-side.'),
66
+ format_options: FormatOptionsSchema,
67
+ });
46
68
  // =============================================================================
47
69
  // Helpers
48
70
  // =============================================================================
@@ -70,6 +92,7 @@ function resolveFormat(full, formatOptions) {
70
92
  *
71
93
  * We promote `priorityid` to `id` and expose the GUID as `sla_priority_id`
72
94
  * to prevent callers from accidentally passing the GUID as priority_id.
95
+ * `slaid` is preserved so callers can disambiguate same-name priorities.
73
96
  */
74
97
  function normalizePriority(p) {
75
98
  return {
@@ -96,6 +119,32 @@ function unwrapList(response, preferredKey) {
96
119
  const record_count = wrapped.record_count ?? items.length;
97
120
  return { items, record_count };
98
121
  }
122
+ /**
123
+ * Compact an outcome record into the token-efficient shape.
124
+ *
125
+ * is_resolution heuristic: name contains "Resolved", "Closed", or "Completed"
126
+ * (case-insensitive), OR systemid is a non-zero value (closure outcomes in
127
+ * Halo carry a non-zero systemid).
128
+ *
129
+ * is_internal_note heuristic: respondtype === 2.
130
+ *
131
+ * NOTE: both flags are heuristics. Verify against your tenant's outcome
132
+ * configuration before relying on them for automation.
133
+ */
134
+ function compactOutcome(o) {
135
+ const resolutionNames = /resolved|closed|completed/i;
136
+ const is_resolution = resolutionNames.test(o.outcome ?? '') || ((o.systemid ?? 0) > 0);
137
+ const is_internal_note = o.respondtype === 2;
138
+ return {
139
+ id: o.id,
140
+ name: o.outcome,
141
+ button: o.buttonname,
142
+ sequence: o.sequence,
143
+ hidden: o.hidden,
144
+ is_resolution,
145
+ is_internal_note,
146
+ };
147
+ }
99
148
  // =============================================================================
100
149
  // Implementations
101
150
  // =============================================================================
@@ -130,25 +179,106 @@ export async function listTicketStatuses(client, args) {
130
179
  }
131
180
  export async function listTicketPriorities(client, args) {
132
181
  const formatOpts = resolveFormat(args.full, args.format_options);
182
+ // Fetch global priority list (12 records = 3 SLAs x 4 levels).
133
183
  const response = await client.getCached('/Priority', { count: 500 }, {
134
184
  enabled: true,
135
185
  ttl: TTL.TICKET_PRIORITIES,
136
186
  keyPrefix: 'ticketpriorities:all',
137
187
  });
138
- const { items: rawItems, record_count } = unwrapList(response, 'priorities');
139
- const items = rawItems.map(normalizePriority);
140
- return formatResponse(items, formatOpts, { record_count });
188
+ const { items: rawItems } = unwrapList(response, 'priorities');
189
+ let normalized = rawItems.map(normalizePriority);
190
+ // Bug 6 fix: if tickettype_id supplied, scope to that type's default SLA.
191
+ if (args.tickettype_id !== undefined) {
192
+ const ttResponse = await client.getCached(`/TicketType/${args.tickettype_id}`, { includedetails: true }, {
193
+ enabled: true,
194
+ ttl: TTL.TICKET_TYPES,
195
+ keyPrefix: `tickettype:detail:${args.tickettype_id}`,
196
+ });
197
+ // Support both field names Halo uses across versions.
198
+ const defaultSla = ttResponse.default_sla ?? ttResponse.sla_id;
199
+ if (defaultSla !== undefined && defaultSla !== null) {
200
+ const filtered = normalized.filter((p) => p.slaid === defaultSla);
201
+ // Fall back to all records if the SLA filter yields nothing (e.g. tenant
202
+ // uses a different field name we have not mapped).
203
+ if (filtered.length > 0) {
204
+ normalized = filtered;
205
+ }
206
+ }
207
+ // If no default SLA on the tickettype, return all 12 (documented fallback).
208
+ }
209
+ return formatResponse(normalized, formatOpts, { record_count: normalized.length });
141
210
  }
142
211
  export async function listTicketCategories(client, args) {
143
212
  const formatOpts = resolveFormat(args.full, args.format_options);
144
- const response = await client.getCached('/Category', { count: 500 }, {
213
+ // Bug 7 fix: single GET /Category no loop, no per-tickettype fetching.
214
+ // The Halo endpoint ignores count/tickettype_id params and always returns
215
+ // the full list (~90 records). Caching prevents repeat hits.
216
+ const response = await client.getCached('/Category', {}, {
145
217
  enabled: true,
146
218
  ttl: TTL.TICKET_CATEGORIES,
147
219
  keyPrefix: 'ticketcategories:all',
148
220
  });
149
- const { items, record_count } = unwrapList(response, 'categories');
221
+ let { items, record_count } = unwrapList(response, 'categories');
222
+ // Optional client-side filter by type_id (== tickettype_id in Halo's schema).
223
+ if (args.tickettype_id !== undefined) {
224
+ items = items.filter((c) => c.type_id === args.tickettype_id);
225
+ record_count = items.length;
226
+ }
150
227
  return formatResponse(items, formatOpts, { record_count });
151
228
  }
229
+ /**
230
+ * List all ticket outcomes from GET /Outcome (cached 1h).
231
+ *
232
+ * If tickettype_id is supplied, fetches the ticket type with includedetails=true
233
+ * and filters to the outcome IDs in its `outcomes[]` array.
234
+ *
235
+ * NOTE: The /Outcome endpoint does not support server-side tickettype filtering.
236
+ * Scoping is always performed client-side.
237
+ */
238
+ export async function listTicketOutcomes(client, args) {
239
+ const formatOpts = args.format_options ?? { format: 'compact' };
240
+ // Fetch global outcome list.
241
+ const response = await client.getCached('/Outcome', { count: 200 }, {
242
+ enabled: true,
243
+ ttl: TTL.TICKET_OUTCOMES,
244
+ keyPrefix: 'ticketoutcomes:all',
245
+ });
246
+ let { items } = unwrapList(response, 'outcomes');
247
+ // Optional client-side filter via tickettype's outcomes[] array.
248
+ if (args.tickettype_id !== undefined) {
249
+ const ttResponse = await client.getCached(`/TicketType/${args.tickettype_id}`, { includedetails: true }, {
250
+ enabled: true,
251
+ ttl: TTL.TICKET_TYPES,
252
+ keyPrefix: `tickettype:detail:${args.tickettype_id}`,
253
+ });
254
+ const allowedIds = ttResponse.outcomes;
255
+ if (Array.isArray(allowedIds) && allowedIds.length > 0) {
256
+ const idSet = new Set(allowedIds);
257
+ items = items.filter((o) => idSet.has(o.id));
258
+ }
259
+ // If the tickettype has no outcomes[] array, return the full global list.
260
+ }
261
+ const compacted = items.map(compactOutcome);
262
+ return formatResponse(compacted, formatOpts, { record_count: compacted.length });
263
+ }
264
+ /**
265
+ * Look up a single outcome by name (case-insensitive, first match).
266
+ *
267
+ * Returns null if no matching outcome is found. Used internally by ticket
268
+ * action tools that need to resolve an outcome name to its ID (e.g., SLA
269
+ * hold/release tools in ticket-actions.ts).
270
+ */
271
+ export async function getOutcomeByName(client, name) {
272
+ const response = await client.getCached('/Outcome', { count: 200 }, {
273
+ enabled: true,
274
+ ttl: TTL.TICKET_OUTCOMES,
275
+ keyPrefix: 'ticketoutcomes:all',
276
+ });
277
+ const { items } = unwrapList(response, 'outcomes');
278
+ const lower = name.toLowerCase();
279
+ const match = items.find((o) => (o.outcome ?? '').toLowerCase() === lower);
280
+ return match ? compactOutcome(match) : null;
281
+ }
152
282
  // =============================================================================
153
283
  // Tool Definitions
154
284
  // =============================================================================
@@ -172,14 +302,20 @@ export const listTicketStatusesTool = {
172
302
  };
173
303
  export const listTicketPrioritiesTool = {
174
304
  name: 'list_halopsa_ticket_priorities',
175
- description: 'List HaloPSA ticket priorities. The `id` field is the integer priority value (1-4) that maps to `Faults.priority_id` on ticket creates — NOT the underlying GUID, which is per-SLA and exposed as `sla_priority_id`. Pass `id` to create/update_halopsa_ticket. Names duplicate across SLAs (3 Critical, 3 High, 3 Medium, 3 Low one per SLA). Includes fix/response time hints. Cached 1h.',
305
+ description: "List HaloPSA ticket priorities. Halo's priorities are SLA-scoped — each SLA has its own priority levels with overlapping integer ids (1-4). Pass `tickettype_id` to filter to that ticket type's default SLA, OR inspect `slaid` on each record to disambiguate. The `id` field is the integer priority value (1-4) that maps to `Faults.priority_id` on ticket creates — NOT the underlying GUID (exposed as `sla_priority_id`). Returns all 12 records (3 SLAs x 4 levels) when no tickettype_id is supplied. Includes fix/response time hints. Cached 1h.",
176
306
  schema: ListTicketPrioritiesArgsSchema,
177
307
  handler: listTicketPriorities,
178
308
  };
179
309
  export const listTicketCategoriesTool = {
180
310
  name: 'list_halopsa_ticket_categories',
181
- description: 'List all HaloPSA ticket categories (used for category_1..category_4 fields on tickets). Cached 1h.',
311
+ description: 'List all HaloPSA ticket categories (used for category_1..category_4 fields on tickets). Fetches GET /Category once and returns all categories (~90 records). Pass optional tickettype_id for client-side filtering by type_id. Cached 1h.',
182
312
  schema: ListTicketCategoriesArgsSchema,
183
313
  handler: listTicketCategories,
184
314
  };
315
+ export const listTicketOutcomesTool = {
316
+ name: 'list_halopsa_ticket_outcomes',
317
+ description: "List HaloPSA ticket outcomes — the action outcomes selectable when adding a ticket action (e.g., Triage, Resolved, Closed, Escalated). Returns compact shape: {id, name, button, sequence, hidden, is_resolution, is_internal_note}. NOTE: is_resolution and is_internal_note are HEURISTICS (name-match + respondtype) — verify against your tenant's outcome configuration before automation. Pass optional tickettype_id to filter to outcomes allowed for that ticket type (client-side via the tickettype's outcomes[] array). GET /Outcome does not support server-side filtering. Cached 1h.",
318
+ schema: ListTicketOutcomesArgsSchema,
319
+ handler: listTicketOutcomes,
320
+ };
185
321
  //# sourceMappingURL=ticket-reference-data.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ticket-reference-data.js","sourceRoot":"","sources":["../../src/tools/ticket-reference-data.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAcxB,OAAO,EAAE,cAAc,EAAsB,MAAM,uBAAuB,CAAC;AAC3E,OAAO,EAAE,GAAG,EAAE,MAAM,0BAA0B,CAAC;AAC/C,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAE3D,gFAAgF;AAChF,UAAU;AACV,gFAAgF;AAEhF,MAAM,yBAAyB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;SACzB,QAAQ,CAAC,yGAAyG,CAAC;IACtH,cAAc,EAAE,mBAAmB;CACpC,CAAC,CAAC;AAEH,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC;IACvC,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE;SAC5B,QAAQ,CAAC,wBAAwB,CAAC;IACrC,cAAc,EAAE,mBAAmB;CACpC,CAAC,CAAC;AAEH,MAAM,4BAA4B,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5C,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;SACzB,QAAQ,CAAC,yDAAyD,CAAC;IACtE,cAAc,EAAE,mBAAmB;CACpC,CAAC,CAAC;AAEH,MAAM,8BAA8B,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9C,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;SACzB,QAAQ,CAAC,yDAAyD,CAAC;IACtE,cAAc,EAAE,mBAAmB;CACpC,CAAC,CAAC;AAEH,MAAM,8BAA8B,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9C,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;SACzB,QAAQ,CAAC,yDAAyD,CAAC;IACtE,cAAc,EAAE,mBAAmB;CACpC,CAAC,CAAC;AAEH,gFAAgF;AAChF,UAAU;AACV,gFAAgF;AAEhF;;;;GAIG;AACH,SAAS,aAAa,CACpB,IAAyB,EACzB,aAAwC;IAExC,MAAM,IAAI,GAAkB,EAAE,GAAG,CAAC,aAAa,IAAI,EAAE,CAAC,EAAE,CAAC;IACzD,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;QACzB,IAAI,CAAC,MAAM,GAAG,UAAU,CAAC;IAC3B,CAAC;IACD,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;QACjB,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC;IAC1B,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,iBAAiB,CAAC,CAAwB;IACjD,OAAO;QACL,EAAE,EAAE,CAAC,CAAC,UAAU;QAChB,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,eAAe,EAAE,CAAC,CAAC,EAAE;QACrB,KAAK,EAAE,CAAC,CAAC,KAAK;QACd,OAAO,EAAE,CAAC,CAAC,OAAO;QAClB,QAAQ,EAAE,CAAC,CAAC,QAAQ;QACpB,YAAY,EAAE,CAAC,CAAC,YAAY;QAC5B,aAAa,EAAE,CAAC,CAAC,aAAa;QAC9B,MAAM,EAAE,CAAC,CAAC,MAAM;QAChB,iBAAiB,EAAE,CAAC,CAAC,iBAAiB;QACtC,kBAAkB,EAAE,CAAC,CAAC,kBAAkB;KACzC,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,SAAS,UAAU,CACjB,QAAsB,EACtB,YAAoB;IAEpB,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5B,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC;IAC5D,CAAC;IACD,MAAM,OAAO,GAAG,QAAmC,CAAC;IACpD,MAAM,KAAK,GAAI,OAAO,CAAC,YAAY,CAAqB,IAAI,EAAE,CAAC;IAC/D,MAAM,YAAY,GAAI,OAAO,CAAC,YAAmC,IAAI,KAAK,CAAC,MAAM,CAAC;IAClF,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC;AACjC,CAAC;AAED,gFAAgF;AAChF,kBAAkB;AAClB,gFAAgF;AAEhF,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,MAAwB,EACxB,IAA+C;IAE/C,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;IAEjE,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,SAAS,CACrC,aAAa,EACb,EAAE,KAAK,EAAE,GAAG,EAAE,EACd;QACE,OAAO,EAAE,IAAI;QACb,GAAG,EAAE,GAAG,CAAC,YAAY;QACrB,SAAS,EAAE,iBAAiB;KAC7B,CACF,CAAC;IAEF,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,GAAG,UAAU,CAAoB,QAAQ,EAAE,aAAa,CAAC,CAAC;IACvF,OAAO,cAAc,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC;AAC7D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,MAAwB,EACxB,IAA6C;IAE7C,MAAM,UAAU,GAAkB,IAAI,CAAC,cAAc,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IAEhF,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,SAAS,CACrC,eAAe,IAAI,CAAC,aAAa,EAAE,EACnC,SAAS,EACT;QACE,OAAO,EAAE,IAAI;QACb,GAAG,EAAE,GAAG,CAAC,YAAY;QACrB,SAAS,EAAE,cAAc,IAAI,CAAC,aAAa,EAAE;KAC9C,CACF,CAAC;IAEF,OAAO,cAAc,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;AAC9C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,MAAwB,EACxB,IAAkD;IAElD,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;IAEjE,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,SAAS,CACrC,SAAS,EACT,EAAE,KAAK,EAAE,GAAG,EAAE,EACd;QACE,OAAO,EAAE,IAAI;QACb,GAAG,EAAE,GAAG,CAAC,eAAe;QACxB,SAAS,EAAE,oBAAoB;KAChC,CACF,CAAC;IAEF,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,GAAG,UAAU,CAAsB,QAAQ,EAAE,UAAU,CAAC,CAAC;IACtF,OAAO,cAAc,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC;AAC7D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,MAAwB,EACxB,IAAoD;IAEpD,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;IAEjE,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,SAAS,CACrC,WAAW,EACX,EAAE,KAAK,EAAE,GAAG,EAAE,EACd;QACE,OAAO,EAAE,IAAI;QACb,GAAG,EAAE,GAAG,CAAC,iBAAiB;QAC1B,SAAS,EAAE,sBAAsB;KAClC,CACF,CAAC;IAEF,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAG,UAAU,CAAwB,QAAQ,EAAE,YAAY,CAAC,CAAC;IACpG,MAAM,KAAK,GAAsC,QAAQ,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IACjF,OAAO,cAAc,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC;AAC7D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,MAAwB,EACxB,IAAoD;IAEpD,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;IAEjE,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,SAAS,CACrC,WAAW,EACX,EAAE,KAAK,EAAE,GAAG,EAAE,EACd;QACE,OAAO,EAAE,IAAI;QACb,GAAG,EAAE,GAAG,CAAC,iBAAiB;QAC1B,SAAS,EAAE,sBAAsB;KAClC,CACF,CAAC;IAEF,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,GAAG,UAAU,CAAwB,QAAQ,EAAE,YAAY,CAAC,CAAC;IAC1F,OAAO,cAAc,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC;AAC7D,CAAC;AAED,gFAAgF;AAChF,mBAAmB;AACnB,gFAAgF;AAEhF,MAAM,CAAC,MAAM,mBAAmB,GAAsB;IACpD,IAAI,EAAE,0BAA0B;IAChC,WAAW,EAAE,6IAA6I;IAC1J,MAAM,EAAE,yBAAyB;IACjC,OAAO,EAAE,eAAe;CACzB,CAAC;AAEF,MAAM,CAAC,MAAM,iBAAiB,GAAsB;IAClD,IAAI,EAAE,wBAAwB;IAC9B,WAAW,EAAE,oDAAoD;IACjE,MAAM,EAAE,uBAAuB;IAC/B,OAAO,EAAE,aAAa;CACvB,CAAC;AAEF,MAAM,CAAC,MAAM,sBAAsB,GAAsB;IACvD,IAAI,EAAE,8BAA8B;IACpC,WAAW,EAAE,2eAA2e;IACxf,MAAM,EAAE,4BAA4B;IACpC,OAAO,EAAE,kBAAkB;CAC5B,CAAC;AAEF,MAAM,CAAC,MAAM,wBAAwB,GAAsB;IACzD,IAAI,EAAE,gCAAgC;IACtC,WAAW,EAAE,+XAA+X;IAC5Y,MAAM,EAAE,8BAA8B;IACtC,OAAO,EAAE,oBAAoB;CAC9B,CAAC;AAEF,MAAM,CAAC,MAAM,wBAAwB,GAAsB;IACzD,IAAI,EAAE,gCAAgC;IACtC,WAAW,EAAE,oGAAoG;IACjH,MAAM,EAAE,8BAA8B;IACtC,OAAO,EAAE,oBAAoB;CAC9B,CAAC"}
1
+ {"version":3,"file":"ticket-reference-data.js","sourceRoot":"","sources":["../../src/tools/ticket-reference-data.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAiBxB,OAAO,EAAE,cAAc,EAAsB,MAAM,uBAAuB,CAAC;AAC3E,OAAO,EAAE,GAAG,EAAE,MAAM,0BAA0B,CAAC;AAC/C,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAE3D,gFAAgF;AAChF,UAAU;AACV,gFAAgF;AAEhF,MAAM,yBAAyB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;SACzB,QAAQ,CAAC,yGAAyG,CAAC;IACtH,cAAc,EAAE,mBAAmB;CACpC,CAAC,CAAC;AAEH,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC;IACvC,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE;SAC5B,QAAQ,CAAC,wBAAwB,CAAC;IACrC,cAAc,EAAE,mBAAmB;CACpC,CAAC,CAAC;AAEH,MAAM,4BAA4B,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5C,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;SACzB,QAAQ,CAAC,yDAAyD,CAAC;IACtE,cAAc,EAAE,mBAAmB;CACpC,CAAC,CAAC;AAEH,MAAM,8BAA8B,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9C,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;SACvC,QAAQ,CACP,6FAA6F;QAC7F,4FAA4F;QAC5F,wFAAwF;QACxF,wCAAwC,CACzC;IACH,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;SACzB,QAAQ,CAAC,yDAAyD,CAAC;IACtE,cAAc,EAAE,mBAAmB;CACpC,CAAC,CAAC;AAEH,MAAM,8BAA8B,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9C,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;SACvC,QAAQ,CACP,iGAAiG;QACjG,sFAAsF,CACvF;IACH,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;SACzB,QAAQ,CAAC,yDAAyD,CAAC;IACtE,cAAc,EAAE,mBAAmB;CACpC,CAAC,CAAC;AAEH,MAAM,4BAA4B,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5C,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;SACvC,QAAQ,CACP,8FAA8F;QAC9F,uFAAuF;QACvF,+FAA+F,CAChG;IACH,cAAc,EAAE,mBAAmB;CACpC,CAAC,CAAC;AAEH,gFAAgF;AAChF,UAAU;AACV,gFAAgF;AAEhF;;;;GAIG;AACH,SAAS,aAAa,CACpB,IAAyB,EACzB,aAAwC;IAExC,MAAM,IAAI,GAAkB,EAAE,GAAG,CAAC,aAAa,IAAI,EAAE,CAAC,EAAE,CAAC;IACzD,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;QACzB,IAAI,CAAC,MAAM,GAAG,UAAU,CAAC;IAC3B,CAAC;IACD,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;QACjB,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC;IAC1B,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAS,iBAAiB,CAAC,CAAwB;IACjD,OAAO;QACL,EAAE,EAAE,CAAC,CAAC,UAAU;QAChB,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,eAAe,EAAE,CAAC,CAAC,EAAE;QACrB,KAAK,EAAE,CAAC,CAAC,KAAK;QACd,OAAO,EAAE,CAAC,CAAC,OAAO;QAClB,QAAQ,EAAE,CAAC,CAAC,QAAQ;QACpB,YAAY,EAAE,CAAC,CAAC,YAAY;QAC5B,aAAa,EAAE,CAAC,CAAC,aAAa;QAC9B,MAAM,EAAE,CAAC,CAAC,MAAM;QAChB,iBAAiB,EAAE,CAAC,CAAC,iBAAiB;QACtC,kBAAkB,EAAE,CAAC,CAAC,kBAAkB;KACzC,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,SAAS,UAAU,CACjB,QAAsB,EACtB,YAAoB;IAEpB,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5B,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC;IAC5D,CAAC;IACD,MAAM,OAAO,GAAG,QAAmC,CAAC;IACpD,MAAM,KAAK,GAAI,OAAO,CAAC,YAAY,CAAqB,IAAI,EAAE,CAAC;IAC/D,MAAM,YAAY,GAAI,OAAO,CAAC,YAAmC,IAAI,KAAK,CAAC,MAAM,CAAC;IAClF,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC;AACjC,CAAC;AAED;;;;;;;;;;;GAWG;AACH,SAAS,cAAc,CAAC,CAAuB;IAC7C,MAAM,eAAe,GAAG,4BAA4B,CAAC;IACrD,MAAM,aAAa,GACjB,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACnE,MAAM,gBAAgB,GAAG,CAAC,CAAC,WAAW,KAAK,CAAC,CAAC;IAE7C,OAAO;QACL,EAAE,EAAE,CAAC,CAAC,EAAE;QACR,IAAI,EAAE,CAAC,CAAC,OAAO;QACf,MAAM,EAAE,CAAC,CAAC,UAAU;QACpB,QAAQ,EAAE,CAAC,CAAC,QAAQ;QACpB,MAAM,EAAE,CAAC,CAAC,MAAM;QAChB,aAAa;QACb,gBAAgB;KACjB,CAAC;AACJ,CAAC;AAED,gFAAgF;AAChF,kBAAkB;AAClB,gFAAgF;AAEhF,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,MAAwB,EACxB,IAA+C;IAE/C,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;IAEjE,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,SAAS,CACrC,aAAa,EACb,EAAE,KAAK,EAAE,GAAG,EAAE,EACd;QACE,OAAO,EAAE,IAAI;QACb,GAAG,EAAE,GAAG,CAAC,YAAY;QACrB,SAAS,EAAE,iBAAiB;KAC7B,CACF,CAAC;IAEF,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,GAAG,UAAU,CAAoB,QAAQ,EAAE,aAAa,CAAC,CAAC;IACvF,OAAO,cAAc,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC;AAC7D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,MAAwB,EACxB,IAA6C;IAE7C,MAAM,UAAU,GAAkB,IAAI,CAAC,cAAc,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IAEhF,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,SAAS,CACrC,eAAe,IAAI,CAAC,aAAa,EAAE,EACnC,SAAS,EACT;QACE,OAAO,EAAE,IAAI;QACb,GAAG,EAAE,GAAG,CAAC,YAAY;QACrB,SAAS,EAAE,cAAc,IAAI,CAAC,aAAa,EAAE;KAC9C,CACF,CAAC;IAEF,OAAO,cAAc,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;AAC9C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,MAAwB,EACxB,IAAkD;IAElD,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;IAEjE,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,SAAS,CACrC,SAAS,EACT,EAAE,KAAK,EAAE,GAAG,EAAE,EACd;QACE,OAAO,EAAE,IAAI;QACb,GAAG,EAAE,GAAG,CAAC,eAAe;QACxB,SAAS,EAAE,oBAAoB;KAChC,CACF,CAAC;IAEF,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,GAAG,UAAU,CAAsB,QAAQ,EAAE,UAAU,CAAC,CAAC;IACtF,OAAO,cAAc,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC;AAC7D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,MAAwB,EACxB,IAAoD;IAEpD,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;IAEjE,+DAA+D;IAC/D,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,SAAS,CACrC,WAAW,EACX,EAAE,KAAK,EAAE,GAAG,EAAE,EACd;QACE,OAAO,EAAE,IAAI;QACb,GAAG,EAAE,GAAG,CAAC,iBAAiB;QAC1B,SAAS,EAAE,sBAAsB;KAClC,CACF,CAAC;IAEF,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,UAAU,CAAwB,QAAQ,EAAE,YAAY,CAAC,CAAC;IACtF,IAAI,UAAU,GAAsC,QAAQ,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IAEpF,0EAA0E;IAC1E,IAAI,IAAI,CAAC,aAAa,KAAK,SAAS,EAAE,CAAC;QACrC,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,SAAS,CACvC,eAAe,IAAI,CAAC,aAAa,EAAE,EACnC,EAAE,cAAc,EAAE,IAAI,EAAE,EACxB;YACE,OAAO,EAAE,IAAI;YACb,GAAG,EAAE,GAAG,CAAC,YAAY;YACrB,SAAS,EAAE,qBAAqB,IAAI,CAAC,aAAa,EAAE;SACrD,CACF,CAAC;QAEF,sDAAsD;QACtD,MAAM,UAAU,GAAG,UAAU,CAAC,WAAW,IAAI,UAAU,CAAC,MAAM,CAAC;QAC/D,IAAI,UAAU,KAAK,SAAS,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;YACpD,MAAM,QAAQ,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,UAAU,CAAC,CAAC;YAClE,yEAAyE;YACzE,mDAAmD;YACnD,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACxB,UAAU,GAAG,QAAQ,CAAC;YACxB,CAAC;QACH,CAAC;QACD,4EAA4E;IAC9E,CAAC;IAED,OAAO,cAAc,CAAC,UAAU,EAAE,UAAU,EAAE,EAAE,YAAY,EAAE,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC;AACrF,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,MAAwB,EACxB,IAAoD;IAEpD,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;IAEjE,yEAAyE;IACzE,0EAA0E;IAC1E,6DAA6D;IAC7D,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,SAAS,CACrC,WAAW,EACX,EAAE,EACF;QACE,OAAO,EAAE,IAAI;QACb,GAAG,EAAE,GAAG,CAAC,iBAAiB;QAC1B,SAAS,EAAE,sBAAsB;KAClC,CACF,CAAC;IAEF,IAAI,EAAE,KAAK,EAAE,YAAY,EAAE,GAAG,UAAU,CAAwB,QAAQ,EAAE,YAAY,CAAC,CAAC;IAExF,8EAA8E;IAC9E,IAAI,IAAI,CAAC,aAAa,KAAK,SAAS,EAAE,CAAC;QACrC,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,IAAI,CAAC,aAAa,CAAC,CAAC;QAC9D,YAAY,GAAG,KAAK,CAAC,MAAM,CAAC;IAC9B,CAAC;IAED,OAAO,cAAc,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC;AAC7D,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,MAAwB,EACxB,IAAkD;IAElD,MAAM,UAAU,GAAkB,IAAI,CAAC,cAAc,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IAE/E,6BAA6B;IAC7B,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,SAAS,CACrC,UAAU,EACV,EAAE,KAAK,EAAE,GAAG,EAAE,EACd;QACE,OAAO,EAAE,IAAI;QACb,GAAG,EAAE,GAAG,CAAC,eAAe;QACxB,SAAS,EAAE,oBAAoB;KAChC,CACF,CAAC;IAEF,IAAI,EAAE,KAAK,EAAE,GAAG,UAAU,CAAuB,QAAQ,EAAE,UAAU,CAAC,CAAC;IAEvE,iEAAiE;IACjE,IAAI,IAAI,CAAC,aAAa,KAAK,SAAS,EAAE,CAAC;QACrC,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,SAAS,CACvC,eAAe,IAAI,CAAC,aAAa,EAAE,EACnC,EAAE,cAAc,EAAE,IAAI,EAAE,EACxB;YACE,OAAO,EAAE,IAAI;YACb,GAAG,EAAE,GAAG,CAAC,YAAY;YACrB,SAAS,EAAE,qBAAqB,IAAI,CAAC,aAAa,EAAE;SACrD,CACF,CAAC;QAEF,MAAM,UAAU,GAAG,UAAU,CAAC,QAAQ,CAAC;QACvC,IAAI,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvD,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC;YAClC,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAC/C,CAAC;QACD,0EAA0E;IAC5E,CAAC;IAED,MAAM,SAAS,GAAkC,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAE3E,OAAO,cAAc,CAAC,SAAS,EAAE,UAAU,EAAE,EAAE,YAAY,EAAE,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC;AACnF,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,MAAwB,EACxB,IAAY;IAEZ,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,SAAS,CACrC,UAAU,EACV,EAAE,KAAK,EAAE,GAAG,EAAE,EACd;QACE,OAAO,EAAE,IAAI;QACb,GAAG,EAAE,GAAG,CAAC,eAAe;QACxB,SAAS,EAAE,oBAAoB;KAChC,CACF,CAAC;IAEF,MAAM,EAAE,KAAK,EAAE,GAAG,UAAU,CAAuB,QAAQ,EAAE,UAAU,CAAC,CAAC;IACzE,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IACjC,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,KAAK,KAAK,CAAC,CAAC;IAC3E,OAAO,KAAK,CAAC,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC9C,CAAC;AAED,gFAAgF;AAChF,mBAAmB;AACnB,gFAAgF;AAEhF,MAAM,CAAC,MAAM,mBAAmB,GAAsB;IACpD,IAAI,EAAE,0BAA0B;IAChC,WAAW,EAAE,6IAA6I;IAC1J,MAAM,EAAE,yBAAyB;IACjC,OAAO,EAAE,eAAe;CACzB,CAAC;AAEF,MAAM,CAAC,MAAM,iBAAiB,GAAsB;IAClD,IAAI,EAAE,wBAAwB;IAC9B,WAAW,EAAE,oDAAoD;IACjE,MAAM,EAAE,uBAAuB;IAC/B,OAAO,EAAE,aAAa;CACvB,CAAC;AAEF,MAAM,CAAC,MAAM,sBAAsB,GAAsB;IACvD,IAAI,EAAE,8BAA8B;IACpC,WAAW,EAAE,2eAA2e;IACxf,MAAM,EAAE,4BAA4B;IACpC,OAAO,EAAE,kBAAkB;CAC5B,CAAC;AAEF,MAAM,CAAC,MAAM,wBAAwB,GAAsB;IACzD,IAAI,EAAE,gCAAgC;IACtC,WAAW,EAAE,8hBAA8hB;IAC3iB,MAAM,EAAE,8BAA8B;IACtC,OAAO,EAAE,oBAAoB;CAC9B,CAAC;AAEF,MAAM,CAAC,MAAM,wBAAwB,GAAsB;IACzD,IAAI,EAAE,gCAAgC;IACtC,WAAW,EAAE,2OAA2O;IACxP,MAAM,EAAE,8BAA8B;IACtC,OAAO,EAAE,oBAAoB;CAC9B,CAAC;AAEF,MAAM,CAAC,MAAM,sBAAsB,GAAsB;IACvD,IAAI,EAAE,8BAA8B;IACpC,WAAW,EAAE,qkBAAqkB;IACllB,MAAM,EAAE,4BAA4B;IACpC,OAAO,EAAE,kBAAkB;CAC5B,CAAC"}
@@ -98,6 +98,8 @@ export declare const CreateTicketArgsSchema: z.ZodObject<{
98
98
  estimate: z.ZodOptional<z.ZodNumber>;
99
99
  estimatedays: z.ZodOptional<z.ZodNumber>;
100
100
  milestone_id: z.ZodOptional<z.ZodNumber>;
101
+ asset_id: z.ZodOptional<z.ZodNumber>;
102
+ asset_ids: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
101
103
  format_options: z.ZodOptional<z.ZodObject<{
102
104
  format: z.ZodOptional<z.ZodEnum<{
103
105
  compact: "compact";
@@ -132,6 +134,8 @@ export declare const UpdateTicketArgsSchema: z.ZodObject<{
132
134
  estimate: z.ZodOptional<z.ZodNumber>;
133
135
  estimatedays: z.ZodOptional<z.ZodNumber>;
134
136
  milestone_id: z.ZodOptional<z.ZodNumber>;
137
+ asset_id: z.ZodOptional<z.ZodNumber>;
138
+ asset_ids: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
135
139
  format_options: z.ZodOptional<z.ZodObject<{
136
140
  format: z.ZodOptional<z.ZodEnum<{
137
141
  compact: "compact";
@@ -151,6 +155,19 @@ export declare const DeleteTicketArgsSchema: z.ZodObject<{
151
155
  reason: z.ZodString;
152
156
  confirm_destructive: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
153
157
  }, z.core.$strip>;
158
+ export declare const ValidateCreateTicketArgsSchema: z.ZodObject<{
159
+ tickettype_id: z.ZodNumber;
160
+ fields: z.ZodRecord<z.ZodString, z.ZodUnknown>;
161
+ format_options: z.ZodOptional<z.ZodObject<{
162
+ format: z.ZodOptional<z.ZodEnum<{
163
+ compact: "compact";
164
+ standard: "standard";
165
+ detailed: "detailed";
166
+ }>>;
167
+ fields: z.ZodOptional<z.ZodArray<z.ZodString>>;
168
+ omit_empty: z.ZodOptional<z.ZodBoolean>;
169
+ }, z.core.$strip>>;
170
+ }, z.core.$strip>;
154
171
  export declare function listTickets(client: HaloPSAAPIClient, args: z.infer<typeof ListTicketsArgsSchema>): Promise<string>;
155
172
  export declare function getTicket(client: HaloPSAAPIClient, args: z.infer<typeof GetTicketArgsSchema>): Promise<string>;
156
173
  export declare function searchTickets(client: HaloPSAAPIClient, args: z.infer<typeof SearchTicketsArgsSchema>): Promise<string>;
@@ -182,6 +199,21 @@ export declare function updateTicket(client: HaloPSAAPIClient, args: z.infer<typ
182
199
  */
183
200
  export declare function deleteTicket(client: HaloPSAAPIClient, args: z.infer<typeof DeleteTicketArgsSchema>): Promise<string>;
184
201
  export declare function closeTicket(client: HaloPSAAPIClient, args: z.infer<typeof CloseTicketArgsSchema>): Promise<string>;
202
+ /**
203
+ * Pure-local validation of a proposed ticket payload against the cached
204
+ * tickettype schema. No write is performed — this is a dry-run helper.
205
+ *
206
+ * Required-field detection logic:
207
+ * Halo's TicketType details response includes a `fields[]` array where each
208
+ * entry has a `fieldinfo` sub-object. The `mandatory` flag on `fieldinfo`
209
+ * indicates a required field. Additionally, `technew` on `fieldinfo` where
210
+ * the value equals 3 indicates "required for tech/API create". When either
211
+ * condition is true, the field is counted as required.
212
+ *
213
+ * Standard ticket fields (summary, client_id, tickettype_id) are always
214
+ * required regardless of the tickettype schema.
215
+ */
216
+ export declare function validateCreateTicket(client: HaloPSAAPIClient, args: z.infer<typeof ValidateCreateTicketArgsSchema>): Promise<string>;
185
217
  export declare const listTicketsTool: ZodToolDefinition;
186
218
  export declare const getTicketTool: ZodToolDefinition;
187
219
  export declare const searchTicketsTool: ZodToolDefinition;
@@ -189,4 +221,5 @@ export declare const createTicketTool: ZodToolDefinition;
189
221
  export declare const updateTicketTool: ZodToolDefinition;
190
222
  export declare const closeTicketTool: ZodToolDefinition;
191
223
  export declare const deleteTicketTool: ZodToolDefinition;
224
+ export declare const validateCreateTicketTool: ZodToolDefinition;
192
225
  //# sourceMappingURL=tickets.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tickets.d.ts","sourceRoot":"","sources":["../../src/tools/tickets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAgCvD,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAwChC,CAAC;AAEH,eAAO,MAAM,mBAAmB;;;;;;;;;;;iBAI9B,CAAC;AAEH,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;iBAUlC,CAAC;AAEH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAwCjC,CAAC;AAEH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA8BjC,CAAC;AAEH,eAAO,MAAM,qBAAqB;;;iBAKhC,CAAC;AAEH,eAAO,MAAM,sBAAsB;;;;iBAQjC,CAAC;AAoGH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,GAC1C,OAAO,CAAC,MAAM,CAAC,CAwCjB;AAED,wBAAsB,SAAS,CAC7B,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,GACxC,OAAO,CAAC,MAAM,CAAC,CAcjB;AAED,wBAAsB,aAAa,CACjC,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,GAC5C,OAAO,CAAC,MAAM,CAAC,CAQjB;AAED,wBAAsB,YAAY,CAChC,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,GAC3C,OAAO,CAAC,MAAM,CAAC,CA2CjB;AAED,wBAAsB,YAAY,CAChC,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,GAC3C,OAAO,CAAC,MAAM,CAAC,CAqGjB;AAgBD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,GAC3C,OAAO,CAAC,MAAM,CAAC,CA8HjB;AAED,wBAAsB,WAAW,CAC/B,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,GAC1C,OAAO,CAAC,MAAM,CAAC,CAiBjB;AAMD,eAAO,MAAM,eAAe,EAAE,iBAK7B,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,iBAK3B,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,iBAK/B,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,iBAK9B,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,iBAK9B,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,iBAK7B,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,iBAK9B,CAAC"}
1
+ {"version":3,"file":"tickets.d.ts","sourceRoot":"","sources":["../../src/tools/tickets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAgCvD,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAwChC,CAAC;AAEH,eAAO,MAAM,mBAAmB;;;;;;;;;;;iBAI9B,CAAC;AAEH,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;iBAUlC,CAAC;AAEH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA6CjC,CAAC;AAEH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAmCjC,CAAC;AAEH,eAAO,MAAM,qBAAqB;;;iBAKhC,CAAC;AAEH,eAAO,MAAM,sBAAsB;;;;iBAQjC,CAAC;AAGH,eAAO,MAAM,8BAA8B;;;;;;;;;;;;iBAMzC,CAAC;AA0HH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,GAC1C,OAAO,CAAC,MAAM,CAAC,CAwCjB;AAED,wBAAsB,SAAS,CAC7B,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,GACxC,OAAO,CAAC,MAAM,CAAC,CAcjB;AAED,wBAAsB,aAAa,CACjC,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,GAC5C,OAAO,CAAC,MAAM,CAAC,CAQjB;AAED,wBAAsB,YAAY,CAChC,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,GAC3C,OAAO,CAAC,MAAM,CAAC,CAoDjB;AAED,wBAAsB,YAAY,CAChC,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,GAC3C,OAAO,CAAC,MAAM,CAAC,CA6GjB;AAgBD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,GAC3C,OAAO,CAAC,MAAM,CAAC,CA8HjB;AAED,wBAAsB,WAAW,CAC/B,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,GAC1C,OAAO,CAAC,MAAM,CAAC,CAiBjB;AAiBD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,8BAA8B,CAAC,GACnD,OAAO,CAAC,MAAM,CAAC,CAiGjB;AAMD,eAAO,MAAM,eAAe,EAAE,iBAK7B,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,iBAK3B,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,iBAK/B,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,iBAK9B,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,iBAK9B,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,iBAK7B,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,iBAK9B,CAAC;AAEF,eAAO,MAAM,wBAAwB,EAAE,iBAMtC,CAAC"}
@@ -131,6 +131,11 @@ export const CreateTicketArgsSchema = z.object({
131
131
  .describe('Estimated business days. Companion to estimate; pass either or neither — Halo auto-computes both from startdate/targetdate when both omitted.'),
132
132
  milestone_id: z.number().int().optional()
133
133
  .describe('Link this ticket to an existing milestone on its parent project ticket. milestone_id orchestrates: the MCP transparently translates this to a parent-project milestones[].tickets[] update. Single milestone per task — setting milestone_id automatically unlinks from any previous milestone. Pass 0 to unlink entirely. Find milestone ids via list_halopsa_ticket_milestones on the parent ticket.'),
134
+ // Bug 9: Asset linking fields.
135
+ asset_id: z.number().int().optional()
136
+ .describe('Single asset to link to this ticket. For multiple, use asset_ids.'),
137
+ asset_ids: z.array(z.number().int()).optional()
138
+ .describe('Multiple asset IDs to link to this ticket. Halo will populate the ticket.assets[] array. If both asset_id and asset_ids are set, they are combined.'),
134
139
  format_options: FormatOptionsSchema,
135
140
  });
136
141
  export const UpdateTicketArgsSchema = z.object({
@@ -162,6 +167,11 @@ export const UpdateTicketArgsSchema = z.object({
162
167
  .describe('Estimated business days. Companion to estimate; pass either or neither — Halo auto-computes both from startdate/targetdate when both omitted.'),
163
168
  milestone_id: z.number().int().optional()
164
169
  .describe('Link this ticket to an existing milestone on its parent project ticket. milestone_id orchestrates: the MCP transparently translates this to a parent-project milestones[].tickets[] update. Single milestone per task — setting milestone_id automatically unlinks from any previous milestone. Pass 0 to unlink entirely. Find milestone ids via list_halopsa_ticket_milestones on the parent ticket.'),
170
+ // Bug 9: Asset linking fields.
171
+ asset_id: z.number().int().optional()
172
+ .describe('Single asset to link to this ticket. For multiple, use asset_ids.'),
173
+ asset_ids: z.array(z.number().int()).optional()
174
+ .describe('Multiple asset IDs to link to this ticket. Halo will populate the ticket.assets[] array. If both asset_id and asset_ids are set, they are combined.'),
165
175
  format_options: FormatOptionsSchema,
166
176
  });
167
177
  export const CloseTicketArgsSchema = z.object({
@@ -175,6 +185,14 @@ export const DeleteTicketArgsSchema = z.object({
175
185
  reason: z.string().min(5).describe('REQUIRED audit-trail reason. To bypass safety blocks, the reason MUST include the substring "OVERRIDE" (case-sensitive) AND confirm_destructive must be true.'),
176
186
  confirm_destructive: z.boolean().optional().default(false).describe('When safety pre-flight checks would block the delete, set true AND include "OVERRIDE" in `reason` to proceed. Default false.'),
177
187
  });
188
+ // Bug 8: Schema for validate_halopsa_create_ticket.
189
+ export const ValidateCreateTicketArgsSchema = z.object({
190
+ tickettype_id: z.number().int()
191
+ .describe('Ticket type ID to validate against — see list_halopsa_tickettypes'),
192
+ fields: z.record(z.string(), z.unknown())
193
+ .describe('Proposed ticket payload as a key-value map. Keys should be field names (e.g. summary, client_id, customfields).'),
194
+ format_options: FormatOptionsSchema,
195
+ });
178
196
  // =============================================================================
179
197
  // Helpers
180
198
  // =============================================================================
@@ -251,6 +269,24 @@ async function resolveClosedStatusId(client) {
251
269
  `(received ${items.length} statuses; "Closed" matches: ${exactClosed.length}, "Completed" matches: ${exactCompleted.length}) ` +
252
270
  'Pass status_id explicitly via update_halopsa_ticket.');
253
271
  }
272
+ /**
273
+ * Build the Halo assets[] array payload from asset_id / asset_ids args.
274
+ * Halo Faults.assets[] accepts elements with at minimum { id: number }.
275
+ * Returns undefined when neither arg is set (no-op).
276
+ */
277
+ function buildAssetsPayload(asset_id, asset_ids) {
278
+ if (asset_id === undefined && asset_ids === undefined) {
279
+ return undefined;
280
+ }
281
+ const idSet = [];
282
+ if (asset_ids !== undefined) {
283
+ idSet.push(...asset_ids);
284
+ }
285
+ if (asset_id !== undefined && !idSet.includes(asset_id)) {
286
+ idSet.push(asset_id);
287
+ }
288
+ return idSet.map((id) => ({ id }));
289
+ }
254
290
  // =============================================================================
255
291
  // Implementations
256
292
  // =============================================================================
@@ -364,14 +400,22 @@ export async function createTicket(client, args) {
364
400
  // milestone_id is NOT sent to Halo as a direct field — writing it to the
365
401
  // child record is a no-op for Project Task tickets (tickettype_id=20).
366
402
  // Milestone membership is orchestrated via linkTicketToMilestone below.
403
+ // Bug 9: Asset linking. Build assets[] from asset_id / asset_ids args.
404
+ // Halo Faults.assets[] accepts elements with at minimum { id: number }.
405
+ const assetsPayload = buildAssetsPayload(args.asset_id, args.asset_ids);
406
+ if (assetsPayload !== undefined) {
407
+ payload.assets = assetsPayload;
408
+ }
367
409
  const created = unwrapWriteResponse(await client.post('/Tickets', [payload]));
368
410
  client.invalidateCache('tickets:*');
369
411
  // Bug 1: Orchestrate milestone linkage via parent project if requested.
370
412
  if (args.milestone_id !== undefined) {
371
413
  await linkTicketToMilestone(client, created.id, args.milestone_id, args.parent_id);
372
414
  }
373
- const format = args.format_options?.format ?? 'compact';
374
- return formatResponse(stripWriteResponse(created, format, 'ticket'), { format });
415
+ // Bug 5: Pass full format_options so fields/omit_empty flow through to formatResponse.
416
+ const formatOpts = args.format_options ?? { format: 'compact' };
417
+ const format = formatOpts.format ?? 'compact';
418
+ return formatResponse(stripWriteResponse(created, format, 'ticket'), formatOpts);
375
419
  }
376
420
  export async function updateTicket(client, args) {
377
421
  const partial = {};
@@ -412,6 +456,11 @@ export async function updateTicket(client, args) {
412
456
  // milestone_id is NOT sent to Halo as a direct field — writing it to the
413
457
  // child record is a no-op for Project Task tickets (tickettype_id=20).
414
458
  // Milestone membership is orchestrated via linkTicketToMilestone below.
459
+ // Bug 9: Asset linking. Build assets[] from asset_id / asset_ids args.
460
+ const assetsPayload = buildAssetsPayload(args.asset_id, args.asset_ids);
461
+ if (assetsPayload !== undefined) {
462
+ partial.assets = assetsPayload;
463
+ }
415
464
  // Determine which fields count for the "at least one" check. We include
416
465
  // milestone_id in the check so that a milestone-only update is valid.
417
466
  const fieldCount = Object.keys(partial).length + (args.milestone_id !== undefined ? 1 : 0);
@@ -462,8 +511,10 @@ export async function updateTicket(client, args) {
462
511
  if (args.milestone_id !== undefined) {
463
512
  await linkTicketToMilestone(client, args.ticket_id, args.milestone_id, args.parent_id);
464
513
  }
465
- const format = args.format_options?.format ?? 'compact';
466
- return formatResponse(stripWriteResponse(response, format, 'ticket'), { format });
514
+ // Bug 5: Pass full format_options so fields/omit_empty flow through to formatResponse.
515
+ const formatOpts = args.format_options ?? { format: 'compact' };
516
+ const format = formatOpts.format ?? 'compact';
517
+ return formatResponse(stripWriteResponse(response, format, 'ticket'), formatOpts);
467
518
  }
468
519
  /**
469
520
  * Permanently delete a HaloPSA ticket by ID after running four safety
@@ -601,6 +652,84 @@ export async function closeTicket(client, args) {
601
652
  status_id: closedStatusId,
602
653
  });
603
654
  }
655
+ /**
656
+ * Pure-local validation of a proposed ticket payload against the cached
657
+ * tickettype schema. No write is performed — this is a dry-run helper.
658
+ *
659
+ * Required-field detection logic:
660
+ * Halo's TicketType details response includes a `fields[]` array where each
661
+ * entry has a `fieldinfo` sub-object. The `mandatory` flag on `fieldinfo`
662
+ * indicates a required field. Additionally, `technew` on `fieldinfo` where
663
+ * the value equals 3 indicates "required for tech/API create". When either
664
+ * condition is true, the field is counted as required.
665
+ *
666
+ * Standard ticket fields (summary, client_id, tickettype_id) are always
667
+ * required regardless of the tickettype schema.
668
+ */
669
+ export async function validateCreateTicket(client, args) {
670
+ // Fetch the tickettype with full field details.
671
+ const tickettype = await client.getCached(`/TicketType/${args.tickettype_id}`, { includedetails: true }, {
672
+ enabled: true,
673
+ ttl: TTL.TICKET_TYPES,
674
+ keyPrefix: `tickettype:${args.tickettype_id}:details`,
675
+ });
676
+ // Standard fields always required on create.
677
+ const STANDARD_REQUIRED = ['summary', 'client_id', 'tickettype_id'];
678
+ // Build the list of required fields from the tickettype schema.
679
+ const schemaFields = Array.isArray(tickettype.fields)
680
+ ? tickettype.fields
681
+ : [];
682
+ const requiredFromSchema = [];
683
+ const allSchemaFieldNames = [];
684
+ for (const f of schemaFields) {
685
+ // Field name may be on fieldinfo or directly on the field entry.
686
+ const info = f.fieldinfo ?? f;
687
+ const name = info.name ?? '';
688
+ const label = info.label ?? name;
689
+ const isCustom = info.custom ?? false;
690
+ if (name) {
691
+ allSchemaFieldNames.push(name);
692
+ }
693
+ // A field is required if mandatory===true OR technew===3.
694
+ const isRequired = info.mandatory === true || info.technew === 3;
695
+ if (isRequired && name) {
696
+ requiredFromSchema.push({ name, label, custom: isCustom });
697
+ }
698
+ }
699
+ // Combine standard required fields with schema-derived required fields,
700
+ // deduplicating by name.
701
+ const allRequiredFields = [
702
+ ...STANDARD_REQUIRED.map((n) => ({ name: n, label: n, custom: false })),
703
+ ...requiredFromSchema.filter((f) => !STANDARD_REQUIRED.includes(f.name)),
704
+ ];
705
+ const providedKeys = new Set(Object.keys(args.fields));
706
+ // Missing: required fields not in the provided payload.
707
+ const missingRequired = allRequiredFields
708
+ .filter((f) => !providedKeys.has(f.name))
709
+ .map((f) => f.name);
710
+ // Unknown: provided fields not in the standard set and not in the schema.
711
+ const knownFieldNames = new Set([
712
+ ...STANDARD_REQUIRED,
713
+ ...allSchemaFieldNames,
714
+ // Common optional standard fields that are always valid.
715
+ 'details_html', 'category_1', 'priority_id', 'status_id', 'source',
716
+ 'agent_id', 'team_id', 'site_id', 'user_id', 'parent_id',
717
+ 'dateoccurred', 'startdate', 'customfields', 'estimate', 'estimatedays',
718
+ 'milestone_id', 'asset_id', 'asset_ids', 'assets',
719
+ ]);
720
+ const unknownFields = [...providedKeys].filter((k) => !knownFieldNames.has(k));
721
+ const valid = missingRequired.length === 0 && unknownFields.length === 0;
722
+ const result = {
723
+ valid,
724
+ missing_required: missingRequired,
725
+ unknown_fields: unknownFields,
726
+ required_fields: allRequiredFields,
727
+ tickettype_id: args.tickettype_id,
728
+ tickettype_name: tickettype.name ?? '',
729
+ };
730
+ const formatOpts = args.format_options ?? { format: 'standard' };
731
+ return formatResponse(result, formatOpts);
732
+ }
604
733
  // =============================================================================
605
734
  // Tool Definitions
606
735
  // =============================================================================
@@ -624,13 +753,13 @@ export const searchTicketsTool = {
624
753
  };
625
754
  export const createTicketTool = {
626
755
  name: 'create_halopsa_ticket',
627
- description: 'Create a new HaloPSA ticket. Required: summary, tickettype_id, client_id. Body is sent as POST /Tickets with array wrapper [{ ... }] (HaloPSA convention). Invalidates ticket cache. Estimate auto-computed from startdate->targetdate (5x8 business hrs) if omitted; pass `estimate` to override.',
756
+ description: 'Create a new HaloPSA ticket. Required: summary, tickettype_id, client_id. Body is sent as POST /Tickets with array wrapper [{ ... }] (HaloPSA convention). Invalidates ticket cache. Estimate auto-computed from startdate->targetdate (5x8 business hrs) if omitted; pass `estimate` to override. Pass asset_id/asset_ids to link assets on creation.',
628
757
  schema: CreateTicketArgsSchema,
629
758
  handler: createTicket,
630
759
  };
631
760
  export const updateTicketTool = {
632
761
  name: 'update_halopsa_ticket',
633
- description: 'Update an existing HaloPSA ticket. Performs read-before-write merge (Halo POST is REPLACE not PATCH). Update by explicit id only — no force/dedup flags (tickets are append-only events). Invalidates ticket cache.',
762
+ description: 'Update an existing HaloPSA ticket. Performs read-before-write merge (Halo POST is REPLACE not PATCH). Update by explicit id only — no force/dedup flags (tickets are append-only events). Invalidates ticket cache. Pass asset_id/asset_ids to link assets on update.',
634
763
  schema: UpdateTicketArgsSchema,
635
764
  handler: updateTicket,
636
765
  };
@@ -646,4 +775,10 @@ export const deleteTicketTool = {
646
775
  schema: DeleteTicketArgsSchema,
647
776
  handler: deleteTicket,
648
777
  };
778
+ export const validateCreateTicketTool = {
779
+ name: 'validate_halopsa_create_ticket',
780
+ description: 'Pure local dry-run validation of a proposed ticket payload against the cached tickettype schema. No Halo write is performed. Returns {valid, missing_required[], unknown_fields[], required_fields[]} so callers can catch missing mandatory fields before calling create_halopsa_ticket. Fetches tickettype via GET /TicketType/{id}?includedetails=true (cached 1h). Use list_halopsa_tickettypes to look up tickettype_id values.',
781
+ schema: ValidateCreateTicketArgsSchema,
782
+ handler: validateCreateTicket,
783
+ };
649
784
  //# sourceMappingURL=tickets.js.map