hippo-memory 1.61.0 → 1.63.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 +37 -53
- package/dist/agent-memories/apply.d.ts +3 -1
- package/dist/agent-memories/apply.js +23 -1
- package/dist/agent-memories/claude-code.d.ts +4 -1
- package/dist/agent-memories/claude-code.js +53 -9
- package/dist/agent-memories/report.d.ts +1 -0
- package/dist/agent-memories/report.js +2 -2
- package/dist/agent-memories/sync.d.ts +3 -3
- package/dist/agent-memories/sync.js +46 -24
- package/dist/agent-memories/types.d.ts +0 -2
- package/dist/ambient-store.d.ts +4 -4
- package/dist/ambient-store.js +4 -3
- package/dist/api/assemble.d.ts +7 -10
- package/dist/api/assemble.js +62 -66
- package/dist/api/audit.d.ts +2 -2
- package/dist/api/audit.js +2 -2
- package/dist/api/auth.d.ts +6 -6
- package/dist/api/auth.js +5 -7
- package/dist/api/context-select.d.ts +50 -0
- package/dist/api/context-select.js +342 -0
- package/dist/api/context-types.d.ts +12 -17
- package/dist/api/context.d.ts +5 -4
- package/dist/api/context.js +208 -535
- package/dist/api/dormant.js +1 -1
- package/dist/api/drill-down.d.ts +4 -4
- package/dist/api/drill-down.js +56 -41
- package/dist/api/outcome.d.ts +8 -13
- package/dist/api/outcome.js +13 -17
- package/dist/api/promote.d.ts +5 -8
- package/dist/api/promote.js +55 -66
- package/dist/api/quarantine.js +3 -2
- package/dist/api/recall-types.d.ts +43 -55
- package/dist/api/recall.d.ts +3 -3
- package/dist/api/recall.js +310 -456
- package/dist/api/remember.d.ts +2 -2
- package/dist/api/sleep.d.ts +7 -35
- package/dist/api/sleep.js +206 -220
- package/dist/api/tokens.d.ts +2 -2
- package/dist/api/tokens.js +2 -2
- package/dist/api/types.d.ts +6 -10
- package/dist/api/types.js +2 -4
- package/dist/audit-prune.d.ts +4 -6
- package/dist/audit-prune.js +3 -5
- package/dist/audit.js +11 -33
- package/dist/auth.d.ts +8 -9
- package/dist/auth.js +5 -7
- package/dist/autolearn.d.ts +1 -1
- package/dist/autolearn.js +1 -1
- package/dist/availability.js +3 -5
- package/dist/capture/command.d.ts +5 -4
- package/dist/capture/command.js +8 -22
- package/dist/capture/compact.d.ts +2 -2
- package/dist/capture/compact.js +11 -15
- package/dist/capture/extract.js +37 -115
- package/dist/capture-error.d.ts +1 -1
- package/dist/capture-error.js +1 -1
- package/dist/churn-git.d.ts +1 -1
- package/dist/churn-git.js +1 -1
- package/dist/cli/audit.js +3 -4
- package/dist/cli/auth.js +3 -6
- package/dist/cli/briefs.js +324 -306
- package/dist/cli/context.js +44 -34
- package/dist/cli/continuity.js +283 -271
- package/dist/cli/curate.d.ts +1 -1
- package/dist/cli/curate.js +43 -58
- package/dist/cli/dag.js +5 -9
- package/dist/cli/decisions.js +334 -345
- package/dist/cli/explain.js +68 -61
- package/dist/cli/goals.js +1 -1
- package/dist/cli/init.js +1 -1
- package/dist/cli/maintenance.js +62 -51
- package/dist/cli/playbooks.js +391 -379
- package/dist/cli/projects.js +11 -6
- package/dist/cli/recall.js +30 -44
- package/dist/cli/remember.js +118 -87
- package/dist/cli/session-hooks.js +106 -115
- package/dist/cli/setup.d.ts +1 -1
- package/dist/cli/setup.js +267 -250
- package/dist/cli/shared.js +3 -3
- package/dist/cli/slack.js +1 -1
- package/dist/cli/sleep.js +27 -1
- package/dist/cli/status.d.ts +4 -4
- package/dist/cli/status.js +80 -76
- package/dist/cli/transfer.js +88 -107
- package/dist/cli/usage.js +9 -6
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +4 -9
- package/dist/compaction-record.d.ts +2 -2
- package/dist/compaction-record.js +89 -68
- package/dist/compare.d.ts +11 -16
- package/dist/compare.js +11 -16
- package/dist/config.d.ts +19 -18
- package/dist/config.js +90 -67
- package/dist/connectors/github/backfill.d.ts +2 -2
- package/dist/connectors/github/backfill.js +8 -15
- package/dist/connectors/github/cli-impl.js +3 -8
- package/dist/connectors/github/deletion.d.ts +5 -12
- package/dist/connectors/github/deletion.js +5 -12
- package/dist/connectors/github/dlq.d.ts +6 -9
- package/dist/connectors/github/dlq.js +2 -3
- package/dist/connectors/github/ingest.d.ts +5 -7
- package/dist/connectors/github/ingest.js +8 -12
- package/dist/connectors/github/octokit-client.d.ts +3 -5
- package/dist/connectors/github/octokit-client.js +5 -6
- package/dist/connectors/github/signature.d.ts +9 -39
- package/dist/connectors/github/signature.js +9 -39
- package/dist/connectors/github/tenant-routing.d.ts +1 -1
- package/dist/connectors/github/tenant-routing.js +1 -1
- package/dist/connectors/github/transform.js +2 -2
- package/dist/connectors/github/types.d.ts +2 -10
- package/dist/connectors/github/types.js +1 -3
- package/dist/connectors/slack/deletion.d.ts +3 -8
- package/dist/connectors/slack/deletion.js +3 -8
- package/dist/connectors/slack/dlq.d.ts +1 -1
- package/dist/connectors/slack/ingest.d.ts +1 -1
- package/dist/connectors/slack/ingest.js +7 -16
- package/dist/connectors/slack/signature.d.ts +1 -1
- package/dist/connectors/slack/tenant-routing.d.ts +3 -5
- package/dist/connectors/slack/tenant-routing.js +3 -5
- package/dist/connectors/slack/transform.d.ts +5 -6
- package/dist/connectors/slack/transform.js +5 -6
- package/dist/connectors/slack/types.d.ts +2 -6
- package/dist/connectors/slack/types.js +1 -3
- package/dist/connectors/slack/web-client.js +10 -3
- package/dist/connectors/slack/workspaces.d.ts +3 -5
- package/dist/connectors/slack/workspaces.js +3 -5
- package/dist/consolidate/conflicts.js +3 -14
- package/dist/consolidate/decay.js +9 -29
- package/dist/consolidate/llm-passes.js +4 -5
- package/dist/consolidate/merge.js +8 -23
- package/dist/consolidate/run.d.ts +1 -8
- package/dist/consolidate/run.js +3 -25
- package/dist/consolidate/sleep.js +5 -17
- package/dist/consolidate/traces.js +9 -21
- package/dist/customer-notes.d.ts +5 -7
- package/dist/customer-notes.js +82 -76
- package/dist/dag.d.ts +10 -21
- package/dist/dag.js +189 -203
- package/dist/db/continuity.js +2 -2
- package/dist/db/migrations/v14.js +1 -1
- package/dist/db/migrations/v15.js +1 -2
- package/dist/db/migrations/v16.js +3 -4
- package/dist/db/migrations/v17.js +2 -3
- package/dist/db/migrations/v19.js +1 -1
- package/dist/db/migrations/v20.js +1 -1
- package/dist/db/migrations/v21.js +2 -6
- package/dist/db/migrations/v22.js +2 -4
- package/dist/db/migrations/v23.js +1 -1
- package/dist/db/migrations/v24.js +4 -6
- package/dist/db/migrations/v25.js +2 -3
- package/dist/db/migrations/v26.js +3 -3
- package/dist/db/migrations/v27.js +2 -10
- package/dist/db/migrations/v28.js +5 -8
- package/dist/db/migrations/v29.js +3 -4
- package/dist/db/migrations/v30.js +2 -2
- package/dist/db/migrations/v31.js +1 -1
- package/dist/db/migrations/v32.js +1 -1
- package/dist/db/migrations/v33.js +3 -3
- package/dist/db/migrations/v34.js +1 -1
- package/dist/db/migrations/v35.js +3 -4
- package/dist/db/migrations/v36.js +3 -4
- package/dist/db/migrations/v37.js +5 -5
- package/dist/db/migrations/v38.js +7 -8
- package/dist/db/migrations/v39.js +1 -1
- package/dist/db/migrations/v40.js +4 -16
- package/dist/db/migrations/v41.js +3 -4
- package/dist/db/migrations/v42.js +3 -4
- package/dist/db/migrations/v45.js +1 -1
- package/dist/db/migrations/v46.js +1 -1
- package/dist/db/migrations/v47.js +1 -1
- package/dist/db/migrations/v48.js +1 -1
- package/dist/decisions.d.ts +2 -2
- package/dist/decisions.js +97 -80
- package/dist/dedupe.js +86 -61
- package/dist/delivery-recorder.js +154 -135
- package/dist/doctor.js +129 -110
- package/dist/dormant.js +1 -4
- package/dist/embedding-provider.d.ts +4 -8
- package/dist/embedding-provider.js +4 -8
- package/dist/embeddings.js +55 -47
- package/dist/env.d.ts +1 -1
- package/dist/env.js +12 -12
- package/dist/escape.d.ts +5 -0
- package/dist/escape.js +10 -0
- package/dist/eval-stats.d.ts +1 -2
- package/dist/eval-stats.js +1 -2
- package/dist/eval-suite.js +27 -21
- package/dist/extract.js +4 -9
- package/dist/failure-log.d.ts +3 -3
- package/dist/failure-log.js +1 -1
- package/dist/forward-claim-detector.d.ts +2 -4
- package/dist/forward-claim-detector.js +6 -11
- package/dist/goals.d.ts +3 -3
- package/dist/goals.js +103 -91
- package/dist/graph/read.d.ts +2 -2
- package/dist/graph/read.js +5 -6
- package/dist/graph/types.d.ts +8 -8
- package/dist/graph/write.d.ts +7 -14
- package/dist/graph/write.js +16 -23
- package/dist/graph-extract.d.ts +7 -8
- package/dist/graph-extract.js +62 -72
- package/dist/graph-recall.d.ts +2 -2
- package/dist/graph-recall.js +55 -49
- package/dist/graph-stream.d.ts +5 -6
- package/dist/graph-stream.js +66 -57
- package/dist/graph-view.d.ts +2 -2
- package/dist/graph-view.js +7 -7
- package/dist/half-life-migration.d.ts +1 -2
- package/dist/half-life-migration.js +2 -3
- package/dist/hooks/codex-session.js +1 -1
- package/dist/hooks/codex-wrapper.d.ts +1 -1
- package/dist/hooks/codex-wrapper.js +3 -2
- package/dist/hooks/json-hooks.d.ts +2 -2
- package/dist/hooks/json-hooks.js +5 -4
- package/dist/hooks/opencode.d.ts +1 -1
- package/dist/hooks/opencode.js +5 -4
- package/dist/hooks/shared.d.ts +3 -7
- package/dist/hooks/shared.js +1 -8
- package/dist/http-util.d.ts +2 -3
- package/dist/http-util.js +3 -0
- package/dist/importers/core.d.ts +2 -9
- package/dist/importers/core.js +15 -30
- package/dist/importers/sources.js +2 -1
- package/dist/importers/vault.js +2 -20
- package/dist/incidents.d.ts +1 -1
- package/dist/incidents.js +46 -39
- package/dist/instruction-detect.d.ts +1 -1
- package/dist/instruction-detect.js +1 -1
- package/dist/invalidation.d.ts +3 -0
- package/dist/invalidation.js +160 -114
- package/dist/json.d.ts +5 -0
- package/dist/json.js +4 -0
- package/dist/judgment.js +1 -2
- package/dist/local-embedding.js +1 -1
- package/dist/mcp/admin-tools.js +7 -17
- package/dist/mcp/format.js +1 -1
- package/dist/mcp/framing.js +3 -6
- package/dist/mcp/protocol.d.ts +2 -5
- package/dist/mcp/protocol.js +1 -3
- package/dist/mcp/recall-tools.js +12 -15
- package/dist/mcp/request.js +4 -3
- package/dist/mcp/session-state.js +2 -3
- package/dist/mcp/stdio.js +2 -1
- package/dist/mcp/tools.js +9 -6
- package/dist/memory-value-weights.d.ts +5 -8
- package/dist/memory-value-weights.js +5 -8
- package/dist/memory-value.d.ts +13 -13
- package/dist/memory-value.js +26 -37
- package/dist/memory.d.ts +20 -22
- package/dist/memory.js +24 -48
- package/dist/multihop.d.ts +1 -1
- package/dist/multihop.js +3 -2
- package/dist/owner-validation.d.ts +4 -5
- package/dist/owner-validation.js +4 -5
- package/dist/physics.d.ts +4 -4
- package/dist/physics.js +7 -9
- package/dist/policies.d.ts +9 -10
- package/dist/policies.js +96 -81
- package/dist/postinstall.js +3 -6
- package/dist/predictions/planning-fallacy.d.ts +9 -14
- package/dist/predictions/planning-fallacy.js +10 -16
- package/dist/predictions/store.d.ts +15 -23
- package/dist/predictions/store.js +36 -33
- package/dist/processes.d.ts +2 -7
- package/dist/processes.js +88 -72
- package/dist/project-briefs.d.ts +2 -3
- package/dist/project-briefs.js +141 -118
- package/dist/project-identity.d.ts +22 -9
- package/dist/project-identity.js +47 -12
- package/dist/project-merge.d.ts +28 -5
- package/dist/project-merge.js +213 -46
- package/dist/project-remote.d.ts +12 -0
- package/dist/project-remote.js +138 -0
- package/dist/prompt-recall.js +1 -2
- package/dist/rate-limit.d.ts +1 -1
- package/dist/rate-limit.js +1 -1
- package/dist/raw-archive.d.ts +9 -0
- package/dist/raw-archive.js +70 -53
- package/dist/recall-history.d.ts +19 -20
- package/dist/recall-history.js +24 -42
- package/dist/recall-pipeline.js +4 -28
- package/dist/recall-scope.d.ts +7 -8
- package/dist/recall-scope.js +7 -8
- package/dist/recall-trace.d.ts +5 -9
- package/dist/recall-trace.js +6 -10
- package/dist/refine-llm.d.ts +1 -1
- package/dist/refine-llm.js +2 -2
- package/dist/reject-flow.d.ts +3 -4
- package/dist/reject-flow.js +122 -117
- package/dist/rejection.d.ts +5 -6
- package/dist/rejection.js +7 -15
- package/dist/rerankers/clef.d.ts +1 -1
- package/dist/rerankers/jev.d.ts +1 -2
- package/dist/rerankers/jev.js +4 -5
- package/dist/rerankers/llm.d.ts +1 -2
- package/dist/rerankers/llm.js +1 -2
- package/dist/rerankers/types.d.ts +1 -2
- package/dist/rrf.d.ts +2 -2
- package/dist/rrf.js +2 -2
- package/dist/search/bm25-search.d.ts +1 -1
- package/dist/search/bm25-search.js +2 -1
- package/dist/search/boosts.js +2 -1
- package/dist/search/hybrid.d.ts +1 -1
- package/dist/search/hybrid.js +2 -1
- package/dist/search/physics-search.d.ts +1 -1
- package/dist/search/physics-search.js +2 -1
- package/dist/search/types.d.ts +2 -0
- package/dist/search/types.js +3 -1
- package/dist/secret-detect.d.ts +4 -5
- package/dist/secret-detect.js +6 -10
- package/dist/server/auth.js +5 -5
- package/dist/server/client-ip.js +1 -1
- package/dist/server/cursor.js +2 -1
- package/dist/server/mcp-http.js +4 -4
- package/dist/server/request.d.ts +3 -6
- package/dist/server/request.js +6 -7
- package/dist/server/routes/admin.js +5 -4
- package/dist/server/routes/customer-notes.js +6 -5
- package/dist/server/routes/decisions.js +4 -3
- package/dist/server/routes/incidents.js +7 -5
- package/dist/server/routes/memories.js +7 -7
- package/dist/server/routes/policies.js +3 -2
- package/dist/server/routes/predictions.js +12 -15
- package/dist/server/routes/processes.js +3 -2
- package/dist/server/routes/project-briefs.js +8 -7
- package/dist/server/routes/recall.js +95 -93
- package/dist/server/routes/skills.js +6 -5
- package/dist/server/types.d.ts +1 -1
- package/dist/server/validation.d.ts +1 -2
- package/dist/server/validation.js +7 -14
- package/dist/server-detect.js +72 -58
- package/dist/server.d.ts +2 -2
- package/dist/server.js +131 -117
- package/dist/shared.d.ts +26 -17
- package/dist/shared.js +102 -104
- package/dist/skills.d.ts +3 -3
- package/dist/skills.js +88 -72
- package/dist/store/audit-event.d.ts +2 -2
- package/dist/store/audit-event.js +1 -1
- package/dist/store/candidates.d.ts +2 -2
- package/dist/store/candidates.js +4 -3
- package/dist/store/conflicts.js +30 -22
- package/dist/store/delete-and-batch.d.ts +11 -14
- package/dist/store/delete-and-batch.js +40 -91
- package/dist/store/entry-reads.d.ts +17 -25
- package/dist/store/entry-reads.js +59 -40
- package/dist/store/entry-row.d.ts +6 -24
- package/dist/store/entry-row.js +6 -24
- package/dist/store/entry-writes.d.ts +6 -7
- package/dist/store/entry-writes.js +13 -11
- package/dist/store/handoffs.d.ts +1 -1
- package/dist/store/handoffs.js +7 -10
- package/dist/store/index-and-stats.d.ts +2 -6
- package/dist/store/index-and-stats.js +4 -10
- package/dist/store/mirrors.d.ts +6 -19
- package/dist/store/mirrors.js +14 -39
- package/dist/store/open.js +9 -31
- package/dist/store/rows.d.ts +5 -11
- package/dist/store/rows.js +6 -11
- package/dist/store/search-rows.d.ts +17 -34
- package/dist/store/search-rows.js +34 -56
- package/dist/store/sessions.d.ts +4 -5
- package/dist/store/sessions.js +5 -6
- package/dist/store/summaries.d.ts +13 -17
- package/dist/store/summaries.js +26 -70
- package/dist/support-bundle.js +4 -8
- package/dist/tenant.d.ts +1 -5
- package/dist/token-ledger.d.ts +1 -1
- package/dist/token-ledger.js +3 -5
- package/dist/trace.js +1 -3
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/working-memory.d.ts +1 -1
- package/extensions/openclaw-plugin/openclaw.plugin.json +1 -1
- package/extensions/openclaw-plugin/package.json +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
|
@@ -8,7 +8,8 @@ import { resolveTenantId } from '../tenant.js';
|
|
|
8
8
|
import { appendAuditEvent, reportAuditWriteFailure } from '../audit.js';
|
|
9
9
|
import { commonDerivationScope } from '../recall-scope.js';
|
|
10
10
|
import { log } from '../log.js';
|
|
11
|
-
import { REPLAY_COUNT_DEFAULT
|
|
11
|
+
import { REPLAY_COUNT_DEFAULT } from './run.js';
|
|
12
|
+
import { isJsonString } from '../json.js';
|
|
12
13
|
// -------------------------------------------------------------------------
|
|
13
14
|
// 1.4. Auto-promote complete sessions to traces
|
|
14
15
|
// -------------------------------------------------------------------------
|
|
@@ -24,10 +25,8 @@ export function promoteSessionTraces(run) {
|
|
|
24
25
|
let tracesSkippedRejected = 0;
|
|
25
26
|
const windowDays = run.config.autoTraceWindowDays ?? 7;
|
|
26
27
|
const sinceMs = run.now.getTime() - windowDays * 24 * 60 * 60 * 1000;
|
|
27
|
-
// Auto-trace
|
|
28
|
-
// tenant
|
|
29
|
-
// consolidation across all tenants need a per-tenant loop layered on top
|
|
30
|
-
// of this — tracked in docs/plans/2026-05-02-continuity-tables-tenant-scope.md.
|
|
28
|
+
// Auto-trace runs single-tenant (the env-resolved tenant); consolidating
|
|
29
|
+
// every tenant needs a per-tenant loop layered on top of this.
|
|
31
30
|
const consolidationTenant = resolveTenantId({});
|
|
32
31
|
const promotable = findPromotableSessions(run.hippoRoot, consolidationTenant, sinceMs);
|
|
33
32
|
for (const session of promotable) {
|
|
@@ -60,7 +59,7 @@ function sessionTrace(run, consolidationTenant, sessionId) {
|
|
|
60
59
|
session_id: sessionId,
|
|
61
60
|
limit: 1000,
|
|
62
61
|
});
|
|
63
|
-
//
|
|
62
|
+
// A mixed-scope session would otherwise leak into one trace.
|
|
64
63
|
const sessionScope = commonDerivationScope(events.map((e) => e.scope));
|
|
65
64
|
if (!sessionScope.ok) {
|
|
66
65
|
run.result.tracesSkippedMixedScope++;
|
|
@@ -91,26 +90,15 @@ function sessionTrace(run, consolidationTenant, sessionId) {
|
|
|
91
90
|
tags: ['auto-promoted'],
|
|
92
91
|
source: 'auto-promote',
|
|
93
92
|
scope: sessionScope.scope,
|
|
94
|
-
//
|
|
95
|
-
// tenant
|
|
96
|
-
// under. Before
|
|
97
|
-
// this, createMemory omitted tenantId and the trace always landed
|
|
98
|
-
// 'default' (memory.ts:535) while the idempotency check ran under
|
|
99
|
-
// consolidationTenant — for any non-default tenant that check never
|
|
100
|
-
// hit, and the trace regenerated every sleep.
|
|
93
|
+
// Same tenant as the traceExistsForSession check above, or a
|
|
94
|
+
// non-default tenant's trace regenerates every sleep.
|
|
101
95
|
tenantId: consolidationTenant,
|
|
102
96
|
baseHalfLifeDays: run.config.defaultHalfLifeDays,
|
|
103
97
|
});
|
|
104
98
|
return { trace, outcome };
|
|
105
99
|
}
|
|
106
|
-
//
|
|
107
|
-
//
|
|
108
|
-
// removed, that idempotency check no longer blocks regeneration, and
|
|
109
|
-
// this write would otherwise reach batchWriteAndDelete's guard bypass
|
|
110
|
-
// unchecked, resurrecting it every sleep. Check under THE ENTRY'S OWN
|
|
111
|
-
// stamped tenantId (read off `trace` after createMemory — never guess
|
|
112
|
-
// the tenant) + the built content's digest. A hit skips the push
|
|
113
|
-
// entirely: not counted as promoted, not added to survivors.
|
|
100
|
+
// traceExistsForSession only sees live rows, so a removed rejected trace would regenerate every
|
|
101
|
+
// sleep; check the tombstone under the entry's own stamped tenantId, never a guessed one.
|
|
114
102
|
function traceRejected(run, trace, sessionId) {
|
|
115
103
|
const consolidateDb = run.getConsolidateDb();
|
|
116
104
|
if (!consolidateDb)
|
package/dist/customer-notes.d.ts
CHANGED
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* (docs/plans/2026-06-01-e2-customer-note-object.md).
|
|
2
|
+
* customer_note first-class object.
|
|
4
3
|
*
|
|
5
4
|
* A `customer_note` is a discrete note recorded against an account/customer entity:
|
|
6
5
|
* a `note` body scoped to a `customer`, evolving via the supersede delta lifecycle.
|
|
7
|
-
* Entity-scoping is a free-form `customer` column (the `entities` table is unbuilt
|
|
8
|
-
*
|
|
6
|
+
* Entity-scoping is a free-form `customer` column (the `entities` table is unbuilt,
|
|
7
|
+
* so an FK is deferred). Unlike project_brief's one-summary-per-repo,
|
|
9
8
|
* a customer accrues MANY discrete notes over time, each with its own supersede chain
|
|
10
9
|
* (correct a note -> a new version preserving history; close retires it).
|
|
11
10
|
*
|
|
12
11
|
* Reuses the project_brief/skill supersede machinery verbatim (superseded_by self-FK
|
|
13
12
|
* + CAS + INSERT-preflight + server-derived version + change_summary + supersede
|
|
14
|
-
* tenant-match trigger). It has NO assembler/renderer (the simplest
|
|
13
|
+
* tenant-match trigger). It has NO assembler/renderer (the simplest first-class object): the
|
|
15
14
|
* contribution is purely the entity-scoping dimension.
|
|
16
15
|
*
|
|
17
16
|
* The `customer_notes` table is the source of truth (survives memory decay); the
|
|
@@ -73,8 +72,7 @@ export interface ListCustomerNotesOpts {
|
|
|
73
72
|
* the new version = predecessor.version + 1 (server-derived).
|
|
74
73
|
*
|
|
75
74
|
* The memory mirror carries a `customer:<lc>` tag (in addition to ['customer_note']
|
|
76
|
-
* + caller extraTags) so scope-aware recall treats the note as entity-local
|
|
77
|
-
* project_brief codex-P2 recall-locality lesson applied to entity scoping. There is
|
|
75
|
+
* + caller extraTags) so scope-aware recall treats the note as entity-local. There is
|
|
78
76
|
* no self-recursion path (customer_note has no receipt-query/refresh).
|
|
79
77
|
*/
|
|
80
78
|
export declare function saveCustomerNote(hippoRoot: string, tenantId: string, opts: SaveCustomerNoteOpts, actor?: string): CustomerNote;
|
package/dist/customer-notes.js
CHANGED
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* (docs/plans/2026-06-01-e2-customer-note-object.md).
|
|
2
|
+
* customer_note first-class object.
|
|
4
3
|
*
|
|
5
4
|
* A `customer_note` is a discrete note recorded against an account/customer entity:
|
|
6
5
|
* a `note` body scoped to a `customer`, evolving via the supersede delta lifecycle.
|
|
7
|
-
* Entity-scoping is a free-form `customer` column (the `entities` table is unbuilt
|
|
8
|
-
*
|
|
6
|
+
* Entity-scoping is a free-form `customer` column (the `entities` table is unbuilt,
|
|
7
|
+
* so an FK is deferred). Unlike project_brief's one-summary-per-repo,
|
|
9
8
|
* a customer accrues MANY discrete notes over time, each with its own supersede chain
|
|
10
9
|
* (correct a note -> a new version preserving history; close retires it).
|
|
11
10
|
*
|
|
12
11
|
* Reuses the project_brief/skill supersede machinery verbatim (superseded_by self-FK
|
|
13
12
|
* + CAS + INSERT-preflight + server-derived version + change_summary + supersede
|
|
14
|
-
* tenant-match trigger). It has NO assembler/renderer (the simplest
|
|
13
|
+
* tenant-match trigger). It has NO assembler/renderer (the simplest first-class object): the
|
|
15
14
|
* contribution is purely the entity-scoping dimension.
|
|
16
15
|
*
|
|
17
16
|
* The `customer_notes` table is the source of truth (survives memory decay); the
|
|
@@ -94,9 +93,78 @@ const NOTE_COLS = `
|
|
|
94
93
|
function buildNoteContent(customer, note) {
|
|
95
94
|
return `${customer}\n\n${note}`;
|
|
96
95
|
}
|
|
97
|
-
//
|
|
98
|
-
//
|
|
99
|
-
//
|
|
96
|
+
// Preflight the supersede target BEFORE inserting the new row (so the new
|
|
97
|
+
// autoincrement id can never be its own supersede target); read the
|
|
98
|
+
// predecessor version in the same SELECT for server-derived versioning.
|
|
99
|
+
function preflightNoteSupersede(db, tenantId, supersedesId) {
|
|
100
|
+
// SAFETY: SELECT projects exactly status, version; .get() returns that
|
|
101
|
+
// shape for the matching row, or undefined when no note/tenant pair matches.
|
|
102
|
+
const pred = db.prepare(`SELECT status, version FROM customer_notes WHERE id = ? AND tenant_id = ?`).get(supersedesId, tenantId);
|
|
103
|
+
if (!pred) {
|
|
104
|
+
throw new NotFoundError(`saveCustomerNote: note ${supersedesId} to supersede not found for tenant ${tenantId}`);
|
|
105
|
+
}
|
|
106
|
+
if (pred.status !== 'active') {
|
|
107
|
+
throw new ConflictError(`saveCustomerNote: note ${supersedesId} is not active (status='${pred.status}'); only active notes can be superseded.`);
|
|
108
|
+
}
|
|
109
|
+
return pred.version + 1;
|
|
110
|
+
}
|
|
111
|
+
function insertNoteRow(db, memoryId, w, version) {
|
|
112
|
+
const result = db.prepare(`
|
|
113
|
+
INSERT INTO customer_notes(
|
|
114
|
+
memory_id, tenant_id, customer, note, version,
|
|
115
|
+
status, superseded_by, superseded_at, change_summary, closed_at, created_at
|
|
116
|
+
) VALUES (?, ?, ?, ?, ?, 'active', NULL, NULL, ?, NULL, ?)
|
|
117
|
+
`).run(memoryId, w.tenantId, w.customer, w.note, version, w.changeSummary, w.now);
|
|
118
|
+
return Number(result.lastInsertRowid ?? 0);
|
|
119
|
+
}
|
|
120
|
+
function supersedeNoteRow(db, w, supersedesId, noteId, version) {
|
|
121
|
+
const sup = db.prepare(`
|
|
122
|
+
UPDATE customer_notes
|
|
123
|
+
SET status = 'superseded', superseded_by = ?, superseded_at = ?
|
|
124
|
+
WHERE id = ? AND tenant_id = ? AND status = 'active' AND id != ?
|
|
125
|
+
`).run(noteId, w.now, supersedesId, w.tenantId, noteId);
|
|
126
|
+
if (sup.changes === 0) {
|
|
127
|
+
throw new ConflictError(`saveCustomerNote: note ${supersedesId} could not be superseded (no longer active or self-reference).`);
|
|
128
|
+
}
|
|
129
|
+
appendAuditEvent(db, {
|
|
130
|
+
tenantId: w.tenantId,
|
|
131
|
+
actor: w.actor,
|
|
132
|
+
op: 'customer_note_supersede',
|
|
133
|
+
targetId: String(supersedesId),
|
|
134
|
+
metadata: {
|
|
135
|
+
note_id: supersedesId,
|
|
136
|
+
superseded_by: noteId,
|
|
137
|
+
new_version: version,
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
/** The afterWrite body: preflight, INSERT, supersede, reload, create audit, all in one SAVEPOINT. */
|
|
142
|
+
function writeNoteRow(db, memoryId, w) {
|
|
143
|
+
const version = w.supersedesId !== undefined ? preflightNoteSupersede(db, w.tenantId, w.supersedesId) : 1;
|
|
144
|
+
const noteId = insertNoteRow(db, memoryId, w, version);
|
|
145
|
+
if (w.supersedesId !== undefined)
|
|
146
|
+
supersedeNoteRow(db, w, w.supersedesId, noteId, version);
|
|
147
|
+
// SAFETY: SELECT ${NOTE_COLS} projects exactly the CustomerNoteRow columns;
|
|
148
|
+
// .get() returns that row, or undefined only if the just-inserted id can't
|
|
149
|
+
// be found.
|
|
150
|
+
const row = db.prepare(`SELECT ${NOTE_COLS} FROM customer_notes WHERE id = ?`)
|
|
151
|
+
.get(noteId);
|
|
152
|
+
if (!row)
|
|
153
|
+
throw new Error('saveCustomerNote: failed to reload saved note row');
|
|
154
|
+
// GDPR-light metadata: ids + flags only, no note text.
|
|
155
|
+
appendAuditEvent(db, {
|
|
156
|
+
tenantId: w.tenantId,
|
|
157
|
+
actor: w.actor,
|
|
158
|
+
op: 'customer_note_create',
|
|
159
|
+
targetId: String(noteId),
|
|
160
|
+
metadata: {
|
|
161
|
+
note_id: noteId,
|
|
162
|
+
customer: w.customer,
|
|
163
|
+
version,
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
return row;
|
|
167
|
+
}
|
|
100
168
|
/**
|
|
101
169
|
* Create a customer_note (or a new version that supersedes an existing one). Writes
|
|
102
170
|
* the memory mirror + the customer_notes row atomically inside writeEntry's SAVEPOINT.
|
|
@@ -105,8 +173,7 @@ function buildNoteContent(customer, note) {
|
|
|
105
173
|
* the new version = predecessor.version + 1 (server-derived).
|
|
106
174
|
*
|
|
107
175
|
* The memory mirror carries a `customer:<lc>` tag (in addition to ['customer_note']
|
|
108
|
-
* + caller extraTags) so scope-aware recall treats the note as entity-local
|
|
109
|
-
* project_brief codex-P2 recall-locality lesson applied to entity scoping. There is
|
|
176
|
+
* + caller extraTags) so scope-aware recall treats the note as entity-local. There is
|
|
110
177
|
* no self-recursion path (customer_note has no receipt-query/refresh).
|
|
111
178
|
*/
|
|
112
179
|
export function saveCustomerNote(hippoRoot, tenantId, opts, actor = 'cli') {
|
|
@@ -125,75 +192,14 @@ export function saveCustomerNote(hippoRoot, tenantId, opts, actor = 'cli') {
|
|
|
125
192
|
baseHalfLifeDays: objectHalfLifeDays(hippoRoot),
|
|
126
193
|
tenantId,
|
|
127
194
|
});
|
|
195
|
+
const w = {
|
|
196
|
+
tenantId, actor, customer, note: opts.note, changeSummary, supersedesId: opts.supersedesNoteId, now,
|
|
197
|
+
};
|
|
128
198
|
let savedRow;
|
|
129
199
|
writeEntry(hippoRoot, mem, {
|
|
130
200
|
actor,
|
|
131
201
|
afterWrite: (db, memoryId) => {
|
|
132
|
-
|
|
133
|
-
// autoincrement id can never be its own supersede target); read the
|
|
134
|
-
// predecessor version in the same SELECT for server-derived versioning.
|
|
135
|
-
// Mirrors saveProjectBrief / saveSkill (codex P1 2026-05-28).
|
|
136
|
-
let version = 1;
|
|
137
|
-
if (opts.supersedesNoteId !== undefined) {
|
|
138
|
-
// SAFETY: SELECT projects exactly status, version; .get() returns that
|
|
139
|
-
// shape for the matching row, or undefined when no note/tenant pair matches.
|
|
140
|
-
const pred = db.prepare(`SELECT status, version FROM customer_notes WHERE id = ? AND tenant_id = ?`).get(opts.supersedesNoteId, tenantId);
|
|
141
|
-
if (!pred) {
|
|
142
|
-
throw new NotFoundError(`saveCustomerNote: note ${opts.supersedesNoteId} to supersede not found for tenant ${tenantId}`);
|
|
143
|
-
}
|
|
144
|
-
if (pred.status !== 'active') {
|
|
145
|
-
throw new ConflictError(`saveCustomerNote: note ${opts.supersedesNoteId} is not active (status='${pred.status}'); only active notes can be superseded.`);
|
|
146
|
-
}
|
|
147
|
-
version = pred.version + 1;
|
|
148
|
-
}
|
|
149
|
-
const result = db.prepare(`
|
|
150
|
-
INSERT INTO customer_notes(
|
|
151
|
-
memory_id, tenant_id, customer, note, version,
|
|
152
|
-
status, superseded_by, superseded_at, change_summary, closed_at, created_at
|
|
153
|
-
) VALUES (?, ?, ?, ?, ?, 'active', NULL, NULL, ?, NULL, ?)
|
|
154
|
-
`).run(memoryId, tenantId, customer, opts.note, version, changeSummary, now);
|
|
155
|
-
const noteId = Number(result.lastInsertRowid ?? 0);
|
|
156
|
-
if (opts.supersedesNoteId !== undefined) {
|
|
157
|
-
const sup = db.prepare(`
|
|
158
|
-
UPDATE customer_notes
|
|
159
|
-
SET status = 'superseded', superseded_by = ?, superseded_at = ?
|
|
160
|
-
WHERE id = ? AND tenant_id = ? AND status = 'active' AND id != ?
|
|
161
|
-
`).run(noteId, now, opts.supersedesNoteId, tenantId, noteId);
|
|
162
|
-
if (sup.changes === 0) {
|
|
163
|
-
throw new ConflictError(`saveCustomerNote: note ${opts.supersedesNoteId} could not be superseded (no longer active or self-reference).`);
|
|
164
|
-
}
|
|
165
|
-
appendAuditEvent(db, {
|
|
166
|
-
tenantId,
|
|
167
|
-
actor,
|
|
168
|
-
op: 'customer_note_supersede',
|
|
169
|
-
targetId: String(opts.supersedesNoteId),
|
|
170
|
-
metadata: {
|
|
171
|
-
note_id: opts.supersedesNoteId,
|
|
172
|
-
superseded_by: noteId,
|
|
173
|
-
new_version: version,
|
|
174
|
-
},
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
// SAFETY: SELECT ${NOTE_COLS} projects exactly the CustomerNoteRow columns;
|
|
178
|
-
// .get() returns that row, or undefined only if the just-inserted id can't
|
|
179
|
-
// be found.
|
|
180
|
-
const row = db.prepare(`SELECT ${NOTE_COLS} FROM customer_notes WHERE id = ?`)
|
|
181
|
-
.get(noteId);
|
|
182
|
-
if (!row)
|
|
183
|
-
throw new Error('saveCustomerNote: failed to reload saved note row');
|
|
184
|
-
savedRow = row;
|
|
185
|
-
// GDPR-light metadata: ids + flags only, no note text.
|
|
186
|
-
appendAuditEvent(db, {
|
|
187
|
-
tenantId,
|
|
188
|
-
actor,
|
|
189
|
-
op: 'customer_note_create',
|
|
190
|
-
targetId: String(noteId),
|
|
191
|
-
metadata: {
|
|
192
|
-
note_id: noteId,
|
|
193
|
-
customer,
|
|
194
|
-
version,
|
|
195
|
-
},
|
|
196
|
-
});
|
|
202
|
+
savedRow = writeNoteRow(db, memoryId, w);
|
|
197
203
|
},
|
|
198
204
|
afterCommit: () => markGraphDirty(hippoRoot, tenantId, mem.id),
|
|
199
205
|
});
|
|
@@ -246,7 +252,7 @@ export function closeCustomerNote(hippoRoot, tenantId, id, actor = 'cli') {
|
|
|
246
252
|
// Closing removes the object from the graph. Remove its rows DIRECTLY (deterministic),
|
|
247
253
|
// not only via an enqueued rebuild whose queue item is lost if the mirror is later
|
|
248
254
|
// forgotten (the queue row cascade-deletes with the memory), which would leave the closed
|
|
249
|
-
// object stale and could block that forget
|
|
255
|
+
// object stale and could block that forget. Still enqueue when a mirror exists
|
|
250
256
|
// so a concurrent rebuild re-derives consistently (harmless if it also runs).
|
|
251
257
|
removeGraphEntitiesForObject(hippoRoot, tenantId, 'customer', closed.id);
|
|
252
258
|
if (closed.memoryId) {
|
package/dist/dag.d.ts
CHANGED
|
@@ -16,11 +16,8 @@ export interface DagBuildResult {
|
|
|
16
16
|
candidateClusters: number;
|
|
17
17
|
summariesCreated: number;
|
|
18
18
|
factsLinked: number;
|
|
19
|
-
/**
|
|
20
|
-
*
|
|
21
|
-
* phase abort). Member re-parenting writes are unaffected by construction
|
|
22
|
-
* (same id + same content = guard-exempt), so this only ever counts
|
|
23
|
-
* summary-creation refusals. */
|
|
19
|
+
/** Clusters skipped because the summary matched a rejected value. Re-parenting writes are
|
|
20
|
+
* guard-exempt (same id + content), so only summary-creation refusals count. */
|
|
24
21
|
rejected: number;
|
|
25
22
|
}
|
|
26
23
|
export declare function buildDag(hippoRoot: string, facts: MemoryEntry[], opts: DagSummaryOptions): Promise<DagBuildResult>;
|
|
@@ -33,21 +30,17 @@ export interface DagRebuildResult {
|
|
|
33
30
|
capped: boolean;
|
|
34
31
|
}
|
|
35
32
|
/**
|
|
36
|
-
*
|
|
33
|
+
* Sleep-cycle phase that drains the dirty L2 summary queue.
|
|
37
34
|
* Thin orchestrator; the heavy lifting lives in store.ts (load + apply)
|
|
38
35
|
* and dag.ts:generateDagSummary (LLM call).
|
|
39
36
|
*
|
|
40
|
-
* Per-summary try/catch
|
|
41
|
-
* throwing rebuild does NOT abort the rest of the queue.
|
|
37
|
+
* Per-summary try/catch: one throwing rebuild does NOT abort the rest of the queue.
|
|
42
38
|
*
|
|
43
39
|
* Race-loser handling: applyRebuildResult's UPDATE WHERE includes
|
|
44
40
|
* AND summary_dirty=1, so concurrent sleep's second writer returns
|
|
45
41
|
* changed=false. Silent skip (neither rebuilt++ nor refused++ nor failed++).
|
|
46
42
|
*
|
|
47
|
-
*
|
|
48
|
-
* (rebuild content matches a previously-rejected value) increments
|
|
49
|
-
* `refused`, not `rebuilt`. Dirty still clears either way; only the stat
|
|
50
|
-
* split changed (docs/plans/2026-08-15-hardening-at1-followups.md T4).
|
|
43
|
+
* A tombstone hit counts as `refused`, not `rebuilt`; dirty clears either way.
|
|
51
44
|
*/
|
|
52
45
|
export declare function rebuildDirtySummaries(hippoRoot: string, opts: DagSummaryOptions & {
|
|
53
46
|
cap?: number;
|
|
@@ -57,21 +50,17 @@ export interface EntityProfilesBuildResult {
|
|
|
57
50
|
profilesCreated: number;
|
|
58
51
|
l2sLinked: number;
|
|
59
52
|
failed: number;
|
|
60
|
-
/**
|
|
61
|
-
*
|
|
62
|
-
* rate-limit) — a tombstone hit is a deliberate refusal, not an error. */
|
|
53
|
+
/** Clusters skipped because the profile matched a rejected value. Kept apart from
|
|
54
|
+
* `failed`: a tombstone hit is a deliberate refusal, not an error. */
|
|
63
55
|
rejected: number;
|
|
64
56
|
}
|
|
65
57
|
/**
|
|
66
|
-
*
|
|
58
|
+
* Build L3 entity profiles by clustering L2 summaries with
|
|
67
59
|
* shared entity tags. Threshold 2+ L2s per entity. Mirrors buildDag L1->L2
|
|
68
60
|
* pattern, one level up.
|
|
69
61
|
*
|
|
70
|
-
* Born-dirty cancellation
|
|
71
|
-
*
|
|
72
|
-
* call clearSummaryDirtyAfterBuild with source='buildEntityProfiles-clean'
|
|
73
|
-
* so E3 sleep-cycle rebuild doesn't re-rebuild the freshly-built L3 this
|
|
74
|
-
* same cycle.
|
|
62
|
+
* Born-dirty cancellation: each L2 link write marks the new L3 dirty, so clear it
|
|
63
|
+
* or the same sleep cycle's rebuild re-rebuilds the freshly built L3.
|
|
75
64
|
*/
|
|
76
65
|
export declare function buildEntityProfiles(hippoRoot: string, l2Summaries: MemoryEntry[], opts: DagSummaryOptions): Promise<EntityProfilesBuildResult>;
|
|
77
66
|
//# sourceMappingURL=dag.d.ts.map
|