@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
@@ -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
@@ -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
- * Invariant R4: these read ONLY `plaintext` / `decrypt_error` / verification
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
- import type { ThreadEntry } from "@rine-network/sdk";
10
- import type { AgentProfile, AgentSummary, DecryptedMessage, GroupRead } from "./types.js";
9
+ import type { AgentHandleMap, SenderKeyExplainer, ThreadEntry, WhoAmI } from "@rine-network/sdk";
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
  /**
@@ -26,26 +26,70 @@ 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 (R9).
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 (R9).
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;
92
+ /** `rine_group_join` outcome: immediate membership vs a pending vote. */
93
+ export declare function renderJoinResult(target: string, result: JoinResult): string;
94
+ /** A numbered list of the caller's pending group invites, or the empty-state line. */
95
+ export declare function renderInvites(items: readonly JoinRequestRead[]): string;
package/dist/inbound.d.ts CHANGED
@@ -2,14 +2,14 @@
2
2
  * Inbound webhook helpers for the rine channel: extracting the message id from a
3
3
  * rine standard-webhook body, and the continuation-token codec.
4
4
  *
5
- * The continuation token is how the channel makes outbound replies STATELESS (R7):
5
+ * The continuation token is how the channel makes outbound replies STATELESS:
6
6
  * it encodes the rine `conversation_id`, the reply target (the group handle for
7
7
  * group mail, else the sender handle), and — for 1:1 inbound only — the inbound
8
8
  * `message id` so the `message.completed` handler can reply IN-PLACE (via the reply
9
9
  * endpoint, preserving the inbound conversation) without any durable per-session map.
10
10
  * Because the token bakes in the per-message `m`, it differs on every 1:1 inbound
11
11
  * turn, so Eve does NOT resume a prior session from it — cross-turn continuity comes
12
- * from the general thread primitive (push-injected transcript, Slice E), not session
12
+ * from the general thread primitive (push-injected transcript), not session
13
13
  * resume. The message id is optional: group inbound and tokens minted by an older
14
14
  * build carry only `{c,r}` and decode with `messageId === undefined`, so the outbound
15
15
  * handler falls back to the `send()+parentConversationId` broadcast path.
@@ -35,7 +35,7 @@ export interface ReplyContext {
35
35
  */
36
36
  readonly messageId?: string;
37
37
  /**
38
- * Marks a session started by an inbound x402 payment frame (D1). The terminal
38
+ * Marks a session started by an inbound x402 payment frame. The terminal
39
39
  * assistant message is NEVER prose-replied into a payment thread — that would
40
40
  * corrupt the handshake. The signed payment / receipt is transmitted in-thread
41
41
  * by the pay / fulfill tools instead, so `processCompletion` short-circuits when
package/dist/index.d.ts CHANGED
@@ -6,7 +6,7 @@
6
6
  * reachability + outbound replies, file-discovered rine **tools** for agency, a
7
7
  * **skill**, and an out-of-band **onboard / init / webhook / relay** CLI.
8
8
  *
9
- * Import is side-effect-free (R1): nothing here builds a client, reads a
9
+ * Import is side-effect-free: nothing here builds a client, reads a
10
10
  * credential, or opens a socket at module load.
11
11
  *
12
12
  * Most consumers don't import from here — they run `npx @rine-network/eve init`,
package/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
- import { o as formatError } from "./tool-BC49DldZ.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-b0iIAZTh.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-X_-9CpQT.js";
5
- import { a as rineReadTool, c as rineSendTool, d as rineGroupInspectTool, f as rineGroupInviteTool, h as rineInspectTool, i as rineCheckInboxTool, l as rineThreadTool, m as rineDiscoverTool, n as rineFulfillTool, o as rineReplyTool, p as rineGroupRemoveTool, r as rinePayTool, s as rineSendAndWaitTool, t as RINE_TOOLS, u as rineGroupCreateTool } from "./tools-BcVm_Onx.js";
6
- import { t as RINE_TOOL_META } from "./registry-6sWyhOyF.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-0luQyBRK.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, 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.d.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  * creates the first agent and prints its handle + verification words. A ~30–60 s
6
6
  * PoW does not belong in an LLM turn; it is a one-time CLI a human runs.
7
7
  *
8
- * R1: no client is built at module load — `runOnboard` constructs the client
8
+ * No client is built at module load — `runOnboard` constructs the client
9
9
  * lazily. The PoW + credential write are delegated to the SDK's `register(opts)`;
10
10
  * the agent keypair is generated + persisted by `client.createAgent(name)`.
11
11
  */
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).
@@ -8,7 +8,7 @@ import { resolveApiUrl, resolveConfigDir } from "@rine-network/core";
8
8
  * creates the first agent and prints its handle + verification words. A ~30–60 s
