node-autotask 0.30.0 → 0.32.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 (42) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/MCP_TOOL_CATALOG.json +6 -6
  3. package/MCP_TOOL_MANIFEST.md +26 -28
  4. package/MCP_TOOL_OVERRIDES.json +0 -12
  5. package/N8N_MCP_MANIFEST.json +8 -8
  6. package/N8N_MCP_MANIFEST.md +8 -8
  7. package/README.md +7 -5
  8. package/capabilities.json +1473 -1449
  9. package/dist/capabilities.cjs +2 -2
  10. package/dist/capabilities.js +1 -1
  11. package/dist/{chunk-EDVSWL32.cjs → chunk-DCTHO7CQ.cjs} +1472 -1448
  12. package/dist/{chunk-RS7QMPRO.cjs → chunk-FTF2FEQ6.cjs} +526 -490
  13. package/dist/{chunk-P4PC32RJ.js → chunk-FVHPN7ZR.js} +61 -25
  14. package/dist/{chunk-LLIB4JGK.cjs → chunk-IKPFR6K2.cjs} +6 -6
  15. package/dist/{chunk-TZC4ZAA6.js → chunk-M2EGFPGF.js} +1472 -1448
  16. package/dist/{chunk-L4TDVUJM.cjs → chunk-QCB4LYKD.cjs} +143 -143
  17. package/dist/{chunk-QQPCJT2L.js → chunk-R6HLJCI2.js} +3 -3
  18. package/dist/{chunk-2ETR5GSO.js → chunk-T6EDJRLT.js} +2 -2
  19. package/dist/{index-B9VNv-EP.d.cts → index-Cw4aNNMo.d.ts} +65 -14
  20. package/dist/{index-DkpaFqyu.d.ts → index-OkbCWcef.d.cts} +65 -14
  21. package/dist/{index-CJ36PwRG.d.cts → index-m-7fOwFl.d.cts} +1 -1
  22. package/dist/{index-CJ36PwRG.d.ts → index-m-7fOwFl.d.ts} +1 -1
  23. package/dist/index.cjs +22 -22
  24. package/dist/index.d.cts +52 -5
  25. package/dist/index.d.ts +52 -5
  26. package/dist/index.js +4 -4
  27. package/dist/operations/index.cjs +112 -112
  28. package/dist/operations/index.d.cts +4 -4
  29. package/dist/operations/index.d.ts +4 -4
  30. package/dist/operations/index.js +2 -2
  31. package/dist/pool.cjs +9 -9
  32. package/dist/pool.d.cts +2 -2
  33. package/dist/pool.d.ts +2 -2
  34. package/dist/pool.js +4 -4
  35. package/dist/resources/index.cjs +4 -4
  36. package/dist/resources/index.d.cts +2 -2
  37. package/dist/resources/index.d.ts +2 -2
  38. package/dist/resources/index.js +3 -3
  39. package/dist/types/index.d.cts +1 -1
  40. package/dist/types/index.d.ts +1 -1
  41. package/docs/API.md +61 -36
  42. package/package.json +1 -1
package/docs/API.md CHANGED
@@ -300,7 +300,7 @@ drift payloads (the `operations.recovery` `candidateIds` refusal, the #13
300
300
  shape-drift marker info). A non-pinned surface gets no `details` at all
301
301
  (no speculation). On the MCP tools' served error payload, `details` is
302
302
  passed through verbatim (live-verified 2026-10-04: the W-D3 pointer note
303
- rides a real `time_entries.getPosted` vendor 500 on the wire) and the
303
+ rides a real `time_entries.getPosted` vendor 500 on the wire; that 500 is gone since 0.32.0, the pointer-note mechanism is unchanged) and the
304
304
  deep-redacted `vendorError` body rides under a fixed key (issue #33: the
305
305
  vendor's own field-level failure text — what separates a deterministic
306
306
  zone wall from a transient outage) — a tool caller sees the same typed
@@ -2162,8 +2162,8 @@ so the answer to "why is this empty?" is data, not speculation:
2162
2162
  field" — 2026-10-06; creates succeeded 2026-10-21, pair not recorded); `authenticate.get` W2:
2163
2163
  designator-only;
2164
2164
  `time_entries.getPosted` / `time_entries.getUnposted` W-D3: this zone's
2165
- TimeEntry entity exposes no posted-state filter field — the working read
2166
- is `time_entries.postedWithin`, §11.13 — NO `emptyReason` on any pointer
2165
+ TimeEntry entity has no `postedDate` — the ops derive posted state from the
2166
+ `billing_items.timeEntryID` join instead (§11.13) — NO `emptyReason` on any pointer
2167
2167
  row, by design: a false reason would be worse than none).
2168
2168
  - **the closed kind set** — `EmptyReasonKind`
