@happyvertical/smrt-personas 0.38.19 → 0.38.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/AGENTS.md +119 -7
  2. package/dist/__smrt-register__.d.ts +2 -0
  3. package/dist/__smrt-register__.d.ts.map +1 -0
  4. package/dist/agent-persona.d.ts +161 -0
  5. package/dist/agent-persona.d.ts.map +1 -0
  6. package/dist/directive-approval.d.ts +94 -0
  7. package/dist/directive-approval.d.ts.map +1 -0
  8. package/dist/directive-principal.d.ts +56 -0
  9. package/dist/directive-principal.d.ts.map +1 -0
  10. package/dist/directive-proposal.d.ts +103 -0
  11. package/dist/directive-proposal.d.ts.map +1 -0
  12. package/dist/feedback.d.ts +127 -0
  13. package/dist/feedback.d.ts.map +1 -0
  14. package/dist/index.d.ts +11 -294
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +829 -19
  17. package/dist/index.js.map +1 -1
  18. package/dist/manifest.json +919 -3
  19. package/dist/persona-instance.d.ts +249 -0
  20. package/dist/persona-instance.d.ts.map +1 -0
  21. package/dist/persona-memory.d.ts +45 -0
  22. package/dist/persona-memory.d.ts.map +1 -0
  23. package/dist/persona-prompt.d.ts +77 -0
  24. package/dist/persona-prompt.d.ts.map +1 -0
  25. package/dist/persona-resolver.d.ts +134 -0
  26. package/dist/persona-resolver.d.ts.map +1 -0
  27. package/dist/reflection-runner.d.ts +113 -0
  28. package/dist/reflection-runner.d.ts.map +1 -0
  29. package/dist/smrt-knowledge.json +519 -11
  30. package/dist/svelte/components/DirectiveReviewQueue.svelte +185 -0
  31. package/dist/svelte/components/DirectiveReviewQueue.svelte.d.ts +15 -0
  32. package/dist/svelte/components/DirectiveReviewQueue.svelte.d.ts.map +1 -0
  33. package/dist/svelte/index.d.ts +16 -0
  34. package/dist/svelte/index.d.ts.map +1 -0
  35. package/dist/svelte/index.js +13 -0
  36. package/dist/svelte/types.d.ts +46 -0
  37. package/dist/svelte/types.d.ts.map +1 -0
  38. package/dist/svelte/types.js +39 -0
  39. package/package.json +23 -7
@@ -1,26 +1,35 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "generatedAt": "2026-07-09T02:02:38.275Z",
3
+ "generatedAt": "2026-07-09T07:04:06.074Z",
4
4
  "packageName": "@happyvertical/smrt-personas",
5
- "packageVersion": "0.38.19",
5
+ "packageVersion": "0.38.21",
6
6
  "sourceManifestPath": "dist/manifest.json",
7
7
  "agentDocPath": "AGENTS.md",
8
8
  "sourceHashes": {
9
- "manifest": "94c1c2b447f941e2fa424248a3730976d46141366b5bb6bee4234dd4c0fae506",
10
- "packageJson": "57b89b69bd4bc4eefb41852706f1aaa4c9c0d4af1608357a0bebd9112af92ba2",
11
- "agents": "ef998dbc563d3d2905c694cd77a99576a6fd2d16d10f675014dbbe9aec1cbf5f"
9
+ "manifest": "7c20bd9c814a3deef5c573d56a172f40cb6dd594baaa40f2e4b8fd36d56c0d8b",
10
+ "packageJson": "7fd08960eca95f18b7b58e209e56fae1b46365531551cbb6212a935dbbbdf35c",
11
+ "agents": "f35cdbfeae2e2235818031815c55c24c59eebbacc3aec00406f079d0f990abd3"
12
12
  },
13
13
  "exports": [
14
14
  ".",
15
15
  "./manifest",
16
- "./manifest.json"
16
+ "./manifest.json",
17
+ "./svelte"
17
18
  ],
