@oneie/sdk 0.8.0 → 0.10.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 (92) hide show
  1. package/LICENSE +18 -0
  2. package/README.md +336 -190
  3. package/dist/auth.d.ts +102 -0
  4. package/dist/auth.d.ts.map +1 -0
  5. package/dist/auth.js +156 -0
  6. package/dist/auth.js.map +1 -0
  7. package/dist/brain.d.ts +36 -0
  8. package/dist/brain.d.ts.map +1 -0
  9. package/dist/brain.js +99 -0
  10. package/dist/brain.js.map +1 -0
  11. package/dist/client.d.ts +222 -30
  12. package/dist/client.d.ts.map +1 -1
  13. package/dist/client.js +339 -173
  14. package/dist/client.js.map +1 -1
  15. package/dist/compile.d.ts +123 -0
  16. package/dist/compile.d.ts.map +1 -0
  17. package/dist/compile.js +652 -0
  18. package/dist/compile.js.map +1 -0
  19. package/dist/fetch.d.ts +40 -0
  20. package/dist/fetch.d.ts.map +1 -0
  21. package/dist/fetch.js +158 -0
  22. package/dist/fetch.js.map +1 -0
  23. package/dist/generated/types.d.ts +104 -0
  24. package/dist/generated/types.d.ts.map +1 -0
  25. package/dist/generated/types.js +5 -0
  26. package/dist/generated/types.js.map +1 -0
  27. package/dist/index.d.ts +17 -1
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +11 -1
  30. package/dist/index.js.map +1 -1
  31. package/dist/meta.d.ts +45 -0
  32. package/dist/meta.d.ts.map +1 -0
  33. package/dist/meta.js +44 -0
  34. package/dist/meta.js.map +1 -0
  35. package/dist/openapi.d.ts +27 -0
  36. package/dist/openapi.d.ts.map +1 -0
  37. package/dist/openapi.js +62 -0
  38. package/dist/openapi.js.map +1 -0
  39. package/dist/pay.d.ts +2 -2
  40. package/dist/pay.d.ts.map +1 -1
  41. package/dist/pay.js +5 -5
  42. package/dist/pay.js.map +1 -1
  43. package/dist/receivers.d.ts +743 -0
  44. package/dist/receivers.d.ts.map +1 -0
  45. package/dist/receivers.js +626 -0
  46. package/dist/receivers.js.map +1 -0
  47. package/dist/schemas.d.ts +39 -0
  48. package/dist/schemas.d.ts.map +1 -1
  49. package/dist/schemas.js +26 -0
  50. package/dist/schemas.js.map +1 -1
  51. package/dist/skills.d.ts +40 -0
  52. package/dist/skills.d.ts.map +1 -0
  53. package/dist/skills.js +16 -0
  54. package/dist/skills.js.map +1 -0
  55. package/dist/soul.d.ts +10 -0
  56. package/dist/soul.d.ts.map +1 -0
  57. package/dist/soul.js +31 -0
  58. package/dist/soul.js.map +1 -0
  59. package/dist/storage.d.ts +7 -8
  60. package/dist/storage.d.ts.map +1 -1
  61. package/dist/storage.js +61 -33
  62. package/dist/storage.js.map +1 -1
  63. package/dist/telemetry.d.ts +9 -0
  64. package/dist/telemetry.d.ts.map +1 -1
  65. package/dist/telemetry.js +11 -0
  66. package/dist/telemetry.js.map +1 -1
  67. package/dist/testing/index.d.ts.map +1 -1
  68. package/dist/testing/index.js +4 -3
  69. package/dist/testing/index.js.map +1 -1
  70. package/dist/types.d.ts +113 -22
  71. package/dist/types.d.ts.map +1 -1
  72. package/package.json +43 -17
  73. package/dist/react/context.d.ts +0 -11
  74. package/dist/react/context.d.ts.map +0 -1
  75. package/dist/react/context.js +0 -13
  76. package/dist/react/context.js.map +0 -1
  77. package/dist/react/hooks.d.ts +0 -104
  78. package/dist/react/hooks.d.ts.map +0 -1
  79. package/dist/react/hooks.js +0 -247
  80. package/dist/react/hooks.js.map +0 -1
  81. package/dist/react/index.d.ts +0 -6
  82. package/dist/react/index.d.ts.map +0 -1
  83. package/dist/react/index.js +0 -5
  84. package/dist/react/index.js.map +0 -1
  85. package/dist/react/optimistic.d.ts +0 -45
  86. package/dist/react/optimistic.d.ts.map +0 -1
  87. package/dist/react/optimistic.js +0 -52
  88. package/dist/react/optimistic.js.map +0 -1
  89. package/dist/react/stream.d.ts +0 -18
  90. package/dist/react/stream.d.ts.map +0 -1
  91. package/dist/react/stream.js +0 -60
  92. package/dist/react/stream.js.map +0 -1
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @oneie/sdk
2
2
 
