@soulcraft/sdk 1.0.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 (149) hide show
  1. package/dist/client/index.d.ts +62 -0
  2. package/dist/client/index.d.ts.map +1 -0
  3. package/dist/client/index.js +60 -0
  4. package/dist/client/index.js.map +1 -0
  5. package/dist/index.d.ts +33 -0
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js +17 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/modules/ai/index.d.ts +55 -0
  10. package/dist/modules/ai/index.d.ts.map +1 -0
  11. package/dist/modules/ai/index.js +263 -0
  12. package/dist/modules/ai/index.js.map +1 -0
  13. package/dist/modules/ai/types.d.ts +216 -0
  14. package/dist/modules/ai/types.d.ts.map +1 -0
  15. package/dist/modules/ai/types.js +30 -0
  16. package/dist/modules/ai/types.js.map +1 -0
  17. package/dist/modules/auth/backchannel.d.ts +85 -0
  18. package/dist/modules/auth/backchannel.d.ts.map +1 -0
  19. package/dist/modules/auth/backchannel.js +168 -0
  20. package/dist/modules/auth/backchannel.js.map +1 -0
  21. package/dist/modules/auth/config.d.ts +122 -0
  22. package/dist/modules/auth/config.d.ts.map +1 -0
  23. package/dist/modules/auth/config.js +158 -0
  24. package/dist/modules/auth/config.js.map +1 -0
  25. package/dist/modules/auth/middleware.d.ts +146 -0
  26. package/dist/modules/auth/middleware.d.ts.map +1 -0
  27. package/dist/modules/auth/middleware.js +204 -0
  28. package/dist/modules/auth/middleware.js.map +1 -0
  29. package/dist/modules/auth/types.d.ts +162 -0
  30. package/dist/modules/auth/types.d.ts.map +1 -0
  31. package/dist/modules/auth/types.js +14 -0
  32. package/dist/modules/auth/types.js.map +1 -0
  33. package/dist/modules/billing/types.d.ts +7 -0
  34. package/dist/modules/billing/types.d.ts.map +1 -0
  35. package/dist/modules/billing/types.js +7 -0
  36. package/dist/modules/billing/types.js.map +1 -0
  37. package/dist/modules/brainy/auth.d.ts +104 -0
  38. package/dist/modules/brainy/auth.d.ts.map +1 -0
  39. package/dist/modules/brainy/auth.js +144 -0
  40. package/dist/modules/brainy/auth.js.map +1 -0
  41. package/dist/modules/brainy/errors.d.ts +118 -0
  42. package/dist/modules/brainy/errors.d.ts.map +1 -0
  43. package/dist/modules/brainy/errors.js +142 -0
  44. package/dist/modules/brainy/errors.js.map +1 -0
  45. package/dist/modules/brainy/events.d.ts +63 -0
  46. package/dist/modules/brainy/events.d.ts.map +1 -0
  47. package/dist/modules/brainy/events.js +14 -0
  48. package/dist/modules/brainy/events.js.map +1 -0
  49. package/dist/modules/brainy/proxy.d.ts +48 -0
  50. package/dist/modules/brainy/proxy.d.ts.map +1 -0
  51. package/dist/modules/brainy/proxy.js +95 -0
  52. package/dist/modules/brainy/proxy.js.map +1 -0
  53. package/dist/modules/brainy/types.d.ts +83 -0
  54. package/dist/modules/brainy/types.d.ts.map +1 -0
  55. package/dist/modules/brainy/types.js +21 -0
  56. package/dist/modules/brainy/types.js.map +1 -0
  57. package/dist/modules/events/index.d.ts +41 -0
  58. package/dist/modules/events/index.d.ts.map +1 -0
  59. package/dist/modules/events/index.js +53 -0
  60. package/dist/modules/events/index.js.map +1 -0
  61. package/dist/modules/events/types.d.ts +129 -0
  62. package/dist/modules/events/types.d.ts.map +1 -0
  63. package/dist/modules/events/types.js +32 -0
  64. package/dist/modules/events/types.js.map +1 -0
  65. package/dist/modules/formats/types.d.ts +7 -0
  66. package/dist/modules/formats/types.d.ts.map +1 -0
  67. package/dist/modules/formats/types.js +7 -0
  68. package/dist/modules/formats/types.js.map +1 -0
  69. package/dist/modules/hall/types.d.ts +56 -0
  70. package/dist/modules/hall/types.d.ts.map +1 -0
  71. package/dist/modules/hall/types.js +16 -0
  72. package/dist/modules/hall/types.js.map +1 -0
  73. package/dist/modules/kits/types.d.ts +7 -0
  74. package/dist/modules/kits/types.d.ts.map +1 -0
  75. package/dist/modules/kits/types.js +7 -0
  76. package/dist/modules/kits/types.js.map +1 -0
  77. package/dist/modules/license/types.d.ts +7 -0
  78. package/dist/modules/license/types.d.ts.map +1 -0
  79. package/dist/modules/license/types.js +7 -0
  80. package/dist/modules/license/types.js.map +1 -0
  81. package/dist/modules/notifications/types.d.ts +7 -0
  82. package/dist/modules/notifications/types.d.ts.map +1 -0
  83. package/dist/modules/notifications/types.js +7 -0
  84. package/dist/modules/notifications/types.js.map +1 -0
  85. package/dist/modules/skills/index.d.ts +60 -0
  86. package/dist/modules/skills/index.d.ts.map +1 -0
  87. package/dist/modules/skills/index.js +253 -0
  88. package/dist/modules/skills/index.js.map +1 -0
  89. package/dist/modules/skills/types.d.ts +127 -0
  90. package/dist/modules/skills/types.d.ts.map +1 -0
  91. package/dist/modules/skills/types.js +23 -0
  92. package/dist/modules/skills/types.js.map +1 -0
  93. package/dist/modules/versions/types.d.ts +31 -0
  94. package/dist/modules/versions/types.d.ts.map +1 -0
  95. package/dist/modules/versions/types.js +9 -0
  96. package/dist/modules/versions/types.js.map +1 -0
  97. package/dist/modules/vfs/types.d.ts +26 -0
  98. package/dist/modules/vfs/types.d.ts.map +1 -0
  99. package/dist/modules/vfs/types.js +11 -0
  100. package/dist/modules/vfs/types.js.map +1 -0
  101. package/dist/server/create-sdk.d.ts +70 -0
  102. package/dist/server/create-sdk.d.ts.map +1 -0
  103. package/dist/server/create-sdk.js +125 -0
  104. package/dist/server/create-sdk.js.map +1 -0
  105. package/dist/server/hall-handlers.d.ts +195 -0
  106. package/dist/server/hall-handlers.d.ts.map +1 -0
  107. package/dist/server/hall-handlers.js +239 -0
  108. package/dist/server/hall-handlers.js.map +1 -0
  109. package/dist/server/handlers.d.ts +216 -0
  110. package/dist/server/handlers.d.ts.map +1 -0
  111. package/dist/server/handlers.js +214 -0
  112. package/dist/server/handlers.js.map +1 -0
  113. package/dist/server/index.d.ts +52 -0
  114. package/dist/server/index.d.ts.map +1 -0
  115. package/dist/server/index.js +50 -0
  116. package/dist/server/index.js.map +1 -0
  117. package/dist/server/instance-pool.d.ts +299 -0
  118. package/dist/server/instance-pool.d.ts.map +1 -0
  119. package/dist/server/instance-pool.js +359 -0
  120. package/dist/server/instance-pool.js.map +1 -0
  121. package/dist/transports/http.d.ts +86 -0
  122. package/dist/transports/http.d.ts.map +1 -0
  123. package/dist/transports/http.js +134 -0
  124. package/dist/transports/http.js.map +1 -0
  125. package/dist/transports/local.d.ts +76 -0
  126. package/dist/transports/local.d.ts.map +1 -0
  127. package/dist/transports/local.js +101 -0
  128. package/dist/transports/local.js.map +1 -0
  129. package/dist/transports/sse.d.ts +99 -0
  130. package/dist/transports/sse.d.ts.map +1 -0
  131. package/dist/transports/sse.js +192 -0
  132. package/dist/transports/sse.js.map +1 -0
  133. package/dist/transports/transport.d.ts +68 -0
  134. package/dist/transports/transport.d.ts.map +1 -0
  135. package/dist/transports/transport.js +14 -0
  136. package/dist/transports/transport.js.map +1 -0
  137. package/dist/transports/ws.d.ts +135 -0
  138. package/dist/transports/ws.d.ts.map +1 -0
  139. package/dist/transports/ws.js +331 -0
  140. package/dist/transports/ws.js.map +1 -0
  141. package/dist/types.d.ts +152 -0
  142. package/dist/types.d.ts.map +1 -0
  143. package/dist/types.js +8 -0
  144. package/dist/types.js.map +1 -0
  145. package/docs/ADR-001-sdk-design.md +282 -0
  146. package/docs/IMPLEMENTATION-PLAN.md +708 -0
  147. package/docs/USAGE.md +646 -0
  148. package/docs/kit-sdk-guide.md +474 -0
  149. package/package.json +61 -0
