@agent-native/core 0.187.0-nightly-20260924172231 → 0.187.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 (35) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/CHANGELOG.md +11 -0
  3. package/corpus/templates/analytics/app/i18n/en-US.ts +1 -0
  4. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +101 -11
  5. package/corpus/templates/analytics/changelog/2026-09-24-session-list-hides-0m-recordings-by-default.md +6 -0
  6. package/corpus/templates/assets/CHANGELOG.md +6 -0
  7. package/corpus/templates/brain/actions/backfill-search-embeddings.ts +337 -0
  8. package/corpus/templates/brain/actions/get-brain-health.ts +1 -1
  9. package/corpus/templates/brain/app/lib/brain.ts +18 -0
  10. package/corpus/templates/brain/jobs/process-ingest-queue.ts +25 -3
  11. package/corpus/templates/brain/server/lib/brain-health.ts +197 -57
  12. package/corpus/templates/brain/server/lib/demo.ts +25 -4
  13. package/corpus/templates/brain/server/lib/hybrid-search.ts +4 -1
  14. package/corpus/templates/brain/server/lib/ingest-queue.ts +1 -0
  15. package/corpus/templates/brain/server/lib/search-index.ts +254 -17
  16. package/corpus/templates/brain/server/lib/search.ts +27 -0
  17. package/corpus/templates/calendar/CHANGELOG.md +6 -0
  18. package/corpus/templates/clips/CHANGELOG.md +10 -0
  19. package/corpus/templates/content/CHANGELOG.md +22 -0
  20. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +559 -227
  21. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +25 -1
  22. package/corpus/templates/design/CHANGELOG.md +36 -0
  23. package/corpus/templates/dispatch/CHANGELOG.md +7 -0
  24. package/corpus/templates/mail/CHANGELOG.md +17 -0
  25. package/corpus/templates/slides/CHANGELOG.md +7 -0
  26. package/dist/automation/index.d.ts +1 -1
  27. package/dist/collab/awareness.d.ts +2 -2
  28. package/dist/collab/struct-routes.d.ts +1 -1
  29. package/dist/embeddings/index.d.ts +5 -0
  30. package/dist/embeddings/index.js +44 -11
  31. package/dist/observability/routes.d.ts +5 -5
  32. package/dist/progress/routes.d.ts +1 -1
  33. package/dist/secrets/routes.d.ts +6 -6
  34. package/dist/server/agent-engine-ollama-models-route.d.ts +1 -1
  35. package/package.json +4 -4
@@ -17,6 +17,7 @@ import {
17
17
  BRAIN_SEARCH_INDEX_VERSION,
18
18
  BRAIN_SENSITIVITY_POLICY_VERSION,
19
19
  } from "./search-index-contracts.js";
20
+ import { readEmbeddingReadiness } from "./search-index.js";
20
21
  import { redactSensitiveText } from "./search.js";
21
22
 
22
23
  const APP_ID = "brain";
@@ -441,59 +442,114 @@ export async function readBrainHealth() {
441
442
  ),
442
443
  )
443
444
  : [];
