@agent-native/core 0.111.3 → 0.112.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 (109) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +18 -0
  3. package/corpus/core/package.json +3 -1
  4. package/corpus/core/src/cli/code-agent-executor.ts +17 -5
  5. package/corpus/core/src/cli/code-agent-runs.ts +43 -2
  6. package/corpus/core/src/cli/migration-codemod.ts +59 -9
  7. package/corpus/core/src/cli/multi-frontier-runs.ts +841 -0
  8. package/corpus/core/src/coding-tools/index.ts +27 -8
  9. package/corpus/core/src/embeddings/index.ts +233 -0
  10. package/corpus/core/src/index.ts +2 -0
  11. package/corpus/core/src/search/index.ts +413 -0
  12. package/corpus/templates/brain/.agents/skills/brain/RUNBOOK.md +48 -5
  13. package/corpus/templates/brain/.agents/skills/brain/SKILL.md +46 -14
  14. package/corpus/templates/brain/README.md +34 -3
  15. package/corpus/templates/brain/actions/approve-proposal.ts +2 -0
  16. package/corpus/templates/brain/actions/claim-distillation.ts +24 -27
  17. package/corpus/templates/brain/actions/create-source.ts +1 -1
  18. package/corpus/templates/brain/actions/get-knowledge.ts +6 -2
  19. package/corpus/templates/brain/actions/get-pilot-report.ts +44 -8
  20. package/corpus/templates/brain/actions/import-capture.ts +21 -9
  21. package/corpus/templates/brain/actions/import-transcript.ts +42 -14
  22. package/corpus/templates/brain/actions/list-captures.ts +10 -1
  23. package/corpus/templates/brain/actions/list-distillation-queue.ts +39 -14
  24. package/corpus/templates/brain/actions/list-projects.ts +51 -0
  25. package/corpus/templates/brain/actions/list-proposals.ts +80 -4
  26. package/corpus/templates/brain/actions/list-sources.ts +17 -3
  27. package/corpus/templates/brain/actions/manage-project.ts +118 -0
  28. package/corpus/templates/brain/actions/mark-capture-distilled.ts +68 -20
  29. package/corpus/templates/brain/actions/navigate.ts +1 -0
  30. package/corpus/templates/brain/actions/reject-proposal.ts +6 -1
  31. package/corpus/templates/brain/actions/resanitize-captures.ts +159 -33
  32. package/corpus/templates/brain/actions/review-proposal.ts +2 -0
  33. package/corpus/templates/brain/actions/search-everything.ts +10 -1
  34. package/corpus/templates/brain/actions/search-knowledge.ts +1 -1
  35. package/corpus/templates/brain/actions/set-settings.ts +36 -1
  36. package/corpus/templates/brain/actions/update-proposal.ts +2 -0
  37. package/corpus/templates/brain/app/hooks/use-distillation-bridge.ts +18 -9
  38. package/corpus/templates/brain/app/hooks/use-navigation-state.ts +3 -0
  39. package/corpus/templates/brain/app/i18n-data.ts +731 -2
  40. package/corpus/templates/brain/app/lib/brain.ts +33 -1
  41. package/corpus/templates/brain/app/routes/ops.tsx +47 -0
  42. package/corpus/templates/brain/app/routes/review.tsx +41 -2
  43. package/corpus/templates/brain/app/routes/search.tsx +33 -2
  44. package/corpus/templates/brain/app/routes/settings.tsx +158 -0
  45. package/corpus/templates/brain/app/routes/sources.tsx +31 -0
  46. package/corpus/templates/brain/changelog/2026-07-19-added-private-source-safe-semantic-search.md +6 -0
  47. package/corpus/templates/brain/jobs/process-ingest-queue.ts +270 -36
  48. package/corpus/templates/brain/package.json +1 -0
  49. package/corpus/templates/brain/server/db/index.ts +18 -0
  50. package/corpus/templates/brain/server/db/schema.ts +197 -1
  51. package/corpus/templates/brain/server/jobs/distillation-queue.ts +12 -5
  52. package/corpus/templates/brain/server/lib/audiences.ts +708 -0
  53. package/corpus/templates/brain/server/lib/brain-health.ts +251 -38
  54. package/corpus/templates/brain/server/lib/brain.ts +877 -50
  55. package/corpus/templates/brain/server/lib/capture-sanitization.ts +217 -53
  56. package/corpus/templates/brain/server/lib/connectors.ts +556 -70
  57. package/corpus/templates/brain/server/lib/demo.ts +17 -4
  58. package/corpus/templates/brain/server/lib/hybrid-search.ts +363 -0
  59. package/corpus/templates/brain/server/lib/ingest-queue.ts +104 -0
  60. package/corpus/templates/brain/server/lib/privacy-readiness.ts +24 -0
  61. package/corpus/templates/brain/server/lib/search-index-contracts.ts +72 -0
  62. package/corpus/templates/brain/server/lib/search-index.ts +714 -0
  63. package/corpus/templates/brain/server/lib/search.ts +120 -4
  64. package/corpus/templates/brain/server/lib/sensitivity-policy.ts +174 -0
  65. package/corpus/templates/brain/server/lib/slack-events.ts +281 -0
  66. package/corpus/templates/brain/server/onboarding.ts +29 -0
  67. package/corpus/templates/brain/server/plugins/db.ts +204 -0
  68. package/corpus/templates/brain/server/register-secrets.ts +46 -0
  69. package/corpus/templates/brain/server/routes/api/_agent-native/brain/ingest.post.ts +36 -16
  70. package/corpus/templates/brain/server/routes/api/_agent-native/brain/slack-events.post.ts +28 -0
  71. package/corpus/templates/brain/shared/types.ts +10 -0
  72. package/dist/cli/code-agent-executor.d.ts +8 -9
  73. package/dist/cli/code-agent-executor.d.ts.map +1 -1
  74. package/dist/cli/code-agent-executor.js +9 -3
  75. package/dist/cli/code-agent-executor.js.map +1 -1
  76. package/dist/cli/code-agent-runs.d.ts +5 -0
  77. package/dist/cli/code-agent-runs.d.ts.map +1 -1
  78. package/dist/cli/code-agent-runs.js +33 -2
  79. package/dist/cli/code-agent-runs.js.map +1 -1
  80. package/dist/cli/migration-codemod.d.ts.map +1 -1
  81. package/dist/cli/migration-codemod.js +48 -10
  82. package/dist/cli/migration-codemod.js.map +1 -1
  83. package/dist/cli/multi-frontier-runs.d.ts +131 -0
  84. package/dist/cli/multi-frontier-runs.d.ts.map +1 -0
  85. package/dist/cli/multi-frontier-runs.js +529 -0
  86. package/dist/cli/multi-frontier-runs.js.map +1 -0
  87. package/dist/coding-tools/index.d.ts +1 -0
  88. package/dist/coding-tools/index.d.ts.map +1 -1
  89. package/dist/coding-tools/index.js +25 -7
  90. package/dist/coding-tools/index.js.map +1 -1
  91. package/dist/collab/awareness.d.ts +2 -2
  92. package/dist/collab/awareness.d.ts.map +1 -1
  93. package/dist/embeddings/index.d.ts +24 -0
  94. package/dist/embeddings/index.d.ts.map +1 -0
  95. package/dist/embeddings/index.js +172 -0
  96. package/dist/embeddings/index.js.map +1 -0
  97. package/dist/index.d.ts +2 -0
  98. package/dist/index.d.ts.map +1 -1
  99. package/dist/index.js +2 -0
  100. package/dist/index.js.map +1 -1
  101. package/dist/notifications/routes.d.ts +3 -3
  102. package/dist/progress/routes.d.ts +1 -1
  103. package/dist/resources/handlers.d.ts +1 -1
  104. package/dist/search/index.d.ts +88 -0
  105. package/dist/search/index.d.ts.map +1 -0
  106. package/dist/search/index.js +267 -0
  107. package/dist/search/index.js.map +1 -0
  108. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  109. package/package.json +3 -1
