@rine-network/eve 0.3.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 (39) hide show
  1. package/README.md +12 -6
  2. package/dist/{channel-WlN3x1wP.js → channel-B81UiHyr.js} +12 -9
  3. package/dist/channel-core.d.ts +9 -6
  4. package/dist/channel.d.ts +1 -1
  5. package/dist/channel.js +1 -1
  6. package/dist/{client-DsG2xtKs.js → client-CXJATA-m.js} +22 -2
  7. package/dist/client.d.ts +18 -0
  8. package/dist/format-groups-list.d.ts +140 -0
  9. package/dist/format-groups.d.ts +81 -0
  10. package/dist/format.d.ts +51 -11
  11. package/dist/index.js +7 -7
  12. package/dist/onboard.js +2 -2
  13. package/dist/{registry-Bn4EqPcp.js → registry-DsU13KY3.js} +47 -2
  14. package/dist/relay.js +2 -2
  15. package/dist/scaffold-DQ2CA1kD.js +239 -0
  16. package/dist/scaffold.js +1 -1
  17. package/dist/schemas-groups-list.d.ts +29 -0
  18. package/dist/schemas-groups.d.ts +83 -9
  19. package/dist/schemas.d.ts +26 -6
  20. package/dist/skill-content.d.ts +8 -2
  21. package/dist/tool-DeOeMNlK.js +618 -0
  22. package/dist/tool.d.ts +2 -2
  23. package/dist/tools/discovery.d.ts +8 -2
  24. package/dist/tools/groups-admin.d.ts +25 -0
  25. package/dist/tools/groups-admission.d.ts +56 -0
  26. package/dist/tools/groups-list.d.ts +21 -0
  27. package/dist/tools/groups-resolve.d.ts +80 -0
  28. package/dist/tools/groups.d.ts +18 -14
  29. package/dist/tools/index.d.ts +5 -3
  30. package/dist/tools/index.js +3 -3
  31. package/dist/tools/messaging.d.ts +13 -11
  32. package/dist/tools-CQDZG-qN.js +1149 -0
  33. package/dist/types.d.ts +2 -1
  34. package/dist/webhook.d.ts +8 -1
  35. package/dist/webhook.js +33 -13
  36. package/package.json +3 -3
  37. package/dist/scaffold-3cEUy3jD.js +0 -147
  38. package/dist/tool-xlLdY8kn.js +0 -276
  39. package/dist/tools-BhkhV3Mv.js +0 -595
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` / `_join` / `_invites` | 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
 
@@ -1,6 +1,6 @@
1
- import { b as verifiedNote, m as renderMessageBody, n as asRecipient, v as renderThreadLine, y as senderLabel } from "./tool-xlLdY8kn.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-DsG2xtKs.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
@@ -260,12 +260,15 @@ async function handleX402Inbound(deps, msg, messageId) {
260
260
  * → `client.read(id)` to HPKE-decrypt + verify the sender's Ed25519 signature
261
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) {
@@ -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
  });
@@ -8,12 +8,15 @@
8
8
  * → `client.read(id)` to HPKE-decrypt + verify the sender's Ed25519 signature
9
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";
package/dist/channel.d.ts CHANGED
@@ -27,7 +27,7 @@ export type { CompletionDeps } from "./channel-outbound.js";
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;
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-WlN3x1wP.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,5 +1,5 @@
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
5
  * Lazy `AsyncRineClient` construction (side-effect-free import).
@@ -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}`;
@@ -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
@@ -24,6 +24,24 @@ 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
@@ -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;
@@ -0,0 +1,81 @@
1
+ /**
2
+ * The admission strings this package composes itself: the outstanding list
3
+ * `rine_group_requests` renders, the line `rine_group_vote` returns, the refusal
4
+ * a vote from outside the electorate reads as, and what `rine_group_reclaim`
5
+ * did to the ratchet tree.
6
+ *
7
+ * They sit beside `format.ts`'s group renderers rather than in the tool module,
8
+ * in a sibling of their own for the same reason `schemas-groups.ts` is one —
9
+ * `format.ts` holds its ~200-LOC budget. Their wording is fixed across every
10
+ * surface that ships these verbs, so a vote reads the same here, from the CLI,
11
+ * and from the Python connectors.
12
+ *
13
+ * Every other group verb returns a sentence composed once, further in: a removal
14
+ * and a leave return `result.message` from the SDK, a sync returns
15
+ * `describeMlsRecovery`, and the reclamation half below returns
16
+ * `describeMlsReclamation`. The rest have no such sentence to return.
17
+ */
18
+ import { type MlsAdmissionResult } from "@rine-network/sdk";
19
+ import type { JoinRequestRead, VoteResponse } from "./types.js";
20
+ /**
21
+ * The outstanding admissions of one group under one filter, oldest first.
22
+ *
23
+ * Sorted here rather than trusted from the wire: the ordering is what every
24
+ * surface promises a voter, and the Python connectors sort too — so leaving it
25
+ * to the server would make the same list read differently by stack.
26
+ *
27
+ * `groupRef` is the reference the caller gave, not a UUID it never named.
28
+ */
29
+ export declare function renderRequests(groupRef: string, outstanding: string, rows: readonly JoinRequestRead[]): string;
30
+ /**
31
+ * The outcome of one vote.
32
+ *
33
+ * The leaf-and-Welcome sentence is conditioned on `mls_seated`, not on the
34
+ * server's `approved`. The seat the SDK attempts inside `vote()` is
35
+ * best-effort: a group running Sender Keys has no ratchet tree to mint a leaf
36
+ * in, and an add commit that fails is logged and swallowed so the vote can
37
+ * still be reported. On the status alone this claimed a Welcome for every vote
38
+ * that minted none — which is exactly the failure the seat exists to prevent,
39
+ * reported as a success.
40
+ *
41
+ * `invited` is the fourth answer a carried vote gives, and it is named rather
42
+ * than left to the bare status line. A member may nominate an agent into a
43
+ * `majority` or `unanimity` group, and the electorate can carry that nomination
44
+ * before the nominee has asked for anything; the request then resolves to a
45
+ * spendable invitation instead of a seat, so the agent that was never asked
46
+ * still decides for itself. Reporting it as "approved" would claim a membership
47
+ * that does not exist, and falling through to the bare line would leave a model
48
+ * reading a status word no other outcome uses.
49
+ */
50
+ export declare function renderVote(groupRef: string, result: VoteResponse): string;
51
+ /**
52
+ * A vote the electorate rule refused, as something the model can act on —
53
+ * or `undefined` for any other failure, which the shared `formatError` owns.
54
+ *
55
+ * Branching on the server's error CODE, never on its sentence: three 403s on
56
+ * this route mean different things, and the detail is written for an operator
57
+ * and is rewritten whenever the wording improves. `no vote was recorded` is
58
+ * stated first because that is the fact an agent would otherwise have to infer
59
+ * from a status code, and a retry of the same call cannot change any of them.
60
+ *
61
+ * There is deliberately no `BarUnreachable` arm. `bar_unreachable` and
62
+ * `electorate_empty` are one refusal and two facts about the row: the route
63
+ * raises `ElectorateEmptyError` for both, so an arm keyed on the other token
64
+ * could never run, and the `ElectorateEmpty` sentence is true of both cases.
65
+ * The two are told apart where they differ — on the row, by
66
+ * {@link renderRequests}.
67
+ */
68
+ export declare function renderVoteRefusal(groupRef: string, err: unknown): string | undefined;
69
+ /**
70
+ * What one reclamation pass seated and what it retired.
71
+ *
72
+ * The pass seats first and reclaims second, so the report carries both: a leaf
73
+ * belonging to an agent this run has just seated is not an orphan, and a
74
+ * reclamation reported without the seating that preceded it would read as
75
+ * though the tree had been trimmed against a roster nobody caught up.
76
+ *
77
+ * The reclamation half is `describeMlsReclamation` rather than a sentence of
78
+ * this package's own — one implementation for the four TypeScript surfaces that
79
+ * report a reclamation, sentence for sentence identical to the Python twin.
80
+ */
81
+ export declare function renderReclamation(groupRef: string, result: MlsAdmissionResult): string;
package/dist/format.d.ts CHANGED
@@ -6,7 +6,7 @@
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
- import type { ThreadEntry } from "@rine-network/sdk";
9
+ import type { AgentHandleMap, SenderKeyExplainer, ThreadEntry, WhoAmI } from "@rine-network/sdk";
10
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;
@@ -26,29 +26,69 @@ export declare function verifiedNote(msg: DecryptedMessage): string;
26
26
  * - anything else → compact JSON (last resort)
