hippo-memory 0.40.0 → 1.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.
package/README.md CHANGED
@@ -85,6 +85,13 @@ hippo recall "data pipeline issues" --budget 2000
85
85
 
86
86
  ---
87
87
 
88
+ ### What's new in v1.0.0
89
+
90
+ - **Tenant-isolation security release.** v0.40.0's measurement gates surfaced a real cross-tenant data leak on the continuity tables (`task_snapshots`, `session_events`, `session_handoffs`). Schema migration v22 closes the gap: every continuity helper now scopes reads and writes by `tenantId`. Markdown mirror files (`buffer/active-task.md`, `buffer/recent-session.md`) are tenant-scoped too; the default tenant keeps the unsuffixed filename for on-disk back-compat.
91
+ - **Slack envelope fix.** `messageToRememberOpts` now sets `owner: 'user:<slack_user_id>'` so ingested Slack rows pass the v0.40.0 `hippo provenance --strict` gate.
92
+ - **Breaking change for JS callers.** 10 store helpers (`saveActiveTaskSnapshot`, `loadLatestHandoff`, `appendSessionEvent`, etc.) gained a required `tenantId` second argument. TypeScript callers get a compile error. JS callers get a runtime guard error (`assertTenantId`) that detects the most common misbinding (passing a `sess-*` session id) and points at the migration. See CHANGELOG for the full helper list.
93
+ - **Schema v22 migration.** Idempotent, transactional, with smart tenant backfill via unambiguous `task_snapshots.session_id` joins.
94
+
88
95
  ### What's new in v0.40.0
89
96
 
90
97
  - **Company Brain measurement gates.** Two new diagnostic commands close the last blocked rows of the Company Brain scorecard. `hippo provenance [--json] [--strict]` audits every `kind='raw'` row for `owner` + `artifact_ref`; `--strict` exits non-zero so CI can block on coverage regressions. `hippo correction-latency [--json]` reports p50 / p95 / max wall-clock lag from receipt to supersession across `superseded_by` chains. Both helpers (`buildProvenanceCoverage`, `buildCorrectionLatency`) are importable from `src/`.
package/dist/cli.js CHANGED
@@ -2500,7 +2500,7 @@ function cmdSnapshot(hippoRoot, args, flags) {
2500
2500
  console.error('Usage: hippo snapshot save --task <task> --summary <summary> --next-step <step> [--source <source>] [--session <session-id>]');
2501
2501
  process.exit(1);
2502
2502
  }
