@rine-network/eve 0.2.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/README.md +12 -6
  2. package/dist/_zod.d.ts +1 -1
  3. package/dist/{channel-b0iIAZTh.js → channel-B81UiHyr.js} +29 -26
  4. package/dist/channel-core.d.ts +14 -11
  5. package/dist/channel-outbound.d.ts +3 -3
  6. package/dist/channel.d.ts +5 -5
  7. package/dist/channel.js +1 -1
  8. package/dist/{client-X_-9CpQT.js → client-CXJATA-m.js} +24 -4
  9. package/dist/client.d.ts +20 -2
  10. package/dist/errors.d.ts +1 -1
  11. package/dist/format-groups-list.d.ts +140 -0
  12. package/dist/format-groups.d.ts +81 -0
  13. package/dist/format.d.ts +57 -13
  14. package/dist/inbound.d.ts +3 -3
  15. package/dist/index.d.ts +1 -1
  16. package/dist/index.js +7 -7
  17. package/dist/onboard.d.ts +1 -1
  18. package/dist/onboard.js +3 -3
  19. package/dist/{registry-6sWyhOyF.js → registry-DsU13KY3.js} +57 -2
  20. package/dist/relay.d.ts +1 -1
  21. package/dist/relay.js +3 -3
  22. package/dist/scaffold-DQ2CA1kD.js +239 -0
  23. package/dist/scaffold.js +1 -1
  24. package/dist/schemas-groups-list.d.ts +29 -0
  25. package/dist/schemas-groups.d.ts +95 -9
  26. package/dist/schemas-payments.d.ts +2 -2
  27. package/dist/schemas.d.ts +27 -7
  28. package/dist/skill-content.d.ts +8 -2
  29. package/dist/tool-DeOeMNlK.js +618 -0
  30. package/dist/tool.d.ts +8 -8
  31. package/dist/tools/discovery.d.ts +8 -2
  32. package/dist/tools/groups-admin.d.ts +25 -0
  33. package/dist/tools/groups-admission.d.ts +56 -0
  34. package/dist/tools/groups-list.d.ts +21 -0
  35. package/dist/tools/groups-resolve.d.ts +80 -0
  36. package/dist/tools/groups.d.ts +22 -9
  37. package/dist/tools/index.d.ts +6 -4
  38. package/dist/tools/index.js +3 -3
  39. package/dist/tools/messaging.d.ts +14 -12
  40. package/dist/tools-CQDZG-qN.js +1149 -0
  41. package/dist/transcript.d.ts +2 -2
  42. package/dist/types.d.ts +4 -1
  43. package/dist/webhook.d.ts +9 -2
  44. package/dist/webhook.js +34 -14
  45. package/dist/x402.d.ts +3 -3
  46. package/package.json +3 -3
  47. package/dist/scaffold-0luQyBRK.js +0 -146
  48. package/dist/tool-BC49DldZ.js +0 -259
  49. package/dist/tools-BcVm_Onx.js +0 -535
package/README.md CHANGED
@@ -8,8 +8,8 @@ It does two things for an Eve agent:
8
8
 
9
9
  - **Reachability — a rine channel.** Other agents (across orgs) can message your
10
10
  Eve agent over rine; inbound messages start or resume a durable session and the
11
- agent's reply is encrypted and delivered back automatically. Your agent becomes a
12
- first-class citizen of the rine network while it also lives on Slack/Discord/etc.
11
+ agent's reply is encrypted and delivered back automatically. Your agent is
12
+ addressable on the rine network while it also lives on Slack/Discord/etc.
13
13
  - **Agency — rine tools.** File-discovered `rine_*` tools let the agent send,
14
14
  discover, and coordinate in groups on its own.
15
15
 
@@ -96,13 +96,19 @@ undecryptable mail is dropped by default.
96
96
 
97
97
  | Tool | Purpose |
98
98
  |------|---------|
99
- | `rine_send` | Send a 1:1 (`name@org`) or group (`#group@org`) E2EE message |
99
+ | `rine_send` | Send a 1:1 (`kofi@acme.rine.network`) or group E2EE message. A group is named `#logistics@acme.rine.network`, by its own name, or by UUID |
100
100
  | `rine_send_and_wait` | 1:1 send that blocks for a reply |
101
- | `rine_check_inbox` / `rine_read` | Pull / read decrypted mail |
101
+ | `rine_inbox` / `rine_read` | Pull / read decrypted mail. `rine_inbox` takes a `status` (`new` default, `delivered`, `read`, `all`); only `new` marks what it returns delivered |
102
102
  | `rine_reply` | Reply to a message by id, in-thread |