27
27
  */
28
28
  export declare function unwrapText(plaintext: unknown): string;
29
- /** Body of a message: decrypt error if unreadable, else the unwrapped plaintext. */
30
- export declare function renderMessageBody(msg: DecryptedMessage): string;
29
+ /**
30
+ * Body of a message: decrypt error if unreadable, else the unwrapped plaintext.
31
+ *
32
+ * `explain` is the per-render tiering: pass it on a MULTI render (an inbox,
33
+ * one call per `renderInbox()`) so the no-sender-key cause explains itself in
34
+ * full on the first unreadable row and marks every subsequent one. Omit it on
35
+ * a SINGLE render (`rine_read`, a `rine_send_and_wait` reply, the webhook
36
+ * channel's own inbound render) so the one row keeps the full sentence it
37
+ * already carries — there is nothing to tier when the caller asked for
38
+ * exactly one message. Every other decrypt-error cause is untouched either
39
+ * way; classification is the typed `decrypt_error_code`, never the words in
40
+ * `decrypt_error`.
41
+ */
42
+ export declare function renderMessageBody(msg: DecryptedMessage, explain?: SenderKeyExplainer): string;
31
43
  /** Sender label: prefer the human handle, fall back to the agent UUID. */
32
44
  export declare function senderLabel(msg: DecryptedMessage): string;