2503
- const snapshot = saveActiveTaskSnapshot(hippoRoot, {
2503
+ const snapshot = saveActiveTaskSnapshot(hippoRoot, resolveTenantId({}), {
2504
2504
  task,
2505
2505
  summary,
2506
2506
  next_step: nextStep,
@@ -2516,7 +2516,7 @@ function cmdSnapshot(hippoRoot, args, flags) {
2516
2516
  return;
2517
2517
  }
2518
2518
  if (subcommand === 'clear') {
2519
- const cleared = clearActiveTaskSnapshot(hippoRoot, String(flags['status'] ?? 'cleared'));
2519
+ const cleared = clearActiveTaskSnapshot(hippoRoot, resolveTenantId({}), String(flags['status'] ?? 'cleared'));
2520
2520
  if (!cleared) {
2521
2521
  console.log('No active task snapshot to clear.');
2522
2522
  return;
@@ -2525,7 +2525,7 @@ function cmdSnapshot(hippoRoot, args, flags) {
2525
2525
  return;
2526
2526
  }
2527
2527
  if (subcommand === 'show') {
2528
- const snapshot = loadActiveTaskSnapshot(hippoRoot);
2528
+ const snapshot = loadActiveTaskSnapshot(hippoRoot, resolveTenantId({}));
2529
2529
  if (!snapshot) {
2530
2530
  if (flags['json']) {
2531
2531
  console.log(JSON.stringify({ snapshot: null }));
@@ -2558,7 +2558,7 @@ function cmdSession(hippoRoot, args, flags) {
2558
2558
  console.error('Usage: hippo session log --id <session-id> --content <text> [--type <type>] [--task <task>] [--source <source>]');
2559
2559
  process.exit(1);
2560
2560
  }
2561
- const event = appendSessionEvent(hippoRoot, {
2561
+ const event = appendSessionEvent(hippoRoot, resolveTenantId({}), {
2562
2562
  session_id: sessionId,
2563
2563
  task: task || null,
2564
2564
  event_type: eventType || 'note',
@@ -2571,7 +2571,7 @@ function cmdSession(hippoRoot, args, flags) {
2571
2571
  return;
2572
2572
  }
2573
2573
  if (subcommand === 'show') {
2574
- const events = listSessionEvents(hippoRoot, {
2574
+ const events = listSessionEvents(hippoRoot, resolveTenantId({}), {
2575
2575
  session_id: sessionId || undefined,
2576
2576
  task: task || undefined,
2577
2577
  limit,
@@ -2584,8 +2584,8 @@ function cmdSession(hippoRoot, args, flags) {
2584
2584
  return;
2585
2585
  }
2586
2586
  if (subcommand === 'latest') {
2587
- const snapshot = loadActiveTaskSnapshot(hippoRoot);
2588
- const events = listSessionEvents(hippoRoot, {
2587
+ const snapshot = loadActiveTaskSnapshot(hippoRoot, resolveTenantId({}));
2588
+ const events = listSessionEvents(hippoRoot, resolveTenantId({}), {
2589
2589
  session_id: sessionId || snapshot?.session_id || undefined,
2590
2590
  limit,
2591
2591
  });
@@ -2618,7 +2618,7 @@ function cmdSession(hippoRoot, args, flags) {
2618
2618
  const metadata = { ended_at: new Date().toISOString() };
2619
2619
  if (summary)
2620
2620
  metadata.summary = summary;
2621
- const event = appendSessionEvent(hippoRoot, {
2621
+ const event = appendSessionEvent(hippoRoot, resolveTenantId({}), {
2622
2622
  session_id: sessionId,
2623
2623
  task: task || null,
2624
2624
  event_type: 'session_complete',
@@ -2630,7 +2630,7 @@ function cmdSession(hippoRoot, args, flags) {
2630
2630
  return;
2631
2631
  }
2632
2632
  if (subcommand === 'resume') {
2633
- const handoff = loadLatestHandoff(hippoRoot, sessionId || undefined);
2633
+ const handoff = loadLatestHandoff(hippoRoot, resolveTenantId({}), sessionId || undefined);
2634
2634
  if (!handoff) {
2635
2635
  console.log('No handoff to resume from.');
2636
2636
  return;
@@ -2703,7 +2703,7 @@ function cmdHandoff(hippoRoot, args, flags) {
2703
2703
  const artifacts = Array.isArray(artifactFlag)
2704
2704
  ? artifactFlag
2705
2705
  : (typeof artifactFlag === 'string' ? [artifactFlag] : []);
2706
- const handoff = saveSessionHandoff(hippoRoot, {
2706
+ const handoff = saveSessionHandoff(hippoRoot, resolveTenantId({}), {
2707
2707
  version: 1,
2708
2708
  sessionId,
2709
2709
  repoRoot: process.cwd(),
@@ -2723,7 +2723,7 @@ function cmdHandoff(hippoRoot, args, flags) {
2723
2723
  }
2724
2724
  if (subcommand === 'latest') {
2725
2725
  const sessionId = String(flags['session'] ?? flags['id'] ?? '').trim() || undefined;
2726
- const handoff = loadLatestHandoff(hippoRoot, sessionId);
2726
+ const handoff = loadLatestHandoff(hippoRoot, resolveTenantId({}), sessionId);
2727
2727
  if (!handoff) {
2728
2728
  if (flags['json']) {
2729
2729
  console.log(JSON.stringify({ handoff: null }));
@@ -2751,7 +2751,7 @@ function cmdHandoff(hippoRoot, args, flags) {
2751
2751
  console.error(`Invalid handoff ID: ${idArg}`);
2752
2752
  process.exit(1);
2753
2753
  }
2754
- const handoff = loadHandoffById(hippoRoot, handoffId);
2754
+ const handoff = loadHandoffById(hippoRoot, resolveTenantId({}), handoffId);
2755
2755
  if (!handoff) {
2756
2756
  if (flags['json']) {
2757
2757
  console.log(JSON.stringify({ handoff: null }));
@@ -2776,9 +2776,9 @@ function cmdCurrent(hippoRoot, args, flags) {
2776
2776
  const subcommand = args[0] ?? 'show';
2777
2777
  if (subcommand === 'show') {
2778
2778
  const asJson = Boolean(flags['json']);
2779
- const snapshot = loadActiveTaskSnapshot(hippoRoot);
2779
+ const snapshot = loadActiveTaskSnapshot(hippoRoot, resolveTenantId({}));
2780
2780
  const sessionId = snapshot?.session_id ?? undefined;
2781
- const events = listSessionEvents(hippoRoot, {
2781
+ const events = listSessionEvents(hippoRoot, resolveTenantId({}), {
2782
2782
  session_id: sessionId,
2783
2783
  limit: 5,
2784
2784
  });
@@ -2867,12 +2867,12 @@ async function cmdContext(hippoRoot, args, flags) {
2867
2867
  let totalTokens = 0;
2868
2868
  // Task snapshots / session events live in the local store. Skip when
2869
2869
  // local isn't initialized — loading would auto-create .hippo in the cwd.
2870
- const activeSnapshot = hasLocal ? loadActiveTaskSnapshot(hippoRoot) : null;
2870
+ const activeSnapshot = hasLocal ? loadActiveTaskSnapshot(hippoRoot, resolveTenantId({})) : null;
2871
2871
  const sessionHandoff = hasLocal && activeSnapshot?.session_id
2872
- ? loadLatestHandoff(hippoRoot, activeSnapshot.session_id)
2872
+ ? loadLatestHandoff(hippoRoot, resolveTenantId({}), activeSnapshot.session_id)
2873
2873
  : null;
2874
2874
  const recentSessionEvents = hasLocal && activeSnapshot?.session_id
2875
- ? listSessionEvents(hippoRoot, { session_id: activeSnapshot.session_id, limit: 5 })
2875
+ ? listSessionEvents(hippoRoot, resolveTenantId({}), { session_id: activeSnapshot.session_id, limit: 5 })
2876
2876
  : [];
2877
2877
  if (localEntries.length === 0 && globalEntries.length === 0 && !activeSnapshot && !sessionHandoff && recentSessionEvents.length === 0) {
2878
2878
  return;