node-autotask 0.9.0 → 0.11.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 (77) hide show
  1. package/MCP_TOOL_CATALOG.json +3 -3
  2. package/MCP_TOOL_MANIFEST.md +5 -5
  3. package/N8N_MCP_MANIFEST.json +2 -2
  4. package/N8N_MCP_MANIFEST.md +3 -3
  5. package/capabilities.json +1354 -1263
  6. package/dist/capabilities.cjs +2 -3
  7. package/dist/capabilities.js +1 -2
  8. package/dist/{chunk-53MC2QRQ.cjs → chunk-25ZUKHS4.cjs} +135 -136
  9. package/dist/{chunk-BZPFDRAE.js → chunk-7L23FDQW.js} +1353 -1263
  10. package/dist/{chunk-UYJ5V262.js → chunk-CJONZQ7E.js} +32 -182
  11. package/dist/chunk-DMMMWO6A.js +0 -0
  12. package/dist/chunk-FLB2X6O5.cjs +1 -0
  13. package/dist/chunk-IDPB4N2Q.cjs +1 -0
  14. package/dist/{chunk-UDWV4VVD.js → chunk-LNRN45XS.js} +214 -20
  15. package/dist/{chunk-OYHKIYVR.cjs → chunk-QAX222R6.cjs} +422 -572
  16. package/dist/{chunk-QJU2N32V.js → chunk-SXHPUQMB.js} +2 -3
  17. package/dist/{chunk-LTKMTXUR.cjs → chunk-T5FLSJOD.cjs} +1353 -1263
  18. package/dist/chunk-UPQIUWOS.js +0 -0
  19. package/dist/{chunk-RWLPTU67.cjs → chunk-YKEX4SK7.cjs} +235 -41
  20. package/dist/{index-ZknpNhtc.d.ts → index-B_xToSpq.d.ts} +8 -8
  21. package/dist/{index-CfU1JDgS.d.cts → index-Dv_GSZaN.d.cts} +8 -8
  22. package/dist/{index-O2zzIMcQ.d.cts → index-cQ6CRexR.d.cts} +3 -1
  23. package/dist/{index-O2zzIMcQ.d.ts → index-cQ6CRexR.d.ts} +3 -1
  24. package/dist/index.cjs +21 -22
  25. package/dist/index.d.cts +4 -4
  26. package/dist/index.d.ts +4 -4
  27. package/dist/index.js +6 -7
  28. package/dist/operations/index.cjs +284 -131
  29. package/dist/operations/index.d.cts +28 -5
  30. package/dist/operations/index.d.ts +28 -5
  31. package/dist/operations/index.js +160 -7
  32. package/dist/pool.cjs +36 -12
  33. package/dist/pool.d.cts +2 -2
  34. package/dist/pool.d.ts +2 -2
  35. package/dist/pool.js +31 -7
  36. package/dist/resources/index.cjs +5 -6
  37. package/dist/resources/index.d.cts +2 -2
  38. package/dist/resources/index.d.ts +2 -2
  39. package/dist/resources/index.js +4 -5
  40. package/dist/types/index.cjs +1 -2
  41. package/dist/types/index.d.cts +1 -1
  42. package/dist/types/index.d.ts +1 -1
  43. package/dist/types/index.js +1 -2
  44. package/dist/untrusted.cjs +0 -1
  45. package/dist/untrusted.js +0 -1
  46. package/docs/API.md +12 -6
  47. package/package.json +1 -1
  48. package/dist/capabilities.cjs.map +0 -1
  49. package/dist/capabilities.js.map +0 -1
  50. package/dist/chunk-53MC2QRQ.cjs.map +0 -1
  51. package/dist/chunk-BZPFDRAE.js.map +0 -1
  52. package/dist/chunk-CXM35TEC.cjs +0 -1
  53. package/dist/chunk-CXM35TEC.cjs.map +0 -1
  54. package/dist/chunk-GIVD2CQ3.js +0 -1
  55. package/dist/chunk-GIVD2CQ3.js.map +0 -1
  56. package/dist/chunk-LTKMTXUR.cjs.map +0 -1
  57. package/dist/chunk-MHBMSBTA.js +0 -1
  58. package/dist/chunk-MHBMSBTA.js.map +0 -1
  59. package/dist/chunk-NZUFSSIZ.cjs +0 -1
  60. package/dist/chunk-NZUFSSIZ.cjs.map +0 -1
  61. package/dist/chunk-OYHKIYVR.cjs.map +0 -1
  62. package/dist/chunk-QJU2N32V.js.map +0 -1
  63. package/dist/chunk-RWLPTU67.cjs.map +0 -1
  64. package/dist/chunk-UDWV4VVD.js.map +0 -1
  65. package/dist/chunk-UYJ5V262.js.map +0 -1
  66. package/dist/index.cjs.map +0 -1
  67. package/dist/index.js.map +0 -1
  68. package/dist/operations/index.cjs.map +0 -1
  69. package/dist/operations/index.js.map +0 -1
  70. package/dist/pool.cjs.map +0 -1
  71. package/dist/pool.js.map +0 -1
  72. package/dist/resources/index.cjs.map +0 -1
  73. package/dist/resources/index.js.map +0 -1
  74. package/dist/types/index.cjs.map +0 -1
  75. package/dist/types/index.js.map +0 -1
  76. package/dist/untrusted.cjs.map +0 -1
  77. package/dist/untrusted.js.map +0 -1