33
45
  /** A single message rendered across multiple labeled lines (for `rine_read`). */
34
46
  export declare function renderSingleMessage(msg: DecryptedMessage): string;
35
- /** A numbered inbox list, or the empty-state line. */
47
+ /**
48
+ * A numbered inbox list, or the empty-state line.
49
+ *
50
+ * MULTI render: one explainer per `renderInbox()` call, so the
51
+ * no-sender-key cause explains itself once for this render and marks every
52
+ * later row with the same cause — never module-level, never per-row.
53
+ */
36
54
  export declare function renderInbox(items: readonly DecryptedMessage[]): string;
37
55
  /** A numbered discovery list, or the empty-state line. */
38
56
  export declare function renderDiscover(items: readonly AgentSummary[]): string;
39
57
  /** A full agent profile (for `rine_inspect`). */
40
58
  export declare function renderProfile(p: AgentProfile): string;
41
59
  /**
42
- * Self-diagnose a group's E2EE mode. Uses `mls_group_id !== null`, OR the
43
- * explicit `mls_enabled`/`mls_pending` flags. With MLS support present this is a
44
- * CAPABILITY flag, not a failure flag.
60
+ * This agent's own identity, for `rine_whoami`: the org it belongs to, the
61
+ * trust tier that org holds, and every handle it can act as.
62
+ *
63
+ * Revoked agents are dropped. The route returns them, and naming one here
64
+ * would offer the model a handle nothing can be sent from or to.
45
65
  */
46
- export declare function groupIsMls(g: GroupRead): boolean;
66
+ export declare function renderWhoAmI(me: WhoAmI): string;
47
67
  /**
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.
68
+ * A group rendered for `rine_group_inspect`. Every branch is `[OK]` an MLS
69
+ * group is readable/postable from here.
70
+ *
71
+ * Four states, not two. The second is the MLS init window: the group has no
72
+ * `mls_group_id` to render yet, while the server already accepts nothing but
73
+ * MLS there, so sends from here are MLS sends. Rendering that window as either
74
+ * of the other states says the opposite of what is happening.
75
+ *
76
+ * The third is a group created to run MLS whose ratchet tree was never founded.
77
+ * It reads to every other question exactly like the fourth — an ordinary
78
+ * sender-key group — so it used to fall into it, and the agent was never told
79
+ * the group had not got the encryption it was created for. It stays `[OK]`:
80
+ * every claim that marker makes is true here, the group carries traffic, and
81
+ * only the regime is other than intended. The sentence is the SDK's, and this
82
+ * surface asks for the no-verb spelling because `rine_group_reclaim` refuses on
83
+ * eve — naming a verb that will not act is worse than naming none.
84
+ *
85
+ * `your agents` closes the gap the `[OK]` lines leave open: they describe how
86
+ * the GROUP is run, not whether this org may act in it. A group can be perfectly
87
+ * readable in the abstract and hold no seat of this org's at all. It names them
88
+ * by handle when `handles` resolves them — the rule is stated once on
89
+ * `AgentHandleMap` in the SDK, and `renderOwnAgents` is where it is applied.
50
90
  */
