hippo-memory 1.61.0 → 1.63.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -53
- package/dist/agent-memories/apply.d.ts +3 -1
- package/dist/agent-memories/apply.js +23 -1
- package/dist/agent-memories/claude-code.d.ts +4 -1
- package/dist/agent-memories/claude-code.js +53 -9
- package/dist/agent-memories/report.d.ts +1 -0
- package/dist/agent-memories/report.js +2 -2
- package/dist/agent-memories/sync.d.ts +3 -3
- package/dist/agent-memories/sync.js +46 -24
- package/dist/agent-memories/types.d.ts +0 -2
- package/dist/ambient-store.d.ts +4 -4
- package/dist/ambient-store.js +4 -3
- package/dist/api/assemble.d.ts +7 -10
- package/dist/api/assemble.js +62 -66
- package/dist/api/audit.d.ts +2 -2
- package/dist/api/audit.js +2 -2
- package/dist/api/auth.d.ts +6 -6
- package/dist/api/auth.js +5 -7
- package/dist/api/context-select.d.ts +50 -0
- package/dist/api/context-select.js +342 -0
- package/dist/api/context-types.d.ts +12 -17
- package/dist/api/context.d.ts +5 -4
- package/dist/api/context.js +208 -535
- package/dist/api/dormant.js +1 -1
- package/dist/api/drill-down.d.ts +4 -4
- package/dist/api/drill-down.js +56 -41
- package/dist/api/outcome.d.ts +8 -13
- package/dist/api/outcome.js +13 -17
- package/dist/api/promote.d.ts +5 -8
- package/dist/api/promote.js +55 -66
- package/dist/api/quarantine.js +3 -2
- package/dist/api/recall-types.d.ts +43 -55
- package/dist/api/recall.d.ts +3 -3
- package/dist/api/recall.js +310 -456
- package/dist/api/remember.d.ts +2 -2
- package/dist/api/sleep.d.ts +7 -35
- package/dist/api/sleep.js +206 -220
- package/dist/api/tokens.d.ts +2 -2
- package/dist/api/tokens.js +2 -2
- package/dist/api/types.d.ts +6 -10
- package/dist/api/types.js +2 -4
- package/dist/audit-prune.d.ts +4 -6
- package/dist/audit-prune.js +3 -5
- package/dist/audit.js +11 -33
- package/dist/auth.d.ts +8 -9
- package/dist/auth.js +5 -7
- package/dist/autolearn.d.ts +1 -1
- package/dist/autolearn.js +1 -1
- package/dist/availability.js +3 -5
- package/dist/capture/command.d.ts +5 -4
- package/dist/capture/command.js +8 -22
- package/dist/capture/compact.d.ts +2 -2
- package/dist/capture/compact.js +11 -15
- package/dist/capture/extract.js +37 -115
- package/dist/capture-error.d.ts +1 -1
- package/dist/capture-error.js +1 -1
- package/dist/churn-git.d.ts +1 -1
- package/dist/churn-git.js +1 -1
- package/dist/cli/audit.js +3 -4
- package/dist/cli/auth.js +3 -6
- package/dist/cli/briefs.js +324 -306
- package/dist/cli/context.js +44 -34
- package/dist/cli/continuity.js +283 -271
- package/dist/cli/curate.d.ts +1 -1
- package/dist/cli/curate.js +43 -58
- package/dist/cli/dag.js +5 -9
- package/dist/cli/decisions.js +334 -345
- package/dist/cli/explain.js +68 -61
- package/dist/cli/goals.js +1 -1
- package/dist/cli/init.js +1 -1
- package/dist/cli/maintenance.js +62 -51
- package/dist/cli/playbooks.js +391 -379
- package/dist/cli/projects.js +11 -6
- package/dist/cli/recall.js +30 -44
- package/dist/cli/remember.js +118 -87
- package/dist/cli/session-hooks.js +106 -115
- package/dist/cli/setup.d.ts +1 -1
- package/dist/cli/setup.js +267 -250
- package/dist/cli/shared.js +3 -3
- package/dist/cli/slack.js +1 -1
- package/dist/cli/sleep.js +27 -1
- package/dist/cli/status.d.ts +4 -4
- package/dist/cli/status.js +80 -76
- package/dist/cli/transfer.js +88 -107
- package/dist/cli/usage.js +9 -6
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +4 -9
- package/dist/compaction-record.d.ts +2 -2
- package/dist/compaction-record.js +89 -68
- package/dist/compare.d.ts +11 -16
- package/dist/compare.js +11 -16
- package/dist/config.d.ts +19 -18
- package/dist/config.js +90 -67
- package/dist/connectors/github/backfill.d.ts +2 -2
- package/dist/connectors/github/backfill.js +8 -15
- package/dist/connectors/github/cli-impl.js +3 -8
- package/dist/connectors/github/deletion.d.ts +5 -12
- package/dist/connectors/github/deletion.js +5 -12
- package/dist/connectors/github/dlq.d.ts +6 -9
- package/dist/connectors/github/dlq.js +2 -3
- package/dist/connectors/github/ingest.d.ts +5 -7
- package/dist/connectors/github/ingest.js +8 -12
- package/dist/connectors/github/octokit-client.d.ts +3 -5
- package/dist/connectors/github/octokit-client.js +5 -6
- package/dist/connectors/github/signature.d.ts +9 -39
- package/dist/connectors/github/signature.js +9 -39
- package/dist/connectors/github/tenant-routing.d.ts +1 -1
- package/dist/connectors/github/tenant-routing.js +1 -1
- package/dist/connectors/github/transform.js +2 -2
- package/dist/connectors/github/types.d.ts +2 -10
- package/dist/connectors/github/types.js +1 -3
- package/dist/connectors/slack/deletion.d.ts +3 -8
- package/dist/connectors/slack/deletion.js +3 -8
- package/dist/connectors/slack/dlq.d.ts +1 -1
- package/dist/connectors/slack/ingest.d.ts +1 -1
- package/dist/connectors/slack/ingest.js +7 -16
- package/dist/connectors/slack/signature.d.ts +1 -1
- package/dist/connectors/slack/tenant-routing.d.ts +3 -5
- package/dist/connectors/slack/tenant-routing.js +3 -5
- package/dist/connectors/slack/transform.d.ts +5 -6
- package/dist/connectors/slack/transform.js +5 -6
- package/dist/connectors/slack/types.d.ts +2 -6
- package/dist/connectors/slack/types.js +1 -3
- package/dist/connectors/slack/web-client.js +10 -3
- package/dist/connectors/slack/workspaces.d.ts +3 -5
- package/dist/connectors/slack/workspaces.js +3 -5
- package/dist/consolidate/conflicts.js +3 -14
- package/dist/consolidate/decay.js +9 -29
- package/dist/consolidate/llm-passes.js +4 -5
- package/dist/consolidate/merge.js +8 -23
- package/dist/consolidate/run.d.ts +1 -8
- package/dist/consolidate/run.js +3 -25
- package/dist/consolidate/sleep.js +5 -17
- package/dist/consolidate/traces.js +9 -21
- package/dist/customer-notes.d.ts +5 -7
- package/dist/customer-notes.js +82 -76
- package/dist/dag.d.ts +10 -21
- package/dist/dag.js +189 -203
- package/dist/db/continuity.js +2 -2
- package/dist/db/migrations/v14.js +1 -1
- package/dist/db/migrations/v15.js +1 -2
- package/dist/db/migrations/v16.js +3 -4
- package/dist/db/migrations/v17.js +2 -3
- package/dist/db/migrations/v19.js +1 -1
- package/dist/db/migrations/v20.js +1 -1
- package/dist/db/migrations/v21.js +2 -6
- package/dist/db/migrations/v22.js +2 -4
- package/dist/db/migrations/v23.js +1 -1
- package/dist/db/migrations/v24.js +4 -6
- package/dist/db/migrations/v25.js +2 -3
- package/dist/db/migrations/v26.js +3 -3
- package/dist/db/migrations/v27.js +2 -10
- package/dist/db/migrations/v28.js +5 -8
- package/dist/db/migrations/v29.js +3 -4
- package/dist/db/migrations/v30.js +2 -2
- package/dist/db/migrations/v31.js +1 -1
- package/dist/db/migrations/v32.js +1 -1
- package/dist/db/migrations/v33.js +3 -3
- package/dist/db/migrations/v34.js +1 -1
- package/dist/db/migrations/v35.js +3 -4
- package/dist/db/migrations/v36.js +3 -4
- package/dist/db/migrations/v37.js +5 -5
- package/dist/db/migrations/v38.js +7 -8
- package/dist/db/migrations/v39.js +1 -1
- package/dist/db/migrations/v40.js +4 -16
- package/dist/db/migrations/v41.js +3 -4
- package/dist/db/migrations/v42.js +3 -4
- package/dist/db/migrations/v45.js +1 -1
- package/dist/db/migrations/v46.js +1 -1
- package/dist/db/migrations/v47.js +1 -1
- package/dist/db/migrations/v48.js +1 -1
- package/dist/decisions.d.ts +2 -2
- package/dist/decisions.js +97 -80
- package/dist/dedupe.js +86 -61
- package/dist/delivery-recorder.js +154 -135
- package/dist/doctor.js +129 -110
- package/dist/dormant.js +1 -4
- package/dist/embedding-provider.d.ts +4 -8
- package/dist/embedding-provider.js +4 -8
- package/dist/embeddings.js +55 -47
- package/dist/env.d.ts +1 -1
- package/dist/env.js +12 -12
- package/dist/escape.d.ts +5 -0
- package/dist/escape.js +10 -0
- package/dist/eval-stats.d.ts +1 -2
- package/dist/eval-stats.js +1 -2
- package/dist/eval-suite.js +27 -21
- package/dist/extract.js +4 -9
- package/dist/failure-log.d.ts +3 -3
- package/dist/failure-log.js +1 -1
- package/dist/forward-claim-detector.d.ts +2 -4
- package/dist/forward-claim-detector.js +6 -11
- package/dist/goals.d.ts +3 -3
- package/dist/goals.js +103 -91
- package/dist/graph/read.d.ts +2 -2
- package/dist/graph/read.js +5 -6
- package/dist/graph/types.d.ts +8 -8
- package/dist/graph/write.d.ts +7 -14
- package/dist/graph/write.js +16 -23
- package/dist/graph-extract.d.ts +7 -8
- package/dist/graph-extract.js +62 -72
- package/dist/graph-recall.d.ts +2 -2
- package/dist/graph-recall.js +55 -49
- package/dist/graph-stream.d.ts +5 -6
- package/dist/graph-stream.js +66 -57
- package/dist/graph-view.d.ts +2 -2
- package/dist/graph-view.js +7 -7
- package/dist/half-life-migration.d.ts +1 -2
- package/dist/half-life-migration.js +2 -3
- package/dist/hooks/codex-session.js +1 -1
- package/dist/hooks/codex-wrapper.d.ts +1 -1
- package/dist/hooks/codex-wrapper.js +3 -2
- package/dist/hooks/json-hooks.d.ts +2 -2
- package/dist/hooks/json-hooks.js +5 -4
- package/dist/hooks/opencode.d.ts +1 -1
- package/dist/hooks/opencode.js +5 -4
- package/dist/hooks/shared.d.ts +3 -7
- package/dist/hooks/shared.js +1 -8
- package/dist/http-util.d.ts +2 -3
- package/dist/http-util.js +3 -0
- package/dist/importers/core.d.ts +2 -9
- package/dist/importers/core.js +15 -30
- package/dist/importers/sources.js +2 -1
- package/dist/importers/vault.js +2 -20
- package/dist/incidents.d.ts +1 -1
- package/dist/incidents.js +46 -39
- package/dist/instruction-detect.d.ts +1 -1
- package/dist/instruction-detect.js +1 -1
- package/dist/invalidation.d.ts +3 -0
- package/dist/invalidation.js +160 -114
- package/dist/json.d.ts +5 -0
- package/dist/json.js +4 -0
- package/dist/judgment.js +1 -2
- package/dist/local-embedding.js +1 -1
- package/dist/mcp/admin-tools.js +7 -17
- package/dist/mcp/format.js +1 -1
- package/dist/mcp/framing.js +3 -6
- package/dist/mcp/protocol.d.ts +2 -5
- package/dist/mcp/protocol.js +1 -3
- package/dist/mcp/recall-tools.js +12 -15
- package/dist/mcp/request.js +4 -3
- package/dist/mcp/session-state.js +2 -3
- package/dist/mcp/stdio.js +2 -1
- package/dist/mcp/tools.js +9 -6
- package/dist/memory-value-weights.d.ts +5 -8
- package/dist/memory-value-weights.js +5 -8
- package/dist/memory-value.d.ts +13 -13
- package/dist/memory-value.js +26 -37
- package/dist/memory.d.ts +20 -22
- package/dist/memory.js +24 -48
- package/dist/multihop.d.ts +1 -1
- package/dist/multihop.js +3 -2
- package/dist/owner-validation.d.ts +4 -5
- package/dist/owner-validation.js +4 -5
- package/dist/physics.d.ts +4 -4
- package/dist/physics.js +7 -9
- package/dist/policies.d.ts +9 -10
- package/dist/policies.js +96 -81
- package/dist/postinstall.js +3 -6
- package/dist/predictions/planning-fallacy.d.ts +9 -14
- package/dist/predictions/planning-fallacy.js +10 -16
- package/dist/predictions/store.d.ts +15 -23
- package/dist/predictions/store.js +36 -33
- package/dist/processes.d.ts +2 -7
- package/dist/processes.js +88 -72
- package/dist/project-briefs.d.ts +2 -3
- package/dist/project-briefs.js +141 -118
- package/dist/project-identity.d.ts +22 -9
- package/dist/project-identity.js +47 -12
- package/dist/project-merge.d.ts +28 -5
- package/dist/project-merge.js +213 -46
- package/dist/project-remote.d.ts +12 -0
- package/dist/project-remote.js +138 -0
- package/dist/prompt-recall.js +1 -2
- package/dist/rate-limit.d.ts +1 -1
- package/dist/rate-limit.js +1 -1
- package/dist/raw-archive.d.ts +9 -0
- package/dist/raw-archive.js +70 -53
- package/dist/recall-history.d.ts +19 -20
- package/dist/recall-history.js +24 -42
- package/dist/recall-pipeline.js +4 -28
- package/dist/recall-scope.d.ts +7 -8
- package/dist/recall-scope.js +7 -8
- package/dist/recall-trace.d.ts +5 -9
- package/dist/recall-trace.js +6 -10
- package/dist/refine-llm.d.ts +1 -1
- package/dist/refine-llm.js +2 -2
- package/dist/reject-flow.d.ts +3 -4
- package/dist/reject-flow.js +122 -117
- package/dist/rejection.d.ts +5 -6
- package/dist/rejection.js +7 -15
- package/dist/rerankers/clef.d.ts +1 -1
- package/dist/rerankers/jev.d.ts +1 -2
- package/dist/rerankers/jev.js +4 -5
- package/dist/rerankers/llm.d.ts +1 -2
- package/dist/rerankers/llm.js +1 -2
- package/dist/rerankers/types.d.ts +1 -2
- package/dist/rrf.d.ts +2 -2
- package/dist/rrf.js +2 -2
- package/dist/search/bm25-search.d.ts +1 -1
- package/dist/search/bm25-search.js +2 -1
- package/dist/search/boosts.js +2 -1
- package/dist/search/hybrid.d.ts +1 -1
- package/dist/search/hybrid.js +2 -1
- package/dist/search/physics-search.d.ts +1 -1
- package/dist/search/physics-search.js +2 -1
- package/dist/search/types.d.ts +2 -0
- package/dist/search/types.js +3 -1
- package/dist/secret-detect.d.ts +4 -5
- package/dist/secret-detect.js +6 -10
- package/dist/server/auth.js +5 -5
- package/dist/server/client-ip.js +1 -1
- package/dist/server/cursor.js +2 -1
- package/dist/server/mcp-http.js +4 -4
- package/dist/server/request.d.ts +3 -6
- package/dist/server/request.js +6 -7
- package/dist/server/routes/admin.js +5 -4
- package/dist/server/routes/customer-notes.js +6 -5
- package/dist/server/routes/decisions.js +4 -3
- package/dist/server/routes/incidents.js +7 -5
- package/dist/server/routes/memories.js +7 -7
- package/dist/server/routes/policies.js +3 -2
- package/dist/server/routes/predictions.js +12 -15
- package/dist/server/routes/processes.js +3 -2
- package/dist/server/routes/project-briefs.js +8 -7
- package/dist/server/routes/recall.js +95 -93
- package/dist/server/routes/skills.js +6 -5
- package/dist/server/types.d.ts +1 -1
- package/dist/server/validation.d.ts +1 -2
- package/dist/server/validation.js +7 -14
- package/dist/server-detect.js +72 -58
- package/dist/server.d.ts +2 -2
- package/dist/server.js +131 -117
- package/dist/shared.d.ts +26 -17
- package/dist/shared.js +102 -104
- package/dist/skills.d.ts +3 -3
- package/dist/skills.js +88 -72
- package/dist/store/audit-event.d.ts +2 -2
- package/dist/store/audit-event.js +1 -1
- package/dist/store/candidates.d.ts +2 -2
- package/dist/store/candidates.js +4 -3
- package/dist/store/conflicts.js +30 -22
- package/dist/store/delete-and-batch.d.ts +11 -14
- package/dist/store/delete-and-batch.js +40 -91
- package/dist/store/entry-reads.d.ts +17 -25
- package/dist/store/entry-reads.js +59 -40
- package/dist/store/entry-row.d.ts +6 -24
- package/dist/store/entry-row.js +6 -24
- package/dist/store/entry-writes.d.ts +6 -7
- package/dist/store/entry-writes.js +13 -11
- package/dist/store/handoffs.d.ts +1 -1
- package/dist/store/handoffs.js +7 -10
- package/dist/store/index-and-stats.d.ts +2 -6
- package/dist/store/index-and-stats.js +4 -10
- package/dist/store/mirrors.d.ts +6 -19
- package/dist/store/mirrors.js +14 -39
- package/dist/store/open.js +9 -31
- package/dist/store/rows.d.ts +5 -11
- package/dist/store/rows.js +6 -11
- package/dist/store/search-rows.d.ts +17 -34
- package/dist/store/search-rows.js +34 -56
- package/dist/store/sessions.d.ts +4 -5
- package/dist/store/sessions.js +5 -6
- package/dist/store/summaries.d.ts +13 -17
- package/dist/store/summaries.js +26 -70
- package/dist/support-bundle.js +4 -8
- package/dist/tenant.d.ts +1 -5
- package/dist/token-ledger.d.ts +1 -1
- package/dist/token-ledger.js +3 -5
- package/dist/trace.js +1 -3
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/working-memory.d.ts +1 -1
- package/extensions/openclaw-plugin/openclaw.plugin.json +1 -1
- package/extensions/openclaw-plugin/package.json +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
|
@@ -8,14 +8,10 @@
|
|
|
8
8
|
* read their key from a conventional env var. The provider is selected by
|
|
9
9
|
* `config.embeddings.provider` (default `'local'`).
|
|
10
10
|
*
|
|
11
|
-
* Design contract
|
|
12
|
-
* - Local provider `id` is the BARE model string
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* `${id}#t2`, docs/plans/2026-07-09-recall-determinism.md T1), the STORED
|
|
16
|
-
* identity carries a `#t<N>` suffix and pre-#t2 stores get exactly one
|
|
17
|
-
* forced reindex on their next embed-touching operation — deliberate,
|
|
18
|
-
* because their vectors were computed over path-contaminated text.)
|
|
11
|
+
* Design contract:
|
|
12
|
+
* - Local provider `id` is the BARE model string; the STORED identity adds a `#t<N>`
|
|
13
|
+
* embed-text-format suffix (`embeddingIndexIdentity`), so older stores reindex once:
|
|
14
|
+
* their vectors were computed over path-contaminated text.
|
|
19
15
|
* - API provider `id` is `${kind}:${model}`; switching to/from an API embedder
|
|
20
16
|
* (or a dimension change) flips the identity and triggers the existing
|
|
21
17
|
* reindex-on-change path.
|
|
@@ -8,14 +8,10 @@
|
|
|
8
8
|
* read their key from a conventional env var. The provider is selected by
|
|
9
9
|
* `config.embeddings.provider` (default `'local'`).
|
|
10
10
|
*
|
|
11
|
-
* Design contract
|
|
12
|
-
* - Local provider `id` is the BARE model string
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* `${id}#t2`, docs/plans/2026-07-09-recall-determinism.md T1), the STORED
|
|
16
|
-
* identity carries a `#t<N>` suffix and pre-#t2 stores get exactly one
|
|
17
|
-
* forced reindex on their next embed-touching operation — deliberate,
|
|
18
|
-
* because their vectors were computed over path-contaminated text.)
|
|
11
|
+
* Design contract:
|
|
12
|
+
* - Local provider `id` is the BARE model string; the STORED identity adds a `#t<N>`
|
|
13
|
+
* embed-text-format suffix (`embeddingIndexIdentity`), so older stores reindex once:
|
|
14
|
+
* their vectors were computed over path-contaminated text.
|
|
19
15
|
* - API provider `id` is `${kind}:${model}`; switching to/from an API embedder
|
|
20
16
|
* (or a dimension change) flips the identity and triggers the existing
|
|
21
17
|
* reindex-on-change path.
|
package/dist/embeddings.js
CHANGED
|
@@ -314,7 +314,7 @@ export async function embedMemory(hippoRoot, entry, model) {
|
|
|
314
314
|
try {
|
|
315
315
|
const identity = provider.id;
|
|
316
316
|
if (embeddingModelRequiresReindex(hippoRoot, identity)) {
|
|
317
|
-
//
|
|
317
|
+
// Host-wide rebuild. The embedding index is keyed by entry.id
|
|
318
318
|
// (which is tenant-scoped) but the index itself is one per hippoRoot.
|
|
319
319
|
// Cross-tenant content equivalence is visible at the vector level.
|
|
320
320
|
// Per-tenant indices would be a larger architecture change.
|
|
@@ -357,6 +357,57 @@ export async function embedMemory(hippoRoot, entry, model) {
|
|
|
357
357
|
log.warn(`skipped embedding ${entry.id} (${err instanceof Error ? err.message : String(err)}); run 'hippo embed' to backfill`);
|
|
358
358
|
});
|
|
359
359
|
}
|
|
360
|
+
/** Throws when an unavailable provider is a misconfiguration rather than an intentional no-op. */
|
|
361
|
+
function throwIfProviderKeyMissing(hippoRoot, provider) {
|
|
362
|
+
// A configured (non-disabled) API provider with a missing key is a
|
|
363
|
+
// misconfiguration, not a no-op: surface it so programmatic callers of the
|
|
364
|
+
// exported embedAll() learn nothing was written. Local-not-installed and an
|
|
365
|
+
// explicit enabled=false stay silent no-ops (best-effort / intentional).
|
|
366
|
+
const cfg = loadConfig(hippoRoot).embeddings;
|
|
367
|
+
if (provider.kind !== 'local' &&
|
|
368
|
+
cfg.enabled !== false &&
|
|
369
|
+
provider.keyEnv &&
|
|
370
|
+
!envByName(provider.keyEnv)?.trim()) {
|
|
371
|
+
throw new Error(`Embedding provider '${provider.kind}' is configured but ${provider.keyEnv} is not set.`);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
// Embed entries without a cached vector in save-checkpointed chunks.
|
|
375
|
+
// provider.embed batches internally (one HTTP request per batchSize for API
|
|
376
|
+
// providers; sequential for local). A `[]` row means that single item could
|
|
377
|
+
// not be embedded and is left for a later run (resumable). On a hard provider
|
|
378
|
+
// failure mid-backfill we persist the chunks already embedded this run rather
|
|
379
|
+
// than discarding paid progress, then stop and resume on the next run.
|
|
380
|
+
async function backfillPending(hippoRoot, provider, pending, model) {
|
|
381
|
+
let count = 0;
|
|
382
|
+
// Initialized to `undefined` (not a known-evidence literal like `null`) so
|
|
383
|
+
// it stays a plain `unknown` binding for the arbitrary caught value below;
|
|
384
|
+
// falsy either way, so `if (backfillError)` behaves identically.
|
|
385
|
+
let backfillError = undefined;
|
|
386
|
+
const SAVE_CHUNK = 64;
|
|
387
|
+
for (let i = 0; i < pending.length; i += SAVE_CHUNK) {
|
|
388
|
+
const chunk = pending.slice(i, i + SAVE_CHUNK);
|
|
389
|
+
let vectors;
|
|
390
|
+
try {
|
|
391
|
+
vectors = await provider.embed(chunk.map((e) => embeddingInputText(e)), 'passage');
|
|
392
|
+
}
|
|
393
|
+
catch (err) {
|
|
394
|
+
// Preserve the chunks already saved this run, then surface the failure
|
|
395
|
+
// below so the explicit `hippo embed` path never reports a false success.
|
|
396
|
+
backfillError = err;
|
|
397
|
+
break;
|
|
398
|
+
}
|
|
399
|
+
const rows = [];
|
|
400
|
+
for (let j = 0; j < chunk.length; j++) {
|
|
401
|
+
const vec = vectors[j];
|
|
402
|
+
if (vec && vec.length > 0)
|
|
403
|
+
rows.push([chunk[j].id, vec]);
|
|
404
|
+
else
|
|
405
|
+
noteSkippedEmbedding(chunk[j].id);
|
|
406
|
+
}
|
|
407
|
+
count += withVectorDb(hippoRoot, (db) => upsertVectors(db, rows, model));
|
|
408
|
+
}
|
|
409
|
+
return { count, backfillError };
|
|
410
|
+
}
|
|
360
411
|
/**
|
|
361
412
|
* Embed all entries in hippoRoot that don't already have cached vectors.
|
|
362
413
|
* Prunes orphaned embeddings for memories that no longer exist.
|
|
@@ -364,22 +415,12 @@ export async function embedMemory(hippoRoot, entry, model) {
|
|
|
364
415
|
*/
|
|
365
416
|
export async function embedAll(hippoRoot, model, provider = resolveEmbeddingProvider(hippoRoot, { model })) {
|
|
366
417
|
if (!provider.isAvailable()) {
|
|
367
|
-
|
|
368
|
-
// misconfiguration, not a no-op: surface it so programmatic callers of the
|
|
369
|
-
// exported embedAll() learn nothing was written. Local-not-installed and an
|
|
370
|
-
// explicit enabled=false stay silent no-ops (best-effort / intentional).
|
|
371
|
-
const cfg = loadConfig(hippoRoot).embeddings;
|
|
372
|
-
if (provider.kind !== 'local' &&
|
|
373
|
-
cfg.enabled !== false &&
|
|
374
|
-
provider.keyEnv &&
|
|
375
|
-
!envByName(provider.keyEnv)?.trim()) {
|
|
376
|
-
throw new Error(`Embedding provider '${provider.kind}' is configured but ${provider.keyEnv} is not set.`);
|
|
377
|
-
}
|
|
418
|
+
throwIfProviderKeyMissing(hippoRoot, provider);
|
|
378
419
|
return 0;
|
|
379
420
|
}
|
|
380
421
|
return withEmbedLock(hippoRoot, async () => {
|
|
381
422
|
const identity = provider.id;
|
|
382
|
-
//
|
|
423
|
+
// Host-wide by design. embedAll backfills vectors for all tenants'
|
|
383
424
|
// entries into the per-host embedding index. Per-tenant filtering would
|
|
384
425
|
// produce partial indices and break recall.
|
|
385
426
|
const entries = loadAllEntries(hippoRoot);
|
|
@@ -395,41 +436,8 @@ export async function embedAll(hippoRoot, model, provider = resolveEmbeddingProv
|
|
|
395
436
|
deleteOrphanVectors(db);
|
|
396
437
|
return storedVectorIds(db);
|
|
397
438
|
});
|
|
398
|
-
// Embed entries without a cached vector in save-checkpointed chunks.
|
|
399
|
-
// provider.embed batches internally (one HTTP request per batchSize for API
|
|
400
|
-
// providers; sequential for local). A `[]` row means that single item could
|
|
401
|
-
// not be embedded and is left for a later run (resumable). On a hard provider
|
|
402
|
-
// failure mid-backfill we persist the chunks already embedded this run rather
|
|
403
|
-
// than discarding paid progress, then stop and resume on the next run.
|
|
404
439
|
const pending = entries.filter((e) => !embedded.has(e.id));
|
|
405
|
-
|
|
406
|
-
// Initialized to `undefined` (not a known-evidence literal like `null`) so
|
|
407
|
-
// it stays a plain `unknown` binding for the arbitrary caught value below;
|
|
408
|
-
// falsy either way, so `if (backfillError)` behaves identically.
|
|
409
|
-
let backfillError = undefined;
|
|
410
|
-
const SAVE_CHUNK = 64;
|
|
411
|
-
for (let i = 0; i < pending.length; i += SAVE_CHUNK) {
|
|
412
|
-
const chunk = pending.slice(i, i + SAVE_CHUNK);
|
|
413
|
-
let vectors;
|
|
414
|
-
try {
|
|
415
|
-
vectors = await provider.embed(chunk.map((e) => embeddingInputText(e)), 'passage');
|
|
416
|
-
}
|
|
417
|
-
catch (err) {
|
|
418
|
-
// Preserve the chunks already saved this run, then surface the failure
|
|
419
|
-
// below so the explicit `hippo embed` path never reports a false success.
|
|
420
|
-
backfillError = err;
|
|
421
|
-
break;
|
|
422
|
-
}
|
|
423
|
-
const rows = [];
|
|
424
|
-
for (let j = 0; j < chunk.length; j++) {
|
|
425
|
-
const vec = vectors[j];
|
|
426
|
-
if (vec && vec.length > 0)
|
|
427
|
-
rows.push([chunk[j].id, vec]);
|
|
428
|
-
else
|
|
429
|
-
noteSkippedEmbedding(chunk[j].id);
|
|
430
|
-
}
|
|
431
|
-
count += withVectorDb(hippoRoot, (db) => upsertVectors(db, rows, model));
|
|
432
|
-
}
|
|
440
|
+
const { count, backfillError } = await backfillPending(hippoRoot, provider, pending, model);
|
|
433
441
|
saveStoredEmbeddingModel(hippoRoot, identity);
|
|
434
442
|
// Partial progress is now persisted; surface a hard backfill failure so the
|
|
435
443
|
// explicit embed path reports it (best-effort callers go via embedMemory).
|
package/dist/env.d.ts
CHANGED
|
@@ -35,7 +35,7 @@ export declare function envTenant(): string;
|
|
|
35
35
|
export declare function envSkipAutoIntegrations(): boolean;
|
|
36
36
|
export declare function envSkipPostinstall(): boolean;
|
|
37
37
|
export declare function envMcpStdio(): boolean;
|
|
38
|
-
/**
|
|
38
|
+
/** `1` or `true`, so `0` and `false` turn it off; `true` stays on for anyone who set it that way. */
|
|
39
39
|
export declare function envRequireServer(): boolean;
|
|
40
40
|
export declare function envRequireSessionScopedFreshTail(): boolean;
|
|
41
41
|
export declare function envStdinWaitMs(): number | undefined;
|
package/dist/env.js
CHANGED
|
@@ -15,9 +15,9 @@ function positiveInt(name) {
|
|
|
15
15
|
const parsed = Number.parseInt(raw(name) ?? '', 10);
|
|
16
16
|
return parsed > 0 ? parsed : undefined;
|
|
17
17
|
}
|
|
18
|
-
/**
|
|
19
|
-
function
|
|
20
|
-
return
|
|
18
|
+
/** Trimmed, with an empty or whitespace-only value read as unset. */
|
|
19
|
+
function trimmed(name) {
|
|
20
|
+
return raw(name)?.trim() || undefined;
|
|
21
21
|
}
|
|
22
22
|
/** The whole environment, for code that takes it as an injectable map (agent-memory machines, owner checks, support bundles). */
|
|
23
23
|
export function processEnv() {
|
|
@@ -61,10 +61,10 @@ export function envHippoHome() { return raw('HIPPO_HOME')?.trim(); }
|
|
|
61
61
|
export function envXdgDataHome() { return raw('XDG_DATA_HOME')?.trim(); }
|
|
62
62
|
export function envHomeDir() { return raw('HOME') || raw('USERPROFILE'); }
|
|
63
63
|
export function envPath() { return raw('PATH'); }
|
|
64
|
-
export function envModelCache() { return
|
|
64
|
+
export function envModelCache() { return trimmed('HIPPO_MODEL_CACHE'); }
|
|
65
65
|
// Session, scope and tenant.
|
|
66
|
-
export function envHippoSessionId() { return
|
|
67
|
-
export function envClaudeCodeSessionId() { return
|
|
66
|
+
export function envHippoSessionId() { return trimmed('HIPPO_SESSION_ID'); }
|
|
67
|
+
export function envClaudeCodeSessionId() { return trimmed('CLAUDE_CODE_SESSION_ID'); }
|
|
68
68
|
export function envScope() { return raw('HIPPO_SCOPE'); }
|
|
69
69
|
export function envGstackSkill() { return raw('GSTACK_SKILL'); }
|
|
70
70
|
export function envOpenclawSkill() { return raw('OPENCLAW_SKILL'); }
|
|
@@ -76,8 +76,8 @@ export function envTenant() {
|
|
|
76
76
|
export function envSkipAutoIntegrations() { return isOne('HIPPO_SKIP_AUTO_INTEGRATIONS'); }
|
|
77
77
|
export function envSkipPostinstall() { return isOne('HIPPO_SKIP_POSTINSTALL'); }
|
|
78
78
|
export function envMcpStdio() { return isOne('HIPPO_MCP_STDIO'); }
|
|
79
|
-
/**
|
|
80
|
-
export function envRequireServer() { return
|
|
79
|
+
/** `1` or `true`, so `0` and `false` turn it off; `true` stays on for anyone who set it that way. */
|
|
80
|
+
export function envRequireServer() { return isOneOrTrue('HIPPO_REQUIRE_SERVER'); }
|
|
81
81
|
export function envRequireSessionScopedFreshTail() { return isOne('HIPPO_REQUIRE_SESSION_SCOPED_FRESH_TAIL'); }
|
|
82
82
|
export function envStdinWaitMs() { return positiveInt('HIPPO_STDIN_WAIT_MS'); }
|
|
83
83
|
export function envLogLevel() { return raw('HIPPO_LOG')?.trim().toLowerCase() ?? ''; }
|
|
@@ -89,18 +89,18 @@ export function envApiKey() { return raw('HIPPO_API_KEY'); }
|
|
|
89
89
|
export function envClientIpHeader() { return raw('HIPPO_CLIENT_IP_HEADER')?.trim().toLowerCase(); }
|
|
90
90
|
export function envTrustedProxies() { return raw('HIPPO_TRUSTED_PROXIES'); }
|
|
91
91
|
export function envMcpSseMaxStreams() { return positiveInt('MCP_SSE_MAX_STREAMS'); }
|
|
92
|
-
export function envMcpSseHeartbeatMs() { return
|
|
93
|
-
export function envMcpSseMaxAgeSec() { return
|
|
92
|
+
export function envMcpSseHeartbeatMs() { return positiveInt('MCP_SSE_HEARTBEAT_MS'); }
|
|
93
|
+
export function envMcpSseMaxAgeSec() { return positiveInt('MCP_SSE_MAX_AGE_SEC'); }
|
|
94
94
|
// LLM and reranker credentials and knobs.
|
|
95
95
|
export function envAnthropicApiKey() { return raw('ANTHROPIC_API_KEY'); }
|
|
96
96
|
export function envLlmTimeoutMs() { return positiveInt('HIPPO_LLM_TIMEOUT_MS'); }
|
|
97
|
-
export function envTypesafeApiKey() { return
|
|
97
|
+
export function envTypesafeApiKey() { return trimmed('TYPESAFE_API_KEY'); }
|
|
98
98
|
export function envJevTimeoutMs() { return positiveInt('HIPPO_JEV_TIMEOUT_MS'); }
|
|
99
99
|
export function envJevModel() { return raw('HIPPO_JEV_MODEL'); }
|
|
100
100
|
export function envLlmRerankerUrl() { return raw('HIPPO_LLM_RERANKER_URL'); }
|
|
101
101
|
export function envLlmRerankerKey() { return raw('HIPPO_LLM_RERANKER_KEY'); }
|
|
102
102
|
export function envLlmRerankerModel() { return raw('HIPPO_LLM_RERANKER_MODEL'); }
|
|
103
|
-
export function envLlmRerankerTimeoutMs() { return
|
|
103
|
+
export function envLlmRerankerTimeoutMs() { return positiveInt('HIPPO_LLM_RERANKER_TIMEOUT_MS'); }
|
|
104
104
|
export function envClefEndpoint() { return raw('HIPPO_CLEF_ENDPOINT')?.trim(); }
|
|
105
105
|
export function envClefEndpointToken() { return raw('HIPPO_CLEF_ENDPOINT_TOKEN')?.trim() || undefined; }
|
|
106
106
|
export function envClefTimeoutMs() { return raw('HIPPO_CLEF_TIMEOUT_MS'); }
|
package/dist/escape.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Escape LIKE metacharacters for a query that declares `ESCAPE '\'`. */
|
|
2
|
+
export declare function escapeLike(term: string): string;
|
|
3
|
+
/** Escape a string for use as a literal inside a RegExp source. */
|
|
4
|
+
export declare function escapeRegex(s: string): string;
|
|
5
|
+
//# sourceMappingURL=escape.d.ts.map
|
package/dist/escape.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Leaf module: literal-match escapes, kept in one place so every LIKE and RegExp caller escapes the same set.
|
|
2
|
+
/** Escape LIKE metacharacters for a query that declares `ESCAPE '\'`. */
|
|
3
|
+
export function escapeLike(term) {
|
|
4
|
+
return term.replace(/[%_\\]/g, '\\$&');
|
|
5
|
+
}
|
|
6
|
+
/** Escape a string for use as a literal inside a RegExp source. */
|
|
7
|
+
export function escapeRegex(s) {
|
|
8
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=escape.js.map
|
package/dist/eval-stats.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Statistics and cost accounting for the token-efficiency evals
|
|
3
|
-
* Part IX, TE3-TE5).
|
|
2
|
+
* Statistics and cost accounting for the token-efficiency evals.
|
|
4
3
|
*
|
|
5
4
|
* - Cost: price provider usage over four buckets (uncached input, cache
|
|
6
5
|
* write, cache read, output). Raw token counts overstate savings when most
|
package/dist/eval-stats.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Statistics and cost accounting for the token-efficiency evals
|
|
3
|
-
* Part IX, TE3-TE5).
|
|
2
|
+
* Statistics and cost accounting for the token-efficiency evals.
|
|
4
3
|
*
|
|
5
4
|
* - Cost: price provider usage over four buckets (uncached input, cache
|
|
6
5
|
* write, cache read, output). Raw token counts overstate savings when most
|
package/dist/eval-suite.js
CHANGED
|
@@ -37,17 +37,13 @@ function mem(id, content, opts = {}) {
|
|
|
37
37
|
entry.created = opts.created;
|
|
38
38
|
return entry;
|
|
39
39
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const cases = [];
|
|
43
|
-
// =========================================================================
|
|
44
|
-
// 1. DIRECT RECALL — basic keyword matching
|
|
45
|
-
// =========================================================================
|
|
40
|
+
/** Direct recall: basic keyword matching. */
|
|
41
|
+
function addDirectRecall(entries, cases) {
|
|
46
42
|
entries.push(mem('dr-1', 'The PostgreSQL database migration failed because the users table had a NOT NULL constraint on the email column', { created: dateOffset(1), tags: ['topic:database'] }), mem('dr-2', 'React component rendering performance improved by 40% after memoizing the expensive computation in useMemo', { created: dateOffset(2), tags: ['topic:frontend'] }), mem('dr-3', 'The API rate limiter should use a sliding window algorithm instead of fixed window to prevent burst traffic', { created: dateOffset(3), tags: ['topic:api'] }), mem('dr-4', 'Docker container memory limits need to be set to 512MB for the worker service to prevent OOM kills', { created: dateOffset(4), tags: ['topic:devops'] }), mem('dr-5', 'The JWT token expiration was set to 24 hours but should be reduced to 1 hour for security compliance', { created: dateOffset(5), tags: ['topic:security'] }), mem('dr-6', 'Webpack bundle size grew to 2.3MB because lodash was imported as a whole instead of cherry-picking', { created: dateOffset(6), tags: ['topic:frontend'] }), mem('dr-7', 'The Redis cache TTL for user sessions should match the JWT expiration to prevent stale sessions', { created: dateOffset(7), tags: ['topic:caching'] }), mem('dr-8', 'GraphQL resolver for nested comments has N+1 query problem solved by DataLoader batching', { created: dateOffset(8), tags: ['topic:api'] }));
|
|
47
43
|
cases.push({ id: 'dr-q1', category: 'direct-recall', query: 'PostgreSQL migration NOT NULL constraint', expectedIds: ['dr-1'], description: 'exact keyword match on DB migration' }, { id: 'dr-q2', category: 'direct-recall', query: 'React useMemo performance memoizing', expectedIds: ['dr-2'], description: 'React performance optimization' }, { id: 'dr-q3', category: 'direct-recall', query: 'sliding window rate limiter API', expectedIds: ['dr-3'], description: 'API rate limiting approach' }, { id: 'dr-q4', category: 'direct-recall', query: 'Docker OOM memory limits worker', expectedIds: ['dr-4'], description: 'Docker memory config' }, { id: 'dr-q5', category: 'direct-recall', query: 'JWT token expiration security', expectedIds: ['dr-5'], description: 'JWT security setting' }, { id: 'dr-q6', category: 'direct-recall', query: 'webpack bundle size lodash', expectedIds: ['dr-6'], description: 'bundle size issue' }, { id: 'dr-q7', category: 'direct-recall', query: 'Redis cache TTL session expiration', expectedIds: ['dr-7'], description: 'cache TTL config' }, { id: 'dr-q8', category: 'direct-recall', query: 'GraphQL N+1 DataLoader batching', expectedIds: ['dr-8'], description: 'GraphQL N+1 fix' });
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
44
|
+
}
|
|
45
|
+
/** Extraction preference: extracted facts should rank above raw source. */
|
|
46
|
+
function addExtractionPreference(entries, cases) {
|
|
51
47
|
entries.push(mem('ep-src-1', 'speaker:Alice: So we had this big meeting yesterday about the deployment pipeline and Bob mentioned that the staging environment is using Kubernetes 1.28 and we should upgrade to 1.30 before the end of Q2 because of the security patches', {
|
|
52
48
|
created: dateOffset(10), tags: ['speaker:Alice', 'topic:infrastructure', 'session:meeting-1'],
|
|
53
49
|
}), mem('ep-ext-1', 'The staging environment runs Kubernetes 1.28 and needs to be upgraded to 1.30 before end of Q2 for security patches', {
|
|
@@ -65,9 +61,9 @@ export function buildSyntheticCorpus() {
|
|
|
65
61
|
extracted_from: 'ep-src-3',
|
|
66
62
|
}));
|
|
67
63
|
cases.push({ id: 'ep-q1', category: 'extraction-preference', query: 'Kubernetes upgrade staging environment', expectedIds: ['ep-ext-1'], description: 'extracted fact about K8s upgrade should rank above raw utterance' }, { id: 'ep-q2', category: 'extraction-preference', query: 'Datadog Grafana alerting migration', expectedIds: ['ep-ext-2'], description: 'extracted fact about monitoring switch' }, { id: 'ep-q3', category: 'extraction-preference', query: 'OAuth2 PKCE login page redesign', expectedIds: ['ep-ext-3'], description: 'extracted fact about auth work' });
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
64
|
+
}
|
|
65
|
+
/** Dag drill-down: summary nodes should surface children. */
|
|
66
|
+
function addDagDrilldown(entries, cases) {
|
|
71
67
|
entries.push(mem('dag-child-1', 'The API response time for /users endpoint degraded from 50ms to 300ms after adding the permissions check', {
|
|
72
68
|
created: dateOffset(15), layer: Layer.Semantic, tags: ['topic:api-performance', 'extracted'],
|
|
73
69
|
dag_level: 1,
|
|
@@ -86,14 +82,14 @@ export function buildSyntheticCorpus() {
|
|
|
86
82
|
child.dag_parent_id = 'dag-summary-1';
|
|
87
83
|
}
|
|
88
84
|
cases.push({ id: 'dag-q1', category: 'dag-drilldown', query: 'API performance problems latency', expectedIds: ['dag-child-1', 'dag-child-2', 'dag-child-3', 'dag-summary-1'], description: 'summary should drill down to all children' }, { id: 'dag-q2', category: 'dag-drilldown', query: 'endpoint response time degradation', expectedIds: ['dag-child-1', 'dag-child-2', 'dag-summary-1'], description: 'query matching summary should surface relevant children' });
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
85
|
+
}
|
|
86
|
+
/** Temporal: recency/oldest cues should affect ranking. */
|
|
87
|
+
function addTemporal(entries, cases) {
|
|
92
88
|
entries.push(mem('tmp-1', 'The team decided to use TypeScript for the new service', { created: dateOffset(-30), tags: ['topic:architecture'] }), mem('tmp-2', 'The team evaluated Rust as an alternative language for the service', { created: dateOffset(-20), tags: ['topic:architecture'] }), mem('tmp-3', 'The team added Go as a candidate language for the service rewrite', { created: dateOffset(-10), tags: ['topic:architecture'] }), mem('tmp-4', 'The team finalized the language choice as Go for the service rewrite', { created: dateOffset(-1), tags: ['topic:architecture'] }));
|
|
93
89
|
cases.push({ id: 'tmp-q1', category: 'temporal', query: 'what did the team recently decide about the service language', expectedIds: ['tmp-4'], description: 'recent cue should boost newest entry' }, { id: 'tmp-q2', category: 'temporal', query: 'what was the first language choice for the service', expectedIds: ['tmp-1'], description: 'oldest cue should boost earliest entry' }, { id: 'tmp-q3', category: 'temporal', query: 'latest update on the service rewrite language', expectedIds: ['tmp-4'], description: 'latest should boost most recent' }, { id: 'tmp-q4', category: 'temporal', query: 'original architecture decision for the service', expectedIds: ['tmp-1'], description: 'original should boost earliest' });
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
90
|
+
}
|
|
91
|
+
/** Noise resistance: relevant memories found despite noise. */
|
|
92
|
+
function addNoiseResistance(entries, cases) {
|
|
97
93
|
// Add 30 noise entries
|
|
98
94
|
const noiseTopics = [
|
|
99
95
|
'breakfast meeting catering ordered sandwiches', 'office temperature thermostat adjusted',
|
|
@@ -121,9 +117,9 @@ export function buildSyntheticCorpus() {
|
|
|
121
117
|
created: dateOffset(21), tags: ['topic:security', 'topic:s3'],
|
|
122
118
|
}));
|
|
123
119
|
cases.push({ id: 'nr-q1', category: 'noise-resistance', query: 'database connection pool exhausted max connections', expectedIds: ['nr-1'], description: 'find DB issue despite 30 noise entries' }, { id: 'nr-q2', category: 'noise-resistance', query: 'S3 bucket public access security misconfiguration', expectedIds: ['nr-2'], description: 'find S3 issue despite noise' }, { id: 'nr-q3', category: 'noise-resistance', query: 'PostgreSQL migration constraint', expectedIds: ['dr-1'], description: 'find earlier DB entry despite noise' }, { id: 'nr-q4', category: 'noise-resistance', query: 'JWT token security expiration', expectedIds: ['dr-5'], description: 'find security entry despite noise' });
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
120
|
+
}
|
|
121
|
+
/** Multi-hop: entity chaining across sessions. */
|
|
122
|
+
function addMultiHop(entries, cases) {
|
|
127
123
|
entries.push(mem('mh-1', 'speaker:Alice works on the payment gateway integration with Stripe. She found a webhook signature validation bug.', {
|
|
128
124
|
created: dateOffset(25), tags: ['speaker:Alice', 'topic:payments'],
|
|
129
125
|
}), mem('mh-2', 'speaker:Bob reviewed the Stripe webhook code and confirmed the signature validation uses the wrong secret key from the test environment.', {
|
|
@@ -134,6 +130,16 @@ export function buildSyntheticCorpus() {
|
|
|
134
130
|
created: dateOffset(28), tags: ['speaker:Carol', 'topic:billing'],
|
|
135
131
|
}));
|
|
136
132
|
cases.push({ id: 'mh-q1', category: 'multi-hop', query: 'Who fixed the Stripe webhook bug and what was the root cause?', expectedIds: ['mh-1', 'mh-2', 'mh-3'], description: 'chain Alice -> Stripe -> Bob -> fix' }, { id: 'mh-q2', category: 'multi-hop', query: 'What are all the payment-related issues the team discussed?', expectedIds: ['mh-1', 'mh-2', 'mh-3', 'mh-4'], description: 'find all payment topics across speakers' });
|
|
133
|
+
}
|
|
134
|
+
export function buildSyntheticCorpus() {
|
|
135
|
+
const entries = [];
|
|
136
|
+
const cases = [];
|
|
137
|
+
addDirectRecall(entries, cases);
|
|
138
|
+
addExtractionPreference(entries, cases);
|
|
139
|
+
addDagDrilldown(entries, cases);
|
|
140
|
+
addTemporal(entries, cases);
|
|
141
|
+
addNoiseResistance(entries, cases);
|
|
142
|
+
addMultiHop(entries, cases);
|
|
137
143
|
return { entries, cases };
|
|
138
144
|
}
|
|
139
145
|
// ---------------------------------------------------------------------------
|
package/dist/extract.js
CHANGED
|
@@ -6,9 +6,7 @@ import { redactSecretsStrict } from './secret-detect.js';
|
|
|
6
6
|
import { fetchWithRetry, llmTimeoutMs } from './http-retry.js';
|
|
7
7
|
import { neverAutoShareTags } from './shared.js';
|
|
8
8
|
import { log } from './log.js';
|
|
9
|
-
|
|
10
|
-
return typeof value === 'string';
|
|
11
|
-
}
|
|
9
|
+
import { isJsonString } from './json.js';
|
|
12
10
|
const EXTRACTION_PROMPT = `You are extracting factual statements from a conversation or memory entry. Extract 1-8 standalone factual statements that would be useful to remember later.
|
|
13
11
|
|
|
14
12
|
Rules:
|
|
@@ -101,15 +99,12 @@ export function storeExtractedFacts(hippoRoot, source, facts) {
|
|
|
101
99
|
source: source.source,
|
|
102
100
|
extracted_from: source.id,
|
|
103
101
|
scope: source.scope,
|
|
104
|
-
//
|
|
105
|
-
//
|
|
106
|
-
// option stamps 'default' (memory.ts:535) regardless of the source
|
|
107
|
-
// entry's own tenant. Thread it through so extracted facts land in
|
|
108
|
-
// the same tenant as the episodic memory they were extracted from.
|
|
102
|
+
// Without it createMemory stamps 'default', and extracted facts leave
|
|
103
|
+
// the tenant of the episodic memory they were extracted from.
|
|
109
104
|
tenantId: source.tenantId,
|
|
110
105
|
baseHalfLifeDays,
|
|
111
106
|
}), origin_project: source.origin_project };
|
|
112
|
-
//
|
|
107
|
+
// A refusal is per-VALUE: one rejected fact must not
|
|
113
108
|
// drop the rest of this batch. writeEntry has already audited the
|
|
114
109
|
// refusal (reject_refusal) before rethrowing, so skip-and-count here.
|
|
115
110
|
try {
|
package/dist/failure-log.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** Failure log
|
|
1
|
+
/** Failure log: every failed tool call the capture-error hook sees, stored or not. */
|
|
2
2
|
import type { CaptureErrorOutcome, RoutineRule } from './capture-error.js';
|
|
3
3
|
import type { DatabaseSyncLike } from './db.js';
|
|
4
4
|
/** Rows older than this are pruned on write, which also bounds how far back a repeat can be found. */
|
|
@@ -31,9 +31,9 @@ export interface SessionFailures {
|
|
|
31
31
|
/** Of those, failures whose signature another session hit first. */
|
|
32
32
|
repeats: number;
|
|
33
33
|
}
|
|
34
|
-
/** Rated failures per session since `sinceIso`, the input for repeat-error rate per arm
|
|
34
|
+
/** Rated failures per session since `sinceIso`, the input for repeat-error rate per arm. */
|
|
35
35
|
export declare function failuresBySession(db: DatabaseSyncLike, tenantId: string, sinceIso: string): SessionFailures[];
|
|
36
|
-
/** Failure log totals over a window, for {@link summarizeFailures}. Counts only: a rate needs a holdout arm
|
|
36
|
+
/** Failure log totals over a window, for {@link summarizeFailures}. Counts only: a rate needs a holdout arm. */
|
|
37
37
|
export interface FailureSummary {
|
|
38
38
|
/** ISO start of the window (inclusive). */
|
|
39
39
|
since: string;
|
package/dist/failure-log.js
CHANGED
|
@@ -11,7 +11,7 @@ export function recordFailure(db, event) {
|
|
|
11
11
|
const cutoff = new Date(Date.parse(now) - FAILURE_LOG_RETENTION_DAYS * 86_400_000).toISOString();
|
|
12
12
|
db.prepare(`DELETE FROM failure_log WHERE ts < ?`).run(cutoff);
|
|
13
13
|
}
|
|
14
|
-
/** Rated failures per session since `sinceIso`, the input for repeat-error rate per arm
|
|
14
|
+
/** Rated failures per session since `sinceIso`, the input for repeat-error rate per arm. */
|
|
15
15
|
export function failuresBySession(db, tenantId, sinceIso) {
|
|
16
16
|
// SAFETY: the SELECT names exactly these three TEXT columns.
|
|
17
17
|
const rows = db.prepare(`SELECT ts, session_id, sig_hash FROM failure_log
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Forward-claim detector: pure-function regex set + token extraction.
|
|
3
3
|
*
|
|
4
4
|
* No DB. No state. No dependencies beyond stdlib regex + String.
|
|
5
5
|
*
|
|
@@ -13,9 +13,7 @@
|
|
|
13
13
|
* Iteration signal: the `recall_autodebias_hint_no_class_match` audit op
|
|
14
14
|
* (emitted by computePlanningFallacyOutput when a phrase matches but no class
|
|
15
15
|
* resolves) is the telemetry channel for deciding whether to add an
|
|
16
|
-
* embedding-based detector
|
|
17
|
-
*
|
|
18
|
-
* Plan: docs/plans/2026-05-26-j32-auto-injection.md (Task 1).
|
|
16
|
+
* embedding-based detector.
|
|
19
17
|
*/
|
|
20
18
|
export interface ForwardClaimMatch {
|
|
21
19
|
/** The regex match snippet, e.g. "will take" or "~3 days". Surfaced
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Forward-claim detector: pure-function regex set + token extraction.
|
|
3
3
|
*
|
|
4
4
|
* No DB. No state. No dependencies beyond stdlib regex + String.
|
|
5
5
|
*
|
|
@@ -13,12 +13,10 @@
|
|
|
13
13
|
* Iteration signal: the `recall_autodebias_hint_no_class_match` audit op
|
|
14
14
|
* (emitted by computePlanningFallacyOutput when a phrase matches but no class
|
|
15
15
|
* resolves) is the telemetry channel for deciding whether to add an
|
|
16
|
-
* embedding-based detector
|
|
17
|
-
*
|
|
18
|
-
* Plan: docs/plans/2026-05-26-j32-auto-injection.md (Task 1).
|
|
16
|
+
* embedding-based detector.
|
|
19
17
|
*/
|
|
20
18
|
/**
|
|
21
|
-
*
|
|
19
|
+
* Each pattern is intentionally narrow:
|
|
22
20
|
* adding a pattern should require evidence from the
|
|
23
21
|
* `recall_autodebias_hint_no_class_match` audit channel that real forward-
|
|
24
22
|
* claims are slipping through, NOT speculative "this might match more".
|
|
@@ -29,8 +27,7 @@
|
|
|
29
27
|
*/
|
|
30
28
|
// Reused fragment for ALL duration-suffix patterns. Requires a digit + unit
|
|
31
29
|
// so 'will take ownership' / 'ship in Docker' (no time component) don't
|
|
32
|
-
// match
|
|
33
|
-
// on every-day non-estimate queries that happened to share a class-token.
|
|
30
|
+
// match: verb-only patterns fire on everyday non-estimate queries that share a class-token.
|
|
34
31
|
// Accepts `<N> unit` (3 days), `a/an unit` (a week — implicit 1), and
|
|
35
32
|
// `one unit` so natural English forward-claims aren't lost.
|
|
36
33
|
const DURATION_TAIL = String.raw `(?:about|around|~|≈)?\s*(?:\d+|a|an|one)\s*(?:day|week|month|hour|hr|min(?:ute)?|sec(?:ond)?)s?\b`;
|
|
@@ -51,8 +48,7 @@ const FORWARD_CLAIM_PATTERNS = [
|
|
|
51
48
|
// Lookbehind asserts start-of-string OR whitespace before the tilde
|
|
52
49
|
// because \b before ~ requires a word char immediately preceding (~ is
|
|
53
50
|
// not a word character), so /\b~/ would only match in 'foo~3 days'
|
|
54
|
-
// (malformed) and silently miss the legitimate cases.
|
|
55
|
-
// round 1 catch.
|
|
51
|
+
// (malformed) and silently miss the legitimate cases.
|
|
56
52
|
/(?<=^|\s)~\s*\d+\s*(?:day|week|month|hour)s?\b/i,
|
|
57
53
|
// Should + verb + duration tail. Same tightening as the leading 'will
|
|
58
54
|
// take' rule: 'should ship by EOD' must include a quantifier.
|
|
@@ -78,8 +74,7 @@ const STOP_WORDS = new Set([
|
|
|
78
74
|
// Duration units appear in every forward-claim phrase by design (the
|
|
79
75
|
// regex set REQUIRES a `<N> <unit>` quantifier after the verb). Letting
|
|
80
76
|
// them through to class resolution lets a class tag containing 'days'
|
|
81
|
-
// win or tie on the unit token instead of the domain token.
|
|
82
|
-
// 2 P3 catch.
|
|
77
|
+
// win or tie on the unit token instead of the domain token.
|
|
83
78
|
'day', 'days', 'week', 'weeks', 'month', 'months',
|
|
84
79
|
'hour', 'hours', 'hr', 'hrs',
|
|
85
80
|
'min', 'mins', 'minute', 'minutes', 'sec', 'secs', 'second', 'seconds',
|
package/dist/goals.d.ts
CHANGED
|
@@ -60,7 +60,7 @@ export interface PushGoalOpts {
|
|
|
60
60
|
}
|
|
61
61
|
export declare function pushGoal(hippoRoot: string, opts: PushGoalOpts): Goal;
|
|
62
62
|
/**
|
|
63
|
-
*
|
|
63
|
+
* Depth-cap enforcer shared by pushGoalWithDb and resumeGoal.
|
|
64
64
|
* If the (tenant, session) has >= MAX_ACTIVE_GOAL_DEPTH active goals,
|
|
65
65
|
* suspend the oldest `overflow` ones.
|
|
66
66
|
*
|
|
@@ -69,7 +69,7 @@ export declare function pushGoal(hippoRoot: string, opts: PushGoalOpts): Goal;
|
|
|
69
69
|
* is impossible to misread the contract at a call site. Both existing call
|
|
70
70
|
* sites (pushGoalWithDb, resumeGoal) wrap in `BEGIN IMMEDIATE` already.
|
|
71
71
|
*
|
|
72
|
-
* @internal
|
|
72
|
+
* @internal Internal goal-stack invariant. Subject to change.
|
|
73
73
|
*/
|
|
74
74
|
export declare function enforceDepthCapWithinTx(db: DatabaseSyncLike, tenantId: string, sessionId: string): void;
|
|
75
75
|
export declare function pushGoalWithDb(db: DatabaseSyncLike, opts: PushGoalOpts): Goal;
|
|
@@ -148,7 +148,7 @@ export declare function applyGoalStackBoost<R extends {
|
|
|
148
148
|
export interface CompleteGoalOpts {
|
|
149
149
|
outcomeScore?: number;
|
|
150
150
|
/**
|
|
151
|
-
*
|
|
151
|
+
* When true, skip the strength-multiplier propagation block.
|
|
152
152
|
* Default false (propagate). The goal's status still transitions to
|
|
153
153
|
* 'completed' and `outcome_score` is still recorded; only the side-effect
|
|
154
154
|
* on recalled memories' strength is suppressed.
|