node-autotask 0.31.0 → 0.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/MCP_TOOL_CATALOG.json +31 -12
  3. package/MCP_TOOL_MANIFEST.md +18 -19
  4. package/MCP_TOOL_OVERRIDES.json +0 -12
  5. package/N8N_MCP_MANIFEST.json +44 -18
  6. package/N8N_MCP_MANIFEST.md +15 -14
  7. package/README.md +8 -6
  8. package/capabilities.json +1579 -1414
  9. package/dist/capabilities.cjs +2 -2
  10. package/dist/capabilities.js +1 -1
  11. package/dist/{chunk-3H3RJ4LA.cjs → chunk-3LAZZT7V.cjs} +143 -143
  12. package/dist/{chunk-PCLL536B.cjs → chunk-4QN43TRP.cjs} +75 -50
  13. package/dist/{chunk-6UA544OR.js → chunk-5DS6F6GF.js} +299 -113
  14. package/dist/{chunk-RQZUV74N.js → chunk-7WNE5MPG.js} +2 -2
  15. package/dist/{chunk-INSFDXHP.js → chunk-MFDQHFGJ.js} +28 -3
  16. package/dist/{chunk-DQLEVKZS.cjs → chunk-OO67MRHN.cjs} +814 -628
  17. package/dist/{chunk-QXUN2VCJ.cjs → chunk-SP2SJJZX.cjs} +1578 -1413
  18. package/dist/{chunk-EPOHRNSX.js → chunk-VKE56TTN.js} +1578 -1413
  19. package/dist/{index-Uqj987EE.d.ts → index-BaeMHZPk.d.ts} +149 -12
  20. package/dist/{index--5jrRl4u.d.cts → index-Cq0VsfSz.d.cts} +149 -12
  21. package/dist/index.cjs +22 -22
  22. package/dist/index.d.cts +2 -2
  23. package/dist/index.d.ts +2 -2
  24. package/dist/index.js +4 -4
  25. package/dist/operations/index.cjs +120 -133
  26. package/dist/operations/index.d.cts +2 -2
  27. package/dist/operations/index.d.ts +2 -2
  28. package/dist/operations/index.js +3 -16
  29. package/dist/pool.cjs +9 -9
  30. package/dist/pool.d.cts +1 -1
  31. package/dist/pool.d.ts +1 -1
  32. package/dist/pool.js +4 -4
  33. package/dist/resources/index.cjs +4 -4
  34. package/dist/resources/index.d.cts +1 -1
  35. package/dist/resources/index.d.ts +1 -1
  36. package/dist/resources/index.js +3 -3
  37. package/docs/API.md +62 -37
  38. package/package.json +2 -1
