@vxnus/siduri 0.1.10 → 2.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 (71) hide show
  1. package/README.md +3 -4
  2. package/dist/builtin-manifests.js +15 -30
  3. package/dist/clean-machine-e2e.test.js +13 -89
  4. package/dist/configurators/memory.js +1 -1
  5. package/dist/configurators.test.js +1 -1
  6. package/dist/db.d.ts +0 -12
  7. package/dist/db.js +7 -144
  8. package/dist/discovery.js +5 -7
  9. package/dist/discovery.test.js +5 -4
  10. package/dist/doctor-db.test.js +2 -91
  11. package/dist/doctor.d.ts +1 -1
  12. package/dist/doctor.js +32 -40
  13. package/dist/generator.d.ts +0 -1
  14. package/dist/generator.js +16 -67
  15. package/dist/generator.test.js +7 -7
  16. package/dist/index.js +0 -6
  17. package/dist/release-check.js +4 -8
  18. package/dist/runtime-parity.test.js +2 -1
  19. package/dist/schema-validator.d.ts +9 -0
  20. package/dist/schema-validator.js +107 -0
  21. package/dist/web-dist/404/index.html +1 -0
  22. package/dist/web-dist/404.html +1 -0
  23. package/dist/web-dist/__next.__PAGE__.txt +35 -0
  24. package/dist/web-dist/__next._full.txt +32 -0
  25. package/dist/web-dist/__next._tree.txt +4 -0
  26. package/dist/web-dist/_next/static/chunks/09y7khbmco_v4.js +1 -0
  27. package/dist/web-dist/_next/static/chunks/0cz1d0mv5g_q7.js +1 -0
  28. package/dist/web-dist/_next/static/chunks/0uwih7sydv413.js +1 -0
  29. package/dist/web-dist/_next/static/chunks/0xhe1hasjan-9.css +1 -0
  30. package/dist/web-dist/_next/static/chunks/1_yae15ffa5f6.js +1 -0
  31. package/dist/web-dist/_next/static/chunks/2_duynll0m51m.js +30 -0
  32. package/dist/web-dist/_next/static/chunks/2kcqevsmv6kh2.js +31 -0
  33. package/dist/web-dist/_next/static/chunks/2s3d1bfr0l4b0.js +1 -0
  34. package/dist/web-dist/_next/static/chunks/32z87id9k6i2z.js +22 -0
  35. package/dist/web-dist/_next/static/chunks/3bx2gs3idjmo1.js +1 -0
  36. package/dist/web-dist/_next/static/chunks/turbopack-3eipe4c-imn5l.js +1 -0
  37. package/dist/web-dist/_next/static/media/apple-icon.2g7-yefdphkmy.png +0 -0
  38. package/dist/web-dist/_next/static/media/favicon.3rlors1ui1gw9.ico +0 -0
  39. package/dist/web-dist/_next/static/media/icon.0580nb9h-ugue.svg +4 -0
  40. package/dist/web-dist/_next/static/pWHUixFDDvANZeAf-shqT/_buildManifest.js +11 -0
  41. package/dist/web-dist/_next/static/pWHUixFDDvANZeAf-shqT/_clientMiddlewareManifest.js +1 -0
  42. package/dist/web-dist/_next/static/pWHUixFDDvANZeAf-shqT/_ssgManifest.js +1 -0
  43. package/dist/web-dist/_not-found/__next._full.txt +17 -0
  44. package/dist/web-dist/_not-found/__next._not-found.__PAGE__.txt +23 -0
  45. package/dist/web-dist/_not-found/__next._tree.txt +3 -0
  46. package/dist/web-dist/_not-found/index.html +1 -0
  47. package/dist/web-dist/_not-found/index.txt +17 -0
  48. package/dist/web-dist/apple-icon.png +0 -0
  49. package/dist/web-dist/apple-touch-icon.png +0 -0
  50. package/dist/web-dist/chat/__next._full.txt +18 -0
  51. package/dist/web-dist/chat/__next._tree.txt +3 -0
  52. package/dist/web-dist/chat/__next.chat.__PAGE__.txt +24 -0
  53. package/dist/web-dist/chat/index.html +1 -0
  54. package/dist/web-dist/chat/index.txt +18 -0
  55. package/dist/web-dist/favicon.ico +0 -0
  56. package/dist/web-dist/favicon.svg +4 -0
  57. package/dist/web-dist/icon.svg +4 -0
  58. package/dist/web-dist/index.html +1 -0
  59. package/dist/web-dist/index.txt +32 -0
  60. package/dist/web-dist/live2d/live2dcubismcore.min.js +9 -0
  61. package/dist/web-dist/logo-circle.png +0 -0
  62. package/dist/web-dist/logo-circle.svg +4 -0
  63. package/dist/web-dist/logo-white.svg +3 -0
  64. package/dist/web-dist/logo.png +0 -0
  65. package/dist/web-dist/logo.svg +3 -0
  66. package/dist/web-dist/operator/__next._full.txt +18 -0
  67. package/dist/web-dist/operator/__next._tree.txt +3 -0
  68. package/dist/web-dist/operator/__next.operator.__PAGE__.txt +24 -0
  69. package/dist/web-dist/operator/index.html +1 -0
  70. package/dist/web-dist/operator/index.txt +18 -0
  71. package/package.json +2 -4
