@openmarket/rooms-client 0.5.1 → 0.7.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/dist/agent/armed-mode.d.ts +0 -8
- package/dist/agent/armed-mode.js +19 -3
- package/dist/agent/clients/common.d.ts +13 -1
- package/dist/agent/clients/common.js +11 -1
- package/dist/agent/topic-inbox.d.ts +6 -0
- package/dist/agent/topic-inbox.js +20 -5
- package/dist/shared/rooms-protocol.d.ts +96 -2
- package/dist/social-client.d.ts +202 -5
- package/dist/social-client.js +372 -5
- package/dist/social-types.d.ts +164 -1
- package/dist/types.d.ts +1 -1
- package/package.json +2 -7
- package/src/agent/armed-mode.ts +20 -3
- package/src/agent/clients/common.ts +18 -2
- package/src/agent/topic-inbox.ts +23 -5
- package/src/shared/rooms-protocol.ts +100 -2
- package/src/social-client.ts +653 -6
- package/src/social-types.ts +175 -1
- package/src/types.ts +3 -0
|
@@ -100,14 +100,6 @@ export declare function rearmedRoomState(roomId: string, retired: ArmedRoomState
|
|
|
100
100
|
/** Seqs of the user's own posts in a window of entries; the reply-trigger
|
|
101
101
|
* check tests `inReplyTo` against this set. Pure. */
|
|
102
102
|
export declare function ownSeqsOf(entries: ReadonlyArray<RoomLedgerEntry>, ownUserId: string): Set<number>;
|
|
103
|
-
/**
|
|
104
|
-
* Whether a single incoming entry is a qualifying TRIGGER: a NEW human POST in
|
|
105
|
-
* the room that @mentions the user's own handle, OR (when `ownSeqs` is given)
|
|
106
|
-
* directly replies to one of the user's own messages. Reply semantics match a
|
|
107
|
-
* mention (replying pings the author), so armed mode treats both as addressed
|
|
108
|
-
* to the user. Agent posts, system entries, the user's own posts, and
|
|
109
|
-
* non-mentions never trigger. Pure.
|
|
110
|
-
*/
|
|
111
103
|
export declare function isTriggeringEntry(entry: RoomLedgerEntry, ownHandle: string, ownUserId: string, ownSeqs?: ReadonlySet<number>): boolean;
|
|
112
104
|
/**
|
|
113
105
|
* The newest qualifying trigger among entries strictly newer than
|
package/dist/agent/armed-mode.js
CHANGED
|
@@ -117,12 +117,24 @@ export function ownSeqsOf(entries, ownUserId) {
|
|
|
117
117
|
* to the user. Agent posts, system entries, the user's own posts, and
|
|
118
118
|
* non-mentions never trigger. Pure.
|
|
119
119
|
*/
|
|
120
|
+
/** Webhook-ingested entries are machine posts even though isAgent is
|
|
121
|
+
* false (the flag marks agent USERS). They must never count as the human
|
|
122
|
+
* in any autonomy gate: a webhook @mention cannot start an unreviewed
|
|
123
|
+
* autonomous turn, and webhook chatter cannot satisfy the
|
|
124
|
+
* human-activity brake. */
|
|
125
|
+
function isWebhookEntry(entry) {
|
|
126
|
+
return Boolean(entry.metadata?.webhook);
|
|
127
|
+
}
|
|
120
128
|
export function isTriggeringEntry(entry, ownHandle, ownUserId, ownSeqs) {
|
|
121
129
|
if (entry.type !== RoomLedgerEntryType.POST)
|
|
122
130
|
return false;
|
|
123
131
|
if (entry.isAgent)
|
|
124
132
|
return false;
|
|
125
|
-
if (
|
|
133
|
+
if (isWebhookEntry(entry))
|
|
134
|
+
return false;
|
|
135
|
+
// A poll is substantive content even with no caption, and a captionless
|
|
136
|
+
// poll REPLY is addressed at us like any other reply.
|
|
137
|
+
if (!entry.text?.trim() && entry.poll == null)
|
|
126
138
|
return false;
|
|
127
139
|
// Never trigger on our own message (we post as this handle/userId).
|
|
128
140
|
const fromHandle = normalizeHandle(entry.handle).toLowerCase();
|
|
@@ -139,7 +151,9 @@ export function isTriggeringEntry(entry, ownHandle, ownUserId, ownSeqs) {
|
|
|
139
151
|
// old mentionsAgent) fired on ordinary words for short handles like "may" /
|
|
140
152
|
// "btc"; armed posting has no human review, so it triggers ONLY on an `@handle`
|
|
141
153
|
// token. Empty handle never matches (mentionsAgentExplicit guards it).
|
|
142
|
-
|
|
154
|
+
// Mentions can live in the poll question too (member-authored content).
|
|
155
|
+
const mentionSource = [entry.text ?? "", entry.poll?.question ?? ""].filter(Boolean).join(" ");
|
|
156
|
+
return mentionsAgentExplicit(mentionSource, ownHandle);
|
|
143
157
|
}
|
|
144
158
|
/**
|
|
145
159
|
* The newest qualifying trigger among entries strictly newer than
|
|
@@ -312,13 +326,15 @@ export function isHumanResetEntry(entry, ownHandle, ownUserId) {
|
|
|
312
326
|
return false;
|
|
313
327
|
if (entry.isAgent)
|
|
314
328
|
return false;
|
|
329
|
+
if (isWebhookEntry(entry))
|
|
330
|
+
return false;
|
|
315
331
|
if (entry.userId === ownUserId)
|
|
316
332
|
return false;
|
|
317
333
|
const fromHandle = normalizeHandle(entry.handle).toLowerCase();
|
|
318
334
|
const me = normalizeHandle(ownHandle).toLowerCase();
|
|
319
335
|
if (me.length > 0 && fromHandle === me)
|
|
320
336
|
return false;
|
|
321
|
-
return Boolean(entry.text?.trim());
|
|
337
|
+
return Boolean(entry.text?.trim()) || entry.poll != null;
|
|
322
338
|
}
|
|
323
339
|
/** Note a turn/post error against the kill switch; returns true once the room
|
|
324
340
|
* should auto-disarm (>= maxConsecutiveErrors in a row). Mutates `state`. */
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { type KnownProvider } from "@earendil-works/pi-ai";
|
|
2
|
-
|
|
2
|
+
/** Every chat surface, as a runtime list. The `SurfaceId` union derives from
|
|
3
|
+
* this literal so per-surface policy checklists (packages/cli/test/
|
|
4
|
+
* harness-budgets.test.ts) can iterate the surfaces at runtime while a new
|
|
5
|
+
* entry still breaks type-exhaustive tables at compile time. Adding a
|
|
6
|
+
* surface here requires classifying it in EVERY policy set (see AGENTS.md,
|
|
7
|
+
* "Harness invariants"). */
|
|
8
|
+
export declare const SURFACE_IDS: readonly ["cli", "telegram", "discord", "slack", "webui"];
|
|
9
|
+
export type SurfaceId = (typeof SURFACE_IDS)[number];
|
|
3
10
|
export interface AgentToolCall {
|
|
4
11
|
id: string;
|
|
5
12
|
name: string;
|
|
@@ -21,6 +28,11 @@ export type AgentMessage = {
|
|
|
21
28
|
export interface ToolDispatchResult {
|
|
22
29
|
isError?: true;
|
|
23
30
|
content: string;
|
|
31
|
+
/** Full-fidelity render payload for in-process SURFACES (e.g. the TUI's
|
|
32
|
+
* backtest card). Present only on the streamed copy channels consume —
|
|
33
|
+
* the agent runtime strips it before the result reaches the model's
|
|
34
|
+
* messages or the conversation store, so it never costs context. */
|
|
35
|
+
artifact?: unknown;
|
|
24
36
|
}
|
|
25
37
|
export interface LlmTool {
|
|
26
38
|
name: string;
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import { getProviders } from "@earendil-works/pi-ai";
|
|
2
|
+
/** Every chat surface, as a runtime list. The `SurfaceId` union derives from
|
|
3
|
+
* this literal so per-surface policy checklists (packages/cli/test/
|
|
4
|
+
* harness-budgets.test.ts) can iterate the surfaces at runtime while a new
|
|
5
|
+
* entry still breaks type-exhaustive tables at compile time. Adding a
|
|
6
|
+
* surface here requires classifying it in EVERY policy set (see AGENTS.md,
|
|
7
|
+
* "Harness invariants"). */
|
|
8
|
+
export const SURFACE_IDS = ["cli", "telegram", "discord", "slack", "webui"];
|
|
2
9
|
/** OM-level provider for any OpenAI-compatible /v1/chat/completions endpoint
|
|
3
10
|
* (Ollama, vLLM, LM Studio, llama.cpp, custom proxies). Not a pi-ai
|
|
4
11
|
* KnownProvider: the model object is constructed at turn time from the
|
|
@@ -32,7 +39,10 @@ export function stringifyToolContent(value) {
|
|
|
32
39
|
return "";
|
|
33
40
|
if (typeof value === "string")
|
|
34
41
|
return value;
|
|
35
|
-
|
|
42
|
+
// Compact on purpose: this string is LLM input, and 2-space pretty-printing
|
|
43
|
+
// inflated every structured tool result by roughly a fifth in tokens for
|
|
44
|
+
// zero model benefit.
|
|
45
|
+
return JSON.stringify(value);
|
|
36
46
|
}
|
|
37
47
|
export function parseToolArguments(raw) {
|
|
38
48
|
if (raw === undefined || raw === null)
|
|
@@ -18,6 +18,12 @@
|
|
|
18
18
|
import type { RoomTopic, RoomTopicAttention, RoomTopicBucketState, RoomTopicsResultPayload } from "../shared/rooms-protocol.js";
|
|
19
19
|
/** The reserved bucket key for untopicked (linear) messages. */
|
|
20
20
|
export declare const UNTOPICKED_BUCKET = "";
|
|
21
|
+
/** Effective attention for a bucket: an elapsed timed mute reads as the
|
|
22
|
+
* base attention ('default'). The store expires timed mutes lazily (no
|
|
23
|
+
* deadline-triggered frame), so a hydrated inbox would otherwise stay
|
|
24
|
+
* muted past the deadline until the next hydration. A null or absent
|
|
25
|
+
* deadline keeps the stored attention (permanent mute). */
|
|
26
|
+
export declare function resolveTopicAttention(state: Pick<RoomTopicBucketState, "attention" | "muteUntil">, nowMs?: number): RoomTopicAttention;
|
|
21
27
|
export interface TopicInboxItem {
|
|
22
28
|
/** Bucket key: a topicId, or UNTOPICKED_BUCKET for the linear lane. */
|
|
23
29
|
key: string;
|
|
@@ -17,6 +17,17 @@
|
|
|
17
17
|
*/
|
|
18
18
|
/** The reserved bucket key for untopicked (linear) messages. */
|
|
19
19
|
export const UNTOPICKED_BUCKET = "";
|
|
20
|
+
/** Effective attention for a bucket: an elapsed timed mute reads as the
|
|
21
|
+
* base attention ('default'). The store expires timed mutes lazily (no
|
|
22
|
+
* deadline-triggered frame), so a hydrated inbox would otherwise stay
|
|
23
|
+
* muted past the deadline until the next hydration. A null or absent
|
|
24
|
+
* deadline keeps the stored attention (permanent mute). */
|
|
25
|
+
export function resolveTopicAttention(state, nowMs = Date.now()) {
|
|
26
|
+
if (state.attention !== "mute" || state.muteUntil == null)
|
|
27
|
+
return state.attention;
|
|
28
|
+
const deadline = Date.parse(state.muteUntil);
|
|
29
|
+
return Number.isFinite(deadline) && deadline <= nowMs ? "default" : "mute";
|
|
30
|
+
}
|
|
20
31
|
function emptyBucket() {
|
|
21
32
|
return { lastSeq: 0, unreadCount: 0, cursor: 0, attention: "default" };
|
|
22
33
|
}
|
|
@@ -111,7 +122,7 @@ export class RoomTopicInbox {
|
|
|
111
122
|
return rows;
|
|
112
123
|
}
|
|
113
124
|
attentionFor(topicId) {
|
|
114
|
-
return this.bucketFor(topicId)
|
|
125
|
+
return resolveTopicAttention(this.bucketFor(topicId));
|
|
115
126
|
}
|
|
116
127
|
/** A registry mutation arrived (TOPIC_UPDATED, or a verb result echo). */
|
|
117
128
|
applyTopicUpdated(topic) {
|
|
@@ -160,6 +171,10 @@ export class RoomTopicInbox {
|
|
|
160
171
|
this.buckets.set(topicId, bucket);
|
|
161
172
|
}
|
|
162
173
|
bucket.attention = attention;
|
|
174
|
+
// The mirror knows only the new attention: a stale deadline from a
|
|
175
|
+
// previous timed mute must not expire it. The next hydration restores
|
|
176
|
+
// any server-side deadline.
|
|
177
|
+
delete bucket.muteUntil;
|
|
163
178
|
}
|
|
164
179
|
/**
|
|
165
180
|
* The viewer saw a bucket up to seq (the view batcher owns the durable
|
|
@@ -188,7 +203,7 @@ export class RoomTopicInbox {
|
|
|
188
203
|
channelUnread() {
|
|
189
204
|
let total = 0;
|
|
190
205
|
for (const bucket of this.buckets.values()) {
|
|
191
|
-
if (bucket
|
|
206
|
+
if (resolveTopicAttention(bucket) === "mute")
|
|
192
207
|
continue;
|
|
193
208
|
total += bucket.unreadCount;
|
|
194
209
|
}
|
|
@@ -205,7 +220,7 @@ export class RoomTopicInbox {
|
|
|
205
220
|
inboxItems() {
|
|
206
221
|
const items = [];
|
|
207
222
|
const untopicked = this.bucketFor(UNTOPICKED_BUCKET);
|
|
208
|
-
if (untopicked.unreadCount > 0 && untopicked
|
|
223
|
+
if (untopicked.unreadCount > 0 && resolveTopicAttention(untopicked) !== "mute") {
|
|
209
224
|
items.push({
|
|
210
225
|
key: UNTOPICKED_BUCKET,
|
|
211
226
|
topic: undefined,
|
|
@@ -218,12 +233,12 @@ export class RoomTopicInbox {
|
|
|
218
233
|
if (topic.mergedInto)
|
|
219
234
|
continue;
|
|
220
235
|
const state = this.bucketFor(topic.topicId);
|
|
221
|
-
if (state
|
|
236
|
+
if (resolveTopicAttention(state) === "mute")
|
|
222
237
|
continue;
|
|
223
238
|
let aliasUnread = 0;
|
|
224
239
|
for (const alias of this.aliasesOf(topic.topicId)) {
|
|
225
240
|
const aliasState = this.bucketFor(alias);
|
|
226
|
-
if (aliasState
|
|
241
|
+
if (resolveTopicAttention(aliasState) === "mute")
|
|
227
242
|
continue;
|
|
228
243
|
aliasUnread += aliasState.unreadCount;
|
|
229
244
|
}
|
|
@@ -222,6 +222,19 @@ export interface RoomLedgerEntry {
|
|
|
222
222
|
count: number;
|
|
223
223
|
}> | undefined;
|
|
224
224
|
editedAt?: string | number | Date | undefined;
|
|
225
|
+
/** Database-serialized edit counter: positive integer, strictly
|
|
226
|
+
* increasing per event row, incremented on every successful text or
|
|
227
|
+
* caption edit; absent means never edited. Orders caption edits where
|
|
228
|
+
* wall-clock editedAt stamps can tie or skew across store replicas.
|
|
229
|
+
* Missing on stores older than edit revisions. */
|
|
230
|
+
editRevision?: number | undefined;
|
|
231
|
+
/** Database-serialized card-mutation counter: positive integer,
|
|
232
|
+
* strictly increasing per event row, bumped on every unfurl card
|
|
233
|
+
* mutation (attach, edit-drop, purge); absent means cards were never
|
|
234
|
+
* mutated. Consumers apply card patches only at or above their known
|
|
235
|
+
* revision, making stale post-removal unfurl frames inert. Missing on
|
|
236
|
+
* stores older than unfurl revisions. */
|
|
237
|
+
unfurlsRevision?: number | undefined;
|
|
225
238
|
deletedAt?: string | number | Date | undefined;
|
|
226
239
|
deletedBy?: string | undefined;
|
|
227
240
|
replyCount?: number | undefined;
|
|
@@ -230,8 +243,14 @@ export interface RoomLedgerEntry {
|
|
|
230
243
|
pinnedBy?: string | null | undefined;
|
|
231
244
|
pinnedAt?: string | number | Date | null | undefined;
|
|
232
245
|
attachments?: RoomAttachment[] | undefined;
|
|
246
|
+
/** Counts-only poll summary when this message carries a poll. Additive:
|
|
247
|
+
* non-poll entries omit it, and live updates ride ENTRY_UPDATED. */
|
|
248
|
+
poll?: RoomPollSummary | null | undefined;
|
|
249
|
+
/** Server-rendered link unfurl cards. Additive: entries without links
|
|
250
|
+
* (and servers older than the unfurl worker) omit it. */
|
|
251
|
+
unfurls?: RoomUnfurl[] | undefined;
|
|
233
252
|
roomRole?: RoomRole | null | undefined;
|
|
234
|
-
metadata?: (Record<string, unknown> & Partial<RoomForwardMetadata>) | undefined;
|
|
253
|
+
metadata?: (Record<string, unknown> & Partial<RoomForwardMetadata> & Partial<RoomWebhookMetadata>) | undefined;
|
|
235
254
|
}
|
|
236
255
|
export type RoomForwardedFrom = {
|
|
237
256
|
kind: "room";
|
|
@@ -245,6 +264,18 @@ export type RoomForwardedFrom = {
|
|
|
245
264
|
export interface RoomForwardMetadata {
|
|
246
265
|
forwardedFrom: RoomForwardedFrom;
|
|
247
266
|
}
|
|
267
|
+
/** Sender identity for messages posted through a channel webhook (the
|
|
268
|
+
* Discord-compatible ingest). Entries carrying it render the webhook's
|
|
269
|
+
* name and avatar with an APP-style badge; the entry's handle mirrors
|
|
270
|
+
* the display name and its userId is the synthetic `webhook:<id>`, so a
|
|
271
|
+
* webhook can never impersonate a member. */
|
|
272
|
+
export interface RoomWebhookMetadata {
|
|
273
|
+
webhook: {
|
|
274
|
+
id: string;
|
|
275
|
+
name: string;
|
|
276
|
+
avatarUrl?: string | undefined;
|
|
277
|
+
};
|
|
278
|
+
}
|
|
248
279
|
export interface RoomAttachment {
|
|
249
280
|
/**
|
|
250
281
|
* Object key under room-attachments/; clients sign it at render time.
|
|
@@ -267,6 +298,46 @@ export interface RoomAttachment {
|
|
|
267
298
|
export type RoomAttachmentInput = RoomAttachment & {
|
|
268
299
|
key: string;
|
|
269
300
|
};
|
|
301
|
+
export interface RoomPollOption {
|
|
302
|
+
id: string;
|
|
303
|
+
text: string;
|
|
304
|
+
/** Vote count for this option. Counts only, never voter identities
|
|
305
|
+
* (voters are fetched on demand, exactly like reactors). */
|
|
306
|
+
count: number;
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Counts-only poll summary denormalized onto the carrying message (a poll
|
|
310
|
+
* IS a message). Vote/close/edit updates ride the existing ENTRY_UPDATED
|
|
311
|
+
* frame with the refreshed summary; the server's side collections stay the
|
|
312
|
+
* authoritative copy.
|
|
313
|
+
*/
|
|
314
|
+
export interface RoomPollSummary {
|
|
315
|
+
question: string;
|
|
316
|
+
options: RoomPollOption[];
|
|
317
|
+
multi: boolean;
|
|
318
|
+
/** ISO date when the poll auto-closes; null or absent means open-ended. */
|
|
319
|
+
closesAt?: string | null | undefined;
|
|
320
|
+
closed: boolean;
|
|
321
|
+
/** Store-serialized poll ordering revision, strictly increasing per
|
|
322
|
+
* poll: consumers reject frames below their known revision so
|
|
323
|
+
* bus-reordered vote/close updates cannot regress tallies. Present
|
|
324
|
+
* where the relay forwards it; missing on older relays. */
|
|
325
|
+
revision?: number | undefined;
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Server-rendered link unfurl card on an entry. `url` is the canonical
|
|
329
|
+
* URL; `thumbnailKey` is the re-hosted copy in the attachments bucket,
|
|
330
|
+
* signed through the member-scoped attachment path. The origin's image URL
|
|
331
|
+
* never crosses the wire. Cards appear via ENTRY_UPDATED when the unfurl
|
|
332
|
+
* worker lands.
|
|
333
|
+
*/
|
|
334
|
+
export interface RoomUnfurl {
|
|
335
|
+
url: string;
|
|
336
|
+
title?: string | undefined;
|
|
337
|
+
description?: string | undefined;
|
|
338
|
+
siteName?: string | undefined;
|
|
339
|
+
thumbnailKey?: string | undefined;
|
|
340
|
+
}
|
|
270
341
|
/**
|
|
271
342
|
* A registry-backed topic inside a channel. The id is permanent: rename
|
|
272
343
|
* changes `name` only, and links (`om://topic/<topicId>`) keep resolving.
|
|
@@ -330,6 +401,21 @@ export interface RoomReadStatePayload {
|
|
|
330
401
|
* Present when the store surfaces it and on ROOM_SET_ATTENTION acks
|
|
331
402
|
* (which reply READ_STATE scoped to the one room, readState empty). */
|
|
332
403
|
attention?: Record<string, RoomTopicAttention> | undefined;
|
|
404
|
+
/** Per-room timed-mute expiry (ISO date), keyed like attention. Rides
|
|
405
|
+
* beside a 'mute' attention entry when the mute carries an expiry; null
|
|
406
|
+
* marks an explicitly cleared or permanent mute. A lapsed expiry never
|
|
407
|
+
* appears: the server resolves it at read time and the attention value
|
|
408
|
+
* reads as its base ('default'). Missing on servers older than
|
|
409
|
+
* mute-with-duration. */
|
|
410
|
+
muteUntil?: Record<string, string | null> | undefined;
|
|
411
|
+
/** Per-room attention commit revision, keyed like attention: a
|
|
412
|
+
* database-serialized, strictly increasing counter per (user, room),
|
|
413
|
+
* present only for rooms whose attention has ever been committed.
|
|
414
|
+
* ORDERING CONTRACT: a consumer that has synced revision R for a room
|
|
415
|
+
* must ignore snapshot attention entries whose revision is <= R (a
|
|
416
|
+
* snapshot read before a newer commit can arrive after that commit's
|
|
417
|
+
* sync frame). Missing on stores older than attention revisions. */
|
|
418
|
+
attentionRevision?: Record<string, number> | undefined;
|
|
333
419
|
/** Unread mention counts by room, server-computed from the notify
|
|
334
420
|
* worker's ROOM_MENTION rows so badges survive reload and clear through
|
|
335
421
|
* the ack path. Unlike readState/attention the map is SPARSE AND
|
|
@@ -501,7 +587,7 @@ export interface RoomDmMessage {
|
|
|
501
587
|
createdAt?: string | number | undefined;
|
|
502
588
|
/** Sealed DM: content is '' and the opaque envelope rides here. */
|
|
503
589
|
secret?: RoomLedgerSecretPayload | null | undefined;
|
|
504
|
-
metadata?: (Record<string, unknown> & Partial<RoomForwardMetadata>) | undefined;
|
|
590
|
+
metadata?: (Record<string, unknown> & Partial<RoomForwardMetadata> & Partial<RoomWebhookMetadata>) | undefined;
|
|
505
591
|
/** Legacy public chat-image URL (renders as-is, no signing). */
|
|
506
592
|
imageUrl?: string | null | undefined;
|
|
507
593
|
/** Private, signed, scanned DM attachments (dm-attachments/ keyspace).
|
|
@@ -852,6 +938,14 @@ export interface RoomTopicBucketState {
|
|
|
852
938
|
unreadCount: number;
|
|
853
939
|
cursor: number;
|
|
854
940
|
attention: RoomTopicAttention;
|
|
941
|
+
/** Standing timed-mute deadline (ISO date; null = permanent mute).
|
|
942
|
+
* Present only on the room-wide '' bucket and only while its attention
|
|
943
|
+
* is an active 'mute'; never on topic buckets. Same semantics as the
|
|
944
|
+
* read-state muteUntil: the store expires timed mutes lazily, so
|
|
945
|
+
* consumers resolve an elapsed deadline back to the base attention
|
|
946
|
+
* ('default') at read time (see resolveTopicAttention). Missing on
|
|
947
|
+
* servers older than mute-with-duration. */
|
|
948
|
+
muteUntil?: string | null | undefined;
|
|
855
949
|
}
|
|
856
950
|
export interface RoomTopicResultPayload {
|
|
857
951
|
room: string;
|
package/dist/social-client.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
1
|
+
import type { RoomTopicAttention } from "./shared/rooms-protocol.js";
|
|
2
|
+
import type { RoomBookmarkSaved, RoomBookmarksResult, RoomDmConversation, RoomDmHistory, RoomDmSendResult, RoomDmUnreadConversation, RoomDoc, RoomDocAttribution, RoomDocChanges, RoomDocConflictHead, RoomDocEditingLease, RoomDocProvenance, RoomDocRevision, RoomDocRunRevertResult, RoomDocWriteResult, RoomFollow, RoomFollowResult, RoomFriend, RoomFriendRequests, RoomNotificationInboxResult, RoomPollVotersResult, RoomPublicProfile, RoomRepliesReadResult, RoomRepliesResult, RoomRestEvent, RoomSocialConfig, RoomSpaceRestorePlan, RoomSpaceRestoreReceipt, RoomSuggestion, RoomSuggestionRequest, RoomUserBlock, RoomUserBlocksResult } from "./social-types.js";
|
|
3
|
+
export type { RoomAttachment, RoomBookmark, RoomBookmarkSaved, RoomBookmarksResult, RoomDmConversation, RoomDmHistory, RoomDmMessage, RoomDmParticipant, RoomDmSendResult, RoomDmUnreadConversation, RoomDocAttribution, RoomDocEditingLease, RoomDocMergeRegion, RoomDocRevisionSource, RoomDocRunRevertResult, RoomFollow, RoomFollowResult, RoomFriend, RoomFriendRequest, RoomFriendRequests, RoomNotificationInboxItem, RoomNotificationInboxResult, RoomNotificationKind, RoomNotificationMessageData, RoomNotifyQuietHours, RoomPollOptionVoters, RoomPollVotersResult, RoomPublicProfile, RoomRepliesReadResult, RoomRepliesResult, RoomReplyInboxItem, RoomRestEvent, RoomSocialConfig, RoomSpaceNotificationOverride, RoomSpaceRestoreAction, RoomSpaceRestoreActionKind, RoomSpaceRestorePlan, RoomSpaceRestoreReceipt, RoomSpaceRestoreSummary, RoomSuggestion, RoomSuggestionIntent, RoomSuggestionRequest, RoomSuggestionSource, RoomsNotificationSettings, RoomUserBlock, RoomUserBlocksResult, } from "./social-types.js";
|
|
3
4
|
/** Largest per-file size any tier accepts (plus tier). The server enforces
|
|
4
5
|
* the per-tier caps (free 10MB/file, plus 25MB/file) and daily quotas; this
|
|
5
6
|
* constant only pre-flights uploads no tier could ever accept. */
|
|
@@ -210,6 +211,31 @@ export declare function updateSpaceRole(config: RoomSocialConfig, spaceId: strin
|
|
|
210
211
|
position?: number;
|
|
211
212
|
}): Promise<RoomSpaceRole>;
|
|
212
213
|
export declare function deleteSpaceRole(config: RoomSocialConfig, spaceId: string, roleId: string): Promise<void>;
|
|
214
|
+
/** A channel webhook: the Discord-compatible ingest capability. The
|
|
215
|
+
* posting URL is `<chat-api>/webhooks/<webhookId>/<token>`; the token is
|
|
216
|
+
* the whole credential, so treat listed webhooks like secrets. */
|
|
217
|
+
export interface RoomWebhook {
|
|
218
|
+
webhookId: string;
|
|
219
|
+
room: string;
|
|
220
|
+
name: string;
|
|
221
|
+
avatarUrl: string | null;
|
|
222
|
+
token: string;
|
|
223
|
+
createdBy: string;
|
|
224
|
+
/** Creator's handle at create time (display; createdBy is the id). */
|
|
225
|
+
createdByHandle?: string | null;
|
|
226
|
+
createdAt: string;
|
|
227
|
+
lastUsedAt: string | null;
|
|
228
|
+
deliveryCount: number;
|
|
229
|
+
disabled: boolean;
|
|
230
|
+
}
|
|
231
|
+
export declare function listRoomWebhooks(config: RoomSocialConfig, room: string): Promise<RoomWebhook[]>;
|
|
232
|
+
export declare function createRoomWebhook(config: RoomSocialConfig, room: string, input: {
|
|
233
|
+
name: string;
|
|
234
|
+
avatarUrl?: string | null;
|
|
235
|
+
}): Promise<RoomWebhook>;
|
|
236
|
+
export declare function deleteRoomWebhook(config: RoomSocialConfig, room: string, webhookId: string): Promise<void>;
|
|
237
|
+
/** Rotate the capability: the old URL dies immediately. */
|
|
238
|
+
export declare function regenerateRoomWebhookToken(config: RoomSocialConfig, room: string, webhookId: string): Promise<RoomWebhook>;
|
|
213
239
|
export declare function setSpaceMemberRoles(config: RoomSocialConfig, spaceId: string, targetUserId: string, roleIds: string[]): Promise<{
|
|
214
240
|
spaceId: string;
|
|
215
241
|
userId: string;
|
|
@@ -229,12 +255,20 @@ export interface RoomSpaceInvite {
|
|
|
229
255
|
invitedBy?: string;
|
|
230
256
|
role: "admin" | "member";
|
|
231
257
|
createdAt?: string;
|
|
258
|
+
/** Expiry deadline (ISO date); null or absent means the invite stands
|
|
259
|
+
* until accepted or declined. Additive (0.6.0 servers). */
|
|
260
|
+
expiresAt?: string | null;
|
|
232
261
|
}
|
|
233
|
-
/** Create (or refresh) a pending invitation. Membership needs their accept.
|
|
262
|
+
/** Create (or refresh) a pending invitation. Membership needs their accept.
|
|
263
|
+
* `expiresAt` (ISO date, future, at most 30 days out) puts a deadline on
|
|
264
|
+
* it; re-inviting without one clears any previous deadline. Fail-closed on
|
|
265
|
+
* servers older than 0.6.0: they persist the invite but drop the deadline,
|
|
266
|
+
* so a deadline the echo omits revokes the invite and throws. */
|
|
234
267
|
export declare function createSpaceInvite(config: RoomSocialConfig, spaceId: string, input: {
|
|
235
268
|
targetUserId?: string;
|
|
236
269
|
targetUsername?: string;
|
|
237
270
|
role?: "admin" | "member";
|
|
271
|
+
expiresAt?: string;
|
|
238
272
|
}): Promise<RoomSpaceInvite>;
|
|
239
273
|
/** The caller's own pending invitations. */
|
|
240
274
|
export declare function listMySpaceInvites(config: RoomSocialConfig): Promise<RoomSpaceInvite[]>;
|
|
@@ -249,10 +283,22 @@ export declare function acceptSpaceInvite(config: RoomSocialConfig, inviteId: st
|
|
|
249
283
|
export declare function declineSpaceInvite(config: RoomSocialConfig, inviteId: string): Promise<{
|
|
250
284
|
declined?: boolean;
|
|
251
285
|
}>;
|
|
252
|
-
/** Mint (or rotate) the shareable join code; plaintext comes back once.
|
|
253
|
-
|
|
286
|
+
/** Mint (or rotate) the shareable join code; plaintext comes back once.
|
|
287
|
+
* Optional caps: `expiresAt` (ISO date, future, at most 30 days out) and
|
|
288
|
+
* `maxUses` (1..10000). Rotation is a fresh grant: the new code starts at
|
|
289
|
+
* zero uses and takes exactly the caps given here (omitted caps clear the
|
|
290
|
+
* previous ones). Servers older than 0.6.0 echo neither cap field; they
|
|
291
|
+
* mint the code but drop the caps, so a requested cap the echo omits
|
|
292
|
+
* fail-closes: the fresh code is revoked and the call throws rather than
|
|
293
|
+
* hand back a live UNLIMITED code the caller believes capped. */
|
|
294
|
+
export declare function setSpaceJoinCode(config: RoomSocialConfig, spaceId: string, options?: {
|
|
295
|
+
expiresAt?: string | undefined;
|
|
296
|
+
maxUses?: number | undefined;
|
|
297
|
+
}): Promise<{
|
|
254
298
|
spaceId: string;
|
|
255
299
|
code: string;
|
|
300
|
+
expiresAt?: string | null;
|
|
301
|
+
maxUses?: number | null;
|
|
256
302
|
}>;
|
|
257
303
|
export declare function clearSpaceJoinCode(config: RoomSocialConfig, spaceId: string): Promise<{
|
|
258
304
|
cleared?: boolean;
|
|
@@ -386,6 +432,154 @@ export declare function listRoomNotifications(config: RoomSocialConfig, options?
|
|
|
386
432
|
unread?: boolean | undefined;
|
|
387
433
|
signal?: AbortSignal | undefined;
|
|
388
434
|
}): Promise<RoomNotificationInboxResult>;
|
|
435
|
+
/**
|
|
436
|
+
* Mark a room unread from `seq` (Discord-style): the room-wide read cursor
|
|
437
|
+
* drops to seq - 1 (server-clamped at 0) so the unread divider and pill
|
|
438
|
+
* return. The one sanctioned cursor-regression path; mention rows an
|
|
439
|
+
* earlier ack swept stay swept, and nothing is re-notified.
|
|
440
|
+
*/
|
|
441
|
+
export declare function markRoomUnread(config: RoomSocialConfig, room: string, seq: number): Promise<{
|
|
442
|
+
success: boolean;
|
|
443
|
+
}>;
|
|
444
|
+
/**
|
|
445
|
+
* Set the caller's room-level attention over REST, the path that carries
|
|
446
|
+
* mute-with-duration: `untilMs` (epoch ms, valid only with 'mute') stamps
|
|
447
|
+
* the expiry that read state later surfaces as `muteUntil`; the mute lapses
|
|
448
|
+
* server-side and reads as 'default' afterwards. Every write settles the
|
|
449
|
+
* expiry, so follow/default (and a plain permanent mute) clear a previous
|
|
450
|
+
* one. The pinned ROOM_SET_ATTENTION frame (setRoomAttentionOnClient)
|
|
451
|
+
* predates `untilMs` and stays the live-session path for permanent
|
|
452
|
+
* postures; servers older than mute-with-duration strip the field and land
|
|
453
|
+
* the mute permanent, so a requested `untilMs` the echo omits fail-closes:
|
|
454
|
+
* the attention is reset and the call throws rather than leave an
|
|
455
|
+
* unbounded mute the caller believes timed. Echoes the persisted
|
|
456
|
+
* attention; `muteUntil` is the ISO expiry, null when none stands.
|
|
457
|
+
*/
|
|
458
|
+
export declare function setRoomAttentionRest(config: RoomSocialConfig, room: string, attention: RoomTopicAttention, options?: {
|
|
459
|
+
untilMs?: number | undefined;
|
|
460
|
+
}): Promise<{
|
|
461
|
+
attention: RoomTopicAttention;
|
|
462
|
+
muteUntil: string | null;
|
|
463
|
+
}>;
|
|
464
|
+
/**
|
|
465
|
+
* Save a message for later (personal, per-user; nothing broadcasts).
|
|
466
|
+
* Idempotent: re-saving an already bookmarked event echoes the standing
|
|
467
|
+
* row with its original createdAt.
|
|
468
|
+
*/
|
|
469
|
+
export declare function addRoomBookmark(config: RoomSocialConfig, room: string, seq: number): Promise<RoomBookmarkSaved>;
|
|
470
|
+
export declare function removeRoomBookmark(config: RoomSocialConfig, room: string, seq: number): Promise<{
|
|
471
|
+
removed: boolean;
|
|
472
|
+
}>;
|
|
473
|
+
/**
|
|
474
|
+
* The caller's bookmarks across every room (GET /users/bookmarks), newest
|
|
475
|
+
* first with the notification inbox's composite cursor: pass a page's
|
|
476
|
+
* lastCursor and lastCursorId back TOGETHER for the next page. Rows in
|
|
477
|
+
* rooms the caller can no longer read are subtracted server-side.
|
|
478
|
+
*/
|
|
479
|
+
export declare function listUserBookmarks(config: RoomSocialConfig, options?: {
|
|
480
|
+
limit?: number | undefined;
|
|
481
|
+
/** Feed a page's lastCursor back here (explicit undefined reads as
|
|
482
|
+
* "first page", so `page.lastCursor ?? undefined` composes under
|
|
483
|
+
* exactOptionalPropertyTypes). */
|
|
484
|
+
cursor?: string | undefined;
|
|
485
|
+
cursorId?: string | undefined;
|
|
486
|
+
signal?: AbortSignal | undefined;
|
|
487
|
+
}): Promise<RoomBookmarksResult>;
|
|
488
|
+
/**
|
|
489
|
+
* The caller's user-block list (GET /users/blocks), newest first with the
|
|
490
|
+
* same composite-cursor paging as the notification inbox. Blocks are
|
|
491
|
+
* directional and the blocker is always the verified caller.
|
|
492
|
+
*/
|
|
493
|
+
export declare function getUserBlocks(config: RoomSocialConfig, options?: {
|
|
494
|
+
limit?: number | undefined;
|
|
495
|
+
cursor?: string | undefined;
|
|
496
|
+
cursorId?: string | undefined;
|
|
497
|
+
signal?: AbortSignal | undefined;
|
|
498
|
+
}): Promise<RoomUserBlocksResult>;
|
|
499
|
+
/** Block a user by userId (PUT, idempotent: re-blocking echoes the standing
|
|
500
|
+
* row with its original createdAt). Server-enforced effects: DM doors shut
|
|
501
|
+
* both directions, friend requests refuse, and the blocked sender produces
|
|
502
|
+
* no notifications for the caller. Room visibility is unchanged. */
|
|
503
|
+
export declare function blockUser(config: RoomSocialConfig, userId: string): Promise<RoomUserBlock>;
|
|
504
|
+
/** Unblock a user (idempotent: unblocking a non-blocked user is a no-op). */
|
|
505
|
+
export declare function unblockUser(config: RoomSocialConfig, userId: string): Promise<{
|
|
506
|
+
success: boolean;
|
|
507
|
+
}>;
|
|
508
|
+
/**
|
|
509
|
+
* Post a poll message. There is no separate create route and no WS input
|
|
510
|
+
* path (the relay's post frame carries text only): a poll rides the normal
|
|
511
|
+
* REST append as the strict body's `poll` block. `options` are option TEXT
|
|
512
|
+
* (2..10 entries; ids are minted server-side) and `closesAt` is an ISO
|
|
513
|
+
* deadline that must sit in the future. `text` is the optional caption;
|
|
514
|
+
* absent is fine, the poll IS the content. Echoes the created store event
|
|
515
|
+
* row with its counts-only summary; the room hears the same message
|
|
516
|
+
* through the ordinary live echo. Additive (0.6.0 servers): older stores
|
|
517
|
+
* reject the unknown block.
|
|
518
|
+
*/
|
|
519
|
+
export declare function createRoomPoll(config: RoomSocialConfig, room: string, input: {
|
|
520
|
+
question: string;
|
|
521
|
+
options: string[];
|
|
522
|
+
multi?: boolean | undefined;
|
|
523
|
+
closesAt?: string | undefined;
|
|
524
|
+
text?: string | undefined;
|
|
525
|
+
/** Exact-case workspace id: lets a COLD relay replica materialize a
|
|
526
|
+
* virtual workspace room without guessing the id's casing from the
|
|
527
|
+
* lowercased room name (owners resolve server-side; non-owner first
|
|
528
|
+
* writers need this). */
|
|
529
|
+
workspaceId?: string | undefined;
|
|
530
|
+
/** Route into a topic lane like any other append. */
|
|
531
|
+
topicId?: string | undefined;
|
|
532
|
+
/** Post the poll as a threaded reply. */
|
|
533
|
+
replyToSeq?: number | undefined;
|
|
534
|
+
/** Server-side idempotency: retries after an ambiguous failure reuse
|
|
535
|
+
* the key instead of minting a duplicate poll. */
|
|
536
|
+
clientMsgId?: string | undefined;
|
|
537
|
+
}): Promise<RoomRestEvent>;
|
|
538
|
+
/**
|
|
539
|
+
* Vote on a poll (the poll is keyed by its carrying message's seq). Echoes
|
|
540
|
+
* the store's public event row (RoomRestEvent, not the relay's mapped
|
|
541
|
+
* ledger entry) with the refreshed counts-only summary; the room hears the
|
|
542
|
+
* same update over ENTRY_UPDATED. Single-choice polls replace the caller's
|
|
543
|
+
* previous ballot; multi-choice polls union into it.
|
|
544
|
+
*/
|
|
545
|
+
export declare function voteRoomPoll(config: RoomSocialConfig, room: string, seq: number, optionIds: string[]): Promise<RoomRestEvent>;
|
|
546
|
+
/** Clear the caller's vote. Echoes the refreshed store event row. */
|
|
547
|
+
export declare function unvoteRoomPoll(config: RoomSocialConfig, room: string, seq: number): Promise<RoomRestEvent>;
|
|
548
|
+
/** Close a poll (idempotent on an already-closed poll). Echoes the store
|
|
549
|
+
* event row with `poll.closed` set. */
|
|
550
|
+
export declare function closeRoomPoll(config: RoomSocialConfig, room: string, seq: number): Promise<RoomRestEvent>;
|
|
551
|
+
/** Edit a poll's question/options (author-only, and locked once any vote
|
|
552
|
+
* exists or the poll closes; at least one field required). Options are
|
|
553
|
+
* full replacement TEXT, like creation. */
|
|
554
|
+
/** Purge an event's frozen unfurl cards without editing or deleting the
|
|
555
|
+
* message. Author or a delete-any moderation power; the store unsets the
|
|
556
|
+
* cards, schedules thumbnail cleanup, and returns the mutated event for
|
|
557
|
+
* ENTRY_UPDATED fan-out. An edit that removes a card's URL from the text
|
|
558
|
+
* drops that card automatically; this route is the explicit purge for
|
|
559
|
+
* everything else (a capability URL that must go without rewording the
|
|
560
|
+
* message, a moderator acting on someone else's post). */
|
|
561
|
+
export declare function unsetRoomEventUnfurls(config: RoomSocialConfig, room: string, seq: number): Promise<void>;
|
|
562
|
+
/** Edit an event's text over REST: the cold-replica-safe mirror of the WS
|
|
563
|
+
* EDIT frame, and the ONE shape the WS path cannot express: an empty text
|
|
564
|
+
* on a poll-bearing message legally clears the optional caption (the poll
|
|
565
|
+
* stays the content; the store rejects empty edits on plain messages).
|
|
566
|
+
* Viewers reconcile through the store's ENTRY_UPDATED fan-out exactly
|
|
567
|
+
* like the REST poll verbs. */
|
|
568
|
+
export declare function editRoomEventRest(config: RoomSocialConfig, room: string, seq: number, text: string): Promise<void>;
|
|
569
|
+
export declare function editRoomPoll(config: RoomSocialConfig, room: string, seq: number, patch: {
|
|
570
|
+
question?: string | undefined;
|
|
571
|
+
options?: string[] | undefined;
|
|
572
|
+
}): Promise<RoomRestEvent>;
|
|
573
|
+
/**
|
|
574
|
+
* Voter identities, on demand (the entry's summary carries counts only).
|
|
575
|
+
* Grouped per option; `optionId` narrows to one option and `limit` caps the
|
|
576
|
+
* identities per option (counts stay exact).
|
|
577
|
+
*/
|
|
578
|
+
export declare function listRoomPollVoters(config: RoomSocialConfig, room: string, seq: number, options?: {
|
|
579
|
+
optionId?: string | undefined;
|
|
580
|
+
limit?: number | undefined;
|
|
581
|
+
signal?: AbortSignal | undefined;
|
|
582
|
+
}): Promise<RoomPollVotersResult>;
|
|
389
583
|
export declare function suggestRooms(config: RoomSocialConfig, request: RoomSuggestionRequest, options?: {
|
|
390
584
|
signal?: AbortSignal;
|
|
391
585
|
}): Promise<RoomSuggestion[]>;
|
|
@@ -395,6 +589,9 @@ export declare function socialRequest<T>(config: RoomSocialConfig, path: string,
|
|
|
395
589
|
signal?: AbortSignal;
|
|
396
590
|
formData?: FormData;
|
|
397
591
|
headers?: Record<string, string>;
|
|
592
|
+
/** Fetch cache mode; token-bearing reads pass "no-store" so posting
|
|
593
|
+
* credentials never persist in a browser HTTP cache. */
|
|
594
|
+
cache?: RequestCache;
|
|
398
595
|
}): Promise<T>;
|
|
399
596
|
export declare function cleanUsername(username: string): string;
|
|
400
597
|
export declare function cleanRoomId(room: string): string;
|