@@ -1388,7 +1388,7 @@ declare class HttpClient {
1388
1388
  * queryBounded noteChars free-text cap (issue #61 — response-side only);
1389
1389
  * - docs/API.md §11.7/§11.8/§11.9/§11.10/§11.11/§11.12 render the same facts.
1390
1390
  *
1391
- * planHash: 030cd1af51b9263090d81a46ee44c1180d67e748d7b25383af89307684ff6b3c
1391
+ * planHash: 913f1120488735c2e607cda13f9d551b11b3e6534d8a4b8310857d0bbd2fdb45
1392
1392
  */
1393
1393
 
1394
1394
  /** One pinned blind-surface stamp (the result-time sentinel object). */
@@ -2713,7 +2713,7 @@ interface CompaniesUpsertByNameResult {
2713
2713
  /** False when the dedup match short-circuited the create. */
2714
2714
  created: boolean;
2715
2715
  /** The matched record (present when created is false). */
2716
- existing?: Record<string, unknown>;
2716
+ existing?: Company;
2717
2717
  /** The dedup boundary carried IN the result (a query-miss is NOT proof of absence). */
2718
2718
  blindSurface: CompaniesBlindSurfaceCaveat;
2719
2719
  /** The vendor side effects reported on the created result (the V5 auto-location). */
@@ -2824,6 +2824,90 @@ interface CompanyFullProfile {
2824
2824
  };
2825
2825
  }
2826
2826
 
2827
+ type CrmIntent = 'create_company' | 'add_contact' | 'update_contact' | 'update_company';
2828
+ interface CrmPrepareInput {
2829
+ intent: CrmIntent;
2830
+ company?: {
2831
+ id?: number;
2832
+ companyName?: string;
2833
+ /** A bare domain or URL; derived from `website` or the contact email when omitted. */
2834
+ domain?: string;
2835
+ website?: string;
2836
+ phone?: string;
2837
+ companyType?: number;
2838
+ ownerResourceID?: number;
2839
+ address1?: string;
2840
+ city?: string;
2841
+ state?: string;
2842
+ postalCode?: string;
2843
+ countryID?: number;
2844
+ };
2845
+ contact?: {
2846
+ id?: number;
2847
+ firstName?: string;
2848
+ lastName?: string;
2849
+ emailAddress?: string;
2850
+ phone?: string;
2851
+ mobilePhone?: string;
2852
+ companyLocationID?: number;
2853
+ /** Used only to pick a location when the company has several. */
2854
+ city?: string;
2855
+ state?: string;
2856
+ postalCode?: string;
2857
+ };
2858
+ }
2859
+ /** One thing the caller must still supply, with how to get it. */
2860
+ interface CrmNeedsInput {
2861
+ field: string;
2862
+ why: string;
2863
+ /** `live-proven`: the vendor demanded it on a real create; `policy`: this planner requires it (CRM hygiene). */
2864
+ source: 'live-proven' | 'policy';
2865
+ /** How to obtain the value: a lookup to run, or candidates to choose from. */
2866
+ lookup?: Record<string, unknown>;
2867
+ /** `web_if_available_else_ask`: search the web when the client can, otherwise ask the user; confirm any web-sourced value before writing. `lookup`: resolve it with the lookup, do not ask. `ask`: only the user knows. */
2868
+ research: 'web_if_available_else_ask' | 'lookup' | 'ask';
2869
+ }
2870
+ interface CrmCompanyCandidate {
2871
+ id: number;
2872
+ companyName: string | null;
2873
+ webAddress: string | null;
2874
+ phone: string | null;
2875
+ isActive: boolean | null;
2876
+ }
2877
+ interface CrmPrepareResult {
2878
+ intent: CrmIntent;
2879
+ company: {
2880
+ status: 'matched' | 'ambiguous' | 'none' | 'not_searched';
2881
+ matchedBy: string | null;
2882
+ candidates: CrmCompanyCandidate[];
2883
+ };
2884
+ locations: null | {
2885
+ items: Array<{
2886
+ id: number;
2887
+ name: string | null;
2888
+ isPrimary: boolean | null;
2889
+ isActive: boolean | null;
2890
+ city: string | null;
2891
+ state: string | null;
2892
+ postalCode: string | null;
2893
+ }>;
2894
+ pick: {
2895
+ rule: 'single' | 'matched' | 'ask' | 'none';
2896
+ locationID: number | null;
2897
+ };
2898
+ };
2899
+ contacts: null | {
2900
+ duplicates: Array<Record<string, unknown>>;
2901
+ matchedBy: 'email' | 'name' | null;
2902
+ };
2903
+ needsInput: CrmNeedsInput[];
2904
+ next: null | {
2905
+ operation: string;
2906
+ note: string;
2907
+ };
2908
+ caveats: string[];
2909
+ }
2910
+
2827
2911
  /** The closed `source` set for `companies.unbilledWork`. */
2828
2912
  type UnbilledWorkSource = 'posted_uninvoiced' | 'invoice' | 'date_range' | 'tickets';
2829
2913
  /** Options for `unbilledWork` (closed bag; the pinned defaults). */
@@ -3003,6 +3087,8 @@ declare class CompaniesResource extends BaseResource<Company, CompanyCreate, Com
3003
3087
  fullProfile(id: Identifier, opts?: CompanyFullProfileOptions): Promise<CompanyFullProfile>;
3004
3088
  /** Node-parity special op (hand-written in src/resources/special/companies.ts). */
3005
3089
  unbilledWork(id: Identifier, opts?: UnbilledWorkOptions): Promise<UnbilledWorkResult>;
3090
+ /** Node-parity special op (hand-written in src/resources/special/companies.ts). */
3091
+ prepareCrmWrite(input: CrmPrepareInput): Promise<CrmPrepareResult>;
3006
3092
  }
3007
3093
 
3008
3094
  /**
@@ -8039,15 +8125,44 @@ interface MoveManualAction {
8039
8125
  uiLink: string | null;
8040
8126
  reason: string;
8041
8127
  }
8128
+ /** The source record as read before the move (a vendor TimeEntry / TicketCharge row; fields vary by resource, so only the common ones are named). */
8129
+ interface MoveSnapshot {
8130
+ id?: number;
8131
+ ticketID?: number | null;
8132
+ taskID?: number | null;
8133
+ contractID?: number | null;
8134
+ resourceID?: number;
8135
+ lastModifiedDateTime?: string;
8136
+ lastModifiedDate?: string;
8137
+ /** Any other vendor field of the source record. */
8138
+ [field: string]: unknown;
8139
+ }
8140
+ /** The soft facts a move reports (not refusals). */
8141
+ interface MoveFacts {
8142
+ /** The tenant's closed-ticket-status read (or the disclosed fallback). */
8143
+ closedStatus: {
8144
+ closedIds: number[];
8145
+ source: 'picklist' | 'assumed';
8146
+ note: string;
8147
+ };
8148
+ sourceTicketId: number;
8149
+ sourceCompanyID: number | null;
8150
+ targetCompanyID: number | null;
8151
+ /** null when the source ticket could not be read. */
8152
+ companyChanged: boolean | null;
8153
+ contractChanged: boolean | null;
8154
+ /** Resource-specific soft facts added by a move spec's checkSource. */
8155
+ [fact: string]: unknown;
8156
+ }
8042
8157
  interface MoveResult {
8043
8158
  status: MoveStatus;
8044
8159
  sourceId: number;
8045
8160
  newId: number;
8046
8161
  correlationId: string;
8047
8162
  /** The full source record as read before the delete — the caller persists it (the SDK stores nothing). */
8048
- snapshot: Record<string, unknown>;
8163
+ snapshot: MoveSnapshot;
8049
8164
  /** Soft facts (not refusals): companyChanged, contractChanged, ... */
8050
- facts: Record<string, unknown>;
8165
+ facts: MoveFacts;
8051
8166
  manualAction?: MoveManualAction;
8052
8167
  }
8053
8168
  interface MoveToTicketOptions {
@@ -8876,14 +8991,36 @@ declare class TicketWebhooksResource extends BaseResource<TicketWebhook, TicketW
8876
8991
  * behaviour bit-for-bit) under its historical name. */
8877
8992
  type TimeEntriesWindowOptions = DateWindowOptions;
8878
8993
  interface TimeEntriesStatusOptions {
8879
- /** Max records (default 500 = the hard cap, issue #128). */
8994
+ /** Max entries read (default 500 = the hard cap, issue #128); `hasMore` flags a capped read. */
8880
8995
  limit?: number;
8881
8996
  /** Optional ticketID eq narrowing (node parity). */
8882
8997
  ticketID?: number;
8998
+ /** Inclusive `dateWorked` window start, 'YYYY-MM-DD' (a calendar day, no tz shift — dateWorked is date-only). */
8999
+ /** OPTIONAL, but pass it: a call without dates reads the tenant's first 500 time entries (effectively the oldest), a near-empty/misleading slice with `hasMore` true. */
9000
+ dateFrom?: string;
9001
+ /** Inclusive `dateWorked` window end, 'YYYY-MM-DD'. */
9002
+ /** OPTIONAL, but pass it together with `dateFrom` (see there). */
9003
+ dateTo?: string;
9004
+ }
9005
+ /** The getPosted / getUnposted result: the classified entries plus an honest truncation flag. */
9006
+ interface TimeEntriesPostedStateResult {
9007
+ /** getPosted: entries a billing item references. getUnposted: entries no billing item references. */
9008
+ items: TimeEntry[];
9009
+ /** True when the entry read or a billing-item join page had more rows than one bounded page: the result is partial (a window with more than 500 entries returns the first 500) — narrow the window. */
9010
+ hasMore: boolean;
9011
+ /** The inclusive dateWorked window queried (second-precision UTC); a side is null when that bound was not given. */
9012
+ window: {
9013
+ from: string | null;
9014
+ to: string | null;
9015
+ };
9016
+ /** INFORMATIONAL only, not posted state: items whose `billingApprovalDateTime` is set. Some tenants (ww29) never populate it. */
9017
+ approvedCount: number;
9018
+ /** INFORMATIONAL only, not posted state: items without `billingApprovalDateTime`. */
9019
+ unapprovedCount: number;
8883
9020
  }
8884
9021
  /** postedWithin options: the optional narrowing + the shared limit bound. */
8885
9022
  interface TimeEntriesPostedWindowOptions {
8886
- /** Posted narrowing: `true` = posted rows only (postedDate exist), `false` = unposted rows only (postedDate notExist), omitted = both (the counts split the returned items). On a zone whose entity lacks the field (W-D3, e.g. ww29 — see the pointer note) the vendor 500s and the typed note explains. */
9023
+ /** Posted narrowing, applied CLIENT-side after the billing-item join: `true` = posted rows only, `false` = unposted rows only (entries no billing item references), omitted = both. No postedDate condition is ever sent. */
8887
9024
  posted?: boolean;
8888
9025
  /** Max records (default 500 = the hard cap — a window with more than 500 matching rows: the bounded query op (time_entries.query) with the same dateWorked filter, its 2500-row walk cap; issue #128). */
8889
9026
  limit?: number;
@@ -8896,18 +9033,18 @@ interface TimeEntriesPostedWindowOptions {
8896
9033
  }
8897
9034
  /** The posted-window read result (issue #10): the window's items + the two-direction count in one pass. */
8898
9035
  interface TimeEntriesPostedWindowResult {
8899
- /** The window's entries (bounded by `limit`). */
9036
+ /** The window's entries read, narrowed by `posted` when set (bounded by `limit`). */
8900
9037
  items: TimeEntry[];
8901
- /** The items carrying a set `postedDate` (the live-entity field — W-D3). */
9038
+ /** Of EVERY entry read (before the `posted` narrowing), those a billing item references (the join — never a `postedDate` read). With `posted` set, `items.length` can differ from postedCount + unpostedCount. */
8902
9039
  postedCount: number;
8903
- /** The items with an empty `postedDate`. */
9040
+ /** Of EVERY entry read (before the `posted` narrowing), those no billing item references — non-zero even with `posted: true` (entries whose join page was truncated are in neither count). */
8904
9041
  unpostedCount: number;
8905
9042
  /** The inclusive UTC window actually queried (wire bounds, second precision). */
8906
9043
  window: {
8907
9044
  from: string;
8908
9045
  to: string;
8909
9046
  };
8910
- /** True when the vendor reports more matching records than the bounded read returned (a live `pageDetails.nextPageUrl` cursor) — the posted/unposted counts then cover the returned rows ONLY (at most `limit`); re-query with a wider window or a narrowing filter to reconcile the totals. */
9047
+ /** True when the entry read has a live `pageDetails.nextPageUrl` cursor (more matching records than the bounded read returned) OR a billing-item join page was truncated (those entries are in neither count) — the posted/unposted counts then cover the returned rows ONLY (at most `limit`); re-query with a wider window or a narrowing filter to reconcile the totals. */
8911
9048
  truncated: boolean;
8912
9049
  }
8913
9050
 
@@ -9115,9 +9252,9 @@ declare class TimeEntriesResource extends BaseResource<TimeEntry, TimeEntryCreat
9115
9252
  /** Node-parity special op (hand-written in src/resources/special/time_entries.ts). */
9116
9253
  dateWorkedWindow(from: string, to: string, opts?: TimeEntriesPostedWindowOptions): Promise<TimeEntriesPostedWindowResult>;
9117
9254
  /** Node-parity special op (hand-written in src/resources/special/time_entries.ts). */
9118
- getPosted(opts?: TimeEntriesStatusOptions): Promise<TimeEntry[]>;
9255
+ getPosted(opts?: TimeEntriesStatusOptions): Promise<TimeEntriesPostedStateResult>;
9119
9256
  /** Node-parity special op (hand-written in src/resources/special/time_entries.ts). */
9120
- getUnposted(opts?: TimeEntriesStatusOptions): Promise<TimeEntry[]>;
9257
+ getUnposted(opts?: TimeEntriesStatusOptions): Promise<TimeEntriesPostedStateResult>;
9121
9258
  /** Node-parity special op (hand-written in src/resources/special/time_entries.ts). */
9122
9259
  last14(opts?: TimeEntriesWindowOptions): Promise<TimeEntry[]>;
9123
9260
  /** Node-parity special op (hand-written in src/resources/special/time_entries.ts). */
@@ -1388,7 +1388,7 @@ declare class HttpClient {
1388
1388
  * queryBounded noteChars free-text cap (issue #61 — response-side only);
1389
1389
  * - docs/API.md §11.7/§11.8/§11.9/§11.10/§11.11/§11.12 render the same facts.
1390
1390
  *
1391
- * planHash: 030cd1af51b9263090d81a46ee44c1180d67e748d7b25383af89307684ff6b3c
1391
+ * planHash: 913f1120488735c2e607cda13f9d551b11b3e6534d8a4b8310857d0bbd2fdb45
1392
1392
  */
1393
1393
 
1394
1394
  /** One pinned blind-surface stamp (the result-time sentinel object). */
@@ -2713,7 +2713,7 @@ interface CompaniesUpsertByNameResult {
2713
2713
  /** False when the dedup match short-circuited the create. */
2714
2714
  created: boolean;
2715
2715
  /** The matched record (present when created is false). */
2716
- existing?: Record<string, unknown>;
2716
+ existing?: Company;
2717
2717
  /** The dedup boundary carried IN the result (a query-miss is NOT proof of absence). */
2718
2718
  blindSurface: CompaniesBlindSurfaceCaveat;
2719
2719
  /** The vendor side effects reported on the created result (the V5 auto-location). */
@@ -2824,6 +2824,90 @@ interface CompanyFullProfile {
2824
2824
  };
2825
2825
  }
2826
2826
 
2827
+ type CrmIntent = 'create_company' | 'add_contact' | 'update_contact' | 'update_company';
2828
+ interface CrmPrepareInput {
2829
+ intent: CrmIntent;
2830
+ company?: {
2831
+ id?: number;
2832
+ companyName?: string;
2833
+ /** A bare domain or URL; derived from `website` or the contact email when omitted. */
2834
+ domain?: string;
2835
+ website?: string;
2836
+ phone?: string;
2837
+ companyType?: number;
2838
+ ownerResourceID?: number;
2839
+ address1?: string;
2840
+ city?: string;
2841
+ state?: string;
2842
+ postalCode?: string;
2843
+ countryID?: number;
2844
+ };
2845
+ contact?: {
2846
+ id?: number;
2847
+ firstName?: string;
2848
+ lastName?: string;
2849
+ emailAddress?: string;
2850
+ phone?: string;
2851
+ mobilePhone?: string;
2852
+ companyLocationID?: number;
2853
+ /** Used only to pick a location when the company has several. */
2854
+ city?: string;
2855
+ state?: string;
2856
+ postalCode?: string;
2857
+ };
2858
+ }
2859
+ /** One thing the caller must still supply, with how to get it. */
2860
+ interface CrmNeedsInput {
2861
+ field: string;
2862
+ why: string;
2863
+ /** `live-proven`: the vendor demanded it on a real create; `policy`: this planner requires it (CRM hygiene). */
2864
+ source: 'live-proven' | 'policy';
2865
+ /** How to obtain the value: a lookup to run, or candidates to choose from. */
2866
+ lookup?: Record<string, unknown>;
2867
+ /** `web_if_available_else_ask`: search the web when the client can, otherwise ask the user; confirm any web-sourced value before writing. `lookup`: resolve it with the lookup, do not ask. `ask`: only the user knows. */
2868
+ research: 'web_if_available_else_ask' | 'lookup' | 'ask';
2869
+ }
2870
+ interface CrmCompanyCandidate {
2871
+ id: number;
2872
+ companyName: string | null;
2873
+ webAddress: string | null;
2874
+ phone: string | null;
2875
+ isActive: boolean | null;
2876
+ }
2877
+ interface CrmPrepareResult {
2878
+ intent: CrmIntent;
2879
+ company: {
2880
+ status: 'matched' | 'ambiguous' | 'none' | 'not_searched';
2881
+ matchedBy: string | null;
2882
+ candidates: CrmCompanyCandidate[];
2883
+ };
2884
+ locations: null | {
2885
+ items: Array<{
2886
+ id: number;
2887
+ name: string | null;
2888
+ isPrimary: boolean | null;
2889
+ isActive: boolean | null;
2890
+ city: string | null;
2891
+ state: string | null;
2892
+ postalCode: string | null;
2893
+ }>;
2894
+ pick: {
2895
+ rule: 'single' | 'matched' | 'ask' | 'none';
2896
+ locationID: number | null;
2897
+ };
2898
+ };
2899
+ contacts: null | {
2900
+ duplicates: Array<Record<string, unknown>>;
2901
+ matchedBy: 'email' | 'name' | null;
2902
+ };
2903
+ needsInput: CrmNeedsInput[];
2904
+ next: null | {
2905
+ operation: string;
2906
+ note: string;
2907
+ };
2908
+ caveats: string[];
2909
+ }
2910
+
2827
2911
  /** The closed `source` set for `companies.unbilledWork`. */
2828
2912
  type UnbilledWorkSource = 'posted_uninvoiced' | 'invoice' | 'date_range' | 'tickets';
2829
2913
  /** Options for `unbilledWork` (closed bag; the pinned defaults). */
@@ -3003,6 +3087,8 @@ declare class CompaniesResource extends BaseResource<Company, CompanyCreate, Com
3003
3087
  fullProfile(id: Identifier, opts?: CompanyFullProfileOptions): Promise<CompanyFullProfile>;
3004
3088
  /** Node-parity special op (hand-written in src/resources/special/companies.ts). */
3005
3089
  unbilledWork(id: Identifier, opts?: UnbilledWorkOptions): Promise<UnbilledWorkResult>;
3090
+ /** Node-parity special op (hand-written in src/resources/special/companies.ts). */
3091
+ prepareCrmWrite(input: CrmPrepareInput): Promise<CrmPrepareResult>;
3006
3092
  }
3007
3093
 
3008
3094
  /**
@@ -8039,15 +8125,44 @@ interface MoveManualAction {
8039
8125
  uiLink: string | null;
8040
8126
  reason: string;
8041
8127
  }
8128
+ /** The source record as read before the move (a vendor TimeEntry / TicketCharge row; fields vary by resource, so only the common ones are named). */
8129
+ interface MoveSnapshot {
8130
+ id?: number;
8131
+ ticketID?: number | null;
8132
+ taskID?: number | null;
8133
+ contractID?: number | null;
8134
+ resourceID?: number;
8135
+ lastModifiedDateTime?: string;
8136
+ lastModifiedDate?: string;
8137
+ /** Any other vendor field of the source record. */
8138
+ [field: string]: unknown;
8139
+ }
8140
+ /** The soft facts a move reports (not refusals). */
8141
+ interface MoveFacts {
8142
+ /** The tenant's closed-ticket-status read (or the disclosed fallback). */
8143
+ closedStatus: {
8144
+ closedIds: number[];
8145
+ source: 'picklist' | 'assumed';
8146
+ note: string;
8147
+ };
8148
+ sourceTicketId: number;
8149
+ sourceCompanyID: number | null;
8150
+ targetCompanyID: number | null;
8151
+ /** null when the source ticket could not be read. */
8152
+ companyChanged: boolean | null;
8153
+ contractChanged: boolean | null;
8154
+ /** Resource-specific soft facts added by a move spec's checkSource. */
8155
+ [fact: string]: unknown;
8156
+ }
8042
8157
  interface MoveResult {
8043
8158
  status: MoveStatus;
8044
8159
  sourceId: number;
8045
8160
  newId: number;
8046
8161
  correlationId: string;
8047
8162
  /** The full source record as read before the delete — the caller persists it (the SDK stores nothing). */
8048
- snapshot: Record<string, unknown>;
8163
+ snapshot: MoveSnapshot;
8049
8164
  /** Soft facts (not refusals): companyChanged, contractChanged, ... */
8050
- facts: Record<string, unknown>;
8165
+ facts: MoveFacts;
8051
8166
  manualAction?: MoveManualAction;
8052
8167
  }
8053
8168
  interface MoveToTicketOptions {
@@ -8876,14 +8991,36 @@ declare class TicketWebhooksResource extends BaseResource<TicketWebhook, TicketW
8876
8991
  * behaviour bit-for-bit) under its historical name. */
8877
8992
  type TimeEntriesWindowOptions = DateWindowOptions;
8878
8993
  interface TimeEntriesStatusOptions {
8879
- /** Max records (default 500 = the hard cap, issue #128). */
8994
+ /** Max entries read (default 500 = the hard cap, issue #128); `hasMore` flags a capped read. */
8880
8995
  limit?: number;
8881
8996
  /** Optional ticketID eq narrowing (node parity). */
8882
8997
  ticketID?: number;
8998
+ /** Inclusive `dateWorked` window start, 'YYYY-MM-DD' (a calendar day, no tz shift — dateWorked is date-only). */
8999
+ /** OPTIONAL, but pass it: a call without dates reads the tenant's first 500 time entries (effectively the oldest), a near-empty/misleading slice with `hasMore` true. */
9000
+ dateFrom?: string;
9001
+ /** Inclusive `dateWorked` window end, 'YYYY-MM-DD'. */
9002
+ /** OPTIONAL, but pass it together with `dateFrom` (see there). */
9003
+ dateTo?: string;
9004
+ }
9005
+ /** The getPosted / getUnposted result: the classified entries plus an honest truncation flag. */
9006
+ interface TimeEntriesPostedStateResult {
9007
+ /** getPosted: entries a billing item references. getUnposted: entries no billing item references. */
9008
+ items: TimeEntry[];
9009
+ /** True when the entry read or a billing-item join page had more rows than one bounded page: the result is partial (a window with more than 500 entries returns the first 500) — narrow the window. */
9010
+ hasMore: boolean;
9011
+ /** The inclusive dateWorked window queried (second-precision UTC); a side is null when that bound was not given. */
9012
+ window: {
9013
+ from: string | null;
9014
+ to: string | null;
9015
+ };
9016
+ /** INFORMATIONAL only, not posted state: items whose `billingApprovalDateTime` is set. Some tenants (ww29) never populate it. */
9017
+ approvedCount: number;
9018
+ /** INFORMATIONAL only, not posted state: items without `billingApprovalDateTime`. */
9019
+ unapprovedCount: number;
8883
9020
  }
8884
9021
  /** postedWithin options: the optional narrowing + the shared limit bound. */
8885
9022
  interface TimeEntriesPostedWindowOptions {
8886
- /** Posted narrowing: `true` = posted rows only (postedDate exist), `false` = unposted rows only (postedDate notExist), omitted = both (the counts split the returned items). On a zone whose entity lacks the field (W-D3, e.g. ww29 — see the pointer note) the vendor 500s and the typed note explains. */
9023
+ /** Posted narrowing, applied CLIENT-side after the billing-item join: `true` = posted rows only, `false` = unposted rows only (entries no billing item references), omitted = both. No postedDate condition is ever sent. */
8887
9024
  posted?: boolean;
8888
9025
  /** Max records (default 500 = the hard cap — a window with more than 500 matching rows: the bounded query op (time_entries.query) with the same dateWorked filter, its 2500-row walk cap; issue #128). */
8889
9026
  limit?: number;
@@ -8896,18 +9033,18 @@ interface TimeEntriesPostedWindowOptions {
8896
9033
  }
8897
9034
  /** The posted-window read result (issue #10): the window's items + the two-direction count in one pass. */
8898
9035
  interface TimeEntriesPostedWindowResult {
8899
- /** The window's entries (bounded by `limit`). */
9036
+ /** The window's entries read, narrowed by `posted` when set (bounded by `limit`). */
8900
9037
  items: TimeEntry[];
8901
- /** The items carrying a set `postedDate` (the live-entity field — W-D3). */
9038
+ /** Of EVERY entry read (before the `posted` narrowing), those a billing item references (the join — never a `postedDate` read). With `posted` set, `items.length` can differ from postedCount + unpostedCount. */
8902
9039
  postedCount: number;
8903
- /** The items with an empty `postedDate`. */
9040
+ /** Of EVERY entry read (before the `posted` narrowing), those no billing item references — non-zero even with `posted: true` (entries whose join page was truncated are in neither count). */
8904
9041
  unpostedCount: number;
8905
9042
  /** The inclusive UTC window actually queried (wire bounds, second precision). */
8906
9043
  window: {
8907
9044
  from: string;
8908
9045
  to: string;
8909
9046
  };
8910
- /** True when the vendor reports more matching records than the bounded read returned (a live `pageDetails.nextPageUrl` cursor) — the posted/unposted counts then cover the returned rows ONLY (at most `limit`); re-query with a wider window or a narrowing filter to reconcile the totals. */
9047
+ /** True when the entry read has a live `pageDetails.nextPageUrl` cursor (more matching records than the bounded read returned) OR a billing-item join page was truncated (those entries are in neither count) — the posted/unposted counts then cover the returned rows ONLY (at most `limit`); re-query with a wider window or a narrowing filter to reconcile the totals. */
8911
9048
  truncated: boolean;
8912
9049
  }
8913
9050
 
@@ -9115,9 +9252,9 @@ declare class TimeEntriesResource extends BaseResource<TimeEntry, TimeEntryCreat
9115
9252
  /** Node-parity special op (hand-written in src/resources/special/time_entries.ts). */
9116
9253
  dateWorkedWindow(from: string, to: string, opts?: TimeEntriesPostedWindowOptions): Promise<TimeEntriesPostedWindowResult>;
9117
9254
  /** Node-parity special op (hand-written in src/resources/special/time_entries.ts). */
9118
- getPosted(opts?: TimeEntriesStatusOptions): Promise<TimeEntry[]>;
9255
+ getPosted(opts?: TimeEntriesStatusOptions): Promise<TimeEntriesPostedStateResult>;
9119
9256
  /** Node-parity special op (hand-written in src/resources/special/time_entries.ts). */
9120
- getUnposted(opts?: TimeEntriesStatusOptions): Promise<TimeEntry[]>;
9257
+ getUnposted(opts?: TimeEntriesStatusOptions): Promise<TimeEntriesPostedStateResult>;
9121
9258
  /** Node-parity special op (hand-written in src/resources/special/time_entries.ts). */
9122
9259
  last14(opts?: TimeEntriesWindowOptions): Promise<TimeEntry[]>;
9123
9260
  /** Node-parity special op (hand-written in src/resources/special/time_entries.ts). */