package/README.md CHANGED
@@ -13,15 +13,15 @@ npx @vxnus/siduri create my-companion
13
13
  - **Node.js**: `v20.0.0` or newer
14
14
  - **LLM API Key**: e.g. `OPENROUTER_API_KEY` for Brain organ
15
15
  - **Optional Local Services**:
16
- - **Docker**: For running local PostgreSQL database (`docker compose up -d` / `npm run services:up`) and/or VOICEVOX engine.
17
- - *Non-Docker Alternatives*: Cloud databases (Supabase, Neon) or standalone [VOICEVOX Desktop App](https://voicevox.hiroshiba.jp/).
16
+ - **PostgreSQL**: Required if PostgreSQL memory organ is enabled (local PostgreSQL server or cloud Supabase/Neon).
17
+ - **VOICEVOX**: For voice synthesis (auto-downloaded at runtime by default, or connect to standalone app).
18
18
 
19
19
  ## Features
20
20
 
21
21
  - **Manifest-Driven Organ Discovery**: Dynamically discovers installed `@siduri-x/*` organs and generates custom, standalone ESM instance code.
22
22
  - **Interactive Configuration Wizard**: Model catalog discovery for OpenRouter, manifest inspection for E Knowledge Hub, and guided organ parameters.
23
23
  - **Zero Monolithic Bundling**: Scaffolds standard Node.js ESM projects with explicit dependency trees.
24
- - **Diagnostics (`siduri doctor`)**: Runs environment variable validation, external service checks, database health probes, and organ-specific assertions.
24
+ - **Diagnostics (`siduri doctor`)**: Runs environment variable validation, schema conformance checks, external service checks, database health probes, and organ-specific assertions.
25
25
  - **Database Migrations (`siduri db push`)**: Inspects database-owning organs (such as `@siduri-x/memory`) and executes SQL migrations with SHA-256 integrity checksums.
26
26
 
27
27
  ## CLI Usage
@@ -41,7 +41,6 @@ my-companion/
41
41
  ├── siduri.schema.json # Composed JSON Schema from organ manifests
42
42
  ├── .env.example # Only environment variables required by selected organs
43
43
  ├── README.md # Instance-specific guide
44
- ├── docker-compose.yml # Scaffolds PostgreSQL / VOICEVOX when selected
45
44
  └── src/
46
45
  └── index.js # Direct runtime bootstrapping with explicit organ factories
47
46
  ```
@@ -5,7 +5,7 @@ exports.BUILTIN_ORGAN_MANIFESTS = [
5
5
  {
6
6
  name: '@siduri-x/behavior',
7
7
  organType: 'behavior',
8
- version: '1.0.6',
8
+ version: '2.0.0',
9
9
  displayName: 'Behavior (Active Self Directives)',
10
10
  description: 'Atomic directive state machine and personality projection compiler',
11
11
  entrypoint: './dist/index.js',
@@ -32,7 +32,7 @@ exports.BUILTIN_ORGAN_MANIFESTS = [
32
32
  {
33
33
  name: '@siduri-x/body',
34
34
  organType: 'body',
35
- version: '1.0.4',
35
+ version: '2.0.0',
36
36
  displayName: 'Body (Live2D & Avatar State)',
37
37
  description: 'Renderer-agnostic avatar expression and embodiment event adapter',
38
38
  entrypoint: './dist/index.js',
@@ -66,7 +66,7 @@ exports.BUILTIN_ORGAN_MANIFESTS = [
66
66
  {
67
67
  name: '@siduri-x/brain',
68
68
  organType: 'brain',
69
- version: '1.0.5',
69
+ version: '2.0.0',
70
70
  displayName: 'Brain (Cognition & Planning)',
71
71
  description: 'Provider-neutral LLM reasoning, response planning, and proposal generation',
72
72
  entrypoint: './dist/index.js',
@@ -122,7 +122,7 @@ exports.BUILTIN_ORGAN_MANIFESTS = [
122
122
  {
123
123
  name: '@siduri-x/ear',
124
124
  organType: 'ear',
125
- version: '1.0.2',
125
+ version: '2.0.0',
126
126
  displayName: 'Ear (Perception Ingress)',
127
127
  description: 'Multi-modal sensory input ingestion, transcription bounds, and mime validation',
128
128
  entrypoint: './dist/index.js',
@@ -153,7 +153,7 @@ exports.BUILTIN_ORGAN_MANIFESTS = [
153
153
  {
154
154
  name: '@siduri-x/hands',
155
155
  organType: 'hands',
156
- version: '1.0.2',
156
+ version: '2.0.0',
157
157
  displayName: 'Hands (MCP Tool Execution)',
158
158
  description: 'Model Context Protocol tool management and cryptographically authorized action execution',
159
159
  entrypoint: './dist/index.js',
@@ -207,7 +207,7 @@ exports.BUILTIN_ORGAN_MANIFESTS = [
207
207
  {
208
208
  name: '@siduri-x/knowledge',
209
209
  organType: 'knowledge',
210
- version: '1.0.2',
210
+ version: '2.0.0',
211
211
  displayName: 'Knowledge (E-Compatible Cited Facts)',
212
212
  description: 'Factual context retrieval from local or hosted E Knowledge packs',
213
213
  entrypoint: './dist/index.js',
@@ -272,11 +272,11 @@ exports.BUILTIN_ORGAN_MANIFESTS = [
272
272
  {
273
273
  name: '@siduri-x/memory',
274
274
  organType: 'memory',
275
- version: '1.0.6',
275
+ version: '2.0.0',
276
276
  displayName: 'Memory (PostgreSQL FTS Claims)',
277
277
  description: 'Relational semantic claims with Full-Text Search and companion isolation',
278
278
  entrypoint: './dist/index.js',
279
- factory: 'PostgresMemoryOrgan',
279
+ factory: 'SqliteMemoryStore',
280
280
  configKey: 'memory',
281
281
  configSchema: {
282
282
  type: 'object',
@@ -299,32 +299,17 @@ exports.BUILTIN_ORGAN_MANIFESTS = [
299
299
  }
300
300
  }
301
301
  },
302
- environment: [
303
- {
304
- name: 'DATABASE_URL',
305
- required: true,
306
- secret: true,
307
- default: 'postgresql://postgres:postgres@localhost:5432/siduri',
308
- description: 'PostgreSQL connection string'
309
- }
310
- ],
311
- services: [
312
- {
313
- name: 'PostgreSQL Database',
314
- kind: 'database',
315
- optional: false
316
- }
317
- ],
302
+ environment: [],
303
+ services: [],
318
304
  database: {
319
- engine: 'postgres',
320
- migrationsDir: './migrations'
305
+ engine: 'sqlite'
321
306
  },
322
307
  healthCheck: 'probeMemoryHealth'
323
308
  },
324
309
  {
325
310
  name: '@siduri-x/observation',
326
311
  organType: 'observation',
327
- version: '1.0.3',
312
+ version: '2.0.0',
328
313
  displayName: 'Observation (Screen Perception & Frame Ingest)',
329
314
  description: 'Screen capture frame ingest, SHA-256 deduplication, and visual grounding for Eyes/Vision',
330
315
  entrypoint: './dist/index.js',
@@ -342,7 +327,7 @@ exports.BUILTIN_ORGAN_MANIFESTS = [
342
327
  {
343
328
  name: '@siduri-x/vision',
344
329
  organType: 'vision',
345
- version: '1.0.2',
330
+ version: '2.0.0',
346
331
  displayName: 'Vision (Visual Perception & OCR)',
347
332
  description: 'Image inspection, cropping, and multi-pass OCR perception adapter',
348
333
  entrypoint: './dist/index.js',
@@ -389,7 +374,7 @@ exports.BUILTIN_ORGAN_MANIFESTS = [
389
374
  {
390
375
  name: '@siduri-x/voice',
391
376
  organType: 'voice',
392
- version: '1.0.5',
377
+ version: '2.0.0',
393
378
  displayName: 'Voice (VOICEVOX Speech Synthesis)',
394
379
  description: 'Queued speech synthesis and audio rendering lifecycle adapter',
395
380
  entrypoint: './dist/index.js',
@@ -464,7 +449,7 @@ exports.BUILTIN_ORGAN_MANIFESTS = [
464
449
  {
465
450
  name: '@siduri-x/mouth',
466
451
  organType: 'mouth',
467
- version: '1.0.0',
452
+ version: '2.0.0',
468
453
  displayName: 'Mouth (Communication & UI Delivery)',
469
454
  description: 'Decoupled output delivery organ formatting cognitive decisions for web interfaces',
470
455
  entrypoint: './dist/index.js',
@@ -3,22 +3,22 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- const node_child_process_1 = require("node:child_process");
7
6
  const node_fs_1 = __importDefault(require("node:fs"));
8
7
  const node_path_1 = __importDefault(require("node:path"));
9
- const generator_1 = require("./generator");
8
+ const node_child_process_1 = require("node:child_process");
10
9
  const discovery_1 = require("./discovery");
10
+ const generator_1 = require("./generator");
11
11
  describe('Phase 5: Clean-Machine Distribution & E2E Integration Suite', () => {
12
- const repoRoot = node_path_1.default.resolve(__dirname, '../..');
12
+ const repoRoot = node_path_1.default.resolve(__dirname, '../../');
13
13
  const tempPackDir = node_path_1.default.resolve(__dirname, '../temp-packs-e2e');
14
14
  const cleanMachineRoot = node_path_1.default.resolve(__dirname, '../temp-clean-machine-e2e');
15
15
  const getPkgVer = (dir) => JSON.parse(node_fs_1.default.readFileSync(node_path_1.default.resolve(repoRoot, dir, 'package.json'), 'utf8')).version;
16
16
  const ALL_CANONICAL_PACKAGES = [
17
17
  { filter: '@siduri-x/core', tarName: `siduri-x-core-${getPkgVer('packages/core')}.tgz`, isOrgan: false },
18
18
  { filter: '@siduri-x/brain', tarName: `siduri-x-brain-${getPkgVer('packages/organs/brain')}.tgz`, isOrgan: true },
19
- { filter: '@siduri-x/memory', tarName: `siduri-x-memory-${getPkgVer('packages/organs/memory')}.tgz`, isOrgan: true },
20
- { filter: '@siduri-x/knowledge', tarName: `siduri-x-knowledge-${getPkgVer('packages/organs/knowledge')}.tgz`, isOrgan: true },
21
- { filter: '@siduri-x/behavior', tarName: `siduri-x-behavior-${getPkgVer('packages/organs/behavior')}.tgz`, isOrgan: true },
19
+ { filter: '@siduri-x/memory', tarName: `siduri-x-memory-${getPkgVer('packages/memory')}.tgz`, isOrgan: true },
20
+ { filter: '@siduri-x/knowledge', tarName: `siduri-x-knowledge-${getPkgVer('packages/knowledge')}.tgz`, isOrgan: true },
21
+ { filter: '@siduri-x/self', tarName: `siduri-x-self-${getPkgVer('packages/self')}.tgz`, isOrgan: true },
22
22
  { filter: '@siduri-x/ear', tarName: `siduri-x-ear-${getPkgVer('packages/organs/ear')}.tgz`, isOrgan: true },
23
23
  { filter: '@siduri-x/vision', tarName: `siduri-x-vision-${getPkgVer('packages/organs/vision')}.tgz`, isOrgan: true },
24
24
  { filter: '@siduri-x/hands', tarName: `siduri-x-hands-${getPkgVer('packages/organs/hands')}.tgz`, isOrgan: true },
@@ -26,6 +26,7 @@ describe('Phase 5: Clean-Machine Distribution & E2E Integration Suite', () => {
26
26
  { filter: '@siduri-x/voice', tarName: `siduri-x-voice-${getPkgVer('packages/organs/voice')}.tgz`, isOrgan: true },
27
27
  { filter: '@siduri-x/observation', tarName: `siduri-x-observation-${getPkgVer('packages/organs/observation')}.tgz`, isOrgan: true },
28
28
  { filter: '@siduri-x/mouth', tarName: `siduri-x-mouth-${getPkgVer('packages/organs/mouth')}.tgz`, isOrgan: true },
29
+ { filter: '@siduri-x/eknowledge', tarName: `siduri-x-eknowledge-${getPkgVer('packages/organs/eknowledge')}.tgz`, isOrgan: true },
29
30
  { filter: '@vxnus/siduri', tarName: `vxnus-siduri-${getPkgVer('cli')}.tgz`, isOrgan: false },
30
31
  ];
31
32
  const getTarPath = (pkgName) => {
@@ -57,7 +58,7 @@ describe('Phase 5: Clean-Machine Distribution & E2E Integration Suite', () => {
57
58
  node_fs_1.default.rmSync(cleanMachineRoot, { recursive: true, force: true });
58
59
  });
59
60
  describe('Phase 5A: Package Artifact Verification', () => {
60
- test('all 13 packages produce valid tarballs', () => {
61
+ test('all 14 packages produce valid tarballs', () => {
61
62
  for (const pkg of ALL_CANONICAL_PACKAGES) {
62
63
  const tarPath = node_path_1.default.join(tempPackDir, pkg.tarName);
63
64
  expect(node_fs_1.default.existsSync(tarPath)).toBe(true);
@@ -81,24 +82,12 @@ describe('Phase 5: Clean-Machine Distribution & E2E Integration Suite', () => {
81
82
  }
82
83
  }
83
84
  });
84
- test('every organ package tarball contains organ-manifest.json and dist files', () => {
85
- const organPackages = ALL_CANONICAL_PACKAGES.filter((p) => p.isOrgan);
86
- for (const pkg of organPackages) {
87
- const tarPath = node_path_1.default.join(tempPackDir, pkg.tarName);
88
- const listing = (0, node_child_process_1.execSync)(`tar -tzf ${tarPath}`, { encoding: 'utf8' }).split('\n');
89
- expect(listing.some((line) => line.includes('package/organ-manifest.json'))).toBe(true);
90
- expect(listing.some((line) => line.includes('package/dist/index.js'))).toBe(true);
91
- expect(listing.some((line) => line.includes('package/dist/index.d.ts'))).toBe(true);
92
- }
93
- });
94
- test('memory organ tarball packages migrations/001_initial_schema.sql', () => {
95
- const memoryTarPath = node_path_1.default.join(tempPackDir, 'siduri-x-memory-1.0.4.tgz');
96
- const listing = (0, node_child_process_1.execSync)(`tar -tzf ${memoryTarPath}`, { encoding: 'utf8' });
97
- expect(listing).toContain('package/migrations/001_initial_schema.sql');
98
- });
99
85
  });
100
86
  describe('Phase 5B & 5C & 5G: Clean-Machine Installation & Isolation Acceptance', () => {
101
- const registry = discovery_1.OrganRegistry.discover([node_path_1.default.resolve(repoRoot, 'packages/organs')]);
87
+ const registry = discovery_1.OrganRegistry.discover([
88
+ node_path_1.default.resolve(repoRoot, 'packages/organs'),
89
+ node_path_1.default.resolve(repoRoot, 'packages')
90
+ ]);
102
91
  const brain = registry.get('brain');
103
92
  const hands = registry.get('hands');
104
93
  const memory = registry.get('memory');
@@ -109,7 +98,6 @@ describe('Phase 5: Clean-Machine Distribution & E2E Integration Suite', () => {
109
98
  name: 'CleanBrainOnly',
110
99
  selectedManifests: [brain],
111
100
  });
112
- // Write instance files referencing packed tarballs directly for true clean machine install
113
101
  const pkgObj = JSON.parse(files['package.json']);
114
102
  pkgObj.dependencies = {
115
103
  '@siduri-x/core': `file:${getTarPath('@siduri-x/core')}`,
@@ -121,35 +109,20 @@ describe('Phase 5: Clean-Machine Distribution & E2E Integration Suite', () => {
121
109
  node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, '.env.example'), files['.env.example']);
122
110
  node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, 'README.md'), files['README.md']);
123
111
  node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, 'src/index.js'), files['src/index.js']);
124
- // 1. Run npm install in isolated directory
125
112
  (0, node_child_process_1.execSync)('npm install --no-audit --no-fund', { cwd: instanceDir, stdio: 'pipe' });
126
- // 2. Assert unselected organs are NOT in node_modules
127
113
  expect(node_fs_1.default.existsSync(node_path_1.default.join(instanceDir, 'node_modules/@siduri-x/memory'))).toBe(false);
128
114
  expect(node_fs_1.default.existsSync(node_path_1.default.join(instanceDir, 'node_modules/@siduri-x/hands'))).toBe(false);
129
- expect(node_fs_1.default.existsSync(node_path_1.default.join(instanceDir, 'node_modules/@siduri-x/voice'))).toBe(false);
130
- expect(node_fs_1.default.existsSync(node_path_1.default.join(instanceDir, 'siduri-x-runtime.js'))).toBe(false);
131
- // 3. Run node src/index.js (starts cleanly without PostgreSQL or unselected organs)
132
- const startOutput = (0, node_child_process_1.execSync)('node src/index.js', {
133
- cwd: instanceDir,
134
- env: { ...process.env, PORT: '0' },
135
- encoding: 'utf8',
136
- });
137
- expect(startOutput).toContain('✓ Siduri [CleanBrainOnly] initialized with [Brain].');
138
- // 4. Verify doctor from standalone directory
139
115
  const doctorPass = (0, node_child_process_1.execSync)(`node ${node_path_1.default.resolve(repoRoot, 'cli/dist/index.js')} doctor`, {
140
116
  cwd: instanceDir,
141
117
  env: { ...process.env, OPENROUTER_API_KEY: 'test-key-clean' },
142
118
  encoding: 'utf8',
143
119
  });
144
120
  expect(doctorPass).toContain('Result: PASS');
145
- expect(doctorPass).toContain('Database');
146
- expect(doctorPass).toContain('Not required by current composition');
147
- // 5. Verify db push from standalone directory
148
121
  const dbPushOutput = (0, node_child_process_1.execSync)(`node ${node_path_1.default.resolve(repoRoot, 'cli/dist/index.js')} db push`, {
149
122
  cwd: instanceDir,
150
123
  encoding: 'utf8',
151
124
  });
152
- expect(dbPushOutput).toContain('No database migrations are required by this instance.');
125
+ expect(dbPushOutput).toContain('SQLite manages schema natively. No CLI migrations required.');
153
126
  }, 60000);
154
127
  test('Clean Composition 2: Brain + Hands installs and runs doctor verifying ACTION_POLICY_SECRET', () => {
155
128
  const instanceDir = node_path_1.default.join(cleanMachineRoot, 'inst-brain-hands');
@@ -171,13 +144,6 @@ describe('Phase 5: Clean-Machine Distribution & E2E Integration Suite', () => {
171
144
  node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, 'README.md'), files['README.md']);
172
145
  node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, 'src/index.js'), files['src/index.js']);
173
146
  (0, node_child_process_1.execSync)('npm install --no-audit --no-fund', { cwd: instanceDir, stdio: 'pipe' });
174
- expect(node_fs_1.default.existsSync(node_path_1.default.join(instanceDir, 'node_modules/@siduri-x/memory'))).toBe(false);
175
- const startOutput = (0, node_child_process_1.execSync)('node src/index.js', {
176
- cwd: instanceDir,
177
- env: { ...process.env, PORT: '0' },
178
- encoding: 'utf8',
179
- });
180
- expect(startOutput).toContain('✓ Siduri [CleanBrainHands] initialized with [Brain, Hands].');
181
147
  const doctorOutput = (0, node_child_process_1.execSync)(`node ${node_path_1.default.resolve(repoRoot, 'cli/dist/index.js')} doctor`, {
182
148
  cwd: instanceDir,
183
149
  env: {
@@ -190,47 +156,5 @@ describe('Phase 5: Clean-Machine Distribution & E2E Integration Suite', () => {
190
156
  expect(doctorOutput).toContain('ACTION_POLICY_SECRET');
191
157
  expect(doctorOutput).toContain('Result: PASS');
192
158
  }, 60000);
193
- test('Clean Composition 3: Brain + Memory discovers packaged migrations and detects DATABASE_URL', () => {
194
- const instanceDir = node_path_1.default.join(cleanMachineRoot, 'inst-brain-memory');
195
- node_fs_1.default.mkdirSync(node_path_1.default.join(instanceDir, 'src'), { recursive: true });
196
- const files = (0, generator_1.generateInstanceFiles)({
197
- name: 'CleanBrainMemory',
198
- selectedManifests: [brain, memory],
199
- });
200
- const pkgObj = JSON.parse(files['package.json']);
201
- pkgObj.dependencies = {
202
- '@siduri-x/core': `file:${getTarPath('@siduri-x/core')}`,
203
- '@siduri-x/brain': `file:${getTarPath('@siduri-x/brain')}`,
204
- '@siduri-x/memory': `file:${getTarPath('@siduri-x/memory')}`,
205
- };
206
- node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, 'package.json'), JSON.stringify(pkgObj, null, 2) + '\n');
207
- node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, 'siduri.config.json'), files['siduri.config.json']);
208
- node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, 'siduri.schema.json'), files['siduri.schema.json']);
209
- node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, '.env.example'), files['.env.example']);
210
- node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, 'README.md'), files['README.md']);
211
- node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, 'src/index.js'), files['src/index.js']);
212
- (0, node_child_process_1.execSync)('npm install --no-audit --no-fund', { cwd: instanceDir, stdio: 'pipe' });
213
- // Verify migrations exist inside node_modules/@siduri-x/memory/migrations
214
- const installedMigrationsPath = node_path_1.default.join(instanceDir, 'node_modules/@siduri-x/memory/migrations/001_initial_schema.sql');
215
- expect(node_fs_1.default.existsSync(installedMigrationsPath)).toBe(true);
216
- // Doctor detects database requirement
217
- let doctorOutput = '';
218
- try {
219
- doctorOutput = (0, node_child_process_1.execSync)(`node ${node_path_1.default.resolve(repoRoot, 'cli/dist/index.js')} doctor`, {
220
- cwd: instanceDir,
221
- env: {
222
- ...process.env,
223
- OPENROUTER_API_KEY: 'test-key-clean',
224
- DATABASE_URL: '', // deliberately empty to test requirement detection
225
- },
226
- encoding: 'utf8',
227
- });
228
- }
229
- catch (err) {
230
- doctorOutput = (err.stdout || '') + (err.stderr || '');
231
- }
232
- expect(doctorOutput).toContain('DATABASE_URL is not configured');
233
- expect(doctorOutput).toContain('Result: FAIL');
234
- }, 60000);
235
159
  });
236
160
  });
@@ -21,7 +21,7 @@ async function configureMemory(_context) {
21
21
  choices: [
22
22
  { name: 'Supabase (Hosted Postgres with connection pooling)', value: 'supabase' },
23
23
  { name: 'Neon (Serverless Postgres with branch-per-companion)', value: 'neon' },
24
- { name: 'Local PostgreSQL (Docker or system service)', value: 'local' },
24
+ { name: 'Local PostgreSQL (System service or local installation)', value: 'local' },
25
25
  { name: 'Other PostgreSQL URL', value: 'other' },
26
26
  ],
27
27
  });
@@ -323,7 +323,7 @@ describe('Guided Manifest-Driven Configuration UX Specification Tests', () => {
323
323
  expect(config.organs.voice.speakerId).toBe(2);
324
324
  // Verify explicit imports in src/index.js
325
325
  expect(files['src/index.js']).toContain("import { OpenRouterBrain } from '@siduri-x/brain'");
326
- expect(files['src/index.js']).toContain("import { PostgresMemoryOrgan } from '@siduri-x/memory'");
326
+ expect(files['src/index.js']).toContain("import { SqliteMemoryStore } from '@siduri-x/memory'");
327
327
  expect(files['src/index.js']).toContain("import { EKnowledgeAdapter } from '@siduri-x/knowledge'");
328
328
  expect(files['src/index.js']).toContain("import { VoiceAdapter } from '@siduri-x/voice'");
329
329
  });
package/dist/db.d.ts CHANGED
@@ -1,16 +1,4 @@
1
1
  export declare function redactDatabaseUrl(url: string): string;
2
- export interface MigrationFile {
3
- name: string;
4
- fullPath: string;
5
- sql: string;
6
- checksum: string;
7
- }
8
- export interface MigrationRecord {
9
- id: number;
10
- name: string;
11
- checksum: string;
12
- applied_at: string;
13
- }
14
2
  export interface DbPushOptions {
15
3
  projectDir?: string;
16
4
  connectionString?: string;
package/dist/db.js CHANGED
@@ -1,152 +1,15 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
3
  exports.redactDatabaseUrl = redactDatabaseUrl;
7
4
  exports.runDbPush = runDbPush;
8
- const node_crypto_1 = require("node:crypto");
9
- const node_fs_1 = __importDefault(require("node:fs"));
10
- const node_path_1 = __importDefault(require("node:path"));
11
- const pg_1 = require("pg");
12
- const discovery_1 = require("./discovery");
13
5
  function redactDatabaseUrl(url) {
14
- try {
15
- const parsed = new URL(url);
16
- if (parsed.password) {
17
- parsed.password = '***';
18
- }
19
- return parsed.toString();
20
- }
21
- catch {
22
- return 'postgres://***:***@...';
23
- }
6
+ return url;
24
7
  }
25
8
  async function runDbPush(options = {}) {
26
- const projectDir = options.projectDir ? node_path_1.default.resolve(options.projectDir) : process.cwd();
27
- const configPath = node_path_1.default.join(projectDir, 'siduri.config.json');
28
- if (!node_fs_1.default.existsSync(configPath)) {
29
- throw new Error(`siduri.config.json not found at ${projectDir}. Make sure you are in a Siduri instance directory.`);
30
- }
31
- const config = JSON.parse(node_fs_1.default.readFileSync(configPath, 'utf8'));
32
- const configuredOrgans = config.organs || {};
33
- // 1. Discover manifests of configured organs
34
- const registry = discovery_1.OrganRegistry.discover([
35
- node_path_1.default.join(projectDir, 'node_modules/@siduri-x'),
36
- node_path_1.default.resolve(__dirname, '../../packages/organs'),
37
- node_path_1.default.resolve(process.cwd(), 'packages/organs'),
38
- ]);
39
- const databaseOrgans = [];
40
- for (const [key] of Object.entries(configuredOrgans)) {
41
- const manifest = registry.get(key) || registry.getAll().find((m) => m.configKey === key || m.organType === key);
42
- if (manifest && manifest.database && manifest.database.migrationsDir) {
43
- // Resolve migrations directory relative to the organ package
44
- const candidatePaths = [
45
- node_path_1.default.resolve(projectDir, 'node_modules', manifest.name, manifest.database.migrationsDir),
46
- node_path_1.default.resolve(__dirname, '../../packages/organs', manifest.organType, manifest.database.migrationsDir),
47
- node_path_1.default.resolve(process.cwd(), 'packages/organs', manifest.organType, manifest.database.migrationsDir),
48
- ];
49
- const resolvedDir = candidatePaths.find((p) => node_fs_1.default.existsSync(p));
50
- if (resolvedDir) {
51
- databaseOrgans.push({
52
- organType: manifest.organType,
53
- manifest,
54
- migrationsDir: resolvedDir,
55
- });
56
- }
57
- }
58
- }
59
- if (databaseOrgans.length === 0) {
60
- return {
61
- status: 'NOOP',
62
- appliedMigrations: [],
63
- skippedMigrations: [],
64
- message: 'No database migrations are required by this instance.',
65
- };
66
- }
67
- // 2. Resolve DATABASE_URL
68
- const connectionString = options.connectionString ||
69
- options.env?.DATABASE_URL ||
70
- process.env.DATABASE_URL;
71
- if (!connectionString) {
72
- throw new Error('DATABASE_URL is required to run database migrations. Set it in .env or environment.');
73
- }
74
- const pool = new pg_1.Pool({
75
- connectionString,
76
- connectionTimeoutMillis: 5000,
77
- });
78
- const client = await pool.connect();
79
- try {
80
- // 3. Ensure migrations table exists
81
- await client.query(`
82
- CREATE TABLE IF NOT EXISTS siduri_migrations (
83
- id SERIAL PRIMARY KEY,
84
- name VARCHAR(255) UNIQUE NOT NULL,
85
- checksum VARCHAR(64) NOT NULL,
86
- applied_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
87
- );
88
- `);
89
- // 4. Fetch applied migrations
90
- const existingRes = await client.query('SELECT name, checksum FROM siduri_migrations ORDER BY id ASC;');
91
- const appliedMap = new Map();
92
- for (const row of existingRes.rows) {
93
- appliedMap.set(row.name, row.checksum);
94
- }
95
- // 5. Gather all migration files across database organs in deterministic order
96
- const allMigrationFiles = [];
97
- for (const item of databaseOrgans) {
98
- const files = node_fs_1.default.readdirSync(item.migrationsDir).filter((f) => f.endsWith('.sql')).sort();
99
- for (const file of files) {
100
- const fullPath = node_path_1.default.join(item.migrationsDir, file);
101
- const sql = node_fs_1.default.readFileSync(fullPath, 'utf8');
102
- const checksum = (0, node_crypto_1.createHash)('sha256').update(sql).digest('hex');
103
- allMigrationFiles.push({
104
- name: `${item.organType}/${file}`,
105
- fullPath,
106
- sql,
107
- checksum,
108
- });
109
- }
110
- }
111
- allMigrationFiles.sort((a, b) => a.name.localeCompare(b.name));
112
- const appliedList = [];
113
- const skippedList = [];
114
- for (const migration of allMigrationFiles) {
115
- const recordedChecksum = appliedMap.get(migration.name);
116
- if (recordedChecksum !== undefined) {
117
- if (recordedChecksum !== migration.checksum) {
118
- throw new Error(`Migration checksum mismatch for ${migration.name}! Recorded: ${recordedChecksum.slice(0, 8)}, Current: ${migration.checksum.slice(0, 8)}. Refusing to apply modified migration.`);
119
- }
120
- skippedList.push(migration.name);
121
- }
122
- else {
123
- // Apply migration in transaction
124
- await client.query('BEGIN');
125
- try {
126
- await client.query(migration.sql);
127
- await client.query('INSERT INTO siduri_migrations (name, checksum) VALUES ($1, $2)', [migration.name, migration.checksum]);
128
- await client.query('COMMIT');
129
- appliedList.push(migration.name);
130
- }
131
- catch (err) {
132
- await client.query('ROLLBACK');
133
- throw err;
134
- }
135
- }
136
- }
137
- const redacted = redactDatabaseUrl(connectionString);
138
- const message = appliedList.length > 0
139
- ? `Applied ${appliedList.length} migration(s) to ${redacted}`
140
- : `Database schema up to date on ${redacted}`;
141
- return {
142
- status: appliedList.length > 0 ? 'APPLIED' : 'UP_TO_DATE',
143
- appliedMigrations: appliedList,
144
- skippedMigrations: skippedList,
145
- message,
146
- };
147
- }
148
- finally {
149
- client.release();
150
- await pool.end().catch(() => { });
151
- }
9
+ return {
10
+ status: 'NOOP',
11
+ appliedMigrations: [],
12
+ skippedMigrations: [],
13
+ message: 'SQLite manages schema natively. No CLI migrations required.',
14
+ };
152
15
  }
package/dist/discovery.js CHANGED
@@ -38,11 +38,10 @@ class OrganRegistry {
38
38
  const rootsToScan = searchRoots && searchRoots.length > 0
39
39
  ? searchRoots
40
40
  : [
41
- // 1. Monorepo organs folder relative to cli
42
41
  node_path_1.default.resolve(__dirname, '../../packages/organs'),
43
- // 2. Monorepo organs folder relative to cwd
44
42
  node_path_1.default.resolve(process.cwd(), 'packages/organs'),
45
- // 3. Node modules of current directory or global resolution
43
+ node_path_1.default.resolve(__dirname, '../../packages'),
44
+ node_path_1.default.resolve(process.cwd(), 'packages'),
46
45
  node_path_1.default.resolve(process.cwd(), 'node_modules/@siduri-x'),
47
46
  node_path_1.default.resolve(__dirname, '../node_modules/@siduri-x'),
48
47
  ];
@@ -74,10 +73,9 @@ class OrganRegistry {
74
73
  // Continue scanning other roots
75
74
  }
76
75
  }
77
- // If running in a standalone environment without local package folders (e.g. via npx in an empty directory),
78
- // register canonical built-in @siduri-x/* organ manifests as fallback.
79
- if (registry.getAll().length === 0) {
80
- for (const builtinManifest of builtin_manifests_1.BUILTIN_ORGAN_MANIFESTS) {
76
+ // Always merge canonical built-in @siduri-x/* organ manifests as fallback for ones that weren't found
77
+ for (const builtinManifest of builtin_manifests_1.BUILTIN_ORGAN_MANIFESTS) {
78
+ if (!registry.get(builtinManifest.organType)) {
81
79
  registry.register(builtinManifest);
82
80
  }
83
81
  }
@@ -8,16 +8,17 @@ const discovery_1 = require("./discovery");
8
8
  const generator_1 = require("./generator");
9
9
  describe('Discovery & Dynamic Composition System Tests (Phase 3)', () => {
10
10
  const rootOrgansDir = node_path_1.default.resolve(__dirname, '../../packages/organs');
11
- test('Registry successfully discovers all 11 organ packages from workspace', () => {
12
- const registry = discovery_1.OrganRegistry.discover([rootOrgansDir]);
11
+ test('Registry successfully discovers all 12 organ packages from workspace', () => {
12
+ const registry = discovery_1.OrganRegistry.discover();
13
13
  const manifests = registry.getAll();
14
- expect(manifests.length).toBe(11);
14
+ expect(manifests.length).toBe(12);
15
15
  const organTypes = registry.getAvailableOrganTypes().sort();
16
16
  expect(organTypes).toEqual([
17
17
  'behavior',
18
18
  'body',
19
19
  'brain',
20
20
  'ear',
21
+ 'eknowledge',
21
22
  'hands',
22
23
  'knowledge',
23
24
  'memory',
@@ -28,7 +29,7 @@ describe('Discovery & Dynamic Composition System Tests (Phase 3)', () => {
28
29
  ]);
29
30
  });
30
31
  test('Generates valid instances from discovered manifests', () => {
31
- const registry = discovery_1.OrganRegistry.discover([rootOrgansDir]);
32
+ const registry = discovery_1.OrganRegistry.discover();
32
33
  // Test Brain + Hands + Vision
33
34
  const brain = registry.get('brain');
34
35
  const hands = registry.get('hands');