@@ -4,34 +4,36 @@ import {
4
4
  BLIND_SURFACE_STAMPS,
5
5
  BaseResource,
6
6
  ConflictError,
7
- FIELD_CATALOG_HONESTY_NOTE,
8
7
  NotFoundError,
9
8
  RELATIONSHIP_MAP,
10
9
  ResolutionError,
11
10
  StaleObjectError,
11
+ TICKET_LABEL_FIELDS,
12
12
  UNTRUSTED_FIELDS_BY_RESOURCE,
13
+ addLabelSibling,
14
+ addNameSibling,
13
15
  blindStampFor,
14
16
  buildDryRunResult,
15
17
  buildFilter,
16
18
  clampPageSize,
19
+ createNamePlan,
17
20
  cursorToPath,
18
21
  decodeFileData,
19
- expandResourceNames,
20
- fieldCatalog,
21
22
  formatVendorDetail,
22
23
  isAutotaskError,
23
24
  mergePartialBody,
24
25
  parseIdentifier,
25
26
  parseVendorErrors,
27
+ resolveCompositeNames,
26
28
  resolveFieldsPreset,
27
29
  truncateNoteChars,
28
30
  unwrapItem,
29
31
  unwrapItems,
30
32
  unwrapOperation
31
- } from "./chunk-UDWV4VVD.js";
33
+ } from "./chunk-LNRN45XS.js";
32
34
  import {
33
35
  CAPABILITIES
34
- } from "./chunk-BZPFDRAE.js";
36
+ } from "./chunk-7L23FDQW.js";
35
37
 
36
38
  // src/resources/appointments.ts
37
39
  var AppointmentsResource = class extends BaseResource {
@@ -820,181 +822,6 @@ var ClassificationIconsResource = class extends BaseResource {
820
822
  }
821
823
  };
822
824
 