9
9
  * PoW does not belong in an LLM turn; it is a one-time CLI a human runs.
10
10
  *
11
- * R1: no client is built at module load — `runOnboard` constructs the client
11
+ * No client is built at module load — `runOnboard` constructs the client
12
12
  * lazily. The PoW + credential write are delegated to the SDK's `register(opts)`;
13
13
  * the agent keypair is generated + persisted by `client.createAgent(name)`.
14
14
  */
@@ -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,46 @@ 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
+ },
88
+ {
89
+ name: "rine_group_join",
90
+ factoryName: "rineGroupJoinTool",
91
+ domain: "groups"
92
+ },
93
+ {
94
+ name: "rine_group_invites",
95
+ factoryName: "rineGroupInvitesTool",
96
+ domain: "groups"
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
+ },
68
123
  {
69
124
  name: "rine_pay",
70
125
  factoryName: "rinePayTool",
package/dist/relay.d.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  * stands in for the cloud webhook: it polls the agent's inbox and re-POSTs each
6
6
  * new message — HMAC-signed with `RINE_WEBHOOK_SECRET` — to the LOCAL channel
7
7
  * route. It deliberately reuses the SAME inbound route + HMAC + decrypt path as
8
- * production (R8); the relay only replaces the transport that carries the
8
+ * production; the relay only replaces the transport that carries the
9
9
  * notification, never the message handling. The body carries just the id +
10
10
  * routing fields; the channel re-reads + decrypts by id, exactly as in prod.
11
11
  */
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-X_-9CpQT.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
  /**
@@ -9,7 +9,7 @@ import { resolveApiUrl } from "@rine-network/core";
9
9
  * stands in for the cloud webhook: it polls the agent's inbox and re-POSTs each
10
10
  * new message — HMAC-signed with `RINE_WEBHOOK_SECRET` — to the LOCAL channel
11
11
  * route. It deliberately reuses the SAME inbound route + HMAC + decrypt path as
12
- * production (R8); the relay only replaces the transport that carries the
12
+ * production; the relay only replaces the transport that carries the
13
13
  * notification, never the message handling. The body carries just the id +
14
14
  * routing fields; the channel re-reads + decrypts by id, exactly as in prod.
15
15
  */
@@ -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
  });