18
19
  "dependencies": {
19
20
  "@happyvertical/smrt-agents": "workspace:*",
20
21
  "@happyvertical/smrt-core": "workspace:*",
22
+ "@happyvertical/smrt-prompts": "workspace:*",
21
23
  "@happyvertical/smrt-tenancy": "workspace:*",
24
+ "@happyvertical/smrt-ui": "workspace:*",
25
+ "@happyvertical/smrt-users": "workspace:*",
26
+ "@happyvertical/sql": "catalog:",
22
27
  "@happyvertical/smrt-vitest": "workspace:*",
28
+ "@sveltejs/package": "^2.5.8",
29
+ "@sveltejs/vite-plugin-svelte": "^7.1.2",
23
30
  "@types/node": "24.13.2",
31
+ "svelte": "^5.56.4",
32
+ "svelte-check": "^4.7.1",
24
33
  "typescript": "^5.9.3",
25
34
  "vite": "^8.1.3",
26
35
  "vitest": "^4.1.9"
@@ -28,10 +37,15 @@
28
37
  "smrtDependencies": [
29
38
  "@happyvertical/smrt-agents",
30
39
  "@happyvertical/smrt-core",
40
+ "@happyvertical/smrt-prompts",
31
41
  "@happyvertical/smrt-tenancy",
42
+ "@happyvertical/smrt-ui",
43
+ "@happyvertical/smrt-users",
32
44
  "@happyvertical/smrt-vitest"
33
45
  ],
34
- "sdkDependencies": [],
46
+ "sdkDependencies": [
47
+ "@happyvertical/sql"
48
+ ],
35
49
  "tags": [],
36
50
  "risks": [],
37
51
  "objects": [
@@ -230,6 +244,408 @@
230
244
  ],
231
245
  "tags": [],
232
246
  "risks": []
247
+ },
248
+ {
249
+ "name": "DirectiveProposal",
250
+ "qualifiedName": "@happyvertical/smrt-personas:DirectiveProposal",
251
+ "collection": "directiveproposals",
252
+ "tableName": "directive_proposals",
253
+ "packageName": "@happyvertical/smrt-personas",
254
+ "extends": "SmrtObject",
255
+ "fields": [
256
+ {
257
+ "name": "tenantId",
258
+ "type": "text",
259
+ "required": true,
260
+ "columnType": "UUID"
261
+ },
262
+ {
263
+ "name": "personaId",
264
+ "type": "foreignKey",
265
+ "required": true,
266
+ "related": "AgentPersona",
267
+ "columnType": "UUID"
268
+ },
269
+ {
270
+ "name": "agentClass",
271
+ "type": "text",
272
+ "required": false,
273
+ "columnType": "TEXT"
274
+ },
275
+ {
276
+ "name": "promptKey",
277
+ "type": "text",
278
+ "required": false,
279
+ "columnType": "TEXT"
280
+ },
281
+ {
282
+ "name": "proposedInstructions",
283
+ "type": "text",
284
+ "required": false,
285
+ "columnType": "TEXT"
286
+ },
287
+ {
288
+ "name": "currentInstructions",
289
+ "type": "text",
290
+ "required": false,
291
+ "columnType": "TEXT"
292
+ },
293
+ {
294
+ "name": "rationale",
295
+ "type": "text",
296
+ "required": false,
297
+ "columnType": "TEXT"
298
+ },
299
+ {
300
+ "name": "evidence",
301
+ "type": "text",
302
+ "required": false,
303
+ "columnType": "TEXT"
304
+ },
305
+ {
306
+ "name": "status",
307
+ "type": "text",
308
+ "required": false,
309
+ "columnType": "TEXT"
310
+ },
311
+ {
312
+ "name": "fingerprint",
313
+ "type": "text",
314
+ "required": false,
315
+ "columnType": "TEXT"
316
+ },
317
+ {
318
+ "name": "confidence",
319
+ "type": "decimal",
320
+ "required": false,
321
+ "columnType": "REAL"
322
+ },
323
+ {
324
+ "name": "proposedBy",
325
+ "type": "text",
326
+ "required": false,
327
+ "columnType": "TEXT"
328
+ },
329
+ {
330
+ "name": "reviewedBy",
331
+ "type": "text",
332
+ "required": false,
333
+ "columnType": "TEXT"
334
+ },
335
+ {
336
+ "name": "reviewedAt",
337
+ "type": "datetime",
338
+ "required": false,
339
+ "columnType": "TIMESTAMP"
340
+ },
341
+ {
342
+ "name": "reviewNote",
343
+ "type": "text",
344
+ "required": false,
345
+ "columnType": "TEXT"
346
+ },
347
+ {
348
+ "name": "activatedOverrideId",
349
+ "type": "text",
350
+ "required": false,
351
+ "columnType": "TEXT"
352
+ }
353
+ ],
354
+ "relationships": [
355
+ {
356
+ "name": "personaId",
357
+ "type": "foreignKey",
358
+ "required": true,
359
+ "related": "AgentPersona",
360
+ "columnType": "UUID"
361
+ }
362
+ ],
363
+ "methods": [
364
+ "getEvidence",
365
+ "isPending",
366
+ "setEvidence"
367
+ ],
368
+ "surfaces": [
369
+ {
370
+ "kind": "api",
371
+ "name": "directiveproposals.list",
372
+ "operation": "list",
373
+ "objectName": "@happyvertical/smrt-personas:DirectiveProposal",
374
+ "path": "/directiveproposals",
375
+ "method": "GET"
376
+ },
377
+ {
378
+ "kind": "api",
379
+ "name": "directiveproposals.get",
380
+ "operation": "get",
381
+ "objectName": "@happyvertical/smrt-personas:DirectiveProposal",
382
+ "path": "/directiveproposals/[id]",
383
+ "method": "GET"
384
+ },
385
+ {
386
+ "kind": "cli",
387
+ "name": "directiveproposal_list",
388
+ "operation": "list",
389
+ "objectName": "@happyvertical/smrt-personas:DirectiveProposal"
390
+ },
391
+ {
392
+ "kind": "cli",
393
+ "name": "directiveproposal_get",
394
+ "operation": "get",
395
+ "objectName": "@happyvertical/smrt-personas:DirectiveProposal"
396
+ },
397
+ {
398
+ "kind": "mcp",
399
+ "name": "directiveproposal_list",
400
+ "operation": "list",
401
+ "objectName": "@happyvertical/smrt-personas:DirectiveProposal"
402
+ },
403
+ {
404
+ "kind": "mcp",
405
+ "name": "directiveproposal_get",
406
+ "operation": "get",
407
+ "objectName": "@happyvertical/smrt-personas:DirectiveProposal"
408
+ }
409
+ ],
410
+ "relationshipFeatures": [
411
+ "foreignKey",
412
+ "uuidColumns"
413
+ ],
414
+ "tags": [],
415
+ "risks": []
416
+ },
417
+ {
418
+ "name": "DirectiveProposalCollection",
419
+ "qualifiedName": "@happyvertical/smrt-personas:DirectiveProposalCollection",
420
+ "collection": "directiveproposals",
421
+ "tableName": "directive_proposals",
422
+ "packageName": "@happyvertical/smrt-personas",
423
+ "extends": "SmrtCollection",
424
+ "fields": [],
425
+ "relationships": [],
426
+ "methods": [
427
+ "findByFingerprint",
428
+ "pending"
429
+ ],
430
+ "surfaces": [],
431
+ "relationshipFeatures": [
432
+ "uuidColumns"
433
+ ],
434
+ "tags": [],
435
+ "risks": []
436
+ },
437
+ {
438
+ "name": "Feedback",
439
+ "qualifiedName": "@happyvertical/smrt-personas:Feedback",
440
+ "collection": "feedbacks",
441
+ "tableName": "agent_feedback",
442
+ "packageName": "@happyvertical/smrt-personas",
443
+ "extends": "SmrtObject",
444
+ "fields": [
445
+ {
446
+ "name": "tenantId",
447
+ "type": "text",
448
+ "required": false,
449
+ "columnType": "UUID"
450
+ },
451
+ {
452
+ "name": "personaId",
453
+ "type": "foreignKey",
454
+ "required": true,
455
+ "related": "AgentPersona",
456
+ "columnType": "UUID"
457
+ },
458
+ {
459
+ "name": "agentClass",
460
+ "type": "text",
461
+ "required": false,
462
+ "columnType": "TEXT"
463
+ },
464
+ {
465
+ "name": "memoryScope",
466
+ "type": "text",
467
+ "required": false,
468
+ "columnType": "TEXT"
469
+ },
470
+ {
471
+ "name": "scope",
472
+ "type": "text",
473
+ "required": false,
474
+ "columnType": "TEXT"
475
+ },
476
+ {
477
+ "name": "key",
478
+ "type": "text",
479
+ "required": false,
480
+ "columnType": "TEXT"
481
+ },
482
+ {
483
+ "name": "signalType",
484
+ "type": "text",
485
+ "required": false,
486
+ "columnType": "TEXT"
487
+ },
488
+ {
489
+ "name": "source",
490
+ "type": "text",
491
+ "required": false,
492
+ "columnType": "TEXT"
493
+ },
494
+ {
495
+ "name": "correlationId",
496
+ "type": "text",
497
+ "required": true,
498
+ "columnType": "TEXT"
499
+ },
500
+ {
501
+ "name": "correlationType",
502
+ "type": "text",
503
+ "required": false,
504
+ "columnType": "TEXT"
505
+ },
506
+ {
507
+ "name": "rating",
508
+ "type": "decimal",
509
+ "required": false,
510
+ "columnType": "REAL"
511
+ },
512
+ {
513
+ "name": "metric",
514
+ "type": "decimal",
515
+ "required": false,
516
+ "columnType": "REAL"
517
+ },
518
+ {
519
+ "name": "success",
520
+ "type": "boolean",
521
+ "required": false,
522
+ "columnType": "BOOLEAN"
523
+ },
524
+ {
525
+ "name": "correction",
526
+ "type": "text",
527
+ "required": false,
528
+ "columnType": "TEXT"
529
+ },
530
+ {
531
+ "name": "comment",
532
+ "type": "text",
533
+ "required": false,
534
+ "columnType": "TEXT"
535
+ },
536
+ {
537
+ "name": "actorId",
538
+ "type": "text",
539
+ "required": false,
540
+ "columnType": "TEXT"
541
+ },
542
+ {
543
+ "name": "metadata",
544
+ "type": "text",
545
+ "required": false,
546
+ "columnType": "TEXT"
547
+ },
548
+ {
549
+ "name": "reinforcedAt",
550
+ "type": "datetime",
551
+ "required": false,
552
+ "columnType": "TIMESTAMP"
553
+ }
554
+ ],
555
+ "relationships": [
556
+ {
557
+ "name": "personaId",
558
+ "type": "foreignKey",
559
+ "required": true,
560
+ "related": "AgentPersona",
561
+ "columnType": "UUID"
562
+ }
563
+ ],
564
+ "methods": [
565
+ "getCorrectionValue",
566
+ "getMetadata",
567
+ "isHuman",
568
+ "setMetadata",
569
+ "toLearningOutcome",
570
+ "updateMetadata"
571
+ ],
572
+ "surfaces": [
573
+ {
574
+ "kind": "api",
575
+ "name": "feedbacks.list",
576
+ "operation": "list",
577
+ "objectName": "@happyvertical/smrt-personas:Feedback",
578
+ "path": "/feedbacks",
579
+ "method": "GET"
580
+ },
581
+ {
582
+ "kind": "api",
583
+ "name": "feedbacks.get",
584
+ "operation": "get",
585
+ "objectName": "@happyvertical/smrt-personas:Feedback",
586
+ "path": "/feedbacks/[id]",
587
+ "method": "GET"
588
+ },
589
+ {
590
+ "kind": "api",
591
+ "name": "feedbacks.create",
592
+ "operation": "create",
593
+ "objectName": "@happyvertical/smrt-personas:Feedback",
594
+ "path": "/feedbacks",
595
+ "method": "POST"
596
+ },
597
+ {
598
+ "kind": "cli",
599
+ "name": "feedback_list",
600
+ "operation": "list",
601
+ "objectName": "@happyvertical/smrt-personas:Feedback"
602
+ },
603
+ {
604
+ "kind": "cli",
605
+ "name": "feedback_get",
606
+ "operation": "get",
607
+ "objectName": "@happyvertical/smrt-personas:Feedback"
608
+ },
609
+ {
610
+ "kind": "mcp",
611
+ "name": "feedback_list",
612
+ "operation": "list",
613
+ "objectName": "@happyvertical/smrt-personas:Feedback"
614
+ },
615
+ {
616
+ "kind": "mcp",
617
+ "name": "feedback_get",
618
+ "operation": "get",
619
+ "objectName": "@happyvertical/smrt-personas:Feedback"
620
+ }
621
+ ],
622
+ "relationshipFeatures": [
623
+ "foreignKey",
624
+ "uuidColumns"
625
+ ],
626
+ "tags": [],
627
+ "risks": []
628
+ },
629
+ {
630
+ "name": "FeedbackCollection",
631
+ "qualifiedName": "@happyvertical/smrt-personas:FeedbackCollection",
632
+ "collection": "feedbacks",
633
+ "tableName": "agent_feedback",
634
+ "packageName": "@happyvertical/smrt-personas",
635
+ "extends": "SmrtCollection",
636
+ "fields": [],
637
+ "relationships": [],
638
+ "methods": [
639
+ "byCorrelation",
640
+ "forPersona",
641
+ "forScope"
642
+ ],
643
+ "surfaces": [],
644
+ "relationshipFeatures": [
645
+ "uuidColumns"
646
+ ],
647
+ "tags": [],
648
+ "risks": []
233
649
  }
