node-autotask 0.12.0 → 0.13.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 (35) hide show
  1. package/MCP_TOOL_CATALOG.json +4 -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 +1238 -1237
  6. package/dist/capabilities.cjs +2 -2
  7. package/dist/capabilities.js +1 -1
  8. package/dist/{chunk-SRJI5XUZ.cjs → chunk-CJA6L6NR.cjs} +135 -135
  9. package/dist/{chunk-JKZVCFRY.cjs → chunk-CXXNONRP.cjs} +1237 -1236
  10. package/dist/{chunk-3WIDDEHP.js → chunk-DDZXN6LN.js} +2 -2
  11. package/dist/{chunk-GD4JRPYF.js → chunk-IKJLIDK3.js} +1 -1
  12. package/dist/{chunk-5N6DYKXO.cjs → chunk-KB2ANVVR.cjs} +2 -2
  13. package/dist/{chunk-TELBX6HK.cjs → chunk-MTDCLVQQ.cjs} +797 -603
  14. package/dist/{chunk-4A6XLQHE.js → chunk-OZD5FQM4.js} +1237 -1236
  15. package/dist/{chunk-MY7ZSXY3.js → chunk-Y6O5VVQ4.js} +387 -193
  16. package/dist/{index-D8I6taEo.d.cts → index-DbMKWnEX.d.cts} +96 -35
  17. package/dist/{index-uAxAcRol.d.ts → index-TuqNq3Ro.d.ts} +96 -35
  18. package/dist/index.cjs +19 -19
  19. package/dist/index.d.cts +2 -2
  20. package/dist/index.d.ts +2 -2
  21. package/dist/index.js +4 -4
  22. package/dist/operations/index.cjs +103 -103
  23. package/dist/operations/index.d.cts +2 -2
  24. package/dist/operations/index.d.ts +2 -2
  25. package/dist/operations/index.js +2 -2
  26. package/dist/pool.cjs +8 -8
  27. package/dist/pool.d.cts +1 -1
  28. package/dist/pool.d.ts +1 -1
  29. package/dist/pool.js +4 -4
  30. package/dist/resources/index.cjs +4 -4
  31. package/dist/resources/index.d.cts +1 -1
  32. package/dist/resources/index.d.ts +1 -1
  33. package/dist/resources/index.js +3 -3
  34. package/docs/API.md +190 -51
  35. package/package.json +1 -1
package/docs/API.md CHANGED
@@ -2608,7 +2608,9 @@ the plan change — a new registry op — is a minor at publish). One call
2608
2608
  returns the contract's reconciled P&L over a date window:
2609
2609
 
2610
2610
  - **`contracts.profitability(contractId, opts)` — the closed opts bag.**
2611
- `dateFrom`/`dateTo` are required (`YYYY-MM-DD`, `dateTo >= dateFrom`) and
2611
+ `opts` itself is required (0.13.0 — the signature and the registry arg map
2612
+ agree; a call without it is a pre-request `CONFIG_ERROR` naming the bag and
2613
+ its `dateFrom`/`dateTo`). `dateFrom`/`dateTo` are required (`YYYY-MM-DD`, `dateTo >= dateFrom`) and
2612
2614
  the window is capped at 36 calendar months (the per-month windowing budget
2613
2615
  stays bounded; OQ1). `labourCostMethod` (default
2614
2616
  `billing_item_our_cost`; the closed 4-set
@@ -2625,34 +2627,62 @@ returns the contract's reconciled P&L over a date window:
2625
2627
  `ticketCategory` activity fold; `staleAfterDays` (default 30, integer `>=
2626
2628
  1`) is the open-stale horizon; `maxTicketsForJoin` (default 500, integer
2627
2629
  1..500) caps the ticket join set fed to the expenses + miscoding probe
2628
- (sorted, first N, the `N of M` note); `limit` (default 50, clamped 500) is
2629
- every part's `MaxRecords` + the capped lists. Validation collects the
2630
+ (sorted, first N, the `N of M` note) and the time-referenced ticket ids;
2631
+ `limit` (default 50, clamped 500) is
2632
+ every part's `MaxRecords` + the capped lists; `includeRows` (default
2633
+ false, boolean) selects the output shape (below). Validation collects the
2630
2634
  COMPLETE problem list and throws ONE typed `CONFIG_ERROR` pre-request —
