@credal/actions 0.2.228 → 0.2.230

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.
@@ -4418,7 +4418,8 @@ export const zendeskCreateZendeskTicketDefinition = {
4418
4418
  },
4419
4419
  subdomain: {
4420
4420
  type: "string",
4421
- description: "The subdomain of the Zendesk account",
4421
+ pattern: "^[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?$",
4422
+ description: "The hostname-label subdomain of the Zendesk account, without a protocol, path, or .zendesk.com suffix",
4422
4423
  tags: ["recommend-predefined"],
4423
4424
  },
4424
4425
  groupId: {
@@ -4446,7 +4447,7 @@ export const zendeskCreateZendeskTicketDefinition = {
4446
4447
  };
4447
4448
  export const zendeskListZendeskTicketsDefinition = {
4448
4449
  displayName: "List Zendesk tickets",
4449
- description: "List tickets in Zendesk from the past 3 months",
4450
+ description: "List compact ticket discovery records from Zendesk from the past 3 months. Use getTicketDetails to investigate selected tickets.",
4450
4451
  scopes: [],
4451
4452
  tags: [],
4452
4453
  parameters: {
@@ -4455,29 +4456,99 @@ export const zendeskListZendeskTicketsDefinition = {
4455
4456
  properties: {
4456
4457
  subdomain: {
4457
4458
  type: "string",
4458
- description: "The subdomain of the Zendesk account",
4459
+ pattern: "^[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?$",
4460
+ description: "The hostname-label subdomain of the Zendesk account, without a protocol, path, or .zendesk.com suffix",
4459
4461
  tags: ["recommend-predefined"],
4460
4462
  },
4461
4463
  status: {
4462
4464
  type: "string",
4465
+ enum: ["new", "open", "pending", "hold", "solved", "closed"],
4463
4466
  description: "Filter tickets by status (new, open, pending, hold, solved, closed)",
4464
4467
  },
4468
+ limit: {
4469
+ type: "integer",
4470
+ minimum: 1,
4471
+ maximum: 50,
4472
+ description: "Number of compact ticket records to return per page (optional, defaults to 20, maximum 50)",
4473
+ },
4474
+ page: {
4475
+ type: "integer",
4476
+ minimum: 1,
4477
+ description: "Offset page number to retrieve (optional, defaults to 1). Pass next_page from a previous response to continue.",
4478
+ },
4465
4479
  },
4466
4480
  },
4467
4481
  output: {
4468
4482
  type: "object",
4469
- required: ["tickets", "count"],
4483
+ required: ["tickets", "count", "returned_count", "has_more"],
4470
4484
  properties: {
4471
4485
  tickets: {
4472
4486
  type: "array",
4473
- description: "List of tickets",
4487
+ description: "Small ticket records for choosing which IDs to fetch with getTicketDetails",
4474
4488
  items: {
4475
4489
  type: "object",
4490
+ required: ["id", "description_excerpt", "description_truncated"],
4491
+ properties: {
4492
+ id: {
4493
+ type: "integer",
4494
+ description: "Zendesk ticket ID",
4495
+ },
4496
+ subject: {
4497
+ type: "string",
4498
+ nullable: true,
4499
+ description: "Ticket subject",
4500
+ },
4501
+ status: {
4502
+ type: "string",
4503
+ nullable: true,
4504
+ description: "Ticket status category",
4505
+ },
4506
+ type: {
4507
+ type: "string",
4508
+ nullable: true,
4509
+ description: "Ticket type such as question, incident, problem, or task",
4510
+ },
4511
+ priority: {
4512
+ type: "string",
4513
+ nullable: true,
4514
+ description: "Ticket priority",
4515
+ },
4516
+ created_at: {
4517
+ type: "string",
4518
+ nullable: true,
4519
+ description: "Ticket creation timestamp",
4520
+ },
4521
+ updated_at: {
4522
+ type: "string",
4523
+ nullable: true,
4524
+ description: "Ticket update timestamp",
4525
+ },
4526
+ description_excerpt: {
4527
+ type: "string",
4528
+ description: "Up to the first 1,000 characters of the ticket description",
4529
+ },
4530
+ description_truncated: {
4531
+ type: "boolean",
4532
+ description: "Whether the full ticket description is longer than description_excerpt",
4533
+ },
4534
+ },
4476
4535
  },
4477
4536
  },
4478
4537
  count: {
4479
4538
  type: "number",
4480
- description: "Number of tickets found",
4539
+ description: "Total number of tickets reported by Zendesk for the list query",
4540
+ },
4541
+ returned_count: {
4542
+ type: "number",
4543
+ description: "Number of compact ticket records returned in this response",
4544
+ },
4545
+ has_more: {
4546
+ type: "boolean",
4547
+ description: "Whether another page of tickets is available",
4548
+ },
4549
+ next_page: {
4550
+ type: "integer",
4551
+ description: "Page number to pass on the next call when has_more is true",
4481
4552
  },
4482
4553
  },
4483
4554
  },
@@ -4486,7 +4557,7 @@ export const zendeskListZendeskTicketsDefinition = {
4486
4557
  };
4487
4558
  export const zendeskGetTicketDetailsDefinition = {
4488
4559
  displayName: "Get ticket details",
4489
- description: "Get details of a ticket in Zendesk",
4560
+ description: "Get the full ticket object for a selected Zendesk ticket.",
4490
4561
  scopes: [],
4491
4562
  tags: [],
4492
4563
  parameters: {
@@ -4499,7 +4570,8 @@ export const zendeskGetTicketDetailsDefinition = {
4499
4570
  },
4500
4571
  subdomain: {
4501
4572
  type: "string",
4502
- description: "The subdomain of the Zendesk account",
4573
+ pattern: "^[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?$",
4574
+ description: "The hostname-label subdomain of the Zendesk account, without a protocol, path, or .zendesk.com suffix",
4503
4575
  tags: ["recommend-predefined"],
4504
4576
  },
4505
4577
  },
@@ -4532,7 +4604,8 @@ export const zendeskUpdateTicketStatusDefinition = {
4532
4604
  },
4533
4605
  subdomain: {
4534
4606
  type: "string",
4535
- description: "The subdomain of the Zendesk account",
4607
+ pattern: "^[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?$",
4608
+ description: "The hostname-label subdomain of the Zendesk account, without a protocol, path, or .zendesk.com suffix",
4536
4609
  tags: ["recommend-predefined"],
4537
4610
  },
4538
4611
  status: {
@@ -4559,7 +4632,8 @@ export const zendeskAddCommentToTicketDefinition = {
4559
4632
  },
4560
4633
  subdomain: {
4561
4634
  type: "string",
4562
- description: "The subdomain of the Zendesk account",
4635
+ pattern: "^[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?$",
4636
+ description: "The hostname-label subdomain of the Zendesk account, without a protocol, path, or .zendesk.com suffix",
4563
4637
  tags: ["recommend-predefined"],
4564
4638
  },
4565
4639
  body: {
@@ -4604,7 +4678,8 @@ export const zendeskAssignTicketDefinition = {
4604
4678
  },
4605
4679
  subdomain: {
4606
4680
  type: "string",
4607
- description: "The subdomain of the Zendesk account",
4681
+ pattern: "^[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?$",
4682
+ description: "The hostname-label subdomain of the Zendesk account, without a protocol, path, or .zendesk.com suffix",
4608
4683
  tags: ["recommend-predefined"],
4609
4684
  },
4610
4685
  assigneeEmail: {
@@ -4627,7 +4702,8 @@ export const zendeskSearchZendeskByQueryDefinition = {
4627
4702
  properties: {
4628
4703
  subdomain: {
4629
4704
  type: "string",
4630
- description: "The subdomain of the Zendesk account",
4705
+ pattern: "^[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?$",
4706
+ description: "The hostname-label subdomain of the Zendesk account, without a protocol, path, or .zendesk.com suffix",
4631
4707
  tags: ["recommend-predefined"],
4632
4708
  },
4633
4709
  query: {
@@ -4667,7 +4743,7 @@ export const zendeskSearchZendeskByQueryDefinition = {
4667
4743
  };
4668
4744
  export const zendeskSearchZendeskTicketsByQueryDefinition = {
4669
4745
  displayName: "Search Zendesk tickets with a query",
4670
- description: "Search Zendesk tickets by query with flexible filtering options. Only returns tickets, never any other type of Zendesk resource.",
4746
+ description: "Search compact Zendesk ticket discovery records with flexible filtering options. Only returns tickets. Use getTicketDetails to investigate selected tickets.",
4671
4747
  scopes: [],
4672
4748
  tags: [],
4673
4749
  parameters: {
@@ -4685,25 +4761,89 @@ export const zendeskSearchZendeskTicketsByQueryDefinition = {
4685
4761
  description: 'Search query string that can include filters like status, priority, tags, assignee, etc. Examples - status:open, priority:high, tags:bug, assignee:user@example.com, or combination like "status:open priority:high". The search is always restricted to tickets, so any type filter in the query is ignored.',
4686
4762
  },
4687
4763
  limit: {
4688
- type: "number",
4689
- description: "Maximum number of tickets to return (optional, defaults to 100)",
4764
+ type: "integer",
4765
+ minimum: 1,
4766
+ maximum: 50,
4767
+ description: "Number of compact ticket records to return per page (optional, defaults to 20, maximum 50)",
4768
+ },
4769
+ page: {
4770
+ type: "integer",
4771
+ minimum: 1,
4772
+ description: "Offset page number to retrieve (optional, defaults to 1). Pass next_page from a previous response to continue.",
4690
4773
  },
4691
4774
  },
4692
4775
  },
4693
4776
  output: {
4694
4777
  type: "object",
4695
- required: ["results", "count"],
4778
+ required: ["results", "count", "returned_count", "has_more"],
4696
4779
  properties: {
4697
4780
  results: {
4698
4781
  type: "array",
4699
- description: "List of tickets matching the query",
4782
+ description: "Small ticket records for choosing which IDs to fetch with getTicketDetails",
4700
4783
  items: {
4701
4784
  type: "object",
4785
+ required: ["id", "description_excerpt", "description_truncated"],
4786
+ properties: {
4787
+ id: {
4788
+ type: "integer",
4789
+ description: "Zendesk ticket ID",
4790
+ },
4791
+ subject: {
4792
+ type: "string",
4793
+ nullable: true,
4794
+ description: "Ticket subject",
4795
+ },
4796
+ status: {
4797
+ type: "string",
4798
+ nullable: true,
4799
+ description: "Ticket status category",
4800
+ },
4801
+ type: {
4802
+ type: "string",
4803
+ nullable: true,
4804
+ description: "Ticket type such as question, incident, problem, or task",
4805
+ },
4806
+ priority: {
4807
+ type: "string",
4808
+ nullable: true,
4809
+ description: "Ticket priority",
4810
+ },
4811
+ created_at: {
4812
+ type: "string",
4813
+ nullable: true,
4814
+ description: "Ticket creation timestamp",
4815
+ },
4816
+ updated_at: {
4817
+ type: "string",
4818
+ nullable: true,
4819
+ description: "Ticket update timestamp",
4820
+ },
4821
+ description_excerpt: {
4822
+ type: "string",
4823
+ description: "Up to the first 1,000 characters of the ticket description",
4824
+ },
4825
+ description_truncated: {
4826
+ type: "boolean",
4827
+ description: "Whether the full ticket description is longer than description_excerpt",
4828
+ },
4829
+ },
4702
4830
  },
4703
4831
  },
4704
4832
  count: {
4705
4833
  type: "number",
4706
- description: "Number of tickets found",
4834
+ description: "Total number of matching tickets reported by Zendesk",
4835
+ },
4836
+ returned_count: {
4837
+ type: "number",
4838
+ description: "Number of compact ticket records returned in this response",
4839
+ },
4840
+ has_more: {
4841
+ type: "boolean",
4842
+ description: "Whether another page of matching tickets is available",
4843
+ },
4844
+ next_page: {
4845
+ type: "integer",
4846
+ description: "Page number to pass on the next call when has_more is true",
4707
4847
  },
4708
4848
  },
4709
4849
  },
@@ -4520,24 +4520,89 @@ export type zendeskCreateZendeskTicketOutputType = z.infer<typeof zendeskCreateZ
4520
4520
  export type zendeskCreateZendeskTicketFunction = ActionFunction<zendeskCreateZendeskTicketParamsType, AuthParamsType, zendeskCreateZendeskTicketOutputType>;
4521
4521
  export declare const zendeskListZendeskTicketsParamsSchema: z.ZodObject<{
4522
4522
  subdomain: z.ZodString;
4523
- status: z.ZodOptional<z.ZodString>;
4523
+ status: z.ZodOptional<z.ZodEnum<["new", "open", "pending", "hold", "solved", "closed"]>>;
4524
+ limit: z.ZodOptional<z.ZodNumber>;
4525
+ page: z.ZodOptional<z.ZodNumber>;
4524
4526
  }, "strip", z.ZodTypeAny, {
4525
4527
  subdomain: string;
4526
- status?: string | undefined;
4528
+ status?: "new" | "open" | "pending" | "hold" | "solved" | "closed" | undefined;
4529
+ limit?: number | undefined;
4530
+ page?: number | undefined;
4527
4531
  }, {
4528
4532
  subdomain: string;
4529
- status?: string | undefined;
4533
+ status?: "new" | "open" | "pending" | "hold" | "solved" | "closed" | undefined;
4534
+ limit?: number | undefined;
4535
+ page?: number | undefined;
4530
4536
  }>;
4531
4537
  export type zendeskListZendeskTicketsParamsType = z.infer<typeof zendeskListZendeskTicketsParamsSchema>;
4532
4538
  export declare const zendeskListZendeskTicketsOutputSchema: z.ZodObject<{
4533
- tickets: z.ZodArray<z.ZodObject<{}, "strip", z.ZodAny, z.objectOutputType<{}, z.ZodAny, "strip">, z.objectInputType<{}, z.ZodAny, "strip">>, "many">;
4539
+ tickets: z.ZodArray<z.ZodObject<{
4540
+ id: z.ZodNumber;
4541
+ subject: z.ZodOptional<z.ZodNullable<z.ZodString>>;
4542
+ status: z.ZodOptional<z.ZodNullable<z.ZodString>>;
4543
+ type: z.ZodOptional<z.ZodNullable<z.ZodString>>;
4544
+ priority: z.ZodOptional<z.ZodNullable<z.ZodString>>;
4545
+ created_at: z.ZodOptional<z.ZodNullable<z.ZodString>>;
4546
+ updated_at: z.ZodOptional<z.ZodNullable<z.ZodString>>;
4547
+ description_excerpt: z.ZodString;
4548
+ description_truncated: z.ZodBoolean;
4549
+ }, "strip", z.ZodTypeAny, {
4550
+ id: number;
4551
+ description_excerpt: string;
4552
+ description_truncated: boolean;
4553
+ type?: string | null | undefined;
4554
+ status?: string | null | undefined;
4555
+ created_at?: string | null | undefined;
4556
+ subject?: string | null | undefined;
4557
+ priority?: string | null | undefined;
4558
+ updated_at?: string | null | undefined;
4559
+ }, {
4560
+ id: number;
4561
+ description_excerpt: string;
4562
+ description_truncated: boolean;
4563
+ type?: string | null | undefined;
4564
+ status?: string | null | undefined;
4565
+ created_at?: string | null | undefined;
4566
+ subject?: string | null | undefined;
4567
+ priority?: string | null | undefined;
4568
+ updated_at?: string | null | undefined;
4569
+ }>, "many">;
4534
4570
  count: z.ZodNumber;
4571
+ returned_count: z.ZodNumber;
4572
+ has_more: z.ZodBoolean;
4573
+ next_page: z.ZodOptional<z.ZodNumber>;
4535
4574
  }, "strip", z.ZodTypeAny, {
4536
4575
  count: number;
4537
- tickets: z.objectOutputType<{}, z.ZodAny, "strip">[];
4576
+ tickets: {
4577
+ id: number;
4578
+ description_excerpt: string;
4579
+ description_truncated: boolean;
4580
+ type?: string | null | undefined;
4581
+ status?: string | null | undefined;
4582
+ created_at?: string | null | undefined;
4583
+ subject?: string | null | undefined;
4584
+ priority?: string | null | undefined;
4585
+ updated_at?: string | null | undefined;
4586
+ }[];
4587
+ returned_count: number;
4588
+ has_more: boolean;
4589
+ next_page?: number | undefined;
4538
4590
  }, {
4539
4591
  count: number;
4540
- tickets: z.objectInputType<{}, z.ZodAny, "strip">[];
4592
+ tickets: {
4593
+ id: number;
4594
+ description_excerpt: string;
4595
+ description_truncated: boolean;
4596
+ type?: string | null | undefined;
4597
+ status?: string | null | undefined;
4598
+ created_at?: string | null | undefined;
4599
+ subject?: string | null | undefined;
4600
+ priority?: string | null | undefined;
4601
+ updated_at?: string | null | undefined;
4602
+ }[];
4603
+ returned_count: number;
4604
+ has_more: boolean;
4605
+ next_page?: number | undefined;
4541
4606
  }>;
4542
4607
  export type zendeskListZendeskTicketsOutputType = z.infer<typeof zendeskListZendeskTicketsOutputSchema>;
4543
4608
  export type zendeskListZendeskTicketsFunction = ActionFunction<zendeskListZendeskTicketsParamsType, AuthParamsType, zendeskListZendeskTicketsOutputType>;
@@ -4661,25 +4726,87 @@ export declare const zendeskSearchZendeskTicketsByQueryParamsSchema: z.ZodObject
4661
4726
  subdomain: z.ZodString;
4662
4727
  query: z.ZodString;
4663
4728
  limit: z.ZodOptional<z.ZodNumber>;
4729
+ page: z.ZodOptional<z.ZodNumber>;
4664
4730
  }, "strip", z.ZodTypeAny, {
4665
4731
  query: string;
4666
4732
  subdomain: string;
4667
4733
  limit?: number | undefined;
4734
+ page?: number | undefined;
4668
4735
  }, {
4669
4736
  query: string;
4670
4737
  subdomain: string;
4671
4738
  limit?: number | undefined;
4739
+ page?: number | undefined;
4672
4740
  }>;
4673
4741
  export type zendeskSearchZendeskTicketsByQueryParamsType = z.infer<typeof zendeskSearchZendeskTicketsByQueryParamsSchema>;
4674
4742
  export declare const zendeskSearchZendeskTicketsByQueryOutputSchema: z.ZodObject<{
4675
- results: z.ZodArray<z.ZodObject<{}, "strip", z.ZodAny, z.objectOutputType<{}, z.ZodAny, "strip">, z.objectInputType<{}, z.ZodAny, "strip">>, "many">;
4743
+ results: z.ZodArray<z.ZodObject<{
4744
+ id: z.ZodNumber;
4745
+ subject: z.ZodOptional<z.ZodNullable<z.ZodString>>;
4746
+ status: z.ZodOptional<z.ZodNullable<z.ZodString>>;
4747
+ type: z.ZodOptional<z.ZodNullable<z.ZodString>>;
4748
+ priority: z.ZodOptional<z.ZodNullable<z.ZodString>>;
4749
+ created_at: z.ZodOptional<z.ZodNullable<z.ZodString>>;
4750
+ updated_at: z.ZodOptional<z.ZodNullable<z.ZodString>>;
4751
+ description_excerpt: z.ZodString;
4752
+ description_truncated: z.ZodBoolean;
4753
+ }, "strip", z.ZodTypeAny, {
4754
+ id: number;
4755
+ description_excerpt: string;
4756
+ description_truncated: boolean;
4757
+ type?: string | null | undefined;
4758
+ status?: string | null | undefined;
4759
+ created_at?: string | null | undefined;
4760
+ subject?: string | null | undefined;
4761
+ priority?: string | null | undefined;
4762
+ updated_at?: string | null | undefined;
4763
+ }, {
4764
+ id: number;
4765
+ description_excerpt: string;
4766
+ description_truncated: boolean;
4767
+ type?: string | null | undefined;
4768
+ status?: string | null | undefined;
4769
+ created_at?: string | null | undefined;
4770
+ subject?: string | null | undefined;
4771
+ priority?: string | null | undefined;
4772
+ updated_at?: string | null | undefined;
4773
+ }>, "many">;
4676
4774
  count: z.ZodNumber;
4775
+ returned_count: z.ZodNumber;
4776
+ has_more: z.ZodBoolean;
4777
+ next_page: z.ZodOptional<z.ZodNumber>;
4677
4778
  }, "strip", z.ZodTypeAny, {
4678
- results: z.objectOutputType<{}, z.ZodAny, "strip">[];
4779
+ results: {
4780
+ id: number;
4781
+ description_excerpt: string;
4782
+ description_truncated: boolean;
4783
+ type?: string | null | undefined;
4784
+ status?: string | null | undefined;
4785
+ created_at?: string | null | undefined;
4786
+ subject?: string | null | undefined;
4787
+ priority?: string | null | undefined;
4788
+ updated_at?: string | null | undefined;
4789
+ }[];
4679
4790
  count: number;
4791
+ returned_count: number;
4792
+ has_more: boolean;
4793
+ next_page?: number | undefined;
4680
4794
  }, {
4681
- results: z.objectInputType<{}, z.ZodAny, "strip">[];
4795
+ results: {
4796
+ id: number;
4797
+ description_excerpt: string;
4798
+ description_truncated: boolean;
4799
+ type?: string | null | undefined;
4800
+ status?: string | null | undefined;
4801
+ created_at?: string | null | undefined;
4802
+ subject?: string | null | undefined;
4803
+ priority?: string | null | undefined;
4804
+ updated_at?: string | null | undefined;
4805
+ }[];
4682
4806
  count: number;
4807
+ returned_count: number;
4808
+ has_more: boolean;
4809
+ next_page?: number | undefined;
4683
4810
  }>;
4684
4811
  export type zendeskSearchZendeskTicketsByQueryOutputType = z.infer<typeof zendeskSearchZendeskTicketsByQueryOutputSchema>;
4685
4812
  export type zendeskSearchZendeskTicketsByQueryFunction = ActionFunction<zendeskSearchZendeskTicketsByQueryParamsType, AuthParamsType, zendeskSearchZendeskTicketsByQueryOutputType>;
@@ -10273,21 +10400,21 @@ export declare const githubListCommitsParamsSchema: z.ZodObject<{
10273
10400
  }, "strip", z.ZodTypeAny, {
10274
10401
  repositoryOwner: string;
10275
10402
  repositoryName: string;
10403
+ page?: number | undefined;
10276
10404
  branch?: string | undefined;
10277
10405
  author?: string | undefined;
10278
10406
  since?: string | undefined;
10279
10407
  until?: string | undefined;
10280
10408
  perPage?: number | undefined;
10281
- page?: number | undefined;
10282
10409
  }, {
10283
10410
  repositoryOwner: string;
10284
10411
  repositoryName: string;
10412
+ page?: number | undefined;
10285
10413
  branch?: string | undefined;
10286
10414
  author?: string | undefined;
10287
10415
  since?: string | undefined;
10288
10416
  until?: string | undefined;
10289
10417
  perPage?: number | undefined;
10290
- page?: number | undefined;
10291
10418
  }>;
10292
10419
  export type githubListCommitsParamsType = z.infer<typeof githubListCommitsParamsSchema>;
10293
10420
  export declare const githubListCommitsOutputSchema: z.ZodObject<{
@@ -1596,7 +1596,10 @@ export const bingGetTopNSearchResultUrlsOutputSchema = z.object({
1596
1596
  export const zendeskCreateZendeskTicketParamsSchema = z.object({
1597
1597
  subject: z.string().describe("The subject of the ticket"),
1598
1598
  body: z.string().describe("The body of the ticket").optional(),
1599
- subdomain: z.string().describe("The subdomain of the Zendesk account"),
1599
+ subdomain: z
1600
+ .string()
1601
+ .regex(new RegExp("^[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?$"))
1602
+ .describe("The hostname-label subdomain of the Zendesk account, without a protocol, path, or .zendesk.com suffix"),
1600
1603
  groupId: z.coerce.number().describe("The ID of the group to assign the ticket to").optional(),
1601
1604
  });
1602
1605
  export const zendeskCreateZendeskTicketOutputSchema = z.object({
@@ -1604,23 +1607,65 @@ export const zendeskCreateZendeskTicketOutputSchema = z.object({
1604
1607
  ticketUrl: z.string().describe("The URL of the ticket created").optional(),
1605
1608
  });
1606
1609
  export const zendeskListZendeskTicketsParamsSchema = z.object({
1607
- subdomain: z.string().describe("The subdomain of the Zendesk account"),
1608
- status: z.string().describe("Filter tickets by status (new, open, pending, hold, solved, closed)").optional(),
1610
+ subdomain: z
1611
+ .string()
1612
+ .regex(new RegExp("^[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?$"))
1613
+ .describe("The hostname-label subdomain of the Zendesk account, without a protocol, path, or .zendesk.com suffix"),
1614
+ status: z
1615
+ .enum(["new", "open", "pending", "hold", "solved", "closed"])
1616
+ .describe("Filter tickets by status (new, open, pending, hold, solved, closed)")
1617
+ .optional(),
1618
+ limit: z.coerce
1619
+ .number()
1620
+ .int()
1621
+ .gte(1)
1622
+ .lte(50)
1623
+ .describe("Number of compact ticket records to return per page (optional, defaults to 20, maximum 50)")
1624
+ .optional(),
1625
+ page: z.coerce
1626
+ .number()
1627
+ .int()
1628
+ .gte(1)
1629
+ .describe("Offset page number to retrieve (optional, defaults to 1). Pass next_page from a previous response to continue.")
1630
+ .optional(),
1609
1631
  });
1610
1632
  export const zendeskListZendeskTicketsOutputSchema = z.object({
1611
- tickets: z.array(z.object({}).catchall(z.any())).describe("List of tickets"),
1612
- count: z.coerce.number().describe("Number of tickets found"),
1633
+ tickets: z
1634
+ .array(z.object({
1635
+ id: z.coerce.number().int().describe("Zendesk ticket ID"),
1636
+ subject: z.string().nullable().describe("Ticket subject").optional(),
1637
+ status: z.string().nullable().describe("Ticket status category").optional(),
1638
+ type: z.string().nullable().describe("Ticket type such as question, incident, problem, or task").optional(),
1639
+ priority: z.string().nullable().describe("Ticket priority").optional(),
1640
+ created_at: z.string().nullable().describe("Ticket creation timestamp").optional(),
1641
+ updated_at: z.string().nullable().describe("Ticket update timestamp").optional(),
1642
+ description_excerpt: z.string().describe("Up to the first 1,000 characters of the ticket description"),
1643
+ description_truncated: z
1644
+ .boolean()
1645
+ .describe("Whether the full ticket description is longer than description_excerpt"),
1646
+ }))
1647
+ .describe("Small ticket records for choosing which IDs to fetch with getTicketDetails"),
1648
+ count: z.coerce.number().describe("Total number of tickets reported by Zendesk for the list query"),
1649
+ returned_count: z.coerce.number().describe("Number of compact ticket records returned in this response"),
1650
+ has_more: z.boolean().describe("Whether another page of tickets is available"),
1651
+ next_page: z.coerce.number().int().describe("Page number to pass on the next call when has_more is true").optional(),
1613
1652
  });
1614
1653
  export const zendeskGetTicketDetailsParamsSchema = z.object({
1615
1654
  ticketId: z.string().describe("The ID of the ticket"),
1616
- subdomain: z.string().describe("The subdomain of the Zendesk account"),
1655
+ subdomain: z
1656
+ .string()
1657
+ .regex(new RegExp("^[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?$"))
1658
+ .describe("The hostname-label subdomain of the Zendesk account, without a protocol, path, or .zendesk.com suffix"),
1617
1659
  });
1618
1660
  export const zendeskGetTicketDetailsOutputSchema = z.object({
1619
1661
  ticket: z.object({}).catchall(z.any()).describe("The details of the ticket"),
1620
1662
  });
1621
1663
  export const zendeskUpdateTicketStatusParamsSchema = z.object({
1622
1664
  ticketId: z.string().describe("The ID of the ticket to update"),
1623
- subdomain: z.string().describe("The subdomain of the Zendesk account"),
1665
+ subdomain: z
1666
+ .string()
1667
+ .regex(new RegExp("^[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?$"))
1668
+ .describe("The hostname-label subdomain of the Zendesk account, without a protocol, path, or .zendesk.com suffix"),
1624
1669
  status: z
1625
1670
  .string()
1626
1671
  .describe('The state of the ticket. If your account has activated custom ticket statuses, this is the ticket\'s status category. Allowed values are "new", "open", "pending", "hold", "solved", or "closed".'),
@@ -1628,7 +1673,10 @@ export const zendeskUpdateTicketStatusParamsSchema = z.object({
1628
1673
  export const zendeskUpdateTicketStatusOutputSchema = z.void();
1629
1674
  export const zendeskAddCommentToTicketParamsSchema = z.object({
1630
1675
  ticketId: z.string().describe("The ID of the ticket to update"),
1631
- subdomain: z.string().describe("The subdomain of the Zendesk account"),
1676
+ subdomain: z
1677
+ .string()
1678
+ .regex(new RegExp("^[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?$"))
1679
+ .describe("The hostname-label subdomain of the Zendesk account, without a protocol, path, or .zendesk.com suffix"),
1632
1680
  body: z.string().describe("The body of the comment"),
1633
1681
  public: z.boolean().describe("Whether the comment should be public (defaults to true)").optional(),
1634
1682
  });
@@ -1638,12 +1686,18 @@ export const zendeskAddCommentToTicketOutputSchema = z.object({
1638
1686
  });
1639
1687
  export const zendeskAssignTicketParamsSchema = z.object({
1640
1688
  ticketId: z.string().describe("The ID of the ticket to update"),
1641
- subdomain: z.string().describe("The subdomain of the Zendesk account"),
1689
+ subdomain: z
1690
+ .string()
1691
+ .regex(new RegExp("^[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?$"))
1692
+ .describe("The hostname-label subdomain of the Zendesk account, without a protocol, path, or .zendesk.com suffix"),
1642
1693
  assigneeEmail: z.string().describe("The email address of the agent to assign the ticket to"),
1643
1694
  });
1644
1695
  export const zendeskAssignTicketOutputSchema = z.void();
1645
1696
  export const zendeskSearchZendeskByQueryParamsSchema = z.object({
1646
- subdomain: z.string().describe("The subdomain of the Zendesk account"),
1697
+ subdomain: z
1698
+ .string()
1699
+ .regex(new RegExp("^[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?$"))
1700
+ .describe("The hostname-label subdomain of the Zendesk account, without a protocol, path, or .zendesk.com suffix"),
1647
1701
  query: z
1648
1702
  .string()
1649
1703
  .describe('Search query string that can include filters like status, priority, tags, assignee, etc. Examples - status:open, priority:high, tags:bug, assignee:user@example.com, or combination like "status:open priority:high"'),
@@ -1665,11 +1719,40 @@ export const zendeskSearchZendeskTicketsByQueryParamsSchema = z.object({
1665
1719
  query: z
1666
1720
  .string()
1667
1721
  .describe('Search query string that can include filters like status, priority, tags, assignee, etc. Examples - status:open, priority:high, tags:bug, assignee:user@example.com, or combination like "status:open priority:high". The search is always restricted to tickets, so any type filter in the query is ignored.'),
1668
- limit: z.coerce.number().describe("Maximum number of tickets to return (optional, defaults to 100)").optional(),
1722
+ limit: z.coerce
1723
+ .number()
1724
+ .int()
1725
+ .gte(1)
1726
+ .lte(50)
1727
+ .describe("Number of compact ticket records to return per page (optional, defaults to 20, maximum 50)")
1728
+ .optional(),
1729
+ page: z.coerce
1730
+ .number()
1731
+ .int()
1732
+ .gte(1)
1733
+ .describe("Offset page number to retrieve (optional, defaults to 1). Pass next_page from a previous response to continue.")
1734
+ .optional(),
1669
1735
  });
1670
1736
  export const zendeskSearchZendeskTicketsByQueryOutputSchema = z.object({
1671
- results: z.array(z.object({}).catchall(z.any())).describe("List of tickets matching the query"),
1672
- count: z.coerce.number().describe("Number of tickets found"),
1737
+ results: z
1738
+ .array(z.object({
1739
+ id: z.coerce.number().int().describe("Zendesk ticket ID"),
1740
+ subject: z.string().nullable().describe("Ticket subject").optional(),
1741
+ status: z.string().nullable().describe("Ticket status category").optional(),
1742
+ type: z.string().nullable().describe("Ticket type such as question, incident, problem, or task").optional(),
1743
+ priority: z.string().nullable().describe("Ticket priority").optional(),
1744
+ created_at: z.string().nullable().describe("Ticket creation timestamp").optional(),
1745
+ updated_at: z.string().nullable().describe("Ticket update timestamp").optional(),
1746
+ description_excerpt: z.string().describe("Up to the first 1,000 characters of the ticket description"),
1747
+ description_truncated: z
1748
+ .boolean()
1749
+ .describe("Whether the full ticket description is longer than description_excerpt"),
1750
+ }))
1751
+ .describe("Small ticket records for choosing which IDs to fetch with getTicketDetails"),
1752
+ count: z.coerce.number().describe("Total number of matching tickets reported by Zendesk"),
1753
+ returned_count: z.coerce.number().describe("Number of compact ticket records returned in this response"),
1754
+ has_more: z.boolean().describe("Whether another page of matching tickets is available"),
1755
+ next_page: z.coerce.number().int().describe("Page number to pass on the next call when has_more is true").optional(),
1673
1756
  });
1674
1757
  export const linkedinCreateShareLinkedinPostUrlParamsSchema = z.object({
1675
1758
  text: z.string().describe("The text for the linkedin post").optional(),
@@ -1,16 +1,18 @@
1
1
  import { createAxiosClientWithRetries } from "../../util/axiosClient.js";
2
2
  import { MISSING_AUTH_TOKEN } from "../../util/missingAuthConstants.js";
3
+ import { getZendeskBaseUrl } from "./utils/getZendeskBaseUrl.js";
3
4
  const addCommentToTicket = async ({ params, authParams, }) => {
4
5
  const { authToken } = authParams;
5
6
  const { subdomain, ticketId, body, public: isPublic } = params;
6
- const url = `https://${subdomain}.zendesk.com/api/v2/tickets/${ticketId}.json`;
7
7
  if (!authToken) {
8
8
  throw new Error(MISSING_AUTH_TOKEN);
9
9
  }
10
+ const zendeskBaseUrl = getZendeskBaseUrl({ subdomain });
11
+ const apiEndpoint = new URL(`/api/v2/tickets/${ticketId}.json`, zendeskBaseUrl);
10
12
  const axiosClient = createAxiosClientWithRetries({ timeout: 20000, retryCount: 5 });
11
13
  try {
12
14
  await axiosClient.request({
13
- url: url,
15
+ url: apiEndpoint.toString(),
14
16
  method: "PUT",
15
17
  headers: {
16
18
  "Content-Type": "application/json",
@@ -27,7 +29,7 @@ const addCommentToTicket = async ({ params, authParams, }) => {
27
29
  });
28
30
  return {
29
31
  success: true,
30
- ticketUrl: `https://${subdomain}.zendesk.com/agent/tickets/${ticketId}`,
32
+ ticketUrl: new URL(`/agent/tickets/${ticketId}`, zendeskBaseUrl).toString(),
31
33
  };
32
34
  }
33
35
  catch (error) {
@@ -1,15 +1,17 @@
1
1
  import { createAxiosClientWithRetries } from "../../util/axiosClient.js";
2
2
  import { MISSING_AUTH_TOKEN } from "../../util/missingAuthConstants.js";
3
+ import { getZendeskBaseUrl } from "./utils/getZendeskBaseUrl.js";
3
4
  const updateTicketStatus = async ({ params, authParams, }) => {
4
5
  const { authToken } = authParams;
5
6
  const { subdomain, ticketId, assigneeEmail } = params;
6
- const url = `https://${subdomain}.zendesk.com/api/v2/tickets/${ticketId}.json`;
7
7
  if (!authToken) {
8
8
  throw new Error(MISSING_AUTH_TOKEN);
9
9
  }
10
+ const zendeskBaseUrl = getZendeskBaseUrl({ subdomain });
11
+ const apiEndpoint = new URL(`/api/v2/tickets/${ticketId}.json`, zendeskBaseUrl);
10
12
  const axiosClient = createAxiosClientWithRetries({ timeout: 10000, retryCount: 4 });
11
13
  await axiosClient.request({
12
- url: url,
14
+ url: apiEndpoint.toString(),
13
15
  method: "PUT",
14
16
  headers: {
15
17
  "Content-Type": "application/json",
@@ -1,9 +1,14 @@
1
1
  import { createAxiosClientWithRetries } from "../../util/axiosClient.js";
2
2
  import { MISSING_AUTH_TOKEN } from "../../util/missingAuthConstants.js";
3
+ import { getZendeskBaseUrl } from "./utils/getZendeskBaseUrl.js";
3
4
  const createZendeskTicket = async ({ params, authParams, }) => {
4
5
  const { authToken } = authParams;
5
6
  const { subdomain, subject, body, groupId } = params;
6
- const url = `https://${subdomain}.zendesk.com/api/v2/tickets.json`;
7
+ if (!authToken) {
8
+ throw new Error(MISSING_AUTH_TOKEN);
9
+ }
10
+ const zendeskBaseUrl = getZendeskBaseUrl({ subdomain });
11
+ const apiEndpoint = new URL("/api/v2/tickets.json", zendeskBaseUrl);
7
12
  const payload = {
8
13
  ticket: {
9
14
  subject,
@@ -13,11 +18,8 @@ const createZendeskTicket = async ({ params, authParams, }) => {
13
18
  group_id: groupId,
14
19
  },
15
20
  };
16
- if (!authToken) {
17
- throw new Error(MISSING_AUTH_TOKEN);
18
- }
19
21
  const axiosClient = createAxiosClientWithRetries({ timeout: 10000, retryCount: 4 });
20
- const response = await axiosClient.post(url, payload, {
22
+ const response = await axiosClient.post(apiEndpoint.toString(), payload, {
21
23
  headers: {
22
24
  "Content-Type": "application/json",
23
25
  Authorization: `Bearer ${authToken}`,
@@ -25,7 +27,7 @@ const createZendeskTicket = async ({ params, authParams, }) => {
25
27
  });
26
28
  return {
27
29
  ticketId: response.data.ticket.id,
28
- ticketUrl: `https://${subdomain}.zendesk.com/requests/${response.data.ticket.id}`,
30
+ ticketUrl: new URL(`/requests/${response.data.ticket.id}`, zendeskBaseUrl).toString(),
29
31
  };
30
32
  };
31
33
  export default createZendeskTicket;
@@ -0,0 +1,3 @@
1
+ import type { zendeskGetTicketCommentsFunction } from "../../autogen/types.js";
2
+ declare const getTicketComments: zendeskGetTicketCommentsFunction;
3
+ export default getTicketComments;
@@ -0,0 +1,80 @@
1
+ import { createAxiosClientWithRetries } from "../../util/axiosClient.js";
2
+ import { MISSING_AUTH_TOKEN } from "../../util/missingAuthConstants.js";
3
+ import { getZendeskBaseUrl } from "./utils/getZendeskBaseUrl.js";
4
+ const getTicketComments = async ({ params, authParams, }) => {
5
+ const { authToken } = authParams;
6
+ const { subdomain, ticketId, limit = 10, cursor, sortOrder = "asc", maxBodyCharacters = 5000 } = params;
7
+ if (!authToken) {
8
+ throw new Error(MISSING_AUTH_TOKEN);
9
+ }
10
+ const zendeskBaseUrl = getZendeskBaseUrl({ subdomain });
11
+ const apiEndpoint = new URL(`/api/v2/tickets/${encodeURIComponent(ticketId)}/comments.json`, zendeskBaseUrl);
12
+ apiEndpoint.searchParams.set("page[size]", limit.toString());
13
+ apiEndpoint.searchParams.set("sort", sortOrder === "desc" ? "-created_at" : "created_at");
14
+ if (cursor) {
15
+ apiEndpoint.searchParams.set("page[after]", cursor);
16
+ }
17
+ const axiosClient = createAxiosClientWithRetries({ timeout: 10000, retryCount: 4 });
18
+ const response = await axiosClient.get(apiEndpoint.toString(), {
19
+ headers: {
20
+ "Content-Type": "application/json",
21
+ Authorization: `Bearer ${authToken}`,
22
+ },
23
+ });
24
+ const rawComments = Array.isArray(response.data.comments) ? response.data.comments : [];
25
+ const comments = rawComments.flatMap(comment => {
26
+ if (!isRecord(comment) || typeof comment.id !== "number") {
27
+ return [];
28
+ }
29
+ const rawComment = comment;
30
+ const fullBody = typeof rawComment.plain_body === "string"
31
+ ? rawComment.plain_body
32
+ : typeof rawComment.body === "string"
33
+ ? rawComment.body
34
+ : "";
35
+ const channel = isRecord(rawComment.via) && typeof rawComment.via.channel === "string" ? rawComment.via.channel : undefined;
36
+ const attachments = compactAttachments(rawComment.attachments);
37
+ return [
38
+ {
39
+ id: comment.id,
40
+ author_id: typeof rawComment.author_id === "number" ? rawComment.author_id : null,
41
+ created_at: typeof rawComment.created_at === "string" ? rawComment.created_at : null,
42
+ public: rawComment.public === true,
43
+ type: typeof rawComment.type === "string" ? rawComment.type : null,
44
+ body: fullBody.slice(0, maxBodyCharacters),
45
+ body_truncated: fullBody.length > maxBodyCharacters,
46
+ ...(channel ? { via: { channel } } : {}),
47
+ ...(attachments.length > 0 ? { attachments } : {}),
48
+ },
49
+ ];
50
+ });
51
+ const hasMore = response.data.meta?.has_more === true;
52
+ const afterCursor = response.data.meta?.after_cursor;
53
+ return {
54
+ comments,
55
+ returned_count: comments.length,
56
+ has_more: hasMore,
57
+ ...(hasMore && typeof afterCursor === "string" ? { next_cursor: afterCursor } : {}),
58
+ };
59
+ };
60
+ function compactAttachments(value) {
61
+ if (!Array.isArray(value))
62
+ return [];
63
+ return value.flatMap(attachment => {
64
+ if (!isRecord(attachment))
65
+ return [];
66
+ return [
67
+ {
68
+ ...(typeof attachment.id === "number" ? { id: attachment.id } : {}),
69
+ ...(typeof attachment.file_name === "string" ? { file_name: attachment.file_name } : {}),
70
+ ...(typeof attachment.content_type === "string" ? { content_type: attachment.content_type } : {}),
71
+ ...(typeof attachment.size === "number" ? { size: attachment.size } : {}),
72
+ ...(typeof attachment.content_url === "string" ? { content_url: attachment.content_url } : {}),
73
+ },
74
+ ];
75
+ });
76
+ }
77
+ function isRecord(value) {
78
+ return typeof value === "object" && value !== null && !Array.isArray(value);
79
+ }
80
+ export default getTicketComments;
@@ -1,15 +1,17 @@
1
1
  import { createAxiosClientWithRetries } from "../../util/axiosClient.js";
2
2
  import { MISSING_AUTH_TOKEN } from "../../util/missingAuthConstants.js";
3
+ import { getZendeskBaseUrl } from "./utils/getZendeskBaseUrl.js";
3
4
  const getZendeskTicketDetails = async ({ params, authParams, }) => {
4
5
  const { authToken } = authParams;
5
6
  const { subdomain, ticketId } = params;
6
- const url = `https://${subdomain}.zendesk.com/api/v2/tickets/${ticketId}.json`;
7
7
  if (!authToken) {
8
8
  throw new Error(MISSING_AUTH_TOKEN);
9
9
  }
10
+ const zendeskBaseUrl = getZendeskBaseUrl({ subdomain });
11
+ const apiEndpoint = new URL(`/api/v2/tickets/${ticketId}.json`, zendeskBaseUrl);
10
12
  const axiosClient = createAxiosClientWithRetries({ timeout: 10000, retryCount: 4 });
11
13
  const response = await axiosClient.request({
12
- url: url,
14
+ url: apiEndpoint.toString(),
13
15
  method: "GET",
14
16
  headers: {
15
17
  "Content-Type": "application/json",
@@ -1,33 +1,43 @@
1
1
  import { createAxiosClientWithRetries } from "../../util/axiosClient.js";
2
2
  import { MISSING_AUTH_TOKEN } from "../../util/missingAuthConstants.js";
3
+ import { compactZendeskTickets, isZendeskTicketSearchResult } from "./utils/compactTicket.js";
4
+ import { getZendeskBaseUrl } from "./utils/getZendeskBaseUrl.js";
3
5
  const listZendeskTickets = async ({ params, authParams, }) => {
4
6
  const { authToken } = authParams;
5
- const { subdomain, status } = params;
7
+ const { subdomain, status, limit = 20, page = 1 } = params;
6
8
  // Calculate date 3 months ago from now
7
9
  const threeMonthsAgo = new Date();
8
10
  threeMonthsAgo.setMonth(threeMonthsAgo.getMonth() - 3);
9
11
  const formattedDate = threeMonthsAgo.toISOString().split("T")[0];
10
- // Endpoint for getting tickets
11
- const url = `https://${subdomain}.zendesk.com/api/v2/tickets.json`;
12
12
  if (!authToken) {
13
13
  throw new Error(MISSING_AUTH_TOKEN);
14
14
  }
15
+ const zendeskBaseUrl = getZendeskBaseUrl({ subdomain });
16
+ const apiEndpoint = new URL("/api/v2/search.json", zendeskBaseUrl);
15
17
  const axiosClient = createAxiosClientWithRetries({ timeout: 10000, retryCount: 4 });
16
- // Add query parameters for filtering
17
- const queryParams = new URLSearchParams();
18
- queryParams.append("created_after", formattedDate);
19
- if (status) {
20
- queryParams.append("status", status);
21
- }
22
- const response = await axiosClient.get(`${url}?${queryParams.toString()}`, {
23
- headers: {
24
- "Content-Type": "application/json",
25
- Authorization: `Bearer ${authToken}`,
26
- },
18
+ const query = [`type:ticket`, `created>${formattedDate}`, ...(status ? [`status:${status}`] : [])].join(" ");
19
+ apiEndpoint.searchParams.set("query", query);
20
+ apiEndpoint.searchParams.set("per_page", limit.toString());
21
+ apiEndpoint.searchParams.set("page", page.toString());
22
+ const headers = {
23
+ "Content-Type": "application/json",
24
+ Authorization: `Bearer ${authToken}`,
25
+ };
26
+ const response = await axiosClient.get(apiEndpoint.toString(), {
27
+ headers,
27
28
  });
29
+ const rawTickets = Array.isArray(response.data.results)
30
+ ? response.data.results.filter(isZendeskTicketSearchResult)
31
+ : [];
32
+ const tickets = compactZendeskTickets(rawTickets);
33
+ const count = typeof response.data.count === "number" ? response.data.count : rawTickets.length;
34
+ const hasMore = typeof response.data.next_page === "string" && response.data.next_page.length > 0;
28
35
  return {
29
- tickets: response.data.tickets,
30
- count: response.data.count,
36
+ tickets,
37
+ count,
38
+ returned_count: tickets.length,
39
+ has_more: hasMore,
40
+ ...(hasMore ? { next_page: page + 1 } : {}),
31
41
  };
32
42
  };
33
43
  export default listZendeskTickets;
@@ -1,19 +1,19 @@
1
1
  import { createAxiosClientWithRetries } from "../../util/axiosClient.js";
2
2
  import { MISSING_AUTH_TOKEN } from "../../util/missingAuthConstants.js";
3
+ import { getZendeskBaseUrl } from "./utils/getZendeskBaseUrl.js";
3
4
  const searchZendeskByQuery = async ({ params, authParams, }) => {
4
5
  const { authToken } = authParams;
5
6
  const { subdomain, query, objectType = "ticket", limit = 100 } = params;
6
- // Endpoint for searching Zendesk objects
7
- const url = `https://${subdomain}.zendesk.com/api/v2/search.json`;
8
7
  if (!authToken) {
9
8
  throw new Error(MISSING_AUTH_TOKEN);
10
9
  }
10
+ const zendeskBaseUrl = getZendeskBaseUrl({ subdomain });
11
+ const apiEndpoint = new URL("/api/v2/search.json", zendeskBaseUrl);
11
12
  const axiosClient = createAxiosClientWithRetries({ timeout: 10000, retryCount: 4 });
12
13
  // Build search query parameters
13
- const queryParams = new URLSearchParams();
14
- queryParams.append("query", `type:${objectType} ${query}`);
15
- queryParams.append("per_page", limit.toString());
16
- const response = await axiosClient.get(`${url}?${queryParams.toString()}`, {
14
+ apiEndpoint.searchParams.set("query", `type:${objectType} ${query}`);
15
+ apiEndpoint.searchParams.set("per_page", limit.toString());
16
+ const response = await axiosClient.get(apiEndpoint.toString(), {
17
17
  headers: {
18
18
  "Content-Type": "application/json",
19
19
  Authorization: `Bearer ${authToken}`,
@@ -1,16 +1,15 @@
1
1
  import { createAxiosClientWithRetries } from "../../util/axiosClient.js";
2
2
  import { MISSING_AUTH_TOKEN } from "../../util/missingAuthConstants.js";
3
- const ZENDESK_SUBDOMAIN_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/i;
3
+ import { compactZendeskTickets, isZendeskTicketSearchResult } from "./utils/compactTicket.js";
4
+ import { getZendeskBaseUrl } from "./utils/getZendeskBaseUrl.js";
4
5
  const searchZendeskTicketsByQuery = async ({ params, authParams, }) => {
5
6
  const { authToken } = authParams;
6
- const { subdomain, query, limit = 100 } = params;
7
+ const { subdomain, query, limit = 20, page = 1 } = params;
7
8
  if (!authToken) {
8
9
  throw new Error(MISSING_AUTH_TOKEN);
9
10
  }
10
- if (!ZENDESK_SUBDOMAIN_PATTERN.test(subdomain)) {
11
- throw new Error("Invalid Zendesk subdomain");
12
- }
13
- const url = new URL(`https://${subdomain}.zendesk.com/api/v2/search.json`);
11
+ const zendeskBaseUrl = getZendeskBaseUrl({ subdomain });
12
+ const apiEndpoint = new URL("/api/v2/search.json", zendeskBaseUrl);
14
13
  const axiosClient = createAxiosClientWithRetries({ timeout: 10000, retryCount: 4 });
15
14
  // Strip any type: filters from the incoming query so it can't target other resource types,
16
15
  // then force the search to tickets only
@@ -18,22 +17,29 @@ const searchZendeskTicketsByQuery = async ({ params, authParams, }) => {
18
17
  .replace(/\btype:\S+/gi, "")
19
18
  .replace(/\s+/g, " ")
20
19
  .trim();
21
- url.searchParams.set("query", `type:ticket ${sanitizedQuery}`.trim());
22
- url.searchParams.set("per_page", limit.toString());
23
- const response = await axiosClient.get(url.toString(), {
24
- headers: {
25
- "Content-Type": "application/json",
26
- Authorization: `Bearer ${authToken}`,
27
- },
20
+ apiEndpoint.searchParams.set("query", `type:ticket ${sanitizedQuery}`.trim());
21
+ apiEndpoint.searchParams.set("per_page", limit.toString());
22
+ apiEndpoint.searchParams.set("page", page.toString());
23
+ const headers = {
24
+ "Content-Type": "application/json",
25
+ Authorization: `Bearer ${authToken}`,
26
+ };
27
+ const response = await axiosClient.get(apiEndpoint.toString(), {
28
+ headers,
28
29
  });
29
30
  // Defense in depth: only return results Zendesk marks as tickets
30
- const results = Array.isArray(response.data.results)
31
- ? response.data.results.filter((result) => result.result_type === "ticket")
31
+ const rawResults = Array.isArray(response.data.results)
32
+ ? response.data.results.filter(isZendeskTicketSearchResult)
32
33
  : [];
33
- const count = typeof response.data.count === "number" ? response.data.count : results.length;
34
+ const results = compactZendeskTickets(rawResults);
35
+ const count = typeof response.data.count === "number" ? response.data.count : rawResults.length;
36
+ const hasMore = typeof response.data.next_page === "string" && response.data.next_page.length > 0;
34
37
  return {
35
38
  results,
36
39
  count,
40
+ returned_count: results.length,
41
+ has_more: hasMore,
42
+ ...(hasMore ? { next_page: page + 1 } : {}),
37
43
  };
38
44
  };
39
45
  export default searchZendeskTicketsByQuery;
@@ -1,15 +1,17 @@
1
1
  import { createAxiosClientWithRetries } from "../../util/axiosClient.js";
2
2
  import { MISSING_AUTH_TOKEN } from "../../util/missingAuthConstants.js";
3
+ import { getZendeskBaseUrl } from "./utils/getZendeskBaseUrl.js";
3
4
  const updateTicketStatus = async ({ params, authParams, }) => {
4
5
  const { authToken } = authParams;
5
6
  const { subdomain, ticketId, status } = params;
6
- const url = `https://${subdomain}.zendesk.com/api/v2/tickets/${ticketId}.json`;
7
7
  if (!authToken) {
8
8
  throw new Error(MISSING_AUTH_TOKEN);
9
9
  }
10
+ const zendeskBaseUrl = getZendeskBaseUrl({ subdomain });
11
+ const apiEndpoint = new URL(`/api/v2/tickets/${ticketId}.json`, zendeskBaseUrl);
10
12
  const axiosClient = createAxiosClientWithRetries({ timeout: 10000, retryCount: 4 });
11
13
  await axiosClient.request({
12
- url: url,
14
+ url: apiEndpoint.toString(),
13
15
  method: "PUT",
14
16
  headers: {
15
17
  "Content-Type": "application/json",
@@ -0,0 +1,22 @@
1
+ import type { zendeskListZendeskTicketsOutputType } from "../../../autogen/types.js";
2
+ type CompactTicket = zendeskListZendeskTicketsOutputType["tickets"][number];
3
+ export interface ZendeskTicketSearchResult {
4
+ id: number;
5
+ result_type: "ticket";
6
+ subject?: string | null;
7
+ status?: string | null;
8
+ type?: string | null;
9
+ priority?: string | null;
10
+ created_at?: string | null;
11
+ updated_at?: string | null;
12
+ description?: string | null;
13
+ }
14
+ /**
15
+ * Narrows an unknown Zendesk Search API result to the ticket fields used for discovery.
16
+ */
17
+ export declare function isZendeskTicketSearchResult(value: unknown): value is ZendeskTicketSearchResult;
18
+ /**
19
+ * Projects Zendesk tickets into small records for choosing which IDs to fetch with getTicketDetails.
20
+ */
21
+ export declare function compactZendeskTickets(tickets: ZendeskTicketSearchResult[]): CompactTicket[];
22
+ export {};
@@ -0,0 +1,40 @@
1
+ const DESCRIPTION_EXCERPT_CHARACTERS = 1000;
2
+ /**
3
+ * Narrows an unknown Zendesk Search API result to the ticket fields used for discovery.
4
+ */
5
+ export function isZendeskTicketSearchResult(value) {
6
+ if (typeof value !== "object" || value === null)
7
+ return false;
8
+ const result = value;
9
+ return (typeof result.id === "number" &&
10
+ result.result_type === "ticket" &&
11
+ isOptionalNullableString(result.subject) &&
12
+ isOptionalNullableString(result.status) &&
13
+ isOptionalNullableString(result.type) &&
14
+ isOptionalNullableString(result.priority) &&
15
+ isOptionalNullableString(result.created_at) &&
16
+ isOptionalNullableString(result.updated_at) &&
17
+ isOptionalNullableString(result.description));
18
+ }
19
+ /**
20
+ * Projects Zendesk tickets into small records for choosing which IDs to fetch with getTicketDetails.
21
+ */
22
+ export function compactZendeskTickets(tickets) {
23
+ return tickets.map(ticket => {
24
+ const description = ticket.description ?? "";
25
+ return {
26
+ id: ticket.id,
27
+ subject: ticket.subject ?? null,
28
+ status: ticket.status ?? null,
29
+ type: ticket.type ?? null,
30
+ priority: ticket.priority ?? null,
31
+ created_at: ticket.created_at ?? null,
32
+ updated_at: ticket.updated_at ?? null,
33
+ description_excerpt: description.slice(0, DESCRIPTION_EXCERPT_CHARACTERS),
34
+ description_truncated: description.length > DESCRIPTION_EXCERPT_CHARACTERS,
35
+ };
36
+ });
37
+ }
38
+ function isOptionalNullableString(value) {
39
+ return value === undefined || value === null || typeof value === "string";
40
+ }
@@ -0,0 +1,3 @@
1
+ export declare const getZendeskBaseUrl: ({ subdomain }: {
2
+ subdomain: string;
3
+ }) => URL;
@@ -0,0 +1,7 @@
1
+ const ZENDESK_SUBDOMAIN_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/i;
2
+ export const getZendeskBaseUrl = ({ subdomain }) => {
3
+ if (!ZENDESK_SUBDOMAIN_PATTERN.test(subdomain)) {
4
+ throw new Error("Invalid Zendesk subdomain");
5
+ }
6
+ return new URL(`https://${subdomain}.zendesk.com/`);
7
+ };
@@ -0,0 +1 @@
1
+ export declare const validateZendeskSubdomain: (subdomain: string) => void;
@@ -0,0 +1,6 @@
1
+ const ZENDESK_SUBDOMAIN_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/i;
2
+ export const validateZendeskSubdomain = (subdomain) => {
3
+ if (!ZENDESK_SUBDOMAIN_PATTERN.test(subdomain)) {
4
+ throw new Error("Invalid Zendesk subdomain");
5
+ }
6
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@credal/actions",
3
- "version": "0.2.228",
3
+ "version": "0.2.230",
4
4
  "type": "module",
5
5
  "description": "AI Actions by Credal AI",
6
6
  "sideEffects": false,