@pidgeon-ai/sdk 1.2.0 → 1.4.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.
- package/CHANGELOG.md +134 -0
- package/LICENSE +21 -0
- package/dist/index.cjs +128 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +344 -13
- package/dist/index.d.ts +344 -13
- package/dist/index.js +125 -4
- package/dist/index.js.map +1 -1
- package/package.json +7 -3
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,140 @@ call shapes, the result shape — not to the API behind it.
|
|
|
9
9
|
|
|
10
10
|
## Unreleased
|
|
11
11
|
|
|
12
|
+
## 1.4.0 — things the prose promised and the types did not have
|
|
13
|
+
|
|
14
|
+
**Additive, and two corrections to descriptions that were wrong.**
|
|
15
|
+
|
|
16
|
+
The rest of what a field test found on 15 October building a shared mailroom.
|
|
17
|
+
Everything below was either returned and undeclared, or described in the
|
|
18
|
+
documentation and absent from the product.
|
|
19
|
+
|
|
20
|
+
**Added to `Message`.**
|
|
21
|
+
|
|
22
|
+
- `headers`, which `skill.md` and the MCP `get_message` tool have both
|
|
23
|
+
promised for months — "its body **and its headers**" — against a schema
|
|
24
|
+
with no such property. It is a subset and the description says which:
|
|
25
|
+
`message-id`, `in-reply-to`, `references`, `date`, `reply-to`,
|
|
26
|
+
`return-path`, the four list and auto-reply headers, `x-mailer` and
|
|
27
|
+
`x-priority`. The whole envelope is `GET /messages/{id}/raw`. The
|
|
28
|
+
threading three are new to the kept set, because _will my reply land in
|
|
29
|
+
the right conversation_ is the question people open this for and nothing
|
|
30
|
+
answered it for mail between two Pidgeon addresses.
|
|
31
|
+
- `authentication` — `spf`, `dkim`, `dmarc`. `/docs/rules` has said since it
|
|
32
|
+
was written that if you need to be certain who sent something you should
|
|
33
|
+
_"read the authentication results on the message rather than its `From:`"_.
|
|
34
|
+
There were none in the contract: no such field anywhere. The columns have
|
|
35
|
+
been written since inbound mail existed. **Null means not reported, not
|
|
36
|
+
failed** — rendering an absent verdict as a failed one is worse than
|
|
37
|
+
showing nothing.
|
|
38
|
+
- `delivered_to` is the envelope recipient on every message. It was null
|
|
39
|
+
unless routing had moved the mail, while `message.received` carried it
|
|
40
|
+
every time, so the event and the read disagreed about the same message.
|
|
41
|
+
|
|
42
|
+
**Added to `Thread`.** `folder`, `from`, `from_name`, `preview` and
|
|
43
|
+
`has_attachments`, from the newest message. `GET /threads` is the only
|
|
44
|
+
workspace-wide listing and a row could say which conversations exist without
|
|
45
|
+
being able to draw one — so a merged inbox had to come from
|
|
46
|
+
`GET /mailboxes/{folder}`, which takes one address and one folder. Three
|
|
47
|
+
addresses and four folders is twelve calls for one screen, against a free
|
|
48
|
+
plan's 120 requests a minute. It is one call now.
|
|
49
|
+
|
|
50
|
+
**Added to `MailboxChange`.** `identity_id`, which was null on every row of
|
|
51
|
+
the only endpoint whose first required parameter is the mailbox.
|
|
52
|
+
|
|
53
|
+
**New error codes.** `blocked` and `not_permitted`, both `403`.
|
|
54
|
+
`/docs/rules`: _"a refusal tells you which kind it was… an agent debugging a
|
|
55
|
+
send needs to know which."_ Both answered `forbidden` and neither word
|
|
56
|
+
appeared in the response, so the only way to tell was to match the sentence.
|
|
57
|
+
The fix is different for each — remove a rule, or change the send policy —
|
|
58
|
+
which is the whole reason to separate them.
|
|
59
|
+
|
|
60
|
+
**Corrected.** Two descriptions that described something the API does not do:
|
|
61
|
+
|
|
62
|
+
- `rejected` on a send is **what the provider would not accept at hand-off**,
|
|
63
|
+
not a recipient your own rules forbid. That refuses the whole send with
|
|
64
|
+
`403`, and nothing goes to anybody. The old sentence told callers to handle
|
|
65
|
+
a partial send that never happens, and not to expect the exception they
|
|
66
|
+
actually get.
|
|
67
|
+
- `is_read` on `PATCH /messages/{id}`, and `read`/`unread` on
|
|
68
|
+
`POST /messages/actions`, are declared refused for an API key. They always
|
|
69
|
+
were — read state belongs to a person — but three surfaces advertised them
|
|
70
|
+
with no hint, including the MCP tool, whose description argued for a
|
|
71
|
+
capability it does not have to the one consumer that cannot check first.
|
|
72
|
+
|
|
73
|
+
**Widened.** `colour` on a label now has to be one of the eight; the
|
|
74
|
+
refusal's sentence is unchanged.
|
|
75
|
+
|
|
76
|
+
## 1.3.0 — a day of API that never reached a registry
|
|
77
|
+
|
|
78
|
+
**Additive, and one fix that was a blocker.**
|
|
79
|
+
|
|
80
|
+
1.2.0 went out on 14 October and then the contract moved for a day without
|
|
81
|
+
this number moving with it. A field test on 15 October installed the published
|
|
82
|
+
package, called a search the live API had been accepting since the morning,
|
|
83
|
+
and got a `TypeError` for an argument the contract no longer requires. It
|
|
84
|
+
built what it needed with a hand-written HTTP client instead. Everything below
|
|
85
|
+
had already shipped; none of it was installable.
|
|
86
|
+
|
|
87
|
+
**Fixed.** `verifyWebhook` reads the signature headers whatever case the
|
|
88
|
+
framework spells them in. The lookup was `headers[name] ?? headers[name
|
|
89
|
+
.toLowerCase()]` and `name` is already a lowercase constant — so it read the
|
|
90
|
+
same key twice, and a plain object keyed `X-Pidgeon-Signature` was rejected as
|
|
91
|
+
unsigned. Every existing test passed, because they all hand it a `Headers` or
|
|
92
|
+
a lowercase object and both are already normalised. The broken branch is
|
|
93
|
+
reached by an object somebody built themselves, which is what API Gateway
|
|
94
|
+
hands a Lambda. `pidgeon` on PyPI was always right about this.
|
|
95
|
+
|
|
96
|
+
**Added.**
|
|
97
|
+
|
|
98
|
+
- `meGet`. What a key is, what it may do, and how many addresses it reaches —
|
|
99
|
+
the first call a credential can make about itself.
|
|
100
|
+
- `eventsStream`'s endpoint is in the document. The client does not wrap SSE,
|
|
101
|
+
because a generated client has nowhere to put a long-lived connection; the
|
|
102
|
+
path, the cursor header and the event shape are typed so hand-rolling it is
|
|
103
|
+
twenty lines rather than a reverse-engineering exercise.
|
|
104
|
+
- `thread.created` is emitted and subscribable. It was declared, accepted by
|
|
105
|
+
`POST /webhooks`, and had never once fired.
|
|
106
|
+
- `from_self` on `message.received`. A workspace with several addresses sees
|
|
107
|
+
its own outbound mail arrive at the others, genuinely inbound, with nothing
|
|
108
|
+
to distinguish it from a stranger — so a handler written the obvious way
|
|
109
|
+
answers its own reply. Check it before you reply.
|
|
110
|
+
- Quota pacing on a refusal: `Retry-After` and `X-RateLimit-*-Day`, so a
|
|
111
|
+
client refused a send can compute when it may try again instead of guessing
|
|
112
|
+
a day.
|
|
113
|
+
|
|
114
|
+
**Declared, having always been returned.** Three fields the API sends and the
|
|
115
|
+
document did not mention, so the generated types dropped them and a caller had
|
|
116
|
+
to cast to reach them:
|
|
117
|
+
|
|
118
|
+
- `sync_cursor` on a folder listing. `GET /mailboxes/changes` documents
|
|
119
|
+
`since` as "the `sync_cursor` from a listing" and no listing declared one,
|
|
120
|
+
so the change feed was unreachable from a typed client without defeating
|
|
121
|
+
its own types.
|
|
122
|
+
- `folder` on a search hit, and `identity` beside `identity_id` — the address
|
|
123
|
+
rather than the uuid, which is what the rest of the API already carries on a
|
|
124
|
+
thread row, on an event and on `GET /messages/{id}`. A search is the one
|
|
125
|
+
endpoint that spans mailboxes, so it was the one that most needed to name
|
|
126
|
+
them and the only one that would not. A hit is now its own type,
|
|
127
|
+
`SearchHit`, rather than a `MessageListItem` with two fields missing.
|
|
128
|
+
- `colour` is the eight-value enum it has always been enforced as, not
|
|
129
|
+
`string`. The palette existed only in the refusal, so the first call
|
|
130
|
+
anybody writes fails — somebody who has written CSS passes a hex. The
|
|
131
|
+
refusal was already good and is kept word for word.
|
|
132
|
+
|
|
133
|
+
**Widened.** `messagesSearch` no longer requires `identity_id`. It searches
|
|
134
|
+
every address the credential reaches, which is what it has always done on the
|
|
135
|
+
server and what `skill.md` has always said; the document said "one mailbox at
|
|
136
|
+
a time" in prose while declaring the parameter optional, and the generator
|
|
137
|
+
followed the prose. `MessageListItem` carries `identity_id`, `to`,
|
|
138
|
+
`counterparty` and `counterparty_name`, so a hit can be attributed to a
|
|
139
|
+
mailbox without a second call.
|
|
140
|
+
|
|
141
|
+
**Also.** `identitiesCreate` on the shared domain, a 404 that stopped
|
|
142
|
+
pretending an address it had just listed did not exist, and `repository`
|
|
143
|
+
fields that resolve — all four packages pointed at a private repository that
|
|
144
|
+
404s for everybody who installs one.
|
|
145
|
+
|
|
12
146
|
## 1.2.0 — the document says what its fields mean
|
|
13
147
|
|
|
14
148
|
**Additive, and a correction.**
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 PCN Labs OÜ
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.cjs
CHANGED
|
@@ -62,6 +62,7 @@ __export(index_exports, {
|
|
|
62
62
|
draftsUpdate: () => draftsUpdate,
|
|
63
63
|
eventsGet: () => eventsGet,
|
|
64
64
|
eventsList: () => eventsList,
|
|
65
|
+
eventsStream: () => eventsStream,
|
|
65
66
|
identitiesAccessCreate: () => identitiesAccessCreate,
|
|
66
67
|
identitiesAccessDelete: () => identitiesAccessDelete,
|
|
67
68
|
identitiesAccessGet: () => identitiesAccessGet,
|
|
@@ -90,6 +91,7 @@ __export(index_exports, {
|
|
|
90
91
|
mailboxesEmptyTrash: () => mailboxesEmptyTrash,
|
|
91
92
|
mailboxesList: () => mailboxesList,
|
|
92
93
|
mailboxesListFolder: () => mailboxesListFolder,
|
|
94
|
+
meGet: () => meGet,
|
|
93
95
|
messagesAct: () => messagesAct,
|
|
94
96
|
messagesForward: () => messagesForward,
|
|
95
97
|
messagesGet: () => messagesGet,
|
|
@@ -107,6 +109,7 @@ __export(index_exports, {
|
|
|
107
109
|
routingDelete: () => routingDelete,
|
|
108
110
|
routingList: () => routingList,
|
|
109
111
|
routingUpdate: () => routingUpdate,
|
|
112
|
+
streamEvents: () => streamEvents,
|
|
110
113
|
threadsGet: () => threadsGet,
|
|
111
114
|
threadsLabelsCreate: () => threadsLabelsCreate,
|
|
112
115
|
threadsLabelsDelete: () => threadsLabelsDelete,
|
|
@@ -173,7 +176,7 @@ function createSseClient({
|
|
|
173
176
|
...options
|
|
174
177
|
}) {
|
|
175
178
|
let lastEventId;
|
|
176
|
-
const
|
|
179
|
+
const sleep2 = sseSleepFn ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
177
180
|
const createStream = async function* () {
|
|
178
181
|
let retryDelay = sseDefaultRetryDelay ?? 3e3;
|
|
179
182
|
let attempt = 0;
|
|
@@ -277,7 +280,7 @@ function createSseClient({
|
|
|
277
280
|
break;
|
|
278
281
|
}
|
|
279
282
|
const backoff = Math.min(retryDelay * 2 ** (attempt - 1), sseMaxRetryDelay ?? 3e4);
|
|
280
|
-
await
|
|
283
|
+
await sleep2(backoff);
|
|
281
284
|
}
|
|
282
285
|
}
|
|
283
286
|
};
|
|
@@ -1117,7 +1120,7 @@ var messagesSend = (options) => (options.client ?? client).post({
|
|
|
1117
1120
|
...options.headers
|
|
1118
1121
|
}
|
|
1119
1122
|
});
|
|
1120
|
-
var messagesSearch = (options) => (options
|
|
1123
|
+
var messagesSearch = (options) => (options?.client ?? client).get({
|
|
1121
1124
|
security: [{ scheme: "bearer", type: "http" }],
|
|
1122
1125
|
url: "/messages/search",
|
|
1123
1126
|
...options
|
|
@@ -1353,6 +1356,11 @@ var appPasswordsRevoke = (options) => (options.client ?? client).delete({
|
|
|
1353
1356
|
url: "/app-passwords/{id}",
|
|
1354
1357
|
...options
|
|
1355
1358
|
});
|
|
1359
|
+
var meGet = (options) => (options?.client ?? client).get({
|
|
1360
|
+
security: [{ scheme: "bearer", type: "http" }],
|
|
1361
|
+
url: "/me",
|
|
1362
|
+
...options
|
|
1363
|
+
});
|
|
1356
1364
|
var workspaceGet = (options) => (options?.client ?? client).get({
|
|
1357
1365
|
security: [{ scheme: "bearer", type: "http" }],
|
|
1358
1366
|
url: "/workspace",
|
|
@@ -1368,6 +1376,11 @@ var eventsList = (options) => (options?.client ?? client).get({
|
|
|
1368
1376
|
url: "/events",
|
|
1369
1377
|
...options
|
|
1370
1378
|
});
|
|
1379
|
+
var eventsStream = (options) => (options?.client ?? client).sse.get({
|
|
1380
|
+
security: [{ scheme: "bearer", type: "http" }],
|
|
1381
|
+
url: "/events/stream",
|
|
1382
|
+
...options
|
|
1383
|
+
});
|
|
1371
1384
|
var eventsGet = (options) => (options.client ?? client).get({
|
|
1372
1385
|
security: [{ scheme: "bearer", type: "http" }],
|
|
1373
1386
|
url: "/events/{id}",
|
|
@@ -1589,7 +1602,16 @@ var attachmentsDownloadUrl = (options) => (options.client ?? client).get({
|
|
|
1589
1602
|
var SIGNATURE_HEADER = "x-pidgeon-signature";
|
|
1590
1603
|
var TIMESTAMP_HEADER = "x-pidgeon-timestamp";
|
|
1591
1604
|
async function verifyWebhook(input) {
|
|
1592
|
-
const get = (name) =>
|
|
1605
|
+
const get = (name) => {
|
|
1606
|
+
if (input.headers instanceof Headers) return input.headers.get(name) ?? void 0;
|
|
1607
|
+
const direct = input.headers[name];
|
|
1608
|
+
if (direct !== void 0) return direct;
|
|
1609
|
+
const wanted = name.toLowerCase();
|
|
1610
|
+
for (const [key2, value] of Object.entries(input.headers)) {
|
|
1611
|
+
if (key2.toLowerCase() === wanted) return value;
|
|
1612
|
+
}
|
|
1613
|
+
return void 0;
|
|
1614
|
+
};
|
|
1593
1615
|
const timestamp = get(TIMESTAMP_HEADER);
|
|
1594
1616
|
const signature = get(SIGNATURE_HEADER);
|
|
1595
1617
|
if (!timestamp || !signature) throw new Error("This request is not signed.");
|
|
@@ -1626,6 +1648,105 @@ function constantTimeEqual(a, b) {
|
|
|
1626
1648
|
}
|
|
1627
1649
|
return difference === 0;
|
|
1628
1650
|
}
|
|
1651
|
+
|
|
1652
|
+
// src/stream.ts
|
|
1653
|
+
async function* streamEvents(options) {
|
|
1654
|
+
const base = (options.baseUrl ?? "https://pidgeon.ai/api/v1").replace(/\/$/, "");
|
|
1655
|
+
let lastEventId;
|
|
1656
|
+
let backoffMs = 1e3;
|
|
1657
|
+
while (!options.signal?.aborted) {
|
|
1658
|
+
const url = new URL(`${base}/events/stream`);
|
|
1659
|
+
if (options.identityId) url.searchParams.set("identity_id", options.identityId);
|
|
1660
|
+
if (options.type) url.searchParams.set("type", options.type);
|
|
1661
|
+
if (options.after && !lastEventId) url.searchParams.set("after", options.after);
|
|
1662
|
+
let response;
|
|
1663
|
+
try {
|
|
1664
|
+
response = await fetch(url, {
|
|
1665
|
+
headers: {
|
|
1666
|
+
authorization: `Bearer ${options.apiKey}`,
|
|
1667
|
+
accept: "text/event-stream",
|
|
1668
|
+
...lastEventId ? { "last-event-id": lastEventId } : {}
|
|
1669
|
+
},
|
|
1670
|
+
signal: options.signal
|
|
1671
|
+
});
|
|
1672
|
+
} catch {
|
|
1673
|
+
if (options.signal?.aborted) return;
|
|
1674
|
+
await sleep(backoffMs, options.signal);
|
|
1675
|
+
backoffMs = Math.min(backoffMs * 2, 3e4);
|
|
1676
|
+
continue;
|
|
1677
|
+
}
|
|
1678
|
+
if (response.status === 401 || response.status === 403 || response.status === 404) {
|
|
1679
|
+
throw new Error(
|
|
1680
|
+
`Pidgeon refused the stream: ${response.status} ${await response.text().catch(() => "")}`.trim()
|
|
1681
|
+
);
|
|
1682
|
+
}
|
|
1683
|
+
if (!response.ok || !response.body) {
|
|
1684
|
+
await sleep(backoffMs, options.signal);
|
|
1685
|
+
backoffMs = Math.min(backoffMs * 2, 3e4);
|
|
1686
|
+
continue;
|
|
1687
|
+
}
|
|
1688
|
+
backoffMs = 1e3;
|
|
1689
|
+
for await (const frame of frames(response.body, options.signal)) {
|
|
1690
|
+
if (frame.id) lastEventId = frame.id;
|
|
1691
|
+
if (!frame.data) continue;
|
|
1692
|
+
if (frame.event === "error") continue;
|
|
1693
|
+
let parsed;
|
|
1694
|
+
try {
|
|
1695
|
+
parsed = JSON.parse(frame.data);
|
|
1696
|
+
} catch {
|
|
1697
|
+
continue;
|
|
1698
|
+
}
|
|
1699
|
+
yield parsed;
|
|
1700
|
+
}
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1703
|
+
async function* frames(body, signal) {
|
|
1704
|
+
const reader = body.getReader();
|
|
1705
|
+
const decoder = new TextDecoder();
|
|
1706
|
+
let buffer = "";
|
|
1707
|
+
try {
|
|
1708
|
+
while (!signal?.aborted) {
|
|
1709
|
+
const { done, value } = await reader.read();
|
|
1710
|
+
if (done) return;
|
|
1711
|
+
buffer += decoder.decode(value, { stream: true });
|
|
1712
|
+
let boundary = buffer.indexOf("\n\n");
|
|
1713
|
+
while (boundary !== -1) {
|
|
1714
|
+
const block = buffer.slice(0, boundary);
|
|
1715
|
+
buffer = buffer.slice(boundary + 2);
|
|
1716
|
+
boundary = buffer.indexOf("\n\n");
|
|
1717
|
+
const frame = {};
|
|
1718
|
+
const data = [];
|
|
1719
|
+
for (const line of block.split("\n")) {
|
|
1720
|
+
if (line.startsWith(":")) continue;
|
|
1721
|
+
const colon = line.indexOf(":");
|
|
1722
|
+
if (colon === -1) continue;
|
|
1723
|
+
const field = line.slice(0, colon);
|
|
1724
|
+
const value2 = line.slice(colon + 1).replace(/^ /, "");
|
|
1725
|
+
if (field === "id") frame.id = value2;
|
|
1726
|
+
else if (field === "event") frame.event = value2;
|
|
1727
|
+
else if (field === "data") data.push(value2);
|
|
1728
|
+
}
|
|
1729
|
+
if (data.length > 0) frame.data = data.join("\n");
|
|
1730
|
+
if (frame.id || frame.data) yield frame;
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
} finally {
|
|
1734
|
+
await reader.cancel().catch(() => void 0);
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
function sleep(ms, signal) {
|
|
1738
|
+
return new Promise((resolve) => {
|
|
1739
|
+
const timer = setTimeout(resolve, ms);
|
|
1740
|
+
signal?.addEventListener(
|
|
1741
|
+
"abort",
|
|
1742
|
+
() => {
|
|
1743
|
+
clearTimeout(timer);
|
|
1744
|
+
resolve();
|
|
1745
|
+
},
|
|
1746
|
+
{ once: true }
|
|
1747
|
+
);
|
|
1748
|
+
});
|
|
1749
|
+
}
|
|
1629
1750
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1630
1751
|
0 && (module.exports = {
|
|
1631
1752
|
SIGNATURE_HEADER,
|
|
@@ -1670,6 +1791,7 @@ function constantTimeEqual(a, b) {
|
|
|
1670
1791
|
draftsUpdate,
|
|
1671
1792
|
eventsGet,
|
|
1672
1793
|
eventsList,
|
|
1794
|
+
eventsStream,
|
|
1673
1795
|
identitiesAccessCreate,
|
|
1674
1796
|
identitiesAccessDelete,
|
|
1675
1797
|
identitiesAccessGet,
|
|
@@ -1698,6 +1820,7 @@ function constantTimeEqual(a, b) {
|
|
|
1698
1820
|
mailboxesEmptyTrash,
|
|
1699
1821
|
mailboxesList,
|
|
1700
1822
|
mailboxesListFolder,
|
|
1823
|
+
meGet,
|
|
1701
1824
|
messagesAct,
|
|
1702
1825
|
messagesForward,
|
|
1703
1826
|
messagesGet,
|
|
@@ -1715,6 +1838,7 @@ function constantTimeEqual(a, b) {
|
|
|
1715
1838
|
routingDelete,
|
|
1716
1839
|
routingList,
|
|
1717
1840
|
routingUpdate,
|
|
1841
|
+
streamEvents,
|
|
1718
1842
|
threadsGet,
|
|
1719
1843
|
threadsLabelsCreate,
|
|
1720
1844
|
threadsLabelsDelete,
|