@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/dist/types.d.ts CHANGED
@@ -7,7 +7,7 @@
7
7
  * schema changes, these follow). These are type-only re-derivations; runtime tool
8
8
  * schemas are authored with the host `zod` in `schemas.ts`.
9
9
  */
10
- import type { AgentProfileSchema, AgentSummarySchema, DecryptedMessageSchema, GroupReadSchema, InviteResultSchema, JoinRequestReadSchema, JoinResultSchema, MessageReadSchema } from "@rine-network/sdk";
10
+ import type { AgentProfileSchema, AgentSummarySchema, DecryptedMessageSchema, GroupReadSchema, InviteResultSchema, JoinRequestReadSchema, JoinResultSchema, MessageReadSchema, VoteResponseSchema } from "@rine-network/sdk";
11
11
  /** Project a Zod schema's output type without importing the SDK's `z` instance. */
12
12
  type Infer<S> = S extends {
13
13
  _output: infer O;
@@ -20,4 +20,5 @@ export type JoinResult = Infer<typeof JoinResultSchema>;
20
20
  export type JoinRequestRead = Infer<typeof JoinRequestReadSchema>;
21
21
  export type AgentSummary = Infer<typeof AgentSummarySchema>;
22
22
  export type AgentProfile = Infer<typeof AgentProfileSchema>;
23
+ export type VoteResponse = Infer<typeof VoteResponseSchema>;
23
24
  export {};
package/dist/webhook.d.ts CHANGED
@@ -11,7 +11,8 @@
11
11
  export interface WebhookOptions {
12
12
  /** Public base URL of the deployed Eve agent (e.g. `https://x.vercel.app`). */
13
13
  baseUrl: string;
14
- /** Acting agent handle/UUID; defaults to `process.env.RINE_AGENT`. */
14
+ /** Acting agent name, handle or UUID; falls back to `RINE_AGENT`, then to
15
+ * the org's only agent. */
15
16
  agent?: string;
16
17
  apiUrl?: string;
17
18
  configDir?: string;
@@ -27,6 +28,12 @@ export interface RegisteredWebhook {
27
28
  /**
28
29
  * Register a rine webhook pointing at the deployed agent's inbound route. Returns
29
30
  * the webhook id + the HMAC secret (shown once) for `RINE_WEBHOOK_SECRET`.
31
+ *
32
+ * The whole acting-agent ladder runs here, because this is the one Eve
33
+ * entry point that must name a *specific* agent — the webhook is created against
34
+ * an agent id, not an org. So a bare name works, a single-agent org needs no
35
+ * `--agent` at all, and the refusal names the org's agents rather than asserting
36
+ * the caller forgot an env var they may well have set to the wrong thing.
30
37
  */
31
38
  export declare function registerRineWebhook(opts: WebhookOptions): Promise<RegisteredWebhook>;
32
39
  /** Delete a previously-registered webhook by id (best-effort teardown). */
package/dist/webhook.js CHANGED
@@ -1,6 +1,6 @@
1
- import { t as getRineClient } from "./client-DsG2xtKs.js";
1
+ import { n as getRineClient, t as actingAgent } from "./client-CXJATA-m.js";
2
2
  import { asAgentUuid, asWebhookUuid } from "@rine-network/sdk";
3
- import { UUID_RE, resolveApiUrl, resolveToUuid } from "@rine-network/core";
3
+ import { ACTOR_SURFACES, UUID_RE, normalizeActor, resolveAgent } from "@rine-network/core";
4
4
  //#region src/webhook.ts
5
5
  /**
6
6
  * Prod inbound wiring — `npx @rine-network/eve webhook --url <publicBase>`.
@@ -12,30 +12,50 @@ import { UUID_RE, resolveApiUrl, resolveToUuid } from "@rine-network/core";
12
12
  * down. The client is built lazily inside the call.
13
13
  */
14
14
  const DEFAULT_INBOUND_PATH = "/rine/v1/inbound";
15
+ /**
16
+ * How Eve spells the acting agent in a refusal.
17
+ *
18
+ * `ACTOR_SURFACES.ts` with the environment rung switched back on: the SDK
19
+ * itself reads no environment, but Eve is a *surface* and
20
+ * `actingAgent()` climbs to `RINE_AGENT` on its behalf — the scaffolder even
21
+ * writes the variable into every generated `.env.example`. A refusal that
22
+ * omitted it would hide the lever this deployment is configured with.
23
+ */
24
+ const EVE_SURFACE = {
25
+ ...ACTOR_SURFACES.ts,
26
+ readsEnv: true
27
+ };
15
28
  function inboundUrl(opts) {
16
29
  const path = opts.path ?? process.env.RINE_INBOUND_PATH ?? DEFAULT_INBOUND_PATH;
17
30
  return `${opts.baseUrl.replace(/\/$/, "")}${path}`;
18
31
  }
19
- async function resolveAgentUuid(apiUrl, agent) {
20
- if (UUID_RE.test(agent)) return asAgentUuid(agent);
21
- return asAgentUuid(await resolveToUuid(apiUrl, agent));
22
- }
23
32
  /**
24
33
  * Register a rine webhook pointing at the deployed agent's inbound route. Returns
25
34
  * the webhook id + the HMAC secret (shown once) for `RINE_WEBHOOK_SECRET`.
35
+ *
36
+ * The whole acting-agent ladder runs here, because this is the one Eve
37
+ * entry point that must name a *specific* agent — the webhook is created against
38
+ * an agent id, not an org. So a bare name works, a single-agent org needs no
39
+ * `--agent` at all, and the refusal names the org's agents rather than asserting
40
+ * the caller forgot an env var they may well have set to the wrong thing.
26
41
  */
27
42
  async function registerRineWebhook(opts) {
28
- const agent = opts.agent ?? process.env.RINE_AGENT;
29
- if (!agent) throw new Error("no acting agent — set RINE_AGENT or pass --agent <handle>");
30
- const apiUrl = opts.apiUrl ?? resolveApiUrl();
31
43
  const client = getRineClient({
32
- agent,
33
44
  apiUrl: opts.apiUrl,
34
45
  configDir: opts.configDir
35
46
  });
36
- const agentUuid = await resolveAgentUuid(apiUrl, agent);
47
+ const orgAgents = async () => (await client.identity.listAgents()).map((a) => ({
48
+ ...a,
49
+ verification_words: a.verification_words ?? void 0,
50
+ warnings: a.warnings ?? void 0,
51
+ poll_url: a.poll_url ?? void 0
52
+ }));
53
+ const named = actingAgent(opts);
54
+ const resolved = named !== void 0 && UUID_RE.test(named) ? { agentId: named } : await resolveAgent(await orgAgents(), named, void 0, EVE_SURFACE, normalizeActor(opts.agent) === void 0 ? "environment" : "argument");
55
+ if (resolved.warning !== void 0) console.warn(`rine: ${resolved.warning}`);
56
+ const agentUuid = asAgentUuid(resolved.agentId);
37
57
  const url = inboundUrl(opts);
38
- const created = await client.webhooks.create(agentUuid, url);
58
+ const created = await client.withAgent(agentUuid).webhooks.create(agentUuid, url);
39
59
  return {
40
60
  id: created.id,
41
61
  secret: created.secret,
@@ -45,7 +65,7 @@ async function registerRineWebhook(opts) {
45
65
  /** Delete a previously-registered webhook by id (best-effort teardown). */
46
66
  async function deleteRineWebhook(id, opts = {}) {
47
67
  await getRineClient({
48
- agent: opts.agent ?? process.env.RINE_AGENT,
68
+ agent: actingAgent(opts),
49
69
  apiUrl: opts.apiUrl,
50
70
  configDir: opts.configDir
51
71
  }).webhooks.delete(asWebhookUuid(id));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rine-network/eve",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "description": "Native Vercel Eve connector for the rine network \u2014 a custom channel that makes an Eve agent reachable over E2E-encrypted (HPKE 1:1, MLS groups RFC 9420, PQ-hybrid) agent-to-agent messaging, plus file-discovered rine tools, a skill, and an init/onboard/relay CLI.",
5
5
  "author": "mmmbs <mmmbs@proton.me>",
6
6
  "license": "EUPL-1.2",
@@ -48,8 +48,8 @@
48
48
  "prepublishOnly": "node scripts/check-no-file-deps.mjs"
49
49
  },
50
50
  "dependencies": {
51
- "@rine-network/core": "^0.12.0",
52
- "@rine-network/sdk": "^0.9.0",
51
+ "@rine-network/core": "^0.14.0",
52
+ "@rine-network/sdk": "^0.11.0",
53
53
  "zod": "^3.25.0",
54
54
  "zod-to-json-schema": "^3.24.1"
55
55
  },
@@ -1,147 +0,0 @@
1
- import { t as RINE_TOOL_META } from "./registry-Bn4EqPcp.js";
2
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
- import { dirname, join } from "node:path";
4
- //#region src/skill-content.ts
5
- /**
6
- * The rine skill text — plain constants with NO `eve` import, so the scaffolder
7
- * and CLI can write `agent/skills/rine/SKILL.md` without resolving the `eve` peer
8
- * dependency. `skill.ts` wraps {@link RINE_SKILL_BODY} in `defineSkill` for the
9
- * TypeScript-skill path.
10
- */
11
- 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.";
12
- /** The skill body (no frontmatter) — used by `rineSkill()`'s `markdown`. */
13
- const RINE_SKILL_BODY = `# rine — agent-to-agent messaging
14
-
15
- rine is an end-to-end-encrypted messaging network for AI agents. Each agent has a
16
- handle like \`name@org\`. You are reachable on rine: other agents message you and
17
- your replies are encrypted and delivered back to them automatically.
18
-
19
- ## When messages arrive
20
-
21
- Inbound rine messages are delivered to you as a normal turn. The context line tells
22
- you who sent it and whether their signature verified. **Just answer** — your final
23
- reply is encrypted and sent back to the sender in the same conversation. You do not
24
- need to call a tool to reply to an inbound rine message.
25
-
26
- Call \`rine_reply\` only when you want to reply to a *specific* earlier message by
27
- its id; call \`rine_send\` to start a *new* conversation with another agent.
28
-
29
- ## Reaching other agents
30
-
31
- - \`rine_discover\` — search the public directory by text/category/language to find
32
- an agent's handle. Do this before messaging an agent you don't already know.
33
- - \`rine_inspect\` — read an agent's full public profile (verification, oversight).
34
- - \`rine_send\` — send a 1:1 message (\`name@org\` / UUID) or a group message
35
- (\`#group@org\`). End-to-end encrypted; a real, irreversible network action.
36
- - \`rine_send_and_wait\` — 1:1 only: send and block up to N seconds for a reply.
37
- - \`rine_check_inbox\` / \`rine_read\` — pull or read mail by id (rarely needed, since
38
- inbound is pushed to you as a turn).
39
- - \`rine_thread\` — fetch the full decrypted transcript of a conversation by its id
40
- (both sides, oldest→newest). Use to recover earlier context on demand.
41
-
42
- ## Groups
43
-
44
- Groups are end-to-end encrypted (MLS / RFC 9420 by default, with forward secrecy).
45
- \`rine_group_create\`, \`rine_group_invite\`, \`rine_group_remove\`,
46
- \`rine_group_inspect\`, \`rine_group_join\`, \`rine_group_invites\`. Sending to a
47
- \`#group@org\` handle posts to the whole group.
48
-
49
- ## Trust
50
-
51
- Every inbound message is HPKE-decrypted and its sender signature is verified before
52
- it reaches you; unverified mail is dropped by default. Treat the sender handle in the
53
- context line as authenticated. Do not put secrets in messages you would not want the
54
- recipient agent to read.
55
- `;
56
- /** The full SKILL.md file contents (frontmatter + body) the scaffolder writes. */
57
- const RINE_SKILL_FILE = `---
58
- description: ${RINE_SKILL_DESCRIPTION}
59
- ---
60
-
61
- ${RINE_SKILL_BODY}`;
62
- //#endregion
63
- //#region src/scaffold.ts
64
- /**
65
- * `init` scaffolding — writes the thin `agent/` files that wire rine into an Eve
66
- * project: the one-line channel re-export, one re-export file per selected tool
67
- * (Eve discovers tools by filename, so the slug IS the tool name), the skill
68
- * markdown, and a `.env.example` block. Pure content builders are exported for
69
- * tests; {@link scaffoldRine} does the filesystem writes.
70
- */
71
- const ENV_MARKER = "# rine — @rine-network/eve";
72
- const DEFAULT_INBOUND_PATH = "/rine/v1/inbound";
73
- /** The default-export channel file (`agent/channels/rine.ts`). */
74
- function channelFileContent() {
75
- 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`;
76
- }
77
- /** A single tool's re-export file (`agent/tools/<name>.ts`). */
78
- function toolFileContent(spec) {
79
- return `import { ${spec.factoryName} } from "@rine-network/eve/tools";\n\nexport default ${spec.factoryName}();\n`;
80
- }
81
- /** The `.env.example` block (with the inbound path applied). */
82
- function envExampleBlock(path) {
83
- return `${ENV_MARKER}
84
- RINE_CONFIG_DIR=
85
- RINE_AGENT=
86
- # RINE_API_URL=https://rine.network
87
- RINE_WEBHOOK_SECRET=
88
- # RINE_WEBHOOK_ID=
89
- # RINE_INBOUND_PATH=${path}
90
- # x402 payments (optional). RINE_X402_AUTO_PAY=1 auto-pays quotes at/below your
91
- # policy's autoPayThreshold with no LLM turn (default OFF). RINE_FACILITATOR is the
92
- # rine_fulfill facilitator — a preset name (cdp/payai/x402-rs) or a base URL.
93
- # RINE_X402_AUTO_PAY=
94
- # RINE_FACILITATOR=
95
- `;
96
- }
97
- /**
98
- * Resolve a `--tools` selection to the ordered tool specs. Accepts `"all"`,
99
- * `"none"`, or a comma-separated list of tool names (`rine_send`) and/or domains
100
- * (`messaging`/`discovery`/`groups`). Unknown tokens throw.
101
- */
102
- function resolveToolSelection(value) {
103
- const v = (value ?? "all").trim();
104
- if (v === "none") return [];
105
- if (v === "all") return [...RINE_TOOL_META];
106
- const tokens = v.split(",").map((t) => t.trim()).filter(Boolean);
107
- const out = [];
108
- for (const spec of RINE_TOOL_META) if (tokens.includes(spec.name) || tokens.includes(spec.domain)) out.push(spec);
109
- const matchedDomains = new Set(RINE_TOOL_META.map((s) => s.domain));
110
- const matchedNames = new Set(RINE_TOOL_META.map((s) => s.name));
111
- 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)`);
112
- return out;
113
- }
114
- /** Write `content` to `file`, honoring `force`; record into the result. */
115
- function writeFile(file, content, force, res) {
116
- if (existsSync(file) && !force) {
117
- res.skipped.push(file);
118
- return;
119
- }
120
- mkdirSync(dirname(file), { recursive: true });
121
- writeFileSync(file, content, "utf-8");
122
- res.written.push(file);
123
- }
124
- /** Scaffold the rine channel, tools, skill, and `.env.example` block. */
125
- function scaffoldRine(opts = {}) {
126
- const cwd = opts.cwd ?? process.cwd();
127
- const agentDir = join(cwd, opts.dir ?? "agent");
128
- const path = opts.path ?? DEFAULT_INBOUND_PATH;
129
- const res = {
130
- written: [],
131
- skipped: []
132
- };
133
- const force = opts.force ?? false;
134
- if (opts.channel ?? true) writeFile(join(agentDir, "channels", "rine.ts"), channelFileContent(), force, res);
135
- for (const spec of resolveToolSelection(opts.tools)) writeFile(join(agentDir, "tools", `${spec.name}.ts`), toolFileContent(spec), force, res);
136
- writeFile(join(agentDir, "skills", "rine", "SKILL.md"), RINE_SKILL_FILE, force, res);
137
- const envFile = join(cwd, ".env.example");
138
- const block = envExampleBlock(path);
139
- const existing = existsSync(envFile) ? readFileSync(envFile, "utf-8") : "";
140
- if (!existing.includes(ENV_MARKER)) {
141
- writeFileSync(envFile, existing.length > 0 ? `${existing.trimEnd()}\n\n${block}` : block, "utf-8");
142
- res.written.push(envFile);
143
- } else res.skipped.push(envFile);
144
- return res;
145
- }
146
- //#endregion
147
- 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 };
@@ -1,276 +0,0 @@
1
- import { t as getRineClient } from "./client-DsG2xtKs.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.
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.
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
- /** `rine_group_join` outcome: immediate membership vs a pending vote. */
124
- function renderJoinResult(target, result) {
125
- if (result.status === "joined") return `Joined ${target}.`;
126
- return `Join request for ${target} submitted — pending approval (request ${result.request.id}).`;
127
- }
128
- /** One pending-invite row: group, inviter, status, and any message. */
129
- function renderInviteRow(inv) {
130
- const group = inv.group_handle ?? inv.group_name ?? inv.group_id;
131
- const from = inv.invited_by ? ` invited by ${inv.invited_by}` : "";
132
- const msg = inv.message ? `: "${inv.message}"` : "";
133
- return `${group} (status ${inv.status})${from}${msg}`;
134
- }
135
- /** A numbered list of the caller's pending group invites, or the empty-state line. */
136
- function renderInvites(items) {
137
- if (items.length === 0) return "No pending group invites.";
138
- return items.map((inv, i) => `${i + 1}. ${renderInviteRow(inv)}`).join("\n");
139
- }
140
- //#endregion
141
- //#region src/errors.ts
142
- /**
143
- * `formatError(err)` — turns any thrown SDK error into a readable
144
- * string for the LLM, never a stack trace. Tools wrap their one `await client.*`
145
- * call in `try/catch → formatError` and RESOLVE (never reject) for mapped errors.
146
- *
147
- * Ordering matters: the TS SDK error classes form a hierarchy rooted at
148
- * `RineApiError`, so this maps MOST-SPECIFIC FIRST and lets `RineApiError` be the
149
- * catch-all for API errors.
150
- */
151
- /** The synchronous message thrown by `sendAndWait` for a `#` group handle. */
152
- const GROUP_ON_WAIT_PREFIX = "sendAndWait() does not support group handles";
153
- /**
154
- * True when `err` is the plain `Error` `sendAndWait` throws for a group handle.
155
- * The `send_and_wait` tool checks this BEFORE `formatError` and returns the
156
- * "1:1 only" guidance; it is not a typed SDK error class.
157
- */
158
- function isGroupUnsupportedOnWait(err) {
159
- return err instanceof Error && err.message.startsWith(GROUP_ON_WAIT_PREFIX);
160
- }
161
- /** The fixed reply for the group-on-`sendAndWait` case. */
162
- const GROUP_ON_WAIT_MESSAGE = "rine_send_and_wait is 1:1 only; use rine_send for groups.";
163
- function formatError(err) {
164
- if (err instanceof ZodError) {
165
- const first = err.issues[0];
166
- return `Invalid input: ${first?.path.length ? first.path.join(".") : "input"} — ${first?.message ?? "validation failed"}.`;
167
- }
168
- if (err instanceof AuthenticationError) return "rine auth failed — onboard with `npx @rine-network/eve onboard` or set RINE_CONFIG_DIR to your credentials directory.";
169
- if (err instanceof AuthorizationError) return `Not authorized: ${err.detail}.`;
170
- if (err instanceof NotFoundError) return `Not found: ${err.detail}. Try rine_discover to find the right handle.`;
171
- if (err instanceof RateLimitError) return `Rate-limited; retry after ${err.retryAfter ?? "a few"}s.`;
172
- if (err instanceof ValidationError) return `Invalid input: ${err.detail}.`;
173
- if (err instanceof SchemaValidationError) return `Invalid input: ${err.message}.`;
174
- if (err instanceof RineTimeoutError) return "Request timed out; try again or raise the timeout.";
175
- if (err instanceof ConfigError) return String(err.message);
176
- if (err instanceof CryptoError) return `Encryption error: ${err.message} (check your agent keys are on disk).`;
177
- if (err instanceof APIConnectionError) return `Could not reach rine: ${err.message}.`;
178
- if (err instanceof RineApiError) return `${err.status}: ${err.detail}`;
179
- return String(messageOf(err));
180
- }
181
- function messageOf(err) {
182
- if (err instanceof Error) return err.message;
183
- return String(err ?? "unknown error");
184
- }
185
- //#endregion
186
- //#region src/tool.ts
187
- /**
188
- * Shared `defineTool` plumbing every rine tool reuses (no duplicated logic):
189
- * env-based identity resolution and the try/catch → `formatError` wrapper
190
- * (errors→strings, never a reject).
191
- *
192
- * Unlike the Mastra integration (which threads identity through a `RequestContext`),
193
- * Eve tools run in the app runtime with full `process.env` access, so identity is
194
- * resolved straight from the environment via `getRineClient` — no per-call context
195
- * plumbing, no credentials in the model-visible input schema.
196
- *
197
- * A tool's per-tool body is just `(client, input, apiUrl) => string`; this helper
198
- * supplies the surrounding contract. The concrete `defineTool({...})` (with the
199
- * model-facing description + schemas) lives in each domain module; this file only
200
- * builds the `execute` closure and the shared option/body types.
201
- */
202
- /**
203
- * Resolve {@link RineToolOpts.needsApproval} to an Eve `needsApproval` callback,
204
- * or `undefined` (no gate) when unset. Only the mutating tool factories apply it.
205
- */
206
- function approvalGate(opts) {
207
- switch (opts.needsApproval) {
208
- case "always": return always();
209
- case "once": return once();
210
- case "never": return never();
211
- default: return;
212
- }
213
- }
214
- /** The acting agent for a tool: factory override → `RINE_AGENT` env → none. */
215
- function resolveAgent(opts) {
216
- return opts.agent ?? process.env.RINE_AGENT ?? void 0;
217
- }
218
- /** Resolve the effective `AsyncRineClient` for one `execute` call (lazy). */
219
- function resolveClient(opts) {
220
- if (opts.client) {
221
- const agent = resolveAgent(opts);
222
- return agent ? opts.client.withAgent(agent) : opts.client;
223
- }
224
- return getRineClient({
225
- configDir: opts.configDir,
226
- apiUrl: opts.apiUrl,
227
- agent: resolveAgent(opts)
228
- });
229
- }
230
- /**
231
- * The same resolved `apiUrl` the client is built against (factory override →
232
- * `RINE_API_URL` env → `resolveApiUrl()`). Exposed so handle→UUID pre-resolution
233
- * hits the SAME server WebFinger the client uses.
234
- */
235
- function resolveApiUrlFor(opts) {
236
- return opts.apiUrl ?? resolveApiUrl();
237
- }
238
- /** Brand a model-supplied recipient string for the SDK send surface. */
239
- function asRecipient(to) {
240
- return to;
241
- }
242
- /**
243
- * Belt-and-suspenders ciphertext redactor for `read` / `check_inbox`.
244
- * The renderers + `outputSchema: z.string()` already guarantee the `execute`
245
- * return is redacted text; this coerces ANY value to plain text before it can
246
- * reach the model, in Eve's `ToolModelOutput` shape.
247
- */
248
- function redactToText(output) {
249
- return {
250
- type: "text",
251
- value: typeof output === "string" ? output : ""
252
- };
253
- }
254
- /**
255
- * Build an Eve-tool `execute(input, ctx)` from a zod schema + body, applying:
256
- * 1. zod `.parse(raw)` of the model input (defaults + coercion + validation),
257
- * 2. the lazy env-resolved client,
258
- * 3. the try/catch→formatError contract (resolves a string, never rejects).
259
- *
260
- * Eve hands `execute` the raw model input as `Record<string, unknown>`; we own the
261
- * parse because Eve receives a JSON Schema (not the zod schema) and may not apply
262
- * zod defaults. The Eve `ToolContext` is intentionally ignored — rine identity is
263
- * environment-resolved, not session-scoped.
264
- */
265
- function makeExecute(schema, opts, body) {
266
- return async (raw) => {
267
- try {
268
- const input = schema.parse(raw);
269
- return await body(resolveClient(opts), input, resolveApiUrlFor(opts));
270
- } catch (err) {
271
- return formatError(err);
272
- }
273
- };
274
- }
275
- //#endregion
276
- export { renderThread as _, GROUP_ON_WAIT_MESSAGE as a, verifiedNote as b, groupIsMls as c, renderInbox as d, renderInvites as f, renderSingleMessage as g, renderProfile as h, redactToText as i, renderDiscover as l, renderMessageBody as m, asRecipient as n, formatError as o, renderJoinResult as p, makeExecute as r, isGroupUnsupportedOnWait as s, approvalGate as t, renderGroup as u, renderThreadLine as v, senderLabel as y };