@@ -1,11 +1,17 @@
1
1
  import { getSetting, putSetting } from "@agent-native/core/settings";
2
- import { accessFilter } from "@agent-native/core/sharing";
2
+ import { accessFilter, assertAccess } from "@agent-native/core/sharing";
3
3
  import { listWorkspaceConnectionProviderCatalogForApp } from "@agent-native/core/workspace-connections";
4
- import { and, desc, eq, inArray, ne } from "drizzle-orm";
4
+ import { and, desc, eq, inArray, isNull, ne, or } from "drizzle-orm";
5
5
 
6
6
  import { getDb, schema } from "../db/index.js";
7
7
  import { nextBrainSourceSyncAt } from "../jobs/sync-sources.js";
8
- import { parseJson } from "./brain.js";
8
+ import { listAccessibleAudienceIds } from "./audiences.js";
9
+ import { parseJson, readBrainSettings } from "./brain.js";
10
+ import { brainPrivacyReadiness } from "./privacy-readiness.js";
11
+ import {
12
+ BRAIN_SEARCH_INDEX_VERSION,
13
+ BRAIN_SENSITIVITY_POLICY_VERSION,
14
+ } from "./search-index-contracts.js";
9
15
  import { redactSensitiveText } from "./search.js";
10
16
 
11
17
  const APP_ID = "brain";
