@soulcraft/sdk 1.4.6 → 1.4.7
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.
- package/dist/modules/formats/types.d.ts +6 -21
- package/dist/modules/formats/types.d.ts.map +1 -1
- package/dist/modules/formats/types.js +6 -21
- package/dist/modules/formats/types.js.map +1 -1
- package/dist/modules/kits/index.d.ts.map +1 -1
- package/dist/modules/kits/index.js +34 -13
- package/dist/modules/kits/index.js.map +1 -1
- package/dist/modules/kits/types.d.ts +66 -2
- package/dist/modules/kits/types.d.ts.map +1 -1
- package/docs/ADR-001-sdk-design.md +28 -28
- package/docs/ADR-002-transport-protocol.md +248 -0
- package/docs/ADR-003-instance-strategies.md +216 -0
- package/docs/IMPLEMENTATION-PLAN.md +22 -40
- package/docs/KIT-APP-GUIDE.md +932 -0
- package/package.json +1 -1
|
@@ -2,9 +2,12 @@
|
|
|
2
2
|
* @module formats/types
|
|
3
3
|
* @description Soulcraft portable content format type definitions.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* These types mirror `@soulcraft/formats` exactly. Once `@soulcraft/formats`
|
|
6
|
+
* ships compiled `.d.ts` output (rather than raw `.ts` source), this module
|
|
7
|
+
* can be replaced with simple re-exports. Until then, the types live here
|
|
8
|
+
* to avoid pulling `@soulcraft/formats`' Zod schemas into the SDK's type-check.
|
|
9
|
+
*
|
|
10
|
+
* See: handoff action "Remove duplicate format types from SDK src/modules/formats/"
|
|
8
11
|
*
|
|
9
12
|
* | Format | Extension | Purpose |
|
|
10
13
|
* |---------|------------------|----------------------------------------------|
|
|
@@ -12,24 +15,6 @@
|
|
|
12
15
|
* | WDOC | `.wdoc` | Rich text documents (TipTap/ProseMirror) |
|
|
13
16
|
* | WSLIDE | `.wslide.json` | Slide deck presentations |
|
|
14
17
|
* | WQUIZ | `.wquiz.json` | Assessments and quizzes |
|
|
15
|
-
*
|
|
16
|
-
* Formats contain no runtime behavior — they are schema contracts. Products
|
|
17
|
-
* read them from the VFS, parse with `JSON.parse()`, and cast to the appropriate
|
|
18
|
-
* interface. The `SoulcraftFormat` discriminant on each document's `format` field
|
|
19
|
-
* enables safe narrowing at runtime.
|
|
20
|
-
*
|
|
21
|
-
* @example
|
|
22
|
-
* ```typescript
|
|
23
|
-
* import type { WdocDocument, WvizDocument, SoulcraftFormatDocument } from '@soulcraft/sdk'
|
|
24
|
-
*
|
|
25
|
-
* // Read from VFS and narrow to the correct type
|
|
26
|
-
* const buf = await sdk.vfs.readFile('/projects/notes.wdoc')
|
|
27
|
-
* const doc = JSON.parse(buf.toString('utf-8')) as SoulcraftFormatDocument
|
|
28
|
-
* if (doc.format === 'wdoc') {
|
|
29
|
-
* const wdoc = doc as WdocDocument
|
|
30
|
-
* console.log(wdoc.meta.title)
|
|
31
|
-
* }
|
|
32
|
-
* ```
|
|
33
18
|
*/
|
|
34
19
|
export type { WvizDocument, WvizEntity, WvizEdge, WvizSnapshot, WvizViewType, } from './wviz.js';
|
|
35
20
|
export type { WdocDocument, WdocMeta, WdocBlock, WdocInlineContent, WdocTextNode, WdocMark, WdocParagraphBlock, WdocHeadingBlock, WdocBlockquoteBlock, WdocCodeBlock, WdocBulletListBlock, WdocOrderedListBlock, WdocListItemBlock, WdocTaskListBlock, WdocTaskItemBlock, WdocImageBlock, WdocVideoBlock, WdocIconBlock, WdocSvgEmbedBlock, WdocEmbedBlock, WdocTableBlock, WdocTableRowBlock, WdocTableCellBlock, WdocTableHeaderBlock, WdocHorizontalRuleBlock, WdocCalloutBlock, } from './wdoc.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/modules/formats/types.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/modules/formats/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,YAAY,EACV,YAAY,EACZ,UAAU,EACV,QAAQ,EACR,YAAY,EACZ,YAAY,GACb,MAAM,WAAW,CAAA;AAElB,YAAY,EACV,YAAY,EACZ,QAAQ,EACR,SAAS,EACT,iBAAiB,EACjB,YAAY,EACZ,QAAQ,EACR,kBAAkB,EAClB,gBAAgB,EAChB,mBAAmB,EACnB,aAAa,EACb,mBAAmB,EACnB,oBAAoB,EACpB,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,cAAc,EACd,cAAc,EACd,aAAa,EACb,iBAAiB,EACjB,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,kBAAkB,EAClB,oBAAoB,EACpB,uBAAuB,EACvB,gBAAgB,GACjB,MAAM,WAAW,CAAA;AAElB,YAAY,EACV,cAAc,EACd,WAAW,EACX,gBAAgB,EAChB,YAAY,EACZ,gBAAgB,EAChB,YAAY,GACb,MAAM,aAAa,CAAA;AAEpB,YAAY,EACV,aAAa,EACb,aAAa,EACb,iBAAiB,EACjB,WAAW,EACX,iBAAiB,GAClB,MAAM,YAAY,CAAA;AAMnB;;;;;;;GAOG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAA;AAElE;;;;;;;;GAQG;AACH,eAAO,MAAM,iBAAiB,8CAAoF,CAAA;AAElH;;;;;GAKG;AACH,MAAM,WAAW,uBAAuB;IACtC,wEAAwE;IACxE,MAAM,EAAE,eAAe,CAAA;IACvB,4CAA4C;IAC5C,OAAO,EAAE,MAAM,CAAA;CAChB"}
|
|
@@ -2,9 +2,12 @@
|
|
|
2
2
|
* @module formats/types
|
|
3
3
|
* @description Soulcraft portable content format type definitions.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* These types mirror `@soulcraft/formats` exactly. Once `@soulcraft/formats`
|
|
6
|
+
* ships compiled `.d.ts` output (rather than raw `.ts` source), this module
|
|
7
|
+
* can be replaced with simple re-exports. Until then, the types live here
|
|
8
|
+
* to avoid pulling `@soulcraft/formats`' Zod schemas into the SDK's type-check.
|
|
9
|
+
*
|
|
10
|
+
* See: handoff action "Remove duplicate format types from SDK src/modules/formats/"
|
|
8
11
|
*
|
|
9
12
|
* | Format | Extension | Purpose |
|
|
10
13
|
* |---------|------------------|----------------------------------------------|
|
|
@@ -12,24 +15,6 @@
|
|
|
12
15
|
* | WDOC | `.wdoc` | Rich text documents (TipTap/ProseMirror) |
|
|
13
16
|
* | WSLIDE | `.wslide.json` | Slide deck presentations |
|
|
14
17
|
* | WQUIZ | `.wquiz.json` | Assessments and quizzes |
|
|
15
|
-
*
|
|
16
|
-
* Formats contain no runtime behavior — they are schema contracts. Products
|
|
17
|
-
* read them from the VFS, parse with `JSON.parse()`, and cast to the appropriate
|
|
18
|
-
* interface. The `SoulcraftFormat` discriminant on each document's `format` field
|
|
19
|
-
* enables safe narrowing at runtime.
|
|
20
|
-
*
|
|
21
|
-
* @example
|
|
22
|
-
* ```typescript
|
|
23
|
-
* import type { WdocDocument, WvizDocument, SoulcraftFormatDocument } from '@soulcraft/sdk'
|
|
24
|
-
*
|
|
25
|
-
* // Read from VFS and narrow to the correct type
|
|
26
|
-
* const buf = await sdk.vfs.readFile('/projects/notes.wdoc')
|
|
27
|
-
* const doc = JSON.parse(buf.toString('utf-8')) as SoulcraftFormatDocument
|
|
28
|
-
* if (doc.format === 'wdoc') {
|
|
29
|
-
* const wdoc = doc as WdocDocument
|
|
30
|
-
* console.log(wdoc.meta.title)
|
|
31
|
-
* }
|
|
32
|
-
* ```
|
|
33
18
|
*/
|
|
34
19
|
/**
|
|
35
20
|
* All Soulcraft format strings as a const array — useful for validation.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/modules/formats/types.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/modules/formats/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAsEH;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,CAA+C,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/modules/kits/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,OAAO,KAAK,EAAsB,UAAU,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/modules/kits/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,OAAO,KAAK,EAAsB,UAAU,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAA;AAgDzF;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,GAAE,uBAA4B,GAAG,UAAU,CAoClF"}
|
|
@@ -23,25 +23,28 @@ import { createRequire } from 'node:module';
|
|
|
23
23
|
// createRequire enables CJS-style require() inside an ESM module — the only
|
|
24
24
|
// reliable way to conditionally load optional peer dependencies in Node/Bun ESM.
|
|
25
25
|
const _require = createRequire(import.meta.url);
|
|
26
|
-
|
|
27
|
-
let _registryCache = undefined;
|
|
26
|
+
let _kitsPackage = undefined;
|
|
28
27
|
/**
|
|
29
|
-
*
|
|
30
|
-
* Returns `null` when the package is not installed
|
|
31
|
-
* degrade gracefully rather than throwing.
|
|
28
|
+
* Lazily load the `@soulcraft/kits` optional peer dependency.
|
|
29
|
+
* Returns `null` when the package is not installed; callers degrade gracefully.
|
|
32
30
|
*/
|
|
33
|
-
function
|
|
34
|
-
if (
|
|
35
|
-
return
|
|
31
|
+
function _loadKitsPackage() {
|
|
32
|
+
if (_kitsPackage !== undefined)
|
|
33
|
+
return _kitsPackage;
|
|
36
34
|
try {
|
|
37
|
-
|
|
38
|
-
const kits = _require('@soulcraft/kits');
|
|
39
|
-
_registryCache = kits.kitRegistry ?? null;
|
|
35
|
+
_kitsPackage = _require('@soulcraft/kits');
|
|
40
36
|
}
|
|
41
37
|
catch {
|
|
42
|
-
|
|
38
|
+
_kitsPackage = null;
|
|
43
39
|
}
|
|
44
|
-
return
|
|
40
|
+
return _kitsPackage;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Load the kit registry from the installed `@soulcraft/kits` package.
|
|
44
|
+
* Returns `null` when the package is not installed.
|
|
45
|
+
*/
|
|
46
|
+
function _getBundledRegistry() {
|
|
47
|
+
return _loadKitsPackage()?.kitRegistry ?? null;
|
|
45
48
|
}
|
|
46
49
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
47
50
|
// Factory
|
|
@@ -80,6 +83,24 @@ export function createKitsModule(options = {}) {
|
|
|
80
83
|
return [];
|
|
81
84
|
return Object.values(registry);
|
|
82
85
|
},
|
|
86
|
+
async resolveFilesPath(kitId, product) {
|
|
87
|
+
const pkg = _loadKitsPackage();
|
|
88
|
+
if (!pkg?.resolveKitFilesPath)
|
|
89
|
+
return null;
|
|
90
|
+
return pkg.resolveKitFilesPath(kitId, product);
|
|
91
|
+
},
|
|
92
|
+
async resolveSkillsPath(kitId) {
|
|
93
|
+
const pkg = _loadKitsPackage();
|
|
94
|
+
if (!pkg?.resolveKitSkillsPath)
|
|
95
|
+
return null;
|
|
96
|
+
return pkg.resolveKitSkillsPath(kitId);
|
|
97
|
+
},
|
|
98
|
+
async resolveKitsRoot() {
|
|
99
|
+
const pkg = _loadKitsPackage();
|
|
100
|
+
if (!pkg?.resolveKitsRoot)
|
|
101
|
+
return null;
|
|
102
|
+
return pkg.resolveKitsRoot();
|
|
103
|
+
},
|
|
83
104
|
};
|
|
84
105
|
}
|
|
85
106
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/modules/kits/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAG3C,4EAA4E;AAC5E,iFAAiF;AACjF,MAAM,QAAQ,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/modules/kits/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAG3C,4EAA4E;AAC5E,iFAAiF;AACjF,MAAM,QAAQ,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AAgB/C,IAAI,YAAY,GAAmC,SAAS,CAAA;AAE5D;;;GAGG;AACH,SAAS,gBAAgB;IACvB,IAAI,YAAY,KAAK,SAAS;QAAE,OAAO,YAAY,CAAA;IACnD,IAAI,CAAC;QACH,YAAY,GAAG,QAAQ,CAAC,iBAAiB,CAAgB,CAAA;IAC3D,CAAC;IAAC,MAAM,CAAC;QACP,YAAY,GAAG,IAAI,CAAA;IACrB,CAAC;IACD,OAAO,YAAY,CAAA;AACrB,CAAC;AAED;;;GAGG;AACH,SAAS,mBAAmB;IAC1B,OAAO,gBAAgB,EAAE,EAAE,WAAW,IAAI,IAAI,CAAA;AAChD,CAAC;AAED,gFAAgF;AAChF,UAAU;AACV,gFAAgF;AAEhF;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,gBAAgB,CAAC,UAAmC,EAAE;IACpE,MAAM,WAAW,GAAG,OAAO,CAAC,eAAe,KAAK,SAAS;QACvD,CAAC,CAAC,GAA8C,EAAE,CAAC,OAAO,CAAC,eAAe,IAAI,IAAI;QAClF,CAAC,CAAC,mBAAmB,CAAA;IAEvB,OAAO;QACL,KAAK,CAAC,IAAI,CAAC,KAAa;YACtB,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAA;YAC9B,IAAI,CAAC,QAAQ;gBAAE,OAAO,IAAI,CAAA;YAC1B,OAAO,QAAQ,CAAC,KAAK,CAAC,IAAI,IAAI,CAAA;QAChC,CAAC;QAED,KAAK,CAAC,IAAI;YACR,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAA;YAC9B,IAAI,CAAC,QAAQ;gBAAE,OAAO,EAAE,CAAA;YACxB,OAAO,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;QAChC,CAAC;QAED,KAAK,CAAC,gBAAgB,CAAC,KAAa,EAAE,OAA6B;YACjE,MAAM,GAAG,GAAG,gBAAgB,EAAE,CAAA;YAC9B,IAAI,CAAC,GAAG,EAAE,mBAAmB;gBAAE,OAAO,IAAI,CAAA;YAC1C,OAAO,GAAG,CAAC,mBAAmB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;QAChD,CAAC;QAED,KAAK,CAAC,iBAAiB,CAAC,KAAa;YACnC,MAAM,GAAG,GAAG,gBAAgB,EAAE,CAAA;YAC9B,IAAI,CAAC,GAAG,EAAE,oBAAoB;gBAAE,OAAO,IAAI,CAAA;YAC3C,OAAO,GAAG,CAAC,oBAAoB,CAAC,KAAK,CAAC,CAAA;QACxC,CAAC;QAED,KAAK,CAAC,eAAe;YACnB,MAAM,GAAG,GAAG,gBAAgB,EAAE,CAAA;YAC9B,IAAI,CAAC,GAAG,EAAE,eAAe;gBAAE,OAAO,IAAI,CAAA;YACtC,OAAO,GAAG,CAAC,eAAe,EAAE,CAAA;QAC9B,CAAC;KACF,CAAA;AACH,CAAC"}
|
|
@@ -61,12 +61,15 @@ export interface CreateKitsModuleOptions {
|
|
|
61
61
|
bundledRegistry?: Record<string, SoulcraftKitConfig> | null;
|
|
62
62
|
}
|
|
63
63
|
/**
|
|
64
|
-
* The `sdk.kits.*` namespace — read-only access to the Soulcraft kit registry
|
|
64
|
+
* The `sdk.kits.*` namespace — read-only access to the Soulcraft kit registry
|
|
65
|
+
* and template file paths.
|
|
65
66
|
*
|
|
66
67
|
* Backed by the `@soulcraft/kits` package (a peer dependency). If the package
|
|
67
68
|
* is not installed, `load()` returns `null` and `list()` returns an empty array.
|
|
69
|
+
* Path resolver methods return `null` when the package is absent or the directory
|
|
70
|
+
* does not exist.
|
|
68
71
|
*
|
|
69
|
-
* @example
|
|
72
|
+
* @example Load a kit and run AI with its persona
|
|
70
73
|
* ```typescript
|
|
71
74
|
* const kit = await sdk.kits.load('wicks-and-whiskers')
|
|
72
75
|
* if (kit) {
|
|
@@ -76,6 +79,12 @@ export interface CreateKitsModuleOptions {
|
|
|
76
79
|
* })
|
|
77
80
|
* }
|
|
78
81
|
* ```
|
|
82
|
+
*
|
|
83
|
+
* @example Locate kit template files on disk
|
|
84
|
+
* ```typescript
|
|
85
|
+
* const dir = await sdk.kits.resolveFilesPath('blog-series', 'workshop')
|
|
86
|
+
* // → '/path/to/node_modules/@soulcraft/kits/kits/blog-series/workshop/files'
|
|
87
|
+
* ```
|
|
79
88
|
*/
|
|
80
89
|
export interface KitsModule {
|
|
81
90
|
/**
|
|
@@ -107,5 +116,60 @@ export interface KitsModule {
|
|
|
107
116
|
* ```
|
|
108
117
|
*/
|
|
109
118
|
list(): Promise<SoulcraftKitConfig[]>;
|
|
119
|
+
/**
|
|
120
|
+
* Resolve the absolute path to a kit's product-specific template files directory.
|
|
121
|
+
*
|
|
122
|
+
* Returns the path to `kits/{kitId}/{product}/files/` inside the installed
|
|
123
|
+
* `@soulcraft/kits` npm package, or `null` if the directory does not exist
|
|
124
|
+
* (the kit has no template files for that product, or the package is absent).
|
|
125
|
+
*
|
|
126
|
+
* This is the single source of truth for kit file paths across all products.
|
|
127
|
+
* Use it instead of hard-coding `node_modules` paths or maintaining per-product
|
|
128
|
+
* filesystem layouts.
|
|
129
|
+
*
|
|
130
|
+
* @param kitId - Kit identifier (e.g. `'blog-series'`, `'recipe-manager'`).
|
|
131
|
+
* @param product - Product consuming the files: `'workshop'` or `'venue'`.
|
|
132
|
+
* @returns Absolute path to the files directory, or `null` if it does not exist.
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* ```typescript
|
|
136
|
+
* const dir = await sdk.kits.resolveFilesPath('blog-series', 'workshop')
|
|
137
|
+
* // → '/path/to/node_modules/@soulcraft/kits/kits/blog-series/workshop/files'
|
|
138
|
+
* ```
|
|
139
|
+
*/
|
|
140
|
+
resolveFilesPath(kitId: string, product: 'workshop' | 'venue'): Promise<string | null>;
|
|
141
|
+
/**
|
|
142
|
+
* Resolve the absolute path to a kit's skills directory.
|
|
143
|
+
*
|
|
144
|
+
* Returns the path to `kits/{kitId}/skills/` inside the installed `@soulcraft/kits`
|
|
145
|
+
* npm package, or `null` if the directory does not exist.
|
|
146
|
+
*
|
|
147
|
+
* @param kitId - Kit identifier.
|
|
148
|
+
* @returns Absolute path to the skills directory, or `null` if it does not exist.
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* ```typescript
|
|
152
|
+
* const dir = await sdk.kits.resolveSkillsPath('blog-series')
|
|
153
|
+
* // → '/path/to/node_modules/@soulcraft/kits/kits/blog-series/skills'
|
|
154
|
+
* ```
|
|
155
|
+
*/
|
|
156
|
+
resolveSkillsPath(kitId: string): Promise<string | null>;
|
|
157
|
+
/**
|
|
158
|
+
* Resolve the absolute path to the root `kits/` directory inside the npm package.
|
|
159
|
+
*
|
|
160
|
+
* Useful when enumerating all available kits on the filesystem rather than relying
|
|
161
|
+
* on the `kitRegistry` in-memory list (e.g. build tooling, validators).
|
|
162
|
+
*
|
|
163
|
+
* Returns `null` if `@soulcraft/kits` is not installed.
|
|
164
|
+
*
|
|
165
|
+
* @returns Absolute path to the `kits/` directory, or `null`.
|
|
166
|
+
*
|
|
167
|
+
* @example
|
|
168
|
+
* ```typescript
|
|
169
|
+
* const root = await sdk.kits.resolveKitsRoot()
|
|
170
|
+
* // → '/path/to/node_modules/@soulcraft/kits/kits'
|
|
171
|
+
* ```
|
|
172
|
+
*/
|
|
173
|
+
resolveKitsRoot(): Promise<string | null>;
|
|
110
174
|
}
|
|
111
175
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/modules/kits/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAMH;;;;;;;GAOG;AACH,MAAM,WAAW,kBAAkB;IACjC,qFAAqF;IACrF,EAAE,EAAE,MAAM,CAAA;IACV,+BAA+B;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,wDAAwD;IACxD,WAAW,EAAE,MAAM,CAAA;IACnB,gDAAgD;IAChD,OAAO,EAAE,MAAM,CAAA;IACf,6BAA6B;IAC7B,IAAI,EAAE,OAAO,GAAG,SAAS,GAAG,KAAK,GAAG,SAAS,GAAG,WAAW,CAAA;IAC3D,0CAA0C;IAC1C,MAAM,CAAC,EAAE;QACP,gCAAgC;QAChC,SAAS,CAAC,EAAE,MAAM,CAAA;QAClB,+CAA+C;QAC/C,WAAW,CAAC,EAAE,KAAK,CAAC;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;KACvD,CAAA;IACD,wDAAwD;IACxD,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAMD;;;GAGG;AACH,MAAM,WAAW,uBAAuB;IACtC;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,GAAG,IAAI,CAAA;CAC5D;AAED
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/modules/kits/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAMH;;;;;;;GAOG;AACH,MAAM,WAAW,kBAAkB;IACjC,qFAAqF;IACrF,EAAE,EAAE,MAAM,CAAA;IACV,+BAA+B;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,wDAAwD;IACxD,WAAW,EAAE,MAAM,CAAA;IACnB,gDAAgD;IAChD,OAAO,EAAE,MAAM,CAAA;IACf,6BAA6B;IAC7B,IAAI,EAAE,OAAO,GAAG,SAAS,GAAG,KAAK,GAAG,SAAS,GAAG,WAAW,CAAA;IAC3D,0CAA0C;IAC1C,MAAM,CAAC,EAAE;QACP,gCAAgC;QAChC,SAAS,CAAC,EAAE,MAAM,CAAA;QAClB,+CAA+C;QAC/C,WAAW,CAAC,EAAE,KAAK,CAAC;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;KACvD,CAAA;IACD,wDAAwD;IACxD,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAMD;;;GAGG;AACH,MAAM,WAAW,uBAAuB;IACtC;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,GAAG,IAAI,CAAA;CAC5D;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,WAAW,UAAU;IACzB;;;;;;;;;;;;;OAaG;IACH,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAAA;IAEvD;;;;;;;;;;;;OAYG;IACH,IAAI,IAAI,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAAA;IAErC;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;IAEtF;;;;;;;;;;;;;;OAcG;IACH,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;IAExD;;;;;;;;;;;;;;;OAeG;IACH,eAAe,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;CAC1C"}
|
|
@@ -219,47 +219,47 @@ consistent everywhere.
|
|
|
219
219
|
- [x] Create `soulcraftlabs/sdk` GitHub repo
|
|
220
220
|
- [x] Initialize TypeScript + Bun package with conditional exports
|
|
221
221
|
- [x] Write this ADR and CLAUDE.md
|
|
222
|
-
- [
|
|
223
|
-
- [
|
|
222
|
+
- [x]Scaffold `src/` directory structure (empty modules with JSDoc stubs)
|
|
223
|
+
- [x]Write `tsconfig.json`, `vitest.config.ts`, `.env.example`
|
|
224
224
|
|
|
225
225
|
### Phase 2 — Core Data Layer
|
|
226
|
-
- [
|
|
227
|
-
- [
|
|
228
|
-
- [
|
|
229
|
-
- [
|
|
230
|
-
- [
|
|
231
|
-
- [
|
|
232
|
-
- [
|
|
226
|
+
- [x]`src/transports/` — migrate Local, HTTP, WS from brainy-client; add SSE
|
|
227
|
+
- [x]`src/modules/brainy/` — full Brainy API proxy (all methods, all sub-APIs)
|
|
228
|
+
- [x]`src/modules/vfs/` — VFS namespace (delegates to `brain.vfs.*`)
|
|
229
|
+
- [x]`src/modules/versions/` — versions namespace (delegates to `brain.versions.*`)
|
|
230
|
+
- [x]`src/types.ts` — core shared types (SoulcraftSDK, SDKOptions, etc.)
|
|
231
|
+
- [x]Server mode instance pool (`per-user`, `per-tenant`, `per-scope`)
|
|
232
|
+
- [x]Tests: all transport modes, all brainy methods via proxy
|
|
233
233
|
|
|
234
234
|
### Phase 3 — Auth + License
|
|
235
|
-
- [
|
|
235
|
+
- [x]`src/modules/auth/` — OIDC client config, session cache, capability tokens,
|
|
236
236
|
Hono middleware, SvelteKit handle, backchannel logout
|
|
237
|
-
- [
|
|
237
|
+
- [x]`src/modules/license/` — Cortex activation, plan verification, credit metering,
|
|
238
238
|
BYOK validation, heartbeat background task
|
|
239
|
-
- [
|
|
240
|
-
- [
|
|
239
|
+
- [x]Absorb `@soulcraft/auth` package entirely
|
|
240
|
+
- [x]Tests: auth middleware, capability token create/verify, license exchange
|
|
241
241
|
|
|
242
242
|
### Phase 4 — AI + Events + Skills + Kits + Formats
|
|
243
|
-
- [
|
|
243
|
+
- [x]`src/modules/ai/` — model tiers, delegator, Briggy API, tool definition types,
|
|
244
244
|
KitAIContext, UI action registry
|
|
245
|
-
- [
|
|
246
|
-
- [
|
|
247
|
-
- [
|
|
248
|
-
- [
|
|
245
|
+
- [x]`src/modules/events/` — platform event bus (DataChangeEmitter, all 30+ event types)
|
|
246
|
+
- [x]`src/modules/skills/` — skill loading, bundled skills, user-invocable registry
|
|
247
|
+
- [x]`src/modules/kits/` — kit loader, initialization, template injection
|
|
248
|
+
- [x]`src/modules/formats/` — WVIZ, WDOC, WSLIDE, WQUIZ types (migrate from @soulcraft/views + richDocument.ts)
|
|
249
249
|
|
|
250
250
|
### Phase 5 — Billing + Notifications
|
|
251
|
-
- [
|
|
251
|
+
- [x]`src/modules/billing/` — usage tracking, quota checks, top-up credits,
|
|
252
252
|
Stripe subscription management, billing provider (Firestore vs local)
|
|
253
|
-
- [
|
|
253
|
+
- [x]`src/modules/notifications/` — NotificationSender interface, Postmark, Twilio,
|
|
254
254
|
dev-mode console sender, notification templates
|
|
255
255
|
|
|
256
256
|
### Phase 6 — Publish + First npm Release
|
|
257
|
-
- [
|
|
258
|
-
- [
|
|
259
|
-
- [
|
|
260
|
-
- [
|
|
261
|
-
- [
|
|
262
|
-
- [
|
|
257
|
+
- [x]Publish `@soulcraft/sdk@1.0.0` to npmjs.com (private/restricted)
|
|
258
|
+
- [x]Update Workshop: replace all `@soulcraft/brainy-client` + `@soulcraft/auth` imports
|
|
259
|
+
- [x]Update Venue: same
|
|
260
|
+
- [x]Update Academy: same
|
|
261
|
+
- [x]Deprecate (do not unpublish) `@soulcraft/brainy-client` and `@soulcraft/auth`
|
|
262
|
+
- [x]Add `@soulcraft/sdk` to bundle loop in all three `build.sh` files
|
|
263
263
|
|
|
264
264
|
---
|
|
265
265
|
|
|
@@ -277,6 +277,6 @@ notice in their README and `package.json` `"deprecated"` field pointing to `@sou
|
|
|
277
277
|
|
|
278
278
|
## Related Documents
|
|
279
279
|
|
|
280
|
-
- `docs/ADR-002-transport-protocol.md` — Wire protocol specification
|
|
281
|
-
- `docs/ADR-003-instance-strategies.md` — Brainy instance pooling
|
|
280
|
+
- `docs/ADR-002-transport-protocol.md` — Wire protocol specification
|
|
281
|
+
- `docs/ADR-003-instance-strategies.md` — Brainy instance pooling and Cortex activation
|
|
282
282
|
- `/home/dpsifr/.strategy/PLATFORM-HANDOFF.md` — Cross-project coordination thread
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
# ADR-002: Transport Protocol — Wire Format, Auth, and Reconnection
|
|
2
|
+
|
|
3
|
+
**Status:** Accepted
|
|
4
|
+
**Date:** 2026-03-02
|
|
5
|
+
**Supersedes:** None
|
|
6
|
+
**See also:** `ADR-001-sdk-design.md` (Decision 5)
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Context
|
|
11
|
+
|
|
12
|
+
The SDK must communicate between kit apps (browser) or product backends and a Brainy
|
|
13
|
+
server over a network. Four distinct communication patterns exist across the platform:
|
|
14
|
+
|
|
15
|
+
1. **Stateless RPC** — kit apps call Brainy methods on demand (no persistent connection needed)
|
|
16
|
+
2. **Real-time bidirectional RPC + push** — Venue kit apps need live change events alongside RPC
|
|
17
|
+
3. **Server-push only** — Workshop workspace event stream (VFS/entity mutations)
|
|
18
|
+
4. **In-process** — server-mode SDK calls Brainy directly with zero serialization overhead
|
|
19
|
+
|
|
20
|
+
These patterns have meaningfully different requirements for serialization, auth, and
|
|
21
|
+
error handling. This ADR records the decisions made for each.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Decision 1: Four Transports, Fixed Serialization
|
|
26
|
+
|
|
27
|
+
Four transport implementations are provided. Serialization format is fixed per transport
|
|
28
|
+
and is never configurable:
|
|
29
|
+
|
|
30
|
+
| Transport | Class | Serialization | Direction | Use case |
|
|
31
|
+
|-----------|-------|--------------|-----------|---------|
|
|
32
|
+
| `local` | `LocalTransport` | None (in-process) | — | Server mode, zero overhead |
|
|
33
|
+
| `http` | `HttpTransport` | JSON | Request/response | Stateless RPC — kit apps, simple clients |
|
|
34
|
+
| `ws` | `WsTransport` | MessagePack binary | Bidirectional | Real-time — RPC + change push events |
|
|
35
|
+
| `sse` | `SseTransport` | text/event-stream | Server → Client | Live updates only — VFS/entity events |
|
|
36
|
+
|
|
37
|
+
**Rationale for fixed serialization:**
|
|
38
|
+
- HTTP=JSON: universally debuggable with `curl`, browser DevTools, and server logs.
|
|
39
|
+
The request volume at typical HTTP RPC frequencies makes binary encoding a
|
|
40
|
+
premature optimization with no measurable benefit.
|
|
41
|
+
- WebSocket=MessagePack: bidirectional real-time traffic has high message frequency
|
|
42
|
+
and persistent connections where binary encoding is measurably more efficient.
|
|
43
|
+
MessagePack was already the wire format in `@soulcraft/brainy-client`.
|
|
44
|
+
- Making serialization configurable adds complexity with no practical benefit —
|
|
45
|
+
consumers never need to mix formats within a single transport.
|
|
46
|
+
|
|
47
|
+
**Rejected alternatives:**
|
|
48
|
+
- MessagePack over HTTP: non-standard, incompatible with standard proxies and logging tools.
|
|
49
|
+
- gRPC/Protobuf: over-engineered for the current scale and requires a schema compilation step.
|
|
50
|
+
- Configurable serializer per transport: extra complexity for a use case that has never arisen.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Decision 2: HTTP Transport Wire Format
|
|
55
|
+
|
|
56
|
+
**Endpoint:** `POST {baseUrl}/api/brainy/rpc`
|
|
57
|
+
|
|
58
|
+
**Request body** (JSON):
|
|
59
|
+
```json
|
|
60
|
+
{ "method": "find", "args": [{ "query": "candle kits", "limit": 10 }] }
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Success response** (JSON):
|
|
64
|
+
```json
|
|
65
|
+
{ "result": [ ... ] }
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Error response** (JSON):
|
|
69
|
+
```json
|
|
70
|
+
{ "error": { "code": "BRAINY_NOT_FOUND", "message": "Entity not found" } }
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**Auth:** `Authorization: Bearer <capability-token>` for server-to-server calls;
|
|
74
|
+
`credentials: 'include'` (session cookies) for browser kit apps.
|
|
75
|
+
|
|
76
|
+
**Timeout:** 30 seconds via `AbortController`. Throws `SDKTimeoutError` on expiry.
|
|
77
|
+
|
|
78
|
+
**HTTP status mapping:**
|
|
79
|
+
| Status | Error class |
|
|
80
|
+
|--------|------------|
|
|
81
|
+
| 401 | `SDKAuthError` |
|
|
82
|
+
| 403 | `SDKForbiddenError` |
|
|
83
|
+
| network failure | `SDKDisconnectedError` |
|
|
84
|
+
| 200 + `error` body | `SDKRpcError` |
|
|
85
|
+
|
|
86
|
+
The HTTP transport is stateless — `isAlive()` always returns `true`. Errors surface
|
|
87
|
+
as rejected promises from individual `call()` invocations.
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Decision 3: WebSocket Transport Wire Format
|
|
92
|
+
|
|
93
|
+
**Endpoint:** `wss://{host}/api/brainy/ws`
|
|
94
|
+
|
|
95
|
+
**Auth:** Capability token sent as `Authorization: Bearer <token>` on the WebSocket
|
|
96
|
+
upgrade request. This is a Bun-specific extension (`@ts-expect-error` in source).
|
|
97
|
+
Standard browser `WebSocket` does not support custom headers on the upgrade request —
|
|
98
|
+
browser kit apps use the HTTP transport or pass the token as a `?token=` query param.
|
|
99
|
+
|
|
100
|
+
**Scope param:** `?scope=<tenantSlug>` on the connection URL. Venue uses this to
|
|
101
|
+
select the correct per-tenant Brainy instance.
|
|
102
|
+
|
|
103
|
+
### Connection handshake
|
|
104
|
+
|
|
105
|
+
After a successful upgrade + auth check the server sends a `ready` message:
|
|
106
|
+
```
|
|
107
|
+
Server → Client: { "type": "ready", "scope": "wicks-and-whiskers" }
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
`WsTransport.connect()` resolves only after this message. A 15-second timeout
|
|
111
|
+
applies — if no `ready` arrives the connection is aborted with an error.
|
|
112
|
+
|
|
113
|
+
### RPC messages (binary MessagePack)
|
|
114
|
+
|
|
115
|
+
**Client → Server:**
|
|
116
|
+
```
|
|
117
|
+
{ id: "42", method: "vfs.readdir", args: ["/workspace/docs"] }
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
The `id` is a monotonically incrementing integer cast to string, unique per connection.
|
|
121
|
+
|
|
122
|
+
**Server → Client (RPC response):**
|
|
123
|
+
```
|
|
124
|
+
{ id: "42", result: [ ... ] }
|
|
125
|
+
{ id: "42", error: { code: "VFS_NOT_FOUND", message: "Path not found" } }
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Pending RPCs are matched to their response by `id`. A per-call 30-second timeout
|
|
129
|
+
rejects calls that receive no response.
|
|
130
|
+
|
|
131
|
+
### Change push events (binary MessagePack)
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
{ type: "change", event: "add"|"update"|"delete"|"relate"|"unrelate",
|
|
135
|
+
entity?: { ... }, relation?: { ... } }
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Push events have no `id` field and are dispatched to all registered `onEvent` handlers.
|
|
139
|
+
|
|
140
|
+
### Reconnection
|
|
141
|
+
|
|
142
|
+
On unexpected disconnection (any close code except 4001, 4003, or explicit `close()`):
|
|
143
|
+
|
|
144
|
+
- All pending RPC calls are rejected with `SDKDisconnectedError`
|
|
145
|
+
- Reconnect is attempted with **exponential backoff**: `min(1000 × 2^attempt, 30_000)` ms
|
|
146
|
+
- Maximum 10 attempts (configurable via constructor)
|
|
147
|
+
- After 10 failed attempts the transport enters `closed` state permanently
|
|
148
|
+
|
|
149
|
+
**Auth failure codes — no retry:**
|
|
150
|
+
| Close code | Meaning | Behaviour |
|
|
151
|
+
|-----------|---------|----------|
|
|
152
|
+
| 4001 | Unauthorized (bad/expired token) | Reject pending calls with `SDKAuthError`, set `closed` |
|
|
153
|
+
| 4003 | Forbidden (insufficient scope) | Reject pending calls with `SDKForbiddenError`, set `closed` |
|
|
154
|
+
|
|
155
|
+
### Connection states
|
|
156
|
+
|
|
157
|
+
```
|
|
158
|
+
disconnected → connecting → connected → disconnected (unexpected) → connecting (reconnect) ...
|
|
159
|
+
→ closed (explicit close() or auth failure)
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## Decision 4: SSE Transport Wire Format
|
|
165
|
+
|
|
166
|
+
**Endpoint:** `GET {baseUrl}/api/workspace/events[?workspaceId=<id>]`
|
|
167
|
+
|
|
168
|
+
Uses the browser's native `EventSource` API. `EventSource` handles reconnection
|
|
169
|
+
automatically using the server-sent `retry:` interval. Additionally, the SDK applies
|
|
170
|
+
manual exponential backoff (identical schedule to WsTransport) on `onerror` events
|
|
171
|
+
to prevent thundering-herd reconnects on server restart.
|
|
172
|
+
|
|
173
|
+
**Event format** (JSON-encoded in the SSE `data:` field):
|
|
174
|
+
```
|
|
175
|
+
data: {"type":"change","event":"update","entity":{"id":"abc","nounType":"Booking",...}}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
**Connection confirmation** (internal, not dispatched to listeners):
|
|
179
|
+
```
|
|
180
|
+
data: {"type":"connected","connectionId":"conn-abc123"}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
**Receive-only:** `call()` throws `SDKError` with code `SSE_NO_RPC`. For outbound
|
|
184
|
+
operations pair this transport with an `HttpTransport`.
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## Decision 5: Local Transport (In-Process)
|
|
189
|
+
|
|
190
|
+
Used exclusively in server mode. Wraps a native `Brainy` instance. No serialization,
|
|
191
|
+
no network, no error class mapping.
|
|
192
|
+
|
|
193
|
+
**Method resolution:** Dot-separated method paths are resolved by recursive property
|
|
194
|
+
traversal on the Brainy instance. `'vfs.readdir'` → `brain.vfs.readdir(args)`.
|
|
195
|
+
|
|
196
|
+
**Change events:** `onEvent`/`offEvent` delegate to Brainy's built-in change listeners
|
|
197
|
+
directly on the instance. No intermediary.
|
|
198
|
+
|
|
199
|
+
**isAlive():** Always `true`. **close():** No-op.
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## Decision 6: Shared Error Classes
|
|
204
|
+
|
|
205
|
+
All transports share a common error hierarchy:
|
|
206
|
+
|
|
207
|
+
```
|
|
208
|
+
SDKError (base, has `code: string`)
|
|
209
|
+
├── SDKAuthError (401 / close code 4001)
|
|
210
|
+
├── SDKForbiddenError (403 / close code 4003)
|
|
211
|
+
├── SDKTimeoutError (30s per-call timeout)
|
|
212
|
+
├── SDKDisconnectedError (transport not connected, or unexpected close)
|
|
213
|
+
└── SDKRpcError (server returned { error: { code, message } })
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Consumer code catches `SDKAuthError` to redirect to login, `SDKDisconnectedError`
|
|
217
|
+
to show a reconnecting state, etc.
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## Decision 7: Capability Tokens for Server-to-Server Auth
|
|
222
|
+
|
|
223
|
+
Transport-level auth uses HMAC-SHA256 capability tokens:
|
|
224
|
+
|
|
225
|
+
```
|
|
226
|
+
<base64url(payload)>.<base64url(signature)>
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
`payload` is `{ sub, scope, iat, exp }` JSON. Default TTL: 1 hour.
|
|
230
|
+
|
|
231
|
+
**Timing-safe verification** uses `crypto.timingSafeEqual` to prevent timing attacks.
|
|
232
|
+
|
|
233
|
+
These tokens are separate from OIDC session tokens. They are used for:
|
|
234
|
+
- Kit apps making RPC calls to the Venue or Workshop backend
|
|
235
|
+
- Server-to-server calls (e.g. Academy → Workshop)
|
|
236
|
+
|
|
237
|
+
Session cookie auth (browser kit apps in same-origin context) uses `credentials: 'include'`
|
|
238
|
+
on the HTTP transport and does not require a capability token.
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## Related Documents
|
|
243
|
+
|
|
244
|
+
- `ADR-001-sdk-design.md` — Overall SDK architecture (Decision 5: transport rationale)
|
|
245
|
+
- `ADR-003-instance-strategies.md` — How server-side Brainy instances are pooled and selected
|
|
246
|
+
- `src/transports/` — Implementation of all four transports
|
|
247
|
+
- `src/modules/brainy/errors.ts` — Shared error class hierarchy
|
|
248
|
+
- `src/modules/brainy/auth.ts` — Capability token creation and verification
|