@rine-network/eve 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,40 @@
1
+ /**
2
+ * `init` scaffolding — writes the thin `agent/` files that wire rine into an Eve
3
+ * project: the one-line channel re-export, one re-export file per selected tool
4
+ * (Eve discovers tools by filename, so the slug IS the tool name), the skill
5
+ * markdown, and a `.env.example` block. Pure content builders are exported for
6
+ * tests; {@link scaffoldRine} does the filesystem writes.
7
+ */
8
+ import { type RineToolMeta } from "./tools/registry.js";
9
+ /** The default-export channel file (`agent/channels/rine.ts`). */
10
+ export declare function channelFileContent(): string;
11
+ /** A single tool's re-export file (`agent/tools/<name>.ts`). */
12
+ export declare function toolFileContent(spec: RineToolMeta): string;
13
+ /** The `.env.example` block (with the inbound path applied). */
14
+ export declare function envExampleBlock(path: string): string;
15
+ /**
16
+ * Resolve a `--tools` selection to the ordered tool specs. Accepts `"all"`,
17
+ * `"none"`, or a comma-separated list of tool names (`rine_send`) and/or domains
18
+ * (`messaging`/`discovery`/`groups`). Unknown tokens throw.
19
+ */
20
+ export declare function resolveToolSelection(value: string | undefined): RineToolMeta[];
21
+ export interface ScaffoldOptions {
22
+ /** Project root (default `process.cwd()`). */
23
+ cwd?: string;
24
+ /** Agent directory relative to cwd (default `agent`). */
25
+ dir?: string;
26
+ /** `--tools` selection (default `all`). */
27
+ tools?: string;
28
+ /** Write the channel file (default true). */
29
+ channel?: boolean;
30
+ /** Inbound path baked into the `.env.example` (default `/rine/v1/inbound`). */
31
+ path?: string;
32
+ /** Overwrite existing files (default false → skip + report). */
33
+ force?: boolean;
34
+ }
35
+ export interface ScaffoldResult {
36
+ readonly written: string[];
37
+ readonly skipped: string[];
38
+ }
39
+ /** Scaffold the rine channel, tools, skill, and `.env.example` block. */
40
+ export declare function scaffoldRine(opts?: ScaffoldOptions): ScaffoldResult;
@@ -0,0 +1,2 @@
1
+ import { a as toolFileContent, i as scaffoldRine, n as envExampleBlock, r as resolveToolSelection, t as channelFileContent } from "./scaffold-Dpac1TMU.js";
2
+ export { channelFileContent, envExampleBlock, resolveToolSelection, scaffoldRine, toolFileContent };
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Zod input schemas for the 4 group tools (split out of `schemas.ts` to hold the
3
+ * ~200-LOC budget — one file per domain). Same authoring rules: rich `.describe()`
4
+ * on every field, NO identity/credentials in the schema (env-injected, R3).
5
+ *
6
+ * Groups are MLS-capable by default — `enableMls` (default true) on create.
7
+ */
8
+ import { z } from "zod";
9
+ export declare const groupCreateInput: z.ZodObject<{
10
+ name: z.ZodString;
11
+ enrollment: z.ZodDefault<z.ZodEnum<["open", "closed", "majority", "unanimity"]>>;
12
+ visibility: z.ZodDefault<z.ZodEnum<["public", "private"]>>;
13
+ description: z.ZodOptional<z.ZodString>;
14
+ enableMls: z.ZodDefault<z.ZodBoolean>;
15
+ }, "strip", z.ZodTypeAny, {
16
+ name: string;
17
+ enrollment: "open" | "closed" | "majority" | "unanimity";
18
+ visibility: "public" | "private";
19
+ enableMls: boolean;
20
+ description?: string | undefined;
21
+ }, {
22
+ name: string;
23
+ enrollment?: "open" | "closed" | "majority" | "unanimity" | undefined;
24
+ visibility?: "public" | "private" | undefined;
25
+ description?: string | undefined;
26
+ enableMls?: boolean | undefined;
27
+ }>;
28
+ export declare const groupInviteInput: z.ZodObject<{
29
+ group: z.ZodString;
30
+ agentToInvite: z.ZodString;
31
+ message: z.ZodOptional<z.ZodString>;
32
+ }, "strip", z.ZodTypeAny, {
33
+ group: string;
34
+ agentToInvite: string;
35
+ message?: string | undefined;
36
+ }, {
37
+ group: string;
38
+ agentToInvite: string;
39
+ message?: string | undefined;
40
+ }>;
41
+ export declare const groupRemoveInput: z.ZodObject<{
42
+ group: z.ZodString;
43
+ agentId: z.ZodString;
44
+ }, "strip", z.ZodTypeAny, {
45
+ group: string;
46
+ agentId: string;
47
+ }, {
48
+ group: string;
49
+ agentId: string;
50
+ }>;
51
+ export declare const groupInspectInput: z.ZodObject<{
52
+ group: z.ZodString;
53
+ }, "strip", z.ZodTypeAny, {
54
+ group: string;
55
+ }, {
56
+ group: string;
57
+ }>;
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Shared Zod input schemas for the 12 rine tools.
3
+ *
4
+ * Rich `.describe()` on EVERY field: the field descriptions are the #1 lever on
5
+ * tool-call accuracy (the AI-DX). Identity/credentials NEVER appear here — the
6
+ * acting agent + config dir come from `process.env` (R3), never the model-visible
7
+ * input schema.
8
+ */
9
+ import { z } from "zod";
10
+ export declare const sendInput: z.ZodObject<{
11
+ to: z.ZodString;
12
+ body: z.ZodString;
13
+ messageType: z.ZodDefault<z.ZodString>;
14
+ idempotencyKey: z.ZodOptional<z.ZodString>;
15
+ }, "strip", z.ZodTypeAny, {
16
+ to: string;
17
+ body: string;
18
+ messageType: string;
19
+ idempotencyKey?: string | undefined;
20
+ }, {
21
+ to: string;
22
+ body: string;
23
+ messageType?: string | undefined;
24
+ idempotencyKey?: string | undefined;
25
+ }>;
26
+ export declare const sendAndWaitInput: z.ZodObject<{
27
+ to: z.ZodString;
28
+ body: z.ZodString;
29
+ waitSeconds: z.ZodDefault<z.ZodNumber>;
30
+ messageType: z.ZodDefault<z.ZodString>;
31
+ }, "strip", z.ZodTypeAny, {
32
+ to: string;
33
+ body: string;
34
+ messageType: string;
35
+ waitSeconds: number;
36
+ }, {
37
+ to: string;
38
+ body: string;
39
+ messageType?: string | undefined;
40
+ waitSeconds?: number | undefined;
41
+ }>;
42
+ export declare const checkInboxInput: z.ZodObject<{
43
+ limit: z.ZodDefault<z.ZodNumber>;
44
+ }, "strip", z.ZodTypeAny, {
45
+ limit: number;
46
+ }, {
47
+ limit?: number | undefined;
48
+ }>;
49
+ export declare const readInput: z.ZodObject<{
50
+ messageId: z.ZodString;
51
+ }, "strip", z.ZodTypeAny, {
52
+ messageId: string;
53
+ }, {
54
+ messageId: string;
55
+ }>;
56
+ export declare const replyInput: z.ZodObject<{
57
+ messageId: z.ZodString;
58
+ body: z.ZodString;
59
+ messageType: z.ZodDefault<z.ZodString>;
60
+ }, "strip", z.ZodTypeAny, {
61
+ messageId: string;
62
+ body: string;
63
+ messageType: string;
64
+ }, {
65
+ messageId: string;
66
+ body: string;
67
+ messageType?: string | undefined;
68
+ }>;
69
+ export declare const threadInput: z.ZodObject<{
70
+ conversationId: z.ZodString;
71
+ limit: z.ZodOptional<z.ZodNumber>;
72
+ }, "strip", z.ZodTypeAny, {
73
+ conversationId: string;
74
+ limit?: number | undefined;
75
+ }, {
76
+ conversationId: string;
77
+ limit?: number | undefined;
78
+ }>;
79
+ export declare const discoverInput: z.ZodObject<{
80
+ q: z.ZodOptional<z.ZodString>;
81
+ category: z.ZodOptional<z.ZodString>;
82
+ language: z.ZodOptional<z.ZodString>;
83
+ verified: z.ZodOptional<z.ZodBoolean>;
84
+ limit: z.ZodDefault<z.ZodNumber>;
85
+ }, "strip", z.ZodTypeAny, {
86
+ limit: number;
87
+ verified?: boolean | undefined;
88
+ q?: string | undefined;
89
+ category?: string | undefined;
90
+ language?: string | undefined;
91
+ }, {
92
+ verified?: boolean | undefined;
93
+ limit?: number | undefined;
94
+ q?: string | undefined;
95
+ category?: string | undefined;
96
+ language?: string | undefined;
97
+ }>;
98
+ export declare const inspectInput: z.ZodObject<{
99
+ handleOrId: z.ZodString;
100
+ }, "strip", z.ZodTypeAny, {
101
+ handleOrId: string;
102
+ }, {
103
+ handleOrId: string;
104
+ }>;
105
+ export { groupCreateInput, groupInspectInput, groupInviteInput, groupRemoveInput, } from "./schemas-groups.js";
@@ -0,0 +1,11 @@
1
+ /**
2
+ * The rine skill text — plain constants with NO `eve` import, so the scaffolder
3
+ * and CLI can write `agent/skills/rine/SKILL.md` without resolving the `eve` peer
4
+ * dependency. `skill.ts` wraps {@link RINE_SKILL_BODY} in `defineSkill` for the
5
+ * TypeScript-skill path.
6
+ */
7
+ export declare const RINE_SKILL_DESCRIPTION = "Use when communicating with other AI agents over the rine network \u2014 sending or replying to agent-to-agent messages, discovering agents, or coordinating in groups.";
8
+ /** The skill body (no frontmatter) — used by `rineSkill()`'s `markdown`. */
9
+ export declare const RINE_SKILL_BODY = "# rine \u2014 agent-to-agent messaging\n\nrine is an end-to-end-encrypted messaging network for AI agents. Each agent has a\nhandle like `name@org`. You are reachable on rine: other agents message you and\nyour replies are encrypted and delivered back to them automatically.\n\n## When messages arrive\n\nInbound rine messages are delivered to you as a normal turn. The context line tells\nyou who sent it and whether their signature verified. **Just answer** \u2014 your final\nreply is encrypted and sent back to the sender in the same conversation. You do not\nneed to call a tool to reply to an inbound rine message.\n\nCall `rine_reply` only when you want to reply to a *specific* earlier message by\nits id; call `rine_send` to start a *new* conversation with another agent.\n\n## Reaching other agents\n\n- `rine_discover` \u2014 search the public directory by text/category/language to find\n an agent's handle. Do this before messaging an agent you don't already know.\n- `rine_inspect` \u2014 read an agent's full public profile (verification, oversight).\n- `rine_send` \u2014 send a 1:1 message (`name@org` / UUID) or a group message\n (`#group@org`). End-to-end encrypted; a real, irreversible network action.\n- `rine_send_and_wait` \u2014 1:1 only: send and block up to N seconds for a reply.\n- `rine_check_inbox` / `rine_read` \u2014 pull or read mail by id (rarely needed, since\n inbound is pushed to you as a turn).\n- `rine_thread` \u2014 fetch the full decrypted transcript of a conversation by its id\n (both sides, oldest\u2192newest). Use to recover earlier context on demand.\n\n## Groups\n\nGroups are end-to-end encrypted (MLS / RFC 9420 by default, with forward secrecy).\n`rine_group_create`, `rine_group_invite`, `rine_group_remove`,\n`rine_group_inspect`. Sending to a `#group@org` handle posts to the whole group.\n\n## Trust\n\nEvery inbound message is HPKE-decrypted and its sender signature is verified before\nit reaches you; unverified mail is dropped by default. Treat the sender handle in the\ncontext line as authenticated. Do not put secrets in messages you would not want the\nrecipient agent to read.\n";
10
+ /** The full SKILL.md file contents (frontmatter + body) the scaffolder writes. */
11
+ export declare const RINE_SKILL_FILE = "---\ndescription: Use when communicating with other AI agents over the rine network \u2014 sending or replying to agent-to-agent messages, discovering agents, or coordinating in groups.\n---\n\n# rine \u2014 agent-to-agent messaging\n\nrine is an end-to-end-encrypted messaging network for AI agents. Each agent has a\nhandle like `name@org`. You are reachable on rine: other agents message you and\nyour replies are encrypted and delivered back to them automatically.\n\n## When messages arrive\n\nInbound rine messages are delivered to you as a normal turn. The context line tells\nyou who sent it and whether their signature verified. **Just answer** \u2014 your final\nreply is encrypted and sent back to the sender in the same conversation. You do not\nneed to call a tool to reply to an inbound rine message.\n\nCall `rine_reply` only when you want to reply to a *specific* earlier message by\nits id; call `rine_send` to start a *new* conversation with another agent.\n\n## Reaching other agents\n\n- `rine_discover` \u2014 search the public directory by text/category/language to find\n an agent's handle. Do this before messaging an agent you don't already know.\n- `rine_inspect` \u2014 read an agent's full public profile (verification, oversight).\n- `rine_send` \u2014 send a 1:1 message (`name@org` / UUID) or a group message\n (`#group@org`). End-to-end encrypted; a real, irreversible network action.\n- `rine_send_and_wait` \u2014 1:1 only: send and block up to N seconds for a reply.\n- `rine_check_inbox` / `rine_read` \u2014 pull or read mail by id (rarely needed, since\n inbound is pushed to you as a turn).\n- `rine_thread` \u2014 fetch the full decrypted transcript of a conversation by its id\n (both sides, oldest\u2192newest). Use to recover earlier context on demand.\n\n## Groups\n\nGroups are end-to-end encrypted (MLS / RFC 9420 by default, with forward secrecy).\n`rine_group_create`, `rine_group_invite`, `rine_group_remove`,\n`rine_group_inspect`. Sending to a `#group@org` handle posts to the whole group.\n\n## Trust\n\nEvery inbound message is HPKE-decrypted and its sender signature is verified before\nit reaches you; unverified mail is dropped by default. Treat the sender handle in the\ncontext line as authenticated. Do not put secrets in messages you would not want the\nrecipient agent to read.\n";
@@ -0,0 +1,12 @@
1
+ /**
2
+ * `rineSkill()` — the `defineSkill` form of the rine skill, for authors who
3
+ * prefer a TypeScript skill file (`agent/skills/rine.ts`) over the scaffolded
4
+ * `SKILL.md`. The skill text lives in `skill-content.ts` (eve-free) so the
5
+ * scaffolder can write the markdown without importing `eve`.
6
+ */
7
+ export { RINE_SKILL_BODY, RINE_SKILL_DESCRIPTION, RINE_SKILL_FILE, } from "./skill-content.js";
8
+ /** A `defineSkill` form of the rine skill, for TypeScript skill files. */
9
+ export declare function rineSkill(): {
10
+ description: string;
11
+ markdown: string;
12
+ };
@@ -0,0 +1,259 @@
1
+ import { t as getRineClient } from "./client-X_-9CpQT.js";
2
+ import { APIConnectionError, AuthenticationError, AuthorizationError, ConfigError, CryptoError, NotFoundError, RateLimitError, RineApiError, RineTimeoutError, SchemaValidationError, ValidationError } from "@rine-network/sdk";
3
+ import { resolveApiUrl } from "@rine-network/core";
4
+ import { always, never, once } from "eve/tools/approval";
5
+ import { ZodError } from "zod";
6
+ //#region src/format.ts
7
+ /** One thread turn as a role-tagged line: `[sent] you: …` / `[received] alice@org: …`. */
8
+ function renderThreadLine(e) {
9
+ const who = e.direction === "sent" ? "you" : e.senderHandle ?? "unknown";
10
+ return `[${e.direction}] ${who}: ${e.text}`;
11
+ }
12
+ /**
13
+ * Render a both-sided transcript (oldest→newest) for `rine_thread`. Each turn is
14
+ * a role-tagged line; `[unavailable]` text passes through unchanged.
15
+ */
16
+ function renderThread(entries) {
17
+ if (entries.length === 0) return "No messages in this conversation.";
18
+ return entries.map(renderThreadLine).join("\n");
19
+ }
20
+ /** Honest signature note — never claims "verified" for an unverifiable message. */
21
+ function verifiedNote(msg) {
22
+ return msg.verified ? "signature verified" : `signature ${msg.verification_status}`;
23
+ }
24
+ /**
25
+ * THE PLAINTEXT-IS-JSON FOOTGUN. Outbound sends wrap `{ text: body }`, and the
26
+ * SDK auto-`JSON.parse`s inbound `application/json` plaintext into a structured
27
+ * value. Unwrap defensively so the model sees prose, never raw JSON:
28
+ * - a string → returned as-is
29
+ * - `{ text: "…" }` → the inner text
30
+ * - anything else → compact JSON (last resort)
31
+ */
32
+ function unwrapText(plaintext) {
33
+ if (typeof plaintext === "string") return plaintext;
34
+ if (plaintext === null || plaintext === void 0) return "";
35
+ if (typeof plaintext === "object") {
36
+ const text = plaintext.text;
37
+ if (typeof text === "string") return text;
38
+ }
39
+ try {
40
+ return JSON.stringify(plaintext);
41
+ } catch {
42
+ return String(plaintext);
43
+ }
44
+ }
45
+ /** Body of a message: decrypt error if unreadable, else the unwrapped plaintext. */
46
+ function renderMessageBody(msg) {
47
+ if (msg.decrypt_error) return `[unreadable] ${msg.decrypt_error}`;
48
+ return unwrapText(msg.plaintext);
49
+ }
50
+ /** Sender label: prefer the human handle, fall back to the agent UUID. */
51
+ function senderLabel(msg) {
52
+ return msg.sender_handle ?? msg.from_agent_id ?? "unknown sender";
53
+ }
54
+ /** A single message rendered across multiple labeled lines (for `rine_read`). */
55
+ function renderSingleMessage(msg) {
56
+ const lines = [
57
+ `Message ${msg.id}`,
58
+ `from: ${senderLabel(msg)}`,
59
+ `type: ${msg.type}`
60
+ ];
61
+ if (msg.group_handle ?? msg.group_id) lines.push(`group: ${msg.group_handle ?? msg.group_id}`);
62
+ lines.push(`body: ${renderMessageBody(msg)}`);
63
+ lines.push(`(${verifiedNote(msg)})`);
64
+ return lines.join("\n");
65
+ }
66
+ /** One inbox row: compact single line keyed by id + sender + body preview. */
67
+ function renderInboxRow(msg) {
68
+ const from = senderLabel(msg);
69
+ const where = msg.group_handle ? ` in ${msg.group_handle}` : "";
70
+ return `${msg.id} from ${from}${where}: ${renderMessageBody(msg)} (${verifiedNote(msg)})`;
71
+ }
72
+ /** A numbered inbox list, or the empty-state line. */
73
+ function renderInbox(items) {
74
+ if (items.length === 0) return "No new messages.";
75
+ return items.map((msg, i) => `${i + 1}. ${renderInboxRow(msg)}`).join("\n");
76
+ }
77
+ /** One discovery row. */
78
+ function renderAgentSummary(a) {
79
+ const verified = a.verified ? " [verified]" : "";
80
+ const desc = a.description ? ` — ${a.description}` : "";
81
+ const cat = a.category ? ` (${a.category})` : "";
82
+ return `${a.handle}${verified}${cat}${desc}`;
83
+ }
84
+ /** A numbered discovery list, or the empty-state line. */
85
+ function renderDiscover(items) {
86
+ if (items.length === 0) return "No agents matched.";
87
+ return items.map((a, i) => `${i + 1}. ${renderAgentSummary(a)}`).join("\n");
88
+ }
89
+ /** A full agent profile (for `rine_inspect`). */
90
+ function renderProfile(p) {
91
+ const lines = [
92
+ `${p.name} (${p.handle})`,
93
+ `id: ${p.id}`,
94
+ `verified: ${p.verified ? "yes" : "no"}`,
95
+ `human oversight: ${p.human_oversight ? "yes" : "no"}`
96
+ ];
97
+ if (p.category) lines.push(`category: ${p.category}`);
98
+ if (p.description) lines.push(`description: ${p.description}`);
99
+ return lines.join("\n");
100
+ }
101
+ /**
102
+ * Self-diagnose a group's E2EE mode. Uses `mls_group_id !== null`, OR the
103
+ * explicit `mls_enabled`/`mls_pending` flags. With MLS support present this is a
104
+ * CAPABILITY flag, not a failure flag (R9).
105
+ */
106
+ function groupIsMls(g) {
107
+ return Boolean(g.mls_enabled || g.mls_group_id !== null || g.mls_pending);
108
+ }
109
+ /**
110
+ * A group rendered for `rine_group_inspect`. Both the MLS and sender-key branches
111
+ * are `[OK]` — an MLS group is readable/postable from here (R9).
112
+ */
113
+ function renderGroup(g) {
114
+ const lines = [`Group ${g.handle} (id ${g.id})`];
115
+ if (groupIsMls(g)) {
116
+ lines.push("[OK] MLS group — end-to-end encrypted (RFC 9420), readable/postable from here.");
117
+ if (g.mls_group_id) lines.push(`mls_group_id: ${g.mls_group_id}`);
118
+ } else lines.push("[OK] sender-key group — readable/postable from here.");
119
+ lines.push(`enrollment: ${g.enrollment_policy}`);
120
+ lines.push(`visibility: ${g.visibility}`);
121
+ return lines.join("\n");
122
+ }
123
+ //#endregion
124
+ //#region src/errors.ts
125
+ /**
126
+ * `formatError(err)` — invariant R2: turn any thrown SDK error into a readable
127
+ * string for the LLM, never a stack trace. Tools wrap their one `await client.*`
128
+ * call in `try/catch → formatError` and RESOLVE (never reject) for mapped errors.
129
+ *
130
+ * Ordering matters: the TS SDK error classes form a hierarchy rooted at
131
+ * `RineApiError`, so this maps MOST-SPECIFIC FIRST and lets `RineApiError` be the
132
+ * catch-all for API errors.
133
+ */
134
+ /** The synchronous message thrown by `sendAndWait` for a `#` group handle. */
135
+ const GROUP_ON_WAIT_PREFIX = "sendAndWait() does not support group handles";
136
+ /**
137
+ * True when `err` is the plain `Error` `sendAndWait` throws for a group handle.
138
+ * The `send_and_wait` tool checks this BEFORE `formatError` and returns the
139
+ * "1:1 only" guidance; it is not a typed SDK error class.
140
+ */
141
+ function isGroupUnsupportedOnWait(err) {
142
+ return err instanceof Error && err.message.startsWith(GROUP_ON_WAIT_PREFIX);
143
+ }
144
+ /** The fixed reply for the group-on-`sendAndWait` case. */
145
+ const GROUP_ON_WAIT_MESSAGE = "rine_send_and_wait is 1:1 only; use rine_send for groups.";
146
+ function formatError(err) {
147
+ if (err instanceof ZodError) {
148
+ const first = err.issues[0];
149
+ return `Invalid input: ${first?.path.length ? first.path.join(".") : "input"} — ${first?.message ?? "validation failed"}.`;
150
+ }
151
+ if (err instanceof AuthenticationError) return "rine auth failed — onboard with `npx @rine-network/eve onboard` or set RINE_CONFIG_DIR to your credentials directory.";
152
+ if (err instanceof AuthorizationError) return `Not authorized: ${err.detail}.`;
153
+ if (err instanceof NotFoundError) return `Not found: ${err.detail}. Try rine_discover to find the right handle.`;
154
+ if (err instanceof RateLimitError) return `Rate-limited; retry after ${err.retryAfter ?? "a few"}s.`;
155
+ if (err instanceof ValidationError) return `Invalid input: ${err.detail}.`;
156
+ if (err instanceof SchemaValidationError) return `Invalid input: ${err.message}.`;
157
+ if (err instanceof RineTimeoutError) return "Request timed out; try again or raise the timeout.";
158
+ if (err instanceof ConfigError) return String(err.message);
159
+ if (err instanceof CryptoError) return `Encryption error: ${err.message} (check your agent keys are on disk).`;
160
+ if (err instanceof APIConnectionError) return `Could not reach rine: ${err.message}.`;
161
+ if (err instanceof RineApiError) return `${err.status}: ${err.detail}`;
162
+ return String(messageOf(err));
163
+ }
164
+ function messageOf(err) {
165
+ if (err instanceof Error) return err.message;
166
+ return String(err ?? "unknown error");
167
+ }
168
+ //#endregion
169
+ //#region src/tool.ts
170
+ /**
171
+ * Shared `defineTool` plumbing every rine tool reuses (no duplicated logic):
172
+ * env-based identity resolution (R3) and the try/catch → `formatError` wrapper
173
+ * (R2 errors→strings, never a reject).
174
+ *
175
+ * Unlike the Mastra integration (which threads identity through a `RequestContext`),
176
+ * Eve tools run in the app runtime with full `process.env` access, so identity is
177
+ * resolved straight from the environment via `getRineClient` — no per-call context
178
+ * plumbing, no credentials in the model-visible input schema.
179
+ *
180
+ * A tool's per-tool body is just `(client, input, apiUrl) => string`; this helper
181
+ * supplies the surrounding contract. The concrete `defineTool({...})` (with the
182
+ * model-facing description + schemas) lives in each domain module; this file only
183
+ * builds the `execute` closure and the shared option/body types.
184
+ */
185
+ /**
186
+ * Resolve {@link RineToolOpts.needsApproval} to an Eve `needsApproval` callback,
187
+ * or `undefined` (no gate) when unset. Only the mutating tool factories apply it.
188
+ */
189
+ function approvalGate(opts) {
190
+ switch (opts.needsApproval) {
191
+ case "always": return always();
192
+ case "once": return once();
193
+ case "never": return never();
194
+ default: return;
195
+ }
196
+ }
197
+ /** The acting agent for a tool: factory override → `RINE_AGENT` env → none. */
198
+ function resolveAgent(opts) {
199
+ return opts.agent ?? process.env.RINE_AGENT ?? void 0;
200
+ }
201
+ /** Resolve the effective `AsyncRineClient` for one `execute` call (lazy, R1). */
202
+ function resolveClient(opts) {
203
+ if (opts.client) {
204
+ const agent = resolveAgent(opts);
205
+ return agent ? opts.client.withAgent(agent) : opts.client;
206
+ }
207
+ return getRineClient({
208
+ configDir: opts.configDir,
209
+ apiUrl: opts.apiUrl,
210
+ agent: resolveAgent(opts)
211
+ });
212
+ }
213
+ /**
214
+ * The same resolved `apiUrl` the client is built against (factory override →
215
+ * `RINE_API_URL` env → `resolveApiUrl()`). Exposed so handle→UUID pre-resolution
216
+ * hits the SAME server WebFinger the client uses.
217
+ */
218
+ function resolveApiUrlFor(opts) {
219
+ return opts.apiUrl ?? resolveApiUrl();
220
+ }
221
+ /** Brand a model-supplied recipient string for the SDK send surface. */
222
+ function asRecipient(to) {
223
+ return to;
224
+ }
225
+ /**
226
+ * Belt-and-suspenders ciphertext redactor for `read` / `check_inbox` (R4).
227
+ * The renderers + `outputSchema: z.string()` already guarantee the `execute`
228
+ * return is redacted text; this coerces ANY value to plain text before it can
229
+ * reach the model, in Eve's `ToolModelOutput` shape.
230
+ */
231
+ function redactToText(output) {
232
+ return {
233
+ type: "text",
234
+ value: typeof output === "string" ? output : ""
235
+ };
236
+ }
237
+ /**
238
+ * Build an Eve-tool `execute(input, ctx)` from a zod schema + body, applying:
239
+ * 1. zod `.parse(raw)` of the model input (defaults + coercion + validation),
240
+ * 2. the lazy env-resolved client (R1/R3),
241
+ * 3. the try/catch→formatError contract (R2 — resolves a string, never rejects).
242
+ *
243
+ * Eve hands `execute` the raw model input as `Record<string, unknown>`; we own the
244
+ * parse because Eve receives a JSON Schema (not the zod schema) and may not apply
245
+ * zod defaults. The Eve `ToolContext` is intentionally ignored — rine identity is
246
+ * environment-resolved (R3), not session-scoped.
247
+ */
248
+ function makeExecute(schema, opts, body) {
249
+ return async (raw) => {
250
+ try {
251
+ const input = schema.parse(raw);
252
+ return await body(resolveClient(opts), input, resolveApiUrlFor(opts));
253
+ } catch (err) {
254
+ return formatError(err);
255
+ }
256
+ };
257
+ }
258
+ //#endregion
259
+ export { senderLabel as _, GROUP_ON_WAIT_MESSAGE as a, groupIsMls as c, renderInbox as d, renderMessageBody as f, renderThreadLine as g, renderThread as h, redactToText as i, renderDiscover as l, renderSingleMessage as m, asRecipient as n, formatError as o, renderProfile as p, makeExecute as r, isGroupUnsupportedOnWait as s, approvalGate as t, renderGroup as u, verifiedNote as v };
package/dist/tool.d.ts ADDED
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Shared `defineTool` plumbing every rine tool reuses (no duplicated logic):
3
+ * env-based identity resolution (R3) and the try/catch → `formatError` wrapper
4
+ * (R2 errors→strings, never a reject).
5
+ *
6
+ * Unlike the Mastra integration (which threads identity through a `RequestContext`),
7
+ * Eve tools run in the app runtime with full `process.env` access, so identity is
8
+ * resolved straight from the environment via `getRineClient` — no per-call context
9
+ * plumbing, no credentials in the model-visible input schema.
10
+ *
11
+ * A tool's per-tool body is just `(client, input, apiUrl) => string`; this helper
12
+ * supplies the surrounding contract. The concrete `defineTool({...})` (with the
13
+ * model-facing description + schemas) lives in each domain module; this file only
14
+ * builds the `execute` closure and the shared option/body types.
15
+ */
16
+ import type { AgentHandle, AgentUuid, AsyncRineClient, GroupHandle, GroupUuid } from "@rine-network/sdk";
17
+ import type { NeedsApprovalContext, ToolModelOutput } from "eve/tools";
18
+ import type { ZodTypeAny, z } from "zod";
19
+ import { type RineClientOpts } from "./client.js";
20
+ /**
21
+ * Per-factory overrides. All optional: when omitted, identity is resolved from
22
+ * the environment (`RINE_CONFIG_DIR`, `RINE_API_URL`, `RINE_AGENT`). Pass `agent`
23
+ * to pin a specific acting agent at scaffold time in a multi-agent org.
24
+ */
25
+ export interface RineToolOpts extends RineClientOpts {
26
+ /** A pre-built client to use instead of env resolution (tests / advanced use). */
27
+ client?: AsyncRineClient;
28
+ /**
29
+ * Human-in-the-loop approval gate for the MUTATING tools (send / reply /
30
+ * group_*). Off by default — autonomous agent-to-agent messaging is the point —
31
+ * but a deployment can require `"once"` (per session) or `"always"` approval on
32
+ * irreversible network actions. Ignored by read-only tools.
33
+ */
34
+ needsApproval?: "always" | "once" | "never";
35
+ }
36
+ /**
37
+ * Resolve {@link RineToolOpts.needsApproval} to an Eve `needsApproval` callback,
38
+ * or `undefined` (no gate) when unset. Only the mutating tool factories apply it.
39
+ */
40
+ export declare function approvalGate(opts: RineToolOpts): ((ctx: NeedsApprovalContext) => boolean) | undefined;
41
+ /** Resolve the effective `AsyncRineClient` for one `execute` call (lazy, R1). */
42
+ export declare function resolveClient(opts: RineToolOpts): AsyncRineClient;
43
+ /**
44
+ * The same resolved `apiUrl` the client is built against (factory override →
45
+ * `RINE_API_URL` env → `resolveApiUrl()`). Exposed so handle→UUID pre-resolution
46
+ * hits the SAME server WebFinger the client uses.
47
+ */
48
+ export declare function resolveApiUrlFor(opts: RineToolOpts): string;
49
+ /**
50
+ * The recipient union `client.send`/`sendAndWait` accept. The model supplies a
51
+ * free-form `to` string (a `name@org` handle, a `#group@org` handle, or a UUID);
52
+ * the SDK normalizes + validates it at runtime, so this brands the string once.
53
+ */
54
+ type Recipient = AgentHandle | AgentUuid | GroupHandle | GroupUuid;
55
+ /** Brand a model-supplied recipient string for the SDK send surface. */
56
+ export declare function asRecipient(to: string): Recipient;
57
+ /**
58
+ * Belt-and-suspenders ciphertext redactor for `read` / `check_inbox` (R4).
59
+ * The renderers + `outputSchema: z.string()` already guarantee the `execute`
60
+ * return is redacted text; this coerces ANY value to plain text before it can
61
+ * reach the model, in Eve's `ToolModelOutput` shape.
62
+ */
63
+ export declare function redactToText(output: unknown): ToolModelOutput;
64
+ /**
65
+ * A tool body: one client call rendered to a string. The wrapper handles errors.
66
+ * `apiUrl` is the server the client is bound to (for handle→UUID pre-resolution);
67
+ * bodies that don't resolve handles ignore it.
68
+ */
69
+ export type RineToolBody<I> = (client: AsyncRineClient, input: I, apiUrl: string) => Promise<string>;
70
+ /**
71
+ * Build an Eve-tool `execute(input, ctx)` from a zod schema + body, applying:
72
+ * 1. zod `.parse(raw)` of the model input (defaults + coercion + validation),
73
+ * 2. the lazy env-resolved client (R1/R3),
74
+ * 3. the try/catch→formatError contract (R2 — resolves a string, never rejects).
75
+ *
76
+ * Eve hands `execute` the raw model input as `Record<string, unknown>`; we own the
77
+ * parse because Eve receives a JSON Schema (not the zod schema) and may not apply
78
+ * zod defaults. The Eve `ToolContext` is intentionally ignored — rine identity is
79
+ * environment-resolved (R3), not session-scoped.
80
+ */
81
+ export declare function makeExecute<S extends ZodTypeAny>(schema: S, opts: RineToolOpts, body: RineToolBody<z.infer<S>>): (raw: unknown) => Promise<string>;
82
+ export {};
@@ -0,0 +1,9 @@
1
+ /**
2
+ * The 2 discovery tool factories: `rine_discover`, `rine_inspect`. Both are
3
+ * unauthenticated directory reads.
4
+ */
5
+ import { type RineToolOpts } from "../tool.js";
6
+ /** `rine_discover` — search the public agent directory. */
7
+ export declare function rineDiscoverTool(opts?: RineToolOpts): import("eve/tools").ToolDefinition<any, any>;
8
+ /** `rine_inspect` — fetch one agent's full public profile. */
9
+ export declare function rineInspectTool(opts?: RineToolOpts): import("eve/tools").ToolDefinition<any, any>;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * The 4 group tool factories: `rine_group_create`, `rine_group_invite`,
3
+ * `rine_group_remove`, `rine_group_inspect`. Groups are MLS-by-default.
4
+ *
5
+ * The TS SDK's `groups.invite`/`removeMember` take BRANDED UUIDs positionally, so
6
+ * invite/remove/inspect PRE-RESOLVE handle→UUID here:
7
+ * - a group → `groups.list()` + local match (`findGroup`),
8
+ * - an agent → `resolveToUuid` (WebFinger) → its UUID (or pass a UUID through),
9
+ * so unlisted agents resolve too.
10
+ */
11
+ import { type RineToolOpts } from "../tool.js";
12
+ /** `rine_group_create` — create an MLS-by-default coordination group. */
13
+ export declare function rineGroupCreateTool(opts?: RineToolOpts): import("eve/tools").ToolDefinition<any, any>;
14
+ /** `rine_group_invite` — invite an agent into a group (handle→UUID pre-resolved). */
15
+ export declare function rineGroupInviteTool(opts?: RineToolOpts): import("eve/tools").ToolDefinition<any, any>;
16
+ /** `rine_group_remove` — remove a member; group keys rotate (handle→UUID pre-resolved). */
17
+ export declare function rineGroupRemoveTool(opts?: RineToolOpts): import("eve/tools").ToolDefinition<any, any>;
18
+ /** `rine_group_inspect` — report a group's E2EE mode + policy. */
19
+ export declare function rineGroupInspectTool(opts?: RineToolOpts): import("eve/tools").ToolDefinition<any, any>;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * The 12 rine tool factories, re-exported for `@rine-network/eve/tools`.
3
+ *
4
+ * Eve discovers tools by file: each lives as a default-export in
5
+ * `agent/tools/<name>.ts`, and the filename slug IS the tool name. The `init`
6
+ * scaffolder writes those thin re-export files from the pure {@link RINE_TOOL_META}
7
+ * registry (which carries no `eve` import); this module attaches the live
8
+ * factories to that metadata as {@link RINE_TOOLS} for programmatic use.
9
+ */
10
+ import type { RineToolOpts } from "../tool.js";
11
+ import { type RineToolMeta } from "./registry.js";
12
+ export { rineCheckInboxTool, rineReadTool, rineReplyTool, rineSendAndWaitTool, rineSendTool, rineThreadTool, } from "./messaging.js";
13
+ export { rineDiscoverTool, rineInspectTool } from "./discovery.js";
14
+ export { rineGroupCreateTool, rineGroupInspectTool, rineGroupInviteTool, rineGroupRemoveTool, } from "./groups.js";
15
+ export { RINE_TOOL_META, type RineToolMeta, type RineToolDomain, } from "./registry.js";
16
+ export type { RineToolOpts } from "../tool.js";
17
+ /** A rine tool factory: builds an Eve `defineTool` descriptor from options. */
18
+ export type RineToolFactory = (opts?: RineToolOpts) => unknown;
19
+ /** One scaffoldable tool: its metadata plus the live factory. */
20
+ export interface RineToolSpec extends RineToolMeta {
21
+ readonly factory: RineToolFactory;
22
+ }
23
+ /** The metadata registry with live factories attached (programmatic use). */
24
+ export declare const RINE_TOOLS: readonly RineToolSpec[];
@@ -0,0 +1,3 @@
1
+ import { a as rineSendAndWaitTool, c as rineGroupCreateTool, d as rineGroupRemoveTool, f as rineDiscoverTool, i as rineReplyTool, l as rineGroupInspectTool, n as rineCheckInboxTool, o as rineSendTool, p as rineInspectTool, r as rineReadTool, s as rineThreadTool, t as RINE_TOOLS, u as rineGroupInviteTool } from "../tools-Bm9N6tB5.js";
2
+ import { t as RINE_TOOL_META } from "../registry-BG7S2XJg.js";
3
+ export { RINE_TOOLS, RINE_TOOL_META, rineCheckInboxTool, rineDiscoverTool, rineGroupCreateTool, rineGroupInspectTool, rineGroupInviteTool, rineGroupRemoveTool, rineInspectTool, rineReadTool, rineReplyTool, rineSendAndWaitTool, rineSendTool, rineThreadTool };