@oortmem/core 0.3.0 → 0.5.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.
@@ -388,12 +388,14 @@ export function storeConformance(name, make) {
388
388
  const others = {
389
389
  'other-org': { ...localScope(), orgId: 'another-org' },
390
390
  'other-customer': { ...localScope(), customerId: 'someone-else' },
391
- 'other-agent': { ...localScope(), agentId: 'another-agent' },
392
391
  };
393
392
  await store.upsert(TG, makeMemory({ id: 'mine', text, embedding }));
394
393
  for (const [id, scope] of Object.entries(others)) {
395
394
  await store.upsert(grantFor(scope), makeMemory({ id, text, embedding, scope }));
396
395
  }
396
+ // Another agent's Star is private to it (CON-62).
397
+ const star = { level: 'agent', orgId: 'local', agentId: 'another-agent' };
398
+ await store.upsert(nodesGrant(star), memoryAt('other-agent', star, { agentId: 'another-agent' }, { text, embedding }));
397
399
  expect(ids(await store.lexical(localGrant(LOCAL), 'auth-service-8080', 10))).toEqual([
398
400
  'mine',
399
401
  ]);
@@ -584,11 +586,31 @@ export function storeConformance(name, make) {
584
586
  expect(await nonInterference(make, seed, 60, { multiNode: true })).toBeNull();
585
587
  }
586
588
  }, 120_000);
589
+ it(`isolation.non-interference-peer-agents-${seeds}-seeds`, async () => {
590
+ // CON-62: another agent of the same person, working in its own Star, never reaches mine.
591
+ for (let seed = 1; seed <= seeds; seed++) {
592
+ expect(await nonInterference(make, seed, 60, { peers: true })).toBeNull();
593
+ }
594
+ }, 120_000);
587
595
  });
588
596
  levelConformance(name, make);
589
597
  }
590
598
  /** The owner every level case belongs to or reads as: the local principal. */
591
599
  const OWNER = { orgId: 'local', customerId: 'self', agentId: 'claude-code' };