3
- Client SDK for the ONE substrate — signals, units, persist, launch handoff.
3
+ TypeScript SDK for the ONE substrate — signal routing, agent registration, skill discovery, pheromone paths, and agent-to-agent payments.
4
4
 
5
5
  ```bash
6
6
  npm install @oneie/sdk
@@ -9,91 +9,143 @@ npm install @oneie/sdk
9
9
  ## Quick Start
10
10
 
11
11
  ```typescript
12
- import { getApiUrl, resolveApiKey } from "@oneie/sdk/urls";
13
- import * as storage from "@oneie/sdk/storage";
12
+ import { SubstrateClient } from "@oneie/sdk";
14
13
 
15
- // Resolve base URL (env: ONEIE_API_URL, default: https://api.one.ie)
16
- const url = getApiUrl();
14
+ const client = SubstrateClient.fromApiKey(process.env.ONEIE_API_KEY);
17
15
 
18
- // Storage CRUD
19
- await storage.put("my-key", { hello: "world" }, { apiKey: resolveApiKey() });
20
- const value = await storage.get("my-key");
21
- await storage.del("my-key");
16
+ // Send a signal and wait for response
17
+ const outcome = await client.ask("tutor:explain", { topic: "TypeScript" });
18
+ if (outcome.kind === "result") {
19
+ console.log("Got:", outcome.result);
20
+ } else if (outcome.kind === "timeout") {
21
+ console.log("Timed out");
22
+ }
23
+
24
+ // Mark a successful path to strengthen routing
25
+ await client.mark("tutor→learner", { fit: 1, form: 1, truth: 1, taste: 1 });
26
+
27
+ // View the highest-strength paths (highways)
28
+ const { highways } = await client.highways(10);
29
+ highways.forEach(h => console.log(`${h.path}: ${h.net}`));
22
30
  ```
23
31
 
24
32
  ## Modules
25
33
 
26
34
  | Import | What |
27
35
  |--------|------|
28
- | `@oneie/sdk` | Types + all re-exports |
36
+ | `@oneie/sdk` | Main client + types + all re-exports |
29
37
  | `@oneie/sdk/urls` | `getApiUrl()`, `resolveApiKey()`, `resolveBaseUrl()` |
30
38
  | `@oneie/sdk/storage` | `get()`, `put()`, `del()`, `list()` — `/api/storage/*` |
31
- | `@oneie/sdk/launch` | `launchToken()` — generate agent launch tokens |
39
+ | `@oneie/sdk/launch` | `launchToken()` — generate agent launch tokens on Sui/EVM |
32
40
  | `@oneie/sdk/handoff` | Token handoff helpers |
41
+ | `@oneie/sdk/compile` | `compileAgent()`, `parse()` — agent markdown → Python / MCP / SKILL.md |
42
+ | `@oneie/sdk/react` | `useAgent()`, `useDiscover()`, `useHighways()`, `streamChat()` — React hooks |
43
+ | `@oneie/sdk/testing` | `createMockSubstrate()` — mock client for tests |
44
+ | `@oneie/sdk/errors` | Error types: `AuthError`, `RateLimitError`, `ValidationError`, etc. |
45
+ | `@oneie/sdk/schemas` | Zod schemas for response validation |
46
+ | `@oneie/sdk/receivers` | `RECEIVERS` catalog + `RECIPES` + `receiver()` + `ReqOf`/`ResOf` — one typed source of capability |
47
+ | `@oneie/sdk/meta` | `metaCatalog()`, `metaRecipe()`, `metaSchema()` — the self-describing surface over `RECEIVERS` |
48
+ | `@oneie/sdk/openapi` | `buildReceiverPayload()` — generate the receiver `oneOf` for `openapi.yaml` (`bun run generate:openapi`) |
33
49
 
34
- ## Types
50
+ ## Core Client
51
+
52
+ ### Initialize
35
53
 
36
54
  ```typescript
37
- import type { SdkConfig, OneSdkError, Outcome } from "@oneie/sdk";
55
+ import { SubstrateClient } from "@oneie/sdk";
56
+
57
+ // From env: ONEIE_API_KEY (required), ONEIE_API_URL (optional, default https://api.one.ie)
58
+ const client = SubstrateClient.fromApiKey(process.env.ONEIE_API_KEY);
38
59
 
39
- // Outcome the 4-result type from the substrate
40
- type Outcome<T> =
41
- | { result: T } // success
42
- | { timeout: true } // slow, not bad
43
- | { dissolved: true } // missing unit/capability
60
+ // Or explicit config
61
+ const client = new SubstrateClient({
62
+ apiKey: "api_...",
63
+ baseUrl: "https://api.one.ie",
64
+ retry: { maxAttempts: 3, backoff: "exp" },
65
+ validate: "strict" // or "warn" (default) or "off"
66
+ });
44
67
  ```
45
68
 
46
- ## Environment Variables
69
+ ### The 6 Verbs
47
70
 
48
- | Variable | Default | Purpose |
49
- |----------|---------|---------|
50
- | `ONEIE_API_URL` | `https://api.one.ie` | Substrate API base URL |
51
- | `ONEIE_API_KEY` | — | Bearer token |
71
+ **Signal & Response:**
52
72
 
53
- ## License
73
+ ```typescript
74
+ // One-way signal (no response expected)
75
+ const sig = await client.signal("sender", "recipient", { data: "..." });
54
76
 
55
- [one.ie/free-license](https://one.ie/free-license)
77
+ // Signal and wait for response — returns one of 4 outcomes
78
+ const outcome = await client.ask("tutor:explain", { topic: "TypeScript" }, timeout=5000);
56
79
 
57
- ## Telemetry
80
+ switch (outcome.kind) {
81
+ case "result": console.log("Success:", outcome.result); break;
82
+ case "timeout": console.log("Timed out"); break;
83
+ case "dissolved": console.log("No handler (missing unit or capability)"); break;
84
+ case "failure": console.log("Handler returned nothing"); break;
85
+ }
86
+ ```
58
87
 
59
- `@oneie/sdk` sends anonymous usage signals to the ONE substrate to improve routing quality.
88
+ **Path Strength (Pheromone):**
60
89
 
61
- **What we send:** package version, method name, outcome type, anonymous session ID (hex hash — no PII), call latency.
90
+ ```typescript
91
+ // Strengthen a path (success feedback)
92
+ await client.mark("tutor→learner", { fit: 1, form: 1, truth: 1, taste: 1 });
62
93
 
63
- **What we never send:** your API key, user IDs, email addresses, file paths, or any personally identifiable information.
94
+ // Weaken a path (failure feedback)
95
+ await client.warn("tutor→learner", { fit: 0, form: 0, truth: 0, taste: 0 });
64
96
 
65
- **Opt out:**
66
- ```bash
67
- # Environment variable (per-session)
68
- ONEIE_TELEMETRY_DISABLE=1 node your-script.js
97
+ // Decay all paths (asymmetric: resistance forgives 2x faster)
98
+ const { before, after, decayed } = await client.fade(trailRate=0.05, resistanceRate=0.10);
69
99
 
70
- # Permanent opt-out
71
- echo '{"telemetry":false}' > ~/.oneie/config.json
100
+ // Highest-strength paths
101
+ const { highways } = await client.highways(limit=10);
72
102
  ```
73
103
 
74
- When opt-out is active, `oneie --version` prints `telemetry: disabled`.
104
+ **Memory & Learning:**
75
105
 
76
- ---
106
+ ```typescript
107
+ // Strongest path for a relationship type
108
+ const best = await client.follow("teach");
109
+
110
+ // Hardened hypotheses (learned patterns)
111
+ const { hypotheses } = await client.recall(status="promoted");
112
+
113
+ // Reveal stored memory for a unit
114
+ const memory = await client.reveal("tutor:42");
77
115
 
78
- ## Cycle 1 Methods — Identity, Commerce, Observability
116
+ // Forget memory (deletion)
117
+ await client.forget("tutor:42");
118
+
119
+ // Frontier: what's being explored
120
+ const frontier = await client.frontier("tutor:42");
121
+
122
+ // Promote highways to hypotheses
123
+ await client.know();
124
+ ```
79
125
 
80
- ### Authentication
126
+ ### Agent Management
127
+
128
+ **Auth & Registration:**
81
129
 
82
130
  ```typescript
83
131
  // Register or retrieve an agent identity
84
- const agent = await client.authAgent({ name: "tutor", kind: "agent" });
132
+ const agent = await client.authAgent({
133
+ name: "tutor",
134
+ kind: "agent"
135
+ });
85
136
  // { uid, name, kind, wallet, apiKey, keyId, returning }
86
137
 
87
- if (!agent.returning) {
88
- console.log("New agent created:", agent.uid);
89
- console.log("API key:", agent.apiKey);
90
- }
138
+ // Register a new agent with capabilities
139
+ const reg = await client.register("marketing:alice", {
140
+ kind: "agent",
141
+ capabilities: [{ skill: "copywriting", price: 0.05 }]
142
+ });
91
143
  ```
92
144
 
93
- ### Agent Sync
145
+ **Deploy Agents from Markdown:**
94
146
 
95
147
  ```typescript
96
- // Deploy a single agent from markdown
148
+ // Single agent
97
149
  const result = await client.syncAgent(`---
98
150
  name: tutor
99
151
  model: meta-llama/llama-4-maverick
@@ -104,47 +156,39 @@ skills:
104
156
  You are a patient tutor.`);
105
157
  // { ok, uid, wallet, skills }
106
158
 
107
- // Deploy a world (multiple agents)
159
+ // Multiple agents (world)
108
160
  const world = await client.syncAgent({
109
161
  world: "marketing",
110
162
  agents: [
111
- { name: "director", content: "---\nname: director\n---\nYou are the director." }
163
+ { name: "director", content: "---\nname: director\n---\nYou are the director." },
164
+ { name: "copywriter", content: "..." }
112
165
  ]
113
166
  });
114
167
  // { ok, world, agents: [{ uid, name, skills }] }
115
168
  ```
116
169
 
117
- ### Discover
170
+ **Discovery & Actions:**
118
171
 
119
172
  ```typescript
120
- // Find agents with a specific skill
173
+ // Find agents with a skill
121
174
  const { agents } = await client.discover("teach", 5);
122
175
  // agents: [{ uid, name, price, successRate, strength }]
123
- ```
124
-
125
- ### Register
126
176
 
127
- ```typescript
128
- // Register an agent with capabilities
129
- const reg = await client.register("marketing:alice", {
130
- kind: "agent",
131
- capabilities: [{ skill: "copywriting", price: 0.05 }]
132
- });
133
- // { ok, uid, status: "registered", walletLinked, capabilities: 1 }
134
- ```
177
+ // Commend an agent (strengthen its path)
178
+ await client.commend("marketing:alice");
135
179
 
136
- ### Pay
180
+ // Flag an agent (weaken its path)
181
+ await client.flag("marketing:alice");
137
182
 
138
- ```typescript
139
- // Send a payment between agents (legacy weight-rail API)
140
- const payment = await client.payWeight("marketing:alice", "tutor:alice", "task-123", 0.05);
141
- // { ok, from, to, task, amount, sui: string | null }
142
- // sui is null for off-chain fast-path, a digest for on-chain
183
+ // Set agent status
184
+ await client.status("marketing:alice", true); // activate
185
+ await client.status("marketing:alice", false); // deactivate
143
186
 
144
- // Note: `client.payWeight(from, to, task, amount)` is the legacy weight-rail single-call API (Sui-direct). For card/crypto rails that flow through `pay.one.ie`, use the `client.pay.accept` / `client.pay.request` / `client.pay.status` namespace.
187
+ // List agent capabilities
188
+ const caps = await client.capabilities("marketing:alice");
145
189
  ```
146
190
 
147
- ### Claw (Edge Deployment)
191
+ **Edge Deployment:**
148
192
 
149
193
  ```typescript
150
194
  // Deploy a NanoClaw edge worker for an agent (requires session auth)
@@ -152,130 +196,161 @@ const claw = await client.claw("tutor", { persona: "one" });
152
196
  // { ok, workerUrl, apiKey }
153
197
  ```
154
198
 
155
- ### Agent Actions
199
+ **Publish / Pull / Unpublish:**
156
200
 
157
- ```typescript
158
- // Commend a well-performing agent (strengthens pheromone path)
159
- await client.commend("marketing:alice");
160
- // { ok, id, action: "commend" }
201
+ Round-trip an authored `agent.md` to a workspace's R2 bucket. After publish the
202
+ agent is live at `https://<slug>.one.ie/chat?agent=<name>` and on
203
+ `/studio/<name>`. Auth uses Bearer `<slug>:<token>` for owner-scoped writes.
161
204
 
162
- // Flag a misbehaving agent (weakens pheromone path)
163
- await client.flag("marketing:alice");
164
- // { ok, id, action: "flag" }
205
+ ```typescript
206
+ // Upload an authored agent.md
207
+ const pub = await client.publishAgent({
208
+ slug: "acme",
209
+ name: "marketing-strategist",
210
+ content: readFileSync("marketing-strategist/agent.md", "utf8"),
211
+ });
212
+ // { ok, name, slug, bytes, url, key }
165
213
 
166
- // Set agent lifecycle status
167
- await client.status("marketing:alice", false); // deactivate
168
- await client.status("marketing:alice", true); // activate
169
- // { ok, id, status: "active" | "inactive" }
214
+ // Pull the live version back (e.g. to diff before next publish)
215
+ const got = await client.pullAgent({ slug: "acme", name: "marketing-strategist" });
216
+ // { ok, slug, name, content, bytes, key }
170
217
 
171
- // List an agent's registered capabilities
172
- const caps = await client.capabilities("marketing:alice");
173
- // CapabilityItem[]
218
+ // Remove a published agent idempotent
219
+ const del = await client.unpublishAgent({ slug: "acme", name: "marketing-strategist" });
220
+ // { ok, removed: true, key } ← false if already gone
174
221
  ```
175
222
 
223
+ Same flow available on the CLI (`oneie agent publish | pull | unpublish`) and
224
+ MCP (`publish_agent`, `pull_agent`, `unpublish_agent`).
225
+
176
226
  ### Observability
177
227
 
178
228
  ```typescript
179
229
  // Substrate-wide stats
180
230
  const stats = await client.stats();
181
- // { units: { total, proven, atRisk }, skills, highways, revenue, signals, timestamp }
231
+ // { units, skills, highways, revenue, signals, timestamp }
182
232
 
183
233
  // Health check
184
234
  const health = await client.health();
185
- // { status: "healthy" | "degraded", world: { units, agents, edges, ... }, version }
235
+ // { status: "healthy" | "degraded", world: {...}, version }
186
236
  if (health.status === "degraded") console.warn("Substrate degraded");
187
237
  ```
188
238
 
189
- ---
190
-
191
- ## Cycle 2 — Type Safety: Zod Schemas, Error Hierarchy, Retry
239
+ ## Compile Module
192
240
 
193
- ### Error Hierarchy
241
+ Compile agent markdown to Python (uAgents), MCP (Claude/Cursor), or SKILL.md (Claude Code).
194
242
 
195
243
  ```typescript
196
- import { SubstrateError, AuthError, RateLimitError, ValidationError } from "@oneie/sdk/errors";
197
-
198
- try {
199
- await client.authAgent();
200
- } catch (err) {
201
- if (err instanceof AuthError) console.error("Auth failed:", err.status);
202
- if (err instanceof RateLimitError) console.error("Rate limited, retry after:", err.retryAfterMs);
203
- if (err instanceof ValidationError) console.error("Bad request:", err.body);
204
- if (err instanceof SubstrateError) console.error("Substrate error:", err.code);
244
+ import { compileAgent, parse } from "@oneie/sdk/compile";
245
+ import { readFileSync, readdirSync, writeFileSync } from "node:fs";
246
+
247
+ // Parse agent markdown into structured metadata + prompt
248
+ const { meta, prompt } = parse(readFileSync("agents/tutor.md", "utf8"));
249
+ // meta: { name, model, skills, description, ... }
250
+ // prompt: string
251
+
252
+ // Load all skills
253
+ const skills: Record<string, string> = {};
254
+ for (const f of readdirSync("skills")) {
255
+ if (f.endsWith(".md")) {
256
+ const name = f.replace(".md", "");
257
+ skills[name] = readFileSync(`skills/${f}`, "utf8");
258
+ }
205
259
  }
206
- ```
207
260
 
208
- ### Retry Configuration
261
+ // Compile to Python (uAgents Protocol)
262
+ const py = compileAgent(readFileSync("agents/tutor.md", "utf8"), {
263
+ skills,
264
+ target: "uagents" // default
265
+ });
266
+ writeFileSync("dist/tutor_agent.py", py);
209
267
 
210
- ```typescript
211
- const client = new SubstrateClient({
212
- apiKey: "...",
213
- retry: { maxAttempts: 3, backoff: "exp" } // retries 503, 429, 502, 504
268
+ // Compile to MCP (for Claude/Cursor)
269
+ const mcp = compileAgent(readFileSync("agents/tutor.md", "utf8"), {
270
+ skills,
271
+ target: "mcp"
214
272
  });
273
+ // Returns JSON: { name, title, version, tools: [...] }
215
274
 
216
- // Or use the static factory
217
- const client = SubstrateClient.fromApiKey("api_...");
275
+ // Compile to SKILL.md (for Claude Code)
276
+ const skillMd = compileAgent(readFileSync("agents/tutor.md", "utf8"), {
277
+ skills,
278
+ target: "skillmd"
279
+ });
280
+ // Returns markdown with all skills concatenated
218
281
  ```
219
282
 
220
- ### Zod Schemas
283
+ **Agent Markdown Format:**
221
284
 
222
- ```typescript
223
- import { HealthSchema, StatsSchema, AuthAgentResponseSchema } from "@oneie/sdk/schemas";
285
+ ```markdown
286
+ ---
287
+ name: tutor
288
+ title: Tutoring Agent
289
+ model: meta-llama/llama-4-maverick
290
+ skills:
291
+ - name: teach
292
+ title: Teach a Topic
293
+ description: Explain a complex topic clearly
294
+ price: 0.01
295
+ inputSchema:
296
+ type: object
297
+ properties:
298
+ topic: { type: string }
299
+ level: { type: string }
300
+ required: [topic]
301
+ outputSchema:
302
+ type: object
303
+ properties:
304
+ explanation: { type: string }
305
+ examples: { type: array }
306
+ version: 1.0.0
307
+ ---
224
308
 
225
- // Parse and validate responses manually
226
- const raw = await fetch("/api/health").then(r => r.json());
227
- const health = HealthSchema.parse(raw); // throws ZodError on mismatch
228
- // health.status is "healthy" | "degraded" — fully inferred
309
+ You are a patient, expert tutor. Explain concepts step by step.
229
310
  ```
230
311
 
231
- ### Outcome<T> with kind
312
+ ## Pay Module
232
313
 
233
- ```typescript
234
- const outcome = await client.ask("tutor:teach", { topic: "TypeScript" });
235
-
236
- switch (outcome.kind) {
237
- case "result": console.log("Got:", outcome.result); break;
238
- case "timeout": console.log("Timed out"); break;
239
- case "dissolved": console.log("No handler"); break;
240
- case "failure": console.log("Handler failed"); break;
241
- }
242
- ```
243
-
244
- ### Validation Mode
314
+ Accept payments, request payments, check status.
245
315
 
246
316
  ```typescript
247
- // strict: throw ValidationError if response shape mismatches schema
248
- // warn: log mismatch but return data (default)
249
- // off: skip validation entirely (perf-optimized)
250
- const client = new SubstrateClient({ validate: "strict" });
251
- ```
317
+ // Create a payment link (skill buyer)
318
+ const { linkUrl, qr, intent } = await client.pay.accept({
319
+ skill: "copywriting",
320
+ price: 25,
321
+ rail: "card" | "crypto" | "auto",
322
+ memo: "Invoice #123"
323
+ });
252
324
 
253
- ---
325
+ // Request payment (agent → agent)
326
+ const { linkUrl, status } = await client.pay.request({
327
+ to: "seller-uid",
328
+ amount: 10,
329
+ memo: "Work completed"
330
+ });
254
331
 
255
- ## Cycle 3 — React Integration: Hooks, Streams, Test Helpers
332
+ // Check payment status
333
+ const { status, ref, amount, rail } = await client.pay.status(ref);
334
+ ```
256
335
 
257
- ### Setup
336
+ Backed by `/api/pay/*`, routed through `pay.one.ie` (crypto) or Stripe (card). Emits `toolkit:sdk:pay:*` telemetry.
258
337
 
259
- ```tsx
260
- import { SubstrateClient } from "@oneie/sdk";
261
- import { SubstrateProvider } from "@oneie/sdk/react";
338
+ ## React Hooks
262
339
 
263
- const client = SubstrateClient.fromApiKey(process.env.ONEIE_API_KEY!);
340
+ ```typescript
341
+ import { SubstrateProvider, useAgent, useDiscover, useHighways } from "@oneie/sdk/react";
264
342
 
343
+ // Provider setup
265
344
  function App() {
345
+ const client = SubstrateClient.fromApiKey(process.env.ONEIE_API_KEY!);
266
346
  return (
267
347
  <SubstrateProvider client={client}>
268
348
  <MyApp />
269
349
  </SubstrateProvider>
270
350
  );
271
351
  }
272
- ```
273
-
274
- ### Data Hooks
275
-
276
- ```tsx
277
- import { useAgent, useDiscover, useHighways } from "@oneie/sdk/react";
278
352
 
353
+ // Fetch agent data
279
354
  function AgentProfile({ uid }: { uid: string }) {
280
355
  const { data, loading, error, refetch } = useAgent(uid);
281
356
  if (loading) return <div>Loading…</div>;
@@ -283,40 +358,33 @@ function AgentProfile({ uid }: { uid: string }) {
283
358
  return <pre>{JSON.stringify(data, null, 2)}</pre>;
284
359
  }
285
360
 
361
+ // Discover agents by skill
362
+ function FindTeachers() {
363
+ const { data } = useDiscover("teach", 10);
364
+ return (
365
+ <ul>
366
+ {data?.agents.map(a => (
367
+ <li key={a.uid}>{a.name} (strength: {a.strength})</li>
368
+ ))}
369
+ </ul>
370
+ );
371
+ }
372
+
373
+ // Top paths (highways)
286
374
  function TopPaths() {
287
375
  const { data, refetch } = useHighways(10);
288
376
  return (
289
377
  <>
290
378
  <button onClick={refetch}>Refresh</button>
291
- {data?.highways.map(h => <div key={h.path}>{h.path}: {h.net}</div>)}
379
+ {data?.highways.map(h => (
380
+ <div key={h.path}>{h.path}: {h.net}</div>
381
+ ))}
292
382
  </>
293
383
  );
294
384
  }
295
- ```
296
-
297
- ### Optimistic Updates
298
385
 
299
- ```tsx
300
- import { useOptimisticMark } from "@oneie/sdk/react";
301
-
302
- function MarkButton({ edge }: { edge: string }) {
303
- const { optimistic, mark } = useOptimisticMark();
304
- return (
305
- <button
306
- disabled={optimistic.pending}
307
- onClick={() => mark(edge, { fit: 1, form: 1, truth: 1, taste: 1 })}
308
- >
309
- {optimistic.pending ? "Marking…" : "Mark ✓"}
310
- </button>
311
- );
312
- }
313
- ```
314
-
315
- ### Streaming Chat
316
-
317
- ```tsx
318
- import { streamChat, useSubstrate } from "@oneie/sdk/react";
319
- import { useState } from "react";
386
+ // Streaming chat
387
+ import { streamChat } from "@oneie/sdk/react";
320
388
 
321
389
  function Chat() {
322
390
  const { client } = useSubstrate();
@@ -338,40 +406,118 @@ function Chat() {
338
406
  }
339
407
  ```
340
408
 
341
- ### Test Helpers
409
+ ## Error Handling
410
+
411
+ ```typescript
412
+ import { SubstrateError, AuthError, RateLimitError, ValidationError } from "@oneie/sdk/errors";
413
+
414
+ try {
415
+ await client.ask("tutor:teach", { topic: "TypeScript" });
416
+ } catch (err) {
417
+ if (err instanceof AuthError) console.error("Auth failed:", err.status);
418
+ if (err instanceof RateLimitError) console.error("Rate limited, retry after:", err.retryAfterMs);
419
+ if (err instanceof ValidationError) console.error("Bad request:", err.body);
420
+ if (err instanceof SubstrateError) console.error("Substrate error:", err.code);
421
+ }
422
+ ```
423
+
424
+ ## Validation & Type Safety
425
+
426
+ ```typescript
427
+ import { HealthSchema, StatsSchema, HighwaysSchema } from "@oneie/sdk/schemas";
428
+
429
+ // Parse and validate responses (strict mode)
430
+ const client = new SubstrateClient({ validate: "strict" });
431
+ const health = HealthSchema.parse(await fetch("/api/health").then(r => r.json()));
432
+ // Throws ZodError on mismatch; fully typed result
433
+
434
+ // Or validate manually
435
+ const raw = await fetch("/api/highways?limit=10").then(r => r.json());
436
+ const highways = HighwaysSchema.parse(raw);
437
+ ```
438
+
439
+ ## Testing
342
440
 
343
441
  ```typescript
344
442
  import { createMockSubstrate } from "@oneie/sdk/testing";
345
443
 
346
444
  const client = createMockSubstrate({
347
- highways: () => Promise.resolve({ highways: [{ path: "a→b", strength: 5, resistance: 1, net: 4 }] })
445
+ highways: () => Promise.resolve({
446
+ highways: [{ path: "a→b", strength: 5, resistance: 1, net: 4 }]
447
+ }),
448
+ ask: async (receiver, data) => ({
449
+ kind: "result" as const,
450
+ result: { echo: data },
451
+ latency: 10
452
+ })
348
453
  });
349
454
 
350
- const result = await client.highways();
351
- // result.highways[0].path === "a→b"
455
+ const { highways } = await client.highways();
456
+ console.assert(highways[0].path === "a→b");
352
457
  ```
353
458
 
354
- ---
459
+ ## Storage
460
+
461
+ Simple key-value storage (backed by `/api/storage/*`).
462
+
463
+ ```typescript
464
+ import * as storage from "@oneie/sdk/storage";
355
465
 
356
- ## Pay
466
+ // Requires apiKey in env or explicit config
467
+ const value = { hello: "world" };
468
+ await storage.put("my-key", value, { apiKey: resolveApiKey() });
357
469
 
358
- Three verbs for agent-to-agent payments:
470
+ const retrieved = await storage.get("my-key");
471
+ console.log(retrieved); // { hello: "world" }
472
+
473
+ const list = await storage.list("prefix-");
474
+ await storage.del("my-key");
475
+ ```
476
+
477
+ ## Configuration
478
+
479
+ **Environment Variables:**
480
+
481
+ | Variable | Default | Purpose |
482
+ |----------|---------|---------|
483
+ | `ONEIE_API_URL` | `https://api.one.ie` | Substrate API base URL |
484
+ | `ONEIE_API_KEY` | — | Bearer token for authenticated endpoints |
485
+ | `ONEIE_TELEMETRY_DISABLE` | — | Set to `1` to opt out of usage signals |
486
+
487
+ **Config Object:**
359
488
 
360
489
  ```typescript
361
- const { linkUrl, qr, intent } = await sdk.pay.accept({
362
- skill: "my-skill",
363
- price: 25,
364
- rail: "card" | "crypto" | "auto",
365
- memo: "optional note"
366
- })
490
+ const client = new SubstrateClient({
491
+ apiKey: "api_...",
492
+ baseUrl: "https://dev.one.ie",
493
+ retry: {
494
+ maxAttempts: 3,
495
+ backoff: "exp" // or "linear", "fixed"
496
+ },
497
+ validate: "strict" // or "warn" (default), "off"
498
+ });
499
+ ```
367
500
 
368
- const { linkUrl, status } = await sdk.pay.request({
369
- to: "seller-uid",
370
- amount: 10,
371
- memo: "invoice #1"
372
- })
501
+ ## Telemetry
502
+
503
+ `@oneie/sdk` sends anonymous usage signals to the ONE substrate to improve routing quality.
504
+
505
+ **What we send:** package version, method name, outcome type, anonymous session ID (hex hash — no PII), call latency.
373
506
 
374
- const { status, ref, amount, rail } = await sdk.pay.status(ref)
507
+ **What we never send:** API key, user IDs, email addresses, file paths, or any personally identifiable information.
508
+
509
+ **Opt out:**
510
+
511
+ ```bash
512
+ # Per-session
513
+ ONEIE_TELEMETRY_DISABLE=1 node your-script.js
514
+
515
+ # Permanent
516
+ echo '{"telemetry":false}' > ~/.oneie/config.json
375
517
  ```
376
518
 
377
- Each call emits `toolkit:sdk:pay:<method>` telemetry. Backed by `/api/pay/create-link` and `/api/pay/status/:ref`, which route through `pay.one.ie` (crypto) or Stripe (card). ADL gates apply on the server side. See [one/pay-todo.md](../../one/pay-todo.md).
519
+ When opt-out is active, the SDK logs `telemetry: disabled`.
520
+
521
+ ## License
522
+
523
+ [one.ie/free-license](https://one.ie/free-license)