823
- // src/operations/compositeNames.ts
824
- var DEFAULT_MAX_NAME_READS = 20;
825
- var TICKET_LABEL_FIELDS = ["status", "priority", "ticketType", "issueType", "subIssueType", "source", "queueID"];
826
- function isVendorId(value) {
827
- if (typeof value === "number") return Number.isSafeInteger(value) && value >= 0;
828
- if (typeof value === "string") return /^\d+$/.test(value);
829
- return false;
830
- }
831
- function createNamePlan(labelFields = []) {
832
- const resourceIds = [];
833
- const resSeen = /* @__PURE__ */ new Set();
834
- const recordFks = [];
835
- const fkSeen = /* @__PURE__ */ new Set();
836
- return {
837
- plan: () => ({ resourceIds, recordFks, labelFields }),
838
- addResource: (value) => {
839
- if (!isVendorId(value)) return;
840
- const k = String(value);
841
- if (!resSeen.has(k)) {
842
- resSeen.add(k);
843
- resourceIds.push(value);
844
- }
845
- },
846
- addFk: (table, value) => {
847
- if (!isVendorId(value)) return;
848
- const k = `${table}:${String(value)}`;
849
- if (!fkSeen.has(k)) {
850
- fkSeen.add(k);
851
- recordFks.push({ table, id: value });
852
- }
853
- }
854
- };
855
- }
856
- function addNameSibling(row, field, resolve) {
857
- const value = row[field];
858
- if (value === void 0) return;
859
- row[`${field}Name`] = isVendorId(value) ? resolve(value) : null;
860
- }
861
- function addLabelSibling(row, field, names) {
862
- const value = row[field];
863
- if (value === void 0) return;
864
- row[`${field}Label`] = names.labelFor(field, value);
865
- }
866
- var FK_TABLE_PATH = {
867
- role: "/V1.0/Roles",
868
- billingCode: "/V1.0/BillingCodes",
869
- contract: "/V1.0/Contracts"
870
- };
871
- var FK_NAME_FIELD = {
872
- role: "name",
873
- billingCode: "name",
874
- contract: "contractName"
875
- };
876
- function fkName(record, table) {
877
- const value = record[FK_NAME_FIELD[table]];
878
- return typeof value === "string" && value.trim() !== "" ? value : null;
879
- }
880
- function faultRow(err) {
881
- const e = err;
882
- return e.httpStatus !== void 0 ? { code: e.code, message: e.message, httpStatus: e.httpStatus } : { code: e.code, message: e.message };
883
- }
884
- async function resolveCompositeNames(http, plan, opts = {}) {
885
- if (opts.maxNameReads !== void 0 && (!Number.isInteger(opts.maxNameReads) || opts.maxNameReads < 1)) {
886
- throw new AutotaskConfigError(`composite names: maxNameReads must be an integer >= 1 (got ${String(opts.maxNameReads)})`);
887
- }
888
- const budget = Math.min(DEFAULT_MAX_NAME_READS, opts.maxNameReads ?? DEFAULT_MAX_NAME_READS);
889
- const client = http.client;
890
- if (client === void 0) {
891
- throw new AutotaskConfigError("composite names: the transport is not wired to an AutotaskClient (the composite must run through the client)");
892
- }
893
- const readCache = http.readCache;
894
- const nameCache = http.resourceNameCache;
895
- const section = { truncated: false, rows: {}, reasons: {} };
896
- let remaining = budget;
897
- const coldResource = [];
898
- for (const id of plan.resourceIds) {
899
- if (nameCache !== void 0 && nameCache.get(id) !== void 0) continue;
900
- if (readCache !== void 0 && readCache.get(`/V1.0/Resources/${id}`) !== void 0) continue;
901
- coldResource.push(id);
902
- }
903
- const withinBudget = coldResource.slice(0, remaining);
904
- const overBudgetResource = coldResource.slice(remaining);
905
- remaining -= withinBudget.length;
906
- for (const id of overBudgetResource) {
907
- section.rows[`resource:${id}`] = { name: null, blind: false, capped: true };
908
- section.truncated = true;
909
- }
910
- const overSet = new Set(overBudgetResource.map((id) => String(id)));
911
- const opIds = plan.resourceIds.filter((id) => !overSet.has(String(id)));
912
- if (opIds.length > 0) {
913
- let pass = opIds;
914
- if (pass.length > 500) {
915
- for (const id of pass.slice(500)) {
916
- section.rows[`resource:${id}`] = { name: null, blind: false, capped: true };
917
- section.truncated = true;
918
- }
919
- pass = pass.slice(0, 500);
920
- }
921
- const res = await expandResourceNames(client, pass);
922
- for (const r of res.results) {
923
- const row = { name: r.name, blind: r.blind, capped: false };
924
- if (r.error !== void 0) row.error = faultRow(r.error);
925
- section.rows[`resource:${r.id}`] = row;
926
- }
927
- }
928
- const holders = {
929
- role: client.roles,
930
- billingCode: client.billingCodes,
931
- contract: client.contracts
932
- };
933
- for (const fk of plan.recordFks) {
934
- const key = `${fk.table}:${fk.id}`;
935
- const cold = readCache === void 0 || readCache.get(`${FK_TABLE_PATH[fk.table]}/${fk.id}`) === void 0;
936
- if (cold) {
937
- if (remaining < 1) {
938
- section.rows[key] = { name: null, blind: false, capped: true };
939
- section.truncated = true;
940
- continue;
941
- }
942
- remaining -= 1;
943
- }
944
- try {
945
- const record = await holders[fk.table].get(fk.id);
946
- section.rows[key] = { name: fkName(record, fk.table), blind: false, capped: false };
947
- } catch (err) {
948
- if (err instanceof NotFoundError) {
949
- section.rows[key] = { name: null, blind: true, capped: false };
950
- } else if (isAutotaskError(err) && err.code !== "ABORTED") {
951
- section.rows[key] = { name: null, blind: false, capped: false, error: faultRow(err) };
952
- } else {
953
- throw err;
954
- }
955
- }
956
- }
957
- const picklists = {};
958
- if (plan.labelFields.length > 0) {
959
- let usable = true;
960
- try {
961
- const catalog = await fieldCatalog(client, { resources: ["tickets"], labels: true, fields: [...plan.labelFields] });
962
- const row = catalog.rows.find((r) => r.resource === "tickets");
963
- if (row === void 0 || row.error !== null || row.fields === void 0) {
964
- usable = false;
965
- } else {
966
- for (const f of plan.labelFields) {
967
- const frow = row.fields.find((x) => x.name === f);
968
- picklists[f] = frow?.picklistValues;
969
- }
970
- }
971
- } catch (err) {
972
- if (isAutotaskError(err) && err.code !== "ABORTED") usable = false;
973
- else throw err;
974
- }
975
- if (!usable) for (const f of plan.labelFields) section.reasons[f] = FIELD_CATALOG_HONESTY_NOTE;
976
- }
977
- return {
978
- section,
979
- resourceName: (id) => section.rows[`resource:${id}`]?.name ?? null,
980
- recordName: (table, id) => section.rows[`${table}:${id}`]?.name ?? null,
981
- labelFor: (field, value) => {
982
- if (value === null || value === void 0) return null;
983
- const values = picklists[field];
984
- if (values === void 0) {
985
- section.reasons[field] = FIELD_CATALOG_HONESTY_NOTE;
986
- return null;
987
- }
988
- const hit = values.find((v) => String(v.value) === String(value));
989
- if (hit === void 0) {
990
- section.reasons[field] = FIELD_CATALOG_HONESTY_NOTE;
991
- return null;
992
- }
993
- return typeof hit.label === "string" ? hit.label : null;
994
- }
995
- };
996
- }
997
-
998
825
  // src/resources/special/companies.ts