234
650
  ],
235
651
  "surfaces": [
@@ -296,16 +712,108 @@
296
712
  "name": "agentpersona_get",
297
713
  "operation": "get",
298
714
  "objectName": "@happyvertical/smrt-personas:AgentPersona"
715
+ },
716
+ {
717
+ "kind": "api",
718
+ "name": "directiveproposals.list",
719
+ "operation": "list",
720
+ "objectName": "@happyvertical/smrt-personas:DirectiveProposal",
721
+ "path": "/directiveproposals",
722
+ "method": "GET"
723
+ },
724
+ {
725
+ "kind": "api",
726
+ "name": "directiveproposals.get",
727
+ "operation": "get",
728
+ "objectName": "@happyvertical/smrt-personas:DirectiveProposal",
729
+ "path": "/directiveproposals/[id]",
730
+ "method": "GET"
731
+ },
732
+ {
733
+ "kind": "cli",
734
+ "name": "directiveproposal_list",
735
+ "operation": "list",
736
+ "objectName": "@happyvertical/smrt-personas:DirectiveProposal"
737
+ },
738
+ {
739
+ "kind": "cli",
740
+ "name": "directiveproposal_get",
741
+ "operation": "get",
742
+ "objectName": "@happyvertical/smrt-personas:DirectiveProposal"
743
+ },
744
+ {
745
+ "kind": "mcp",
746
+ "name": "directiveproposal_list",
747
+ "operation": "list",
748
+ "objectName": "@happyvertical/smrt-personas:DirectiveProposal"
749
+ },
750
+ {
751
+ "kind": "mcp",
752
+ "name": "directiveproposal_get",
753
+ "operation": "get",
754
+ "objectName": "@happyvertical/smrt-personas:DirectiveProposal"
755
+ },
756
+ {
757
+ "kind": "api",
758
+ "name": "feedbacks.list",
759
+ "operation": "list",
760
+ "objectName": "@happyvertical/smrt-personas:Feedback",
761
+ "path": "/feedbacks",
762
+ "method": "GET"
763
+ },
764
+ {
765
+ "kind": "api",
766
+ "name": "feedbacks.get",
767
+ "operation": "get",
768
+ "objectName": "@happyvertical/smrt-personas:Feedback",
769
+ "path": "/feedbacks/[id]",
770
+ "method": "GET"
771
+ },
772
+ {
773
+ "kind": "api",
774
+ "name": "feedbacks.create",
775
+ "operation": "create",
776
+ "objectName": "@happyvertical/smrt-personas:Feedback",
777
+ "path": "/feedbacks",
778
+ "method": "POST"
779
+ },
780
+ {
781
+ "kind": "cli",
782
+ "name": "feedback_list",
783
+ "operation": "list",
784
+ "objectName": "@happyvertical/smrt-personas:Feedback"
785
+ },
786
+ {
787
+ "kind": "cli",
788
+ "name": "feedback_get",
789
+ "operation": "get",
790
+ "objectName": "@happyvertical/smrt-personas:Feedback"
791
+ },
792
+ {
793
+ "kind": "mcp",
794
+ "name": "feedback_list",
795
+ "operation": "list",
796
+ "objectName": "@happyvertical/smrt-personas:Feedback"
797
+ },
798
+ {
799
+ "kind": "mcp",
800
+ "name": "feedback_get",
801
+ "operation": "get",
802
+ "objectName": "@happyvertical/smrt-personas:Feedback"
803
+ }
804
+ ],
805
+ "prompts": [
806
+ {
807
+ "filePath": "src/persona-prompt.ts"
299
808
  }
300
809
  ],