2631
2635
  zero wire on any malformed bag (the refusal table is house style: every
2632
2636
  message names the key + the valid values).
2633
- - **Wire plan: ≤18 bounded reads, zero mutating, all pre-existing ops.**
2637
+ - **Wire plan: ≤21 bounded reads, zero mutating, all pre-existing ops.**
2634
2638
  Issue order: contract `get` → customer `GET /V1.0/Companies/{companyID}` →
2635
- `ContractServices` / `ContractServiceUnits` / `ContractCharges` /
2636
- `ContractRoleCosts` / `ContractRates` / `BillingItems` queries → invoices
2639
+ the Contracts `entityInformation/fields` read (the `contractType` LABEL —
2640
+ verified on the ww29 sandbox 2026-10-16: 1 Time & Materials, 3 Fixed
2641
+ Price, 4 Block Hours, 6 Retainer, 7 Recurring Service, 8 Per Ticket, 9
2642
+ Umbrella; the folds still branch on `/block\s*hours?/i` and
2643
+ `/time.*materials?/i` over the label, never an id; `contractCategory` is
2644
+ ignored — all its values are inactive; an absent/0 type skips the read; an absent type, an
2645
+ unlisted id or a catalog fault is `CONTRACT_TYPE_UNKNOWN`, surfaced in
2646
+ `contract_type { id, label, absent? }`) → `ContractServices` / `ContractServiceUnits` (windowed on the period
2647
+ `startDate`) / `ContractCharges` / `ContractRoleCosts` / `ContractRates`
2648
+ queries → `ContractBlocks` query (Block Hours contracts only, by label;
2649
+ skipped otherwise) → `BillingItems` query → invoices
2637
2650
  (fetch-by-ids on the distinct billed `invoiceID`s; skipped on none) →