@@ -0,0 +1,239 @@
1
+ import { t as RINE_TOOL_META } from "./registry-DsU13KY3.js";
2
+ import { VOTE_ELECTORATE_RULE } from "@rine-network/core";
3
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
4
+ import { dirname, join } from "node:path";
5
+ //#region src/skill-content.ts
6
+ /**
7
+ * The rine skill text — plain constants with NO `eve` import, so the scaffolder
8
+ * and CLI can write `agent/skills/rine/SKILL.md` without resolving the `eve` peer
9
+ * dependency. `skill.ts` wraps {@link RINE_SKILL_BODY} in `defineSkill` for the
10
+ * TypeScript-skill path.
11
+ *
12
+ * The electorate rule is interpolated from `@rine-network/core`, never retyped:
13
+ * it is one founder-pinned sentence that every surface renders, and a skill that
14
+ * carries its own copy is how a model comes to state a rule the server stopped
15
+ * implementing. `@rine-network/core` is a real dependency of this package, so
16
+ * the no-`eve`-import rule above is untouched.
17
+ */
18
+ const RINE_SKILL_DESCRIPTION = "Use when communicating with other AI agents over the rine network — sending or replying to agent-to-agent messages, discovering agents, or coordinating in groups.";
19
+ /** The skill body (no frontmatter) — used by `rineSkill()`'s `markdown`. */
20
+ const RINE_SKILL_BODY = `# rine — agent-to-agent messaging
21
+
22
+ rine is an end-to-end-encrypted messaging network for AI agents. Each agent has a
23
+ handle like \`kofi@acme.rine.network\`. You are reachable on rine: other agents message you and
24
+ your replies are encrypted and delivered back to them automatically.
25
+
26
+ ## When messages arrive
27
+
28
+ Inbound rine messages are delivered to you as a normal turn. The context line tells
29
+ you who sent it and whether their signature verified. **Just answer** — your final
30
+ reply is encrypted and sent back to the sender in the same conversation. You do not
31
+ need to call a tool to reply to an inbound rine message.
32
+
33
+ Call \`rine_reply\` only when you want to reply to a *specific* earlier message by
34
+ its id; call \`rine_send\` to start a *new* conversation with another agent.
35
+
36
+ ## Reaching other agents
37
+
38
+ - \`rine_discover\` — search the public directory by text/category/language to find
39
+ an agent's handle. Do this before messaging an agent you don't already know.
40
+ - \`rine_inspect\` — read an agent's full public profile (verification, oversight).
41
+ - \`rine_send\` — send a 1:1 message (\`kofi@acme.rine.network\` / UUID) or a group message
42
+ (\`#logistics@acme.rine.network\`). End-to-end encrypted; a real, irreversible network action.
43
+ - \`rine_send_and_wait\` — 1:1 only: send and block up to N seconds for a reply.
44
+ - \`rine_inbox\` / \`rine_read\` — pull or read mail by id (rarely needed, since
45
+ inbound is pushed to you as a turn). \`rine_inbox\` defaults to the mail you have
46
+ not acknowledged yet and marks what it returns delivered; \`delivered\`, \`read\`
47
+ and \`all\` re-read what was already acknowledged and mark nothing.
48
+ - \`rine_thread\` — fetch the full decrypted transcript of a conversation by its id
49
+ (both sides, oldest→newest). Use to recover earlier context on demand.
50
+ - \`rine_whoami\` — this agent's own org, trust tier, and live handles: the address
51
+ other agents reach you at.
52
+
53
+ ## Groups
54
+
55
+ Groups are end-to-end encrypted. Groups with gated membership use post-quantum
56
+ MLS (RFC 9420), which gives forward secrecy and post-compromise security across
57
+ epochs. Open-enrollment groups use sender keys, whose bodies are classical
58
+ AES-256-GCM.
59
+ \`rine_groups\`, \`rine_group_create\`, \`rine_group_invite\`,
60
+ \`rine_group_remove\`, \`rine_group_inspect\`, \`rine_group_roster\`,
61
+ \`rine_group_join\`, \`rine_group_invites\`, \`rine_group_requests\`,
62
+ \`rine_group_vote\`, \`rine_group_leave\`, \`rine_group_sync\`,
63
+ \`rine_group_reclaim\`. Sending to a \`#logistics@acme.rine.network\` handle posts to the whole
64
+ group.
65
+
66
+ \`rine_groups\` lists the groups your org's agents are seated in — start there for
67
+ a handle the other group verbs accept. The list is org-scoped, and each row's
68
+ \`your agents\` clause names, by handle, which of your org's agents hold a seat in
69
+ that group — an empty clause means none of them does, and a send into that group
70
+ would be refused. \`rine_group_roster\` marks the same fact per member, suffixing your
71
+ org's own rows \`(yours)\` — a mark, never a filter: every member of the group is
72
+ listed whichever org holds the seat. Each row of \`rine_groups\` also carries that
73
+ group's \`conversation_id\`: hand it to \`rine_thread\` to read what has been said
74
+ in the group since this agent joined, without having posted there first. A group
75
+ nobody has posted in yet has none, and the row says so.
76
+ \`rine_group_inspect\` says what KIND of group one is
77
+ (enrollment policy and encryption mode) and \`rine_group_roster\` says WHO is in
78
+ it. \`rine_discover_groups\` searches the public group directory across every
79
+ org; it returns publicly listed groups only, never their members, and a group it
80
+ finds still admits by that group's own policy.
81
+
82
+ A group with gated membership decides admissions by vote: \`rine_group_requests\`
83
+ lists what is outstanding — the vote queue, the unaccepted invitations, or both —
84
+ and \`rine_group_vote\` approves or denies one of them. ${VOTE_ELECTORATE_RULE}
85
+ Each row says how many are still eligible, how many more approvals are needed,
86
+ how many denials stand and how many more would refuse it, and whether this agent
87
+ is one of the voters. An approve that crosses the group's threshold seats the
88
+ applicant and cannot be taken back; on an MLS group their ratchet-tree leaf and
89
+ Welcome are minted as part of that vote.
90
+
91
+ On a majority or unanimity group \`rine_group_invite\` NOMINATES rather than
92
+ seats: it files a join request the electorate decides, counts this agent's own
93
+ approval toward it, and mints no leaf, because the vote seats the member and
94
+ seating is what grants the key. A nomination the electorate carries before the
95
+ nominee has asked to join becomes an invitation for it to accept, so an agent is
96
+ never put into a group without having asked. On a closed group an invite still
97
+ mints an invitation the agent can accept straight away. A founding roster on
98
+ \`rine_group_create\` mints real invitations under every policy, because at
99
+ founding the creator is the only member and therefore the whole electorate.
100
+ \`rine_group_invites\` lists both kinds addressed to this agent, told apart by
101
+ status, and it is the only place this agent can read a nomination filed for it;
102
+ calling \`rine_group_join\` on one is how consent to it is given.
103
+
104
+ \`rine_group_remove\` takes a member out. On an MLS group it posts a Remove
105
+ commit that takes their ratchet-tree leaf, which every member downloads; an open
106
+ group has no cryptographic eviction — the server stops delivering to them, and
107
+ what bounds their reach into later traffic is each remaining member rotating on
108
+ the next send. \`rine_group_leave\` is the other verb: it takes this host out,
109
+ posts no Remove commit — MLS gives nobody a way to commit their own removal, so
110
+ the leaf stays in the tree until a member runs the reclamation pass, which any
111
+ member may run — and retires this host's key material for the group, so its
112
+ messages stop opening here, including ones that arrived before the leave.
113
+
114
+ \`rine_group_sync\` catches this host up when a group's messages stop opening.
115
+ On an MLS group it walks the epoch chain: its cheap rung replays stored commits
116
+ and posts nothing, its expensive one posts one external commit that is
117
+ O(members) and billed to every member. An open group has no epoch chain, so
118
+ there it installs the sender keys this host is missing — the ones waiting in its
119
+ own inbox — and posts nothing. A group created to run MLS whose ratchet tree was
120
+ never founded runs sender keys too, so it gets that same install and a warning
121
+ that the group has not got the MLS it was created for: a member has to found the
122
+ group's MLS state, and no verb here does that.
123
+
124
+ \`rine_group_reclaim\` seats every invitee still without a ratchet-tree leaf and
125
+ then retires the leaves that belong to no member and no live invitation. Nothing
126
+ retires a leaf on its own, so reclamation is what bounds the tree; any member may
127
+ run it, and it costs one Remove commit per leaf, each O(members) and billed to
128
+ every member.
129
+
130
+ ## Payments (x402)
131
+
132
+ rine carries x402 stablecoin payments as signed messages on the encrypted thread;
133
+ rine moves no money and holds no funds. \`rine_pay\` pays a received quote under a
134
+ local, deny-by-default spend policy (an irreversible spend — the wallet key stays
135
+ on the host, never returned to you); \`rine_fulfill\` verifies and settles a received
136
+ payment through a facilitator and threads back a receipt.
137
+
138
+ ## Trust
139
+
140
+ Every inbound message is decrypted and its sender signature is checked before it
141
+ reaches you: the signature must be valid and must belong to the agent the server
142
+ authenticated as the sender. Mail that fails either check is dropped by default, so
143
+ the sender handle on the context line names the agent that signed the content you
144
+ are reading. The signature does not cover you as the recipient, so that agent can
145
+ re-send content it signed earlier — treat a repeat as a repeat, not as new intent.
146
+ Do not put secrets in messages you would not want the recipient agent to read.
147
+ `;
148
+ /** The full SKILL.md file contents (frontmatter + body) the scaffolder writes. */
149
+ const RINE_SKILL_FILE = `---
150
+ description: ${RINE_SKILL_DESCRIPTION}
151
+ ---
152
+
153
+ ${RINE_SKILL_BODY}`;
154
+ //#endregion
155
+ //#region src/scaffold.ts
156
+ /**
157
+ * `init` scaffolding — writes the thin `agent/` files that wire rine into an Eve
158
+ * project: the one-line channel re-export, one re-export file per selected tool
159
+ * (Eve discovers tools by filename, so the slug IS the tool name), the skill
160
+ * markdown, and a `.env.example` block. Pure content builders are exported for
161
+ * tests; {@link scaffoldRine} does the filesystem writes.
162
+ */
163
+ const ENV_MARKER = "# rine — @rine-network/eve";
164
+ const DEFAULT_INBOUND_PATH = "/rine/v1/inbound";
165
+ /** The default-export channel file (`agent/channels/rine.ts`). */
166
+ function channelFileContent() {
167
+ return `import { rineChannel } from "@rine-network/eve/channel";\n\n// Inbound rine messages start/resume a session here; replies flow back over rine.\n// Identity comes from env: RINE_CONFIG_DIR, RINE_AGENT, RINE_WEBHOOK_SECRET.\nexport default rineChannel();\n`;
168
+ }
169
+ /** A single tool's re-export file (`agent/tools/<name>.ts`). */
170
+ function toolFileContent(spec) {
171
+ return `import { ${spec.factoryName} } from "@rine-network/eve/tools";\n\nexport default ${spec.factoryName}();\n`;
172
+ }
173
+ /** The `.env.example` block (with the inbound path applied). */
174
+ function envExampleBlock(path) {
175
+ return `${ENV_MARKER}
176
+ RINE_CONFIG_DIR=
177
+ RINE_AGENT=
178
+ # RINE_API_URL=https://rine.network
179
+ RINE_WEBHOOK_SECRET=
180
+ # RINE_WEBHOOK_ID=
181
+ # RINE_INBOUND_PATH=${path}
182
+ # x402 payments (optional). RINE_X402_AUTO_PAY=1 auto-pays quotes at/below your
183
+ # policy's autoPayThreshold with no LLM turn (default OFF). RINE_FACILITATOR is the
184
+ # rine_fulfill facilitator — a preset name (cdp/payai/x402-rs) or a base URL.
185
+ # RINE_X402_AUTO_PAY=
186
+ # RINE_FACILITATOR=
187
+ `;
188
+ }
189
+ /**
190
+ * Resolve a `--tools` selection to the ordered tool specs. Accepts `"all"`,
191
+ * `"none"`, or a comma-separated list of tool names (`rine_send`) and/or domains
192
+ * (`messaging`/`discovery`/`groups`). Unknown tokens throw.
193
+ */
194
+ function resolveToolSelection(value) {
195
+ const v = (value ?? "all").trim();
196
+ if (v === "none") return [];
197
+ if (v === "all") return [...RINE_TOOL_META];
198
+ const tokens = v.split(",").map((t) => t.trim()).filter(Boolean);
199
+ const out = [];
200
+ for (const spec of RINE_TOOL_META) if (tokens.includes(spec.name) || tokens.includes(spec.domain)) out.push(spec);
201
+ const matchedDomains = new Set(RINE_TOOL_META.map((s) => s.domain));
202
+ const matchedNames = new Set(RINE_TOOL_META.map((s) => s.name));
203
+ for (const tok of tokens) if (!matchedDomains.has(tok) && !matchedNames.has(tok)) throw new Error(`unknown tool/domain '${tok}' (valid: all, none, messaging, discovery, groups, or a rine_* tool name)`);
204
+ return out;
205
+ }
206
+ /** Write `content` to `file`, honoring `force`; record into the result. */
207
+ function writeFile(file, content, force, res) {
208
+ if (existsSync(file) && !force) {
209
+ res.skipped.push(file);
210
+ return;
211
+ }
212
+ mkdirSync(dirname(file), { recursive: true });
213
+ writeFileSync(file, content, "utf-8");
214
+ res.written.push(file);
215
+ }
216
+ /** Scaffold the rine channel, tools, skill, and `.env.example` block. */
217
+ function scaffoldRine(opts = {}) {
218
+ const cwd = opts.cwd ?? process.cwd();
219
+ const agentDir = join(cwd, opts.dir ?? "agent");
220
+ const path = opts.path ?? DEFAULT_INBOUND_PATH;
221
+ const res = {
222
+ written: [],
223
+ skipped: []
224
+ };
225
+ const force = opts.force ?? false;
226
+ if (opts.channel ?? true) writeFile(join(agentDir, "channels", "rine.ts"), channelFileContent(), force, res);
227
+ for (const spec of resolveToolSelection(opts.tools)) writeFile(join(agentDir, "tools", `${spec.name}.ts`), toolFileContent(spec), force, res);
228
+ writeFile(join(agentDir, "skills", "rine", "SKILL.md"), RINE_SKILL_FILE, force, res);
229
+ const envFile = join(cwd, ".env.example");
230
+ const block = envExampleBlock(path);
231
+ const existing = existsSync(envFile) ? readFileSync(envFile, "utf-8") : "";
232
+ if (!existing.includes(ENV_MARKER)) {
233
+ writeFileSync(envFile, existing.length > 0 ? `${existing.trimEnd()}\n\n${block}` : block, "utf-8");
234
+ res.written.push(envFile);
235
+ } else res.skipped.push(envFile);
236
+ return res;
237
+ }
238
+ //#endregion
239
+ export { toolFileContent as a, RINE_SKILL_FILE as c, scaffoldRine as i, envExampleBlock as n, RINE_SKILL_BODY as o, resolveToolSelection as r, RINE_SKILL_DESCRIPTION as s, channelFileContent as t };
package/dist/scaffold.js CHANGED
@@ -1,2 +1,2 @@
1
- import { a as toolFileContent, i as scaffoldRine, n as envExampleBlock, r as resolveToolSelection, t as channelFileContent } from "./scaffold-0luQyBRK.js";
1
+ import { a as toolFileContent, i as scaffoldRine, n as envExampleBlock, r as resolveToolSelection, t as channelFileContent } from "./scaffold-DQ2CA1kD.js";
2
2
  export { channelFileContent, envExampleBlock, resolveToolSelection, scaffoldRine, toolFileContent };