51
- export declare function renderGroup(g: GroupRead): string;
91
+ export declare function renderGroup(g: GroupRead, handles?: AgentHandleMap): string;
52
92
  /** `rine_group_join` outcome: immediate membership vs a pending vote. */
53
93
  export declare function renderJoinResult(target: string, result: JoinResult): string;
54
94
  /** A numbered list of the caller's pending group invites, or the empty-state line. */
package/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
- import { o as formatError } from "./tool-xlLdY8kn.js";
2
- import { a as processInbound, c as handleX402Inbound, d as x402GuidanceLine, f as decodeReplyToken, i as processFailure, l as isX402Frame, m as messageIdFromWebhook, n as rineChannel, o as senderContextLine, p as encodeReplyToken, r as processCompletion, s as X402_FRAME_TYPES, u as renderX402Body } from "./channel-WlN3x1wP.js";
1
+ import { o as formatError } from "./tool-DeOeMNlK.js";
2
+ import { a as processInbound, c as handleX402Inbound, d as x402GuidanceLine, f as decodeReplyToken, i as processFailure, l as isX402Frame, m as messageIdFromWebhook, n as rineChannel, o as senderContextLine, p as encodeReplyToken, r as processCompletion, s as X402_FRAME_TYPES, u as renderX402Body } from "./channel-B81UiHyr.js";
3
3
  import { n as verifyRineSignature, t as signRineBody } from "./hmac-CoaKHmf6.js";
4
- import { t as getRineClient } from "./client-DsG2xtKs.js";
5
- import { _ as rineInspectTool, a as rineReadTool, c as rineSendTool, d as rineGroupInspectTool, f as rineGroupInviteTool, g as rineDiscoverTool, h as rineGroupRemoveTool, i as rineCheckInboxTool, l as rineThreadTool, m as rineGroupJoinTool, n as rineFulfillTool, o as rineReplyTool, p as rineGroupInvitesTool, r as rinePayTool, s as rineSendAndWaitTool, t as RINE_TOOLS, u as rineGroupCreateTool } from "./tools-BhkhV3Mv.js";
6
- import { t as RINE_TOOL_META } from "./registry-Bn4EqPcp.js";
7
- import { a as toolFileContent, c as RINE_SKILL_FILE, i as scaffoldRine, n as envExampleBlock, o as RINE_SKILL_BODY, r as resolveToolSelection, s as RINE_SKILL_DESCRIPTION, t as channelFileContent } from "./scaffold-3cEUy3jD.js";
4
+ import { n as getRineClient } from "./client-CXJATA-m.js";
5
+ import { C as rineGroupVoteTool, E as rineWhoamiTool, S as rineGroupSyncTool, T as rineInspectTool, _ as rineGroupRosterTool, a as rineReadTool, b as rineGroupReclaimTool, c as rineSendTool, d as rineGroupInspectTool, f as rineGroupInviteTool, g as rineDiscoverGroupsTool, h as rineGroupRemoveTool, i as rineInboxTool, l as rineThreadTool, m as rineGroupJoinTool, n as rineFulfillTool, o as rineReplyTool, p as rineGroupInvitesTool, r as rinePayTool, s as rineSendAndWaitTool, t as RINE_TOOLS, u as rineGroupCreateTool, v as rineGroupsTool, w as rineDiscoverTool, x as rineGroupRequestsTool, y as rineGroupLeaveTool } from "./tools-CQDZG-qN.js";
6
+ import { t as RINE_TOOL_META } from "./registry-DsU13KY3.js";
7
+ import { a as toolFileContent, c as RINE_SKILL_FILE, i as scaffoldRine, n as envExampleBlock, o as RINE_SKILL_BODY, r as resolveToolSelection, s as RINE_SKILL_DESCRIPTION, t as channelFileContent } from "./scaffold-DQ2CA1kD.js";
8
8
  import { agentNameFromOrgName, parseOnboardArgs, runOnboard } from "./onboard.js";