2638
- `TimeEntries` query → in-window `Tickets` query (the activity population) →
2639
- the tickets `entityInformation/fields` metadata read (the open-status
2640
- picklist) → open `Tickets` query (`status in` the open set) → resources
2641
- (fetch-by-ids on the distinct time-row `resourceID`s; skipped on none) →
2642
- `ExpenseItems` query + the ONE extra bounded miscoding probe
2643
- (`TimeEntries` with `ticketID in join` + `or[contractID noteq, contractID
2644
- notExist]`; skipped on an empty ticket set) → `ConfigurationItems` query
2645
- (default-on) → `Subscriptions` query (CI join; skipped on no CIs). The
2651
+ `TimeEntries` query → `Tickets` activity query (`or[ and[contractID,
2652
+ createDate in window], id in <the ticket ids the in-window time
2653
+ references> ]` — an older or closed ticket carrying this period's time is
2654
+ fetched, never reported as unresolvable) → the tickets
2655
+ `entityInformation/fields` metadata read (the open-status picklist) → open
2656
+ `Tickets` query (`status in` the open set) → resources (fetch-by-ids on the
2657
+ distinct time-row `resourceID`s; skipped on none) → `ExpenseItems` query +
2658
+ the ONE extra bounded miscoding probe (`TimeEntries` with `ticketID in
2659
+ join` + `or[contractID noteq, contractID notExist]`; the join is the
2660
+ fetched tickets whose record says THIS contract; skipped on an empty set)
2661
+ → `ConfigurationItems` query (`contractID` = the CIs this contract covers,
2662
+ never the whole company; default-on) → `Subscriptions` query (CI join;
2663
+ skipped on no CIs) → the Subscriptions `entityInformation/fields` read
2664
+ (the `status` + `periodType` labels; only when subscriptions came back).
2665
+ Every window filter is `gte dateFrom` + `lt` the day
2666
+ after `dateTo` (a datetime field keeps the last day's later rows). All
2667
+ windows and month buckets are UTC days: date-only fields (`itemDate`,
2668
+ units `startDate`/`endDate`) come back as UTC midnight (`Z`), datetimes
2669
+ (ticket `createDate`) as UTC instants, and bucketing slices the ISO
2670
+ string — in a UTC+10/11 tenant a ticket created late local time buckets
2671
+ one UTC day (possibly one month) earlier than its local date. The
2646
2672
  `time_entries.getPosted` / `getUnposted` endpoints are NEVER called (the
2647
2673
  pinned vendor 500 on AU03); posted/unposted derives from the
2648
2674
  `billingItems.timeEntryID` join (unposted = no billing item exists yet).
2649
2675
  The structural exclusions (`/Services/`, `/ExpenseReports/`,
2650
2676
  `/BillingCodes/` list reads, getPosted/getUnposted) are pinned by the
2651
2677
  recorded-request test. Baseline = 12 (a resolvable contract); the
2652
- typical busy contract = 17; the full fixture = 18. `wires: { issued,
2653
- budget: 18, note? }` rides the result as the honesty stamp (actual vs
2654
- budget).
2655
- - **Per-month windowing (parts `billingItems` + `timeEntries`).** One
2678
+ typical busy contract = 18; the full fixture = 20 (21 on a Block Hours
2679
+ contract). `wires: { issued, requests, budget: 21, note? }` rides the
2680
+ result as the honesty stamp: `issued` counts logical part reads (a
2681
+ fetch-by-ids part counts once), `requests` the raw HTTP requests (its
2682
+ chunk queries + per-id gets counted individually; a read-cache-served
2683
+ contract `get` adds no request).
2684
+ - **Per-month windowing (parts `billingItems` + `timeEntries` +
2685
+ `contractServiceUnits`).** One
2656
2686
  full-window page (`MaxRecords: limit`); when it comes back FULL with a
2657
2687
  live next cursor, the part re-queries PER CALENDAR MONTH intersecting the
2658
2688
  window (one page per month, the date field bounded to that month, in
@@ -2662,7 +2692,8 @@ returns the contract's reconciled P&L over a date window:
2662
2692
  higher limit`) — NO cursor walk (the house single-page rule; the composite
2663
2693
  owns its own windowing so per-month figures stay correct by construction).
2664
2694
  Every expansion read counts toward `wires.issued` (the note says why);
2665
- the 36-month cap bounds the worst case.
2695
+ the 36-month cap bounds the worst case. A still-full month is also named
2696
+ in that month's `coverage_gaps` (`<part>: truncated at limit N`).
2666
2697
  - **The cost-rate ladder (per time entry, zero extra wire).** Rung 1
2667
2698
  `billing_item.ourCost`: a LINE cost, not a rate — Σ the finite `>= 0`
2668
2699
  `ourCost` over the entry's billed lines (partial billing across months
@@ -2687,25 +2718,81 @@ returns the contract's reconciled P&L over a date window:
2687
2718
  is NOT a ladder rung (the customer billing rate — the wrong side of the
2688
2719
  P&L).
2689
2720
  - **Monthly P&L (one row per calendar month in the window).**
2690
- `revenue_invoiced` (Σ `extendedPrice` of the month's items on
2691
- non-voided invoices) · `revenue_voided_excluded` (Σ on voided invoices) ·
2692
- `revenue_expected` (Σ `service.unitPrice × units` of the units effective
2693
- in the month; `null` when the services/units basis is absent) ·
2694
- `revenue_expected_unbilled = max(0, expected − billed)` · `labour_hours`
2695
- (Σ raw `hoursWorked`) · `labour_cost { total, unresolvedHours,
2696
- rateSources }` (the per-month mirror of the ladder audit) ·
2697
- `tooling_cost { vendor, caller_supplied }` (vendor = Σ subscription
2698
- `periodCost` per active month in the window; `null` when the
2699
- subscriptions part is skipped; caller-supplied = the bag value, `0` when
2700
- absent) · `recurring_cost` (charges `billableAmount` + units `cost`,
2701
- when included) · `expense_cost` (Σ local-currency expense amounts) ·
2702
- `total_cost` · `profit = revenue_invoiced − total_cost` ·
2703
- `margin = profit / revenue_invoiced` when `> 0`, else `null` (the
2704
- division is refused, not zeroed) · `break_even_hours`
2705
- (`(total_cost − labour) / (Σcost / Σhours over resolved rows)`, `0` when
2706
- fixed `<= 0`, `null` with no resolved hours — including an empty month).
2707
- All amounts are the tenant's LOCAL currency fields
2708
- (`extendedPrice`, `expenseCurrencyExpenseAmount`).
2721
+ `revenue_invoiced` (Σ the line revenue of the month's items on
2722
+ non-voided invoices; line revenue = `internalCurrencyExtendedPrice`,
2723
+ falling back to `extendedPrice`) · `revenue_by_type { recurring, labour,
2724
+ charges, expenses, other }` (the invoiced split, keyed on the verified
2725
+ `billingItemType` picklist: 1 Labor / 2 Labor Adjustment → labour, 3 Cost
2726
+ → charges, 4 Expense → expenses, 5 Subscription → other, 6 Recurring
2727
+ Service/Bundle → recurring, 7 Recurring Contract Setup Fee → charges, 8
2728
+ Milestone → other; an absent/unknown type falls back to the line's FKs in
2729
+ the order `timeEntryID` → labour, `expenseItemID` → expenses, a charge FK
2730
+ → charges, a contract service/bundle/period/adjustment FK → recurring —
2731
+ labour billed against a service carries `contractServiceID` too, verified)
2732
+ ·
2733
+ `revenue_voided_excluded` (Σ on voided invoices) ·
2734
+ `revenue_posted_uninvoiced` (Σ on posted items with no invoice yet) ·
2735
+ `revenue_unresolved_invoice` (Σ on items whose invoice could not be read
2736
+ or was beyond the join cap — not recognized revenue, never silent: the
2737
+ month's `coverage_gaps` names `invoices: N line(s) on unresolved
2738
+ invoices`) · `revenue_expected` (Σ the period `internalCurrencyPrice`
2739
+ (fallback `price`) of the service units whose
2740
+ period STARTS in the month — a multi-month period is counted once, when
2741
+ it bills; `null` when the services/units basis is absent or the units
2742
+ part is `absent` — unknown, not 0. Unit `price`/`cost` are PERIOD totals
2743
+ (units × per-unit, prorated for a partial period — verified: the type-6
2744
+ recurring billing item has `itemDate` = the unit `startDate` and
2745
+ `extendedPrice` = the unit `price`). Units are normally one month but can
2746
+ span a year; the start-month attribution matches billing, so the monthly
2747
+ figures are a billing-period (cash) view, NOT accrual — an annual unit
2748
+ lands in its start month in one lump) ·
2749
+ `revenue_expected_unbilled = max(0, expected − the month's invoiced
2750
+ SERVICE lines)` (labour and charges never mask a missed recurring
2751
+ invoice) · `unposted_time { entries, hours, hoursAbsent?, value, absent? }`
2752
+ (billable time with no billing item yet — derived from the billing join,
2753
+ never getPosted/getUnposted; `hours` = Σ `max(0, hoursToBill)`, or `null`
2754
+ + `hoursAbsent: 'HOURS_TO_BILL_MISSING'` when any of the month's unposted
2755
+ entries carries no numeric `hoursToBill` — unknown, never a summed 0; a
2756
+ deliberate `hoursToBill: 0` stays 0; `value` = Σ `hoursToBill ×
2757
+ internalCurrencyContractHourlyRate` (fallback `contractHourlyRate`) on
2758
+ Time & Materials contracts only, else `null` + `absent:
2759
+ 'NOT_TIME_AND_MATERIALS_CONTRACT'`, `'CONTRACT_TYPE_UNKNOWN'`, or
2760
+ `'NO_CONTRACT_RATE'` when a role has no contract rate, or
2761
+ `'HOURS_TO_BILL_MISSING'` on a T&M contract when the hours are unknown) · `labour_hours`
2762
+ (Σ `hoursWorked` over positive entries — the same rule as the cost) ·
2763
+ `labour_cost { total, unresolvedHours, rateSources }` (the per-month
2764
+ mirror of the ladder audit) · `tooling_cost { vendor, caller_supplied }`
2765
+ (vendor = Σ `periodCost × the periodType monthly share` — by the tenant
2766
+ `periodType` LABEL (monthly 1, quarterly 1/3, semi-annual 1/6,
2767
+ annual/yearly 1/12); a One-Time subscription is never prorated: its
2768
+ `periodCost` is booked whole in its `effectiveDate` month; when the
2769
+ Subscriptions catalog read fails, the verified id maps apply (periodType
2770
+ 1 One-Time, 2 Monthly, 3 Quarterly, 4 Semi-Annual, 5 Yearly; status 1
2771
+ Active, 2 Cancelled, 3 Inactive) and the note says so; an unrecognised
2772
+ periodType is excluded and named; only Active subscriptions count —
2773
+ Cancelled and Inactive are excluded — over the subscriptions on this
2774
+ contract's CIs active in the month, REPORTED ONLY:
2775
+ never folded into `total_cost`, since subscriptions bill outside the
2776
+ contract; `null` when the subscriptions part is skipped/absent;
2777
+ caller-supplied = the bag value, `0` when absent) · `recurring_cost`
2778
+ (charge COST — `extendedCost`, falling back to `unitCost × unitQuantity`
2779
+ when it is absent or 0 while a unit cost exists; never `billableAmount` —
2780
+ plus the `cost` of the units starting in the month, when included) ·
2781
+ `expense_cost` (Σ `internalCurrencyExpenseAmount`) · `total_cost` (labour
2782
+ + caller-supplied tooling + recurring + expenses) · `profit =
2783
+ revenue_invoiced − total_cost` · `margin = profit / revenue_invoiced`
2784
+ when `> 0`, else `null` (the division is refused, not zeroed) ·
2785
+ `break_even_hours` (the labour hours the invoiced revenue absorbs at the
2786
+ average resolved cost rate before profit hits zero: `(revenue_invoiced −
2787
+ non-labour cost) / (Σcost / Σhours over resolved rows)`; `0` when the
2788
+ non-labour cost already exceeds revenue; `null` with no resolved hours or
2789
+ a zero rate — including an empty month) · `coverage_gaps` (why the
2790
+ month's figures may be incomplete: `<part>: absent (<code>)` /
2791
+ `truncated at limit N` / `capped at limit N` over units, charges, role
2792
+ costs, billing, time, expenses and subscriptions, plus the unresolved
2793
+ invoice lines; `[]` = complete). The P&L is single-currency in the
2794
+ INTERNAL currency (revenue and costs alike); money fields are emitted
2795
+ rounded to cents.
2709
2796
  - **The 11 data-quality checks (always present, order-stable, `count: 0` =
2710
2797
  a passed check).** Typed `{ code, count, sample_ids }` warnings
2711
2798
  (`count` = the full in-memory/fetched-set count, never capped;
@@ -2721,11 +2808,13 @@ returns the contract's reconciled P&L over a date window:
2721
2808
  `missing_cost_rate` (the ladder's `missing` rows; the resource NOT_FOUND
2722
2809
  misses ride the note) · `recurring_cost_drift` (month-over-month beyond
2723
2810
  the 0.005 float dust; samples the charge/unit ids whose monthly
2724
- contribution changed) · `expected_unbilled_revenue` (expected >
2725
- billed + 0.005; the pure "expected with nothing billed" case carries the
2726
- EMPTY sample — that is the assertion) · `cross_contract_tickets` (the
2811
+ contribution changed) · `expected_unbilled_revenue` (expected > the
2812
+ invoiced service lines + 0.005; the pure "expected with nothing billed"
2813
+ case carries the EMPTY sample — that is the assertion) ·
2814
+ `cross_contract_tickets` (the
2727
2815
  ticket-side drift + the entry-side unresolvable set) ·
2728
- `open_stale_tickets` (`now − lastActivityDate > staleAfterDays`, `now` =
2816
+ `open_stale_tickets` (this contract's tickets; `now − lastActivityDate >
2817
+ staleAfterDays`, `now` =
2729
2818
  the run's wall clock stamped `period.asOf`; absent `lastActivityDate` is
2730
2819
  unknown, not zero) · `estimate_vs_actual` (both estimates prorated to the
2731
2820
  window — `estimate × overlapDays / termDays` over the contract term — and
@@ -2738,12 +2827,26 @@ returns the contract's reconciled P&L over a date window:
2738
2827
  never silently dropped) · `possible_miscoding` (the probe rows + the
2739
2828
  confirmed cross-contract entries, closed reason set
2740
2829
  `no_contract_on_entry` / `ticket_of_other_contract` /
2741
- `ticket_not_in_fetched_set`) · `open_renewal_items` (open tickets with
2742
- the stale-days detail, subscription expirations, the contract end, the
2743
- per-month `included_hours_burn` row). The probe rows are EXCLUDED from
2744
- the P&L folds (diagnostic — time not coded to this contract).
2745
- - **Degradation (per part typed `ok` / `blind` / `skipped`; the op NEVER
2746
- fails the call on a degraded part).** Every tickets call is
2830
+ `ticket_not_in_fetched_set`) · `open_renewal_items` (this contract's open
2831
+ tickets with the stale-days detail, subscription expirations with the
2832
+ prorated monthly cost, the contract end, one `included_hours_burn` row
2833
+ per purchased hour block) · `included_hours` (Block Hours contracts only:
2834
+ `{ value: { purchased, approved, remaining } }` from the `ContractBlocks`
2835
+ `hours` vs the vendor `hoursApproved`; any other contract type is `{
2836
+ value: null, absent: 'NOT_BLOCK_HOURS_CONTRACT' }`, an unresolvable type
2837
+ `'CONTRACT_TYPE_UNKNOWN'` — service units are a
2838
+ quantity, never read as hours, and retainers are dollar-denominated).
2839
+ The probe rows are EXCLUDED from the P&L folds (diagnostic — time not
2840
+ coded to this contract).
2841
+ - **Degradation (per part typed `ok` / `blind` / `skipped` / `absent`; the
2842
+ op NEVER fails the call on a degraded part).** Only the contract read is
2843
+ fatal (the root: `NOT_FOUND` — or any other fault on it — propagates).
2844
+ A vendor fault on any bounded list part degrades that part to `status:
2845
+ 'absent'` + `absent: <the typed error code>` + the note (never a true
2846
+ zero — no `emptyReason`), and every monthly row it feeds names it in
2847
+ `coverage_gaps`; a non-404 fault on the customer read degrades to
2848
+ `customer: null` + `parts.customer: 'absent'` + `customerAbsent: <code>`.
2849
+ Every tickets call is
2747
2850
  `contractID`-filtered, so the pinned `unfiltered-blind` tickets class
2748
2851
  never stamps a filtered empty (a filtered 0 is a TRUE zero with the
2749
2852
  honest note); the 0-row parts carry the pinned
@@ -2758,6 +2861,34 @@ returns the contract's reconciled P&L over a date window:
2758
2861
  rows with the divisions refused (`margin: null`, `break_even_hours:
2759
2862
  null`, `tooling_cost.vendor: null`, `revenue_expected: null`), all 11
2760
2863
  warnings at `count: 0`, zero tool failure.
2864
+ - **Summary-first output (0.13.0).** By default each of the 16 per-part
2865
+ containers (`contractServices` … `subscriptions`) is a
2866
+ `ProfitabilityPartDigest`: every stamp (`status`, `capped`, `hasMore`,
2867
+ `note`, `blindSurface`, `emptyReason`, `absent`) plus `rows_fetched` (the
2868
+ row count), WITHOUT the raw `items` array. Every computed figure is
2869
+ returned either way — `monthly` (incl. `coverage_gaps`), `contract_type`,
2870
+ `included_hours`, `currency`, the activity/costliest-ticket/miscoding/
2871
+ renewal folds, `data_quality_warnings`, `costRateResolution`, `parts`,
2872
+ `wires` — as are the `contract` and `customer` records. `includeRows: true`
2873
+ returns the full `ProfitabilityPart<T>` containers (the pre-0.13.0 shape,
2874
+ `items` included, no `rows_fetched`); the wire is identical in both modes.
2875
+ The part fields are typed `ProfitabilityPart<T> | ProfitabilityPartDigest`
2876
+ (both exported from the package root with the other option/result types):
2877
+ narrow with `'items' in part` before reading rows, e.g.
2878
+ `const rows = 'items' in res.billingItems ? res.billingItems.items : [];`.
2879
+ On the 2-month test fixture the serialised result is 8,657 bytes without
2880
+ rows vs 15,060 with them (−43%); the fixture rows are trimmed to a few
2881
+ fields, so real vendor rows over a 12-month window make the rows the bulk
2882
+ of the payload and the saving much larger.
2883
+ - **0.12.1 corrections.** The 0.12.0 folds counted the charge
2884
+ `billableAmount` (sell price) as recurring cost, read service `units` as
2885
+ included hours, joined every company CI's subscriptions and added a
2886
+ yearly `periodCost` every month, compared the expected recurring revenue
2887
+ against all billed lines (labour included) and priced it at today's
2888
+ `unitPrice`, read service units unwindowed, reported time on older
2889
+ tickets as unresolvable, defined break-even without revenue, folded
2890
+ expenses in their own currency, and threw on any bounded-part fault. The
2891
+ bullets above are the corrected semantics.
2761
2892
  - **Registration surface.** The plan row (`capabilities.plan.json` →
2762
2893
  re-derived hash) carries `kind: 'helper'`, `helperBasis: 'composite'`,
2763
2894
  `effect: 'read'`, `dryRun: false`, the closed-bag `inputSchema` (the
@@ -2770,17 +2901,25 @@ returns the contract's reconciled P&L over a date window:
2770
2901
  subset is ticket/contact/company/resource/time_entry only).
2771
2902
  - **Test pins.** `test/resources/contracts.profitability.test.ts`: the
2772
2903
  hand-computed 2-month synthetic fixture (every figure derived from the
2773
- fixture rows; `wires.issued = 18` with the exact queue; every wire pinned
2904
+ fixture rows; `wires.issued = 20` with the exact queue; every wire pinned
2774
2905
  in issue order; the structural exclusions + zero-mutating recorded paths;
2775
2906
  the ladder audit incl. the per-month `rateSources`; the 11 checks with
2776
2907
  exact counts/samples; the activity/highest-cost/miscoding/renewal folds;
2777
2908
  the per-month windowing (exact extra month queries, id-dedup, the
2778
2909
  still-full month note; `wires.issued` counts the expansion); the
2779
2910
  join-set/`limit` caps; the malformed-bag refusal table (one complete
2780
- problem list, zero wire); the contract NOT_FOUND).
2911
+ problem list, zero wire); the contract NOT_FOUND; the summary-first pins
2912
+ (the default digest = the full container minus `items` plus the
2913
+ hand-counted `rows_fetched` per part, every computed figure equal across
2914
+ modes, a non-boolean `includeRows` and a missing opts bag refused with
2915
+ zero wire); the 0.12.1 pins: the
2916
+ charge cost/period-type/line-type classifiers, the Block Hours
2917
+ `contract_blocks` path, T&M unposted pricing + the time-referenced old
2918
+ ticket, the exclusive window ends, the month-level `coverage_gaps`).
2781
2919
  `test/resources/profitability-degradation.test.ts`: the empty-tenant
2782
2920
  13-wire suite (every part degrades, zero tool failure), the dangling
2783
- customer FK, the resources blind-list get-fallback (rung 3 from the
2921
+ customer FK, the non-404 customer fault (`absent`), a bounded-part fault
2922
+ (`absent` + `coverage_gaps`, the call resolves), the resources blind-list get-fallback (rung 3 from the
2784
2923
  gets, the quirk note), the per-item invoice fault (retries off — the
2785
2924
  fault semantics are the pin, not the transport's 5xx budget), the
2786
2925
  fieldCatalog-fault open-pass skip, the status-helper sentinel
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-autotask",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "description": "Fully-typed TypeScript SDK for the Autotask (Datto) PSA REST API. Zero runtime dependencies. Built for building MCP servers and integrations.",
5
5
  "type": "module",
6
6
  "sideEffects": false,