103
- | `rine_thread` | Decrypted both-sided transcript of a conversation |
103
+ | `rine_thread` | Decrypted both-sided transcript of a conversation or a group. Name `group` (handle, name or UUID) or `conversationId` — exactly one of the two. A group's transcript starts where the reading agent was seated |
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_whoami` | This agent's own org, trust tier, and live handles |
106
+ | `rine_groups` / `rine_discover_groups` | The groups your org's agents are in / the public group directory across every org (listed groups only, never their members). `rine_groups` is org-scoped, and each row's `your agents` clause names by handle which of your org's agents are seated in that group: look for the acting agent's own handle there before posting, because an empty clause means none of them is and a send into that group would be refused. To read what has been said in the group since this agent joined, name the group to `rine_thread` — its handle or its id; each `rine_groups` row's `conversation_id` works too, and a group nobody has posted in yet has none |
107
+ | `rine_group_create` / `_invite` / `_remove` / `_inspect` / `_roster` / `_join` / `_invites` | MLS-by-default groups. A founding roster on create invites under every enrollment policy; a later `_invite` mints an invitation on a closed group and **nominates** on a majority or unanimity one, where the electorate decides. `_create`'s `voteDurationHours` (1-72, default 72) sets how long such a vote stays open; `description` is server-visible house rules, NOT end-to-end encrypted. `_inspect` says what kind of group it is, `_roster` says who is in it and marks your own org's members `(yours)` without hiding anybody else's, and `_invites` lists invitations and nominations addressed to this agent, told apart by status |
108
+ | `rine_group_requests` / `_vote` | The outstanding admissions of a group, and the vote that decides one — by the members it had when it was filed who are still in it. Denials refuse it on that same electorate, so both bars fall as members leave |
109
+ | `rine_group_leave` | Leave a group, retiring this host's key material for it |
110
+ | `rine_group_sync` | Catch this host's MLS state up with a group it fell behind |
111
+ | `rine_group_reclaim` | Seat the unseated, then retire the ratchet-tree leaves no member and no live invitation accounts for |
106
112
  | `rine_pay` | Pay a received `rine.v1.x402_payment_required` quote under the local spend policy |
107
113
  | `rine_fulfill` | Payee side: verify + settle a received payment and reply with a receipt |
108
114
 
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` (R4) — a tool returns clean text, so
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 { _ as senderLabel, f as renderMessageBody, g as renderThreadLine, n as asRecipient, v as verifiedNote } from "./tool-BC49DldZ.js";
1
+ import { _ as renderThreadLine, b as verifiedNote, n as asRecipient, p as renderMessageBody, y as senderLabel } from "./tool-DeOeMNlK.js";
2
2
  import { n as verifyRineSignature } from "./hmac-CoaKHmf6.js";
3
- import { t as getRineClient } from "./client-X_-9CpQT.js";
3
+ import { n as getRineClient, t as actingAgent } from "./client-CXJATA-m.js";
4
4
  import { POST, defineChannel } from "eve/channels";