2169
2169
  (`'not-found' | 'blind-surface' | 'tenant-empty-for-user' |
@@ -2305,30 +2305,50 @@ clause that stops this fact from being lost a second time. The create op's
2305
2305
  registry record carries the same typed block as `metadata.createRequirements`
2306
2306
  (the `autotask_describe` channel).
2307
2307
 
2308
- ### 11.13 The posted/unposted wall (W-D3) + `postedWithin`, and drifted-create marker recovery (issues #10/#13)
2309
-
2310
- - **W-D3 (live, ww29, 2026-10-04, re-verified)** — this zone's TimeEntry
2311
- entity exposes **no posted-state filter field**: the vendor 500s
2312
- `postingStatus` (the G4-era wire), `postedDate` (the spec's field) and
2313
- `status` alike — a live field-existence oracle (only `dateWorked` /
2314
- `resourceID` / `ticketID` / `taskID` resolve as filter fields). Zones
2315
- honouring the published spec answer `getPosted` / `getUnposted`
2316
- (`postedDate` exist/notExist) normally; on such zones the 500 is the
2317
- TYPED known wall — the pinned `pointerNote` (§11.10) rides the error on
2318
- both the typed client and the served MCP payload, naming the working read.
2319
- - **`time_entries.postedWithin(from, to, opts?)`** — the working posted-state
2320
- window read: an inclusive UTC `dateWorked` window (`from`/`to` are
2321
- `YYYY-MM-DD` date strings — the S1c guard forbids Date positionals), ONE
2322
- bounded query (`limit` 1-500, default 500 — the hard cap, issue #128: a
2323
- window with more than 500 matching rows needs `time_entries.query` with the
2324
- same `dateWorked` filter, its 2500-row walk cap) returning
2325
- `{ items, postedCount, unpostedCount, window }`; `opts.posted` narrows
2326
- (true = posted rows only, false = unposted rows only, omitted = both
2327
- counted in one pass) and `ticketID` / `resourceID` narrow further.
2328
- Live-verified on ww29 with data (25 unposted rows in-window, 0 posted;
2329
- re-verified 2026-10-06: 12 unposted rows in the 2026-10-01..06 window —
2330
- also the live evidence for the visibility class: unfiltered query 0 rows,
2331
- windowed read has data).
2308
+ ### 11.13 Posted state from billing items (W-D3), `getPosted` / `getUnposted` / `postedWithin`, and drifted-create marker recovery (issues #10/#13/#237)
2309
+
2310
+ - **W-D3 (live, ww29, re-verified 2026-10-27)** — this zone's TimeEntry entity
2311
+ has **no posted-state field**: time-entry reads carry no `postedDate` /
2312
+ `postedOnTime` key, and `postedDate exist` / `notExist` filters fail with
2313
+ `SERVER_ERROR: Unable to find postedDate in the TimeEntry Entity` (earlier
2314
+ 2026-10-04 oracle: `postingStatus` and `status` are rejected too; only
2315
+ `dateWorked` / `resourceID` / `ticketID` / `taskID` resolve as filter
2316
+ fields). `billingApprovalDateTime` exists on the entity but is not
2317
+ populated on this tenant, so it cannot gate posted state. Earlier text claiming `postedWithin` was "live-verified" with "25
2318
+ unposted rows, 0 posted" came from the defect itself: `postedDate` was never
2319
+ present, so every row counted as unposted.
2320
+ - **Posted state is derived from `billing_items.timeEntryID`.** An entry is
2321
+ posted when a billing item references it, for **every** entry read;
2322
+ approval is not consulted. Live ww29 (week 2026-03-02..08): 1321 entries,
2323
+ none with `billingApprovalDateTime`, yet 817 referenced by billing items —
2324
+ so an approval-gated join would call everything unposted. No `postedDate`
2325
+ condition is ever sent. The join is one `BillingItems` query per 250 ids;
2326
+ the entry read is capped at 500, so a call costs at most 1 read + 2 joins
2327
+ and a window with more than 500 entries returns the first 500 with
2328
+ `hasMore` / `truncated` true. A 429 or vendor fault propagates as a typed
2329
+ error. The API user therefore needs BillingItems read permission; a permission fault propagates as a typed error.
2330
+ - **`time_entries.getPosted(opts?)` / `getUnposted(opts?)`** — `opts` is
2331
+ `{ limit?, ticketID?, dateFrom?, dateTo? }` (`dateFrom`/`dateTo`:
2332
+ inclusive `dateWorked` days, `YYYY-MM-DD`, never tz-shifted). They return
2333
+ `{ items, hasMore, window, approvedCount, unapprovedCount }` (a breaking
2334
+ change from the bare `TimeEntry[]`, 0.32.0): `getPosted` items are entries
2335
+ a billing item references; `getUnposted` items are entries none references.
2336
+ `approvedCount` / `unapprovedCount` are informational (items with / without
2337
+ `billingApprovalDateTime`) and are **not** posted state. `window` echoes the
2338
+ bounds (`null` for a bound not given); `hasMore` true means the result is
2339
+ partial (entry read capped, or a join page truncated — unconfirmed entries
2340
+ are withheld, never called unposted). `dateFrom`/`dateTo` are optional, but a call without dates reads the tenant's first 500 time entries (effectively the oldest): a near-empty, misleading slice with `hasMore` true — pass a window.
2341
+ - **`time_entries.postedWithin(from, to, opts?)`** — an inclusive UTC
2342
+ `dateWorked` window (`from`/`to` are `YYYY-MM-DD` date strings — the S1c
2343
+ guard forbids Date positionals), one bounded read (`limit` 1-500, default
2344
+ 500 — the hard cap, issue #128: a larger window needs `time_entries.query`
2345
+ with the same `dateWorked` filter, its 2500-row walk cap) returning
2346
+ `{ items, postedCount, unpostedCount, window, truncated }`; the counts come
2347
+ from the billing-item join (any billing item referencing the entry), `opts.posted` narrows client-side after the join
2348
+ (true = posted only, false = unposted only, omitted = both); the counts still split every entry read (before the narrowing), so `items.length` can differ from their sum and `ticketID` /
2349
+ `resourceID` narrow the read. Live evidence for the visibility class
2350
+ (unfiltered query 0 rows, windowed read has data) from 2026-10-06 still
2351
+ stands; the posted/unposted split from that period does not.
2332
2352
  - **`truncated` + the `dateWorkedWindow` alias + `tz` (issue #66)** — the
2333
2353
  result is now `{ items, postedCount, unpostedCount, window, truncated }`:
2334
2354
  `truncated` is true when the bounded read came back with a live
@@ -3029,13 +3049,17 @@ additive, and mirrors the family:
3029
3049
  fields, not the invoice date).
3030
3050
  - **The zone contract (shared)** — `opts.tz` takes an explicit IANA zone
3031
3051
  (e.g. `'Australia/Sydney'`); OMITTED `tz` = UTC, the current behaviour
3032
- bit-for-bit (the regression pin). With a zone, the window's calendar
3033
- day/period is computed in that zone's LOCAL calendar and converted to UTC
3034
- for the wire (vendor filter values stay second-precision UTC ISO) — for
3035
- `billing_items` and `invoices`. The `time_entries` family is DATE-ONLY
3036
- (`dateWorked` is stored at 00:00 UTC): the zone picks the calendar DATES and
3037
- the wire bounds are whole UTC days of them; explicit `from`/`to` days are never
3038
- shifted (0.24.0; a zone-converted instant window picked the wrong day behind UTC). DST is
3052
+ bit-for-bit (the regression pin). All three families are DATE-ONLY
3053
+ (`dateWorked`, `itemDate`, `invoiceDateTime` are stored at T00:00:00Z): the
3054
+ zone picks which calendar DATES a relative window means (what today / last
3055
+ full month is in that zone) and the wire bounds are whole UTC days of them
3056
+ (second-precision UTC ISO); explicit `from`/`to` days are never shifted and
3057
+ `tz` is only validated. `time_entries` has worked this way since 0.24.0;
3058
+ `billing_items` and `invoices` since 0.32.0 (behaviour change: before, a zone
3059
+ behind UTC, e.g. America/New_York, sent a zone-converted instant window that
3060
+ dropped the first day and added an extra day; zones ahead of UTC returned the
3061
+ right rows by accident). A zone-converted instant window picks the wrong day
3062
+ behind UTC. DST is
3039
3063
  handled by inverting local midnights to UTC instants (a local day that
3040
3064
  gains or loses an hour still pins both boundaries to its TRUE midnights —
3041
3065
  never clock-hour arithmetic); `last24h` is rolling (`now − 24h → now`)
@@ -3290,8 +3314,9 @@ returns the contract's reconciled P&L over a date window:
3290
3314
  (ticket `createDate`) as UTC instants, and bucketing slices the ISO
3291
3315
  string — in a UTC+10/11 tenant a ticket created late local time buckets
3292
3316
  one UTC day (possibly one month) earlier than its local date. The
3293
- `time_entries.getPosted` / `getUnposted` endpoints are NEVER called (the
3294
- pinned vendor 500 on AU03); posted/unposted derives from the
3317
+ `time_entries.getPosted` / `getUnposted` ops are NEVER called (the
3318
+ profitability fold already holds the billing items, so the join is
3319
+ in-memory with no extra wire); posted/unposted derives from the
3295
3320
  `billingItems.timeEntryID` join (unposted = no billing item exists yet).
3296
3321
  The structural exclusions (`/Services/`, `/ExpenseReports/`,
3297
3322
  `/BillingCodes/` list reads, getPosted/getUnposted) are pinned by the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-autotask",
3
- "version": "0.30.0",
3
+ "version": "0.32.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,