301
- "prompts": [],
302
810
  "relationshipsV2": {
303
- "foreignKeyFields": 0,
811
+ "foreignKeyFields": 2,
304
812
  "crossPackageRefFields": 2,
305
813
  "junctionCollections": 0,
306
814
  "hierarchicalObjects": 0,
307
815
  "polymorphicAssociations": 0,
308
- "uuidColumns": 5
816
+ "uuidColumns": 13
309
817
  },
310
- "agentDoc": "# @happyvertical/smrt-personas\n\nTenant-owned, context-scoped agent personas and their resolution. Layers over\n`@happyvertical/smrt-agents`' `TenantAgent` availability/ceiling gate to decide\n*how* an agent behaves for a given tenant and context (its `TenantAgent` binding\ndecides *whether* it runs and caps its capabilities).\n\nThis is the L2 foundation of the \"learning agents\" epic (#1885). Foundation\nonly: `AgentPersona` + `PersonaResolver`. `ExecuteAsPrincipal`, feedback, the\nreflection runner, and multi-instance routing are separate issues\n(#1888/#1889/#1890).\n\n## Models\n\n- **AgentPersona** (`@TenantScoped({ mode: 'required' })`, table `agent_personas`):\n a behavioral profile for one agent class within one tenant. Many per\n `(tenant, agentClass)`; unique on `(tenant_id, agent_class, name)` via\n `conflictColumns`. Fields:\n - `agentClass` — canonical qualified agent type (e.g.\n `@happyvertical/smrt-agents:Praeco`); stored canonical so it lines up with\n `TenantAgent.agentClass`.\n - `name` — persona name, unique per `(tenant, agentClass)`.\n - `contextType` / `contextId` — optional scoping. No `contextType` = a\n tenant-wide default; `contextType` only = type-scoped; both = exact context.\n - `instructions` — system prompt layered onto the agent.\n - `allowedTools` — JSON array of tool ids stored as a string; use\n `getAllowedTools()` / `setAllowedTools()` (tolerant `try/catch` parse).\n - `runAsUserId` — `@crossPackageRef('@happyvertical/smrt-users:User')`, the\n principal the persona runs as.\n - `actsAsProfileId` — optional `@crossPackageRef('@happyvertical/smrt-profiles:Profile')`,\n a `Bot` identity for audit/attribution.\n - `memoryScope` — memory/reflection partition key (empty → resolver-derived).\n - `priority` — integer; higher wins among personas that apply at the same\n tenant level.\n - `enabled` — boolean; only enabled personas are resolved.\n\n## Collection\n\n`AgentPersonaCollection`:\n\n- `byTenantAndClass(tenantId, agentClass)` — all personas for the pair\n (canonicalized), ordered by descending priority then name.\n- `findActive(tenantId, agentClass, context?)` — enabled personas applicable to\n a context, same ordering.\n\n## PersonaResolver\n\n`resolve(tenantId, agentClass, context, options) → ResolvedPersona` layers,\nbottom to top: **manifest defaults → `TenantAgent` gate/ceiling → AgentPersona**.\n\n- **Hierarchy inheritance** mirrors `TenantAgentCollection.resolveForTenant`:\n walks the requesting tenant then its ancestors (`options.getAncestorIds`,\n nearest first). The nearest level with any applicable persona wins (a closer\n level shadows a farther one).\n- **Selection within a level**: most context-specific first\n (exact > type-scoped > tenant-wide), then highest `priority`, then name.\n- **Ceiling**: `options.availability` (a `PersonaAvailabilityGate`) gates\n availability and intersects the resolved `allowedTools`. Build one from a\n `ResolvedAgentAvailability` with `availabilityFromResolvedAgent()`.\n- **Default fallback**: when no persona applies, returns a defined default\n (`source: 'default'`, `name: 'default'`) from manifest defaults + the gate.\n\nBecause the walk reads personas across tenants, `resolve()` is intended for a\nsystem/admin path where cross-tenant reads are allowed — not inside a strict\nper-tenant interceptor context (same expectation as `resolveForTenant`).\n\n## Dependencies\n\nLeaf package (acyclic by construction — nothing depends back on it):\n\n- Runtime: `@happyvertical/smrt-core`, `@happyvertical/smrt-tenancy`,\n `@happyvertical/smrt-agents` (the `TenantAgent` type it bridges).\n- `runAsUserId` / `actsAsProfileId` reference `@happyvertical/smrt-users` and\n `@happyvertical/smrt-profiles` via `@crossPackageRef` string ids only — no\n package edge (keeps the DAG minimal). No inter-`smrt-*` `peerDependencies`.\n\n## Gotchas\n\n- **`conflictColumns` replaces the default `(slug, context)` unique index** — the\n unique key is `(tenant_id, agent_class, name)`, so same-named personas across\n tenants are allowed and tenant isolation is preserved.\n- **`allowedTools` is text, not `type: 'json'`** — stored as a JSON string and\n read through the helpers, so it never auto-hydrates to an object.\n- **Resolver runs outside strict tenant context** — it deliberately crosses\n tenants to walk the hierarchy.\n"
818
+ "agentDoc": "# @happyvertical/smrt-personas\n\nTenant-owned, context-scoped agent personas and their resolution. Layers over\n`@happyvertical/smrt-agents`' `TenantAgent` availability/ceiling gate to decide\n*how* an agent behaves for a given tenant and context (its `TenantAgent` binding\ndecides *whether* it runs and caps its capabilities).\n\nThis is the L2 layer of the \"learning agents\" epic (#1885): the persona foundation\n(`AgentPersona` + `PersonaResolver`), the **persona learning & adaptation loop**\n(#1889) — feedback → confidence-scored reinforcement → a scheduled reflection\nrunner that emits pending directive proposals → a permission-gated approval\nsurface that activates an approved rewrite as a tenant/persona-scoped prompt\noverride — and the persona-as-durable-instance / multi-instance helpers (#1890,\nbelow). `ExecuteAsPrincipal` remains a separate issue (#1888).\n\n## Models\n\n- **AgentPersona** (`@TenantScoped({ mode: 'required' })`, table `agent_personas`):\n a behavioral profile for one agent class within one tenant. Many per\n `(tenant, agentClass)`; unique on `(tenant_id, agent_class, name)` via\n `conflictColumns`. Fields:\n - `agentClass` — canonical qualified agent type (e.g.\n `@happyvertical/smrt-agents:Praeco`); stored canonical so it lines up with\n `TenantAgent.agentClass`.\n - `name` — persona name, unique per `(tenant, agentClass)`.\n - `contextType` / `contextId` — optional scoping. No `contextType` = a\n tenant-wide default; `contextType` only = type-scoped; both = exact context.\n - `instructions` — system prompt layered onto the agent.\n - `allowedTools` — JSON array of tool ids stored as a string; use\n `getAllowedTools()` / `setAllowedTools()` (tolerant `try/catch` parse).\n - `runAsUserId` — `@crossPackageRef('@happyvertical/smrt-users:User')`, the\n principal the persona runs as.\n - `actsAsProfileId` — optional `@crossPackageRef('@happyvertical/smrt-profiles:Profile')`,\n a `Bot` identity for audit/attribution.\n - `memoryScope` — memory/reflection partition key (empty → resolver-derived).\n - `priority` — integer; higher wins among personas that apply at the same\n tenant level.\n - `enabled` — boolean; only enabled personas are resolved.\n\n- **Feedback** (`@TenantScoped({ mode: 'optional' })`, table `agent_feedback`):\n one reinforcement signal against a persona and the learning episode it judges.\n Human signals (`accept` / `reject` / `correction` / `rating`) and autonomous\n signals (`outcome` / `metric`) each carry a required **`correlationId`** (+\n optional `correlationType`) back to the AI call / dispatch / job. `scope` /\n `key` name the `LearningMemory` episode; `toLearningOutcome()` maps the signal\n onto a `LearningOutcome` (feeds reinforcement, #1886). `FeedbackCollection`:\n `forPersona`, `forScope`, `byCorrelation`.\n\n- **DirectiveProposal** (`@TenantScoped({ mode: 'required' })`, table\n `directive_proposals`): a pending, human-reviewable proposed edit to a\n persona's `instructions`. Read-only generated surface (`api`/`cli`/`mcp` =\n `list`/`get`) — writes go through the gated approval service, not CRUD. Key\n fields: `promptKey`, `proposedInstructions`, `currentInstructions` (diff),\n `rationale`, `evidence` (JSON: episode + feedback ids), `status`\n (`pending`/`approved`/`rejected`/`superseded`), `fingerprint` (dedup so a\n rejected rewrite is never re-surfaced), `proposedBy`, `reviewedBy` /\n `reviewedAt` / `activatedOverrideId`. `DirectiveProposalCollection`:\n `pending(...)` (the review queue), `findByFingerprint(...)`.\n\n## Learning loop (#1889)\n\n- **Persona instructions via prompt overrides** (`persona-prompt.ts`): a\n persona's instructions apply through a per-persona registered prompt key\n (`persona.<id>.instructions`, neutral empty base template) plus a\n tenant-scoped `prompt_override`. Two personas of the same tenant/class resolve\n different instructions without colliding on the override `(key, context)`\n identity. `ensurePersonaInstructionsPrompt` (idempotent registration,\n `editable` configurable), `applyPersonaInstructions` /\n `upsertPromptTemplateOverride` (write the override — flows through\n `PromptOverride.save()` → `validatePromptOverride`, so a non-editable template\n is refused), `resolvePersonaInstructions`.\n- **Memory scoping** (`persona-memory.ts`): `personaLearningMemory` routes a\n `LearningMemory` by the persona's `memoryScope` (used as `owner_id`), so\n personas learn independently. `reinforceFromFeedback` applies a `Feedback`\n signal to memory as automatic, confidence-only reinforcement (ungated).\n- **ReflectionRunner** (`reflection-runner.ts`): a schedulable pass that, under\n the autonomous reflection principal (which **lacks**\n `personas.activate-directive`), reinforces recent feedback, consolidates\n episodes + feedback via an injected `reflect` boundary (the AI call, mocked in\n tests), and records a **pending** `DirectiveProposal` (deduped by fingerprint).\n It cannot activate anything.\n- **The gate = a permission split** (`directive-principal.ts`,\n `directive-approval.ts`): activating a directive is the permissioned operation\n `personas.activate-directive` (contributed to the manifest permission catalog\n via `registerPermissionDefinitions`). `DirectiveApprovalService.approve` /\n `reject` first `assertCanActivate(principal)`; approval writes the\n persona-scoped override and records an `accept` signal, rejection records a\n `reject` signal and closes the proposal. A `DirectivePrincipal` is built from\n granted slugs (`principalFromPermissions`) or a `PermissionResolver`\n (`resolveDirectivePrincipal`).\n\n## Svelte UI\n\n`@happyvertical/smrt-personas/svelte` ships a minimal, presentational\n`DirectiveReviewQueue` component (Svelte 5): it lists pending proposals with a\ncurrent-vs-proposed diff and Approve/Reject actions, delegating to `onApprove`\n(optionally with reviewer-edited text) / `onReject` callbacks a host wires to\nthe gated `DirectiveApprovalService`. Built with `svelte-package`; Svelte tests\nrun in CI only.\n\n## Collection\n\n`AgentPersonaCollection`:\n\n- `byTenantAndClass(tenantId, agentClass)` — all personas for the pair\n (canonicalized), ordered by descending priority then name.\n- `findActive(tenantId, agentClass, context?)` — enabled personas applicable to\n a context, same ordering.\n\n## PersonaResolver\n\n`resolve(tenantId, agentClass, context, options) → ResolvedPersona` layers,\nbottom to top: **manifest defaults → `TenantAgent` gate/ceiling → AgentPersona**.\n\n- **Hierarchy inheritance** mirrors `TenantAgentCollection.resolveForTenant`:\n walks the requesting tenant then its ancestors (`options.getAncestorIds`,\n nearest first). The nearest level with any applicable persona wins (a closer\n level shadows a farther one).\n- **Selection within a level**: most context-specific first\n (exact > type-scoped > tenant-wide), then highest `priority`, then name.\n- **Ceiling**: `options.availability` (a `PersonaAvailabilityGate`) gates\n availability and intersects the resolved `allowedTools`. Build one from a\n `ResolvedAgentAvailability` with `availabilityFromResolvedAgent()`.\n- **Default fallback**: when no persona applies, returns a defined default\n (`source: 'default'`, `name: 'default'`) from manifest defaults + the gate.\n\nBecause the walk reads personas across tenants, `resolve()` is intended for a\nsystem/admin path where cross-tenant reads are allowed — not inside a strict\nper-tenant interceptor context (same expectation as `resolveForTenant`).\n\n## Persona as Durable Instance (#1890)\n\nA persona is a **durable instance** of an agent class — N per tenant, each\nindependently scheduled, memory-scoped, and permission-scoped. `persona-instance.ts`\nwires that to the `@happyvertical/smrt-agents` multi-instance primitives (which are\nper-package opt-in via `static multiInstance`, singleton by default):\n\n- **Identity** — `personaInstanceKey(persona)` returns the persona id, or `null`\n for the `default` persona (`DEFAULT_PERSONA_NAME`). A `null` key reuses the\n **singleton** runtime identity (class-keyed dispatch subscriber + un-suffixed\n memory scope). `agentOptionsForPersona(persona)` projects `{ instanceKey,\n tenantId }` to spread into the agent constructor.\n- **Scheduling** — `schedulePersonaInstance(schedules, persona, { cron, … })`\n creates an `AgentSchedule` for the instance. The instance key rides in\n `agentConfig` (which the scheduler spreads into the agent constructor →\n `AgentOptions.instanceKey`); `agentId` is left **null** on purpose, because the\n scheduler copies `agentId`→`SmrtJob.objectId` and the `TaskRunner`\n `loadFromId()`s it against the agent's STI table — a persona id is not a row\n there. The default persona carries no key → runs the singleton.\n- **Admin** — `buildPersonaInstanceAdmin(personas, { tenantId, agentClass,\n manifest })` renders the class's `uiSlots`/`adminRoutes` **once per instance**\n (with the per-instance dispatch subscriber); `addPersonaInstance()` /\n `removePersonaInstance(personaId)` are the add/remove affordances.\n- **Non-destructive upgrade** — `upgradeSingletonToDefaultPersona(personas, {\n tenantId, agentClass, runAsUserId, … })` maps an existing singleton to a single\n `default` persona. It's null-keyed, so the running agent's dispatch subscriber,\n memory, and config are untouched; idempotent (a second call returns the\n existing default with `created: false` and never overwrites it).\n\n## Dependencies\n\nLeaf package (acyclic by construction — nothing depends back on it):\n\n- Runtime: `@happyvertical/smrt-core`, `@happyvertical/smrt-tenancy`,\n `@happyvertical/smrt-agents` (the `TenantAgent` type it bridges, plus the\n `AgentSchedule`/multi-instance surface `persona-instance.ts` builds on),\n `@happyvertical/smrt-prompts` (persona instructions via `PromptOverride` +\n `resolvePrompt`), `@happyvertical/smrt-users` (the permission catalog slug +\n `PermissionResolver` principal), and `@happyvertical/sql` (`DatabaseInterface`\n type). These edges are all acyclic (none of those packages depend on personas).\n- Svelte peer + `@happyvertical/smrt-ui` for the optional `./svelte` review-queue\n component.\n- `runAsUserId` / `actsAsProfileId` reference `@happyvertical/smrt-users` and\n `@happyvertical/smrt-profiles` via `@crossPackageRef` string ids only. No\n inter-`smrt-*` `peerDependencies`.\n\n## Gotchas\n\n- **`conflictColumns` replaces the default `(slug, context)` unique index** — the\n unique key is `(tenant_id, agent_class, name)`, so same-named personas across\n tenants are allowed and tenant isolation is preserved.\n- **`allowedTools` is text, not `type: 'json'`** — stored as a JSON string and\n read through the helpers, so it never auto-hydrates to an object.\n- **Resolver runs outside strict tenant context** — it deliberately crosses\n tenants to walk the hierarchy.\n- **The gate is the permission split, not a bespoke check** — the reflection\n principal simply lacks `personas.activate-directive`, so it can only propose;\n everything privileged in `DirectiveApprovalService` calls `assertCanActivate`\n first. Instruction rewrites are gated; confidence-only reinforcement is not.\n- **Persona instructions live in the override layer, not the base prompt** — the\n per-persona prompt key registers a neutral empty template, so re-registration\n stays idempotent as instructions change; the tenant-scoped override carries the\n actual text. Activation still honours `validatePromptOverride` editability.\n- **Directive dedup is by fingerprint across all statuses** — the runner skips a\n proposal whose `(personaId, promptKey, proposedInstructions)` fingerprint\n already exists (pending, approved, or rejected), so a rejected rewrite is never\n re-surfaced.\n"
311
819
  }