@@ -139,6 +145,7 @@ function sourceConfig(source: SourceRow) {
139
145
  function sourceHasSlackChannels(source: SourceRow) {
140
146
  if (source.provider !== "slack") return false;
141
147
  const config = sourceConfig(source);
148
+ if (config.includePublicChannels === true) return true;
142
149
  const values = [
143
150
  config.channelIds,
144
151
  config.channels,
@@ -314,6 +321,19 @@ export async function readBrainHealth() {
314
321
  )
315
322
  .orderBy(desc(schema.brainSources.updatedAt));
316
323
  const sourceIds = sourceRows.map((source) => source.id);
324
+ const audienceIds = await listAccessibleAudienceIds(sourceIds);
325
+ const adminSourceIds = (
326
+ await Promise.all(
327
+ sourceIds.map(async (sourceId) => {
328
+ try {
329
+ await assertAccess("brain-source", sourceId, "admin");
330
+ return sourceId;
331
+ } catch {
332
+ return null;
333
+ }
334
+ }),
335
+ )
336
+ ).filter((sourceId): sourceId is string => Boolean(sourceId));
317
337
  const syncRows = sourceIds.length
318
338
  ? await db
319
339
  .select()
@@ -329,46 +349,146 @@ export async function readBrainHealth() {
329
349
  }
330
350
  }
331
351
 
332
- const captureRows = sourceIds.length
333
- ? await db
334
- .select({
335
- sourceId: schema.brainRawCaptures.sourceId,
336
- status: schema.brainRawCaptures.status,
337
- createdAt: schema.brainRawCaptures.createdAt,
338
- capturedAt: schema.brainRawCaptures.capturedAt,
339
- })
340
- .from(schema.brainRawCaptures)
341
- .where(inArray(schema.brainRawCaptures.sourceId, sourceIds))
342
- : [];
352
+ const captureRows =
353
+ sourceIds.length && audienceIds.length
354
+ ? await db
355
+ .select({
356
+ sourceId: schema.brainRawCaptures.sourceId,
357
+ status: schema.brainRawCaptures.status,
358
+ sensitivityDisposition:
359
+ schema.brainRawCaptures.sensitivityDisposition,
360
+ createdAt: schema.brainRawCaptures.createdAt,
361
+ capturedAt: schema.brainRawCaptures.capturedAt,
362
+ })
363
+ .from(schema.brainRawCaptures)
364
+ .innerJoin(
365
+ schema.brainCaptureAudiences,
366
+ eq(
367
+ schema.brainCaptureAudiences.captureId,
368
+ schema.brainRawCaptures.id,
369
+ ),
370
+ )
371
+ .where(
372
+ and(
373
+ inArray(schema.brainRawCaptures.sourceId, sourceIds),
374
+ eq(schema.brainRawCaptures.sensitivityDisposition, "allowed"),
375
+ inArray(schema.brainCaptureAudiences.audienceId, audienceIds),
376
+ ),
377
+ )
378
+ : [];
343
379
  const proposalRows = await db
344
380
  .select({ status: schema.brainProposals.status })
345
381
  .from(schema.brainProposals)
346
- .where(accessFilter(schema.brainProposals, schema.brainProposalShares));
382
+ .where(
383
+ and(
384
+ accessFilter(schema.brainProposals, schema.brainProposalShares),
385
+ or(
386
+ isNull(schema.brainProposals.captureId),
387
+ audienceIds.length
388
+ ? inArray(schema.brainProposals.audienceId, audienceIds)
389
+ : undefined,
390
+ ),
391
+ ),
392
+ );
347
393
  const knowledgeRows = await db
348
394
  .select({ status: schema.brainKnowledge.status })
349
395
  .from(schema.brainKnowledge)
350
- .where(accessFilter(schema.brainKnowledge, schema.brainKnowledgeShares));
351
- const queueRows = await db
352
- .select({
353
- status: schema.brainIngestQueue.status,
354
- updatedAt: schema.brainIngestQueue.updatedAt,
355
- sourceId: schema.brainSources.id,
356
- })
357
- .from(schema.brainIngestQueue)
358
- .innerJoin(
359
- schema.brainRawCaptures,
360
- eq(schema.brainIngestQueue.captureId, schema.brainRawCaptures.id),
361
- )
362
- .innerJoin(
363
- schema.brainSources,
364
- eq(schema.brainRawCaptures.sourceId, schema.brainSources.id),
365
- )
366
396
  .where(
367
397
  and(
368
- eq(schema.brainIngestQueue.operation, "distill"),
369
- accessFilter(schema.brainSources, schema.brainSourceShares),
398
+ accessFilter(schema.brainKnowledge, schema.brainKnowledgeShares),
399
+ or(
400
+ isNull(schema.brainKnowledge.captureId),
401
+ audienceIds.length
402
+ ? inArray(schema.brainKnowledge.audienceId, audienceIds)
403
+ : undefined,
404
+ ),
370
405
  ),
371
406
  );
407
+ const queueRows = audienceIds.length
408
+ ? await db
409
+ .select({
410
+ operation: schema.brainIngestQueue.operation,
411
+ status: schema.brainIngestQueue.status,
412
+ updatedAt: schema.brainIngestQueue.updatedAt,
413
+ sourceId: schema.brainSources.id,
414
+ })
415
+ .from(schema.brainIngestQueue)
416
+ .innerJoin(
417
+ schema.brainRawCaptures,
418
+ eq(schema.brainIngestQueue.captureId, schema.brainRawCaptures.id),
419
+ )
420
+ .innerJoin(
421
+ schema.brainSources,
422
+ eq(schema.brainRawCaptures.sourceId, schema.brainSources.id),
423
+ )
424
+ .innerJoin(
425
+ schema.brainCaptureAudiences,
426
+ eq(
427
+ schema.brainCaptureAudiences.captureId,
428
+ schema.brainRawCaptures.id,
429
+ ),
430
+ )
431
+ .where(
432
+ and(
433
+ accessFilter(schema.brainSources, schema.brainSourceShares),
434
+ eq(schema.brainRawCaptures.sensitivityDisposition, "allowed"),
435
+ inArray(schema.brainCaptureAudiences.audienceId, audienceIds),
436
+ ),
437
+ )
438
+ : [];
439
+ const [sensitivityRows, artifactRows, embeddingRows] = await Promise.all([
440
+ adminSourceIds.length
441
+ ? db
442
+ .select({
443
+ disposition: schema.brainSensitivityEvents.disposition,
444
+ confidenceBand: schema.brainSensitivityEvents.confidenceBand,
445
+ })
446
+ .from(schema.brainSensitivityEvents)
447
+ .innerJoin(
448
+ schema.brainSources,
449
+ eq(schema.brainSensitivityEvents.sourceId, schema.brainSources.id),
450
+ )
451
+ .where(
452
+ and(
453
+ accessFilter(schema.brainSources, schema.brainSourceShares),
454
+ inArray(schema.brainSensitivityEvents.sourceId, adminSourceIds),
455
+ ),
456
+ )
457
+ : Promise.resolve([]),
458
+ audienceIds.length
459
+ ? db
460
+ .select({
461
+ captureId: schema.brainSearchArtifacts.captureId,
462
+ status: schema.brainSearchArtifacts.status,
463
+ })
464
+ .from(schema.brainSearchArtifacts)
465
+ .innerJoin(
466
+ schema.brainSources,
467
+ eq(schema.brainSearchArtifacts.sourceId, schema.brainSources.id),
468
+ )
469
+ .where(
470
+ and(
471
+ accessFilter(schema.brainSources, schema.brainSourceShares),
472
+ inArray(schema.brainSearchArtifacts.audienceId, audienceIds),
473
+ ),
474
+ )
475
+ : Promise.resolve([]),
476
+ audienceIds.length
477
+ ? db
478
+ .select({ status: schema.brainSearchEmbeddings.status })
479
+ .from(schema.brainSearchEmbeddings)
480
+ .innerJoin(
481
+ schema.brainSources,
482
+ eq(schema.brainSearchEmbeddings.sourceId, schema.brainSources.id),
483
+ )
484
+ .where(
485
+ and(
486
+ accessFilter(schema.brainSources, schema.brainSourceShares),
487
+ inArray(schema.brainSearchEmbeddings.audienceId, audienceIds),
488
+ ),
489
+ )
490
+ : Promise.resolve([]),
491
+ ]);
372
492
 
373
493
  const configuredCounts = new Map<string, number>();
374
494
  for (const source of sourceRows) {
@@ -417,13 +537,51 @@ export async function readBrainHealth() {
417
537
  ...sourceRows.map((source) => ({ status: source.status })),
418
538
  ]);
419
539
  const captureCounts = countStatuses(captureRows);
540
+ const captureDispositionCounts = countStatuses(
541
+ captureRows.map((capture) => ({
542
+ status: capture.sensitivityDisposition,
543
+ })),
544
+ );
420
545
  const proposalCounts = countStatuses(proposalRows);
421
546
  const knowledgeCounts = countStatuses(knowledgeRows);
422
- const staleQueue = queueRows.filter(isStaleProcessing).length;
423
- const failedQueue = queueRows.filter((row) => row.status === "failed").length;
424
- const pendingQueue = queueRows.filter(
547
+ const distillationQueueRows = queueRows.filter(
548
+ (row) => row.operation === "distill",
549
+ );
550
+ const searchQueueRows = queueRows.filter(
551
+ (row) =>
552
+ row.operation === "search-index" || row.operation === "search-unindex",
553
+ );
554
+ const staleQueue = distillationQueueRows.filter(isStaleProcessing).length;
555
+ const failedQueue = distillationQueueRows.filter(
556
+ (row) => row.status === "failed",
557
+ ).length;
558
+ const pendingQueue = distillationQueueRows.filter(
425
559
  (row) => row.status === "queued" || row.status === "processing",
426
560
  ).length;
561
+ const staleSearchQueue = searchQueueRows.filter(isStaleProcessing).length;
562
+ const failedSearchQueue = searchQueueRows.filter(
563
+ (row) => row.status === "failed",
564
+ ).length;
565
+ const pendingSearchQueue = searchQueueRows.filter(
566
+ (row) => row.status === "queued" || row.status === "processing",
567
+ ).length;
568
+ const sensitivityCounts = countStatuses(
569
+ sensitivityRows.map((row) => ({ status: row.disposition })),
570
+ );
571
+ const sensitivityConfidenceCounts = countStatuses(
572
+ sensitivityRows.map((row) => ({ status: row.confidenceBand })),
573
+ );
574
+ const artifactCounts = countStatuses(artifactRows);
575
+ const embeddingCounts = countStatuses(embeddingRows);
576
+ const allowedCaptureCount = captureDispositionCounts.allowed ?? 0;
577
+ const activeArtifactCount = artifactCounts.active ?? 0;
578
+ const indexedCaptureCount = new Set(
579
+ artifactRows
580
+ .filter((artifact) => artifact.status === "active")
581
+ .map((artifact) => artifact.captureId),
582
+ ).size;
583
+ const privacySettings = await readBrainSettings();
584
+ const privacyClassifier = brainPrivacyReadiness(privacySettings);
427
585
  const lastEval = await readBrainEvalSnapshot();
428
586
 
429
587
  const realSources = sourceSummaries.filter((source) => !source.demo);
@@ -489,6 +647,9 @@ export async function readBrainHealth() {
489
647
  ]);
490
648
 
491
649
  const nextSteps = [
650
+ !privacyClassifier.configured
651
+ ? "Configure Brain's approved privacy classifier. Until then, only deterministic-clean captures are stored; uncertain content is quarantined."
652
+ : null,
492
653
  ...setupSteps
493
654
  .filter((step) => step.status === "next")
494
655
  .map((step) => step.detail),
@@ -563,6 +724,23 @@ export async function readBrainHealth() {
563
724
  captureRows.map((capture) => capture.capturedAt ?? capture.createdAt),
564
725
  ),
565
726
  counts: captureCounts,
727
+ sensitivityDispositionCounts: captureDispositionCounts,
728
+ },
729
+ privacy: {
730
+ classifier: {
731
+ ...privacyClassifier,
732
+ },
733
+ events: {
734
+ total: sensitivityRows.length,
735
+ suppressed: sensitivityCounts.suppressed ?? 0,
736
+ quarantined: sensitivityCounts.quarantined ?? 0,
737
+ released: sensitivityCounts.released ?? 0,
738
+ discarded: sensitivityCounts.discarded ?? 0,
739
+ expired: sensitivityCounts.expired ?? 0,
740
+ counts: sensitivityCounts,
741
+ confidenceCounts: sensitivityConfidenceCounts,
742
+ },
743
+ policyVersion: BRAIN_SENSITIVITY_POLICY_VERSION,
566
744
  },
567
745
  proposals: {
568
746
  pending: proposalCounts.pending ?? 0,
@@ -583,8 +761,43 @@ export async function readBrainHealth() {
583
761
  pending: pendingQueue,
584
762
  failed: failedQueue,
585
763
  stale: staleQueue,
586
- total: queueRows.length,
587
- counts: countStatuses(queueRows),
764
+ total: distillationQueueRows.length,
765
+ counts: countStatuses(distillationQueueRows),
766
+ },
767
+ searchIndex: {
768
+ indexVersion: BRAIN_SEARCH_INDEX_VERSION,
769
+ coverage: {
770
+ eligibleCaptures: allowedCaptureCount,
771
+ activeArtifacts: activeArtifactCount,
772
+ indexedCaptures: indexedCaptureCount,
773
+ missingCaptures: Math.max(0, allowedCaptureCount - indexedCaptureCount),
774
+ percent:
775
+ allowedCaptureCount === 0
776
+ ? 100
777
+ : Math.round((indexedCaptureCount / allowedCaptureCount) * 10_000) /
778
+ 100,
779
+ },
780
+ artifacts: {
781
+ total: artifactRows.length,
782
+ active: artifactCounts.active ?? 0,
783
+ stale: artifactCounts.stale ?? 0,
784
+ deleted: artifactCounts.deleted ?? 0,
785
+ counts: artifactCounts,
786
+ },
787
+ embeddings: {
788
+ total: embeddingRows.length,
789
+ active: embeddingCounts.active ?? 0,
790
+ stale: embeddingCounts.stale ?? 0,
791
+ deleted: embeddingCounts.deleted ?? 0,
792
+ counts: embeddingCounts,
793
+ },
794
+ queue: {
795
+ pending: pendingSearchQueue,
796
+ failed: failedSearchQueue,
797
+ stale: staleSearchQueue,
798
+ total: searchQueueRows.length,
799
+ counts: countStatuses(searchQueueRows),
800
+ },
588
801
  },
589
802
  retrieval: {
590
803
  lastEval,