@wilnertech/halopsa-mcp-server 1.2.0 → 1.4.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 (56) hide show
  1. package/README.md +43 -2
  2. package/dist/cache/memory-cache.d.ts +3 -0
  3. package/dist/cache/memory-cache.d.ts.map +1 -1
  4. package/dist/cache/memory-cache.js +4 -0
  5. package/dist/cache/memory-cache.js.map +1 -1
  6. package/dist/tools/batch-operations.d.ts +47 -0
  7. package/dist/tools/batch-operations.d.ts.map +1 -1
  8. package/dist/tools/batch-operations.js +211 -68
  9. package/dist/tools/batch-operations.js.map +1 -1
  10. package/dist/tools/budgets.d.ts +130 -0
  11. package/dist/tools/budgets.d.ts.map +1 -0
  12. package/dist/tools/budgets.js +277 -0
  13. package/dist/tools/budgets.js.map +1 -0
  14. package/dist/tools/milestones.d.ts +129 -0
  15. package/dist/tools/milestones.d.ts.map +1 -0
  16. package/dist/tools/milestones.js +281 -0
  17. package/dist/tools/milestones.js.map +1 -0
  18. package/dist/tools/registrations.d.ts +6 -2
  19. package/dist/tools/registrations.d.ts.map +1 -1
  20. package/dist/tools/registrations.js +39 -5
  21. package/dist/tools/registrations.js.map +1 -1
  22. package/dist/tools/ticket-links.d.ts +41 -0
  23. package/dist/tools/ticket-links.d.ts.map +1 -0
  24. package/dist/tools/ticket-links.js +84 -0
  25. package/dist/tools/ticket-links.js.map +1 -0
  26. package/dist/tools/tickets.d.ts +2 -0
  27. package/dist/tools/tickets.d.ts.map +1 -1
  28. package/dist/tools/tickets.js +59 -2
  29. package/dist/tools/tickets.js.map +1 -1
  30. package/dist/tools/workflows.d.ts +125 -0
  31. package/dist/tools/workflows.d.ts.map +1 -0
  32. package/dist/tools/workflows.js +355 -0
  33. package/dist/tools/workflows.js.map +1 -0
  34. package/dist/types/budgets.d.ts +45 -0
  35. package/dist/types/budgets.d.ts.map +1 -0
  36. package/dist/types/budgets.js +12 -0
  37. package/dist/types/budgets.js.map +1 -0
  38. package/dist/types/milestones.d.ts +65 -0
  39. package/dist/types/milestones.d.ts.map +1 -0
  40. package/dist/types/milestones.js +14 -0
  41. package/dist/types/milestones.js.map +1 -0
  42. package/dist/types/tickets.d.ts +4 -0
  43. package/dist/types/tickets.d.ts.map +1 -1
  44. package/dist/types/workflows.d.ts +77 -0
  45. package/dist/types/workflows.d.ts.map +1 -0
  46. package/dist/types/workflows.js +11 -0
  47. package/dist/types/workflows.js.map +1 -0
  48. package/dist/utils/concurrency.d.ts +42 -0
  49. package/dist/utils/concurrency.d.ts.map +1 -0
  50. package/dist/utils/concurrency.js +82 -0
  51. package/dist/utils/concurrency.js.map +1 -0
  52. package/dist/utils/formatter.d.ts +1 -1
  53. package/dist/utils/formatter.d.ts.map +1 -1
  54. package/dist/utils/formatter.js +30 -3
  55. package/dist/utils/formatter.js.map +1 -1
  56. package/package.json +1 -1
