hippo-memory 1.62.0 → 1.63.1
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 +11 -2
- package/dist/agent-memories/apply.d.ts +2 -0
- package/dist/agent-memories/apply.js +23 -1
- package/dist/agent-memories/claude-code.d.ts +2 -1
- package/dist/agent-memories/claude-code.js +4 -4
- package/dist/agent-memories/report.d.ts +1 -0
- package/dist/agent-memories/report.js +2 -2
- package/dist/agent-memories/sync.js +37 -23
- 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 +7 -12
- 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 +4 -3
- package/dist/api/context-select.js +9 -11
- package/dist/api/context-types.d.ts +12 -17
- package/dist/api/context.d.ts +3 -2
- package/dist/api/context.js +20 -20
- package/dist/api/dormant.js +1 -1
- package/dist/api/drill-down.d.ts +4 -4
- package/dist/api/drill-down.js +22 -10
- 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/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 +7 -18
- package/dist/api/remember.d.ts +2 -2
- package/dist/api/sleep.d.ts +7 -35
- package/dist/api/sleep.js +3 -2
- 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 +1 -1
- package/dist/capture/compact.js +9 -13
- 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/curate.d.ts +1 -1
- package/dist/cli/curate.js +8 -24
- package/dist/cli/dag.js +5 -9
- package/dist/cli/decisions.js +10 -21
- package/dist/cli/explain.js +2 -1
- package/dist/cli/goals.js +1 -1
- package/dist/cli/init.js +1 -1
- package/dist/cli/playbooks.js +4 -9
- package/dist/cli/projects.js +3 -1
- package/dist/cli/recall.js +2 -1
- package/dist/cli/remember.js +6 -18
- package/dist/cli/session-hooks.js +21 -40
- package/dist/cli/setup.d.ts +1 -1
- package/dist/cli/setup.js +2 -6
- 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 +7 -12
- package/dist/cli/transfer.js +8 -13
- package/dist/cli/usage.d.ts +1 -1
- package/dist/cli/usage.js +10 -7
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +4 -9
- package/dist/compaction-record.d.ts +2 -0
- 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 +3 -8
- package/dist/consolidate/run.js +21 -26
- package/dist/consolidate/sleep.js +7 -18
- package/dist/consolidate/traces.js +9 -21
- package/dist/customer-notes.d.ts +5 -7
- package/dist/customer-notes.js +6 -9
- package/dist/dag.d.ts +10 -21
- package/dist/dag.js +23 -73
- package/dist/db/continuity.d.ts +5 -0
- package/dist/db/continuity.js +43 -25
- package/dist/db/meta.d.ts +1 -0
- package/dist/db/meta.js +4 -3
- package/dist/db/migrate.d.ts +9 -0
- package/dist/db/migrate.js +110 -52
- 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/db.d.ts +1 -1
- package/dist/db.js +1 -1
- package/dist/decisions.d.ts +2 -2
- package/dist/decisions.js +6 -6
- package/dist/dedupe.js +86 -61
- package/dist/delivery-recorder.js +154 -135
- package/dist/doctor.js +140 -105
- 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 +5 -6
- 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 +1 -1
- 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 +12 -14
- 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 +3 -3
- package/dist/project-briefs.d.ts +2 -3
- package/dist/project-briefs.js +9 -13
- package/dist/project-identity.d.ts +22 -9
- package/dist/project-identity.js +47 -12
- package/dist/project-merge.d.ts +17 -2
- package/dist/project-merge.js +109 -27
- 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 +60 -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 +17 -17
- package/dist/shared.js +93 -97
- package/dist/skills.d.ts +3 -3
- package/dist/skills.js +8 -8
- 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 +11 -36
- 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 +31 -59
- 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/README.md +3 -1
- package/extensions/openclaw-plugin/openclaw.plugin.json +2 -2
- package/extensions/openclaw-plugin/package.json +3 -3
- package/openclaw.plugin.json +2 -2
- package/package.json +2 -2
package/dist/dedupe.js
CHANGED
|
@@ -22,7 +22,9 @@
|
|
|
22
22
|
*/
|
|
23
23
|
import { textOverlap } from './tokenize.js';
|
|
24
24
|
import { loadAllEntries } from './store/entry-reads.js';
|
|
25
|
-
import {
|
|
25
|
+
import { deleteEntryOn, memoriesBackingObjects } from './store/delete-and-batch.js';
|
|
26
|
+
import { openStore } from './store/open.js';
|
|
27
|
+
import { closeHippoDb } from './db.js';
|
|
26
28
|
import { compareEntryIdentity } from './compare.js';
|
|
27
29
|
import { canAutoDelete } from './memory.js';
|
|
28
30
|
import { derivationPartitionKey } from './recall-scope.js';
|
|
@@ -56,6 +58,71 @@ export function strengthBucket(strength) {
|
|
|
56
58
|
const s = strength ?? 0;
|
|
57
59
|
return Number.isFinite(s) ? Math.round(s / STRENGTH_TIE_EPSILON) : 0;
|
|
58
60
|
}
|
|
61
|
+
// finiteCount mirrors strengthBucket's non-finite hardening on the
|
|
62
|
+
// retrieval leg: a NaN retrieval_count would make the comparator return
|
|
63
|
+
// NaN and break the total order the same way a NaN bucket would.
|
|
64
|
+
// Unreachable via the schema (non-nullable INTEGER column), so this is
|
|
65
|
+
// symmetry, not a live bug.
|
|
66
|
+
const finiteCount = (n) => Number.isFinite(n ?? 0) ? (n ?? 0) : 0;
|
|
67
|
+
/** The v1.26.3 survivor total order: strength bucket desc, then retrieval_count desc, then compareEntryIdentity. */
|
|
68
|
+
function survivorOrder(a, b) {
|
|
69
|
+
const bucketDiff = strengthBucket(b.strength) - strengthBucket(a.strength);
|
|
70
|
+
if (bucketDiff !== 0)
|
|
71
|
+
return bucketDiff;
|
|
72
|
+
const retrievalDiff = finiteCount(b.retrieval_count) - finiteCount(a.retrieval_count);
|
|
73
|
+
if (retrievalDiff !== 0)
|
|
74
|
+
return retrievalDiff;
|
|
75
|
+
return compareEntryIdentity(a, b);
|
|
76
|
+
}
|
|
77
|
+
// Tenant partition (mirrors consolidate.ts mergeCandidatesByTenant and
|
|
78
|
+
// dag.ts unparentedByTenant): group by tenantId BEFORE the sort so a
|
|
79
|
+
// duplicate pair can never form across tenants. Map preserves insertion
|
|
80
|
+
// order, so a single-tenant store (every row 'default') gets exactly one
|
|
81
|
+
// group and the sort plus pair loop below run byte-identical to the
|
|
82
|
+
// pre-fix global pass.
|
|
83
|
+
function entriesByPartition(entries) {
|
|
84
|
+
const entriesByTenant = new Map();
|
|
85
|
+
for (const entry of entries) {
|
|
86
|
+
// EI2: also split by restricted scope, else a private copy can delete the readable one.
|
|
87
|
+
const key = derivationPartitionKey(entry.tenantId, entry.scope, entry.origin_project);
|
|
88
|
+
const bucket = entriesByTenant.get(key);
|
|
89
|
+
if (bucket)
|
|
90
|
+
bucket.push(entry);
|
|
91
|
+
else
|
|
92
|
+
entriesByTenant.set(key, [entry]);
|
|
93
|
+
}
|
|
94
|
+
return entriesByTenant;
|
|
95
|
+
}
|
|
96
|
+
function dedupPair(kept, dropped) {
|
|
97
|
+
return {
|
|
98
|
+
kept: kept.id,
|
|
99
|
+
keptContent: kept.content,
|
|
100
|
+
keptLayer: kept.layer,
|
|
101
|
+
keptStrength: kept.strength ?? 0,
|
|
102
|
+
removed: dropped.id,
|
|
103
|
+
removedContent: dropped.content,
|
|
104
|
+
removedLayer: dropped.layer,
|
|
105
|
+
removedStrength: dropped.strength ?? 0,
|
|
106
|
+
similarity: textOverlap(kept.content, dropped.content),
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
/** Pairs within one partition, already in survivor order, grouped by survivor; marks each loser in `removed`. */
|
|
110
|
+
function partitionPairs(tenantEntries, removed, backing) {
|
|
111
|
+
const groups = new Map();
|
|
112
|
+
for (const entry of tenantEntries) {
|
|
113
|
+
const key = duplicateKey(entry.content);
|
|
114
|
+
const group = groups.get(key);
|
|
115
|
+
if (!group) {
|
|
116
|
+
groups.set(key, { survivor: entry, pairs: [] });
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
if (!canAutoDelete(entry) || backing.has(entry.id))
|
|
120
|
+
continue;
|
|
121
|
+
removed.add(entry.id);
|
|
122
|
+
group.pairs.push(dedupPair(group.survivor, entry));
|
|
123
|
+
}
|
|
124
|
+
return [...groups.values()].flatMap((group) => group.pairs);
|
|
125
|
+
}
|
|
59
126
|
/**
|
|
60
127
|
* Scan the store for duplicates and remove the weaker copy: same text apart
|
|
61
128
|
* from spacing, since a near-duplicate can differ in a value (port, version,
|
|
@@ -70,28 +137,7 @@ export function deduplicateStore(hippoRoot, options = {}) {
|
|
|
70
137
|
// Only current distilled rows compete: raw rows are append-only (the delete
|
|
71
138
|
// trigger would abort sleep mid-loop) and superseded rows are history, as in consolidate.ts.
|
|
72
139
|
const entries = loadAllEntries(hippoRoot).filter((e) => e.kind === 'distilled' && !e.superseded_by);
|
|
73
|
-
|
|
74
|
-
// dag.ts unparentedByTenant): group by tenantId BEFORE the sort so a
|
|
75
|
-
// duplicate pair can never form across tenants. Map preserves insertion
|
|
76
|
-
// order, so a single-tenant store (every row 'default') gets exactly one
|
|
77
|
-
// group and the sort plus pair loop below run byte-identical to the
|
|
78
|
-
// pre-fix global pass.
|
|
79
|
-
const entriesByTenant = new Map();
|
|
80
|
-
for (const entry of entries) {
|
|
81
|
-
// EI2: also split by restricted scope, else a private copy can delete the readable one.
|
|
82
|
-
const key = derivationPartitionKey(entry.tenantId, entry.scope, entry.origin_project);
|
|
83
|
-
const bucket = entriesByTenant.get(key);
|
|
84
|
-
if (bucket)
|
|
85
|
-
bucket.push(entry);
|
|
86
|
-
else
|
|
87
|
-
entriesByTenant.set(key, [entry]);
|
|
88
|
-
}
|
|
89
|
-
// finiteCount mirrors strengthBucket's non-finite hardening on the
|
|
90
|
-
// retrieval leg: a NaN retrieval_count would make the comparator return
|
|
91
|
-
// NaN and break the total order the same way a NaN bucket would.
|
|
92
|
-
// Unreachable via the schema (non-nullable INTEGER column), so this is
|
|
93
|
-
// symmetry, not a live bug.
|
|
94
|
-
const finiteCount = (n) => Number.isFinite(n ?? 0) ? (n ?? 0) : 0;
|
|
140
|
+
const entriesByTenant = entriesByPartition(entries);
|
|
95
141
|
// Shared across tenant groups: safe because memory ids are globally
|
|
96
142
|
// unique (crypto.randomUUID at creation), so an id in `removed` can never
|
|
97
143
|
// collide with another tenant's row, and deleteEntry below deletes by
|
|
@@ -100,45 +146,24 @@ export function deduplicateStore(hippoRoot, options = {}) {
|
|
|
100
146
|
const pairs = [];
|
|
101
147
|
const backing = memoriesBackingObjects(hippoRoot);
|
|
102
148
|
for (const tenantEntries of entriesByTenant.values()) {
|
|
103
|
-
// The
|
|
104
|
-
//
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
tenantEntries.sort((a, b) => {
|
|
108
|
-
const bucketDiff = strengthBucket(b.strength) - strengthBucket(a.strength);
|
|
109
|
-
if (bucketDiff !== 0)
|
|
110
|
-
return bucketDiff;
|
|
111
|
-
const retrievalDiff = finiteCount(b.retrieval_count) - finiteCount(a.retrieval_count);
|
|
112
|
-
if (retrievalDiff !== 0)
|
|
113
|
-
return retrievalDiff;
|
|
114
|
-
return compareEntryIdentity(a, b);
|
|
115
|
-
});
|
|
116
|
-
const texts = tenantEntries.map((e) => duplicateKey(e.content));
|
|
117
|
-
for (let i = 0; i < tenantEntries.length; i++) {
|
|
118
|
-
if (removed.has(tenantEntries[i].id))
|
|
119
|
-
continue;
|
|
120
|
-
for (let j = i + 1; j < tenantEntries.length; j++) {
|
|
121
|
-
if (removed.has(tenantEntries[j].id) || !canAutoDelete(tenantEntries[j]) || backing.has(tenantEntries[j].id))
|
|
122
|
-
continue;
|
|
123
|
-
if (texts[j] !== texts[i])
|
|
124
|
-
continue;
|
|
125
|
-
const similarity = textOverlap(tenantEntries[i].content, tenantEntries[j].content);
|
|
126
|
-
removed.add(tenantEntries[j].id);
|
|
127
|
-
pairs.push({
|
|
128
|
-
kept: tenantEntries[i].id,
|
|
129
|
-
keptContent: tenantEntries[i].content,
|
|
130
|
-
keptLayer: tenantEntries[i].layer,
|
|
131
|
-
keptStrength: tenantEntries[i].strength ?? 0,
|
|
132
|
-
removed: tenantEntries[j].id,
|
|
133
|
-
removedContent: tenantEntries[j].content,
|
|
134
|
-
removedLayer: tenantEntries[j].layer,
|
|
135
|
-
removedStrength: tenantEntries[j].strength ?? 0,
|
|
136
|
-
similarity,
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
|
-
}
|
|
149
|
+
// The survivor total order (see the file-level docstring), scoped per
|
|
150
|
+
// tenant group: the total order holds within a tenant only, matching the partition above.
|
|
151
|
+
tenantEntries.sort(survivorOrder);
|
|
152
|
+
pairs.push(...partitionPairs(tenantEntries, removed, backing));
|
|
140
153
|
}
|
|
141
|
-
const done = dryRun ? pairs :
|
|
154
|
+
const done = dryRun ? pairs : deletePairs(hippoRoot, pairs, options.actor);
|
|
142
155
|
return { removed: done.length, pairs: done };
|
|
143
156
|
}
|
|
157
|
+
/** The pairs whose loser was deleted; one store handle, one transaction per delete. */
|
|
158
|
+
function deletePairs(hippoRoot, pairs, actor) {
|
|
159
|
+
if (pairs.length === 0)
|
|
160
|
+
return [];
|
|
161
|
+
const db = openStore(hippoRoot);
|
|
162
|
+
try {
|
|
163
|
+
return pairs.filter((p) => deleteEntryOn(db, hippoRoot, p.removed, { actor, reason: `dedup: duplicate of ${p.kept}`, automatic: true }));
|
|
164
|
+
}
|
|
165
|
+
finally {
|
|
166
|
+
closeHippoDb(db);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
144
169
|
//# sourceMappingURL=dedupe.js.map
|
|
@@ -24,12 +24,7 @@ function byDepthThenScore(a, b) {
|
|
|
24
24
|
return score;
|
|
25
25
|
return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
|
|
26
26
|
}
|
|
27
|
-
|
|
28
|
-
export function createDeliveryRecorder(init) {
|
|
29
|
-
const startedMs = Date.now();
|
|
30
|
-
const ts = evalNow().toISOString();
|
|
31
|
-
// Test-only fault injection, as HIPPO_FAKE_NOW is for time.
|
|
32
|
-
const fault = envTestDeliveryFault();
|
|
27
|
+
function readPayload(init) {
|
|
33
28
|
const payloadSession = hookPayloadSessionId(init.stdinText);
|
|
34
29
|
const subagent = isSubagentPayload(init.stdinText);
|
|
35
30
|
const envSession = init.envSessionId !== undefined && init.envSessionId !== '' ? init.envSessionId : null;
|
|
@@ -40,148 +35,127 @@ export function createDeliveryRecorder(init) {
|
|
|
40
35
|
const sessionState = subagent
|
|
41
36
|
? 'subagent'
|
|
42
37
|
: payloadSession !== null ? 'payload' : envSession !== null ? 'env' : 'missing';
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
emittedCount: rows.filter((r) => r.outcome === 'emitted').length,
|
|
132
|
-
rejectedCount: rejected.length + undecided,
|
|
133
|
-
rejectedUnlisted: overflow + undecided,
|
|
134
|
-
sectionsShown: shown,
|
|
135
|
-
sectionsDropped: dropped,
|
|
136
|
-
budgetTokens: facts?.budgetTokens ?? 0,
|
|
137
|
-
selectedTokens: [...picked.values()].reduce((sum, p) => sum + p.tokens, 0),
|
|
138
|
-
injectedTokens: emitted !== null ? estimateTokens(emitted) : 0,
|
|
139
|
-
staticHash: outcome.staticHash ?? null,
|
|
140
|
-
recallHash: outcome.recallHash ?? null,
|
|
141
|
-
emittedHash: emitted !== null ? blockHash(emitted) : null,
|
|
142
|
-
elapsedMs: Math.max(0, Date.now() - startedMs),
|
|
143
|
-
candidates: rows,
|
|
144
|
-
};
|
|
38
|
+
return { payloadSession, envSession, prompt, hostTurnId, hookEvent, sessionState };
|
|
39
|
+
}
|
|
40
|
+
function rejectId(state, id, stage, reason, score, tokens) {
|
|
41
|
+
const held = state.candidates.get(id);
|
|
42
|
+
// First rejection wins; an id never offered is not a candidate of this call.
|
|
43
|
+
if (!held || held.reason !== null)
|
|
44
|
+
return;
|
|
45
|
+
state.candidates.set(id, { ...held, stage, reason, score, tokens });
|
|
46
|
+
}
|
|
47
|
+
function pickedRows(state) {
|
|
48
|
+
const staticReused = state.outcome.staticReused === true;
|
|
49
|
+
const rows = [];
|
|
50
|
+
for (const p of state.picked.values()) {
|
|
51
|
+
const held = state.candidates.get(p.entry.id);
|
|
52
|
+
rows.push({
|
|
53
|
+
memoryId: p.entry.id,
|
|
54
|
+
sourceStore: p.sourceStore,
|
|
55
|
+
pool: p.promptRecall ? 'prompt-recall' : held?.pool === 'pin' || p.entry.pinned ? 'pin' : 'recent',
|
|
56
|
+
stage: 'final',
|
|
57
|
+
outcome: staticReused && !p.promptRecall ? 'reused' : 'emitted',
|
|
58
|
+
reason: null,
|
|
59
|
+
rank: p.rank,
|
|
60
|
+
score: p.score,
|
|
61
|
+
tokens: p.tokens,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
return rows;
|
|
65
|
+
}
|
|
66
|
+
/** Rejected candidates deepest first, capped at DELIVERY_REJECTED_ROW_CAP rows. */
|
|
67
|
+
function rejectedRows(state) {
|
|
68
|
+
const rejected = [];
|
|
69
|
+
let undecided = 0;
|
|
70
|
+
for (const c of state.candidates.values()) {
|
|
71
|
+
if (state.picked.has(c.id))
|
|
72
|
+
continue;
|
|
73
|
+
if (c.reason === null)
|
|
74
|
+
undecided += 1;
|
|
75
|
+
else
|
|
76
|
+
rejected.push(c);
|
|
77
|
+
}
|
|
78
|
+
rejected.sort(byDepthThenScore);
|
|
79
|
+
const rows = rejected.slice(0, DELIVERY_REJECTED_ROW_CAP).map((c) => ({
|
|
80
|
+
memoryId: c.id, sourceStore: c.sourceStore, pool: c.pool, stage: c.stage ?? 'load', outcome: 'rejected',
|
|
81
|
+
reason: c.reason, rank: null, score: c.score, tokens: c.tokens,
|
|
82
|
+
}));
|
|
83
|
+
const overflow = Math.max(0, rejected.length - DELIVERY_REJECTED_ROW_CAP);
|
|
84
|
+
return { rows, rejected: rejected.length, undecided, overflow };
|
|
85
|
+
}
|
|
86
|
+
function buildEvent(init, payload, state, ts, startedMs) {
|
|
87
|
+
const { facts, outcome } = state;
|
|
88
|
+
const picked = pickedRows(state);
|
|
89
|
+
const rejected = rejectedRows(state);
|
|
90
|
+
const rows = [...picked, ...rejected.rows];
|
|
91
|
+
const emitted = outcome.emittedText ?? null;
|
|
92
|
+
return {
|
|
93
|
+
ts,
|
|
94
|
+
tenantId: init.tenantId,
|
|
95
|
+
runtime: payload.hostTurnId !== null ? 'codex' : payload.hookEvent !== null ? 'claude-code' : 'unknown',
|
|
96
|
+
eventType: payload.hookEvent === 'UserPromptSubmit' ? 'prompt-submit' : 'pinned-manual',
|
|
97
|
+
surface: 'hook',
|
|
98
|
+
storeHash: init.storeHash,
|
|
99
|
+
writeStore: init.writeStore,
|
|
100
|
+
projectHash: facts !== null && facts.projectName !== '' ? blockHash(facts.projectName) : null,
|
|
101
|
+
sessionId: payload.payloadSession ?? payload.envSession,
|
|
102
|
+
sessionState: payload.sessionState,
|
|
103
|
+
hostTurnId: payload.hostTurnId,
|
|
104
|
+
promptHash: payload.prompt !== null ? blockHash(payload.prompt) : null,
|
|
105
|
+
promptLength: payload.prompt?.length ?? 0,
|
|
106
|
+
queryHash: null,
|
|
107
|
+
recallTraceId: null,
|
|
108
|
+
blockState: state.disabledSeen ? 'disabled' : outcome.state,
|
|
109
|
+
promptRecall: facts?.promptRecall === true,
|
|
110
|
+
consideredCount: new Set([...state.candidates.keys(), ...state.picked.keys()]).size,
|
|
111
|
+
filteredCount: state.filtered.size,
|
|
112
|
+
selectedCount: state.picked.size,
|
|
113
|
+
emittedCount: rows.filter((r) => r.outcome === 'emitted').length,
|
|
114
|
+
rejectedCount: rejected.rejected + rejected.undecided,
|
|
115
|
+
rejectedUnlisted: rejected.overflow + rejected.undecided,
|
|
116
|
+
sectionsShown: state.shown,
|
|
117
|
+
sectionsDropped: state.dropped,
|
|
118
|
+
budgetTokens: facts?.budgetTokens ?? 0,
|
|
119
|
+
selectedTokens: [...state.picked.values()].reduce((sum, p) => sum + p.tokens, 0),
|
|
120
|
+
injectedTokens: emitted !== null ? estimateTokens(emitted) : 0,
|
|
121
|
+
staticHash: outcome.staticHash ?? null,
|
|
122
|
+
recallHash: outcome.recallHash ?? null,
|
|
123
|
+
emittedHash: emitted !== null ? blockHash(emitted) : null,
|
|
124
|
+
elapsedMs: Math.max(0, Date.now() - startedMs),
|
|
125
|
+
candidates: rows,
|
|
145
126
|
};
|
|
127
|
+
}
|
|
128
|
+
/** The candidate-tracking half of the observer, each method run through `guard`. */
|
|
129
|
+
function candidateMethods(state, guard) {
|
|
146
130
|
return {
|
|
147
|
-
root: init.root,
|
|
148
|
-
facts: (f) => guard(() => { facts = { ...f }; }),
|
|
149
|
-
sections: (s, d) => guard(() => { shown = s; dropped = d; }),
|
|
150
|
-
watchAdmit: (admit) => (e) => {
|
|
151
|
-
const ok = admit(e);
|
|
152
|
-
if (!ok)
|
|
153
|
-
guard(() => { filtered.add(e.id); });
|
|
154
|
-
return ok;
|
|
155
|
-
},
|
|
156
131
|
qualityDropped: (e, isGlobal) => guard(() => {
|
|
157
|
-
filtered.add(e.id);
|
|
158
|
-
if (!candidates.has(e.id)) {
|
|
159
|
-
candidates.set(e.id, {
|
|
132
|
+
state.filtered.add(e.id);
|
|
133
|
+
if (!state.candidates.has(e.id)) {
|
|
134
|
+
state.candidates.set(e.id, {
|
|
160
135
|
id: e.id, sourceStore: storeOf(isGlobal), pool: 'recent', stage: null, reason: null, score: null, tokens: null,
|
|
161
136
|
});
|
|
162
137
|
}
|
|
163
|
-
rejectId(e.id, 'load', 'quality', null, null);
|
|
138
|
+
rejectId(state, e.id, 'load', 'quality', null, null);
|
|
164
139
|
}),
|
|
165
|
-
disabled: () => guard(() => { disabledSeen = true; }),
|
|
166
140
|
offer: (entries, isGlobal, pool) => guard(() => {
|
|
167
141
|
for (const e of entries) {
|
|
168
|
-
const held = candidates.get(e.id);
|
|
142
|
+
const held = state.candidates.get(e.id);
|
|
169
143
|
const wanted = pool ?? (e.pinned ? 'pin' : 'recent');
|
|
170
144
|
// A loaded recent row the prompt-recall gate then judges belongs to that pool.
|
|
171
145
|
const relabel = held !== undefined && held.reason === null && held.pool === 'recent' && wanted === 'prompt-recall';
|
|
172
146
|
if (held !== undefined && !relabel)
|
|
173
147
|
continue;
|
|
174
|
-
candidates.set(e.id, {
|
|
148
|
+
state.candidates.set(e.id, {
|
|
175
149
|
id: e.id, sourceStore: storeOf(isGlobal), pool: wanted, stage: null, reason: null, score: null, tokens: null,
|
|
176
150
|
});
|
|
177
151
|
}
|
|
178
152
|
}),
|
|
179
|
-
reject: (e, stage, reason, score, tokens) => guard(() => rejectId(e.id, stage, reason, score ?? null, tokens ?? null)),
|
|
153
|
+
reject: (e, stage, reason, score, tokens) => guard(() => rejectId(state, e.id, stage, reason, score ?? null, tokens ?? null)),
|
|
180
154
|
dropMissing: (before, after, stage, reason) => guard(() => {
|
|
181
155
|
const kept = new Set(after.map((e) => e.id));
|
|
182
156
|
for (const e of before)
|
|
183
157
|
if (!kept.has(e.id))
|
|
184
|
-
rejectId(e.id, stage, reason, null, null);
|
|
158
|
+
rejectId(state, e.id, stage, reason, null, null);
|
|
185
159
|
}),
|
|
186
160
|
gated: (prompt, items, gate, kept) => guard(() => {
|
|
187
161
|
const keptIds = new Set(kept.map((g) => g.item.id));
|
|
@@ -190,29 +164,74 @@ export function createDeliveryRecorder(init) {
|
|
|
190
164
|
continue;
|
|
191
165
|
const { score, shared } = scoreOverlap(prompt, c.tokens, gate.metric);
|
|
192
166
|
const cleared = score >= gate.threshold && shared >= gate.minShared;
|
|
193
|
-
rejectId(c.id, 'gate', cleared ? 'gate-max-items' : 'gate-below-threshold', score, null);
|
|
167
|
+
rejectId(state, c.id, 'gate', cleared ? 'gate-max-items' : 'gate-below-threshold', score, null);
|
|
194
168
|
}
|
|
195
169
|
}),
|
|
196
170
|
selected: (items) => guard(() => {
|
|
197
|
-
picked.clear();
|
|
171
|
+
state.picked.clear();
|
|
198
172
|
items.forEach((r, i) => {
|
|
199
|
-
picked.set(r.entry.id, {
|
|
173
|
+
state.picked.set(r.entry.id, {
|
|
200
174
|
entry: r.entry, rank: i + 1, score: r.score, tokens: r.tokens,
|
|
201
175
|
sourceStore: storeOf(r.isGlobal), promptRecall: r.promptRecall === true,
|
|
202
176
|
});
|
|
203
177
|
});
|
|
204
178
|
}),
|
|
205
|
-
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
/** A recorder for one call; every observer method is guarded, and a throw marks it broken instead of escaping. */
|
|
182
|
+
export function createDeliveryRecorder(init) {
|
|
183
|
+
const startedMs = Date.now();
|
|
184
|
+
const ts = evalNow().toISOString();
|
|
185
|
+
// Test-only fault injection, as HIPPO_FAKE_NOW is for time.
|
|
186
|
+
const fault = envTestDeliveryFault();
|
|
187
|
+
const payload = readPayload(init);
|
|
188
|
+
const state = {
|
|
189
|
+
candidates: new Map(), picked: new Map(), filtered: new Set(), facts: null, shown: 0, dropped: 0,
|
|
190
|
+
disabledSeen: false, outcome: { state: 'empty' }, broken: null, flushed: false,
|
|
191
|
+
};
|
|
192
|
+
const guard = (fn) => {
|
|
193
|
+
if (state.broken !== null)
|
|
194
|
+
return;
|
|
195
|
+
try {
|
|
196
|
+
if (fault === 'observe')
|
|
197
|
+
throw new Error('injected observe fault');
|
|
198
|
+
fn();
|
|
199
|
+
}
|
|
200
|
+
catch (error) {
|
|
201
|
+
state.broken = error instanceof Error ? error.message : String(error);
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
const { qualityDropped, offer, reject, dropMissing, gated, selected } = candidateMethods(state, guard);
|
|
205
|
+
return {
|
|
206
|
+
root: init.root,
|
|
207
|
+
facts: (f) => guard(() => { state.facts = { ...f }; }),
|
|
208
|
+
sections: (s, d) => guard(() => { state.shown = s; state.dropped = d; }),
|
|
209
|
+
watchAdmit: (admit) => (e) => {
|
|
210
|
+
const ok = admit(e);
|
|
211
|
+
if (!ok)
|
|
212
|
+
guard(() => { state.filtered.add(e.id); });
|
|
213
|
+
return ok;
|
|
214
|
+
},
|
|
215
|
+
qualityDropped,
|
|
216
|
+
disabled: () => guard(() => { state.disabledSeen = true; }),
|
|
217
|
+
offer,
|
|
218
|
+
reject,
|
|
219
|
+
dropMissing,
|
|
220
|
+
gated,
|
|
221
|
+
selected,
|
|
222
|
+
delivered: (o) => guard(() => { state.outcome = { ...o }; }),
|
|
206
223
|
flush: (write) => {
|
|
207
|
-
if (flushed)
|
|
224
|
+
if (state.flushed)
|
|
208
225
|
return;
|
|
209
|
-
flushed = true;
|
|
210
|
-
if (broken !== null) {
|
|
226
|
+
state.flushed = true;
|
|
227
|
+
if (state.broken !== null) {
|
|
211
228
|
// Same pinned `[hippo] delivery ledger` hook stderr line as recall-trace.ts's write failure.
|
|
212
|
-
console.error(`[hippo] delivery ledger skipped: recorder failed: ${broken}`);
|
|
229
|
+
console.error(`[hippo] delivery ledger skipped: recorder failed: ${state.broken}`);
|
|
213
230
|
return;
|
|
214
231
|
}
|
|
215
|
-
|
|
232
|
+
if (fault === 'build')
|
|
233
|
+
throw new Error('injected build fault');
|
|
234
|
+
const input = buildEvent(init, payload, state, ts, startedMs);
|
|
216
235
|
if (fault === 'flush')
|
|
217
236
|
throw new Error('injected flush fault');
|
|
218
237
|
write(input);
|