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
package/dist/goals.js
CHANGED
|
@@ -29,7 +29,7 @@ export function pushGoal(hippoRoot, opts) {
|
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
/**
|
|
32
|
-
*
|
|
32
|
+
* Depth-cap enforcer shared by pushGoalWithDb and resumeGoal.
|
|
33
33
|
* If the (tenant, session) has >= MAX_ACTIVE_GOAL_DEPTH active goals,
|
|
34
34
|
* suspend the oldest `overflow` ones.
|
|
35
35
|
*
|
|
@@ -38,7 +38,7 @@ export function pushGoal(hippoRoot, opts) {
|
|
|
38
38
|
* is impossible to misread the contract at a call site. Both existing call
|
|
39
39
|
* sites (pushGoalWithDb, resumeGoal) wrap in `BEGIN IMMEDIATE` already.
|
|
40
40
|
*
|
|
41
|
-
* @internal
|
|
41
|
+
* @internal Internal goal-stack invariant. Subject to change.
|
|
42
42
|
*/
|
|
43
43
|
export function enforceDepthCapWithinTx(db, tenantId, sessionId) {
|
|
44
44
|
// SAFETY: the row comes from the SELECT above, which projects exactly one
|
|
@@ -159,35 +159,11 @@ export function getActiveGoalsWithDb(db, opts) {
|
|
|
159
159
|
`).all(opts.tenantId, opts.sessionId);
|
|
160
160
|
return rows.map(rowToGoal);
|
|
161
161
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
* Caller responsibilities:
|
|
168
|
-
* - Do NOT call when an explicit `goalTag` is set (caller's gate)
|
|
169
|
-
* - Pass entry-backed rows (with `entry.tags`, `entry.id`, optional
|
|
170
|
-
* `entry.schema_fit`)
|
|
171
|
-
* - Manage the db handle lifecycle (helper neither opens nor closes)
|
|
172
|
-
* - Recompute `tokens` after if returned rows are projected to a budgeted
|
|
173
|
-
* shape
|
|
174
|
-
*
|
|
175
|
-
* Log rows cover the top `limit` boosted rows that live in this store's
|
|
176
|
-
* `memories` table (global-only ids are skipped to keep the FK on
|
|
177
|
-
* goal_recall_log.memory_id valid).
|
|
178
|
-
*
|
|
179
|
-
* @internal Recall ranking helper. Subject to change.
|
|
180
|
-
*/
|
|
181
|
-
export function computeGoalStackBoost(db, results, opts) {
|
|
182
|
-
const { sessionId, tenantId, limit, trace } = opts;
|
|
183
|
-
const active = getActiveGoalsWithDb(db, { sessionId, tenantId });
|
|
184
|
-
if (active.length === 0)
|
|
185
|
-
return { results, log: [] };
|
|
186
|
-
const goalsByTag = new Map(active.map((g) => [g.goalName, g]));
|
|
187
|
-
// Load retrieval_policy rows for active goals so per-policy multipliers
|
|
188
|
-
// can compose onto the base goal-tag boost. Composed result is hard-capped
|
|
189
|
-
// at MAX_FINAL_MULTIPLIER (3.0x) BEFORE applying to score -- even an
|
|
190
|
-
// `errorPriority: 9.0` policy cannot exceed 3.0x.
|
|
162
|
+
// Load retrieval_policy rows for active goals so per-policy multipliers
|
|
163
|
+
// can compose onto the base goal-tag boost. Composed result is hard-capped
|
|
164
|
+
// at MAX_FINAL_MULTIPLIER (3.0x) BEFORE applying to score -- even an
|
|
165
|
+
// `errorPriority: 9.0` policy cannot exceed 3.0x.
|
|
166
|
+
function loadGoalPolicies(db, active) {
|
|
191
167
|
const policiesByGoalId = new Map();
|
|
192
168
|
for (const g of active) {
|
|
193
169
|
if (!g.retrievalPolicyId)
|
|
@@ -210,67 +186,42 @@ export function computeGoalStackBoost(db, results, opts) {
|
|
|
210
186
|
});
|
|
211
187
|
}
|
|
212
188
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
const goal = goalsByTag.get(tag);
|
|
228
|
-
const policy = policiesByGoalId.get(goal.id);
|
|
229
|
-
if (!policy)
|
|
230
|
-
continue;
|
|
231
|
-
if (policy.policyType === 'error-prioritized' && tags.includes('error')) {
|
|
232
|
-
multiplier *= policy.errorPriority;
|
|
233
|
-
}
|
|
234
|
-
else if (policy.policyType === 'schema-fit-biased') {
|
|
235
|
-
// Linearly weight schema_fit in [0,1] up to (weightSchemaFit)x.
|
|
236
|
-
// Default 1.0 is a no-op.
|
|
237
|
-
multiplier *=
|
|
238
|
-
1.0 +
|
|
239
|
-
Math.max(0, policy.weightSchemaFit - 1.0) *
|
|
240
|
-
(r.entry.schema_fit ?? 0.5);
|
|
241
|
-
}
|
|
242
|
-
else if (policy.policyType === 'recency-first') {
|
|
243
|
-
multiplier *= policy.weightRecency;
|
|
244
|
-
}
|
|
245
|
-
else if (policy.policyType === 'hybrid') {
|
|
246
|
-
multiplier *= policy.weightOutcome;
|
|
247
|
-
}
|
|
189
|
+
return policiesByGoalId;
|
|
190
|
+
}
|
|
191
|
+
/** The capped boost for one row whose tags match `matches` active goals. */
|
|
192
|
+
function goalBoostMultiplier(entry, tags, matches, goalsByTag, policiesByGoalId) {
|
|
193
|
+
// Base 2.0x for first match, +0.5x per additional, capped at 3.0x.
|
|
194
|
+
let multiplier = Math.min(2.0 + 0.5 * (matches.length - 1), MAX_FINAL_MULTIPLIER);
|
|
195
|
+
// Compose per-policy multipliers per matched tag.
|
|
196
|
+
for (const tag of matches) {
|
|
197
|
+
const goal = goalsByTag.get(tag);
|
|
198
|
+
const policy = policiesByGoalId.get(goal.id);
|
|
199
|
+
if (!policy)
|
|
200
|
+
continue;
|
|
201
|
+
if (policy.policyType === 'error-prioritized' && tags.includes('error')) {
|
|
202
|
+
multiplier *= policy.errorPriority;
|
|
248
203
|
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
stage: 'goal-boost',
|
|
257
|
-
multiplier,
|
|
258
|
-
scoreBefore: r.score,
|
|
259
|
-
scoreAfter: r.score * multiplier,
|
|
260
|
-
note: matches.join(', '),
|
|
261
|
-
});
|
|
204
|
+
else if (policy.policyType === 'schema-fit-biased') {
|
|
205
|
+
// Linearly weight schema_fit in [0,1] up to (weightSchemaFit)x.
|
|
206
|
+
// Default 1.0 is a no-op.
|
|
207
|
+
multiplier *=
|
|
208
|
+
1.0 +
|
|
209
|
+
Math.max(0, policy.weightSchemaFit - 1.0) *
|
|
210
|
+
(entry.schema_fit ?? 0.5);
|
|
262
211
|
}
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
212
|
+
else if (policy.policyType === 'recency-first') {
|
|
213
|
+
multiplier *= policy.weightRecency;
|
|
214
|
+
}
|
|
215
|
+
else if (policy.policyType === 'hybrid') {
|
|
216
|
+
multiplier *= policy.weightOutcome;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
// Hard cap AFTER all composition.
|
|
220
|
+
return Math.min(multiplier, MAX_FINAL_MULTIPLIER);
|
|
221
|
+
}
|
|
222
|
+
/** Log rows for the top `limit` boosted rows, one per matched goal. */
|
|
223
|
+
function buildGoalRecallLog(db, boosted, matchesByEntryId, goalsByTag, opts) {
|
|
224
|
+
const { sessionId, tenantId, limit } = opts;
|
|
274
225
|
// Filter to local memories only -- global memory IDs aren't in this DB's
|
|
275
226
|
// memories table, so the FK on goal_recall_log.memory_id would fail.
|
|
276
227
|
// dlPFC depth's outcome propagation is session-scoped to local; boost on
|
|
@@ -301,7 +252,68 @@ export function computeGoalStackBoost(db, results, opts) {
|
|
|
301
252
|
log.push({ goalId: goal.id, memoryId: r.entry.id, tenantId, sessionId, recalledAt, score: r.score });
|
|
302
253
|
}
|
|
303
254
|
}
|
|
304
|
-
return
|
|
255
|
+
return log;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* dlPFC goal-stack boost. Applies the multi-goal boost to entry-backed scored
|
|
259
|
+
* rows when (tenant, session) has active goals and returns the log rows to
|
|
260
|
+
* write, without writing them; {@link writeGoalRecallLog} persists them.
|
|
261
|
+
*
|
|
262
|
+
* Caller responsibilities:
|
|
263
|
+
* - Do NOT call when an explicit `goalTag` is set (caller's gate)
|
|
264
|
+
* - Pass entry-backed rows (with `entry.tags`, `entry.id`, optional
|
|
265
|
+
* `entry.schema_fit`)
|
|
266
|
+
* - Manage the db handle lifecycle (helper neither opens nor closes)
|
|
267
|
+
* - Recompute `tokens` after if returned rows are projected to a budgeted
|
|
268
|
+
* shape
|
|
269
|
+
*
|
|
270
|
+
* Log rows cover the top `limit` boosted rows that live in this store's
|
|
271
|
+
* `memories` table (global-only ids are skipped to keep the FK on
|
|
272
|
+
* goal_recall_log.memory_id valid).
|
|
273
|
+
*
|
|
274
|
+
* @internal Recall ranking helper. Subject to change.
|
|
275
|
+
*/
|
|
276
|
+
export function computeGoalStackBoost(db, results, opts) {
|
|
277
|
+
const { sessionId, tenantId, trace } = opts;
|
|
278
|
+
const active = getActiveGoalsWithDb(db, { sessionId, tenantId });
|
|
279
|
+
if (active.length === 0)
|
|
280
|
+
return { results, log: [] };
|
|
281
|
+
const goalsByTag = new Map(active.map((g) => [g.goalName, g]));
|
|
282
|
+
const policiesByGoalId = loadGoalPolicies(db, active);
|
|
283
|
+
// Goal-tag matches per boosted row, keyed by entry id. Kept as a side table
|
|
284
|
+
// (rather than a spread-on `_goalMatches` marker property) so `boosted`
|
|
285
|
+
// stays exactly R[] end to end, with no cast-tag-then-strip round trip.
|
|
286
|
+
const matchesByEntryId = new Map();
|
|
287
|
+
const boosted = results
|
|
288
|
+
.map((r) => {
|
|
289
|
+
const tags = r.entry.tags ?? [];
|
|
290
|
+
const matches = tags.filter((t) => goalsByTag.has(t));
|
|
291
|
+
if (matches.length === 0)
|
|
292
|
+
return r;
|
|
293
|
+
const multiplier = goalBoostMultiplier(r.entry, tags, matches, goalsByTag, policiesByGoalId);
|
|
294
|
+
// Recall-trace side-channel: record the goal-boost step BEFORE the
|
|
295
|
+
// score is mutated, keyed by entry id; a pure read of r.score.
|
|
296
|
+
if (trace) {
|
|
297
|
+
trace.set(r.entry.id, {
|
|
298
|
+
stage: 'goal-boost',
|
|
299
|
+
multiplier,
|
|
300
|
+
scoreBefore: r.score,
|
|
301
|
+
scoreAfter: r.score * multiplier,
|
|
302
|
+
note: matches.join(', '),
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
matchesByEntryId.set(r.entry.id, matches);
|
|
306
|
+
// SAFETY: spreading a generic-constrained `r: R` widens the result to
|
|
307
|
+
// the spread's plain object type; only `score` changes, so the value
|
|
308
|
+
// still satisfies R's shape exactly.
|
|
309
|
+
return { ...r, score: r.score * multiplier };
|
|
310
|
+
})
|
|
311
|
+
// Deliberately a PLAIN stable score sort, no compareEntryIdentity
|
|
312
|
+
// tail -- a re-sort of an already deterministically-ordered ranking
|
|
313
|
+
// inherits its determinism via sort stability, and ties preserve the
|
|
314
|
+
// prior (meaningful) rank instead of reordering by content.
|
|
315
|
+
.sort((a, b) => b.score - a.score);
|
|
316
|
+
return { results: boosted, log: buildGoalRecallLog(db, boosted, matchesByEntryId, goalsByTag, opts) };
|
|
305
317
|
}
|
|
306
318
|
/**
|
|
307
319
|
* Writes goal-boost log rows. INSERT OR IGNORE because UNIQUE(memory_id, goal_id)
|
package/dist/graph/read.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ export declare function loadRelations(hippoRoot: string, tenantId: string, opts?
|
|
|
16
16
|
limit?: number;
|
|
17
17
|
}, txDb?: GraphTxDb): Relation[];
|
|
18
18
|
/**
|
|
19
|
-
* Map consolidated source memory ids -> their graph entities. The SEED step of
|
|
19
|
+
* Map consolidated source memory ids -> their graph entities. The SEED step of
|
|
20
20
|
* multi-hop recall (recall result memory ids -> entities to traverse from). Tenant-
|
|
21
21
|
* scoped, read-only; chunks the IN-list under the SQLite variable cap.
|
|
22
22
|
*/
|
|
@@ -28,7 +28,7 @@ export declare function loadEntitiesByMemoryId(hippoRoot: string, tenantId: stri
|
|
|
28
28
|
export declare function loadEntitiesByIds(hippoRoot: string, tenantId: string, ids: number[], txDb?: GraphTxDb): Entity[];
|
|
29
29
|
/**
|
|
30
30
|
* All relations touching ANY of `entityIds` in EITHER direction (from OR to) — the
|
|
31
|
-
* per-hop neighbour query for
|
|
31
|
+
* per-hop neighbour query for multi-hop traversal. ONE query for the whole frontier
|
|
32
32
|
* (not one per node): this is the bidirectional read `loadRelations` (from-only) lacks,
|
|
33
33
|
* and avoids an N+1 across BFS frontier nodes. `limit` caps rows for the frontier and
|
|
34
34
|
* must be a non-negative integer (the raw `LIMIT ?` rejects a fractional value).
|
package/dist/graph/read.js
CHANGED
|
@@ -98,14 +98,14 @@ export function loadRelations(hippoRoot, tenantId, opts = {}, txDb) {
|
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
// ---------------------------------------------------------------------------
|
|
101
|
-
//
|
|
101
|
+
// Multi-hop recall read helpers (SELECT-only; the check-graph-writes lint
|
|
102
102
|
// permits these here and in the read-only consumer src/graph-recall.ts).
|
|
103
103
|
// ---------------------------------------------------------------------------
|
|
104
104
|
/** Chunk size for IN-list queries: well under SQLite's 999-bound-variable default
|
|
105
105
|
* (leaves headroom for the tenant_id param + the doubled list in neighbour lookups). */
|
|
106
106
|
const IN_LIST_CHUNK = 400;
|
|
107
107
|
/**
|
|
108
|
-
* Map consolidated source memory ids -> their graph entities. The SEED step of
|
|
108
|
+
* Map consolidated source memory ids -> their graph entities. The SEED step of
|
|
109
109
|
* multi-hop recall (recall result memory ids -> entities to traverse from). Tenant-
|
|
110
110
|
* scoped, read-only; chunks the IN-list under the SQLite variable cap.
|
|
111
111
|
*/
|
|
@@ -119,8 +119,7 @@ export function loadEntitiesByMemoryId(hippoRoot, tenantId, memoryIds) {
|
|
|
119
119
|
for (let i = 0; i < memoryIds.length; i += IN_LIST_CHUNK) {
|
|
120
120
|
const slice = memoryIds.slice(i, i + IN_LIST_CHUNK);
|
|
121
121
|
const ph = slice.map(() => '?').join(',');
|
|
122
|
-
//
|
|
123
|
-
// makes it deterministic (entities.id is an autoincrement integer PK).
|
|
122
|
+
// id ASC so chunk-local scan order never decides ties (id is an autoincrement PK).
|
|
124
123
|
// SAFETY: rows' shape matches the columns named in ENTITY_COLS above.
|
|
125
124
|
const rows = db.prepare(`
|
|
126
125
|
SELECT ${ENTITY_COLS} FROM entities
|
|
@@ -166,7 +165,7 @@ export function loadEntitiesByIds(hippoRoot, tenantId, ids, txDb) {
|
|
|
166
165
|
}
|
|
167
166
|
/**
|
|
168
167
|
* All relations touching ANY of `entityIds` in EITHER direction (from OR to) — the
|
|
169
|
-
* per-hop neighbour query for
|
|
168
|
+
* per-hop neighbour query for multi-hop traversal. ONE query for the whole frontier
|
|
170
169
|
* (not one per node): this is the bidirectional read `loadRelations` (from-only) lacks,
|
|
171
170
|
* and avoids an N+1 across BFS frontier nodes. `limit` caps rows for the frontier and
|
|
172
171
|
* must be a non-negative integer (the raw `LIMIT ?` rejects a fractional value).
|
|
@@ -183,7 +182,7 @@ export function loadNeighborRelations(hippoRoot, tenantId, entityIds, opts = {},
|
|
|
183
182
|
const db = txDb ?? ownDb;
|
|
184
183
|
try {
|
|
185
184
|
// `limit` is applied PER CHUNK; a frontier spanning >IN_LIST_CHUNK ids could return
|
|
186
|
-
// up to limit*chunks rows before the by-id dedup below. Harmless for
|
|
185
|
+
// up to limit*chunks rows before the by-id dedup below. Harmless for multi-hop recall (the
|
|
187
186
|
// frontier is bounded by maxNeighbors <= 200 << IN_LIST_CHUNK, so a single chunk,
|
|
188
187
|
// and the BFS re-enforces the per-hop fanout cap), but note the semantics if a
|
|
189
188
|
// tighter total cap is ever needed.
|
package/dist/graph/types.d.ts
CHANGED
|
@@ -8,10 +8,10 @@ export type RelationType = 'owns' | 'supersedes' | 'depends-on' | 'blocked-by' |
|
|
|
8
8
|
export type GraphQueueStatus = 'pending' | 'processed' | 'skipped';
|
|
9
9
|
/** The consolidated source kinds the graph is permitted to index (never 'raw'). */
|
|
10
10
|
export type SourceKind = 'distilled' | 'superseded';
|
|
11
|
-
/** The authoritative
|
|
11
|
+
/** The authoritative first-class object types a graph row may be anchored to (the object
|
|
12
12
|
* provenance path, alongside the memory path). Maps to source_object_type. */
|
|
13
13
|
export type SourceObjectType = 'decision' | 'policy' | 'customer' | 'project';
|
|
14
|
-
/** A soft (type,id) pointer to the authoritative
|
|
14
|
+
/** A soft (type,id) pointer to the authoritative object row a graph row descends from.
|
|
15
15
|
* Survives a mirror memory forget/prune (memory_id may go NULL); the rebuild
|
|
16
16
|
* re-validates it (it is not a hard FK). */
|
|
17
17
|
export interface SourceObjectRef {
|
|
@@ -32,8 +32,8 @@ export interface Entity {
|
|
|
32
32
|
* its source_object provenance. */
|
|
33
33
|
memoryId: string | null;
|
|
34
34
|
sourceKind: SourceKind;
|
|
35
|
-
/** The authoritative
|
|
36
|
-
* Set for
|
|
35
|
+
/** The authoritative object this entity is anchored to (object-provenance path).
|
|
36
|
+
* Set for object-sourced entities; absent for memory-only (prose/NLP) entities. */
|
|
37
37
|
sourceObjectType?: SourceObjectType;
|
|
38
38
|
sourceObjectId?: number;
|
|
39
39
|
createdAt: string;
|
|
@@ -64,9 +64,9 @@ export interface InsertEntityOpts {
|
|
|
64
64
|
entityType: EntityType;
|
|
65
65
|
name: string;
|
|
66
66
|
/** A consolidated (distilled/superseded) memory; raw is rejected. NULL/omitted when
|
|
67
|
-
* the entity is anchored only to its
|
|
67
|
+
* the entity is anchored only to its source object (mirror forgotten/pruned). */
|
|
68
68
|
memoryId?: string | null;
|
|
69
|
-
/** The authoritative
|
|
69
|
+
/** The authoritative object this entity descends from. Required when memoryId is
|
|
70
70
|
* null; optional alongside a live memory (both paths may be set). */
|
|
71
71
|
sourceObject?: SourceObjectRef;
|
|
72
72
|
}
|
|
@@ -75,9 +75,9 @@ export interface InsertRelationOpts {
|
|
|
75
75
|
toEntityId: number;
|
|
76
76
|
relType: RelationType;
|
|
77
77
|
/** A consolidated (distilled/superseded) memory; raw is rejected. NULL/omitted when
|
|
78
|
-
* the relation is anchored only to its
|
|
78
|
+
* the relation is anchored only to its source object. */
|
|
79
79
|
memoryId?: string | null;
|
|
80
|
-
/** The authoritative
|
|
80
|
+
/** The authoritative object this relation descends from. */
|
|
81
81
|
sourceObject?: SourceObjectRef;
|
|
82
82
|
}
|
|
83
83
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/graph/write.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* (docs/plans/2026-06-01-e3-graph-guard.md).
|
|
2
|
+
* Graph layer over consolidated state - the graph-on-consolidated guard.
|
|
4
3
|
*
|
|
5
4
|
* A graph of canonical `entities` (person/project/customer/system/policy/decision) and
|
|
6
5
|
* `relations` (owns/supersedes/depends-on/blocked-by/references) sits ON TOP OF
|
|
@@ -11,11 +10,6 @@
|
|
|
11
10
|
* actual kind and enforce tenant-match - relations also reject cross-tenant edges), so
|
|
12
11
|
* the forbidden state is unrepresentable regardless of code path. These helpers
|
|
13
12
|
* surface the same guard as clear throws BEFORE hitting the trigger backstop.
|
|
14
|
-
*
|
|
15
|
-
* Scope (E3.3 first slice): the substrate + the guard + a thin insert/load/enqueue
|
|
16
|
-
* API. The `graph_extraction_queue` is the interface the deferred `hippo sleep`
|
|
17
|
-
* enqueue-hook + E3.1 entity extraction will call. No operator surface (CLI/HTTP/SDK)
|
|
18
|
-
* until E3.2 multi-hop recall.
|
|
19
13
|
*/
|
|
20
14
|
import { type GraphTxDb, type SourceObjectType, type Entity, type Relation, type GraphQueueItem, type InsertEntityOpts, type InsertRelationOpts } from './types.js';
|
|
21
15
|
/**
|
|
@@ -30,8 +24,7 @@ export declare function insertEntity(hippoRoot: string, tenantId: string, opts:
|
|
|
30
24
|
export declare function insertRelation(hippoRoot: string, tenantId: string, opts: InsertRelationOpts, txDb?: GraphTxDb): Relation;
|
|
31
25
|
/**
|
|
32
26
|
* Enqueue a consolidated memory for later graph extraction. Rejects a raw / missing /
|
|
33
|
-
* cross-tenant memory (the DB trigger is the backstop).
|
|
34
|
-
* `hippo sleep` is deferred (E3.1); this is the API it will call.
|
|
27
|
+
* cross-tenant memory (the DB trigger is the backstop).
|
|
35
28
|
*/
|
|
36
29
|
export declare function enqueueExtraction(hippoRoot: string, tenantId: string, memoryId: string): GraphQueueItem;
|
|
37
30
|
/**
|
|
@@ -46,7 +39,7 @@ export declare function markExtractionProcessed(hippoRoot: string, tenantId: str
|
|
|
46
39
|
* the number of entities deleted. The rebuild primitive for graph extraction: the
|
|
47
40
|
* deterministic graph is a pure derived function of the consolidated objects, so an
|
|
48
41
|
* extract clears then re-derives. Lives in graph.ts (the sole sanctioned graph
|
|
49
|
-
* writer), so the
|
|
42
|
+
* writer), so the CI lint permits this `DELETE FROM entities`. Does NOT touch
|
|
50
43
|
* graph_extraction_queue (the enqueue-hook's domain).
|
|
51
44
|
*/
|
|
52
45
|
export declare function clearGraph(hippoRoot: string, tenantId: string, txDb?: GraphTxDb): number;
|
|
@@ -63,19 +56,19 @@ export declare function runGraphRebuildTransaction<T>(hippoRoot: string, tenantI
|
|
|
63
56
|
/**
|
|
64
57
|
* Fail-soft producer hook: mark a tenant dirty for graph re-extraction by
|
|
65
58
|
* enqueuing its consolidated mirror memory. NEVER throws into the caller — a
|
|
66
|
-
* graph-dirty signal failing must not abort a core
|
|
59
|
+
* graph-dirty signal failing must not abort a core object write. Graph staleness is
|
|
67
60
|
* recoverable (next sleep / manual `graph extract`); a broken `hippo decide` is
|
|
68
|
-
* not. Called POST-COMMIT from the
|
|
61
|
+
* not. Called POST-COMMIT from the graph-source save/close mutations of
|
|
69
62
|
* decision, policy, customer_note and project_brief. A null memoryId (a
|
|
70
63
|
* forgotten mirror) is a no-op.
|
|
71
64
|
*/
|
|
72
65
|
export declare function markGraphDirty(hippoRoot: string, tenantId: string, memoryId: string | null): void;
|
|
73
66
|
/**
|
|
74
|
-
* Remove the graph rows sourced from one
|
|
67
|
+
* Remove the graph rows sourced from one first-class object, by its (type, id). Used when a
|
|
75
68
|
* MIRRORLESS object is closed: it has no mirror memory, so `markGraphDirty` cannot
|
|
76
69
|
* enqueue a rebuild (the queue is memory-keyed). Closing must still drop the object's
|
|
77
70
|
* now-stale entity + edges from the graph, so we remove them directly here. Fail-soft
|
|
78
|
-
* like `markGraphDirty` (never throws into the
|
|
71
|
+
* like `markGraphDirty` (never throws into the object close caller; graph staleness is
|
|
79
72
|
* recoverable). Deleting the entity cascade-deletes any relation where it is an endpoint
|
|
80
73
|
* (relations FK entities ON DELETE CASCADE); the explicit relations DELETE also covers a
|
|
81
74
|
* relation whose OWN provenance is this object (defensive — every such edge has the object
|
package/dist/graph/write.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* (docs/plans/2026-06-01-e3-graph-guard.md).
|
|
2
|
+
* Graph layer over consolidated state - the graph-on-consolidated guard.
|
|
4
3
|
*
|
|
5
4
|
* A graph of canonical `entities` (person/project/customer/system/policy/decision) and
|
|
6
5
|
* `relations` (owns/supersedes/depends-on/blocked-by/references) sits ON TOP OF
|
|
@@ -11,11 +10,6 @@
|
|
|
11
10
|
* actual kind and enforce tenant-match - relations also reject cross-tenant edges), so
|
|
12
11
|
* the forbidden state is unrepresentable regardless of code path. These helpers
|
|
13
12
|
* surface the same guard as clear throws BEFORE hitting the trigger backstop.
|
|
14
|
-
*
|
|
15
|
-
* Scope (E3.3 first slice): the substrate + the guard + a thin insert/load/enqueue
|
|
16
|
-
* API. The `graph_extraction_queue` is the interface the deferred `hippo sleep`
|
|
17
|
-
* enqueue-hook + E3.1 entity extraction will call. No operator surface (CLI/HTTP/SDK)
|
|
18
|
-
* until E3.2 multi-hop recall.
|
|
19
13
|
*/
|
|
20
14
|
import { openHippoDb, closeHippoDb } from '../db.js';
|
|
21
15
|
import { assertTenantId } from '../tenant.js';
|
|
@@ -37,9 +31,9 @@ function resolveConsolidatedSource(db, tenantId, memoryId, sourceObject, label)
|
|
|
37
31
|
const row = db.prepare(`SELECT kind, tenant_id FROM memories WHERE id = ?`).get(memoryId);
|
|
38
32
|
if (!row) {
|
|
39
33
|
// Stale / forgotten mirror. Tolerate it IFF a valid source object provides provenance:
|
|
40
|
-
// graph-extract reads
|
|
41
|
-
// must NOT roll back the whole tenant rebuild - the active
|
|
42
|
-
//
|
|
34
|
+
// graph-extract reads object rows then inserts, and a mirror forgotten/pruned in that window
|
|
35
|
+
// must NOT roll back the whole tenant rebuild - the active object survives mirror loss.
|
|
36
|
+
// Anchor to the object; drop the dead memory pointer.
|
|
43
37
|
if (sourceObject == null) {
|
|
44
38
|
throw new Error(`${label}: source memory ${memoryId} not found`);
|
|
45
39
|
}
|
|
@@ -58,8 +52,8 @@ function resolveConsolidatedSource(db, tenantId, memoryId, sourceObject, label)
|
|
|
58
52
|
memKind = row.kind;
|
|
59
53
|
}
|
|
60
54
|
}
|
|
61
|
-
// Validate the object pointer WHENEVER it is provided
|
|
62
|
-
//
|
|
55
|
+
// Validate the object pointer WHENEVER it is provided, not only when memory is null:
|
|
56
|
+
// a dual-set row whose object is wrong/closed/cross-tenant would become
|
|
63
57
|
// the active provenance after ON DELETE SET NULL and could then block the memory delete.
|
|
64
58
|
if (sourceObject != null) {
|
|
65
59
|
const table = SOURCE_OBJECT_TABLE[sourceObject.type];
|
|
@@ -79,7 +73,7 @@ function resolveConsolidatedSource(db, tenantId, memoryId, sourceObject, label)
|
|
|
79
73
|
}
|
|
80
74
|
}
|
|
81
75
|
// source_kind is the memory's kind when a memory is present, else 'distilled' for an
|
|
82
|
-
// object-only row (
|
|
76
|
+
// object-only row (objects are consolidated by construction). All-null is rejected.
|
|
83
77
|
if (memKind != null)
|
|
84
78
|
return { sourceKind: memKind, memoryId: effectiveMemoryId };
|
|
85
79
|
if (sourceObject != null)
|
|
@@ -170,12 +164,11 @@ export function insertRelation(hippoRoot, tenantId, opts, txDb) {
|
|
|
170
164
|
}
|
|
171
165
|
}
|
|
172
166
|
// ---------------------------------------------------------------------------
|
|
173
|
-
// Extraction queue
|
|
167
|
+
// Extraction queue
|
|
174
168
|
// ---------------------------------------------------------------------------
|
|
175
169
|
/**
|
|
176
170
|
* Enqueue a consolidated memory for later graph extraction. Rejects a raw / missing /
|
|
177
|
-
* cross-tenant memory (the DB trigger is the backstop).
|
|
178
|
-
* `hippo sleep` is deferred (E3.1); this is the API it will call.
|
|
171
|
+
* cross-tenant memory (the DB trigger is the backstop).
|
|
179
172
|
*/
|
|
180
173
|
export function enqueueExtraction(hippoRoot, tenantId, memoryId) {
|
|
181
174
|
assertTenantId('enqueueExtraction', tenantId);
|
|
@@ -241,7 +234,7 @@ export function markExtractionProcessed(hippoRoot, tenantId, id, status = 'proce
|
|
|
241
234
|
* the number of entities deleted. The rebuild primitive for graph extraction: the
|
|
242
235
|
* deterministic graph is a pure derived function of the consolidated objects, so an
|
|
243
236
|
* extract clears then re-derives. Lives in graph.ts (the sole sanctioned graph
|
|
244
|
-
* writer), so the
|
|
237
|
+
* writer), so the CI lint permits this `DELETE FROM entities`. Does NOT touch
|
|
245
238
|
* graph_extraction_queue (the enqueue-hook's domain).
|
|
246
239
|
*/
|
|
247
240
|
export function clearGraph(hippoRoot, tenantId, txDb) {
|
|
@@ -292,14 +285,14 @@ export function runGraphRebuildTransaction(hippoRoot, tenantId, fn) {
|
|
|
292
285
|
}
|
|
293
286
|
}
|
|
294
287
|
// ---------------------------------------------------------------------------
|
|
295
|
-
//
|
|
288
|
+
// Sleep enqueue-hook: producer helper + drain support
|
|
296
289
|
// ---------------------------------------------------------------------------
|
|
297
290
|
/**
|
|
298
291
|
* Fail-soft producer hook: mark a tenant dirty for graph re-extraction by
|
|
299
292
|
* enqueuing its consolidated mirror memory. NEVER throws into the caller — a
|
|
300
|
-
* graph-dirty signal failing must not abort a core
|
|
293
|
+
* graph-dirty signal failing must not abort a core object write. Graph staleness is
|
|
301
294
|
* recoverable (next sleep / manual `graph extract`); a broken `hippo decide` is
|
|
302
|
-
* not. Called POST-COMMIT from the
|
|
295
|
+
* not. Called POST-COMMIT from the graph-source save/close mutations of
|
|
303
296
|
* decision, policy, customer_note and project_brief. A null memoryId (a
|
|
304
297
|
* forgotten mirror) is a no-op.
|
|
305
298
|
*/
|
|
@@ -311,16 +304,16 @@ export function markGraphDirty(hippoRoot, tenantId, memoryId) {
|
|
|
311
304
|
}
|
|
312
305
|
catch (err) {
|
|
313
306
|
// Logged (warn) so a SYSTEMATIC enqueue failure surfaces to operators, but
|
|
314
|
-
// swallowed so the already-committed
|
|
307
|
+
// swallowed so the already-committed object write is never rolled back.
|
|
315
308
|
log.warn(`markGraphDirty: enqueue failed for tenant=${tenantId} memory=${memoryId}: ${err instanceof Error ? err.message : String(err)}`);
|
|
316
309
|
}
|
|
317
310
|
}
|
|
318
311
|
/**
|
|
319
|
-
* Remove the graph rows sourced from one
|
|
312
|
+
* Remove the graph rows sourced from one first-class object, by its (type, id). Used when a
|
|
320
313
|
* MIRRORLESS object is closed: it has no mirror memory, so `markGraphDirty` cannot
|
|
321
314
|
* enqueue a rebuild (the queue is memory-keyed). Closing must still drop the object's
|
|
322
315
|
* now-stale entity + edges from the graph, so we remove them directly here. Fail-soft
|
|
323
|
-
* like `markGraphDirty` (never throws into the
|
|
316
|
+
* like `markGraphDirty` (never throws into the object close caller; graph staleness is
|
|
324
317
|
* recoverable). Deleting the entity cascade-deletes any relation where it is an endpoint
|
|
325
318
|
* (relations FK entities ON DELETE CASCADE); the explicit relations DELETE also covers a
|
|
326
319
|
* relation whose OWN provenance is this object (defensive — every such edge has the object
|
package/dist/graph-extract.d.ts
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* (docs/plans/2026-06-01-e3-deterministic-extraction.md).
|
|
2
|
+
* Deterministic entity extraction.
|
|
4
3
|
*
|
|
5
|
-
* Populates the
|
|
4
|
+
* Populates the graph from the already-structured consolidated first-class object tables -
|
|
6
5
|
* NO NLP, no precision gate for entities + supersedes. The graph is a pure derived
|
|
7
|
-
* function of the current
|
|
6
|
+
* function of the current object state, so `extractGraph` is an idempotent REBUILD: clear
|
|
8
7
|
* the tenant's graph, then re-derive entities + `supersedes` relations from decisions /
|
|
9
|
-
* policies / customer_notes / project_briefs (the four
|
|
8
|
+
* policies / customer_notes / project_briefs (the four object types whose kind maps to the
|
|
10
9
|
* `entity_type` enum). All writes go through the src/graph.ts consolidated-source guard
|
|
11
10
|
* (insertEntity / insertRelation / clearGraph); this module issues no raw SQL.
|
|
12
11
|
*
|
|
13
|
-
* Pass 3
|
|
12
|
+
* Pass 3 adds the
|
|
14
13
|
* first CROSS-OBJECT relations: a deterministic NAME-MATCH heuristic that emits a
|
|
15
14
|
* `references` edge when one consolidated object's text contains another entity's name.
|
|
16
15
|
* It is conservative (word-boundary, length-bounded, ambiguity-guarded, per-source
|
|
@@ -47,9 +46,9 @@ export interface ExtractResult {
|
|
|
47
46
|
truncated: string[];
|
|
48
47
|
}
|
|
49
48
|
/**
|
|
50
|
-
* Idempotent rebuild of the tenant's deterministic graph from its consolidated
|
|
49
|
+
* Idempotent rebuild of the tenant's deterministic graph from its consolidated first-class
|
|
51
50
|
* objects. Returns the entity/relation counts (+ which types were truncated at the
|
|
52
|
-
* per-type cap). Safe to re-run: output is a pure function of the current
|
|
51
|
+
* per-type cap). Safe to re-run: output is a pure function of the current object state.
|
|
53
52
|
*/
|
|
54
53
|
export declare function extractGraph(hippoRoot: string, tenantId: string): ExtractResult;
|
|
55
54
|
//# sourceMappingURL=graph-extract.d.ts.map
|