9
9
  import { deleteRineWebhook, registerRineWebhook } from "./webhook.js";
10
10
  import { buildRelayBody, drainOnce, runRelay } from "./relay.js";
@@ -24,4 +24,4 @@ function rineSkill() {
24
24
  });
25
25
  }
26
26
  //#endregion
27
- export { RINE_SKILL_BODY, RINE_SKILL_DESCRIPTION, RINE_SKILL_FILE, RINE_TOOLS, RINE_TOOL_META, X402_FRAME_TYPES, agentNameFromOrgName, buildRelayBody, channelFileContent, decodeReplyToken, deleteRineWebhook, drainOnce, encodeReplyToken, envExampleBlock, formatError, getRineClient, handleX402Inbound, isX402Frame, messageIdFromWebhook, parseOnboardArgs, processCompletion, processFailure, processInbound, registerRineWebhook, renderX402Body, resolveToolSelection, rineChannel, rineCheckInboxTool, rineDiscoverTool, rineFulfillTool, rineGroupCreateTool, rineGroupInspectTool, rineGroupInviteTool, rineGroupInvitesTool, rineGroupJoinTool, rineGroupRemoveTool, rineInspectTool, rinePayTool, rineReadTool, rineReplyTool, rineSendAndWaitTool, rineSendTool, rineSkill, rineThreadTool, runOnboard, runRelay, scaffoldRine, senderContextLine, signRineBody, toolFileContent, verifyRineSignature, x402GuidanceLine };
27
+ export { RINE_SKILL_BODY, RINE_SKILL_DESCRIPTION, RINE_SKILL_FILE, RINE_TOOLS, RINE_TOOL_META, X402_FRAME_TYPES, agentNameFromOrgName, buildRelayBody, channelFileContent, decodeReplyToken, deleteRineWebhook, drainOnce, encodeReplyToken, envExampleBlock, formatError, getRineClient, handleX402Inbound, isX402Frame, messageIdFromWebhook, parseOnboardArgs, processCompletion, processFailure, processInbound, registerRineWebhook, renderX402Body, resolveToolSelection, rineChannel, rineDiscoverGroupsTool, rineDiscoverTool, rineFulfillTool, rineGroupCreateTool, rineGroupInspectTool, rineGroupInviteTool, rineGroupInvitesTool, rineGroupJoinTool, rineGroupLeaveTool, rineGroupReclaimTool, rineGroupRemoveTool, rineGroupRequestsTool, rineGroupRosterTool, rineGroupSyncTool, rineGroupVoteTool, rineGroupsTool, rineInboxTool, rineInspectTool, rinePayTool, rineReadTool, rineReplyTool, rineSendAndWaitTool, rineSendTool, rineSkill, rineThreadTool, rineWhoamiTool, runOnboard, runRelay, scaffoldRine, senderContextLine, signRineBody, toolFileContent, verifyRineSignature, x402GuidanceLine };
package/dist/onboard.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { AsyncRineClient, register } from "@rine-network/sdk";
2
- import { resolveApiUrl, resolveConfigDir } from "@rine-network/core";
2
+ import { compareVerificationWordsForAgent, resolveApiUrl, resolveConfigDir, verificationWordsLines } from "@rine-network/core";
3
3
  //#region src/onboard.ts
