@rine-network/sdk 0.1.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.
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Messages resource — send, inbox, read, reply, sendAndWait.
3
+ *
4
+ * All outbound messages are E2E-encrypted before leaving the SDK. Inbound
5
+ * messages are auto-decrypted with sender-key retry on read().
6
+ */
7
+ import { type AgentRead as CoreAgentRead, type HttpClient as CoreHttpClient } from "@rine-network/core";
8
+ import type { SDKHttpClient } from "../api/http.js";
9
+ import type { InboxOptions, ReadOptions, ReplyOptions, SendAndWaitOptions, SendOptions } from "../client.js";
10
+ import { type DecryptedMessage, type MessageRead, type SendAndWaitResult } from "../types.js";
11
+ import type { AgentHandle, AgentUuid, GroupHandle, GroupUuid, MessageUuid } from "../types.js";
12
+ import { CursorPage } from "../utils/cursor-page.js";
13
+ type Recipient = AgentHandle | AgentUuid | GroupHandle | GroupUuid;
14
+ export declare class MessagesResource {
15
+ private readonly http;
16
+ constructor(http: SDKHttpClient);
17
+ send(to: Recipient, payload: unknown, opts?: SendOptions): Promise<MessageRead>;
18
+ /**
19
+ * Fetch the current agent's inbox with auto-decryption (fixes audit C2).
20
+ *
21
+ * Route is `GET /agents/{agentId}/messages` — requires resolving the
22
+ * caller's agent UUID up-front (via the same `resolveSenderId()` path as
23
+ * `send()` / `reply()`), so the inbox is always scoped to one specific
24
+ * agent even in multi-agent orgs.
25
+ *
26
+ * Decryption runs per-item via `decryptEnvelope()`. Failures populate
27
+ * `decrypt_error` on the returned `DecryptedMessage` instead of throwing
28
+ * — the page still contains the envelope, just without plaintext. This
29
+ * matches the Python SDK's "best-effort" inbox contract.
30
+ */
31
+ inbox(opts?: InboxOptions): Promise<CursorPage<DecryptedMessage>>;
32
+ /**
33
+ * Fetch and decrypt a single message by id (fixes audit C3).
34
+ *
35
+ * Dispatches to `decryptMessage` (HPKE) or `decryptGroupMessage`
36
+ * (sender-key) based on `encryption_version`. On a sender-key failure
37
+ * (e.g. the reader has not yet ingested the sender's distribution),
38
+ * retries once after calling `fetchAndIngestPendingSKDistributions` via
39
+ * the rine-core bridge — matches Python SDK discipline.
40
+ *
41
+ * Failures on the HPKE path or after the sender-key retry populate
42
+ * `decrypt_error` on the returned message instead of throwing, so
43
+ * callers can branch on `msg.decrypt_error !== null`.
44
+ */
45
+ read(messageId: MessageUuid, opts?: ReadOptions): Promise<DecryptedMessage>;
46
+ reply(messageId: MessageUuid, payload: unknown, opts?: ReplyOptions): Promise<MessageRead>;
47
+ /**
48
+ * Atomic send-and-wait via `POST /messages/sync`.
49
+ *
50
+ * The server's `/messages/sync` endpoint accepts a full `MessageCreate`
51
+ * body (same shape as `POST /messages`) plus a `?timeout_ms` query param.
52
+ * It creates the message, delivers it, then long-polls for a reply — all
53
+ * in one HTTP request. Returns `SyncMessageResponse = { message, reply,
54
+ * conversation_id, status }`.
55
+ *
56
+ * Returns `{ sent, reply }`. `reply` is null on long-poll timeout.
57
+ *
58
+ * Note: the server rejects group handles on `/messages/sync`, so this
59
+ * method is 1:1 DMs only.
60
+ */
61
+ sendAndWait(to: Recipient, payload: unknown, opts?: SendAndWaitOptions): Promise<SendAndWaitResult>;
62
+ /**
63
+ * Build the `POST /messages` body with real E2EE output.
64
+ *
65
+ * - Group handle → `getOrCreateSenderKey` → `encryptGroupMessage` → body
66
+ * carries `to_handle` + sender-key envelope.
67
+ * - Everything else (agent handle, agent UUID, group UUID) → resolve to
68
+ * UUID via rine-core, HPKE-seal with `encryptMessage`, body carries
69
+ * `to_agent_id` + HPKE envelope.
70
+ *
71
+ * Note on group UUIDs: the server body shape for groups requires the
72
+ * handle (`to_handle`). Callers routing a group by UUID must pass the
73
+ * group handle instead — the UUID form is accepted at the type level for
74
+ * API symmetry but will be encrypted as a 1:1 and rejected server-side.
75
+ */
76
+ private buildSendBody;
77
+ /**
78
+ * Resolve the sender agent to a UUID suitable for loading local keys.
79
+ *
80
+ * Precedence: explicit override → SDK default agent → single-agent
81
+ * shortcut (errors on multi-agent orgs).
82
+ *
83
+ * Makes one `GET /agents` request per send; caching is deferred (the
84
+ * Python SDK and rine-cli do not cache either).
85
+ *
86
+ * Also verifies the resolved agent is in the caller's own org. `resolveAgent`
87
+ * short-circuits on UUIDs without validating membership, so a foreign-org
88
+ * UUID (e.g. an `original.to_agent_id` from a misrouted reply) would slip
89
+ * through and fail later inside `loadAgentKeys` with a confusing "keys file
90
+ * not found" filesystem error. The membership check turns that into a clear
91
+ * "not your agent" error (addresses Step 11 audit R3).
92
+ *
93
+ * Exposed as `public` (not `private`) so `client.messages()` (§11.1) can
94
+ * reuse the same resolution path for its SSE-bound decryption loop.
95
+ */
96
+ resolveSenderId(core: CoreHttpClient, explicit: string | undefined, preFetchedAgents?: CoreAgentRead[]): Promise<string>;
97
+ private defaultAgentHint;
98
+ /**
99
+ * Open an operation-scoped cancellation window that spans the whole
100
+ * encrypt → post chain. One timer, one AbortController. The `clear`
101
+ * callback must run in `finally` to satisfy audit MINOR-4 (timer leak).
102
+ *
103
+ * The op-timer aborts with a `RineTimeoutError` reason so that callers
104
+ * and `SDKHttpClient.request()` can distinguish user-signal abort
105
+ * (native AbortError) from SDK op-timer expiry (`RineTimeoutError`).
106
+ * Fixes the step-27 crypto audit Cr1 (SPEC §14.4).
107
+ */
108
+ private beginOp;
109
+ }
110
+ export {};
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Polling resource — lightweight count check + callback-based watch().
3
+ */
4
+ import type { SDKHttpClient } from "../api/http.js";
5
+ export declare class PollingResource {
6
+ private readonly http;
7
+ constructor(http: SDKHttpClient);
8
+ /**
9
+ * Lightweight poll — returns message count for the authenticated agent.
10
+ * No auth required, firewall-friendly.
11
+ *
12
+ * Route: reads `poll_url` from the default credential entry (a full path
13
+ * like `/poll/<token>` stamped in at agent-create time). The server does
14
+ * NOT expose a bare `/poll` endpoint — this was a route drift in the
15
+ * original TS rework surfaced by the step-27 audit. Matches Python
16
+ * `AsyncRineClient.poll` (`_client.py:623-636`).
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * const count = await client.polling.poll();
21
+ * ```
22
+ */
23
+ poll(opts?: {
24
+ signal?: AbortSignal;
25
+ }): Promise<number>;
26
+ /**
27
+ * watchPoll — call a callback each time the poll count increases.
28
+ *
29
+ * Returns an unsubscribe function.
30
+ *
31
+ * @example
32
+ * ```ts
33
+ * const unsubscribe = await client.polling.watchPoll((newCount) => {
34
+ * console.log('New message count:', newCount);
35
+ * });
36
+ * // Later: unsubscribe()
37
+ * ```
38
+ */
39
+ watchPoll(onCountChange: (newCount: number) => void, opts?: {
40
+ interval?: number;
41
+ signal?: AbortSignal;
42
+ }): () => void;
43
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * SSE streaming resource — /agents/{id}/stream with auto-reconnect.
3
+ *
4
+ * Delivers AsyncIterable<RineEvent> with:
5
+ * - Exponential backoff on disconnect (cap: 30s)
6
+ * - Last-Event-ID resume after reconnect
7
+ * - Heartbeat events during quiet periods
8
+ * - Full AbortSignal cancellation
9
+ */
10
+ import type { SDKHttpClient } from "../api/http.js";
11
+ import type { RineEvent } from "../types.js";
12
+ export interface StreamOptions {
13
+ /** External AbortSignal for cancellation. */
14
+ signal?: AbortSignal;
15
+ /** Agent ID/handle to stream events for. Defaults to authenticated agent. */
16
+ agent?: string;
17
+ /** Maximum stream duration in ms (timeout). */
18
+ timeout?: number;
19
+ /**
20
+ * Resume token for server-side replay. Sent as the initial
21
+ * `Last-Event-ID` request header; the server replays events after this
22
+ * id, then continues live. Updated automatically as live events arrive,
23
+ * so the SPEC §12.4 reconnect loop uses the most recent seen id on
24
+ * subsequent reconnects regardless of what the caller passed.
25
+ */
26
+ lastEventId?: string;
27
+ }
28
+ export declare class StreamsResource {
29
+ private readonly http;
30
+ constructor(http: SDKHttpClient);
31
+ /**
32
+ * Open an SSE stream yielding RineEvents.
33
+ *
34
+ * For agentic use cases, prefer `client.messages()` (SPEC §11.1) which
35
+ * wraps this with local decryption and cleartext type filtering — this
36
+ * method is the lower-level raw-event primitive.
37
+ *
38
+ * @example
39
+ * ```ts
40
+ * const ac = new AbortController();
41
+ * setTimeout(() => ac.abort(), 60_000);
42
+ *
43
+ * for await (const event of client.streams.stream({ signal: ac.signal })) {
44
+ * console.log(event.type, event.id);
45
+ * }
46
+ * ```
47
+ */
48
+ stream(opts?: StreamOptions): AsyncIterable<RineEvent>;
49
+ private connect;
50
+ }
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Webhooks resource — create, list, update, delete, deliveries.
3
+ *
4
+ * Step 10 — Track B: `create()` now takes the required `agentId` + optional
5
+ * `events` list per server schema; `update()` accepts only the `active`
6
+ * toggle (matches Python SDK). SPEC §10.7.
7
+ */
8
+ import type { SDKHttpClient } from "../api/http.js";
9
+ import type { AgentUuid, WebhookUuid } from "../types.js";
10
+ export declare class WebhooksResource {
11
+ private readonly http;
12
+ constructor(http: SDKHttpClient);
13
+ /**
14
+ * Register a webhook.
15
+ *
16
+ * Route: `POST /webhooks` with body `{agent_id, url, events?}`.
17
+ * `agent_id` is required by the server schema — previous TS SDK
18
+ * omitted it entirely, which 422'd every call.
19
+ */
20
+ create(agentId: AgentUuid, url: string, opts?: {
21
+ events?: readonly string[];
22
+ signal?: AbortSignal;
23
+ }): Promise<{
24
+ id: string;
25
+ created_at: string;
26
+ agent_id: string;
27
+ url: string;
28
+ active: boolean;
29
+ secret: string;
30
+ }>;
31
+ list(opts?: {
32
+ agentId?: AgentUuid;
33
+ includeInactive?: boolean;
34
+ signal?: AbortSignal;
35
+ }): Promise<import("../index.js").CursorPage<{
36
+ id: string;
37
+ created_at: string;
38
+ agent_id: string;
39
+ url: string;
40
+ active: boolean;
41
+ }>>;
42
+ /**
43
+ * Toggle a webhook's active flag.
44
+ *
45
+ * Body restricted to `{active}` per server schema — the previous TS
46
+ * SDK accepted `Partial<WebhookRead>` which did not match the server.
47
+ */
48
+ update(id: WebhookUuid, opts: {
49
+ active: boolean;
50
+ signal?: AbortSignal;
51
+ }): Promise<{
52
+ id: string;
53
+ created_at: string;
54
+ agent_id: string;
55
+ url: string;
56
+ active: boolean;
57
+ }>;
58
+ delete(id: WebhookUuid): Promise<void>;
59
+ deliveries(id: WebhookUuid, opts?: {
60
+ status?: "pending" | "processing" | "delivered" | "failed" | "dead";
61
+ limit?: number;
62
+ offset?: number;
63
+ signal?: AbortSignal;
64
+ }): Promise<import("../index.js").CursorPage<{
65
+ id: string;
66
+ created_at: string;
67
+ status: string;
68
+ webhook_id: string;
69
+ message_id: string;
70
+ attempts: number;
71
+ max_attempts: number;
72
+ delivered_at?: string | null | undefined;
73
+ last_error?: string | null | undefined;
74
+ next_attempt_at?: string | null | undefined;
75
+ }>>;
76
+ /**
77
+ * Aggregated delivery counts for a webhook.
78
+ *
79
+ * Route: `GET /webhooks/{id}/deliveries/summary`. Matches Python SDK
80
+ * `webhooks.delivery_summary` (SPEC §10.7).
81
+ */
82
+ deliverySummary(id: WebhookUuid, opts?: {
83
+ signal?: AbortSignal;
84
+ }): Promise<{
85
+ failed: number;
86
+ pending: number;
87
+ processing: number;
88
+ delivered: number;
89
+ dead: number;
90
+ total: number;
91
+ }>;
92
+ }