600
+ /**
601
+ * The grant every local caller had before CON-62: the owner's Planet, restricted to memories its
602
+ * own agent wrote. Kept to prove data written before levels reads back unchanged.
603
+ */
604
+ const PRE_LEVEL = nodeGrant({
605
+ principal: OWNER,
606
+ read: [
607
+ {
608
+ node: { level: 'user', orgId: 'local', customerId: 'self' },
609
+ projectIds: 'all',
610
+ authorAgentId: 'claude-code',
611
+ },
612
+ ],
613
+ });
592
614
  const PROVENANCE = {
593
615
  derivedFrom: [{ vault: 'acct:1', memoryId: 'src-1' }],
594
616
  contributors: [
@@ -727,14 +749,38 @@ function levelConformance(name, make) {
727
749
  it('levels.pre-level-grants-see-no-other-level', async () => {
728
750
  for (const c of cases())
729
751
  await store.upsert(c.g, c.m);
730
- const legacy = [TG, localGrant({ ...OWNER, projectIds: ['p1', null] })];
731
- for (const g of legacy) {
732
- for (const c of cases())
733
- expect(await store.get(g, c.m.id), c.name).toBeNull();
734
- expect(await store.list(g, { validOnly: false })).toEqual([]);
735
- expect(await store.lexical(g, 'levelprobe', 50)).toEqual([]);
736
- expect(await store.dense(g, vec(1, 1, -1, 2), 50)).toEqual([]);
752
+ for (const c of cases())
753
+ expect(await store.get(PRE_LEVEL, c.m.id), c.name).toBeNull();
754
+ expect(await store.list(PRE_LEVEL, { validOnly: false })).toEqual([]);
755
+ expect(await store.lexical(PRE_LEVEL, 'levelprobe', 50)).toEqual([]);
756
+ expect(await store.dense(PRE_LEVEL, vec(1, 1, -1, 2), 50)).toEqual([]);
757
+ });
758
+ it('levels.local-grants-see-the-planet-and-their-own-star', async () => {
759
+ // CON-62: a person's agents share the Planet; each agent's Star is its own.
760
+ for (const c of cases())
761
+ await store.upsert(c.g, c.m);
762
+ const ccStar = { level: 'agent', orgId: 'local', agentId: 'claude-code' };
763
+ await store.upsert(nodesGrant(ccStar), memoryAt('cc-star', ccStar, { agentId: 'claude-code' }, { text: 'levelprobe cc star' }));
764
+ const asClaude = [TG, localGrant({ ...OWNER, projectIds: ['p1', null] })];
765
+ for (const g of asClaude) {
766
+ expect(ids(await store.list(g, { validOnly: false })).sort()).toEqual([
767
+ 'cc-star',
768
+ 'lv-planet-other-author',
769
+ ]);
770
+ expect(ids(await store.lexical(g, 'levelprobe', 50)).sort()).toEqual([
771
+ 'cc-star',
772
+ 'lv-planet-other-author',
773
+ ]);
774
+ expect(await store.get(g, 'lv-star')).toBeNull();
775
+ expect(await store.setTier(g, 'lv-star', 'hot')).toBe(false);
737
776
  }
777
+ const asCursor = localGrant({ ...OWNER, agentId: 'cursor', projectIds: ['p1', null] });
778
+ expect(ids(await store.list(asCursor, { validOnly: false })).sort()).toEqual([
779
+ 'lv-planet-other-author',
780
+ 'lv-star',
781
+ ]);
782
+ expect(await store.get(asCursor, 'cc-star')).toBeNull();
783
+ expect(ids(await store.dense(asCursor, vec(2, 1, -1, 2), 50))).not.toContain('cc-star');
738
784
  });
739
785
  it('levels.existing-data-unchanged', async () => {
740
786
  const legacy = [
@@ -750,15 +796,16 @@ function levelConformance(name, make) {
750
796
  ];
751
797
  await store.upsertMany(TG, legacy);
752
798
  await seedCold(store, makeMemory({ id: 'old-4', text: 'levelprobe cold', embedding: vec(3, 1, -1, 2) }));
799
+ const g = PRE_LEVEL;
753
800
  const view = async () => {
754
801
  const out = [];
755
802
  for (const id of ['old-1', 'old-2', 'old-3', 'old-4']) {
756
- out.push(render(await store.get(TG, id)), render(await store.coldGet(TG, id)));
803
+ out.push(render(await store.get(g, id)), render(await store.coldGet(g, id)));
757
804
  }
758
- out.push((await store.list(TG, { validOnly: false })).map(render).join('|'));
759
- out.push(JSON.stringify(await store.dense(TG, vec(1, 1, -1, 2), 10)));
805
+ out.push((await store.list(g, { validOnly: false })).map(render).join('|'));
806
+ out.push(JSON.stringify(await store.dense(g, vec(1, 1, -1, 2), 10)));
760
807
  // bm25 scores use table-wide statistics: compare which memories match.
761
- out.push(ids(await store.lexical(TG, 'levelprobe orbit', 50))
808
+ out.push(ids(await store.lexical(g, 'levelprobe orbit', 50))
762
809
  .sort()
763
810
  .join(','));
764
811
  return out;
@@ -846,7 +893,12 @@ function levelConformance(name, make) {
846
893
  'by-cc',
847
894
  'by-cursor',
848
895
  ]);
849
- expect(ids(await store.lexical(TG, 'authorprobe', 10))).toEqual(['by-cc']);
896
+ expect(ids(await store.lexical(PRE_LEVEL, 'authorprobe', 10))).toEqual(['by-cc']);
897
+ // The local grant shares the Planet among the person's agents (CON-62).
898
+ expect(ids(await store.lexical(TG, 'authorprobe', 10)).sort()).toEqual([
899
+ 'by-cc',
900
+ 'by-cursor',
901
+ ]);
850
902
  const cursor = nodeGrant({
851
903
  principal: { ...OWNER, agentId: 'cursor' },
852
904
  read: [{ node: planet, projectIds: 'all', authorAgentId: 'cursor' }],
@@ -869,7 +921,8 @@ function levelConformance(name, make) {
869
921
  read: [...TG.readNodes, { node: nodeOf(hive.address), projectIds: ['p1'] }],
870
922
  write: TG.writeNodes,
871
923
  });
872
- const visible = ['own', hive.m.id].sort();
924
+ const other = all.find((c) => c.name === 'planet-other-author');
925
+ const visible = ['own', other.m.id, hive.m.id].sort();
873
926
  expect(ids(await store.list(g, { validOnly: false })).sort()).toEqual(visible);
874
927
  expect(ids(await store.lexical(g, 'levelprobe', 50)).sort()).toEqual(visible);
875
928
  const dense = await store.dense(g, vec(9, 1, -1, 2), 50);
@@ -885,8 +938,8 @@ function levelConformance(name, make) {
885
938
  expect(await store.get(hive.g, hive.m.id)).not.toBeNull();
886
939
  await expect(store.upsert(g, memoryAt('new-hive', hive.address, hive.author))).rejects.toThrow(GrantError);
887
940
  // A project outside the pattern stays out.
888
- const other = memoryAt('hive-p2', { ...hive.address, projectId: 'p2' }, hive.author);
889
- await store.upsert(hive.g, other);
941
+ const hiveP2 = memoryAt('hive-p2', { ...hive.address, projectId: 'p2' }, hive.author);
942
+ await store.upsert(hive.g, hiveP2);
890
943
  expect(await store.get(g, 'hive-p2')).toBeNull();
891
944
  });
892
945
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oortmem/core",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "description": "Oort core: the tiered hot, warm and cold memory engine for AI agents (types, scoring, recall, consolidation).",
5
5
  "keywords": [
6
6
  "oort",