999
826
  var DEFAULT_LIMIT2 = 25;
1000
827
  var MAX_LIMIT2 = 500;
@@ -1092,6 +919,22 @@ async function familyChildPage(http, parentID) {
1092
919
  const { items, pageDetails } = unwrapItems(res, "companies.search");
1093
920
  return { rows: items, hasNext: pageDetails.nextPageUrl !== null };
1094
921
  }
922
+ function assertSearchWithDetailOpts(opts) {
923
+ if (opts === void 0) return;
924
+ if (typeof opts !== "object" || opts === null || Array.isArray(opts)) {
925
+ throw new AutotaskConfigError("companies.searchWithDetail: opts must be an object { limit?: number }", {
926
+ operation: "companies.searchWithDetail"
927
+ });
928
+ }
929
+ for (const key of Object.keys(opts)) {
930
+ if (key !== "limit") {
931
+ throw new AutotaskConfigError(
932
+ `companies.searchWithDetail: unknown opts key "${key}" \u2014 the bag is closed: limit (includeFamily is a companies.search option)`,
933
+ { operation: "companies.searchWithDetail" }
934
+ );
935
+ }
936
+ }
937
+ }
1095
938
  async function searchWithFamily(_resource, query, opts = {}, http) {
1096
939
  if (opts !== void 0 && (typeof opts !== "object" || opts === null || Array.isArray(opts))) {
1097
940
  throw new AutotaskConfigError("companies.search: opts must be an object { limit?: number, includeFamily?: boolean }");
@@ -1640,7 +1483,8 @@ async function unbilledWork(resource, companyId, opts = {}, http) {
1640
1483
  capped: false,
1641
1484
  hasMore: false,
1642
1485
  status: "skipped",
1643
- note: "skipped: the company (set) has no tickets or tasks in the returned join set \u2014 no tasks/tickets to join against is NOT evidence of no time entries (zero wire)"
1486
+ // joinNotes ride the skipped part too: an empty join set from a PARTIAL read (e.g. a full projects page) is not a true zero
1487
+ note: ["skipped: the company (set) has no tickets or tasks in the returned join set \u2014 no tasks/tickets to join against is NOT evidence of no time entries (zero wire)", ...joinNotes].join(" | ")
1644
1488
  };
1645
1489
  } else {
1646
1490
  const timeConditions = taskCap.ids.length > 0 && ticketCap.ids.length > 0 ? [{ op: "or", items: [{ field: "taskID", op: "in", value: taskCap.ids }, { field: "ticketID", op: "in", value: ticketCap.ids }] }] : taskCap.ids.length > 0 ? [{ field: "taskID", op: "in", value: taskCap.ids }] : [{ field: "ticketID", op: "in", value: ticketCap.ids }];
@@ -1833,6 +1677,13 @@ var CompaniesResource = class extends BaseResource {
1833
1677
  async search(query, opts) {
1834
1678
  return searchWithFamily(this, query, opts, this.http);
1835
1679
  }
1680
+ /** `search` with an empty result explained. Same closed opts discipline as `search`: only `limit` is
1681
+ * accepted here (an unknown key is refused pre-request, zero wire — never silently dropped); the
1682
+ * family block is a `search`-only option. */
1683
+ async searchWithDetail(query, opts = {}) {
1684
+ assertSearchWithDetailOpts(opts);
1685
+ return super.searchWithDetail(query, opts);
1686
+ }
1836
1687
  /** Get the Company with its primary context records in one call (each part a single-record
1837
1688
  * fetch; no sub-lists; absent/dangling references come back null). */
1838
1689
  async getContext(id) {
@@ -11082,4 +10933,3 @@ export {
11082
10933
  VersionResource,
11083
10934
  ZoneInformationResource
11084
10935
  };
11085
- //# sourceMappingURL=chunk-UYJ5V262.js.map
File without changes
@@ -0,0 +1 @@
1
+ "use strict";
@@ -0,0 +1 @@
1
+ "use strict";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  CAPABILITIES
3
- } from "./chunk-BZPFDRAE.js";
3
+ } from "./chunk-7L23FDQW.js";
4
4
 
5
5
  // src/auth.ts
6
6
  var AUTH_HEADERS = {
@@ -349,14 +349,15 @@ var QueryTooLongError = class extends AutotaskError {
349
349
  this.name = "QueryTooLongError";
350
350
  }
351
351
  };
352
- var MISSING_REQUIRED_FIELD = /^Missing Required Field:\s*([A-Za-z_][A-Za-z0-9_]*)/;
352
+ var MISSING_REQUIRED_FIELD = /Missing Required Field:\s*([A-Za-z_][A-Za-z0-9_]*)/;
353
+ var VALUE_DOES_NOT_EXIST_FOR_FIELD = /Value does not exist for the required field\s+([A-Za-z_][A-Za-z0-9_]*)/;
353
354
  function parseVendorErrors(body) {
354
355
  if (!isRecord(body) || !Array.isArray(body.errors)) return void 0;
355
356
  const out = [];
356
357
  for (const entry of body.errors) {
357
358
  if (typeof entry === "string") {
358
359
  const item2 = { message: entry };
359
- const m = MISSING_REQUIRED_FIELD.exec(entry);
360
+ const m = MISSING_REQUIRED_FIELD.exec(entry) ?? VALUE_DOES_NOT_EXIST_FOR_FIELD.exec(entry);
360
361
  if (m) item2.field = m[1];
361
362
  out.push(item2);
362
363
  continue;
@@ -372,7 +373,11 @@ function parseVendorErrors(body) {
372
373
  }
373
374
  return out;
374
375
  }
375
- var DETERMINISTIC_VENDOR_500_PHRASES = [/exceeds maximum length/i];
376
+ var DETERMINISTIC_VENDOR_500_PHRASES = [
377
+ /exceeds maximum length/i,
378
+ /Missing Required Field/i,
379
+ /Value does not exist for the required field/i
380
+ ];
376
381
  function isDeterministicVendor500(entries) {
377
382
  if (entries === void 0) return false;
378
383
  return entries.some(
@@ -2621,16 +2626,20 @@ var RELATIONSHIP_MAP = {
2621
2626
  "liveProvenPrefix": [
2622
2627
  "endDateTime",
2623
2628
  "actionType",
2624
- "startDateTime"
2629
+ "startDateTime",
2630
+ "assignedResourceID"
2625
2631
  ],
2626
- "unproven": true,
2632
+ "unproven": false,
2627
2633
  "blocker": {
2628
- "kind": "unknown",
2629
- "message": "create refused (stage-11 apply-matrix, marker FULL100W-1791110339): the final vendor response is preserved verbatim in vendorEvidence (the campaign artifact; its 300-char detail truncation is as recorded on disk)",
2630
- "remedy": "the wall is opaque in the campaign record \u2014 inspect vendorEvidence for the exact vendor text, re-run the create with a live probe (dry-run first), and adjudicate against the vendored spec before retrying."
2634
+ "kind": "delete-absent",
2635
+ "message": 'create succeeded live (DG-1 verification, 2026-10-11, ww29, parent company 29683380, record id 30230658, marker name "node-autotask DG-1 create probe") once the four vendor-required body fields were present \u2014 actionType, assignedResourceID, startDateTime, endDateTime \u2014 plus the parent companyID in the path. The vendor reports a missing required field as HTTP 500 SERVER_ERROR naming the field; it is a deterministic rejection, not a transient fault (the SDK now answers retryable: false \u2014 add the field, do not retry). The vendor defines no DELETE endpoint for this resource (W-A1) \u2014 the record is residue; cleanup is UI-only.',
2636
+ "remedy": "provision via the API as proven: send actionType (picklist, e.g. 3 = Note: General), assignedResourceID (a resource id), startDateTime and endDateTime (UTC ISO) in the body and the company id as the parent; name and note are optional content fields. Clean up the residue record in the Autotask UI (the marker name is the lookup handle) \u2014 there is no API delete path."
2631
2637
  },
2632
2638
  "vendorEvidence": [
2633
- '{"error":true,"code":"SERVER_ERROR","category":"server","retryable":true,"httpStatus":500,"operation":"company_notes.create","suggestedAction":"Retry with backoff; the server error is transient.","correlationId":"7ef594cb-74dd-4a29-a7f7-2a5d5eb515f0","message":"Value does not exist for the required '
2639
+ `{"error":true,"code":"SERVER_ERROR","category":"server","retryable":false,"httpStatus":500,"operation":"company_notes.create","message":"Field 'assignedResourceID': Value does not exist for the required field assignedResourceID. ; on record number [1].","suggestedAction":"Deterministic field rejection \u2014 fix the value, do not retry. Consult operations.fieldCatalog for the field catalog (types, required, maxLength)."}`,
2640
+ `{"error":true,"code":"SERVER_ERROR","category":"server","retryable":false,"httpStatus":500,"operation":"company_notes.create","message":"Field 'actionType': Missing Required Field: actionType. ; on record number [1].","suggestedAction":"Deterministic field rejection \u2014 fix the value, do not retry. Consult operations.fieldCatalog for the field catalog (types, required, maxLength)."}`,
2641
+ `{"error":true,"code":"SERVER_ERROR","category":"server","retryable":false,"httpStatus":500,"operation":"company_notes.create","message":"Field 'endDateTime': Missing Required Field: endDateTime. ; on record number [1].","suggestedAction":"Deterministic field rejection \u2014 fix the value, do not retry. Consult operations.fieldCatalog for the field catalog (types, required, maxLength)."}`,
2642
+ `{"error":true,"code":"SERVER_ERROR","category":"server","retryable":false,"httpStatus":500,"operation":"company_notes.create","message":"Field 'startDateTime': Missing Required Field: startDateTime. ; on record number [1].","suggestedAction":"Deterministic field rejection \u2014 fix the value, do not retry. Consult operations.fieldCatalog for the field catalog (types, required, maxLength)."}`
2634
2643
  ],
2635
2644
  "deleteBoundary": {
2636
2645
  "available": false,
@@ -11940,16 +11949,20 @@ var CREATE_GUIDANCE = {
11940
11949
  "liveProvenPrefix": [
11941
11950
  "endDateTime",
11942
11951
  "actionType",
11943
- "startDateTime"
11952
+ "startDateTime",
11953
+ "assignedResourceID"
11944
11954
  ],
11945
- "unproven": true,
11955
+ "unproven": false,
11946
11956
  "blocker": {
11947
- "kind": "unknown",
11948
- "message": "create refused (stage-11 apply-matrix, marker FULL100W-1791110339): the final vendor response is preserved verbatim in vendorEvidence (the campaign artifact; its 300-char detail truncation is as recorded on disk)",
11949
- "remedy": "the wall is opaque in the campaign record \u2014 inspect vendorEvidence for the exact vendor text, re-run the create with a live probe (dry-run first), and adjudicate against the vendored spec before retrying."
11957
+ "kind": "delete-absent",
11958
+ "message": 'create succeeded live (DG-1 verification, 2026-10-11, ww29, parent company 29683380, record id 30230658, marker name "node-autotask DG-1 create probe") once the four vendor-required body fields were present \u2014 actionType, assignedResourceID, startDateTime, endDateTime \u2014 plus the parent companyID in the path. The vendor reports a missing required field as HTTP 500 SERVER_ERROR naming the field; it is a deterministic rejection, not a transient fault (the SDK now answers retryable: false \u2014 add the field, do not retry). The vendor defines no DELETE endpoint for this resource (W-A1) \u2014 the record is residue; cleanup is UI-only.',
11959
+ "remedy": "provision via the API as proven: send actionType (picklist, e.g. 3 = Note: General), assignedResourceID (a resource id), startDateTime and endDateTime (UTC ISO) in the body and the company id as the parent; name and note are optional content fields. Clean up the residue record in the Autotask UI (the marker name is the lookup handle) \u2014 there is no API delete path."
11950
11960
  },
11951
11961
  "vendorEvidence": [
11952
- '{"error":true,"code":"SERVER_ERROR","category":"server","retryable":true,"httpStatus":500,"operation":"company_notes.create","suggestedAction":"Retry with backoff; the server error is transient.","correlationId":"7ef594cb-74dd-4a29-a7f7-2a5d5eb515f0","message":"Value does not exist for the required '
11962
+ `{"error":true,"code":"SERVER_ERROR","category":"server","retryable":false,"httpStatus":500,"operation":"company_notes.create","message":"Field 'assignedResourceID': Value does not exist for the required field assignedResourceID. ; on record number [1].","suggestedAction":"Deterministic field rejection \u2014 fix the value, do not retry. Consult operations.fieldCatalog for the field catalog (types, required, maxLength)."}`,
11963
+ `{"error":true,"code":"SERVER_ERROR","category":"server","retryable":false,"httpStatus":500,"operation":"company_notes.create","message":"Field 'actionType': Missing Required Field: actionType. ; on record number [1].","suggestedAction":"Deterministic field rejection \u2014 fix the value, do not retry. Consult operations.fieldCatalog for the field catalog (types, required, maxLength)."}`,
11964
+ `{"error":true,"code":"SERVER_ERROR","category":"server","retryable":false,"httpStatus":500,"operation":"company_notes.create","message":"Field 'endDateTime': Missing Required Field: endDateTime. ; on record number [1].","suggestedAction":"Deterministic field rejection \u2014 fix the value, do not retry. Consult operations.fieldCatalog for the field catalog (types, required, maxLength)."}`,
11965
+ `{"error":true,"code":"SERVER_ERROR","category":"server","retryable":false,"httpStatus":500,"operation":"company_notes.create","message":"Field 'startDateTime': Missing Required Field: startDateTime. ; on record number [1].","suggestedAction":"Deterministic field rejection \u2014 fix the value, do not retry. Consult operations.fieldCatalog for the field catalog (types, required, maxLength)."}`
11953
11966
  ],
11954
11967
  "deleteBoundary": {
11955
11968
  "available": false,
@@ -13936,9 +13949,9 @@ var CREATE_GUIDANCE = {
13936
13949
  "picklist-unexposed": 5,
13937
13950
  "undocumented-ladder": 1,
13938
13951
  "resource-role-pair": 2,
13939
- "delete-absent": 7,
13952
+ "delete-absent": 8,
13940
13953
  "non-mutable": 0,
13941
- "unknown": 13
13954
+ "unknown": 12
13942
13955
  }
13943
13956
  }
13944
13957
  };
@@ -16126,6 +16139,182 @@ async function fieldCatalog(client, opts) {
16126
16139
  };
16127
16140
  }
16128
16141
 
16142
+ // src/operations/compositeNames.ts
16143
+ var PICKLIST_VALUE_NOT_SERVED_NOTE = "The picklist is served to this API user, but this record's value is not in it (an inactive or unexposed value) \u2014 the label cannot be resolved and is null; refreshing the field catalog will not surface it. (A picklist the vendor does not expose at all is reported with the field-catalog honesty note instead.)";
16144
+ var DEFAULT_MAX_NAME_READS = 20;
16145
+ var TICKET_LABEL_FIELDS = ["status", "priority", "ticketType", "issueType", "subIssueType", "source", "queueID"];
16146
+ function isVendorId(value) {
16147
+ if (typeof value === "number") return Number.isSafeInteger(value) && value >= 0;
16148
+ if (typeof value === "string") return /^\d+$/.test(value);
16149
+ return false;
16150
+ }
16151
+ function createNamePlan(labelFields = []) {
16152
+ const resourceIds = [];
16153
+ const resSeen = /* @__PURE__ */ new Set();
16154
+ const recordFks = [];
16155
+ const fkSeen = /* @__PURE__ */ new Set();
16156
+ return {
16157
+ plan: () => ({ resourceIds, recordFks, labelFields }),
16158
+ addResource: (value) => {
16159
+ if (!isVendorId(value)) return;
16160
+ const k = String(value);
16161
+ if (!resSeen.has(k)) {
16162
+ resSeen.add(k);
16163
+ resourceIds.push(value);
16164
+ }
16165
+ },
16166
+ addFk: (table, value) => {
16167
+ if (!isVendorId(value)) return;
16168
+ const k = `${table}:${String(value)}`;
16169
+ if (!fkSeen.has(k)) {
16170
+ fkSeen.add(k);
16171
+ recordFks.push({ table, id: value });
16172
+ }
16173
+ }
16174
+ };
16175
+ }
16176
+ function addNameSibling(row, field, resolve) {
16177
+ const value = row[field];
16178
+ if (value === void 0) return;
16179
+ row[`${field}Name`] = isVendorId(value) ? resolve(value) : null;
16180
+ }
16181
+ function addLabelSibling(row, field, names) {
16182
+ const value = row[field];
16183
+ if (value === void 0) return;
16184
+ row[`${field}Label`] = names.labelFor(field, value);
16185
+ }
16186
+ var FK_TABLE_PATH = {
16187
+ role: "/V1.0/Roles",
16188
+ billingCode: "/V1.0/BillingCodes",
16189
+ contract: "/V1.0/Contracts"
16190
+ };
16191
+ var FK_NAME_FIELD = {
16192
+ role: "name",
16193
+ billingCode: "name",
16194
+ contract: "contractName"
16195
+ };
16196
+ function fkName(record, table) {
16197
+ const value = record[FK_NAME_FIELD[table]];
16198
+ return typeof value === "string" && value.trim() !== "" ? value : null;
16199
+ }
16200
+ function faultRow(err) {
16201
+ const e = err;
16202
+ return e.httpStatus !== void 0 ? { code: e.code, message: e.message, httpStatus: e.httpStatus } : { code: e.code, message: e.message };
16203
+ }
16204
+ async function resolveCompositeNames(http, plan, opts = {}) {
16205
+ if (opts.maxNameReads !== void 0 && (!Number.isInteger(opts.maxNameReads) || opts.maxNameReads < 1)) {
16206
+ throw new AutotaskConfigError(`composite names: maxNameReads must be an integer >= 1 (got ${String(opts.maxNameReads)})`);
16207
+ }
16208
+ const budget = Math.min(DEFAULT_MAX_NAME_READS, opts.maxNameReads ?? DEFAULT_MAX_NAME_READS);
16209
+ const client = http.client;
16210
+ if (client === void 0) {
16211
+ throw new AutotaskConfigError("composite names: the transport is not wired to an AutotaskClient (the composite must run through the client)");
16212
+ }
16213
+ const readCache = http.readCache;
16214
+ const nameCache = http.resourceNameCache;
16215
+ const section = { truncated: false, rows: {}, reasons: {} };
16216
+ let remaining = budget;
16217
+ const coldResource = [];
16218
+ for (const id of plan.resourceIds) {
16219
+ if (nameCache !== void 0 && nameCache.get(id) !== void 0) continue;
16220
+ if (readCache !== void 0 && readCache.get(`/V1.0/Resources/${id}`) !== void 0) continue;
16221
+ coldResource.push(id);
16222
+ }
16223
+ const withinBudget = coldResource.slice(0, remaining);
16224
+ const overBudgetResource = coldResource.slice(remaining);
16225
+ remaining -= withinBudget.length;
16226
+ for (const id of overBudgetResource) {
16227
+ section.rows[`resource:${id}`] = { name: null, blind: false, capped: true };
16228
+ section.truncated = true;
16229
+ }
16230
+ const overSet = new Set(overBudgetResource.map((id) => String(id)));
16231
+ const opIds = plan.resourceIds.filter((id) => !overSet.has(String(id)));
16232
+ if (opIds.length > 0) {
16233
+ let pass = opIds;
16234
+ if (pass.length > 500) {
16235
+ for (const id of pass.slice(500)) {
16236
+ section.rows[`resource:${id}`] = { name: null, blind: false, capped: true };
16237
+ section.truncated = true;
16238
+ }
16239
+ pass = pass.slice(0, 500);
16240
+ }
16241
+ const res = await expandResourceNames(client, pass);
16242
+ for (const r of res.results) {
16243
+ const row = { name: r.name, blind: r.blind, capped: false };
16244
+ if (r.error !== void 0) row.error = faultRow(r.error);
16245
+ section.rows[`resource:${r.id}`] = row;
16246
+ }
16247
+ }
16248
+ const holders = {
16249
+ role: client.roles,
16250
+ billingCode: client.billingCodes,
16251
+ contract: client.contracts
16252
+ };
16253
+ for (const fk of plan.recordFks) {
16254
+ const key = `${fk.table}:${fk.id}`;
16255
+ const cold = readCache === void 0 || readCache.get(`${FK_TABLE_PATH[fk.table]}/${fk.id}`) === void 0;
16256
+ if (cold) {
16257
+ if (remaining < 1) {
16258
+ section.rows[key] = { name: null, blind: false, capped: true };
16259
+ section.truncated = true;
16260
+ continue;
16261
+ }
16262
+ remaining -= 1;
16263
+ }
16264
+ try {
16265
+ const record = await holders[fk.table].get(fk.id);
16266
+ section.rows[key] = { name: fkName(record, fk.table), blind: false, capped: false };
16267
+ } catch (err) {
16268
+ if (err instanceof NotFoundError) {
16269
+ section.rows[key] = { name: null, blind: true, capped: false };
16270
+ } else if (isAutotaskError(err) && err.code !== "ABORTED") {
16271
+ section.rows[key] = { name: null, blind: false, capped: false, error: faultRow(err) };
16272
+ } else {
16273
+ throw err;
16274
+ }
16275
+ }
16276
+ }
16277
+ const picklists = {};
16278
+ if (plan.labelFields.length > 0) {
16279
+ let usable = true;
16280
+ try {
16281
+ const catalog = await fieldCatalog(client, { resources: ["tickets"], labels: true, fields: [...plan.labelFields] });
16282
+ const row = catalog.rows.find((r) => r.resource === "tickets");
16283
+ if (row === void 0 || row.error !== null || row.fields === void 0) {
16284
+ usable = false;
16285
+ } else {
16286
+ for (const f of plan.labelFields) {
16287
+ const frow = row.fields.find((x) => x.name === f);
16288
+ picklists[f] = frow?.picklistValues;
16289
+ }
16290
+ }
16291
+ } catch (err) {
16292
+ if (isAutotaskError(err) && err.code !== "ABORTED") usable = false;
16293
+ else throw err;
16294
+ }
16295
+ if (!usable) for (const f of plan.labelFields) section.reasons[f] = FIELD_CATALOG_HONESTY_NOTE;
16296
+ }
16297
+ return {
16298
+ section,
16299
+ resourceName: (id) => section.rows[`resource:${id}`]?.name ?? null,
16300
+ recordName: (table, id) => section.rows[`${table}:${id}`]?.name ?? null,
16301
+ labelFor: (field, value) => {
16302
+ if (value === null || value === void 0) return null;
16303
+ const values = picklists[field];
16304
+ if (values === void 0) {
16305
+ section.reasons[field] = FIELD_CATALOG_HONESTY_NOTE;
16306
+ return null;
16307
+ }
16308
+ const hit = values.find((v) => String(v.value) === String(value));
16309
+ if (hit === void 0) {
16310
+ section.reasons[field] = PICKLIST_VALUE_NOT_SERVED_NOTE;
16311
+ return null;
16312
+ }
16313
+ return typeof hit.label === "string" ? hit.label : null;
16314
+ }
16315
+ };
16316
+ }
16317
+
16129
16318
  // src/resources/base.ts
16130
16319
  var BOUNDED_QUERY_DEFAULT_LIMIT = 500;
16131
16320
  var BOUNDED_QUERY_MAX_LIMIT = 2500;
@@ -18008,6 +18197,11 @@ export {
18008
18197
  WORKFLOW_RESOURCES,
18009
18198
  MAX_FIELD_CATALOG_WIRE,
18010
18199
  FIELD_CATALOG_HONESTY_NOTE,
18011
- fieldCatalog
18200
+ fieldCatalog,
18201
+ PICKLIST_VALUE_NOT_SERVED_NOTE,
18202
+ TICKET_LABEL_FIELDS,
18203
+ createNamePlan,
18204
+ addNameSibling,
18205
+ addLabelSibling,
18206
+ resolveCompositeNames
18012
18207
  };
18013
- //# sourceMappingURL=chunk-UDWV4VVD.js.map