444
- const [sensitivityRows, artifactRows, embeddingRows] = await Promise.all([
445
- adminSourceIds.length
446
- ? db
447
- .select({
448
- disposition: schema.brainSensitivityEvents.disposition,
449
- confidenceBand: schema.brainSensitivityEvents.confidenceBand,
450
- })
451
- .from(schema.brainSensitivityEvents)
452
- .innerJoin(
453
- schema.brainSources,
454
- eq(schema.brainSensitivityEvents.sourceId, schema.brainSources.id),
455
- )
456
- .where(
457
- and(
458
- accessFilter(schema.brainSources, schema.brainSourceShares),
459
- inArray(schema.brainSensitivityEvents.sourceId, adminSourceIds),
460
- ),
461
- )
462
- : Promise.resolve([]),
463
- audienceIds.length
464
- ? db
465
- .select({
466
- captureId: schema.brainSearchArtifacts.captureId,
467
- status: schema.brainSearchArtifacts.status,
468
- })
469
- .from(schema.brainSearchArtifacts)
470
- .innerJoin(
471
- schema.brainSources,
472
- eq(schema.brainSearchArtifacts.sourceId, schema.brainSources.id),
473
- )
474
- .where(
475
- and(
476
- accessFilter(schema.brainSources, schema.brainSourceShares),
477
- inArray(schema.brainSearchArtifacts.audienceId, audienceIds),
478
- ),
479
- )
480
- : Promise.resolve([]),
481
- audienceIds.length
482
- ? db
483
- .select({ status: schema.brainSearchEmbeddings.status })
484
- .from(schema.brainSearchEmbeddings)
485
- .innerJoin(
486
- schema.brainSources,
487
- eq(schema.brainSearchEmbeddings.sourceId, schema.brainSources.id),
488
- )
489
- .where(
490
- and(
491
- accessFilter(schema.brainSources, schema.brainSourceShares),
492
- inArray(schema.brainSearchEmbeddings.audienceId, audienceIds),
493
- ),
494
- )
495
- : Promise.resolve([]),
496
- ]);
445
+ const [sensitivityRows, artifactRows, embeddingRows, burstRows] =
446
+ await Promise.all([
447
+ adminSourceIds.length
448
+ ? db
449
+ .select({
450
+ disposition: schema.brainSensitivityEvents.disposition,
451
+ confidenceBand: schema.brainSensitivityEvents.confidenceBand,
452
+ })
453
+ .from(schema.brainSensitivityEvents)
454
+ .innerJoin(
455
+ schema.brainSources,
456
+ eq(
457
+ schema.brainSensitivityEvents.sourceId,
458
+ schema.brainSources.id,
459
+ ),
460
+ )
461
+ .where(
462
+ and(
463
+ accessFilter(schema.brainSources, schema.brainSourceShares),
464
+ inArray(schema.brainSensitivityEvents.sourceId, adminSourceIds),
465
+ ),
466
+ )
467
+ : Promise.resolve([]),
468
+ audienceIds.length
469
+ ? db
470
+ .select({
471
+ id: schema.brainSearchArtifacts.id,
472
+ captureId: schema.brainSearchArtifacts.captureId,
473
+ contentHash: schema.brainSearchArtifacts.contentHash,
474
+ sensitivityPolicyVersion:
475
+ schema.brainSearchArtifacts.sensitivityPolicyVersion,
476
+ aclHash: schema.brainSearchArtifacts.aclHash,
477
+ indexVersion: schema.brainSearchArtifacts.indexVersion,
478
+ status: schema.brainSearchArtifacts.status,
479
+ captureContentHash: schema.brainRawCaptures.contentHash,
480
+ captureSensitivityDisposition:
481
+ schema.brainRawCaptures.sensitivityDisposition,
482
+ captureSensitivityPolicyVersion:
483
+ schema.brainRawCaptures.sensitivityPolicyVersion,
484
+ captureAclHash: schema.brainRawCaptures.audienceAclHash,
485
+ })
486
+ .from(schema.brainSearchArtifacts)
487
+ .innerJoin(
488
+ schema.brainSources,
489
+ eq(schema.brainSearchArtifacts.sourceId, schema.brainSources.id),
490
+ )
491
+ .innerJoin(
492
+ schema.brainRawCaptures,
493
+ eq(
494
+ schema.brainSearchArtifacts.captureId,
495
+ schema.brainRawCaptures.id,
496
+ ),
497
+ )
498
+ .where(
499
+ and(
500
+ accessFilter(schema.brainSources, schema.brainSourceShares),
501
+ inArray(schema.brainSearchArtifacts.audienceId, audienceIds),
502
+ ),
503
+ )
504
+ : Promise.resolve([]),
505
+ audienceIds.length
506
+ ? db
507
+ .select({
508
+ targetType: schema.brainSearchEmbeddings.targetType,
509
+ targetId: schema.brainSearchEmbeddings.targetId,
510
+ embeddingSetId: schema.brainSearchEmbeddings.embeddingSetId,
511
+ contentHash: schema.brainSearchEmbeddings.contentHash,
512
+ sensitivityPolicyVersion:
513
+ schema.brainSearchEmbeddings.sensitivityPolicyVersion,
514
+ aclHash: schema.brainSearchEmbeddings.aclHash,
515
+ indexVersion: schema.brainSearchEmbeddings.indexVersion,
516
+ status: schema.brainSearchEmbeddings.status,
517
+ })
518
+ .from(schema.brainSearchEmbeddings)
519
+ .innerJoin(
520
+ schema.brainSources,
521
+ eq(schema.brainSearchEmbeddings.sourceId, schema.brainSources.id),
522
+ )
523
+ .where(
524
+ and(
525
+ accessFilter(schema.brainSources, schema.brainSourceShares),
526
+ inArray(schema.brainSearchEmbeddings.audienceId, audienceIds),
527
+ ),
528
+ )
529
+ : Promise.resolve([]),
530
+ audienceIds.length
531
+ ? db
532
+ .select({
533
+ id: schema.brainSearchBursts.id,
534
+ artifactId: schema.brainSearchBursts.artifactId,
535
+ aclHash: schema.brainSearchBursts.aclHash,
536
+ indexed: schema.brainSearchBursts.indexed,
537
+ contentHash: schema.brainSearchBursts.contentHash,
538
+ indexVersion: schema.brainSearchBursts.indexVersion,
539
+ })
540
+ .from(schema.brainSearchBursts)
541
+ .innerJoin(
542
+ schema.brainSources,
543
+ eq(schema.brainSearchBursts.sourceId, schema.brainSources.id),
544
+ )
545
+ .where(
546
+ and(
547
+ accessFilter(schema.brainSources, schema.brainSourceShares),
548
+ inArray(schema.brainSearchBursts.audienceId, audienceIds),
549
+ ),
550
+ )
551
+ : Promise.resolve([]),
552
+ ]);
497
553
 