@@ -0,0 +1,474 @@
1
+ # Kit SDK Integration Guide
2
+
3
+ This guide is for developers building applications with Soulcraft application kits —
4
+ whether from scratch, extending an existing kit, or integrating with AI assistance.
5
+ It covers how to use `@soulcraft/sdk` to give kit-based applications access to Brainy,
6
+ VFS, AI (Claude), skills, auth, and events.
7
+
8
+ ---
9
+
10
+ ## What Are Kits?
11
+
12
+ Kits (`@soulcraft/kits`) are pure **configuration** — `kit.json` manifests and
13
+ `SKILL.md` prompt files. They describe a domain, persona, glossary, and workflow.
14
+ They contain no TypeScript logic, no server code, and no SDK imports.
15
+
16
+ The **application code** that loads and runs a kit is what uses the SDK. That code
17
+ lives in Workshop, Venue, Academy, or your own custom product.
18
+
19
+ ```
20
+ @soulcraft/kits → Domain config (kit.json, SKILL.md)
21
+ @soulcraft/sdk → Runtime: Brainy, AI, auth, events, skills
22
+ Your application code → Wires the two together
23
+ ```
24
+
25
+ ---
26
+
27
+ ## Getting Started
28
+
29
+ Install the SDK in your server application:
30
+
31
+ ```bash
32
+ bun add @soulcraft/sdk @soulcraft/brainy
33
+ ```
34
+
35
+ For server-side use (Node.js, Bun, Hono, SvelteKit server routes):
36
+
37
+ ```typescript
38
+ import { BrainyInstancePool, createSDK, createAuthMiddleware } from '@soulcraft/sdk/server'
39
+ import type { SoulcraftSessionUser } from '@soulcraft/sdk'
40
+ ```
41
+
42
+ For browser/client use (kit app components, SvelteKit frontend):
43
+
44
+ ```typescript
45
+ import { createBrainyProxy, HttpTransport, WsTransport } from '@soulcraft/sdk/client'
46
+ ```
47
+
48
+ ---
49
+
50
+ ## Loading a Kit and Accessing Brainy
51
+
52
+ Every kit application needs a Brainy instance to store and retrieve its graph data.
53
+ Use `BrainyInstancePool` on the server to manage instance lifecycle, then `createSDK`
54
+ to get the full SDK namespace:
55
+
56
+ ```typescript
57
+ import { BrainyInstancePool, createSDK } from '@soulcraft/sdk/server'
58
+ import { kitRegistry } from '@soulcraft/kits'
59
+
60
+ // One pool per deployment — typically a module singleton
61
+ const pool = new BrainyInstancePool({
62
+ storage: process.env.STORAGE_TYPE === 'mmap-filesystem' ? 'mmap-filesystem' : 'filesystem',
63
+ dataPath: process.env.BRAINY_DATA_PATH ?? './brainy-data',
64
+ strategy: 'per-tenant', // per-user for Workshop, per-tenant for Venue, per-scope for custom
65
+ maxInstances: 50,
66
+ flushOnEvict: true,
67
+ })
68
+
69
+ // Load the kit config
70
+ const kit = kitRegistry['wicks-and-whiskers']
71
+ console.log(kit.name) // → "Wicks & Whiskers"
72
+
73
+ // In a request handler: get the Brainy instance for this tenant
74
+ const brain = await pool.forTenant('wicks-charlotte')
75
+
76
+ // Create the SDK — wraps the brain instance with all namespaces
77
+ const sdk = createSDK({ brain })
78
+
79
+ // Use Brainy directly through the SDK:
80
+ const candles = await sdk.brainy.find({ query: 'candle inventory' })
81
+
82
+ // Or use the raw brain instance directly (same thing, different style):
83
+ const candles2 = await brain.find({ query: 'candle inventory' })
84
+ ```
85
+
86
+ ---
87
+
88
+ ## Storing Kit Data in Brainy
89
+
90
+ Kit entities are Brainy graph nodes. Shape your entity types using the noun types
91
+ your kit defines in its `glossary` and `aiExpertise`:
92
+
93
+ ```typescript
94
+ // Add a product entity from a Venue kit
95
+ await sdk.brainy.add({
96
+ id: 'product-lavender-soy',
97
+ type: 'Product',
98
+ metadata: {
99
+ name: 'Lavender Soy Candle',
100
+ price: 28.00,
101
+ category: 'candles',
102
+ stock: 45,
103
+ kitId: 'wicks-and-whiskers',
104
+ }
105
+ })
106
+
107
+ // Relate it to a category
108
+ await sdk.brainy.relate({
109
+ from: 'product-lavender-soy',
110
+ to: 'category-candles',
111
+ type: 'BelongsTo',
112
+ })
113
+
114
+ // Find similar products using semantic search
115
+ const similar = await sdk.brainy.find({
116
+ query: 'soy candle lavender scent',
117
+ type: 'Product',
118
+ limit: 10,
119
+ })
120
+ ```
121
+
122
+ ---
123
+
124
+ ## Using the VFS for Kit Files
125
+
126
+ Kits that work with documents, code, or media use the Virtual Filesystem (VFS).
127
+ Access it through `sdk.vfs.*`:
128
+
129
+ ```typescript
130
+ // Write a generated document to the VFS
131
+ await sdk.vfs.writeFile('/projects/my-project/README.md', readmeContent)
132
+
133
+ // Read it back — returns a Buffer; convert to string for text:
134
+ const buffer = await sdk.vfs.readFile('/projects/my-project/README.md')
135
+ const content = buffer.toString('utf-8')
136
+
137
+ // List the project directory
138
+ const entries = await sdk.vfs.readdir('/projects/my-project')
139
+ // → ['README.md', 'package.json', 'src']
140
+
141
+ // File info
142
+ const stat = await sdk.vfs.stat('/projects/my-project/README.md')
143
+
144
+ // Rename / delete
145
+ await sdk.vfs.rename('/projects/old-name', '/projects/new-name')
146
+ await sdk.vfs.unlink('/projects/my-project/old-file.md')
147
+ ```
148
+
149
+ ---
150
+
151
+ ## Loading Skills
152
+
153
+ Skills (`SKILL.md`) define the AI persona, capabilities, and workflow steps for
154
+ a kit. Load them from the VFS or the bundled `@soulcraft/kits` registry:
155
+
156
+ ```typescript
157
+ // Load a single skill (VFS first, bundled registry fallback):
158
+ const skill = await sdk.skills.load('inventory-health', 'wicks-and-whiskers')
159
+ if (skill) {
160
+ // Use skill.content (the full SKILL.md string) to build a system prompt
161
+ console.log(skill.source) // 'vfs' or 'bundled'
162
+ }
163
+
164
+ // Load all skills for a kit:
165
+ const skills = await sdk.skills.list({ kitId: 'wicks-and-whiskers' })
166
+ const systemPrompt = buildSystemPrompt(kit, skills.map(s => s.content))
167
+
168
+ // Install a custom skill into the VFS:
169
+ await sdk.skills.install({
170
+ id: 'custom-upsell-flow',
171
+ kitId: 'wicks-and-whiskers',
172
+ content: `---
173
+ id: custom-upsell-flow
174
+ title: Candle Upsell Flow
175
+ ---
176
+ You are an expert at recommending complementary candle products...`,
177
+ })
178
+ ```
179
+
180
+ For the lower-level approach (loading directly from the bundled kits package):
181
+
182
+ ```typescript
183
+ import { skillRegistry } from '@soulcraft/kits'
184
+
185
+ // skillRegistry['wicks-and-whiskers'] → Record<skillId, content>
186
+ const kitSkills = skillRegistry['wicks-and-whiskers']
187
+ const systemPrompt = buildSystemPrompt(kit, Object.values(kitSkills ?? {}))
188
+ ```
189
+
190
+ ---
191
+
192
+ ## AI Integration (Claude)
193
+
194
+ Use the AI module to interact with Claude in the context of a kit's domain:
195
+
196
+ ```typescript
197
+ import { AI_MODELS } from '@soulcraft/sdk'
198
+
199
+ // Invoke the AI with the kit's domain context
200
+ const response = await sdk.ai.complete({
201
+ messages: [{ role: 'user', content: 'What candles are low in stock?' }],
202
+ systemPrompt: kit.shared.aiPersona, // kit.json → shared.aiPersona
203
+ model: AI_MODELS.haiku, // Haiku for fast queries
204
+ tools: [
205
+ {
206
+ name: 'search_inventory',
207
+ description: 'Search Brainy for inventory data',
208
+ inputSchema: {
209
+ type: 'object',
210
+ properties: { query: { type: 'string' } },
211
+ required: ['query'],
212
+ },
213
+ }
214
+ ],
215
+ })
216
+
217
+ // Handle tool calls — execute the tool and continue the conversation
218
+ if (response.toolCalls) {
219
+ for (const call of response.toolCalls) {
220
+ if (call.name === 'search_inventory') {
221
+ const results = await sdk.brainy.find({
222
+ query: call.input.query as string,
223
+ type: 'Product',
224
+ })
225
+ // Append a tool_result message and call sdk.ai.complete() again
226
+ }
227
+ }
228
+ }
229
+
230
+ // Streaming — useful for long responses:
231
+ for await (const event of sdk.ai.stream({
232
+ messages: [{ role: 'user', content: 'Write a product description for lavender candles' }],
233
+ systemPrompt: kit.shared.aiPersona,
234
+ model: AI_MODELS.sonnet,
235
+ })) {
236
+ if (event.type === 'text') process.stdout.write(event.text)
237
+ if (event.type === 'done') console.log('Tokens used:', event.result.usage)
238
+ }
239
+ ```
240
+
241
+ ---
242
+
243
+ ## Events
244
+
245
+ Subscribe to and emit platform events across the kit application:
246
+
247
+ ```typescript
248
+ // Subscribe to Brainy change events
249
+ sdk.events.on('brainy:change', (event) => {
250
+ if (event.event === 'add' && event.entity?.nounType === 'Product') {
251
+ updateInventoryCache(event.entity)
252
+ }
253
+ })
254
+
255
+ // Subscribe to VFS events for live document awareness
256
+ sdk.events.on('vfs:write', ({ path, content }) => {
257
+ if (path.startsWith('/projects/')) {
258
+ notifyCollaborators(path)
259
+ }
260
+ })
261
+
262
+ // Emit custom application events (declare the type first via declaration merging)
263
+ sdk.events.emit('kit:session-completed', {
264
+ userId: user.id,
265
+ sessionId,
266
+ kitId: 'wicks-and-whiskers',
267
+ })
268
+ ```
269
+
270
+ **Note:** The event bus is local to the SDK instance. Events do not cross process
271
+ boundaries. For real-time cross-client events, use the WebSocket transport's `onDataChange`
272
+ or broadcast via your own WebSocket/SSE channel.
273
+
274
+ ---
275
+
276
+ ## Authentication
277
+
278
+ Kit applications that need user authentication use `createAuthMiddleware` from the
279
+ SDK, wired to a better-auth instance:
280
+
281
+ ```typescript
282
+ import { betterAuth } from 'better-auth'
283
+ import {
284
+ SOULCRAFT_USER_FIELDS,
285
+ SOULCRAFT_SESSION_CONFIG,
286
+ computeEmailHash,
287
+ createAuthMiddleware,
288
+ } from '@soulcraft/sdk/server'
289
+
290
+ const auth = betterAuth({
291
+ database: new Database('./auth.db'),
292
+ secret: process.env.BETTER_AUTH_SECRET!,
293
+ session: SOULCRAFT_SESSION_CONFIG,
294
+ user: { additionalFields: SOULCRAFT_USER_FIELDS },
295
+ databaseHooks: {
296
+ user: {
297
+ create: {
298
+ before: async (user) => ({
299
+ data: { ...user, emailHash: computeEmailHash(user.email), platformRole: 'creator' }
300
+ })
301
+ }
302
+ }
303
+ },
304
+ })
305
+
306
+ const { requireAuth, optionalAuth } = createAuthMiddleware(auth)
307
+
308
+ // Protect kit routes
309
+ app.get('/api/kit-data', requireAuth, async (c) => {
310
+ const user = c.get('user')! // SoulcraftSessionUser — id, email, emailHash, platformRole
311
+ const brain = await pool.forUser(user.emailHash, 'my-workspace')
312
+ const sdk = createSDK({ brain })
313
+ // ...
314
+ })
315
+ ```
316
+
317
+ **Auth modes:**
318
+ - **Standalone** (dev default, `SOULCRAFT_IDP_URL` not set): better-auth handles auth locally
319
+ - **OIDC** (production, `SOULCRAFT_IDP_URL=https://auth.soulcraft.com`): central IdP handles all auth
320
+
321
+ ---
322
+
323
+ ## Capability Tokens (Cross-Product Access)
324
+
325
+ When a kit application needs to read from another product's Brainy (e.g. Workshop
326
+ reads from Venue), use capability tokens:
327
+
328
+ ```typescript
329
+ import { createCapabilityToken, verifyCapabilityToken } from '@soulcraft/sdk/server'
330
+
331
+ // Server A: Issue a scoped token
332
+ const token = await createCapabilityToken({
333
+ email: user.email,
334
+ scope: 'wicks-charlotte',
335
+ ttlMs: 3600 * 1000,
336
+ secret: process.env.CAPABILITY_TOKEN_SECRET!,
337
+ })
338
+
339
+ // Server B: Verify it in the RPC handler
340
+ const claims = await verifyCapabilityToken(token, process.env.CAPABILITY_TOKEN_SECRET!)
341
+ if (!claims) return c.json({ error: 'Unauthorized' }, 401)
342
+ ```
343
+
344
+ ---
345
+
346
+ ## Full Application Example
347
+
348
+ A minimal kit application combining auth, Brainy, and AI:
349
+
350
+ ```typescript
351
+ import { Hono } from 'hono'
352
+ import { betterAuth } from 'better-auth'
353
+ import { Database } from 'bun:sqlite'
354
+ import {
355
+ BrainyInstancePool,
356
+ createSDK,
357
+ createAuthMiddleware,
358
+ SOULCRAFT_USER_FIELDS,
359
+ SOULCRAFT_SESSION_CONFIG,
360
+ computeEmailHash,
361
+ } from '@soulcraft/sdk/server'
362
+ import { AI_MODELS } from '@soulcraft/sdk'
363
+ import { kitRegistry } from '@soulcraft/kits'
364
+
365
+ // ── Kit config ────────────────────────────────────────────────────────────────
366
+ const kit = kitRegistry['wicks-and-whiskers']
367
+
368
+ // ── Auth ──────────────────────────────────────────────────────────────────────
369
+ const auth = betterAuth({
370
+ database: new Database('./auth.db'),
371
+ secret: process.env.BETTER_AUTH_SECRET!,
372
+ session: SOULCRAFT_SESSION_CONFIG,
373
+ user: { additionalFields: SOULCRAFT_USER_FIELDS },
374
+ databaseHooks: {
375
+ user: {
376
+ create: {
377
+ before: async (u) => ({
378
+ data: { ...u, emailHash: computeEmailHash(u.email), platformRole: 'creator' }
379
+ })
380
+ }
381
+ }
382
+ },
383
+ })
384
+ const { requireAuth } = createAuthMiddleware(auth)
385
+
386
+ // ── Brainy pool ───────────────────────────────────────────────────────────────
387
+ const pool = new BrainyInstancePool({
388
+ storage: 'filesystem',
389
+ dataPath: './brainy-data',
390
+ strategy: 'per-user',
391
+ maxInstances: 100,
392
+ })
393
+
394
+ // ── Hono app ──────────────────────────────────────────────────────────────────
395
+ const app = new Hono()
396
+
397
+ app.all('/api/auth/*', (c) => auth.handler(c.req.raw))
398
+
399
+ app.get('/api/inventory', requireAuth, async (c) => {
400
+ const user = c.get('user')!
401
+ const brain = await pool.forUser(user.emailHash, 'main')
402
+ const sdk = createSDK({ brain })
403
+
404
+ const items = await sdk.brainy.find({
405
+ query: 'inventory items in stock',
406
+ type: 'Product',
407
+ limit: 50,
408
+ })
409
+
410
+ return c.json({ kit: kit.name, items })
411
+ })
412
+
413
+ app.post('/api/ask', requireAuth, async (c) => {
414
+ const user = c.get('user')!
415
+ const { question } = await c.req.json<{ question: string }>()
416
+
417
+ const brain = await pool.forUser(user.emailHash, 'main')
418
+ const sdk = createSDK({ brain })
419
+
420
+ const skills = await sdk.skills.list({ kitId: 'wicks-and-whiskers' })
421
+ const systemPrompt = [
422
+ kit.shared.aiPersona,
423
+ ...skills.map(s => s.content),
424
+ ].join('\n\n')
425
+
426
+ const response = await sdk.ai.complete({
427
+ messages: [{ role: 'user', content: question }],
428
+ systemPrompt,
429
+ model: AI_MODELS.haiku,
430
+ })
431
+
432
+ return c.json({ answer: response.text })
433
+ })
434
+
435
+ export default app
436
+ ```
437
+
438
+ ---
439
+
440
+ ## Instance Strategies
441
+
442
+ Choose the pooling strategy that fits your kit application:
443
+
444
+ | Strategy | When to use | Pool method |
445
+ |----------|-------------|-------------|
446
+ | `per-user` | One Brainy per user (Workshop pattern) | `pool.forUser(emailHash, workspaceId)` |
447
+ | `per-tenant` | One Brainy per organization/location (Venue pattern) | `pool.forTenant(slug)` |
448
+ | `per-scope` | Custom key — Academy's content/learner split, multi-branch, etc. | `pool.forScope(key, factory)` |
449
+
450
+ Use `per-scope` when:
451
+ - Your kit has multiple instance types (e.g. Academy's content brain vs learner brain)
452
+ - You need multi-branch support (e.g. `userId:workspaceId:branch` key)
453
+ - You need post-init logic in the factory (migrations, integrity checks)
454
+
455
+ ---
456
+
457
+ ## Kit API Reference
458
+
459
+ | Package | What it provides |
460
+ |---------|-----------------|
461
+ | `@soulcraft/kits` | `kitRegistry`, `skillRegistry`, per-kit TypeScript types |
462
+ | `@soulcraft/sdk` | Shared types, `AI_MODELS`, error classes |
463
+ | `@soulcraft/sdk/server` | `BrainyInstancePool`, `createSDK`, `createAuthMiddleware`, `createBrainyHandler`, `createBrainyWsHandler`, `createBackchannelLogoutHandler`, `computeEmailHash`, `createCapabilityToken`, `verifyCapabilityToken`, `SOULCRAFT_USER_FIELDS`, `SOULCRAFT_SESSION_CONFIG` |
464
+ | `@soulcraft/sdk/client` | `createBrainyProxy`, `HttpTransport`, `WsTransport`, `SseTransport` |
465
+ | `@soulcraft/brainy` | `Brainy` class (peer dep — version must match server) |
466
+
467
+ ---
468
+
469
+ ## See Also
470
+
471
+ - `docs/USAGE.md` — Full SDK usage reference for all namespaces
472
+ - `docs/ADR-001-sdk-design.md` — Architecture decision record
473
+ - `@soulcraft/kit-schema` — Kit manifest JSON schema and TypeScript types
474
+ - `@soulcraft/kits` — Kit registry with 57+ domain kits
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@soulcraft/sdk",
3
+ "version": "1.0.0",
4
+ "description": "The unified Soulcraft platform SDK — data, auth, AI, billing, and notifications",
5
+ "type": "module",
6
+ "publishConfig": {
7
+ "access": "restricted",
8
+ "registry": "https://registry.npmjs.org"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "docs",
13
+ "README.md"
14
+ ],
15
+ "exports": {
16
+ ".": {
17
+ "types": "./dist/index.d.ts",
18
+ "import": "./dist/index.js"
19
+ },
20
+ "./server": {
21
+ "types": "./dist/server/index.d.ts",
22
+ "import": "./dist/server/index.js"
23
+ },
24
+ "./client": {
25
+ "types": "./dist/client/index.d.ts",
26
+ "import": "./dist/client/index.js"
27
+ }
28
+ },
29
+ "scripts": {
30
+ "build": "tsc",
31
+ "dev": "tsc --watch",
32
+ "test": "vitest run",
33
+ "test:watch": "vitest",
34
+ "check": "tsc --noEmit",
35
+ "lint": "eslint src/",
36
+ "publish:npm": "npm publish --access restricted"
37
+ },
38
+ "peerDependencies": {
39
+ "@soulcraft/brainy": ">=7.17.0",
40
+ "@soulcraft/cortex": ">=2.1.5",
41
+ "@soulcraft/hall": ">=0.1.0"
42
+ },
43
+ "peerDependenciesMeta": {
44
+ "@soulcraft/hall": {
45
+ "optional": true
46
+ }
47
+ },
48
+ "dependencies": {
49
+ "@anthropic-ai/sdk": "^0.39.0",
50
+ "@msgpack/msgpack": "^3.0.0",
51
+ "better-auth": "^1.0.0",
52
+ "lru-cache": "^11.0.0"
53
+ },
54
+ "devDependencies": {
55
+ "@soulcraft/hall": "link:@soulcraft/hall",
56
+ "@types/node": "^22.0.0",
57
+ "hono": "^4.12.3",
58
+ "typescript": "^5.7.0",
59
+ "vitest": "^3.0.0"
60
+ }
61
+ }