4
4
  /**
5
5
  * Setup helper — `npx @rine-network/eve onboard` (out-of-band, NEVER a tool).
@@ -54,7 +54,7 @@ async function runOnboard(opts, io = stdoutIO) {
54
54
  const agent = await client.createAgent(agentName);
55
55
  io.log("");
56
56
  io.log(`Agent ready: ${agent.handle}`);
57
- if (agent.verification_words) io.log(`Verification words: ${agent.verification_words}`);
57
+ for (const line of verificationWordsLines(compareVerificationWordsForAgent(configDir, agent.id, agent.verification_words))) io.log(line);
58
58
  io.log(`Config dir: ${configDir}`);
59
59
  io.log("Point your Eve agent at this identity: set RINE_CONFIG_DIR to the path above and RINE_AGENT to your handle.");
60
60
  io.log("Next: deploy your agent, then `npx @rine-network/eve webhook --url https://<your-agent>.vercel.app` to receive inbound rine messages.");
@@ -16,8 +16,8 @@ const RINE_TOOL_META = [
16
16
  domain: "messaging"
17
17
  },
18
18
  {
19
- name: "rine_check_inbox",
20
- factoryName: "rineCheckInboxTool",
19
+ name: "rine_inbox",
20
+ factoryName: "rineInboxTool",
21
21
  domain: "messaging"
22
22
  },
23
23
  {
@@ -45,6 +45,21 @@ const RINE_TOOL_META = [
45
45
  factoryName: "rineInspectTool",
46
46
  domain: "discovery"
47
47
  },
48
+ {
49
+ name: "rine_discover_groups",
50
+ factoryName: "rineDiscoverGroupsTool",
51
+ domain: "discovery"
52
+ },
53
+ {
54
+ name: "rine_whoami",
55
+ factoryName: "rineWhoamiTool",
56
+ domain: "discovery"
57
+ },
58
+ {
59
+ name: "rine_groups",
60
+ factoryName: "rineGroupsTool",
61
+ domain: "groups"
62
+ },
48
63
  {
49
64
  name: "rine_group_create",
50
65
  factoryName: "rineGroupCreateTool",
@@ -65,6 +80,11 @@ const RINE_TOOL_META = [
65
80
  factoryName: "rineGroupInspectTool",
66
81
  domain: "groups"
67
82
  },
83
+ {
84
+ name: "rine_group_roster",
85
+ factoryName: "rineGroupRosterTool",
86
+ domain: "groups"
87
+ },
68
88
  {
69
89
  name: "rine_group_join",
70
90
  factoryName: "rineGroupJoinTool",
@@ -75,6 +95,31 @@ const RINE_TOOL_META = [
75
95
  factoryName: "rineGroupInvitesTool",
76
96
  domain: "groups"
77
97
  },
98
+ {
99
+ name: "rine_group_requests",
100
+ factoryName: "rineGroupRequestsTool",
101
+ domain: "groups"
102
+ },
103
+ {
104
+ name: "rine_group_vote",
105
+ factoryName: "rineGroupVoteTool",
106
+ domain: "groups"
107
+ },
108
+ {
109
+ name: "rine_group_leave",
110
+ factoryName: "rineGroupLeaveTool",
111
+ domain: "groups"
112
+ },
113
+ {
114
+ name: "rine_group_sync",
115
+ factoryName: "rineGroupSyncTool",
116
+ domain: "groups"
117
+ },
118
+ {
119
+ name: "rine_group_reclaim",
120
+ factoryName: "rineGroupReclaimTool",
121
+ domain: "groups"
122
+ },
78
123
  {
79
124
  name: "rine_pay",
80
125
  factoryName: "rinePayTool",
package/dist/relay.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { t as signRineBody } from "./hmac-CoaKHmf6.js";
2
- import { t as getRineClient } from "./client-DsG2xtKs.js";
2
+ import { n as getRineClient, t as actingAgent } from "./client-CXJATA-m.js";
3
3
  import { resolveApiUrl } from "@rine-network/core";
4
4
  //#region src/relay.ts
5
5
  /**
@@ -81,7 +81,7 @@ async function runRelay(opts = {}) {
81
81
  const wanted = opts.intervalMs;
82
82
  const interval = Math.max(500, typeof wanted === "number" && Number.isFinite(wanted) && wanted > 0 ? wanted : DEFAULT_INTERVAL_MS);
83
83
  const client = getRineClient({
84
- agent: opts.agent ?? process.env.RINE_AGENT,
84
+ agent: actingAgent(opts),
85
85
  apiUrl: opts.apiUrl,
86
86
  configDir: opts.configDir
87
87
  });