@rine-network/eve 0.1.0 → 0.3.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/README.md +25 -1
- package/dist/_zod.d.ts +1 -1
- package/dist/{channel-Bg8l58gg.js → channel-WlN3x1wP.js} +185 -29
- package/dist/channel-core.d.ts +15 -34
- package/dist/channel-outbound.d.ts +36 -0
- package/dist/channel.d.ts +26 -3
- package/dist/channel.js +2 -2
- package/dist/{client-X_-9CpQT.js → client-DsG2xtKs.js} +2 -2
- package/dist/client.d.ts +2 -2
- package/dist/errors.d.ts +1 -1
- package/dist/format.d.ts +8 -4
- package/dist/inbound.d.ts +22 -3
- package/dist/index.d.ts +2 -1
- package/dist/index.js +7 -7
- package/dist/onboard.d.ts +1 -1
- package/dist/onboard.js +1 -1
- package/dist/{registry-BG7S2XJg.js → registry-Bn4EqPcp.js} +22 -1
- package/dist/relay.d.ts +1 -1
- package/dist/relay.js +2 -2
- package/dist/{scaffold-Dpac1TMU.js → scaffold-3cEUy3jD.js} +8 -2
- package/dist/scaffold.js +1 -1
- package/dist/schemas-groups.d.ts +15 -3
- package/dist/schemas-payments.d.ts +34 -0
- package/dist/schemas.d.ts +3 -2
- package/dist/skill-content.d.ts +2 -2
- package/dist/{tool-BC49DldZ.js → tool-xlLdY8kn.js} +29 -12
- package/dist/tool.d.ts +16 -7
- package/dist/tools/groups.d.ts +11 -2
- package/dist/tools/index.d.ts +3 -2
- package/dist/tools/index.js +3 -3
- package/dist/tools/messaging.d.ts +1 -1
- package/dist/tools/payments.d.ts +21 -0
- package/dist/tools/registry.d.ts +3 -2
- package/dist/{tools-Bm9N6tB5.js → tools-BhkhV3Mv.js} +237 -11
- package/dist/transcript.d.ts +2 -2
- package/dist/types.d.ts +3 -1
- package/dist/webhook.d.ts +1 -1
- package/dist/webhook.js +2 -2
- package/dist/x402.d.ts +38 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -102,12 +102,34 @@ undecryptable mail is dropped by default.
|
|
|
102
102
|
| `rine_reply` | Reply to a message by id, in-thread |
|
|
103
103
|
| `rine_thread` | Decrypted both-sided transcript of a conversation |
|
|
104
104
|
| `rine_discover` / `rine_inspect` | Search the directory / read a profile |
|
|
105
|
-
| `rine_group_create` / `_invite` / `_remove` / `_inspect` | MLS-by-default groups |
|
|
105
|
+
| `rine_group_create` / `_invite` / `_remove` / `_inspect` / `_join` / `_invites` | MLS-by-default groups |
|
|
106
|
+
| `rine_pay` | Pay a received `rine.v1.x402_payment_required` quote under the local spend policy |
|
|
107
|
+
| `rine_fulfill` | Payee side: verify + settle a received payment and reply with a receipt |
|
|
106
108
|
|
|
107
109
|
Identity is resolved from the environment (`RINE_CONFIG_DIR`, `RINE_AGENT`,
|
|
108
110
|
`RINE_API_URL`) — never from a tool's model-visible input. Tools return plain text
|
|
109
111
|
and never surface ciphertext.
|
|
110
112
|
|
|
113
|
+
## Payments (x402)
|
|
114
|
+
|
|
115
|
+
`rine_pay` and `rine_fulfill` carry [x402](https://docs.rine.network/concepts/x402-payments/)
|
|
116
|
+
stablecoin payments as signed messages in the same encrypted thread; both wrap
|
|
117
|
+
`client.payments`. An inbound x402 frame **wakes a payment-aware turn** — a
|
|
118
|
+
`payment_required` points the agent at `rine_pay`, a `payment` at `rine_fulfill`, a
|
|
119
|
+
`receipt` is informational — and the terminal assistant prose into a payment thread stays
|
|
120
|
+
suppressed. `init --tools payments` scaffolds both tools. The wallet key stays on the host
|
|
121
|
+
and is never returned to the model, and a deny-by-default spend policy bounds every
|
|
122
|
+
signature. `rine_pay` returns one of the shared payer statuses (`payment-submitted`,
|
|
123
|
+
`no-wallet`, `not-payment-required`, `policy-refused`, `above-auto-pay-threshold`,
|
|
124
|
+
`already-paid`, `wallet-busy`); `rine_fulfill` reports `settled` / `settlement-failed` /
|
|
125
|
+
`verification-failed` / `facilitator-error` / `no-facilitator`.
|
|
126
|
+
|
|
127
|
+
Auto-pay is **opt-in, off by default**: `rineChannel({ payments: { autoPay: true } })` or
|
|
128
|
+
`RINE_X402_AUTO_PAY=1` lets an inbound quote at/below the wallet policy's auto-pay threshold
|
|
129
|
+
be paid with no LLM turn; a quote above the threshold falls back to surfacing it to the
|
|
130
|
+
model. `rine_fulfill`'s facilitator comes from `RINE_FACILITATOR` (a preset — `cdp` /
|
|
131
|
+
`payai` / `x402-rs` — or an `https://` base URL) or the tool's `facilitator` option.
|
|
132
|
+
|
|
111
133
|
**Human-in-the-loop (optional).** The mutating tools (`send`, `send_and_wait`,
|
|
112
134
|
`reply`, `group_*`) accept an opt-in approval gate — `rineSendTool({ needsApproval:
|
|
113
135
|
"once" })` (or `"always"`) — wired to Eve's approval flow. It's off by default, since
|
|
@@ -127,6 +149,8 @@ forever. Configure via `rineChannel({ ignoreTypes: [...] })`.
|
|
|
127
149
|
| `RINE_WEBHOOK_SECRET` | HMAC secret the channel verifies inbound with |
|
|
128
150
|
| `RINE_WEBHOOK_ID` | registered webhook id (for `webhook --delete`) |
|
|
129
151
|
| `RINE_INBOUND_PATH` | channel route path (default `/rine/v1/inbound`) |
|
|
152
|
+
| `RINE_X402_AUTO_PAY` | set `1` to auto-pay quotes at/below the policy's auto-pay threshold (default off) |
|
|
153
|
+
| `RINE_FACILITATOR` | `rine_fulfill` facilitator: a preset (`cdp` / `payai` / `x402-rs`) or an `https://` base URL |
|
|
130
154
|
|
|
131
155
|
## License
|
|
132
156
|
|
package/dist/_zod.d.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* accuracy) and parse the raw model input with the zod schema inside `execute`
|
|
10
10
|
* (applying defaults + coercion). One source of truth (zod), version-independent.
|
|
11
11
|
*
|
|
12
|
-
* Every tool's `outputSchema` is `string`
|
|
12
|
+
* Every tool's `outputSchema` is `string` — a tool returns clean text, so
|
|
13
13
|
* ciphertext can never ride a struct field.
|
|
14
14
|
*/
|
|
15
15
|
import { z, type ZodTypeAny } from "zod";
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { b as verifiedNote, m as renderMessageBody, n as asRecipient, v as renderThreadLine, y as senderLabel } from "./tool-xlLdY8kn.js";
|
|
2
2
|
import { n as verifyRineSignature } from "./hmac-CoaKHmf6.js";
|
|
3
|
-
import { t as getRineClient } from "./client-
|
|
3
|
+
import { t as getRineClient } from "./client-DsG2xtKs.js";
|
|
4
4
|
import { POST, defineChannel } from "eve/channels";
|
|
5
|
-
import { asMessageUuid } from "@rine-network/sdk";
|
|
5
|
+
import { X402Error, X402_MESSAGE_TYPE, asMessageUuid } from "@rine-network/sdk";
|
|
6
6
|
//#region src/transcript.ts
|
|
7
|
-
/** char ≈ 4 × tokens — the coarse proxy the spec specifies
|
|
7
|
+
/** char ≈ 4 × tokens — the coarse proxy the spec specifies. */
|
|
8
8
|
const CHARS_PER_TOKEN = 4;
|
|
9
9
|
/** Marker prepended when older turns were dropped to fit the budget. */
|
|
10
10
|
const OMITTED_MARKER = "[…earlier turns omitted]";
|
|
@@ -40,20 +40,31 @@ function renderTranscriptContext(entries, tokenBudget) {
|
|
|
40
40
|
* Inbound webhook helpers for the rine channel: extracting the message id from a
|
|
41
41
|
* rine standard-webhook body, and the continuation-token codec.
|
|
42
42
|
*
|
|
43
|
-
* The continuation token is how the channel makes outbound replies STATELESS
|
|
43
|
+
* The continuation token is how the channel makes outbound replies STATELESS:
|
|
44
44
|
* it encodes the rine `conversation_id`, the reply target (the group handle for
|
|
45
45
|
* group mail, else the sender handle), and — for 1:1 inbound only — the inbound
|
|
46
46
|
* `message id` so the `message.completed` handler can reply IN-PLACE (via the reply
|
|
47
47
|
* endpoint, preserving the inbound conversation) without any durable per-session map.
|
|
48
48
|
* Because the token bakes in the per-message `m`, it differs on every 1:1 inbound
|
|
49
49
|
* turn, so Eve does NOT resume a prior session from it — cross-turn continuity comes
|
|
50
|
-
* from the general thread primitive (push-injected transcript
|
|
50
|
+
* from the general thread primitive (push-injected transcript), not session
|
|
51
51
|
* resume. The message id is optional: group inbound and tokens minted by an older
|
|
52
52
|
* build carry only `{c,r}` and decode with `messageId === undefined`, so the outbound
|
|
53
53
|
* handler falls back to the `send()+parentConversationId` broadcast path.
|
|
54
54
|
*/
|
|
55
55
|
/** Sentinel marking our token payload, robust to Eve's `<channel>:` namespacing. */
|
|
56
56
|
const TOKEN_MARKER = "r1.";
|
|
57
|
+
/**
|
|
58
|
+
* Record a handled message id in the bounded per-runtime dedupe set. The set is
|
|
59
|
+
* cleared wholesale when it reaches {@link SEEN_CAP} (a coarse but allocation-free
|
|
60
|
+
* eviction — the server-side `delivered_at` ack is the durable dedupe). A no-op
|
|
61
|
+
* when no set is provided.
|
|
62
|
+
*/
|
|
63
|
+
function rememberSeen(seen, id) {
|
|
64
|
+
if (!seen) return;
|
|
65
|
+
if (seen.size >= 5e3) seen.clear();
|
|
66
|
+
seen.add(id);
|
|
67
|
+
}
|
|
57
68
|
/** base64url (no padding) encode of a UTF-8 string. */
|
|
58
69
|
function b64urlEncode(s) {
|
|
59
70
|
return Buffer.from(s, "utf-8").toString("base64url");
|
|
@@ -67,12 +78,13 @@ function b64urlDecode(s) {
|
|
|
67
78
|
* (`rine:`); we additionally fence our payload with {@link TOKEN_MARKER} so the
|
|
68
79
|
* decoder can recover it regardless of any prefix the framework adds.
|
|
69
80
|
*/
|
|
70
|
-
function encodeReplyToken(conversationId, replyTarget, messageId) {
|
|
81
|
+
function encodeReplyToken(conversationId, replyTarget, messageId, opts = {}) {
|
|
71
82
|
const payload = {
|
|
72
83
|
c: conversationId,
|
|
73
84
|
r: replyTarget
|
|
74
85
|
};
|
|
75
86
|
if (messageId) payload.m = messageId;
|
|
87
|
+
if (opts.x402) payload.x = 1;
|
|
76
88
|
return `${TOKEN_MARKER}${b64urlEncode(JSON.stringify(payload))}`;
|
|
77
89
|
}
|
|
78
90
|
/**
|
|
@@ -94,7 +106,8 @@ function decodeReplyToken(token) {
|
|
|
94
106
|
return {
|
|
95
107
|
conversationId,
|
|
96
108
|
replyTarget,
|
|
97
|
-
messageId: typeof m === "string" && m.length > 0 ? m : void 0
|
|
109
|
+
messageId: typeof m === "string" && m.length > 0 ? m : void 0,
|
|
110
|
+
x402: payload?.x === 1 ? true : void 0
|
|
98
111
|
};
|
|
99
112
|
} catch {
|
|
100
113
|
return null;
|
|
@@ -118,18 +131,134 @@ function messageIdFromWebhook(body) {
|
|
|
118
131
|
return typeof id === "string" && id.length > 0 ? id : void 0;
|
|
119
132
|
}
|
|
120
133
|
//#endregion
|
|
134
|
+
//#region src/x402.ts
|
|
135
|
+
/**
|
|
136
|
+
* x402 payment-aware inbound re-surfacing for the rine channel.
|
|
137
|
+
*
|
|
138
|
+
* The three x402 payment frames are first-class message types carrying a verbatim
|
|
139
|
+
* x402 V2 object, NOT a chat turn. v1.0 dropped them wholesale (a stateless gateway
|
|
140
|
+
* that auto-replied prose into a payment handshake would corrupt it). v1.1 re-surfaces
|
|
141
|
+
* them as an agent turn with STRUCTURED payment context so the model can act via the
|
|
142
|
+
* dedicated pay / fulfill tools — while the terminal assistant prose stays suppressed
|
|
143
|
+
* in EVERY case (the `x402` continuation-token flag; the guard hazard never re-arms):
|
|
144
|
+
*
|
|
145
|
+
* - `x402_payment_required` → the quote + guidance to pay via `rine_pay`.
|
|
146
|
+
* - `x402_payment` → the signed authorization + guidance to `rine_fulfill`.
|
|
147
|
+
* - `x402_receipt` → the settlement outcome, informational (no reply).
|
|
148
|
+
*
|
|
149
|
+
* Auto-pay (opt-in, DEFAULT OFF): when enabled, a `payment_required` at/below the
|
|
150
|
+
* policy's `autoPayThreshold` is paid with NO LLM turn — still bounded by the policy
|
|
151
|
+
* caps, deny-by-default, journal and reserve-lock (all in rine-core). Any decline
|
|
152
|
+
* falls back to surfacing the quote to the model.
|
|
153
|
+
*/
|
|
154
|
+
/** The three x402 frame types this module re-surfaces. */
|
|
155
|
+
const X402_FRAME_TYPES = [
|
|
156
|
+
X402_MESSAGE_TYPE.PAYMENT_REQUIRED,
|
|
157
|
+
X402_MESSAGE_TYPE.PAYMENT,
|
|
158
|
+
X402_MESSAGE_TYPE.RECEIPT
|
|
159
|
+
];
|
|
160
|
+
const X402_FRAME_SET = new Set(X402_FRAME_TYPES);
|
|
161
|
+
/** Whether a message type is one of the three x402 payment frames. */
|
|
162
|
+
function isX402Frame(type) {
|
|
163
|
+
return X402_FRAME_SET.has(type);
|
|
164
|
+
}
|
|
165
|
+
/** Atomic-unit amount of a requirement (x402 V2 `amount`, else V1 spelling). */
|
|
166
|
+
function requirementAmount(r) {
|
|
167
|
+
return r.amount ?? r.maxAmountRequired ?? "?";
|
|
168
|
+
}
|
|
169
|
+
/** One `accepts[]` requirement as a compact model-readable line. */
|
|
170
|
+
function requirementLine(r) {
|
|
171
|
+
return `${requirementAmount(r)} of ${r.asset} on ${r.network} → ${r.payTo}`;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* The structured payment body handed to the model as the turn's primary message.
|
|
175
|
+
* Reads only the decrypted plaintext (the verbatim x402 object) — never ciphertext.
|
|
176
|
+
*/
|
|
177
|
+
function renderX402Body(msg) {
|
|
178
|
+
const p = msg.plaintext;
|
|
179
|
+
if (msg.type === X402_MESSAGE_TYPE.PAYMENT_REQUIRED) {
|
|
180
|
+
const pr = p ?? {};
|
|
181
|
+
const accepts = Array.isArray(pr.accepts) ? pr.accepts : [];
|
|
182
|
+
const opts = accepts.length > 0 ? accepts.map((r) => ` - ${requirementLine(r)}`).join("\n") : " (no acceptable requirements advertised)";
|
|
183
|
+
const res = pr.resource?.description ?? pr.resource?.url;
|
|
184
|
+
return `x402 payment request. Accepted ways to pay:\n${opts}${res ? `\nfor: ${res}` : ""}`;
|
|
185
|
+
}
|
|
186
|
+
if (msg.type === X402_MESSAGE_TYPE.PAYMENT) {
|
|
187
|
+
const pp = p ?? {};
|
|
188
|
+
const acc = pp.accepted;
|
|
189
|
+
return `x402 signed payment authorization from ${pp.payload?.authorization?.from ?? "unknown payer"}${acc ? ` for ${requirementLine(acc)}` : ""}.`;
|
|
190
|
+
}
|
|
191
|
+
const sr = p ?? {};
|
|
192
|
+
return `x402 settlement receipt — ${sr.success ? "settled" : "failed"} (${sr.success ? `tx ${sr.transaction} on ${sr.network}` : `reason: ${sr.errorReason ?? "unknown"}`}).`;
|
|
193
|
+
}
|
|
194
|
+
/** The routing-guidance line: what the model should do, and the do-not-reply rule. */
|
|
195
|
+
function x402GuidanceLine(msg) {
|
|
196
|
+
const who = senderLabel(msg);
|
|
197
|
+
const note = verifiedNote(msg);
|
|
198
|
+
if (msg.type === X402_MESSAGE_TYPE.PAYMENT_REQUIRED) return `x402 quote from ${who} (${note}). To pay, call rine_pay with message_id ${msg.id} — the spend is bounded by your local policy (deny-by-default, caps). Do NOT send a text reply into this payment thread.`;
|
|
199
|
+
if (msg.type === X402_MESSAGE_TYPE.PAYMENT) return `x402 payment from ${who} (${note}). To verify, settle, and send a receipt, call rine_fulfill with message_id ${msg.id}. Do NOT send a text reply into this payment thread.`;
|
|
200
|
+
return `x402 receipt from ${who} (${note}). This is informational — no reply is expected.`;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Auto-pay attempt. Returns `null` when the quote was paid (no session needed),
|
|
204
|
+
* or a one-line decline note when auto-pay was refused or errored (the caller then
|
|
205
|
+
* surfaces the quote to the model). A policy refusal / above-threshold quote is a
|
|
206
|
+
* typed {@link X402Error}; anything else (network, wallet) is reported verbatim.
|
|
207
|
+
*/
|
|
208
|
+
async function tryAutoPay(deps, msg) {
|
|
209
|
+
try {
|
|
210
|
+
await deps.client.payments.pay(msg, { autoPay: true });
|
|
211
|
+
return null;
|
|
212
|
+
} catch (err) {
|
|
213
|
+
if (err instanceof X402Error) return `[auto-pay declined (${err.code}): ${err.message}]`;
|
|
214
|
+
return `[auto-pay failed: ${err instanceof Error ? err.message : String(err)}]`;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
/** Surface an x402 frame to the model without arming the prose auto-reply. */
|
|
218
|
+
async function surfaceX402(deps, msg, messageId, note) {
|
|
219
|
+
const replyTarget = msg.group_handle ?? msg.sender_handle ?? senderLabel(msg);
|
|
220
|
+
await deps.client.markDelivered([msg.id]).catch(() => {});
|
|
221
|
+
rememberSeen(deps.seen, messageId);
|
|
222
|
+
const context = [x402GuidanceLine(msg)];
|
|
223
|
+
if (note) context.push(note);
|
|
224
|
+
const token = encodeReplyToken(msg.conversation_id, replyTarget, void 0, { x402: true });
|
|
225
|
+
await deps.send({
|
|
226
|
+
message: renderX402Body(msg),
|
|
227
|
+
context
|
|
228
|
+
}, {
|
|
229
|
+
auth: null,
|
|
230
|
+
continuationToken: token
|
|
231
|
+
});
|
|
232
|
+
return new Response("ok", { status: 200 });
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Handle a verified inbound x402 frame. `payment_required` may auto-pay
|
|
236
|
+
* when enabled and at/below threshold; otherwise, and for `payment` / `receipt`,
|
|
237
|
+
* the frame is surfaced to the model as a payment-aware turn.
|
|
238
|
+
*/
|
|
239
|
+
async function handleX402Inbound(deps, msg, messageId) {
|
|
240
|
+
if (msg.type === X402_MESSAGE_TYPE.PAYMENT_REQUIRED && deps.autoPay) {
|
|
241
|
+
const decline = await tryAutoPay(deps, msg);
|
|
242
|
+
if (decline === null) {
|
|
243
|
+
await deps.client.markDelivered([msg.id]).catch(() => {});
|
|
244
|
+
rememberSeen(deps.seen, messageId);
|
|
245
|
+
return new Response("auto-paid", { status: 200 });
|
|
246
|
+
}
|
|
247
|
+
return surfaceX402(deps, msg, messageId, decline);
|
|
248
|
+
}
|
|
249
|
+
return surfaceX402(deps, msg, messageId, null);
|
|
250
|
+
}
|
|
251
|
+
//#endregion
|
|
121
252
|
//#region src/channel-core.ts
|
|
122
253
|
/**
|
|
123
|
-
*
|
|
124
|
-
* unit-testable with fake clients/sends. `channel.ts` wires
|
|
125
|
-
* `defineChannel`
|
|
254
|
+
* INBOUND half of the pure rine channel pipeline — no `eve` import, so it is
|
|
255
|
+
* unit-testable with fake clients/sends. `channel.ts` wires this to Eve's
|
|
256
|
+
* `defineChannel` route; the OUTBOUND half (`processCompletion`/`processFailure`)
|
|
257
|
+
* lives in `channel-outbound.ts`.
|
|
126
258
|
*
|
|
127
|
-
* - INBOUND (`processInbound`): HMAC-verify (transport auth
|
|
259
|
+
* - INBOUND (`processInbound`): HMAC-verify (transport auth) → parse → dedupe
|
|
128
260
|
* → `client.read(id)` to HPKE-decrypt + verify the sender's Ed25519 signature
|
|
129
|
-
* (content auth
|
|
130
|
-
* - OUTBOUND (`processCompletion`): on a terminal assistant message (R6) reply
|
|
131
|
-
* back over rine IN-PLACE via the reply endpoint, preserving the inbound
|
|
132
|
-
* conversation (R7); falls back to `parentConversationId` for legacy tokens.
|
|
261
|
+
* (content auth) → loop-guard → mark delivered → start/resume the session.
|
|
133
262
|
*
|
|
134
263
|
* TRUST NOTE: `verified === true` cryptographically binds the message's *signer*
|
|
135
264
|
* (the envelope `kid`), but the SDK exposes only the server-asserted
|
|
@@ -138,17 +267,16 @@ function messageIdFromWebhook(body) {
|
|
|
138
267
|
* verified signer needs an SDK change to surface `senderKid`; tracked as a
|
|
139
268
|
* follow-up. Matches the sibling rine connectors' behavior.
|
|
140
269
|
*/
|
|
141
|
-
const SEEN_CAP = 5e3;
|
|
142
270
|
/** The line of context handed to the model so it knows who/where the message is from. */
|
|
143
271
|
function senderContextLine(msg) {
|
|
144
272
|
const where = msg.group_handle ? ` in group ${msg.group_handle}` : "";
|
|
145
273
|
return `Inbound rine message from ${senderLabel(msg)}${where} (type ${msg.type}, ${verifiedNote(msg)}). Your reply is delivered back to the sender over rine.`;
|
|
146
274
|
}
|
|
147
|
-
/** Cap on turns fetched for the push-injected transcript
|
|
275
|
+
/** Cap on turns fetched for the push-injected transcript. */
|
|
148
276
|
const THREAD_FETCH_LIMIT = 50;
|
|
149
277
|
/**
|
|
150
278
|
* The inbound pipeline. Returns the HTTP {@link Response} the route should send.
|
|
151
|
-
* Status codes are retry-aware
|
|
279
|
+
* Status codes are retry-aware: `5xx` = transient (caller should retry),
|
|
152
280
|
* `4xx` = terminal drop, `2xx` = accepted/benign-ignore.
|
|
153
281
|
*/
|
|
154
282
|
async function processInbound(deps) {
|
|
@@ -173,17 +301,15 @@ async function processInbound(deps) {
|
|
|
173
301
|
if (msg.decrypt_error) return new Response("undecryptable", { status: 422 });
|
|
174
302
|
if (!msg.verified && !deps.acceptUnverified) return new Response("unverified sender signature", { status: 422 });
|
|
175
303
|
if (deps.ignoreTypes.includes(msg.type)) {
|
|
176
|
-
deps.seen
|
|
304
|
+
rememberSeen(deps.seen, messageId);
|
|
177
305
|
return new Response("ignored type", { status: 202 });
|
|
178
306
|
}
|
|
307
|
+
if (isX402Frame(msg.type)) return handleX402Inbound(deps, msg, messageId);
|
|
179
308
|
const replyTarget = msg.group_handle ?? msg.sender_handle;
|
|
180
309
|
if (!replyTarget) return new Response("no reply target", { status: 422 });
|
|
181
310
|
const token = msg.group_handle ? encodeReplyToken(msg.conversation_id, replyTarget) : encodeReplyToken(msg.conversation_id, replyTarget, msg.id);
|
|
182
311
|
await deps.client.markDelivered([msg.id]).catch(() => {});
|
|
183
|
-
|
|
184
|
-
if (deps.seen.size >= SEEN_CAP) deps.seen.clear();
|
|
185
|
-
deps.seen.add(messageId);
|
|
186
|
-
}
|
|
312
|
+
rememberSeen(deps.seen, messageId);
|
|
187
313
|
const context = [senderContextLine(msg)];
|
|
188
314
|
if (deps.threadContextTokenBudget != null) {
|
|
189
315
|
const priorTurns = (await deps.client.thread(msg.conversation_id, { limit: THREAD_FETCH_LIMIT }).catch(() => [])).slice(0, -1);
|
|
@@ -198,9 +324,22 @@ async function processInbound(deps) {
|
|
|
198
324
|
});
|
|
199
325
|
return new Response("ok", { status: 200 });
|
|
200
326
|
}
|
|
327
|
+
//#endregion
|
|
328
|
+
//#region src/channel-outbound.ts
|
|
329
|
+
/**
|
|
330
|
+
* OUTBOUND half of the pure rine channel pipeline — no `eve` import, so it is
|
|
331
|
+
* unit-testable with fake clients. `channel.ts` wires these to Eve's
|
|
332
|
+
* `message.completed` / failure events; the INBOUND half lives in `channel-core.ts`.
|
|
333
|
+
*
|
|
334
|
+
* On a terminal assistant message, reply back over rine IN-PLACE via the reply
|
|
335
|
+
* endpoint, preserving the inbound conversation; falls back to
|
|
336
|
+
* `parentConversationId` for legacy tokens. A session started by an x402 frame never
|
|
337
|
+
* prose-replies into the payment thread (the pay / fulfill tools transmit the signed
|
|
338
|
+
* frame in-thread instead) — the `x402` continuation-token flag short-circuits both.
|
|
339
|
+
*/
|
|
201
340
|
/**
|
|
202
341
|
* Outbound for `message.completed`: every TERMINAL assistant message (any
|
|
203
|
-
* `finishReason` except the intermediate `tool-calls
|
|
342
|
+
* `finishReason` except the intermediate `tool-calls`) with text becomes a
|
|
204
343
|
* reply. When the continuation token carries the inbound message id we reply
|
|
205
344
|
* IN-PLACE via the reply endpoint (same conversation, no fork) keeping the
|
|
206
345
|
* connector's `replyMessageType` unchanged — type-preserving, so terminal/wake
|
|
@@ -213,6 +352,7 @@ async function processCompletion(deps) {
|
|
|
213
352
|
if (!deps.message) return;
|
|
214
353
|
const rc = decodeReplyToken(deps.continuationToken);
|
|
215
354
|
if (!rc) return;
|
|
355
|
+
if (rc.x402) return;
|
|
216
356
|
if (rc.messageId) {
|
|
217
357
|
await deps.client.reply(asMessageUuid(rc.messageId), { text: deps.message }, { type: deps.replyMessageType });
|
|
218
358
|
return;
|
|
@@ -230,6 +370,7 @@ async function processCompletion(deps) {
|
|
|
230
370
|
async function processFailure(continuationToken, client) {
|
|
231
371
|
const rc = decodeReplyToken(continuationToken);
|
|
232
372
|
if (!rc) return;
|
|
373
|
+
if (rc.x402) return;
|
|
233
374
|
const text = "Sorry — I hit an internal error handling your request.";
|
|
234
375
|
if (rc.messageId) {
|
|
235
376
|
await client.reply(asMessageUuid(rc.messageId), { text }, { type: "rine.v1.error" });
|
|
@@ -245,14 +386,27 @@ async function processFailure(continuationToken, client) {
|
|
|
245
386
|
const DEFAULT_INBOUND_PATH = "/rine/v1/inbound";
|
|
246
387
|
const DEFAULT_REPLY_TYPE = "rine.v1.task_response";
|
|
247
388
|
const DEFAULT_MAX_BODY_BYTES = 512 * 1024;
|
|
248
|
-
/** Default token budget for the push-injected transcript (
|
|
389
|
+
/** Default token budget for the push-injected transcript (≈8000 chars). */
|
|
249
390
|
const DEFAULT_THREAD_CONTEXT_TOKEN_BUDGET = 2e3;
|
|
250
|
-
/**
|
|
391
|
+
/**
|
|
392
|
+
* Message types never auto-replied to (loop guard): the connector's OWN
|
|
393
|
+
* outbound types (task_response/error/receipt), so two rine-eve agents don't
|
|
394
|
+
* auto-reply to each other forever.
|
|
395
|
+
*
|
|
396
|
+
* The three x402 payment frames are NOT here: v1.1 re-surfaces them as
|
|
397
|
+
* payment-aware agent turns, routed by `handleX402Inbound` with the prose
|
|
398
|
+
* auto-reply suppressed (the `x402` continuation-token flag), never by the generic
|
|
399
|
+
* assistant reply. Adding them here would revert to the v1.0 silent-drop behavior.
|
|
400
|
+
*/
|
|
251
401
|
const DEFAULT_IGNORE_TYPES = [
|
|
252
402
|
"rine.v1.task_response",
|
|
253
403
|
"rine.v1.error",
|
|
254
404
|
"rine.v1.receipt"
|
|
255
405
|
];
|
|
406
|
+
/** Whether an env flag is truthy (`1` / `true`, case-insensitive). */
|
|
407
|
+
function envFlag(value) {
|
|
408
|
+
return value === "1" || value?.toLowerCase() === "true";
|
|
409
|
+
}
|
|
256
410
|
/** Build the rine channel. Default-export the result from `agent/channels/rine.ts`. */
|
|
257
411
|
function rineChannel(opts = {}) {
|
|
258
412
|
const path = opts.path ?? process.env.RINE_INBOUND_PATH ?? DEFAULT_INBOUND_PATH;
|
|
@@ -261,6 +415,7 @@ function rineChannel(opts = {}) {
|
|
|
261
415
|
const acceptUnverified = opts.acceptUnverified ?? false;
|
|
262
416
|
const ignoreTypes = opts.ignoreTypes ?? DEFAULT_IGNORE_TYPES;
|
|
263
417
|
const threadContextTokenBudget = opts.threadContextTokenBudget ?? DEFAULT_THREAD_CONTEXT_TOKEN_BUDGET;
|
|
418
|
+
const autoPay = opts.payments?.autoPay ?? envFlag(process.env.RINE_X402_AUTO_PAY);
|
|
264
419
|
const seen = /* @__PURE__ */ new Set();
|
|
265
420
|
const client = () => opts.client ?? getRineClient({
|
|
266
421
|
agent: opts.agent ?? process.env.RINE_AGENT,
|
|
@@ -278,7 +433,8 @@ function rineChannel(opts = {}) {
|
|
|
278
433
|
maxBodyBytes,
|
|
279
434
|
ignoreTypes,
|
|
280
435
|
seen,
|
|
281
|
-
threadContextTokenBudget
|
|
436
|
+
threadContextTokenBudget,
|
|
437
|
+
autoPay
|
|
282
438
|
}))],
|
|
283
439
|
events: {
|
|
284
440
|
"message.completed": async (data, channel) => {
|
|
@@ -301,4 +457,4 @@ function rineChannel(opts = {}) {
|
|
|
301
457
|
});
|
|
302
458
|
}
|
|
303
459
|
//#endregion
|
|
304
|
-
export {
|
|
460
|
+
export { processInbound as a, handleX402Inbound as c, x402GuidanceLine as d, decodeReplyToken as f, processFailure as i, isX402Frame as l, messageIdFromWebhook as m, rineChannel as n, senderContextLine as o, encodeReplyToken as p, processCompletion as r, X402_FRAME_TYPES as s, DEFAULT_IGNORE_TYPES as t, renderX402Body as u };
|
package/dist/channel-core.d.ts
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* unit-testable with fake clients/sends. `channel.ts` wires
|
|
4
|
-
* `defineChannel`
|
|
2
|
+
* INBOUND half of the pure rine channel pipeline — no `eve` import, so it is
|
|
3
|
+
* unit-testable with fake clients/sends. `channel.ts` wires this to Eve's
|
|
4
|
+
* `defineChannel` route; the OUTBOUND half (`processCompletion`/`processFailure`)
|
|
5
|
+
* lives in `channel-outbound.ts`.
|
|
5
6
|
*
|
|
6
|
-
* - INBOUND (`processInbound`): HMAC-verify (transport auth
|
|
7
|
+
* - INBOUND (`processInbound`): HMAC-verify (transport auth) → parse → dedupe
|
|
7
8
|
* → `client.read(id)` to HPKE-decrypt + verify the sender's Ed25519 signature
|
|
8
|
-
* (content auth
|
|
9
|
-
* - OUTBOUND (`processCompletion`): on a terminal assistant message (R6) reply
|
|
10
|
-
* back over rine IN-PLACE via the reply endpoint, preserving the inbound
|
|
11
|
-
* conversation (R7); falls back to `parentConversationId` for legacy tokens.
|
|
9
|
+
* (content auth) → loop-guard → mark delivered → start/resume the session.
|
|
12
10
|
*
|
|
13
11
|
* TRUST NOTE: `verified === true` cryptographically binds the message's *signer*
|
|
14
12
|
* (the envelope `kid`), but the SDK exposes only the server-asserted
|
|
@@ -42,41 +40,24 @@ export interface InboundDeps {
|
|
|
42
40
|
maxBodyBytes: number;
|
|
43
41
|
/** Inbound types to ignore (loop guard). */
|
|
44
42
|
ignoreTypes: readonly string[];
|
|
43
|
+
/**
|
|
44
|
+
* Opt-in auto-pay (default OFF). When true, an inbound `x402_payment_required`
|
|
45
|
+
* at/below the policy's `autoPayThreshold` is paid with NO LLM turn — still
|
|
46
|
+
* bounded by the caps, deny-by-default, journal and reserve-lock. Any decline
|
|
47
|
+
* falls back to surfacing the quote to the model.
|
|
48
|
+
*/
|
|
49
|
+
autoPay?: boolean;
|
|
45
50
|
/** Bounded set of already-handled message ids (dedupe across retries/replays). */
|
|
46
51
|
seen?: Set<string>;
|
|
47
52
|
/**
|
|
48
53
|
* Approximate token budget for the role-tagged transcript push-injected into the
|
|
49
|
-
* session context
|
|
54
|
+
* session context. When unset, only the sender line is injected.
|
|
50
55
|
*/
|
|
51
56
|
threadContextTokenBudget?: number;
|
|
52
57
|
}
|
|
53
58
|
/**
|
|
54
59
|
* The inbound pipeline. Returns the HTTP {@link Response} the route should send.
|
|
55
|
-
* Status codes are retry-aware
|
|
60
|
+
* Status codes are retry-aware: `5xx` = transient (caller should retry),
|
|
56
61
|
* `4xx` = terminal drop, `2xx` = accepted/benign-ignore.
|
|
57
62
|
*/
|
|
58
63
|
export declare function processInbound(deps: InboundDeps): Promise<Response>;
|
|
59
|
-
export interface CompletionDeps {
|
|
60
|
-
finishReason: string;
|
|
61
|
-
message: string | null;
|
|
62
|
-
continuationToken: string;
|
|
63
|
-
client: AsyncRineClient;
|
|
64
|
-
replyMessageType: string;
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Outbound for `message.completed`: every TERMINAL assistant message (any
|
|
68
|
-
* `finishReason` except the intermediate `tool-calls`, R6) with text becomes a
|
|
69
|
-
* reply. When the continuation token carries the inbound message id we reply
|
|
70
|
-
* IN-PLACE via the reply endpoint (same conversation, no fork) keeping the
|
|
71
|
-
* connector's `replyMessageType` unchanged — type-preserving, so terminal/wake
|
|
72
|
-
* semantics are exactly as before. Legacy tokens (no message id) fall back to the
|
|
73
|
-
* historical `send()+parentConversationId` path. No-ops for non-rine sessions
|
|
74
|
-
* (token not ours) or empty messages.
|
|
75
|
-
*/
|
|
76
|
-
export declare function processCompletion(deps: CompletionDeps): Promise<void>;
|
|
77
|
-
/**
|
|
78
|
-
* Best-effort error notice back to the sender on a terminal session failure.
|
|
79
|
-
* Replies IN-PLACE (reply endpoint, same conversation) keeping the `rine.v1.error`
|
|
80
|
-
* type; falls back to `send()+parentConversationId` for legacy tokens.
|
|
81
|
-
*/
|
|
82
|
-
export declare function processFailure(continuationToken: string, client: AsyncRineClient): Promise<void>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OUTBOUND half of the pure rine channel pipeline — no `eve` import, so it is
|
|
3
|
+
* unit-testable with fake clients. `channel.ts` wires these to Eve's
|
|
4
|
+
* `message.completed` / failure events; the INBOUND half lives in `channel-core.ts`.
|
|
5
|
+
*
|
|
6
|
+
* On a terminal assistant message, reply back over rine IN-PLACE via the reply
|
|
7
|
+
* endpoint, preserving the inbound conversation; falls back to
|
|
8
|
+
* `parentConversationId` for legacy tokens. A session started by an x402 frame never
|
|
9
|
+
* prose-replies into the payment thread (the pay / fulfill tools transmit the signed
|
|
10
|
+
* frame in-thread instead) — the `x402` continuation-token flag short-circuits both.
|
|
11
|
+
*/
|
|
12
|
+
import type { AsyncRineClient } from "@rine-network/sdk";
|
|
13
|
+
export interface CompletionDeps {
|
|
14
|
+
finishReason: string;
|
|
15
|
+
message: string | null;
|
|
16
|
+
continuationToken: string;
|
|
17
|
+
client: AsyncRineClient;
|
|
18
|
+
replyMessageType: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Outbound for `message.completed`: every TERMINAL assistant message (any
|
|
22
|
+
* `finishReason` except the intermediate `tool-calls`) with text becomes a
|
|
23
|
+
* reply. When the continuation token carries the inbound message id we reply
|
|
24
|
+
* IN-PLACE via the reply endpoint (same conversation, no fork) keeping the
|
|
25
|
+
* connector's `replyMessageType` unchanged — type-preserving, so terminal/wake
|
|
26
|
+
* semantics are exactly as before. Legacy tokens (no message id) fall back to the
|
|
27
|
+
* historical `send()+parentConversationId` path. No-ops for non-rine sessions
|
|
28
|
+
* (token not ours) or empty messages.
|
|
29
|
+
*/
|
|
30
|
+
export declare function processCompletion(deps: CompletionDeps): Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* Best-effort error notice back to the sender on a terminal session failure.
|
|
33
|
+
* Replies IN-PLACE (reply endpoint, same conversation) keeping the `rine.v1.error`
|
|
34
|
+
* type; falls back to `send()+parentConversationId` for legacy tokens.
|
|
35
|
+
*/
|
|
36
|
+
export declare function processFailure(continuationToken: string, client: AsyncRineClient): Promise<void>;
|
package/dist/channel.d.ts
CHANGED
|
@@ -10,8 +10,21 @@
|
|
|
10
10
|
* `channel-core.ts` (pure, testable); this file is the `defineChannel` wiring.
|
|
11
11
|
*/
|
|
12
12
|
import type { AsyncRineClient } from "@rine-network/sdk";
|
|
13
|
-
export { processInbound,
|
|
14
|
-
export
|
|
13
|
+
export { processInbound, senderContextLine } from "./channel-core.js";
|
|
14
|
+
export { processCompletion, processFailure } from "./channel-outbound.js";
|
|
15
|
+
export type { ChannelSend, InboundDeps } from "./channel-core.js";
|
|
16
|
+
export type { CompletionDeps } from "./channel-outbound.js";
|
|
17
|
+
/**
|
|
18
|
+
* Message types never auto-replied to (loop guard): the connector's OWN
|
|
19
|
+
* outbound types (task_response/error/receipt), so two rine-eve agents don't
|
|
20
|
+
* auto-reply to each other forever.
|
|
21
|
+
*
|
|
22
|
+
* The three x402 payment frames are NOT here: v1.1 re-surfaces them as
|
|
23
|
+
* payment-aware agent turns, routed by `handleX402Inbound` with the prose
|
|
24
|
+
* auto-reply suppressed (the `x402` continuation-token flag), never by the generic
|
|
25
|
+
* assistant reply. Adding them here would revert to the v1.0 silent-drop behavior.
|
|
26
|
+
*/
|
|
27
|
+
export declare const DEFAULT_IGNORE_TYPES: string[];
|
|
15
28
|
/** Options for {@link rineChannel}. All optional; identity defaults to env. */
|
|
16
29
|
export interface RineChannelOptions {
|
|
17
30
|
/** Acting agent (handle/name/UUID); defaults to `process.env.RINE_AGENT`. */
|
|
@@ -34,13 +47,23 @@ export interface RineChannelOptions {
|
|
|
34
47
|
maxBodyBytes?: number;
|
|
35
48
|
/**
|
|
36
49
|
* Approximate token budget for the role-tagged recent-transcript push-injected
|
|
37
|
-
* into the stateless Eve session context on each inbound
|
|
50
|
+
* into the stateless Eve session context on each inbound. Older
|
|
38
51
|
* turns are truncated first with a `[…earlier turns omitted]` marker; a
|
|
39
52
|
* single-message thread injects no extra transcript. Default ~2000 tokens.
|
|
40
53
|
*/
|
|
41
54
|
threadContextTokenBudget?: number;
|
|
42
55
|
/** A pre-built client (tests / advanced use); otherwise env-resolved lazily. */
|
|
43
56
|
client?: AsyncRineClient;
|
|
57
|
+
/**
|
|
58
|
+
* x402 payment behavior. `autoPay` is OPT-IN and DEFAULT OFF: when true, an
|
|
59
|
+
* inbound `x402_payment_required` at/below the policy's `autoPayThreshold` is paid
|
|
60
|
+
* with no LLM turn (still bounded by the caps, deny-by-default, journal and
|
|
61
|
+
* reserve-lock). Off ⇒ every quote is surfaced to the agent, which pays via
|
|
62
|
+
* `rine_pay`. Falls back to `RINE_X402_AUTO_PAY` (`1`/`true`) when unset.
|
|
63
|
+
*/
|
|
64
|
+
payments?: {
|
|
65
|
+
autoPay?: boolean;
|
|
66
|
+
};
|
|
44
67
|
}
|
|
45
68
|
/** Build the rine channel. Default-export the result from `agent/channels/rine.ts`. */
|
|
46
69
|
export declare function rineChannel(opts?: RineChannelOptions): import("eve/channels").Channel<undefined, Record<string, unknown>, Record<string, unknown>>;
|
package/dist/channel.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
export { processCompletion, processFailure, processInbound, rineChannel, senderContextLine };
|
|
1
|
+
import { a as processInbound, i as processFailure, n as rineChannel, o as senderContextLine, r as processCompletion, t as DEFAULT_IGNORE_TYPES } from "./channel-WlN3x1wP.js";
|
|
2
|
+
export { DEFAULT_IGNORE_TYPES, processCompletion, processFailure, processInbound, rineChannel, senderContextLine };
|
|
@@ -2,7 +2,7 @@ import { AsyncRineClient } from "@rine-network/sdk";
|
|
|
2
2
|
import { resolveApiUrl, resolveConfigDir } from "@rine-network/core";
|
|
3
3
|
//#region src/client.ts
|
|
4
4
|
/**
|
|
5
|
-
* Lazy `AsyncRineClient` construction (
|
|
5
|
+
* Lazy `AsyncRineClient` construction (side-effect-free import).
|
|
6
6
|
*
|
|
7
7
|
* No client is built at module load. The SDK client is created the first time a
|
|
8
8
|
* tool's `execute` (or a channel handler) actually fires, then memoized per
|
|
@@ -24,7 +24,7 @@ function cacheKey(configDir, apiUrl) {
|
|
|
24
24
|
/**
|
|
25
25
|
* Get (or lazily build + memoize) the shared `AsyncRineClient` for `opts`,
|
|
26
26
|
* already scoped to `opts.agent` when present. Call ONLY from inside a tool
|
|
27
|
-
* `execute` / a channel handler — never at module top level
|
|
27
|
+
* `execute` / a channel handler — never at module top level.
|
|
28
28
|
*/
|
|
29
29
|
function getRineClient(opts = {}) {
|
|
30
30
|
const configDir = opts.configDir ?? resolveConfigDir();
|
package/dist/client.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Lazy `AsyncRineClient` construction (
|
|
2
|
+
* Lazy `AsyncRineClient` construction (side-effect-free import).
|
|
3
3
|
*
|
|
4
4
|
* No client is built at module load. The SDK client is created the first time a
|
|
5
5
|
* tool's `execute` (or a channel handler) actually fires, then memoized per
|
|
@@ -27,7 +27,7 @@ export interface RineClientOpts {
|
|
|
27
27
|
/**
|
|
28
28
|
* Get (or lazily build + memoize) the shared `AsyncRineClient` for `opts`,
|
|
29
29
|
* already scoped to `opts.agent` when present. Call ONLY from inside a tool
|
|
30
|
-
* `execute` / a channel handler — never at module top level
|
|
30
|
+
* `execute` / a channel handler — never at module top level.
|
|
31
31
|
*/
|
|
32
32
|
export declare function getRineClient(opts?: RineClientOpts): AsyncRineClient;
|
|
33
33
|
/** Test-only: drop all memoized clients (not part of the public surface). */
|
package/dist/errors.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `formatError(err)` —
|
|
2
|
+
* `formatError(err)` — turns any thrown SDK error into a readable
|
|
3
3
|
* string for the LLM, never a stack trace. Tools wrap their one `await client.*`
|
|
4
4
|
* call in `try/catch → formatError` and RESOLVE (never reject) for mapped errors.
|
|
5
5
|
*
|
package/dist/format.d.ts
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
* Pure render functions: turn SDK return values into the human-readable strings
|
|
3
3
|
* tools + the channel hand to the LLM / send back over rine.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
5
|
+
* These read ONLY `plaintext` / `decrypt_error` / verification
|
|
6
6
|
* fields. They NEVER read `encrypted_payload` or any envelope/ciphertext field,
|
|
7
7
|
* so ciphertext can never reach the LLM context through a rendered string.
|
|
8
8
|
*/
|
|
9
9
|
import type { ThreadEntry } from "@rine-network/sdk";
|
|
10
|
-
import type { AgentProfile, AgentSummary, DecryptedMessage, GroupRead } from "./types.js";
|
|
10
|
+
import type { AgentProfile, AgentSummary, DecryptedMessage, GroupRead, JoinRequestRead, JoinResult } from "./types.js";
|
|
11
11
|
/** One thread turn as a role-tagged line: `[sent] you: …` / `[received] alice@org: …`. */
|
|
12
12
|
export declare function renderThreadLine(e: ThreadEntry): string;
|
|
13
13
|
/**
|
|
@@ -41,11 +41,15 @@ export declare function renderProfile(p: AgentProfile): string;
|
|
|
41
41
|
/**
|
|
42
42
|
* Self-diagnose a group's E2EE mode. Uses `mls_group_id !== null`, OR the
|
|
43
43
|
* explicit `mls_enabled`/`mls_pending` flags. With MLS support present this is a
|
|
44
|
-
* CAPABILITY flag, not a failure flag
|
|
44
|
+
* CAPABILITY flag, not a failure flag.
|
|
45
45
|
*/
|
|
46
46
|
export declare function groupIsMls(g: GroupRead): boolean;
|
|
47
47
|
/**
|
|
48
48
|
* A group rendered for `rine_group_inspect`. Both the MLS and sender-key branches
|
|
49
|
-
* are `[OK]` — an MLS group is readable/postable from here
|
|
49
|
+
* are `[OK]` — an MLS group is readable/postable from here.
|
|
50
50
|
*/
|
|
51
51
|
export declare function renderGroup(g: GroupRead): string;
|
|
52
|
+
/** `rine_group_join` outcome: immediate membership vs a pending vote. */
|
|
53
|
+
export declare function renderJoinResult(target: string, result: JoinResult): string;
|
|
54
|
+
/** A numbered list of the caller's pending group invites, or the empty-state line. */
|
|
55
|
+
export declare function renderInvites(items: readonly JoinRequestRead[]): string;
|