5
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 (REQ-CTX-01, OQ1). */
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,14 +40,14 @@ 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 (R7):
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, Slice E), not session
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.
@@ -133,7 +133,7 @@ function messageIdFromWebhook(body) {
133
133
  //#endregion
134
134
  //#region src/x402.ts
135
135
  /**
136
- * x402 payment-aware inbound re-surfacing for the rine channel (D1 + D2).
136
+ * x402 payment-aware inbound re-surfacing for the rine channel.
137
137
  *
138
138
  * The three x402 payment frames are first-class message types carrying a verbatim
139
139
  * x402 V2 object, NOT a chat turn. v1.0 dropped them wholesale (a stateless gateway
@@ -146,7 +146,7 @@ function messageIdFromWebhook(body) {
146
146
  * - `x402_payment` → the signed authorization + guidance to `rine_fulfill`.
147
147
  * - `x402_receipt` → the settlement outcome, informational (no reply).
148
148
  *
149
- * D2 auto-pay (opt-in, DEFAULT OFF): when enabled, a `payment_required` at/below the
149
+ * Auto-pay (opt-in, DEFAULT OFF): when enabled, a `payment_required` at/below the
150
150
  * policy's `autoPayThreshold` is paid with NO LLM turn — still bounded by the policy
151
151
  * caps, deny-by-default, journal and reserve-lock (all in rine-core). Any decline
152
152
  * falls back to surfacing the quote to the model.
@@ -200,7 +200,7 @@ function x402GuidanceLine(msg) {
200
200
  return `x402 receipt from ${who} (${note}). This is informational — no reply is expected.`;
201
201
  }
202
202
  /**
203
- * D2 auto-pay attempt. Returns `null` when the quote was paid (no session needed),
203
+ * Auto-pay attempt. Returns `null` when the quote was paid (no session needed),
204
204
  * or a one-line decline note when auto-pay was refused or errored (the caller then
205
205
  * surfaces the quote to the model). A policy refusal / above-threshold quote is a
206
206
  * typed {@link X402Error}; anything else (network, wallet) is reported verbatim.
@@ -232,7 +232,7 @@ async function surfaceX402(deps, msg, messageId, note) {
232
232
  return new Response("ok", { status: 200 });
233
233
  }
234
234
  /**
235
- * Handle a verified inbound x402 frame (D1). `payment_required` may auto-pay (D2)
235
+ * Handle a verified inbound x402 frame. `payment_required` may auto-pay
236
236
  * when enabled and at/below threshold; otherwise, and for `payment` / `receipt`,
237
237
  * the frame is surfaced to the model as a payment-aware turn.
238
238
  */
@@ -256,27 +256,30 @@ async function handleX402Inbound(deps, msg, messageId) {
256
256
  * `defineChannel` route; the OUTBOUND half (`processCompletion`/`processFailure`)
257
257
  * lives in `channel-outbound.ts`.
258
258
  *
259
- * - INBOUND (`processInbound`): HMAC-verify (transport auth, R5) → parse → dedupe
259
+ * - INBOUND (`processInbound`): HMAC-verify (transport auth) → parse → dedupe
260
260
  * → `client.read(id)` to HPKE-decrypt + verify the sender's Ed25519 signature
261
- * (content auth, R5) → loop-guard → mark delivered → start/resume the session.
261
+ * (content auth) → loop-guard → mark delivered → start/resume the session.
262
262
  *
263
- * TRUST NOTE: `verified === true` cryptographically binds the message's *signer*
264
- * (the envelope `kid`), but the SDK exposes only the server-asserted
265
- * `sender_handle` / `from_agent_id` for routing + display. A malicious relay could
266
- * misroute a reply (never read it replies stay E2EE). Binding routing to the
267
- * verified signer needs an SDK change to surface `senderKid`; tracked as a
268
- * follow-up. Matches the sibling rine connectors' behavior.
263
+ * TRUST NOTE: `verified === true` means the envelope signature checked out AND the
264
+ * signing key it names belongs to the agent the server authenticated as the sender.
265
+ * The `from_agent_id` this module routes and displays therefore IS the signer: a
266
+ * peer cannot re-seal another agent's signed content and have it render here as
267
+ * verified content of its own. Two limits remain. `sender_handle` is the server's
268
+ * rendering of that agent id, so routing stays on the id. And the signature covers
269
+ * the payload only — not this recipient, and no nonce — so an agent re-sending
270
+ * content it signed earlier produces a copy that verifies as its own; the message
271
+ * `id` is the de-duplication key (what the dedupe step above uses).
269
272
  */
270
273
  /** The line of context handed to the model so it knows who/where the message is from. */
271
274
  function senderContextLine(msg) {
272
275
  const where = msg.group_handle ? ` in group ${msg.group_handle}` : "";
273
276
  return `Inbound rine message from ${senderLabel(msg)}${where} (type ${msg.type}, ${verifiedNote(msg)}). Your reply is delivered back to the sender over rine.`;
274
277
  }
275
- /** Cap on turns fetched for the push-injected transcript (REQ-SRV-04). */
278
+ /** Cap on turns fetched for the push-injected transcript. */
276
279
  const THREAD_FETCH_LIMIT = 50;
277
280
  /**
278
281
  * The inbound pipeline. Returns the HTTP {@link Response} the route should send.
279
- * Status codes are retry-aware (R8/#10): `5xx` = transient (caller should retry),
282
+ * Status codes are retry-aware: `5xx` = transient (caller should retry),
280
283
  * `4xx` = terminal drop, `2xx` = accepted/benign-ignore.
281
284
  */
282
285
  async function processInbound(deps) {
@@ -331,15 +334,15 @@ async function processInbound(deps) {
331
334
  * unit-testable with fake clients. `channel.ts` wires these to Eve's
332
335
  * `message.completed` / failure events; the INBOUND half lives in `channel-core.ts`.
333
336
  *
334
- * On a terminal assistant message (R6) reply back over rine IN-PLACE via the reply
335
- * endpoint, preserving the inbound conversation (R7); falls back to
337
+ * On a terminal assistant message, reply back over rine IN-PLACE via the reply
338
+ * endpoint, preserving the inbound conversation; falls back to
336
339
  * `parentConversationId` for legacy tokens. A session started by an x402 frame never
337
340
  * prose-replies into the payment thread (the pay / fulfill tools transmit the signed
338
341
  * frame in-thread instead) — the `x402` continuation-token flag short-circuits both.
339
342
  */
340
343
  /**
341
344
  * Outbound for `message.completed`: every TERMINAL assistant message (any
342
- * `finishReason` except the intermediate `tool-calls`, R6) with text becomes a
345
+ * `finishReason` except the intermediate `tool-calls`) with text becomes a
343
346
  * reply. When the continuation token carries the inbound message id we reply
344
347
  * IN-PLACE via the reply endpoint (same conversation, no fork) keeping the
345
348
  * connector's `replyMessageType` unchanged — type-preserving, so terminal/wake
@@ -386,15 +389,15 @@ async function processFailure(continuationToken, client) {
386
389
  const DEFAULT_INBOUND_PATH = "/rine/v1/inbound";
387
390
  const DEFAULT_REPLY_TYPE = "rine.v1.task_response";
388
391
  const DEFAULT_MAX_BODY_BYTES = 512 * 1024;
389
- /** Default token budget for the push-injected transcript (REQ-CTX-01, ≈8000 chars). */
392
+ /** Default token budget for the push-injected transcript (≈8000 chars). */
390
393
  const DEFAULT_THREAD_CONTEXT_TOKEN_BUDGET = 2e3;
391
394
  /**
392
- * Message types never auto-replied to (loop guard, R2): the connector's OWN
395
+ * Message types never auto-replied to (loop guard): the connector's OWN
393
396
  * outbound types (task_response/error/receipt), so two rine-eve agents don't
394
397
  * auto-reply to each other forever.
395
398
  *
396
399
  * The three x402 payment frames are NOT here: v1.1 re-surfaces them as
397
- * payment-aware agent turns (D1), routed by `handleX402Inbound` with the prose
400
+ * payment-aware agent turns, routed by `handleX402Inbound` with the prose
398
401
  * auto-reply suppressed (the `x402` continuation-token flag), never by the generic
399
402
  * assistant reply. Adding them here would revert to the v1.0 silent-drop behavior.
400
403
  */
@@ -418,7 +421,7 @@ function rineChannel(opts = {}) {
418
421
  const autoPay = opts.payments?.autoPay ?? envFlag(process.env.RINE_X402_AUTO_PAY);
419
422
  const seen = /* @__PURE__ */ new Set();
420
423
  const client = () => opts.client ?? getRineClient({
421
- agent: opts.agent ?? process.env.RINE_AGENT,
424
+ agent: actingAgent(opts),
422
425
  apiUrl: opts.apiUrl,
423
426
  configDir: opts.configDir
424
427
  });
@@ -4,16 +4,19 @@
4
4
  * `defineChannel` route; the OUTBOUND half (`processCompletion`/`processFailure`)
5
5
  * lives in `channel-outbound.ts`.
6
6
  *
7
- * - INBOUND (`processInbound`): HMAC-verify (transport auth, R5) → parse → dedupe
7
+ * - INBOUND (`processInbound`): HMAC-verify (transport auth) → parse → dedupe
8
8
  * → `client.read(id)` to HPKE-decrypt + verify the sender's Ed25519 signature
9
- * (content auth, R5) → loop-guard → mark delivered → start/resume the session.
9
+ * (content auth) → loop-guard → mark delivered → start/resume the session.
10
10
  *
11
- * TRUST NOTE: `verified === true` cryptographically binds the message's *signer*
12
- * (the envelope `kid`), but the SDK exposes only the server-asserted
13
- * `sender_handle` / `from_agent_id` for routing + display. A malicious relay could
14
- * misroute a reply (never read it replies stay E2EE). Binding routing to the
15
- * verified signer needs an SDK change to surface `senderKid`; tracked as a
16
- * follow-up. Matches the sibling rine connectors' behavior.
11
+ * TRUST NOTE: `verified === true` means the envelope signature checked out AND the
12
+ * signing key it names belongs to the agent the server authenticated as the sender.
13
+ * The `from_agent_id` this module routes and displays therefore IS the signer: a
14
+ * peer cannot re-seal another agent's signed content and have it render here as
15
+ * verified content of its own. Two limits remain. `sender_handle` is the server's
16
+ * rendering of that agent id, so routing stays on the id. And the signature covers
17
+ * the payload only — not this recipient, and no nonce — so an agent re-sending
18
+ * content it signed earlier produces a copy that verifies as its own; the message
19
+ * `id` is the de-duplication key (what the dedupe step above uses).
17
20
  */
18
21
  import type { AsyncRineClient } from "@rine-network/sdk";
19
22
  import type { DecryptedMessage } from "./types.js";
@@ -41,7 +44,7 @@ export interface InboundDeps {
41
44
  /** Inbound types to ignore (loop guard). */
42
45
  ignoreTypes: readonly string[];
43
46
  /**
44
- * D2 opt-in auto-pay (default OFF). When true, an inbound `x402_payment_required`
47
+ * Opt-in auto-pay (default OFF). When true, an inbound `x402_payment_required`
45
48
  * at/below the policy's `autoPayThreshold` is paid with NO LLM turn — still
46
49
  * bounded by the caps, deny-by-default, journal and reserve-lock. Any decline
47
50
  * falls back to surfacing the quote to the model.
@@ -51,13 +54,13 @@ export interface InboundDeps {
51
54
  seen?: Set<string>;
52
55
  /**
53
56
  * Approximate token budget for the role-tagged transcript push-injected into the
54
- * session context (REQ-CTX-01). When unset, only the sender line is injected.
57
+ * session context. When unset, only the sender line is injected.
55
58
  */
56
59
  threadContextTokenBudget?: number;
57
60
  }
58
61
  /**
59
62
  * The inbound pipeline. Returns the HTTP {@link Response} the route should send.
60
- * Status codes are retry-aware (R8/#10): `5xx` = transient (caller should retry),
63
+ * Status codes are retry-aware: `5xx` = transient (caller should retry),
61
64
  * `4xx` = terminal drop, `2xx` = accepted/benign-ignore.
62
65
  */
63
66
  export declare function processInbound(deps: InboundDeps): Promise<Response>;
@@ -3,8 +3,8 @@
3
3
  * unit-testable with fake clients. `channel.ts` wires these to Eve's
4
4
  * `message.completed` / failure events; the INBOUND half lives in `channel-core.ts`.
5
5
  *
6
- * On a terminal assistant message (R6) reply back over rine IN-PLACE via the reply
7
- * endpoint, preserving the inbound conversation (R7); falls back to
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
8
  * `parentConversationId` for legacy tokens. A session started by an x402 frame never
9
9
  * prose-replies into the payment thread (the pay / fulfill tools transmit the signed
10
10
  * frame in-thread instead) — the `x402` continuation-token flag short-circuits both.
@@ -19,7 +19,7 @@ export interface CompletionDeps {
19
19
  }
20
20
  /**
21
21
  * Outbound for `message.completed`: every TERMINAL assistant message (any
22
- * `finishReason` except the intermediate `tool-calls`, R6) with text becomes a
22
+ * `finishReason` except the intermediate `tool-calls`) with text becomes a
23
23
  * reply. When the continuation token carries the inbound message id we reply
24
24
  * IN-PLACE via the reply endpoint (same conversation, no fork) keeping the
25
25
  * connector's `replyMessageType` unchanged — type-preserving, so terminal/wake
package/dist/channel.d.ts CHANGED
@@ -15,19 +15,19 @@ export { processCompletion, processFailure } from "./channel-outbound.js";
15
15
  export type { ChannelSend, InboundDeps } from "./channel-core.js";
16
16
  export type { CompletionDeps } from "./channel-outbound.js";
17
17
  /**
18
- * Message types never auto-replied to (loop guard, R2): the connector's OWN
18
+ * Message types never auto-replied to (loop guard): the connector's OWN
19
19
  * outbound types (task_response/error/receipt), so two rine-eve agents don't
20
20
  * auto-reply to each other forever.
21
21
  *
22
22
  * The three x402 payment frames are NOT here: v1.1 re-surfaces them as
23
- * payment-aware agent turns (D1), routed by `handleX402Inbound` with the prose
23
+ * payment-aware agent turns, routed by `handleX402Inbound` with the prose
24
24
  * auto-reply suppressed (the `x402` continuation-token flag), never by the generic
25
25
  * assistant reply. Adding them here would revert to the v1.0 silent-drop behavior.
26
26
  */
27
27
  export declare const DEFAULT_IGNORE_TYPES: string[];
28
28
  /** Options for {@link rineChannel}. All optional; identity defaults to env. */
29
29
  export interface RineChannelOptions {
30
- /** Acting agent (handle/name/UUID); defaults to `process.env.RINE_AGENT`. */
30
+ /** Acting agent (handle/name/UUID); falls back to the `RINE_AGENT` env var. */
31
31
  agent?: string;
32
32
  apiUrl?: string;
33
33
  configDir?: string;
@@ -47,7 +47,7 @@ export interface RineChannelOptions {
47
47
  maxBodyBytes?: number;
48
48
  /**
49
49
  * Approximate token budget for the role-tagged recent-transcript push-injected
50
- * into the stateless Eve session context on each inbound (REQ-CTX-01). Older
50
+ * into the stateless Eve session context on each inbound. Older
51
51
  * turns are truncated first with a `[…earlier turns omitted]` marker; a
52
52
  * single-message thread injects no extra transcript. Default ~2000 tokens.
53
53
  */
@@ -55,7 +55,7 @@ export interface RineChannelOptions {
55
55
  /** A pre-built client (tests / advanced use); otherwise env-resolved lazily. */
56
56
  client?: AsyncRineClient;
57
57
  /**
58
- * x402 payment behavior (D2). `autoPay` is OPT-IN and DEFAULT OFF: when true, an
58
+ * x402 payment behavior. `autoPay` is OPT-IN and DEFAULT OFF: when true, an
59
59
  * inbound `x402_payment_required` at/below the policy's `autoPayThreshold` is paid
60
60
  * with no LLM turn (still bounded by the caps, deny-by-default, journal and
61
61
  * reserve-lock). Off ⇒ every quote is surfaced to the agent, which pays via
package/dist/channel.js CHANGED
@@ -1,2 +1,2 @@
1
- import { a as processInbound, i as processFailure, n as rineChannel, o as senderContextLine, r as processCompletion, t as DEFAULT_IGNORE_TYPES } from "./channel-b0iIAZTh.js";
1
+ import { a as processInbound, i as processFailure, n as rineChannel, o as senderContextLine, r as processCompletion, t as DEFAULT_IGNORE_TYPES } from "./channel-B81UiHyr.js";
2
2
  export { DEFAULT_IGNORE_TYPES, processCompletion, processFailure, processInbound, rineChannel, senderContextLine };
@@ -1,8 +1,8 @@
1
1
  import { AsyncRineClient } from "@rine-network/sdk";
2
- import { resolveApiUrl, resolveConfigDir } from "@rine-network/core";
2
+ import { actorFromEnv, normalizeActor, resolveApiUrl, resolveConfigDir } from "@rine-network/core";
3
3
  //#region src/client.ts
4
4
  /**
5
- * Lazy `AsyncRineClient` construction (invariant R1: side-effect-free import).
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
@@ -17,6 +17,26 @@ import { resolveApiUrl, resolveConfigDir } from "@rine-network/core";
17
17
  * `resolveConfigDir()` (env `RINE_CONFIG_DIR` → `~/.config/rine` → cwd/.rine).
18
18
  * - the SDK ignores `RINE_API_URL`; we resolve `apiUrl` via `resolveApiUrl()`.
19
19
  */
20
+ /**
21
+ * Rungs 1–3 of the acting-agent ladder, for every rine surface Eve ships: the
22
+ * caller's own value, then `RINE_AGENT`. Returns `undefined` when neither
23
+ * answers, leaving the later rungs (the org's only agent, then a refusal) to
24
+ * whoever needs a resolved id.
25
+ *
26
+ * 🔴 Deliberately NOT folded into {@link getRineClient}. The environment read is
27
+ * a *surface* decision — `rine-ts-sdk` reads no environment at all, because
28
+ * `rine-mastra` builds a bare client for an operator-fixed actor and would
29
+ * inherit anything ambient the SDK picked up. Eve is a surface and opts in here,
30
+ * once, so the four call sites cannot drift apart. Reading it inside
31
+ * `getRineClient` would also make the memoized bare client env-dependent.
32
+ *
33
+ * `normalizeActor` trims and treats empty-after-trim as absent, which is what
34
+ * makes the bare `RINE_AGENT=` line `scaffold.ts` writes into every generated
35
+ * project mean "no actor set" rather than an actor named "".
36
+ */
37
+ function actingAgent(opts = {}) {
38
+ return normalizeActor(opts.agent) ?? actorFromEnv();
39
+ }
20
40
  const clientCache = /* @__PURE__ */ new Map();
21
41
  function cacheKey(configDir, apiUrl) {
22
42
  return `${configDir} ${apiUrl}`;
@@ -24,7 +44,7 @@ function cacheKey(configDir, apiUrl) {
24
44
  /**
25
45
  * Get (or lazily build + memoize) the shared `AsyncRineClient` for `opts`,
26
46
  * already scoped to `opts.agent` when present. Call ONLY from inside a tool
27
- * `execute` / a channel handler — never at module top level (R1).
47
+ * `execute` / a channel handler — never at module top level.
28
48
  */
29
49
  function getRineClient(opts = {}) {
30
50
  const configDir = opts.configDir ?? resolveConfigDir();
@@ -41,4 +61,4 @@ function getRineClient(opts = {}) {
41
61
  return opts.agent ? base.withAgent(opts.agent) : base;
42
62
  }
43
63
  //#endregion
44
- export { getRineClient as t };
64
+ export { getRineClient as n, actingAgent as t };
package/dist/client.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Lazy `AsyncRineClient` construction (invariant R1: side-effect-free import).
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
@@ -24,10 +24,28 @@ export interface RineClientOpts {
24
24
  /** Acting agent (handle/name/UUID) sent as `X-Rine-Agent` on every request. */
25
25
  agent?: string;
26
26
  }
27
+ /**
28
+ * Rungs 1–3 of the acting-agent ladder, for every rine surface Eve ships: the
29
+ * caller's own value, then `RINE_AGENT`. Returns `undefined` when neither
30
+ * answers, leaving the later rungs (the org's only agent, then a refusal) to
31
+ * whoever needs a resolved id.
32
+ *
33
+ * 🔴 Deliberately NOT folded into {@link getRineClient}. The environment read is
34
+ * a *surface* decision — `rine-ts-sdk` reads no environment at all, because
35
+ * `rine-mastra` builds a bare client for an operator-fixed actor and would
36
+ * inherit anything ambient the SDK picked up. Eve is a surface and opts in here,
37
+ * once, so the four call sites cannot drift apart. Reading it inside
38
+ * `getRineClient` would also make the memoized bare client env-dependent.
39
+ *
40
+ * `normalizeActor` trims and treats empty-after-trim as absent, which is what
41
+ * makes the bare `RINE_AGENT=` line `scaffold.ts` writes into every generated
42
+ * project mean "no actor set" rather than an actor named "".
43
+ */
44
+ export declare function actingAgent(opts?: Pick<RineClientOpts, "agent">): string | undefined;
27
45
  /**
28
46
  * Get (or lazily build + memoize) the shared `AsyncRineClient` for `opts`,
29
47
  * already scoped to `opts.agent` when present. Call ONLY from inside a tool
30
- * `execute` / a channel handler — never at module top level (R1).
48
+ * `execute` / a channel handler — never at module top level.
31
49
  */
32
50
  export declare function getRineClient(opts?: RineClientOpts): AsyncRineClient;
33
51
  /** 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)` — invariant R2: turn any thrown SDK error into a readable
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
  *
@@ -0,0 +1,140 @@
1
+ /**
2
+ * The plural group renderers: the caller's own groups, a directory search, and
3
+ * one group's roster. `format.ts`'s `renderGroup` renders exactly ONE group and
4
+ * is what `rine_group_inspect` returns; these render pages, one numbered row
5
+ * each, in the shape `renderInbox`/`renderDiscover` already use.
6
+ *
7
+ * A sibling of `format.ts` rather than part of it, for the same reason
8
+ * `format-groups.ts` is one — `format.ts` holds its ~200-LOC budget.
9
+ *
10
+ * The types come from `@rine-network/sdk` directly: the SDK exports them
11
+ * alongside their schemas, so there is nothing here to re-derive.
12
+ */
13
+ import { type AgentHandleMap, type GroupMember, type GroupRead, type GroupSummary } from "@rine-network/sdk";
14
+ /**
15
+ * What a group with no conversation renders instead of an id.
16
+ *
17
+ * A group's thread is created lazily on its first post, so a null
18
+ * `conversation_id` is the fact "nothing has been said in this group yet" — not
19
+ * an error, not a refusal, and not a missing field. The clause is never omitted
20
+ * on null: a model that sees it on some rows and not others reads the absence
21
+ * as a rendering fault rather than as a fact about the group.
22
+ *
23
+ * `format.ts`'s `renderGroup` imports it from here so the two renderers cannot
24
+ * tell a model two different things about the same group. The same sentence is
25
+ * printed by the CLI, crewai, langchain, hermes and mastra; it is restated per
26
+ * package because there is no runtime module all of them import.
27
+ */
28
+ export declare const NO_CONVERSATION = "(none \u2014 nothing has been said in this group yet)";
29
+ /**
30
+ * What a group renders in place of an agent list when this org holds no seat.
31
+ *
32
+ * The group list is ORG-scoped — it reports every group any agent of this org
33
+ * is in — so a row on it was never proof that the agent this host is acting as
34
+ * may post there. The empty list is that proof's absence stated plainly: no
35
+ * agent of this org is seated, and a send into the group would be refused.
36
+ *
37
+ * Never omitted, for the reason `NO_CONVERSATION` gives above: a clause a model
38
+ * sees on some rows and not others reads as a rendering fault rather than as a
39
+ * fact about the group. Restated per package because there is no runtime module
40
+ * all seven rendering surfaces import — though this package and its twin do
41
+ * share one (`@rine-network/sdk`), so the day either reworded its row would be
42
+ * the day the sentence belongs there instead.
43
+ *
44
+ * Pinned meanwhile: `tests/test_client-server-contract_group0.py` compares the
45
+ * four declared copies against `rine.group_words.NO_OWN_AGENTS`, which the
46
+ * three Python surfaces import, so a reword in one package fails there rather
47
+ * than shipping two answers to one question.
48
+ */
49
+ export declare const NO_OWN_AGENTS = "(none \u2014 no agent of your org is seated in this group)";
50
+ /**
51
+ * The seated-agents clause, from the one place both renderers read it.
52
+ *
53
+ * `format.ts`'s `renderGroup` calls this too, so the row and the single-group
54
+ * view cannot tell a model two different things about the same group — the same
55
+ * reason `NO_CONVERSATION` is imported there rather than retyped.
56
+ *
57
+ * Each id renders as its HANDLE. The three cases, and why a name is never one
58
+ * of them, are stated once on `AgentHandleMap` in the SDK
59
+ * (`rine-ts-sdk/src/resources/agent-handles.ts`); this renderer is one of the
60
+ * four that follow them. In short: a known id prints its handle, an unknown one
61
+ * prints itself, an empty list prints {@link NO_OWN_AGENTS}. A model handed a
62
+ * UUID cannot tell which of its agents it names — that is the whole reason
63
+ * handle resolution exists here — but a UUID is still addressable, so an
64
+ * unresolved id is degraded, not lost.
65
+ *
66
+ * `handles` is omitted whenever the org's agent list could not be read; that is
67
+ * case 2 for every id, and it is why nothing here throws.
68
+ *
69
+ * `undefined` is accepted on purpose. `GroupReadSchema` defaults the field to
70
+ * `[]`, so a parsed row always has one — but a row that never went through that
71
+ * parse (a hand-built object, a fake) has nothing, and a renderer whose job is
72
+ * to state an absence must not throw on one.
73
+ */
74
+ export declare function renderOwnAgents(ids: readonly string[] | undefined, handles?: AgentHandleMap): string;
75
+ /**
76
+ * How this package names a group's regime in one field, for a LIST ROW.
77
+ *
78
+ * One definition for the row, and for the row only. It is NOT what the create
79
+ * confirmation head asks: what a freshly created group runs is decided from the
80
+ * create RESULT, by `createdGroupMls` in `@rine-network/core`, because the row
81
+ * alone cannot tell a founded group from an unfounded one. `renderGroup`'s
82
+ * `[OK]` line re-derives its first clause from the predicates directly and has
83
+ * never called this either. So a reword here reaches this row and nothing else,
84
+ * and a reword in either of those two does not reach this row.
85
+ *
86
+ * The init window is reported as MLS because sends into it already are MLS, and
87
+ * a field that said "sender-key" there would state the opposite of what is
88
+ * happening. The window's own distinction is `rine_group_inspect`'s to make.
89
+ *
90
+ * A group created to run MLS whose ratchet tree was never founded really does
91
+ * carry sender-key traffic, so it names sender keys — and says the rest of the
92
+ * answer, because that row is otherwise field-for-field an open group's and
93
+ * nothing else on the page can tell the two apart. The parenthesis carries NO
94
+ * comma: the row below is comma-separated, which is the same reason its last
95
+ * clause opens with `; `. The whole sentence lives in `rine_group_inspect`; a
96
+ * list row is not the place for it.
97
+ */
98
+ export declare function groupModeLabel(g: GroupRead): string;
99
+ /**
100
+ * A numbered list of the groups this org's agents are seated in, or the
101
+ * empty-state line.
102
+ *
103
+ * The list is org-scoped, and each row's `your agents` clause names which of
104
+ * this org's agents are seated in that group — an empty clause means none of
105
+ * them is, and a send into that group would be refused. The clause names them
106
+ * by handle because that is the spelling `rine_whoami` prints, so the two
107
+ * outputs can be read against each other — a UUID matched nothing this surface
108
+ * had ever shown the model. What neither tool says is which agent this host is
109
+ * acting AS: `WhoAmI` carries no such field and `renderWhoAmI` marks no row, so
110
+ * in a multi-agent org that still has to come from the host's own config (the
111
+ * `agent` this client was built with, which may itself have been given as a
112
+ * UUID).
113
+ */
114
+ export declare function renderGroups(items: readonly GroupRead[], handles?: AgentHandleMap): string;
115
+ /**
116
+ * A numbered list of publicly listed groups, or the empty-state line.
117
+ *
118
+ * The header says what the list IS. `GET /directory/groups` is an
119
+ * unauthenticated public-visibility scan across every org and carries no
120
+ * identity at all, so nothing on a row can say whether this agent holds a seat
121
+ * in that group — while `renderGroups` above prints near-identical rows that
122
+ * end with exactly that. Two numbered lists told apart by two words in a header
123
+ * is not enough, so this one says the difference outright.
124
+ *
125
+ * It names no verb: the header states what the caller is looking at, and which
126
+ * tool to reach for next is the tool description's sentence, not this one's.
127
+ */
128
+ export declare function renderGroupSummaries(items: readonly GroupSummary[]): string;
129
+ /**
130
+ * A numbered roster, or the empty-state line.
131
+ *
132
+ * The roster is members only. An invitation and a nomination hold a seat
133
+ * against the group's ceiling without appearing here — `rine_group_requests`
134
+ * is what reports those — so a roster shorter than the ceiling suggests is not
135
+ * a gap in this list.
136
+ *
137
+ * Every member is listed, from every org. Nothing here filters on whose agent
138
+ * a row is; `(yours)` marks this org's rows and takes none away.
139
+ */
140
+ export declare function renderRoster(items: readonly GroupMember[]): string;