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/api/dormant.js
CHANGED
|
@@ -63,7 +63,7 @@ export function restoreDormant(ctx, id) {
|
|
|
63
63
|
writeEntryDbOnly(db, restored, { actor: ctx.actor.subject });
|
|
64
64
|
deleteDormantRow(db, ctx.tenantId, id);
|
|
65
65
|
// A restore is a labelled "forgot it, then needed it" event: the
|
|
66
|
-
// signal a learned lifecycle
|
|
66
|
+
// signal a learned lifecycle trains on. Same transaction
|
|
67
67
|
// as the restore, so the label exists exactly when the restore does.
|
|
68
68
|
appendAuditEvent(db, {
|
|
69
69
|
tenantId: ctx.tenantId,
|
package/dist/api/drill-down.d.ts
CHANGED
|
@@ -73,11 +73,11 @@ export type DrillDownOutcome = DrillDownResult | DrillDownFailure;
|
|
|
73
73
|
* if the underlying DAG accidentally linked across scopes.
|
|
74
74
|
*
|
|
75
75
|
* Returns a discriminated `DrillDownOutcome`: `DrillDownResult` on success,
|
|
76
|
-
* or `{failure: '...'}` for `not_found` (covers genuinely-missing
|
|
77
|
-
* tenant, intentionally indistinguishable)
|
|
78
|
-
*
|
|
76
|
+
* or `{failure: '...'}` for `not_found` (covers genuinely-missing, wrong-
|
|
77
|
+
* tenant and scope-blocked, intentionally indistinguishable) or
|
|
78
|
+
* `not_drillable` (id is a leaf row).
|
|
79
79
|
*
|
|
80
|
-
* Pre-v1.6.4 returned null for all
|
|
80
|
+
* Pre-v1.6.4 returned null for all three cases. JS callers migrate via
|
|
81
81
|
* `'failure' in result` checks; HTTP route maps `not_drillable` to 422.
|
|
82
82
|
*/
|
|
83
83
|
export declare function drillDown(ctx: Context, summaryId: string, opts?: DrillDownOpts): DrillDownOutcome;
|
package/dist/api/drill-down.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// DAG drill-down from a summary to its children.
|
|
2
|
-
import {
|
|
2
|
+
import { closeHippoDb } from '../db.js';
|
|
3
|
+
import { openStore } from '../store/open.js';
|
|
4
|
+
import { selectEntriesByIds, selectChildrenByParent } from '../store/entry-reads.js';
|
|
3
5
|
import { estimateTokens } from '../token-ledger.js';
|
|
4
6
|
import { passesScopeFilterForRecall } from '../recall-scope.js';
|
|
5
7
|
/**
|
|
@@ -14,11 +16,11 @@ import { passesScopeFilterForRecall } from '../recall-scope.js';
|
|
|
14
16
|
* if the underlying DAG accidentally linked across scopes.
|
|
15
17
|
*
|
|
16
18
|
* Returns a discriminated `DrillDownOutcome`: `DrillDownResult` on success,
|
|
17
|
-
* or `{failure: '...'}` for `not_found` (covers genuinely-missing
|
|
18
|
-
* tenant, intentionally indistinguishable)
|
|
19
|
-
*
|
|
19
|
+
* or `{failure: '...'}` for `not_found` (covers genuinely-missing, wrong-
|
|
20
|
+
* tenant and scope-blocked, intentionally indistinguishable) or
|
|
21
|
+
* `not_drillable` (id is a leaf row).
|
|
20
22
|
*
|
|
21
|
-
* Pre-v1.6.4 returned null for all
|
|
23
|
+
* Pre-v1.6.4 returned null for all three cases. JS callers migrate via
|
|
22
24
|
* `'failure' in result` checks; HTTP route maps `not_drillable` to 422.
|
|
23
25
|
*/
|
|
24
26
|
export function drillDown(ctx, summaryId, opts = {}) {
|
|
@@ -26,8 +28,17 @@ export function drillDown(ctx, summaryId, opts = {}) {
|
|
|
26
28
|
// v0.30 / E5: depth defaults 1 (backward compat); hard cap 10 levels
|
|
27
29
|
// prevents pathological deep trees. CLI/HTTP/MCP reject invalid values.
|
|
28
30
|
const depth = Math.max(1, Math.min(Math.trunc(opts.depth ?? 1), 10));
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
+
const db = openStore(ctx.hippoRoot);
|
|
32
|
+
try {
|
|
33
|
+
return drillDownOn(db, ctx, summaryId, depth, opts, limit);
|
|
34
|
+
}
|
|
35
|
+
finally {
|
|
36
|
+
closeHippoDb(db);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function drillDownOn(db, ctx, summaryId, depth, opts, limit) {
|
|
40
|
+
const summary = selectEntriesByIds(db, [summaryId], ctx.tenantId).get(summaryId) ?? null;
|
|
41
|
+
// No unscoped cross-tenant probe here: the tenant-scoped read's miss covers
|
|
31
42
|
// both "doesn't exist" and "exists in another tenant" by design.
|
|
32
43
|
// Distinguishing them via an unscoped lookup would leak existence to
|
|
33
44
|
// unauthorised tenants. The two cases collapse into not_found.
|
|
@@ -42,21 +53,46 @@ export function drillDown(ctx, summaryId, opts = {}) {
|
|
|
42
53
|
// already preventing. Match the HTTP behaviour at the API level.
|
|
43
54
|
return { failure: 'not_found' };
|
|
44
55
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
56
|
+
const { collected, level0DirectCount } = collectDescendants(db, ctx.tenantId, summaryId, depth);
|
|
57
|
+
const summaryOut = {
|
|
58
|
+
id: summary.id,
|
|
59
|
+
content: summary.content,
|
|
60
|
+
// v0.30 / E5: the STORED direct-child count; the legacy fallback counts
|
|
61
|
+
// level-0 children, never the BFS-depth-N total (independent-review MED #4).
|
|
62
|
+
descendantCount: summary.descendant_count ?? level0DirectCount,
|
|
63
|
+
earliestAt: summary.earliest_at ?? null,
|
|
64
|
+
latestAt: summary.latest_at ?? null,
|
|
65
|
+
};
|
|
66
|
+
const all = collected.map((c) => ({
|
|
67
|
+
id: c.id,
|
|
68
|
+
content: c.content,
|
|
69
|
+
layer: c.layer,
|
|
70
|
+
dagLevel: c.dag_level ?? 0,
|
|
71
|
+
created: c.created,
|
|
72
|
+
}));
|
|
73
|
+
const { children, truncated } = capChildren(all, summaryOut, opts, limit);
|
|
74
|
+
return {
|
|
75
|
+
summary: summaryOut,
|
|
76
|
+
children,
|
|
77
|
+
// v0.30 / E5: totalChildren = BFS-collected count (depth-aware). For
|
|
78
|
+
// depth=1 this equals the eligible direct-children count (backward
|
|
79
|
+
// compat). For depth>1 it is the cumulative count across levels.
|
|
80
|
+
totalChildren: collected.length,
|
|
81
|
+
truncated,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
// BFS with a visited set: dag_parent_id is not unique, so a misconfigured tree could emit a child twice past depth 1.
|
|
85
|
+
// The level-0 count is kept apart so a legacy summary's descendantCount fallback counts direct children only.
|
|
86
|
+
function collectDescendants(db, tenantId, summaryId, depth) {
|
|
49
87
|
const collected = [];
|
|
50
88
|
const visited = new Set([summaryId]);
|
|
51
89
|
let frontier = [summaryId];
|
|
52
|
-
// independent-review MED #4 fold: track level-0 direct-children count
|
|
53
|
-
// separately so the descendantCount fallback (for legacy summaries with
|
|
54
|
-
// null descendant_count) reflects DIRECT children, not BFS-collected total.
|
|
55
90
|
let level0DirectCount = 0;
|
|
56
91
|
for (let level = 0; level < depth; level++) {
|
|
57
92
|
const nextFrontier = [];
|
|
93
|
+
const kidsByParent = selectChildrenByParent(db, frontier, tenantId);
|
|
58
94
|
for (const parentId of frontier) {
|
|
59
|
-
const kids =
|
|
95
|
+
const kids = kidsByParent.get(parentId) ?? [];
|
|
60
96
|
const eligibleKids = kids.filter((c) => passesScopeFilterForRecall(c.scope ?? null, undefined));
|
|
61
97
|
for (const k of eligibleKids) {
|
|
62
98
|
if (visited.has(k.id))
|
|
@@ -72,23 +108,10 @@ export function drillDown(ctx, summaryId, opts = {}) {
|
|
|
72
108
|
break;
|
|
73
109
|
frontier = nextFrontier;
|
|
74
110
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
// level-0 children, never the BFS-depth-N total (independent-review MED #4).
|
|
80
|
-
descendantCount: summary.descendant_count ?? level0DirectCount,
|
|
81
|
-
earliestAt: summary.earliest_at ?? null,
|
|
82
|
-
latestAt: summary.latest_at ?? null,
|
|
83
|
-
};
|
|
84
|
-
const all = collected.map((c) => ({
|
|
85
|
-
id: c.id,
|
|
86
|
-
content: c.content,
|
|
87
|
-
layer: c.layer,
|
|
88
|
-
dagLevel: c.dag_level ?? 0,
|
|
89
|
-
created: c.created,
|
|
90
|
-
}));
|
|
91
|
-
// Apply global cumulative token budget + limit cap on collected.
|
|
111
|
+
return { collected, level0DirectCount };
|
|
112
|
+
}
|
|
113
|
+
/** Global cumulative token budget first, then the `limit` cap. */
|
|
114
|
+
function capChildren(all, summaryOut, opts, limit) {
|
|
92
115
|
let children = all;
|
|
93
116
|
let truncated = false;
|
|
94
117
|
if (opts.budget !== undefined) {
|
|
@@ -110,14 +133,6 @@ export function drillDown(ctx, summaryId, opts = {}) {
|
|
|
110
133
|
children = children.slice(0, limit);
|
|
111
134
|
truncated = true;
|
|
112
135
|
}
|
|
113
|
-
return {
|
|
114
|
-
summary: summaryOut,
|
|
115
|
-
children,
|
|
116
|
-
// v0.30 / E5: totalChildren = BFS-collected count (depth-aware). For
|
|
117
|
-
// depth=1 this equals the eligible direct-children count (backward
|
|
118
|
-
// compat). For depth>1 it is the cumulative count across levels.
|
|
119
|
-
totalChildren: collected.length,
|
|
120
|
-
truncated,
|
|
121
|
-
};
|
|
136
|
+
return { children, truncated };
|
|
122
137
|
}
|
|
123
138
|
//# sourceMappingURL=drill-down.js.map
|
package/dist/api/outcome.d.ts
CHANGED
|
@@ -8,14 +8,13 @@ import type { Context } from './types.js';
|
|
|
8
8
|
* op='outcome' tagged with ctx.actor.subject.
|
|
9
9
|
*
|
|
10
10
|
* Returns `{applied, appliedIds}`. `appliedIds` is the tenant-filtered subset
|
|
11
|
-
* of input ids that actually had `applyOutcome` run on them (i.e. ids
|
|
12
|
-
*
|
|
11
|
+
* of input ids that actually had `applyOutcome` run on them (i.e. ids found
|
|
12
|
+
* in ctx.tenantId). Callers that surface the id list
|
|
13
13
|
* over a multi-tenant boundary (HTTP /v1/outcome last-recall path, Python SDK)
|
|
14
14
|
* MUST return `appliedIds` instead of the raw input list — otherwise the
|
|
15
|
-
* non-applied (cross-tenant) ids leak to the caller.
|
|
16
|
-
* close that disclosure path on POST /v1/outcome.
|
|
15
|
+
* non-applied (cross-tenant) ids leak to the caller.
|
|
17
16
|
*
|
|
18
|
-
* `opts.traceId
|
|
17
|
+
* `opts.traceId`:
|
|
19
18
|
* OPTIONAL additive opt so a programmatic caller can link this outcome to
|
|
20
19
|
* the recall_traces row it judges. NOT applied unconditionally — an SDK
|
|
21
20
|
* caller passing explicit ids with no preceding CLI/context recall would
|
|
@@ -36,18 +35,14 @@ export declare function outcome(ctx: Context, ids: ReadonlyArray<string>, good:
|
|
|
36
35
|
*
|
|
37
36
|
* Reads `loadIndex(ctx.hippoRoot).last_retrieval_ids` (per-hippoRoot local
|
|
38
37
|
* state; not tenant-scoped at the index layer) and forwards to `outcome()`,
|
|
39
|
-
* which DOES tenant-filter
|
|
38
|
+
* which DOES tenant-filter its read by `ctx.tenantId`. Cross-tenant
|
|
40
39
|
* ids in `last_retrieval_ids` are silently skipped, matching the MCP
|
|
41
40
|
* `hippo_outcome` semantics.
|
|
42
41
|
*
|
|
43
|
-
* **Tenant-safe response shape
|
|
42
|
+
* **Tenant-safe response shape:** the returned `ids`
|
|
44
43
|
* field contains ONLY the tenant-filtered subset that actually had outcomes
|
|
45
|
-
* applied (i.e. `appliedIds` from the inner `outcome()` call).
|
|
46
|
-
*
|
|
47
|
-
* leaked cross-tenant memory IDs to the caller via POST /v1/outcome's
|
|
48
|
-
* no-body last-recall response. The fix is at this helper so all callers
|
|
49
|
-
* (CLI cmdOutcome, HTTP /v1/outcome, MCP `hippo_outcome` if added later)
|
|
50
|
-
* inherit the tenant-safe contract.
|
|
44
|
+
* applied (i.e. `appliedIds` from the inner `outcome()` call). It lives in this
|
|
45
|
+
* helper so every caller (CLI, HTTP /v1/outcome, MCP) inherits the contract.
|
|
51
46
|
*
|
|
52
47
|
* Do NOT tighten `loadIndex` with `tenantId` inside this helper — doing so
|
|
53
48
|
* would break the (correct) cross-tenant-silent-skip behavior covered by
|
package/dist/api/outcome.js
CHANGED
|
@@ -1,24 +1,27 @@
|
|
|
1
1
|
// Outcome feedback on recalled memories.
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { closeHippoDb } from '../db.js';
|
|
3
|
+
import { openStore } from '../store/open.js';
|
|
4
|
+
import { writeEntryOn } from '../store/entry-writes.js';
|
|
5
|
+
import { selectEntriesByIds } from '../store/entry-reads.js';
|
|
5
6
|
import { loadIndex } from '../store/index-and-stats.js';
|
|
6
7
|
import { applyOutcome, CHURN_STALE_TAG } from '../memory.js';
|
|
7
8
|
import { appendAuditEvent } from '../audit.js';
|
|
8
9
|
import { recordTraceOutcome } from '../recall-trace.js';
|
|
9
10
|
export function outcome(ctx, ids, good, opts) {
|
|
10
11
|
const appliedIds = [];
|
|
11
|
-
const db =
|
|
12
|
+
const db = openStore(ctx.hippoRoot);
|
|
12
13
|
try {
|
|
14
|
+
const live = selectEntriesByIds(db, ids, ctx.tenantId);
|
|
13
15
|
for (const id of ids) {
|
|
14
|
-
const entry =
|
|
16
|
+
const entry = live.get(id);
|
|
15
17
|
if (!entry)
|
|
16
18
|
continue;
|
|
17
19
|
let updated = applyOutcome(entry, good);
|
|
18
|
-
if (good && updated.tags.includes(CHURN_STALE_TAG)) { //
|
|
20
|
+
if (good && updated.tags.includes(CHURN_STALE_TAG)) { // a good outcome reconfirms the entry
|
|
19
21
|
updated = { ...updated, tags: updated.tags.filter((t) => t !== CHURN_STALE_TAG) };
|
|
20
22
|
}
|
|
21
|
-
|
|
23
|
+
writeEntryOn(db, ctx.hippoRoot, updated, { actor: ctx.actor.subject });
|
|
24
|
+
live.set(id, updated); // a repeated id builds on its first outcome, as a fresh read would
|
|
22
25
|
appendAuditEvent(db, {
|
|
23
26
|
tenantId: ctx.tenantId,
|
|
24
27
|
actor: ctx.actor.subject,
|
|
@@ -28,7 +31,7 @@ export function outcome(ctx, ids, good, opts) {
|
|
|
28
31
|
});
|
|
29
32
|
appliedIds.push(id);
|
|
30
33
|
}
|
|
31
|
-
//
|
|
34
|
+
// Link the outcome to its trace, recording only the ids actually
|
|
32
35
|
// credited (post tenant-filtering, matches appliedIds). Lives in its own
|
|
33
36
|
// append-only table so audit_log pruning can never erase training data.
|
|
34
37
|
if (opts?.traceId !== undefined && appliedIds.length > 0) {
|
|
@@ -50,15 +53,8 @@ export function outcomeForLastRecall(ctx, good) {
|
|
|
50
53
|
const ids = idx.last_retrieval_ids;
|
|
51
54
|
if (ids.length === 0)
|
|
52
55
|
return { applied: 0, ids: [] };
|
|
53
|
-
//
|
|
54
|
-
//
|
|
55
|
-
// (idx.last_trace_id) — a single-snapshot read, not a second DB round
|
|
56
|
-
// trip via a now-deleted readLastTraceId helper. The value is already
|
|
57
|
-
// strict-parsed by buildIndexFromDb's parseLastTraceId (store.ts): every
|
|
58
|
-
// consumer gets a clean positive-integer string or null, never a garbage
|
|
59
|
-
// value that could reach outcome() and INSERT trace_id=0/NaN. null on a
|
|
60
|
-
// fresh store / pre-v40 flow / api.recall-only usage — outcome() skips
|
|
61
|
-
// linkage silently when traceId is undefined.
|
|
56
|
+
// Same `loadIndex` snapshot as the ids; buildIndexFromDb already strict-parses it to a
|
|
57
|
+
// positive-integer string or null, so no trace_id=0/NaN reaches outcome().
|
|
62
58
|
const traceId = idx.last_trace_id !== null ? Number(idx.last_trace_id) : null;
|
|
63
59
|
const { applied, appliedIds } = outcome(ctx, ids, good, traceId !== null ? { traceId } : undefined);
|
|
64
60
|
return { applied, ids: appliedIds };
|
package/dist/api/promote.d.ts
CHANGED
|
@@ -8,8 +8,7 @@ import type { Context } from './types.js';
|
|
|
8
8
|
*
|
|
9
9
|
* Note: `promoteToGlobal` does not currently take a tenantId override — it
|
|
10
10
|
* reads the entry from the local root via `readEntry` (no tenant filter) and
|
|
11
|
-
* preserves the entry's existing tenantId on the global side.
|
|
12
|
-
* tighten this once writeEntry/readEntry thread tenant context.
|
|
11
|
+
* preserves the entry's existing tenantId on the global side.
|
|
13
12
|
*/
|
|
14
13
|
export interface PromoteResult {
|
|
15
14
|
ok: true;
|
|
@@ -19,9 +18,8 @@ export interface PromoteResult {
|
|
|
19
18
|
export declare function promote(ctx: Context, id: string): PromoteResult;
|
|
20
19
|
/**
|
|
21
20
|
* Replace an old memory with new content, chaining old.superseded_by = new.id.
|
|
22
|
-
* Mirrors `cmdSupersede` in cli.ts
|
|
23
|
-
*
|
|
24
|
-
* flags and pass the resolved values once Task 4 lands).
|
|
21
|
+
* Mirrors `cmdSupersede` in cli.ts minus the flag-driven layer/tag/pin
|
|
22
|
+
* overrides: the CLI handler resolves those so the API stays minimal.
|
|
25
23
|
*/
|
|
26
24
|
export interface SupersedeResult {
|
|
27
25
|
ok: true;
|
|
@@ -34,13 +32,12 @@ export declare function supersede(ctx: Context, oldId: string, newContent: strin
|
|
|
34
32
|
*
|
|
35
33
|
* `archiveRawMemory` audits the operation internally (op='archive_raw') using the
|
|
36
34
|
* row's own tenant_id. We DO NOT emit a second audit event here to avoid double-
|
|
37
|
-
* emitting the archive_raw op
|
|
38
|
-
* helpers hardcode actor='cli'). Instead we pass `ctx.actor.subject` through as `who`,
|
|
35
|
+
* emitting the archive_raw op. Instead we pass `ctx.actor.subject` through as `who`,
|
|
39
36
|
* and raw-archive.ts uses that for the audit row.
|
|
40
37
|
*/
|
|
41
38
|
export interface ArchiveRawOpts {
|
|
42
39
|
/**
|
|
43
|
-
* Connector idempotency hook
|
|
40
|
+
* Connector idempotency hook. Runs inside the same
|
|
44
41
|
* SAVEPOINT as the archive — throwing rolls the archive back. Used by the
|
|
45
42
|
* Slack deletion connector to mark the deletion event seen atomically.
|
|
46
43
|
*/
|
package/dist/api/promote.js
CHANGED
|
@@ -47,19 +47,7 @@ export function promote(ctx, id) {
|
|
|
47
47
|
return { ok: true, sourceId: id, globalId: globalEntry.id };
|
|
48
48
|
}
|
|
49
49
|
export function supersede(ctx, oldId, newContent) {
|
|
50
|
-
|
|
51
|
-
// tenant A on tenant B's id throws "Memory not found" here without any
|
|
52
|
-
// info leak.
|
|
53
|
-
const old = readEntry(ctx.hippoRoot, oldId, ctx.tenantId);
|
|
54
|
-
if (!old) {
|
|
55
|
-
throw new NotFoundError(`Memory not found: ${oldId}`);
|
|
56
|
-
}
|
|
57
|
-
// Guard: not already superseded. The CAS UPDATE below race-safely closes
|
|
58
|
-
// the window between this read and the write; this check just produces a
|
|
59
|
-
// clearer error in the common single-writer case.
|
|
60
|
-
if (old.superseded_by) {
|
|
61
|
-
throw new ConflictError(`Memory ${oldId} is already superseded by ${old.superseded_by}. Supersede that one instead.`);
|
|
62
|
-
}
|
|
50
|
+
const old = readSupersedable(ctx, oldId);
|
|
63
51
|
const newEntry = createSuccessor(old, newContent, {
|
|
64
52
|
tenantId: ctx.tenantId,
|
|
65
53
|
baseHalfLifeDays: loadConfig(ctx.hippoRoot).defaultHalfLifeDays,
|
|
@@ -72,59 +60,7 @@ export function supersede(ctx, oldId, newContent) {
|
|
|
72
60
|
// the old.superseded_by pointer.
|
|
73
61
|
const db = openHippoDb(ctx.hippoRoot);
|
|
74
62
|
try {
|
|
75
|
-
db
|
|
76
|
-
try {
|
|
77
|
-
// 1. CAS update: only succeed if old.superseded_by IS NULL AND the
|
|
78
|
-
// row still belongs to ctx.tenantId. Tenant filter is belt-and-
|
|
79
|
-
// braces with the readEntry above — it costs nothing and closes
|
|
80
|
-
// a hypothetical window where ownership changes between read and
|
|
81
|
-
// update.
|
|
82
|
-
const result = db.prepare(`
|
|
83
|
-
UPDATE memories
|
|
84
|
-
SET superseded_by = ?
|
|
85
|
-
WHERE id = ? AND tenant_id = ? AND superseded_by IS NULL
|
|
86
|
-
`).run(newEntry.id, oldId, ctx.tenantId);
|
|
87
|
-
if ((result.changes ?? 0) === 0) {
|
|
88
|
-
db.exec('ROLLBACK');
|
|
89
|
-
throw new ConflictError(`Memory ${oldId} already superseded by another writer`);
|
|
90
|
-
}
|
|
91
|
-
// v0.30 / E2 — DAG live-coupling: OLD entry just transitioned to
|
|
92
|
-
// superseded. Its parent (if any) needs rebuild. Lands strictly
|
|
93
|
-
// between the rollback guard above and the writeEntryDbOnly(NEW)
|
|
94
|
-
// below so a failed CAS hits throw before this hook. The NEW
|
|
95
|
-
// entry's parent (typically same parent) is auto-marked by the
|
|
96
|
-
// writeEntryDbOnly hook (same parent → idempotent, audits once).
|
|
97
|
-
if (old.dag_parent_id) {
|
|
98
|
-
markSummaryDirtyInTx(db, old.dag_parent_id, ctx.tenantId, ctx.actor.subject);
|
|
99
|
-
}
|
|
100
|
-
// 2. Write new memory inside same tx via writeEntryDbOnly (DB-only
|
|
101
|
-
// path). This emits its OWN 'remember' audit row for the new
|
|
102
|
-
// memory inside the SAVEPOINT — atomic with the row INSERT.
|
|
103
|
-
writeEntryDbOnly(db, stampOriginProject(ctx.hippoRoot, newEntry), { actor: ctx.actor.subject });
|
|
104
|
-
// 3. User-facing 'supersede' audit row inside the same tx so the
|
|
105
|
-
// chain pointer + audit trail commit atomically.
|
|
106
|
-
appendAuditEvent(db, {
|
|
107
|
-
tenantId: ctx.tenantId,
|
|
108
|
-
actor: ctx.actor.subject,
|
|
109
|
-
op: 'supersede',
|
|
110
|
-
targetId: oldId,
|
|
111
|
-
metadata: { newId: newEntry.id },
|
|
112
|
-
});
|
|
113
|
-
db.exec('COMMIT');
|
|
114
|
-
}
|
|
115
|
-
catch (err) {
|
|
116
|
-
try {
|
|
117
|
-
db.exec('ROLLBACK');
|
|
118
|
-
}
|
|
119
|
-
catch { /* already rolled back */ }
|
|
120
|
-
// AT1 (plan §3): refusal audit lands post-ROLLBACK, in a fresh
|
|
121
|
-
// implicit transaction the aborted outer one cannot claw back — then
|
|
122
|
-
// rethrow so the caller sees the refusal.
|
|
123
|
-
if (err instanceof RejectedValueError) {
|
|
124
|
-
auditRejectionRefusal(db, err, ctx.actor.subject);
|
|
125
|
-
}
|
|
126
|
-
throw err;
|
|
127
|
-
}
|
|
63
|
+
commitSupersede(db, ctx, oldId, old, newEntry);
|
|
128
64
|
// Mirrors after COMMIT, while the db handle is still open. Same
|
|
129
65
|
// invariant as the original writeEntry: a mirror failure leaves disk
|
|
130
66
|
// MISSING the markdown for the new memory (rebuildIndex rewrites every
|
|
@@ -142,6 +78,59 @@ export function supersede(ctx, oldId, newContent) {
|
|
|
142
78
|
}
|
|
143
79
|
return { ok: true, oldId, newId: newEntry.id };
|
|
144
80
|
}
|
|
81
|
+
/** The tenant-scoped row to supersede; readEntry's tenant filter makes another tenant's id read as not found. */
|
|
82
|
+
function readSupersedable(ctx, oldId) {
|
|
83
|
+
const old = readEntry(ctx.hippoRoot, oldId, ctx.tenantId);
|
|
84
|
+
if (!old) {
|
|
85
|
+
throw new NotFoundError(`Memory not found: ${oldId}`);
|
|
86
|
+
}
|
|
87
|
+
// The CAS UPDATE closes the race; this check only gives a clearer error in the common single-writer case.
|
|
88
|
+
if (old.superseded_by) {
|
|
89
|
+
throw new ConflictError(`Memory ${oldId} is already superseded by ${old.superseded_by}. Supersede that one instead.`);
|
|
90
|
+
}
|
|
91
|
+
return old;
|
|
92
|
+
}
|
|
93
|
+
/** CAS on the old row, the successor's insert and the 'supersede' audit row, in one BEGIN IMMEDIATE transaction. */
|
|
94
|
+
function commitSupersede(db, ctx, oldId, old, newEntry) {
|
|
95
|
+
db.exec('BEGIN IMMEDIATE');
|
|
96
|
+
try {
|
|
97
|
+
// The tenant filter repeats readEntry's check at no cost, closing an ownership change between read and update.
|
|
98
|
+
const result = db.prepare(`
|
|
99
|
+
UPDATE memories
|
|
100
|
+
SET superseded_by = ?
|
|
101
|
+
WHERE id = ? AND tenant_id = ? AND superseded_by IS NULL
|
|
102
|
+
`).run(newEntry.id, oldId, ctx.tenantId);
|
|
103
|
+
if ((result.changes ?? 0) === 0) {
|
|
104
|
+
db.exec('ROLLBACK');
|
|
105
|
+
throw new ConflictError(`Memory ${oldId} already superseded by another writer`);
|
|
106
|
+
}
|
|
107
|
+
// After the CAS guard, so a lost race throws before the old parent is marked for rebuild.
|
|
108
|
+
if (old.dag_parent_id) {
|
|
109
|
+
markSummaryDirtyInTx(db, old.dag_parent_id, ctx.tenantId, ctx.actor.subject);
|
|
110
|
+
}
|
|
111
|
+
// Emits its own 'remember' audit row inside the same transaction.
|
|
112
|
+
writeEntryDbOnly(db, stampOriginProject(ctx.hippoRoot, newEntry), { actor: ctx.actor.subject });
|
|
113
|
+
appendAuditEvent(db, {
|
|
114
|
+
tenantId: ctx.tenantId,
|
|
115
|
+
actor: ctx.actor.subject,
|
|
116
|
+
op: 'supersede',
|
|
117
|
+
targetId: oldId,
|
|
118
|
+
metadata: { newId: newEntry.id },
|
|
119
|
+
});
|
|
120
|
+
db.exec('COMMIT');
|
|
121
|
+
}
|
|
122
|
+
catch (err) {
|
|
123
|
+
try {
|
|
124
|
+
db.exec('ROLLBACK');
|
|
125
|
+
}
|
|
126
|
+
catch { /* already rolled back */ }
|
|
127
|
+
// The refusal audit lands after ROLLBACK, in a fresh implicit transaction the aborted one cannot undo.
|
|
128
|
+
if (err instanceof RejectedValueError) {
|
|
129
|
+
auditRejectionRefusal(db, err, ctx.actor.subject);
|
|
130
|
+
}
|
|
131
|
+
throw err;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
145
134
|
export function archiveRaw(ctx, id, reason, opts = {}) {
|
|
146
135
|
const db = openHippoDb(ctx.hippoRoot);
|
|
147
136
|
let mirrorOk = false;
|
package/dist/api/quarantine.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { openHippoDb, closeHippoDb } from '../db.js';
|
|
3
3
|
import { ConflictError, ForbiddenError, NotFoundError } from '../api-errors.js';
|
|
4
4
|
import { writeEntryMirrors } from '../store/entry-writes.js';
|
|
5
|
-
import { readEntry } from '../store/entry-reads.js';
|
|
5
|
+
import { readEntry, selectEntriesByIds } from '../store/entry-reads.js';
|
|
6
6
|
import { quarantineScopeFor, getQuarantineRow, listQuarantineRows, approveQuarantineRow, rejectQuarantineRow, } from '../quarantine.js';
|
|
7
7
|
import { log } from '../log.js';
|
|
8
8
|
import { appendAuditEvent } from '../audit.js';
|
|
@@ -12,8 +12,9 @@ export function quarantineList(ctx, opts = {}) {
|
|
|
12
12
|
const db = openHippoDb(ctx.hippoRoot);
|
|
13
13
|
try {
|
|
14
14
|
const rows = listQuarantineRows(db, ctx.tenantId, opts.status ?? 'pending', opts.limit, opts.after);
|
|
15
|
+
const entries = selectEntriesByIds(db, rows.map((row) => row.memoryId), ctx.tenantId);
|
|
15
16
|
return rows.map((row) => {
|
|
16
|
-
const entry =
|
|
17
|
+
const entry = entries.get(row.memoryId);
|
|
17
18
|
return {
|
|
18
19
|
id: row.memoryId,
|
|
19
20
|
originalScope: row.originalScope,
|