@@ -0,0 +1,130 @@
1
+ /**
2
+ * HaloPSA Budget Management tools.
3
+ *
4
+ * Covers two domains:
5
+ * 1. BudgetType — top-level reference CRUD (/BudgetType).
6
+ * 2. Per-ticket budgets — nested ticket.budgets[] array, managed via
7
+ * read-before-write POST /Tickets (Halo REPLACE, not PATCH semantics).
8
+ *
9
+ * Safety:
10
+ * - delete_halopsa_budget_type applies a two-factor OVERRIDE gate (same
11
+ * pattern as delete_halopsa_ticket): reason >= 5 chars required;
12
+ * confirm_destructive + "OVERRIDE" substring to bypass a soft refusal.
13
+ * - set_halopsa_ticket_budget REPLACES all budgets on the ticket; callers
14
+ * should fetch current budgets first if they need a partial update.
15
+ */
16
+ import { z } from 'zod';
17
+ import type { HaloPSAAPIClient } from '../api/client.js';
18
+ import type { ZodToolDefinition } from './registry.js';
19
+ export declare const ListBudgetTypesArgsSchema: z.ZodObject<{
20
+ format_options: z.ZodOptional<z.ZodObject<{
21
+ format: z.ZodOptional<z.ZodEnum<{
22
+ compact: "compact";
23
+ standard: "standard";
24
+ detailed: "detailed";
25
+ }>>;
26
+ fields: z.ZodOptional<z.ZodArray<z.ZodString>>;
27
+ omit_empty: z.ZodOptional<z.ZodBoolean>;
28
+ }, z.core.$strip>>;
29
+ }, z.core.$strip>;
30
+ export declare const CreateBudgetTypeArgsSchema: z.ZodObject<{
31
+ name: z.ZodString;
32
+ defaultrate: z.ZodOptional<z.ZodNumber>;
33
+ chargerate: z.ZodOptional<z.ZodNumber>;
34
+ format_options: z.ZodOptional<z.ZodObject<{
35
+ format: z.ZodOptional<z.ZodEnum<{
36
+ compact: "compact";
37
+ standard: "standard";
38
+ detailed: "detailed";
39
+ }>>;
40
+ fields: z.ZodOptional<z.ZodArray<z.ZodString>>;
41
+ omit_empty: z.ZodOptional<z.ZodBoolean>;
42
+ }, z.core.$strip>>;
43
+ }, z.core.$strip>;
44
+ export declare const DeleteBudgetTypeArgsSchema: z.ZodObject<{
45
+ budget_type_id: z.ZodNumber;
46
+ reason: z.ZodString;
47
+ confirm_destructive: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
48
+ format_options: z.ZodOptional<z.ZodObject<{
49
+ format: z.ZodOptional<z.ZodEnum<{
50
+ compact: "compact";
51
+ standard: "standard";
52
+ detailed: "detailed";
53
+ }>>;
54
+ fields: z.ZodOptional<z.ZodArray<z.ZodString>>;
55
+ omit_empty: z.ZodOptional<z.ZodBoolean>;
56
+ }, z.core.$strip>>;
57
+ }, z.core.$strip>;
58
+ export declare const GetTicketBudgetArgsSchema: z.ZodObject<{
59
+ ticket_id: z.ZodNumber;
60
+ format_options: z.ZodOptional<z.ZodObject<{
61
+ format: z.ZodOptional<z.ZodEnum<{
62
+ compact: "compact";
63
+ standard: "standard";
64
+ detailed: "detailed";
65
+ }>>;
66
+ fields: z.ZodOptional<z.ZodArray<z.ZodString>>;
67
+ omit_empty: z.ZodOptional<z.ZodBoolean>;
68
+ }, z.core.$strip>>;
69
+ }, z.core.$strip>;
70
+ export declare const SetTicketBudgetArgsSchema: z.ZodObject<{
71
+ ticket_id: z.ZodNumber;
72
+ budgets: z.ZodArray<z.ZodObject<{
73
+ budgettype_id: z.ZodNumber;
74
+ hours: z.ZodOptional<z.ZodNumber>;
75
+ days: z.ZodOptional<z.ZodNumber>;
76
+ rate: z.ZodOptional<z.ZodNumber>;
77
+ }, z.core.$strip>>;
78
+ format_options: z.ZodOptional<z.ZodObject<{
79
+ format: z.ZodOptional<z.ZodEnum<{
80
+ compact: "compact";
81
+ standard: "standard";
82
+ detailed: "detailed";
83
+ }>>;
84
+ fields: z.ZodOptional<z.ZodArray<z.ZodString>>;
85
+ omit_empty: z.ZodOptional<z.ZodBoolean>;
86
+ }, z.core.$strip>>;
87
+ }, z.core.$strip>;
88
+ /**
89
+ * List all budget types (reference data).
90
+ * Cached 1 hour. Compact response returns {id, name, defaultrate}.
91
+ */
92
+ export declare function listBudgetTypes(client: HaloPSAAPIClient, args: z.infer<typeof ListBudgetTypesArgsSchema>): Promise<string>;
93
+ /**
94
+ * Create a new budget type.
95
+ * POST /BudgetType with array-wrapped body (Halo convention).
96
+ * Invalidates budget-types cache.
97
+ */
98
+ export declare function createBudgetType(client: HaloPSAAPIClient, args: z.infer<typeof CreateBudgetTypeArgsSchema>): Promise<string>;
99
+ /**
100
+ * Delete a budget type by ID.
101
+ *
102
+ * Safety gate (adapted from deleteTicket):
103
+ * - Reason is always required (min 5 chars; validated by Zod).
104
+ * - A soft refusal is issued by default with a warning that dependent tickets
105
+ * cannot be automatically checked (Halo does not support budgettype_id
106
+ * filtering on /Tickets). The caller is instructed to verify manually.
107
+ * - To bypass: confirm_destructive: true AND reason includes "OVERRIDE".
108
+ * - 404 on GET is idempotent (already_absent: true).
109
+ */
110
+ export declare function deleteBudgetType(client: HaloPSAAPIClient, args: z.infer<typeof DeleteBudgetTypeArgsSchema>): Promise<string>;
111
+ /**
112
+ * Get all budget entries for a specific ticket.
113
+ * Fetches GET /Tickets/{id}?includedetails=true and returns ticket.budgets[].
114
+ * Cache key 'ticket-budgets:{id}' TTL 1 min.
115
+ */
116
+ export declare function getTicketBudget(client: HaloPSAAPIClient, args: z.infer<typeof GetTicketBudgetArgsSchema>): Promise<string>;
117
+ /**
118
+ * Replace the budget array on a ticket.
119
+ *
120
+ * Read-before-write merge: fetches the existing ticket, spreads it into the
121
+ * POST body, and replaces the budgets[] array entirely (Halo REPLACE semantics).
122
+ * Returns the updated ticket's budgets[] after strip.
123
+ */
124
+ export declare function setTicketBudget(client: HaloPSAAPIClient, args: z.infer<typeof SetTicketBudgetArgsSchema>): Promise<string>;
125
+ export declare const listBudgetTypesTool: ZodToolDefinition;
126
+ export declare const createBudgetTypeTool: ZodToolDefinition;
127
+ export declare const deleteBudgetTypeTool: ZodToolDefinition;
128
+ export declare const getTicketBudgetTool: ZodToolDefinition;
129
+ export declare const setTicketBudgetTool: ZodToolDefinition;
130
+ //# sourceMappingURL=budgets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"budgets.d.ts","sourceRoot":"","sources":["../../src/tools/budgets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;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;AAWvD,eAAO,MAAM,yBAAyB;;;;;;;;;;iBAEpC,CAAC;AAEH,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;iBAQrC,CAAC;AAEH,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;iBAYrC,CAAC;AAEH,eAAO,MAAM,yBAAyB;;;;;;;;;;;iBAIpC,CAAC;AAcH,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;iBASpC,CAAC;AA8BH;;;GAGG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,GAC9C,OAAO,CAAC,MAAM,CAAC,CAyBjB;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,GAC/C,OAAO,CAAC,MAAM,CAAC,CAgBjB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,GAC/C,OAAO,CAAC,MAAM,CAAC,CAkEjB;AAED;;;;GAIG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,GAC9C,OAAO,CAAC,MAAM,CAAC,CAejB;AAED;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,GAC9C,OAAO,CAAC,MAAM,CAAC,CAoCjB;AAMD,eAAO,MAAM,mBAAmB,EAAE,iBAMjC,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,iBAMlC,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,iBAQlC,CAAC;AAEF,eAAO,MAAM,mBAAmB,EAAE,iBAMjC,CAAC;AAEF,eAAO,MAAM,mBAAmB,EAAE,iBAQjC,CAAC"}
@@ -0,0 +1,277 @@
1
+ /**
2
+ * HaloPSA Budget Management tools.
3
+ *
4
+ * Covers two domains:
5
+ * 1. BudgetType — top-level reference CRUD (/BudgetType).
6
+ * 2. Per-ticket budgets — nested ticket.budgets[] array, managed via
7
+ * read-before-write POST /Tickets (Halo REPLACE, not PATCH semantics).
8
+ *
9
+ * Safety:
10
+ * - delete_halopsa_budget_type applies a two-factor OVERRIDE gate (same
11
+ * pattern as delete_halopsa_ticket): reason >= 5 chars required;
12
+ * confirm_destructive + "OVERRIDE" substring to bypass a soft refusal.
13
+ * - set_halopsa_ticket_budget REPLACES all budgets on the ticket; callers
14
+ * should fetch current budgets first if they need a partial update.
15
+ */
16
+ import { z } from 'zod';
17
+ import { formatResponse, stripWriteResponse } from '../utils/formatter.js';
18
+ import { TTL } from '../cache/memory-cache.js';
19
+ import { FormatOptionsSchema } from '../schemas/common.js';
20
+ // =============================================================================
21
+ // Schemas
22
+ // =============================================================================
23
+ export const ListBudgetTypesArgsSchema = z.object({
24
+ format_options: FormatOptionsSchema,
25
+ });
26
+ export const CreateBudgetTypeArgsSchema = z.object({
27
+ name: z.string().min(1)
28
+ .describe('Budget type name (required)'),
29
+ defaultrate: z.number().optional()
30
+ .describe('Default hourly rate for this budget type'),
31
+ chargerate: z.number().optional()
32
+ .describe('Charge rate override for this budget type'),
33
+ format_options: FormatOptionsSchema,
34
+ });
35
+ export const DeleteBudgetTypeArgsSchema = z.object({
36
+ budget_type_id: z.number().int()
37
+ .describe('HaloPSA BudgetType ID to delete'),
38
+ reason: z.string().min(5).describe('REQUIRED audit-trail reason (min 5 chars). To bypass the soft safety refusal, ' +
39
+ 'the reason MUST include the substring "OVERRIDE" (case-sensitive) AND ' +
40
+ 'confirm_destructive must be true.'),
41
+ confirm_destructive: z.boolean().optional().default(false).describe('Set true AND include "OVERRIDE" in reason to bypass the soft safety refusal. Default false.'),
42
+ format_options: FormatOptionsSchema,
43
+ });
44
+ export const GetTicketBudgetArgsSchema = z.object({
45
+ ticket_id: z.number().int()
46
+ .describe('HaloPSA ticket ID'),
47
+ format_options: FormatOptionsSchema,
48
+ });
49
+ /** Input shape for a single budget entry in set_halopsa_ticket_budget. */
50
+ const BudgetEntryInputSchema = z.object({
51
+ budgettype_id: z.number().int()
52
+ .describe('Budget type ID — see list_halopsa_budget_types'),
53
+ hours: z.number().optional()
54
+ .describe('Budgeted hours for this type'),
55
+ days: z.number().optional()
56
+ .describe('Budgeted days for this type'),
57
+ rate: z.number().optional()
58
+ .describe('Rate override for this budget entry'),
59
+ });
60
+ export const SetTicketBudgetArgsSchema = z.object({
61
+ ticket_id: z.number().int()
62
+ .describe('HaloPSA ticket ID'),
63
+ budgets: z.array(BudgetEntryInputSchema).min(1)
64
+ .describe('Full replacement budget array. REPLACES all existing budgets on the ticket. ' +
65
+ 'Each entry requires budgettype_id; hours/days/rate are optional.'),
66
+ format_options: FormatOptionsSchema,
67
+ });
68
+ // =============================================================================
69
+ // Helpers
70
+ // =============================================================================
71
+ /** Compact fields for budget type list responses. */
72
+ const BUDGET_TYPE_COMPACT_FIELDS = ['id', 'name', 'defaultrate'];
73
+ function resolveBudgetTypeListFormat(input) {
74
+ const opts = { ...(input || {}) };
75
+ if (!opts.format) {
76
+ opts.format = 'standard';
77
+ opts.fields = opts.fields ?? BUDGET_TYPE_COMPACT_FIELDS;
78
+ }
79
+ return opts;
80
+ }
81
+ /**
82
+ * Unwrap a BudgetType write response — Halo POST returns either a single
83
+ * object or a single-element array depending on tenant config.
84
+ */
85
+ function unwrapBudgetTypeWrite(response) {
86
+ return Array.isArray(response) ? response[0] : response;
87
+ }
88
+ // =============================================================================
89
+ // Implementations
90
+ // =============================================================================
91
+ /**
92
+ * List all budget types (reference data).
93
+ * Cached 1 hour. Compact response returns {id, name, defaultrate}.
94
+ */
95
+ export async function listBudgetTypes(client, args) {
96
+ const formatOpts = resolveBudgetTypeListFormat(args.format_options);
97
+ const response = await client.getCached('/BudgetType', { count: 100 }, {
98
+ enabled: true,
99
+ ttl: TTL.BUDGET_TYPES,
100
+ keyPrefix: 'budget-types:all',
101
+ });
102
+ let budgetTypes;
103
+ let recordCount;
104
+ if (Array.isArray(response)) {
105
+ budgetTypes = response;
106
+ recordCount = response.length;
107
+ }
108
+ else {
109
+ budgetTypes = response.budget_types ?? [];
110
+ recordCount = response.record_count ?? budgetTypes.length;
111
+ }
112
+ return formatResponse(budgetTypes, formatOpts, { record_count: recordCount });
113
+ }
114
+ /**
115
+ * Create a new budget type.
116
+ * POST /BudgetType with array-wrapped body (Halo convention).
117
+ * Invalidates budget-types cache.
118
+ */
119
+ export async function createBudgetType(client, args) {
120
+ const payload = {
121
+ name: args.name,
122
+ };
123
+ if (args.defaultrate !== undefined)
124
+ payload.defaultrate = args.defaultrate;
125
+ if (args.chargerate !== undefined)
126
+ payload.chargerate = args.chargerate;
127
+ const created = unwrapBudgetTypeWrite(await client.post('/BudgetType', [payload]));
128
+ client.invalidateCache('budget-types:*');
129
+ const format = args.format_options?.format ?? 'compact';
130
+ // Budget types have no heavy nested objects — passthrough (no entity-specific strip needed).
131
+ return formatResponse(created, { format });
132
+ }
133
+ /**
134
+ * Delete a budget type by ID.
135
+ *
136
+ * Safety gate (adapted from deleteTicket):
137
+ * - Reason is always required (min 5 chars; validated by Zod).
138
+ * - A soft refusal is issued by default with a warning that dependent tickets
139
+ * cannot be automatically checked (Halo does not support budgettype_id
140
+ * filtering on /Tickets). The caller is instructed to verify manually.
141
+ * - To bypass: confirm_destructive: true AND reason includes "OVERRIDE".
142
+ * - 404 on GET is idempotent (already_absent: true).
143
+ */
144
+ export async function deleteBudgetType(client, args) {
145
+ // Step 1: Verify existence. 404 is idempotent.
146
+ let existing;
147
+ try {
148
+ existing = await client.get(`/BudgetType/${args.budget_type_id}`);
149
+ }
150
+ catch (error) {
151
+ const status = error?.response?.status;
152
+ if (status === 404) {
153
+ return JSON.stringify({ already_absent: true, deleted_id: args.budget_type_id }, null, 2);
154
+ }
155
+ throw error;
156
+ }
157
+ // Step 2: Accumulate violations. Halo does not expose a query filter for
158
+ // budgettype_id on /Tickets, so we cannot automatically probe for open-ticket
159
+ // references. Issue a standing warning instead.
160
+ const violations = [];
161
+ violations.push('dependency-check-unavailable: Halo /Tickets does not support budgettype_id filtering. ' +
162
+ `Caller must verify that no open tickets reference budgettype_id=${args.budget_type_id} ` +
163
+ `(name: "${existing.name}") before deletion.`);
164
+ // Step 3: Decision matrix.
165
+ const overrideProvided = args.confirm_destructive === true && args.reason.includes('OVERRIDE');
166
+ if (!overrideProvided) {
167
+ return JSON.stringify({
168
+ deleted: false,
169
+ refused: true,
170
+ budget_type_id: args.budget_type_id,
171
+ budget_type_name: existing.name,
172
+ violations,
173
+ remediation: "Pass confirm_destructive: true AND include 'OVERRIDE' (case-sensitive) in the reason to proceed. " +
174
+ 'Manually confirm no open tickets reference this budget type before overriding.',
175
+ }, null, 2);
176
+ }
177
+ // Step 4: Execute DELETE.
178
+ await client.delete(`/BudgetType/${args.budget_type_id}?reason=${encodeURIComponent(args.reason)}`);
179
+ // Step 5: Cache invalidation.
180
+ client.invalidateCache('budget-types:*');
181
+ // Step 6: Structured success.
182
+ return JSON.stringify({
183
+ deleted: true,
184
+ budget_type_id: args.budget_type_id,
185
+ budget_type_name: existing.name,
186
+ reason: args.reason,
187
+ override_used: true,
188
+ safety_violations_bypassed: violations,
189
+ }, null, 2);
190
+ }
191
+ /**
192
+ * Get all budget entries for a specific ticket.
193
+ * Fetches GET /Tickets/{id}?includedetails=true and returns ticket.budgets[].
194
+ * Cache key 'ticket-budgets:{id}' TTL 1 min.
195
+ */
196
+ export async function getTicketBudget(client, args) {
197
+ const formatOpts = args.format_options || { format: 'detailed' };
198
+ const ticket = await client.getCached(`/Tickets/${args.ticket_id}`, { includedetails: true }, {
199
+ enabled: true,
200
+ ttl: TTL.TICKET_LIST,
201
+ keyPrefix: `ticket-budgets:${args.ticket_id}`,
202
+ });
203
+ const budgets = ticket.budgets ?? [];
204
+ return formatResponse(budgets, formatOpts, { record_count: budgets.length, ticket_id: args.ticket_id });
205
+ }
206
+ /**
207
+ * Replace the budget array on a ticket.
208
+ *
209
+ * Read-before-write merge: fetches the existing ticket, spreads it into the
210
+ * POST body, and replaces the budgets[] array entirely (Halo REPLACE semantics).
211
+ * Returns the updated ticket's budgets[] after strip.
212
+ */
213
+ export async function setTicketBudget(client, args) {
214
+ // Read the baseline ticket (no cache — must be current).
215
+ const existing = await client.get(`/Tickets/${args.ticket_id}`, { includedetails: true });
216
+ const newBudgets = args.budgets.map((b) => {
217
+ const entry = { budgettype_id: b.budgettype_id };
218
+ if (b.hours !== undefined)
219
+ entry.hours = b.hours;
220
+ if (b.days !== undefined)
221
+ entry.days = b.days;
222
+ if (b.rate !== undefined)
223
+ entry.rate = b.rate;
224
+ return entry;
225
+ });
226
+ const mergedPayload = { ...existing, budgets: newBudgets, id: args.ticket_id };
227
+ // POST /Tickets with array wrapper (Halo convention).
228
+ const rawResponse = await client.post('/Tickets', [mergedPayload]);
229
+ const updated = Array.isArray(rawResponse) ? rawResponse[0] : rawResponse;
230
+ // Cache invalidation.
231
+ client.invalidateCache('tickets:*');
232
+ client.invalidateCache(`ticket:${args.ticket_id}*`);
233
+ client.invalidateCache(`ticket-budgets:${args.ticket_id}*`);
234
+ const format = args.format_options?.format ?? 'compact';
235
+ const stripped = stripWriteResponse(updated, format, 'ticket');
236
+ // Return only the budgets sub-array from the updated ticket for clarity.
237
+ const returnedBudgets = stripped['budgets'] ?? updated.budgets ?? [];
238
+ return formatResponse(returnedBudgets, { format }, { ticket_id: args.ticket_id });
239
+ }
240
+ // =============================================================================
241
+ // Tool Definitions
242
+ // =============================================================================
243
+ export const listBudgetTypesTool = {
244
+ name: 'list_halopsa_budget_types',
245
+ description: 'List all HaloPSA budget types (reference data). Compact response returns {id, name, defaultrate}. Cached 1 hour.',
246
+ schema: ListBudgetTypesArgsSchema,
247
+ handler: listBudgetTypes,
248
+ };
249
+ export const createBudgetTypeTool = {
250
+ name: 'create_halopsa_budget_type',
251
+ description: 'Create a new HaloPSA budget type. POST /BudgetType with array-wrapped body (Halo convention). Invalidates budget-types cache.',
252
+ schema: CreateBudgetTypeArgsSchema,
253
+ handler: createBudgetType,
254
+ };
255
+ export const deleteBudgetTypeTool = {
256
+ name: 'delete_halopsa_budget_type',
257
+ description: 'Delete a HaloPSA budget type by ID. Issues a soft safety refusal by default because Halo does not support ' +
258
+ 'automatic dependency checking via /Tickets?budgettype_id=X. Pass confirm_destructive: true AND include ' +
259
+ '"OVERRIDE" (case-sensitive) in reason to proceed after manual verification. 404 returns already_absent: true (idempotent).',
260
+ schema: DeleteBudgetTypeArgsSchema,
261
+ handler: deleteBudgetType,
262
+ };
263
+ export const getTicketBudgetTool = {
264
+ name: 'get_halopsa_ticket_budget',
265
+ description: 'Get the budget entries for a HaloPSA ticket. Returns ticket.budgets[] (or [] if none set). Cached 1 min.',
266
+ schema: GetTicketBudgetArgsSchema,
267
+ handler: getTicketBudget,
268
+ };
269
+ export const setTicketBudgetTool = {
270
+ name: 'set_halopsa_ticket_budget',
271
+ description: 'Replace the budget array on a HaloPSA ticket. Performs read-before-write merge (Halo POST is REPLACE not PATCH). ' +
272
+ 'The supplied budgets[] array FULLY REPLACES any existing budgets. Each entry requires budgettype_id; ' +
273
+ 'hours/days/rate are optional. Returns the updated ticket budgets field.',
274
+ schema: SetTicketBudgetArgsSchema,
275
+ handler: setTicketBudget,
276
+ };
277
+ //# sourceMappingURL=budgets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"budgets.js","sourceRoot":"","sources":["../../src/tools/budgets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAsB,MAAM,uBAAuB,CAAC;AAC/F,OAAO,EAAE,GAAG,EAAE,MAAM,0BAA0B,CAAC;AAC/C,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAE3D,gFAAgF;AAChF,UAAU;AACV,gFAAgF;AAEhF,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC,MAAM,CAAC;IAChD,cAAc,EAAE,mBAAmB;CACpC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,CAAC,MAAM,CAAC;IACjD,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;SACpB,QAAQ,CAAC,6BAA6B,CAAC;IAC1C,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;SAC/B,QAAQ,CAAC,0CAA0C,CAAC;IACvD,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;SAC9B,QAAQ,CAAC,2CAA2C,CAAC;IACxD,cAAc,EAAE,mBAAmB;CACpC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,CAAC,MAAM,CAAC;IACjD,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE;SAC7B,QAAQ,CAAC,iCAAiC,CAAC;IAC9C,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAChC,gFAAgF;QAChF,wEAAwE;QACxE,mCAAmC,CACpC;IACD,mBAAmB,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,QAAQ,CACjE,6FAA6F,CAC9F;IACD,cAAc,EAAE,mBAAmB;CACpC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC,MAAM,CAAC;IAChD,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE;SACxB,QAAQ,CAAC,mBAAmB,CAAC;IAChC,cAAc,EAAE,mBAAmB;CACpC,CAAC,CAAC;AAEH,0EAA0E;AAC1E,MAAM,sBAAsB,GAAG,CAAC,CAAC,MAAM,CAAC;IACtC,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE;SAC5B,QAAQ,CAAC,gDAAgD,CAAC;IAC7D,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;SACzB,QAAQ,CAAC,8BAA8B,CAAC;IAC3C,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;SACxB,QAAQ,CAAC,6BAA6B,CAAC;IAC1C,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;SACxB,QAAQ,CAAC,qCAAqC,CAAC;CACnD,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC,MAAM,CAAC;IAChD,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE;SACxB,QAAQ,CAAC,mBAAmB,CAAC;IAChC,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;SAC5C,QAAQ,CACP,8EAA8E;QAC9E,kEAAkE,CACnE;IACH,cAAc,EAAE,mBAAmB;CACpC,CAAC,CAAC;AAEH,gFAAgF;AAChF,UAAU;AACV,gFAAgF;AAEhF,qDAAqD;AACrD,MAAM,0BAA0B,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC;AAEjE,SAAS,2BAA2B,CAAC,KAAgC;IACnE,MAAM,IAAI,GAAkB,EAAE,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC,EAAE,CAAC;IACjD,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;QACjB,IAAI,CAAC,MAAM,GAAG,UAAU,CAAC;QACzB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,0BAA0B,CAAC;IAC1D,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,SAAS,qBAAqB,CAAI,QAAiB;IACjD,OAAO,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;AAC1D,CAAC;AAED,gFAAgF;AAChF,kBAAkB;AAClB,gFAAgF;AAEhF;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,MAAwB,EACxB,IAA+C;IAE/C,MAAM,UAAU,GAAG,2BAA2B,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAEpE,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,kBAAkB;KAC9B,CACF,CAAC;IAEF,IAAI,WAAgC,CAAC;IACrC,IAAI,WAAmB,CAAC;IAExB,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5B,WAAW,GAAG,QAAQ,CAAC;QACvB,WAAW,GAAG,QAAQ,CAAC,MAAM,CAAC;IAChC,CAAC;SAAM,CAAC;QACN,WAAW,GAAG,QAAQ,CAAC,YAAY,IAAI,EAAE,CAAC;QAC1C,WAAW,GAAG,QAAQ,CAAC,YAAY,IAAI,WAAW,CAAC,MAAM,CAAC;IAC5D,CAAC;IAED,OAAO,cAAc,CAAC,WAAW,EAAE,UAAU,EAAE,EAAE,YAAY,EAAE,WAAW,EAAE,CAAC,CAAC;AAChF,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,MAAwB,EACxB,IAAgD;IAEhD,MAAM,OAAO,GAA4B;QACvC,IAAI,EAAE,IAAI,CAAC,IAAI;KAChB,CAAC;IACF,IAAI,IAAI,CAAC,WAAW,KAAK,SAAS;QAAE,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC;IAC3E,IAAI,IAAI,CAAC,UAAU,KAAK,SAAS;QAAE,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC;IAExE,MAAM,OAAO,GAAG,qBAAqB,CACnC,MAAM,MAAM,CAAC,IAAI,CAA0C,aAAa,EAAE,CAAC,OAAO,CAAC,CAAC,CACrF,CAAC;IAEF,MAAM,CAAC,eAAe,CAAC,gBAAgB,CAAC,CAAC;IAEzC,MAAM,MAAM,GAAG,IAAI,CAAC,cAAc,EAAE,MAAM,IAAI,SAAS,CAAC;IACxD,6FAA6F;IAC7F,OAAO,cAAc,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;AAC7C,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,MAAwB,EACxB,IAAgD;IAEhD,+CAA+C;IAC/C,IAAI,QAA2B,CAAC;IAChC,IAAI,CAAC;QACH,QAAQ,GAAG,MAAM,MAAM,CAAC,GAAG,CAAoB,eAAe,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC;IACvF,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACxB,MAAM,MAAM,GAAI,KAA4C,EAAE,QAAQ,EAAE,MAAM,CAAC;QAC/E,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;YACnB,OAAO,IAAI,CAAC,SAAS,CAAC,EAAE,cAAc,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,CAAC,cAAc,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAC5F,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;IAED,yEAAyE;IACzE,8EAA8E;IAC9E,gDAAgD;IAChD,MAAM,UAAU,GAAa,EAAE,CAAC;IAEhC,UAAU,CAAC,IAAI,CACb,wFAAwF;QACxF,mEAAmE,IAAI,CAAC,cAAc,GAAG;QACzF,WAAW,QAAQ,CAAC,IAAI,qBAAqB,CAC9C,CAAC;IAEF,2BAA2B;IAC3B,MAAM,gBAAgB,GACpB,IAAI,CAAC,mBAAmB,KAAK,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;IAExE,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACtB,OAAO,IAAI,CAAC,SAAS,CACnB;YACE,OAAO,EAAE,KAAK;YACd,OAAO,EAAE,IAAI;YACb,cAAc,EAAE,IAAI,CAAC,cAAc;YACnC,gBAAgB,EAAE,QAAQ,CAAC,IAAI;YAC/B,UAAU;YACV,WAAW,EACT,mGAAmG;gBACnG,gFAAgF;SACnF,EACD,IAAI,EACJ,CAAC,CACF,CAAC;IACJ,CAAC;IAED,0BAA0B;IAC1B,MAAM,MAAM,CAAC,MAAM,CACjB,eAAe,IAAI,CAAC,cAAc,WAAW,kBAAkB,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAC/E,CAAC;IAEF,8BAA8B;IAC9B,MAAM,CAAC,eAAe,CAAC,gBAAgB,CAAC,CAAC;IAEzC,8BAA8B;IAC9B,OAAO,IAAI,CAAC,SAAS,CACnB;QACE,OAAO,EAAE,IAAI;QACb,cAAc,EAAE,IAAI,CAAC,cAAc;QACnC,gBAAgB,EAAE,QAAQ,CAAC,IAAI;QAC/B,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,aAAa,EAAE,IAAI;QACnB,0BAA0B,EAAE,UAAU;KACvC,EACD,IAAI,EACJ,CAAC,CACF,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,MAAwB,EACxB,IAA+C;IAE/C,MAAM,UAAU,GAAkB,IAAI,CAAC,cAAc,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IAEhF,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,SAAS,CACnC,YAAY,IAAI,CAAC,SAAS,EAAE,EAC5B,EAAE,cAAc,EAAE,IAAI,EAAE,EACxB;QACE,OAAO,EAAE,IAAI;QACb,GAAG,EAAE,GAAG,CAAC,WAAW;QACpB,SAAS,EAAE,kBAAkB,IAAI,CAAC,SAAS,EAAE;KAC9C,CACF,CAAC;IAEF,MAAM,OAAO,GAAyB,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC;IAC3D,OAAO,cAAc,CAAC,OAAO,EAAE,UAAU,EAAE,EAAE,YAAY,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;AAC1G,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,MAAwB,EACxB,IAA+C;IAE/C,yDAAyD;IACzD,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,GAAG,CAC/B,YAAY,IAAI,CAAC,SAAS,EAAE,EAC5B,EAAE,cAAc,EAAE,IAAI,EAAE,CACzB,CAAC;IAEF,MAAM,UAAU,GAAyB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QAC9D,MAAM,KAAK,GAAuB,EAAE,aAAa,EAAE,CAAC,CAAC,aAAa,EAAE,CAAC;QACrE,IAAI,CAAC,CAAC,KAAK,KAAK,SAAS;YAAE,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC;QACjD,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS;YAAE,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC;QAC9C,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS;YAAE,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC;QAC9C,OAAO,KAAK,CAAC;IACf,CAAC,CAAC,CAAC;IAEH,MAAM,aAAa,GAAG,EAAE,GAAG,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,EAAE,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC;IAE/E,sDAAsD;IACtD,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,IAAI,CAGnC,UAAU,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC;IAE/B,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC;IAE1E,sBAAsB;IACtB,MAAM,CAAC,eAAe,CAAC,WAAW,CAAC,CAAC;IACpC,MAAM,CAAC,eAAe,CAAC,UAAU,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC;IACpD,MAAM,CAAC,eAAe,CAAC,kBAAkB,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC;IAE5D,MAAM,MAAM,GAAG,IAAI,CAAC,cAAc,EAAE,MAAM,IAAI,SAAS,CAAC;IACxD,MAAM,QAAQ,GAAG,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAA4B,CAAC;IAE1F,yEAAyE;IACzE,MAAM,eAAe,GAAG,QAAQ,CAAC,SAAS,CAAC,IAAI,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC;IACrE,OAAO,cAAc,CAAC,eAAe,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;AACpF,CAAC;AAED,gFAAgF;AAChF,mBAAmB;AACnB,gFAAgF;AAEhF,MAAM,CAAC,MAAM,mBAAmB,GAAsB;IACpD,IAAI,EAAE,2BAA2B;IACjC,WAAW,EACT,kHAAkH;IACpH,MAAM,EAAE,yBAAyB;IACjC,OAAO,EAAE,eAAe;CACzB,CAAC;AAEF,MAAM,CAAC,MAAM,oBAAoB,GAAsB;IACrD,IAAI,EAAE,4BAA4B;IAClC,WAAW,EACT,+HAA+H;IACjI,MAAM,EAAE,0BAA0B;IAClC,OAAO,EAAE,gBAAgB;CAC1B,CAAC;AAEF,MAAM,CAAC,MAAM,oBAAoB,GAAsB;IACrD,IAAI,EAAE,4BAA4B;IAClC,WAAW,EACT,4GAA4G;QAC5G,yGAAyG;QACzG,4HAA4H;IAC9H,MAAM,EAAE,0BAA0B;IAClC,OAAO,EAAE,gBAAgB;CAC1B,CAAC;AAEF,MAAM,CAAC,MAAM,mBAAmB,GAAsB;IACpD,IAAI,EAAE,2BAA2B;IACjC,WAAW,EACT,0GAA0G;IAC5G,MAAM,EAAE,yBAAyB;IACjC,OAAO,EAAE,eAAe;CACzB,CAAC;AAEF,MAAM,CAAC,MAAM,mBAAmB,GAAsB;IACpD,IAAI,EAAE,2BAA2B;IACjC,WAAW,EACT,mHAAmH;QACnH,uGAAuG;QACvG,yEAAyE;IAC3E,MAAM,EAAE,yBAAyB;IACjC,OAAO,EAAE,eAAe;CACzB,CAAC"}
@@ -0,0 +1,129 @@
1
+ /**
2
+ * HaloPSA milestone management tools.
3
+ *
4
+ * Milestones are stored as a nested `milestones[]` array on the ticket
5
+ * itself — there is no top-level /Milestone endpoint (GET /Milestone
6
+ * returns 404). All reads use GET /Tickets/{id}?includedetails=true
7
+ * and all writes use POST /Tickets with a read-before-write merge,
8
+ * replacing the milestones array in its entirety (same pattern as
9
+ * customfields).
10
+ *
11
+ * Discovery note (2026-05-11): confirmed `milestones: []` (empty array)
12
+ * on ticket 22541; Swagger declares `MileStone` schema with 24 props.
13
+ */
14
+ import { z } from 'zod';
15
+ import type { HaloPSAAPIClient } from '../api/client.js';
16
+ import type { ZodToolDefinition } from './registry.js';
17
+ export declare const ListTicketMilestonesArgsSchema: z.ZodObject<{
18
+ ticket_id: z.ZodNumber;
19
+ format_options: z.ZodOptional<z.ZodObject<{
20
+ format: z.ZodOptional<z.ZodEnum<{
21
+ compact: "compact";
22
+ standard: "standard";
23
+ detailed: "detailed";
24
+ }>>;
25
+ fields: z.ZodOptional<z.ZodArray<z.ZodString>>;
26
+ omit_empty: z.ZodOptional<z.ZodBoolean>;
27
+ }, z.core.$strip>>;
28
+ }, z.core.$strip>;
29
+ export declare const SetTicketMilestonesArgsSchema: z.ZodObject<{
30
+ ticket_id: z.ZodNumber;
31
+ milestones: z.ZodArray<z.ZodObject<{
32
+ name: z.ZodString;
33
+ target_date: z.ZodString;
34
+ start_date: z.ZodOptional<z.ZodString>;
35
+ sequence: z.ZodOptional<z.ZodNumber>;
36
+ billing_amount: z.ZodOptional<z.ZodNumber>;
37
+ status_id: z.ZodOptional<z.ZodNumber>;
38
+ state: z.ZodOptional<z.ZodNumber>;
39
+ }, z.core.$strip>>;
40
+ format_options: z.ZodOptional<z.ZodObject<{
41
+ format: z.ZodOptional<z.ZodEnum<{
42
+ compact: "compact";
43
+ standard: "standard";
44
+ detailed: "detailed";
45
+ }>>;
46
+ fields: z.ZodOptional<z.ZodArray<z.ZodString>>;
47
+ omit_empty: z.ZodOptional<z.ZodBoolean>;
48
+ }, z.core.$strip>>;
49
+ }, z.core.$strip>;
50
+ export declare const AddTicketMilestoneArgsSchema: z.ZodObject<{
51
+ ticket_id: z.ZodNumber;
52
+ milestone: z.ZodObject<{
53
+ name: z.ZodString;
54
+ target_date: z.ZodString;
55
+ start_date: z.ZodOptional<z.ZodString>;
56
+ sequence: z.ZodOptional<z.ZodNumber>;
57
+ billing_amount: z.ZodOptional<z.ZodNumber>;
58
+ status_id: z.ZodOptional<z.ZodNumber>;
59
+ state: z.ZodOptional<z.ZodNumber>;
60
+ }, z.core.$strip>;
61
+ format_options: z.ZodOptional<z.ZodObject<{
62
+ format: z.ZodOptional<z.ZodEnum<{
63
+ compact: "compact";
64
+ standard: "standard";
65
+ detailed: "detailed";
66
+ }>>;
67
+ fields: z.ZodOptional<z.ZodArray<z.ZodString>>;
68
+ omit_empty: z.ZodOptional<z.ZodBoolean>;
69
+ }, z.core.$strip>>;
70
+ }, z.core.$strip>;
71
+ export declare const RemoveTicketMilestoneArgsSchema: z.ZodObject<{
72
+ ticket_id: z.ZodNumber;
73
+ milestone_id: z.ZodNumber;
74
+ format_options: z.ZodOptional<z.ZodObject<{
75
+ format: z.ZodOptional<z.ZodEnum<{
76
+ compact: "compact";
77
+ standard: "standard";
78
+ detailed: "detailed";
79
+ }>>;
80
+ fields: z.ZodOptional<z.ZodArray<z.ZodString>>;
81
+ omit_empty: z.ZodOptional<z.ZodBoolean>;
82
+ }, z.core.$strip>>;
83
+ }, z.core.$strip>;
84
+ export declare const GetCurrentMilestoneArgsSchema: z.ZodObject<{
85
+ parent_ticket_id: z.ZodNumber;
86
+ format_options: z.ZodOptional<z.ZodObject<{
87
+ format: z.ZodOptional<z.ZodEnum<{
88
+ compact: "compact";
89
+ standard: "standard";
90
+ detailed: "detailed";
91
+ }>>;
92
+ fields: z.ZodOptional<z.ZodArray<z.ZodString>>;
93
+ omit_empty: z.ZodOptional<z.ZodBoolean>;
94
+ }, z.core.$strip>>;
95
+ }, z.core.$strip>;
96
+ /**
97
+ * Link (or unlink) a child ticket to a milestone on its parent project ticket.
98
+ *
99
+ * Halo's Project Task tickettype (id=20) has NO milestone field on the child
100
+ * side — writing milestone_id to the child's own record is a no-op (silently
101
+ * dropped). Milestone membership is stored exclusively in the parent's
102
+ * milestones[].tickets[] array. This helper:
103
+ *
104
+ * 1. Optionally fetches the child to discover its parent_id.
105
+ * 2. Fetches the parent (fresh, includedetails=true).
106
+ * 3. Walks every milestone, removing childTicketId from both tickets[] and
107
+ * the runtime-only tickets_list[] (int array sometimes returned by Halo
108
+ * but not declared in the swagger schema — handled defensively).
109
+ * 4. Appends the child to the target milestone's arrays (unless
110
+ * milestoneId === 0 or null — "unlink" mode, step 3 only).
111
+ * 5. POSTs the parent back with the mutated milestones[] (read-before-write
112
+ * merge preserving all other parent fields).
113
+ * 6. Invalidates relevant caches.
114
+ *
115
+ * Throws a descriptive Error for common mis-configurations so the caller
116
+ * surface (createTicket / updateTicket) can surface a clear message.
117
+ */
118
+ export declare function linkTicketToMilestone(client: HaloPSAAPIClient, childTicketId: number, milestoneId: number | null, parentTicketId?: number): Promise<void>;
119
+ export declare function listTicketMilestones(client: HaloPSAAPIClient, args: z.infer<typeof ListTicketMilestonesArgsSchema>): Promise<string>;
120
+ export declare function setTicketMilestones(client: HaloPSAAPIClient, args: z.infer<typeof SetTicketMilestonesArgsSchema>): Promise<string>;
121
+ export declare function addTicketMilestone(client: HaloPSAAPIClient, args: z.infer<typeof AddTicketMilestoneArgsSchema>): Promise<string>;
122
+ export declare function removeTicketMilestone(client: HaloPSAAPIClient, args: z.infer<typeof RemoveTicketMilestoneArgsSchema>): Promise<string>;
123
+ export declare function getCurrentMilestone(client: HaloPSAAPIClient, args: z.infer<typeof GetCurrentMilestoneArgsSchema>): Promise<string>;
124
+ export declare const listTicketMilestonesTool: ZodToolDefinition;
125
+ export declare const setTicketMilestonesTool: ZodToolDefinition;
126
+ export declare const addTicketMilestoneTool: ZodToolDefinition;
127
+ export declare const removeTicketMilestoneTool: ZodToolDefinition;
128
+ export declare const getCurrentMilestoneTool: ZodToolDefinition;
129
+ //# sourceMappingURL=milestones.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"milestones.d.ts","sourceRoot":"","sources":["../../src/tools/milestones.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;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;AA+BvD,eAAO,MAAM,8BAA8B;;;;;;;;;;;iBAIzC,CAAC;AAEH,eAAO,MAAM,6BAA6B;;;;;;;;;;;;;;;;;;;;iBAMxC,CAAC;AAEH,eAAO,MAAM,4BAA4B;;;;;;;;;;;;;;;;;;;;iBAMvC,CAAC;AAEH,eAAO,MAAM,+BAA+B;;;;;;;;;;;;iBAM1C,CAAC;AAEH,eAAO,MAAM,6BAA6B;;;;;;;;;;;iBAIxC,CAAC;AAmEH;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,gBAAgB,EACxB,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,MAAM,GAAG,IAAI,EAC1B,cAAc,CAAC,EAAE,MAAM,GACtB,OAAO,CAAC,IAAI,CAAC,CAef;AAMD,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,8BAA8B,CAAC,GACnD,OAAO,CAAC,MAAM,CAAC,CA0CjB;AAED,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,6BAA6B,CAAC,GAClD,OAAO,CAAC,MAAM,CAAC,CAKjB;AAED,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,GACjD,OAAO,CAAC,MAAM,CAAC,CAQjB;AAED,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,+BAA+B,CAAC,GACpD,OAAO,CAAC,MAAM,CAAC,CAiBjB;AAED,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,6BAA6B,CAAC,GAClD,OAAO,CAAC,MAAM,CAAC,CA2CjB;AAMD,eAAO,MAAM,wBAAwB,EAAE,iBAKtC,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,iBAKrC,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,iBAKpC,CAAC;AAEF,eAAO,MAAM,yBAAyB,EAAE,iBAKvC,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,iBAKrC,CAAC"}