498
554
  const configuredCounts = new Map<string, number>();
499
555
  for (const source of sourceRows) {
@@ -576,12 +632,77 @@ export async function readBrainHealth() {
576
632
  const artifactCounts = countStatuses(artifactRows);
577
633
  const embeddingCounts = countStatuses(embeddingRows);
578
634
  const allowedCaptureCount = captureDispositionCounts.allowed ?? 0;
579
- const activeArtifactCount = artifactCounts.active ?? 0;
635
+ const currentArtifacts = artifactRows.filter(
636
+ (artifact) =>
637
+ artifact.status === "active" &&
638
+ artifact.captureSensitivityDisposition === "allowed" &&
639
+ artifact.contentHash === artifact.captureContentHash &&
640
+ artifact.sensitivityPolicyVersion ===
641
+ artifact.captureSensitivityPolicyVersion &&
642
+ artifact.aclHash === artifact.captureAclHash &&
643
+ artifact.indexVersion === BRAIN_SEARCH_INDEX_VERSION,
644
+ );
645
+ const activeArtifactCount = currentArtifacts.length;
646
+ const currentArtifactById = new Map(
647
+ currentArtifacts.map((artifact) => [artifact.id, artifact]),
648
+ );
580
649
  const indexedCaptureCount = new Set(
581
- artifactRows
582
- .filter((artifact) => artifact.status === "active")
583
- .map((artifact) => artifact.captureId),
650
+ currentArtifacts.map((artifact) => artifact.captureId),
584
651
  ).size;
652
+ const embeddingReadiness = await readEmbeddingReadiness();
653
+ const currentBursts = burstRows.filter((burst) => {
654
+ const artifact = currentArtifactById.get(burst.artifactId);
655
+ return Boolean(
656
+ artifact &&
657
+ burst.indexed === 1 &&
658
+ burst.contentHash === artifact.contentHash &&
659
+ burst.aclHash === artifact.aclHash &&
660
+ burst.indexVersion === BRAIN_SEARCH_INDEX_VERSION,
661
+ );
662
+ });
663
+ const artifactIdByBurstId = new Map(
664
+ currentBursts.map((burst) => [burst.id, burst.artifactId]),
665
+ );
666
+ const expectedBurstIdsByArtifactId = new Map<string, string[]>();
667
+ for (const burst of currentBursts) {
668
+ const ids = expectedBurstIdsByArtifactId.get(burst.artifactId) ?? [];
669
+ ids.push(burst.id);
670
+ expectedBurstIdsByArtifactId.set(burst.artifactId, ids);
671
+ }
672
+ const embeddedTargetKeys = new Set(
673
+ embeddingRows.flatMap((embedding) => {
674
+ const artifactId =
675
+ embedding.targetType === "artifact"
676
+ ? embedding.targetId
677
+ : embedding.targetType === "burst"
678
+ ? artifactIdByBurstId.get(embedding.targetId)
679
+ : undefined;
680
+ const artifact = artifactId
681
+ ? currentArtifactById.get(artifactId)
682
+ : undefined;
683
+ return artifact &&
684
+ embedding.status === "active" &&
685
+ embedding.embeddingSetId === embeddingReadiness.embeddingSetId &&
686
+ embedding.contentHash === artifact.contentHash &&
687
+ embedding.sensitivityPolicyVersion ===
688
+ artifact.sensitivityPolicyVersion &&
689
+ embedding.aclHash === artifact.aclHash &&
690
+ embedding.indexVersion === BRAIN_SEARCH_INDEX_VERSION
691
+ ? [`${embedding.targetType}:${embedding.targetId}`]
692
+ : [];
693
+ }),
694
+ );
695
+ const embeddedArtifactCount = currentArtifacts.filter((artifact) => {
696
+ const burstIds = expectedBurstIdsByArtifactId.get(artifact.id) ?? [];
697
+ return (
698
+ embeddedTargetKeys.has(`artifact:${artifact.id}`) &&
699
+ burstIds.every((id) => embeddedTargetKeys.has(`burst:${id}`))
700
+ );
701
+ }).length;
702
+ const missingArtifactEmbeddings = Math.max(
703
+ 0,
704
+ activeArtifactCount - embeddedArtifactCount,
705
+ );
585
706
  const privacySettings = await readBrainSettings();
586
707
  const privacyClassifier = brainPrivacyReadiness(
587
708
  privacySettings,
@@ -673,6 +794,13 @@ export async function readBrainHealth() {
673
794
  (proposalCounts.pending ?? 0) > 0
674
795
  ? "Review pending proposals before expanding source scope."
675
796
  : null,
797
+ !embeddingReadiness.ready
798
+ ? (embeddingReadiness.warning ??
799
+ "Configure one embedding provider to enable semantic retrieval.")
800
+ : null,
801
+ embeddingReadiness.ready && missingArtifactEmbeddings > 0
802
+ ? `Backfill embeddings for ${missingArtifactEmbeddings} active search artifact${missingArtifactEmbeddings === 1 ? "" : "s"}.`
803
+ : null,
676
804
  ]
677
805
  .filter((step): step is string => Boolean(step))
678
806
  .slice(0, 4);
@@ -793,6 +921,18 @@ export async function readBrainHealth() {
793
921
  counts: artifactCounts,
794
922
  },
795
923
  embeddings: {
924
+ readiness: embeddingReadiness,
925
+ coverage: {
926
+ eligibleArtifacts: activeArtifactCount,
927
+ embeddedArtifacts: embeddedArtifactCount,
928
+ missingArtifacts: missingArtifactEmbeddings,
929
+ percent:
930
+ activeArtifactCount === 0
931
+ ? 100
932
+ : Math.round(
933
+ (embeddedArtifactCount / activeArtifactCount) * 10_000,
934
+ ) / 100,
935
+ },
796
936
  total: embeddingRows.length,
797
937
  active: embeddingCounts.active ?? 0,
798
938
  stale: embeddingCounts.stale ?? 0,
@@ -6,6 +6,7 @@ import { getDb, schema } from "../db/index.js";
6
6
  import {
7
7
  createCapture,
8
8
  createSource,
9
+ getAccessibleCapture,
9
10
  nowIso,
10
11
  parseJson,
11
12
  sanitizeEvidenceCitationUrls,
@@ -17,9 +18,11 @@ import {
17
18
  type WriteKnowledgeInput,
18
19
  } from "./brain.js";
19
20
  import {
21
+ citationEvidenceMatchesCapture,
20
22
  redactSensitiveText,
21
23
  redactSensitiveValue,
22
24
  searchEverythingRows,
25
+ sourceUrlFromMetadata,
23
26
  } from "./search.js";
24
27
 
25
28
  const DEMO_SEED_ID = "brain-product-decisions-demo-v1";
@@ -1283,15 +1286,31 @@ function searchResultCitationUrl(
1283
1286
  return result.citation?.sourceUrl ?? result.sourceUrl ?? null;
1284
1287
  }
1285
1288
 
1286
- function hasExpectedCitation(
1289
+ async function hasExpectedCitation(
1287
1290
  result: Awaited<ReturnType<typeof searchEverythingRows>>[number],
1288
1291
  evalCase: RetrievalEvalCase,
1289
1292
  ) {
1290
1293
  if (!evalCase.requireCitation) return true;
1291
1294
  const url = searchResultCitationUrl(result);
1292
1295
  if (!url?.startsWith("https://")) return false;
1293
- if (!evalCase.requireSlackProvider) return true;
1294
- return (result.provider ?? result.source?.provider) === "slack";
1296
+ const captureId = result.citation?.captureId;
1297
+ if (!captureId) return false;
1298
+ const access = await getAccessibleCapture(captureId);
1299
+ if (!access) return false;
1300
+ if (
1301
+ evalCase.requireSlackProvider &&
1302
+ ((result.provider ?? result.source?.provider) !== "slack" ||
1303
+ access.source.provider !== "slack")
1304
+ ) {
1305
+ return false;
1306
+ }
1307
+ const canonicalUrl = sourceUrlFromMetadata(
1308
+ parseJson<Record<string, unknown>>(access.capture.metadataJson, {}),
1309
+ );
1310
+ return Boolean(
1311
+ canonicalUrl === url &&
1312
+ citationEvidenceMatchesCapture(result.citation, access.capture.content),
1313
+ );
1295
1314
  }
1296
1315
 
1297
1316
  function findRetrievalEvalMatch(
@@ -1324,7 +1343,9 @@ async function evaluateRetrievalEvalCases() {
1324
1343
  limit: 8,
1325
1344
  });
1326
1345
  const match = findRetrievalEvalMatch(results, evalCase);
1327
- const citationOk = match ? hasExpectedCitation(match, evalCase) : false;
1346
+ const citationOk = match
1347
+ ? await hasExpectedCitation(match, evalCase)
1348
+ : false;
1328
1349
 
1329
1350
  if (evalCase.kind === "answer") {
1330
1351
  answerCaseCount += 1;
@@ -202,6 +202,10 @@ export async function hybridSearchArtifacts(input: {
202
202
  namespace: SEARCH_NAMESPACE,
203
203
  });
204
204
  ftsRanks = new Map(fts.map((hit, index) => [hit.chunkId, index + 1]));
205
+ } catch {
206
+ ftsRanks = new Map();
207
+ }
208
+ try {
205
209
  const family = defaultEmbeddingFamily(await availableEmbeddingFamilies());
206
210
  if (family) {
207
211
  const [queryVector] = await family.embed(
@@ -273,7 +277,6 @@ export async function hybridSearchArtifacts(input: {
273
277
  }
274
278
  }
275
279
  } catch {
276
- ftsRanks = new Map();
277
280
  semanticRanks = new Map();
278
281
  }
279
282
  }
@@ -46,6 +46,7 @@ export async function enqueueBrainOperation(input: {
46
46
  operation: input.operation,
47
47
  status: "queued",
48
48
  priority: input.priority ?? 50,
49
+ attempts: 0,
49
50
  payloadJson: stableJson(input.payload ?? {}),
50
51
  leaseToken: null,
51
52
  leaseExpiresAt: null,