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/graph-view.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* graph
|
|
2
|
+
* Graph observability and visualization, READ-ONLY over the entity/relation
|
|
3
|
+
* graph.
|
|
4
4
|
*
|
|
5
5
|
* This module only READS the graph (`loadEntities` / `loadRelations`) and renders
|
|
6
6
|
* view-models; it issues no INSERT/UPDATE/DELETE, so `scripts/check-graph-writes.mjs`
|
package/dist/graph-view.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* graph
|
|
2
|
+
* Graph observability and visualization, READ-ONLY over the entity/relation
|
|
3
|
+
* graph.
|
|
4
4
|
*
|
|
5
5
|
* This module only READS the graph (`loadEntities` / `loadRelations`) and renders
|
|
6
6
|
* view-models; it issues no INSERT/UPDATE/DELETE, so `scripts/check-graph-writes.mjs`
|
|
@@ -20,7 +20,7 @@ export function buildGraphModel(hippoRoot, tenantId, opts = {}) {
|
|
|
20
20
|
const limit = opts.limit ?? DEFAULT_VIEW_LIMIT;
|
|
21
21
|
// All reads run inside ONE read snapshot so a concurrent `graph extract` /
|
|
22
22
|
// sleep-drain rebuild can't make the model mix old entity ids with new relation
|
|
23
|
-
// ids
|
|
23
|
+
// ids. Every load* call below is passed the snapshot connection `db`.
|
|
24
24
|
return withGraphReadSnapshot(hippoRoot, (db) => {
|
|
25
25
|
let nodeEntities;
|
|
26
26
|
let relations;
|
|
@@ -31,7 +31,7 @@ export function buildGraphModel(hippoRoot, tenantId, opts = {}) {
|
|
|
31
31
|
// (2) A name can map to MANY entities (e.g. many notes for one customer), so
|
|
32
32
|
// cap the focus matches. (3) Discover 1-hop neighbours and cap the UNION to
|
|
33
33
|
// `limit` nodes. (4) Load ALL edges AMONG the union so neighbour-to-neighbour
|
|
34
|
-
// edges that don't touch the focus are included too.
|
|
34
|
+
// edges that don't touch the focus are included too.
|
|
35
35
|
const focus = loadEntitiesByName(hippoRoot, tenantId, opts.entity, { limit }, db);
|
|
36
36
|
if (focus.length === 0)
|
|
37
37
|
return { nodes: [], edges: [], truncated: false };
|
|
@@ -51,12 +51,12 @@ export function buildGraphModel(hippoRoot, tenantId, opts = {}) {
|
|
|
51
51
|
nodeEntities = loadEntitiesByIds(hippoRoot, tenantId, unionIds, db);
|
|
52
52
|
// Edges AMONG the union (BOTH endpoints in the set): includes
|
|
53
53
|
// neighbour-to-neighbour edges, and the LIMIT can never drop a valid in-union
|
|
54
|
-
// edge in favour of out-of-union rows
|
|
54
|
+
// edge in favour of out-of-union rows.
|
|
55
55
|
relations = loadRelationsAmong(hippoRoot, tenantId, unionIds, { limit }, db);
|
|
56
56
|
truncated =
|
|
57
57
|
focus.length >= limit ||
|
|
58
|
-
hop.length >= limit || // neighbour scan capped -> a 1-hop neighbour may be omitted
|
|
59
|
-
neighboursCapped || // node cap filled before all neighbours were consumed
|
|
58
|
+
hop.length >= limit || // neighbour scan capped -> a 1-hop neighbour may be omitted
|
|
59
|
+
neighboursCapped || // node cap filled before all neighbours were consumed
|
|
60
60
|
union.size > unionIds.length ||
|
|
61
61
|
relations.length >= limit;
|
|
62
62
|
}
|
|
@@ -5,8 +5,7 @@
|
|
|
5
5
|
* default base and a few write-time multipliers (`deriveHalfLife`). Changing
|
|
6
6
|
* the default therefore reaches only new memories; without this migration a
|
|
7
7
|
* store would mix old-base and new-base memories, a state the decay
|
|
8
|
-
* evaluation never tested
|
|
9
|
-
* Migration). The rule, declared there before any run:
|
|
8
|
+
* evaluation never tested. The rule, declared before any run:
|
|
10
9
|
*
|
|
11
10
|
* - only a memory still on the old base is rescaled: its half-life is
|
|
12
11
|
* `deriveHalfLife(from, entry)` plus its recall bonus. A memory hippo shortened since
|
|
@@ -5,8 +5,7 @@
|
|
|
5
5
|
* default base and a few write-time multipliers (`deriveHalfLife`). Changing
|
|
6
6
|
* the default therefore reaches only new memories; without this migration a
|
|
7
7
|
* store would mix old-base and new-base memories, a state the decay
|
|
8
|
-
* evaluation never tested
|
|
9
|
-
* Migration). The rule, declared there before any run:
|
|
8
|
+
* evaluation never tested. The rule, declared before any run:
|
|
10
9
|
*
|
|
11
10
|
* - only a memory still on the old base is rescaled: its half-life is
|
|
12
11
|
* `deriveHalfLife(from, entry)` plus its recall bonus. A memory hippo shortened since
|
|
@@ -151,7 +150,7 @@ function objectMemoryIds(db) {
|
|
|
151
150
|
}
|
|
152
151
|
return { all, retired };
|
|
153
152
|
}
|
|
154
|
-
/** Memories that lost a conflict, which resolveConflict halved untagged. A resolved conflict with no audit row (before
|
|
153
|
+
/** Memories that lost a conflict, which resolveConflict halved untagged. A resolved conflict with no audit row (resolved before resolves were audited, or found stale) names no winner, so both sides count. */
|
|
155
154
|
function conflictLosers(db) {
|
|
156
155
|
// SAFETY: SELECT of two fields every conflict_resolve audit row carries (ConflictResolveMeta in store.ts).
|
|
157
156
|
const audited = db.prepare(`SELECT json_extract(metadata_json, '$.conflictId') AS conflictId, json_extract(metadata_json, '$.loserId') AS loserId FROM audit_log WHERE op = 'conflict_resolve'`).all();
|
|
@@ -49,7 +49,7 @@ export declare function isCodexWrapperInstalled(): boolean;
|
|
|
49
49
|
* opt-in record. Never performs a first install. Replacing another vendor's
|
|
50
50
|
* binary must stay behind the explicit `hippo hook install codex` command;
|
|
51
51
|
* doing it from postinstall or routine commands is a consent violation and
|
|
52
|
-
* reads as binary hijacking to security scanners
|
|
52
|
+
* reads as binary hijacking to security scanners.
|
|
53
53
|
*/
|
|
54
54
|
export declare function repairCodexWrapperIfInstalled(hippoCliPath?: string): EnsureCodexWrapperResult;
|
|
55
55
|
//# sourceMappingURL=codex-wrapper.d.ts.map
|
|
@@ -2,7 +2,8 @@ import { envPath } from '../env.js';
|
|
|
2
2
|
import * as fs from 'fs';
|
|
3
3
|
import * as path from 'path';
|
|
4
4
|
import { fileURLToPath } from 'url';
|
|
5
|
-
import {
|
|
5
|
+
import { HIPPO_CODEX_WRAPPER_MARKER, homeDir, codexHomeDir, ensureDir } from './shared.js';
|
|
6
|
+
import { isJsonString } from '../json.js';
|
|
6
7
|
export function resolveCodexWrapperPaths() {
|
|
7
8
|
const home = homeDir();
|
|
8
9
|
const codexHome = codexHomeDir(home);
|
|
@@ -271,7 +272,7 @@ export function isCodexWrapperInstalled() {
|
|
|
271
272
|
* opt-in record. Never performs a first install. Replacing another vendor's
|
|
272
273
|
* binary must stay behind the explicit `hippo hook install codex` command;
|
|
273
274
|
* doing it from postinstall or routine commands is a consent violation and
|
|
274
|
-
* reads as binary hijacking to security scanners
|
|
275
|
+
* reads as binary hijacking to security scanners.
|
|
275
276
|
*/
|
|
276
277
|
export function repairCodexWrapperIfInstalled(hippoCliPath = resolveHippoCliPath()) {
|
|
277
278
|
if (readCodexWrapperMetadata() === null) {
|
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
*
|
|
24
24
|
* 2. Plugin install (OpenCode only). OpenCode does NOT share Claude Code's
|
|
25
25
|
* JSON-hook schema — its config has `additionalProperties: false` and no
|
|
26
|
-
* `hooks` key, so
|
|
27
|
-
*
|
|
26
|
+
* `hooks` key, so a JSON-hook install breaks opencode launch. Hippo
|
|
27
|
+
* installs a TypeScript plugin at
|
|
28
28
|
* `~/.config/opencode/plugins/hippo.ts` subscribing to opencode's
|
|
29
29
|
* `session.idle` (→ `hippo session-end`) and `session.created` (→
|
|
30
30
|
* `hippo last-sleep`) events. See OPENCODE_PLUGIN_SOURCE below for the
|
package/dist/hooks/json-hooks.js
CHANGED
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
*
|
|
24
24
|
* 2. Plugin install (OpenCode only). OpenCode does NOT share Claude Code's
|
|
25
25
|
* JSON-hook schema — its config has `additionalProperties: false` and no
|
|
26
|
-
* `hooks` key, so
|
|
27
|
-
*
|
|
26
|
+
* `hooks` key, so a JSON-hook install breaks opencode launch. Hippo
|
|
27
|
+
* installs a TypeScript plugin at
|
|
28
28
|
* `~/.config/opencode/plugins/hippo.ts` subscribing to opencode's
|
|
29
29
|
* `session.idle` (→ `hippo session-end`) and `session.created` (→
|
|
30
30
|
* `hippo last-sleep`) events. See OPENCODE_PLUGIN_SOURCE below for the
|
|
@@ -34,7 +34,8 @@
|
|
|
34
34
|
*/
|
|
35
35
|
import * as fs from 'fs';
|
|
36
36
|
import * as path from 'path';
|
|
37
|
-
import {
|
|
37
|
+
import { isJsonObject, HIPPO_SLEEP_MARKER, HIPPO_LAST_SLEEP_MARKER, HIPPO_CAPTURE_MARKER, HIPPO_SESSION_END_MARKER, HIPPO_PINNED_INJECT_MARKER, HIPPO_PINNED_INJECT_COMMAND, HIPPO_PRE_COMPACT_MARKER, HIPPO_COMPACT_RESUME_MARKER, HIPPO_CAPTURE_ERROR_MARKER, HIPPO_POST_COMPACT_MARKER, homeDir, codexHomeDir, defaultPreCompactLogPath } from './shared.js';
|
|
38
|
+
import { isJsonString } from '../json.js';
|
|
38
39
|
export function resolveJsonHookPaths(target) {
|
|
39
40
|
const home = homeDir();
|
|
40
41
|
const logsDir = path.join(home, '.hippo', 'logs');
|
|
@@ -91,7 +92,7 @@ function migratePinnedInjectRecentCommands(hookArray) {
|
|
|
91
92
|
}
|
|
92
93
|
/**
|
|
93
94
|
* Returns true when `hooks.SessionEnd` still contains either of the legacy
|
|
94
|
-
*
|
|
95
|
+
* split entries (bare `hippo sleep` / `hippo capture --last-session`)
|
|
95
96
|
* without the current consolidated `hippo session-end` entry.
|
|
96
97
|
*/
|
|
97
98
|
function hasLegacySplitSessionEnd(hookArray) {
|
package/dist/hooks/opencode.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ declare const HIPPO_OPENCODE_PLUGIN_MARKER = "HIPPO_OPENCODE_PLUGIN_V1";
|
|
|
7
7
|
* session.idle → `hippo session-end` (Claude Code's SessionEnd equiv)
|
|
8
8
|
* session.created → `hippo last-sleep` (Claude Code's SessionStart equiv)
|
|
9
9
|
*
|
|
10
|
-
* Design choices
|
|
10
|
+
* Design choices:
|
|
11
11
|
*
|
|
12
12
|
* 1. No `import type { Plugin } from "@opencode-ai/plugin"`. The package's npm
|
|
13
13
|
* publication status was unverifiable from the build sandbox (npmjs.com
|
package/dist/hooks/opencode.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as path from 'path';
|
|
3
|
-
import {
|
|
3
|
+
import { isJsonObject, homeDir } from './shared.js';
|
|
4
|
+
import { isJsonString } from '../json.js';
|
|
4
5
|
const HIPPO_OPENCODE_PLUGIN_MARKER = 'HIPPO_OPENCODE_PLUGIN_V1';
|
|
5
6
|
/**
|
|
6
7
|
* The opencode plugin file we install at ~/.config/opencode/plugins/hippo.ts.
|
|
@@ -10,7 +11,7 @@ const HIPPO_OPENCODE_PLUGIN_MARKER = 'HIPPO_OPENCODE_PLUGIN_V1';
|
|
|
10
11
|
* session.idle → `hippo session-end` (Claude Code's SessionEnd equiv)
|
|
11
12
|
* session.created → `hippo last-sleep` (Claude Code's SessionStart equiv)
|
|
12
13
|
*
|
|
13
|
-
* Design choices
|
|
14
|
+
* Design choices:
|
|
14
15
|
*
|
|
15
16
|
* 1. No `import type { Plugin } from "@opencode-ai/plugin"`. The package's npm
|
|
16
17
|
* publication status was unverifiable from the build sandbox (npmjs.com
|
|
@@ -79,11 +80,11 @@ function resolveOpencodeConfigPath() {
|
|
|
79
80
|
* hippo itself installs (session-end, last-sleep, sleep, capture, context),
|
|
80
81
|
* not any third-party tool that happens to be named `hippo`.
|
|
81
82
|
*
|
|
82
|
-
*
|
|
83
|
+
* Structural check: substring matching against
|
|
83
84
|
* arbitrary user content is unsafe — a user's
|
|
84
85
|
* `echo "remember to hippo sleep your laptop"` is not a hippo-owned hook.
|
|
85
86
|
*
|
|
86
|
-
* Per-hook (not per-entry) granularity
|
|
87
|
+
* Per-hook (not per-entry) granularity: an entry whose inner
|
|
87
88
|
* hooks array mixes hippo-installed commands with user-authored commands
|
|
88
89
|
* must NOT lose the user-authored commands. The migration filters the inner
|
|
89
90
|
* array per-hook, then drops the entry only when its inner array is empty.
|
package/dist/hooks/shared.d.ts
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* `typeof value === 'string'` for every value JSON.parse can ever produce. */
|
|
5
|
-
export declare function isJsonString(value: JsonValue | undefined): value is string;
|
|
6
|
-
/** JSON-value plain-object check (excludes arrays and null), typeof-free for the same
|
|
7
|
-
* reason as isJsonString above. */
|
|
1
|
+
import type { JsonObject } from '../working-memory.js';
|
|
2
|
+
import type { JsonValue } from '../json.js';
|
|
3
|
+
/** JSON-value plain-object check (excludes arrays and null); `constructor` avoids the `typeof` tag banned by anti-slop/no-runtime-typeof. */
|
|
8
4
|
export declare function isJsonObject(value: JsonValue | undefined): value is JsonObject;
|
|
9
5
|
export type JsonHookTarget = 'claude-code' | 'codex';
|
|
10
6
|
export interface ToolDetection {
|
package/dist/hooks/shared.js
CHANGED
|
@@ -2,14 +2,7 @@ import { envHomeDir, processEnv } from '../env.js';
|
|
|
2
2
|
import * as fs from 'fs';
|
|
3
3
|
import * as os from 'os';
|
|
4
4
|
import * as path from 'path';
|
|
5
|
-
/** JSON-value
|
|
6
|
-
* instead of inspecting the `typeof` tag (anti-slop/no-runtime-typeof); equivalent to
|
|
7
|
-
* `typeof value === 'string'` for every value JSON.parse can ever produce. */
|
|
8
|
-
export function isJsonString(value) {
|
|
9
|
-
return value !== undefined && value !== null && value.constructor === String;
|
|
10
|
-
}
|
|
11
|
-
/** JSON-value plain-object check (excludes arrays and null), typeof-free for the same
|
|
12
|
-
* reason as isJsonString above. */
|
|
5
|
+
/** JSON-value plain-object check (excludes arrays and null); `constructor` avoids the `typeof` tag banned by anti-slop/no-runtime-typeof. */
|
|
13
6
|
export function isJsonObject(value) {
|
|
14
7
|
return value !== undefined && value !== null && !Array.isArray(value) && value.constructor === Object;
|
|
15
8
|
}
|
package/dist/http-util.d.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
|
-
|
|
3
|
-
[key: string]: JsonValue;
|
|
4
|
-
};
|
|
2
|
+
import type { JsonValue } from './json.js';
|
|
5
3
|
export declare function isJsonObjectRecord(value: JsonValue | undefined): value is Record<string, JsonValue>;
|
|
6
4
|
export declare function isHeaderString(value: string | string[] | undefined): value is string;
|
|
5
|
+
export declare const MAX_ID_LEN = 256;
|
|
7
6
|
export declare const JSON_HEADERS: {
|
|
8
7
|
readonly 'content-type': "application/json";
|
|
9
8
|
};
|
package/dist/http-util.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ApiError } from './api-errors.js';
|
|
2
|
+
// Leaf module shared by server.ts and the connector webhook receivers; it must not import either.
|
|
2
3
|
export function isJsonObjectRecord(value) {
|
|
3
4
|
return value !== undefined && value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
4
5
|
}
|
|
@@ -10,6 +11,8 @@ export function isHeaderString(value) {
|
|
|
10
11
|
// 1 MB body cap. The CLI never sends payloads near this; anything bigger is
|
|
11
12
|
// almost certainly a misconfigured client or a deliberate memory-blowup attempt.
|
|
12
13
|
const MAX_BODY_BYTES = 1024 * 1024;
|
|
14
|
+
// Cap for id-shaped request fields (ids, tenant, session, scope, class): far above real values, small enough to bound logs and indexes.
|
|
15
|
+
export const MAX_ID_LEN = 256;
|
|
13
16
|
export const JSON_HEADERS = { 'content-type': 'application/json' };
|
|
14
17
|
export class HttpError extends Error {
|
|
15
18
|
status;
|
package/dist/importers/core.d.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Imports memories from ChatGPT, Claude, Cursor, generic files, and structured markdown.
|
|
4
4
|
*/
|
|
5
5
|
import { MemoryEntry } from '../memory.js';
|
|
6
|
+
import type { JsonValue } from '../json.js';
|
|
6
7
|
export interface ImportResult {
|
|
7
8
|
total: number;
|
|
8
9
|
imported: number;
|
|
@@ -44,7 +45,7 @@ export interface ImportOptions {
|
|
|
44
45
|
* sharing a basename would collide and clobber each other). importVault throws
|
|
45
46
|
* if it is missing or blank. Optional in this shared type only because the
|
|
46
47
|
* other importers ignore it. Operator-supplied, so the loader query LIKE-escapes
|
|
47
|
-
* it (
|
|
48
|
+
* it (`escapeLike` in src/escape.ts).
|
|
48
49
|
*/
|
|
49
50
|
name?: string;
|
|
50
51
|
/**
|
|
@@ -58,14 +59,6 @@ export interface ImportOptions {
|
|
|
58
59
|
* create MemoryEntry objects, write them (unless dry-run), and return a result.
|
|
59
60
|
*/
|
|
60
61
|
export declare function importEntries(chunks: string[], source: string, tags: string[], options: ImportOptions): ImportResult;
|
|
61
|
-
/** The full value space `JSON.parse` can produce. Boundary-guard functions in
|
|
62
|
-
* this file accept `JsonValue` (never `unknown`) so a value's origin as
|
|
63
|
-
* unparsed external JSON stays visible in its type, then narrow it via the
|
|
64
|
-
* `isJson*` predicates below. */
|
|
65
|
-
export type JsonValue = string | number | boolean | null | JsonValue[] | {
|
|
66
|
-
[key: string]: JsonValue;
|
|
67
|
-
};
|
|
68
|
-
export declare function isJsonString(x: JsonValue): x is string;
|
|
69
62
|
export declare function isJsonPlainObject(x: JsonValue): x is {
|
|
70
63
|
[key: string]: JsonValue;
|
|
71
64
|
};
|
package/dist/importers/core.js
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
* Imports memories from ChatGPT, Claude, Cursor, generic files, and structured markdown.
|
|
4
4
|
*/
|
|
5
5
|
import { createMemory, Layer } from '../memory.js';
|
|
6
|
-
import {
|
|
6
|
+
import { writeEntryOn } from '../store/entry-writes.js';
|
|
7
|
+
import { openStore } from '../store/open.js';
|
|
7
8
|
import { loadAllEntries } from '../store/entry-reads.js';
|
|
8
9
|
import { duplicateKey, storedTextKeys } from '../same-text.js';
|
|
9
10
|
import { getGlobalRoot, initGlobal } from '../shared.js';
|
|
@@ -34,12 +35,8 @@ export function importEntries(chunks, source, tags, options) {
|
|
|
34
35
|
let rejected = 0;
|
|
35
36
|
let redacted = 0;
|
|
36
37
|
const entries = [];
|
|
37
|
-
//
|
|
38
|
-
|
|
39
|
-
// `imported` even when a real run would refuse it, making the preview's
|
|
40
|
-
// `rejected` count silently 0. Probe (read-only) via the same guard
|
|
41
|
-
// writeEntry uses internally, without ever writing.
|
|
42
|
-
const dryRunDb = options.dryRun ? openHippoDb(targetRoot) : null;
|
|
38
|
+
// A dry run probes the rejection guard read-only; a real run writes every chunk on this one handle.
|
|
39
|
+
const db = options.dryRun ? openHippoDb(targetRoot) : openStore(targetRoot);
|
|
43
40
|
try {
|
|
44
41
|
for (const raw of chunks) {
|
|
45
42
|
const { chunk, wasRedacted } = prepareImportChunk(raw, allTags);
|
|
@@ -55,7 +52,7 @@ export function importEntries(chunks, source, tags, options) {
|
|
|
55
52
|
continue;
|
|
56
53
|
}
|
|
57
54
|
const entry = createImportEntry(chunk, source, allTags, options, baseHalfLifeDays);
|
|
58
|
-
if (!writeOrProbeImport(targetRoot, entry, options
|
|
55
|
+
if (!writeOrProbeImport(db, targetRoot, entry, options)) {
|
|
59
56
|
rejected++;
|
|
60
57
|
continue;
|
|
61
58
|
}
|
|
@@ -70,8 +67,7 @@ export function importEntries(chunks, source, tags, options) {
|
|
|
70
67
|
return { total, imported, skipped, rejected, redacted, entries };
|
|
71
68
|
}
|
|
72
69
|
finally {
|
|
73
|
-
|
|
74
|
-
closeHippoDb(dryRunDb);
|
|
70
|
+
closeHippoDb(db);
|
|
75
71
|
}
|
|
76
72
|
}
|
|
77
73
|
/** The secret-vetted chunk capped at 1000 chars, and whether vetting changed it. */
|
|
@@ -106,24 +102,13 @@ function createImportEntry(chunk, source, allTags, options, baseHalfLifeDays) {
|
|
|
106
102
|
});
|
|
107
103
|
}
|
|
108
104
|
/** Writes the entry, or on a dry run only probes the guard; false when a rejected value refuses it. */
|
|
109
|
-
function writeOrProbeImport(targetRoot, entry, options
|
|
110
|
-
|
|
111
|
-
if (dryRunDb) {
|
|
112
|
-
try {
|
|
113
|
-
checkRejectionGuard(dryRunDb, entry.tenantId ?? 'default', entry.id, entry.content);
|
|
114
|
-
}
|
|
115
|
-
catch (err) {
|
|
116
|
-
if (err instanceof RejectedValueError)
|
|
117
|
-
return false;
|
|
118
|
-
throw err;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
return true;
|
|
122
|
-
}
|
|
123
|
-
// AT1 (plan §3 containment): a rejection refuses one CHUNK, not the
|
|
124
|
-
// whole import batch. Caught per-item so siblings still land.
|
|
105
|
+
function writeOrProbeImport(db, targetRoot, entry, options) {
|
|
106
|
+
// A rejection refuses one chunk, not the whole import, so siblings still land.
|
|
125
107
|
try {
|
|
126
|
-
|
|
108
|
+
if (options.dryRun)
|
|
109
|
+
checkRejectionGuard(db, entry.tenantId ?? 'default', entry.id, entry.content);
|
|
110
|
+
else
|
|
111
|
+
writeEntryOn(db, targetRoot, entry);
|
|
127
112
|
}
|
|
128
113
|
catch (err) {
|
|
129
114
|
if (err instanceof RejectedValueError)
|
|
@@ -132,9 +117,9 @@ function writeOrProbeImport(targetRoot, entry, options, dryRunDb) {
|
|
|
132
117
|
}
|
|
133
118
|
return true;
|
|
134
119
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
120
|
+
// ---------------------------------------------------------------------------
|
|
121
|
+
// ChatGPT importer
|
|
122
|
+
// ---------------------------------------------------------------------------
|
|
138
123
|
export function isJsonPlainObject(x) {
|
|
139
124
|
return x !== null && !Array.isArray(x) && typeof x === 'object';
|
|
140
125
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as path from 'path';
|
|
3
|
-
import { importEntries,
|
|
3
|
+
import { importEntries, isJsonPlainObject } from './core.js';
|
|
4
4
|
import { parseFrontmatter, collectMarkdownFiles } from './markdown-parse.js';
|
|
5
|
+
import { isJsonString } from '../json.js';
|
|
5
6
|
/** Coerce one imported record (string, `{content|text: ...}` object, or
|
|
6
7
|
* anything else) into the plain-text memory chunk it represents. */
|
|
7
8
|
function extractMemoryText(candidate) {
|
package/dist/importers/vault.js
CHANGED
|
@@ -9,27 +9,9 @@ import { RejectedValueError, checkRejectionGuard } from '../rejection.js';
|
|
|
9
9
|
import { loadConfig } from '../config.js';
|
|
10
10
|
import { vetSecrets } from '../secret-detect.js';
|
|
11
11
|
import { log } from '../log.js';
|
|
12
|
-
import { isJsonString } from './core.js';
|
|
13
12
|
import { parseFrontmatter, frontmatterList, parseWikilinks, collectMarkdownFiles, realpathOrResolve } from './markdown-parse.js';
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
//
|
|
17
|
-
// MIRRORS THE CONNECTOR PATTERN (src/connectors/slack|github), NOT the
|
|
18
|
-
// single-file importers above. Each note becomes a single kind='raw' row with
|
|
19
|
-
// provenance in TAGS (`source:vault` + `vault:<name>`), an artifactRef cursor
|
|
20
|
-
// key, and a content-hash tag. Changes APPEND a new raw row after archiveRaw of
|
|
21
|
-
// the old one; deletions archiveRaw the orphaned rows. We NEVER `supersede` a
|
|
22
|
-
// raw row (supersede yields kind='distilled', losing raw-append-only protection
|
|
23
|
-
// and escaping the kind='raw' deletion rescan) — all raw deletions route through
|
|
24
|
-
// `archiveRaw` (the only trigger-legit raw delete).
|
|
25
|
-
// ---------------------------------------------------------------------------
|
|
26
|
-
/** Escape LIKE wildcards in operator-supplied text (mirror of
|
|
27
|
-
* src/project-briefs.ts:477 / src/store/search-rows.ts, kept local since neither is
|
|
28
|
-
* exported). Used so a `%`/`_`/`\` in the vault name cannot over-match the
|
|
29
|
-
* loader prefix and archive another vault's rows. */
|
|
30
|
-
function escapeLike(term) {
|
|
31
|
-
return term.replace(/[%_\\]/g, '\\$&');
|
|
32
|
-
}
|
|
13
|
+
import { isJsonString } from '../json.js';
|
|
14
|
+
import { escapeLike } from '../escape.js';
|
|
33
15
|
/**
|
|
34
16
|
* Import a markdown vault FOLDER as `kind='raw'` memories.
|
|
35
17
|
*
|
package/dist/incidents.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Incident first-class object.
|
|
3
3
|
*
|
|
4
4
|
* An incident is a postmortem capsule: a recorded operational event with a
|
|
5
5
|
* lifecycle and optional linked receipts (the memories that are its evidence).
|
package/dist/incidents.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Incident first-class object.
|
|
3
3
|
*
|
|
4
4
|
* An incident is a postmortem capsule: a recorded operational event with a
|
|
5
5
|
* lifecycle and optional linked receipts (the memories that are its evidence).
|
|
@@ -77,6 +77,50 @@ const INCIDENT_COLS = `
|
|
|
77
77
|
// ---------------------------------------------------------------------------
|
|
78
78
|
// Public API
|
|
79
79
|
// ---------------------------------------------------------------------------
|
|
80
|
+
// Validate every linked receipt BEFORE inserting the row. Each must be a
|
|
81
|
+
// memory in the SAME tenant; a cross-tenant or nonexistent id rejects the
|
|
82
|
+
// whole write rather than recording an unverifiable receipt.
|
|
83
|
+
function validateLinkedMemoryIds(db, tenantId, linkInput) {
|
|
84
|
+
const validated = [];
|
|
85
|
+
for (const linkId of linkInput) {
|
|
86
|
+
// SAFETY: row shape matches the single `id` column named in the SELECT above.
|
|
87
|
+
const exists = db.prepare(`SELECT id FROM memories WHERE id = ? AND tenant_id = ?`).get(linkId, tenantId);
|
|
88
|
+
if (!exists) {
|
|
89
|
+
throw new NotFoundError(`saveIncident: linked memory ${linkId} not found for tenant ${tenantId}`);
|
|
90
|
+
}
|
|
91
|
+
validated.push(linkId);
|
|
92
|
+
}
|
|
93
|
+
return validated;
|
|
94
|
+
}
|
|
95
|
+
/** The afterWrite body: link validation, INSERT, reload, open audit, all in one SAVEPOINT. */
|
|
96
|
+
function writeIncidentRow(db, memoryId, tenantId, opts, actor, now) {
|
|
97
|
+
const validated = validateLinkedMemoryIds(db, tenantId, opts.linkedMemoryIds ?? []);
|
|
98
|
+
const result = db.prepare(`
|
|
99
|
+
INSERT INTO incidents(
|
|
100
|
+
memory_id, tenant_id, incident_text, context,
|
|
101
|
+
status, resolution_text, resolved_at, closed_at, linked_memory_ids, created_at
|
|
102
|
+
) VALUES (?, ?, ?, ?, 'open', NULL, NULL, NULL, ?, ?)
|
|
103
|
+
`).run(memoryId, tenantId, opts.incidentText, opts.context ?? null, JSON.stringify(validated), now);
|
|
104
|
+
const incidentId = Number(result.lastInsertRowid ?? 0);
|
|
105
|
+
// SAFETY: row's shape matches the columns named in INCIDENT_COLS above.
|
|
106
|
+
const row = db.prepare(`SELECT ${INCIDENT_COLS} FROM incidents WHERE id = ?`)
|
|
107
|
+
.get(incidentId);
|
|
108
|
+
if (!row)
|
|
109
|
+
throw new Error('saveIncident: failed to reload saved incident row');
|
|
110
|
+
// GDPR-light metadata: id + flag only, no incident_text.
|
|
111
|
+
appendAuditEvent(db, {
|
|
112
|
+
tenantId,
|
|
113
|
+
actor,
|
|
114
|
+
op: 'incident_open',
|
|
115
|
+
targetId: String(incidentId),
|
|
116
|
+
metadata: {
|
|
117
|
+
incident_id: incidentId,
|
|
118
|
+
has_context: opts.context !== undefined && opts.context !== null && opts.context !== '',
|
|
119
|
+
linked_memory_count: validated.length,
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
return row;
|
|
123
|
+
}
|
|
80
124
|
/**
|
|
81
125
|
* Create an incident. Writes the memory mirror + the incidents row atomically
|
|
82
126
|
* inside writeEntry's SAVEPOINT 'write_entry'.
|
|
@@ -106,50 +150,13 @@ export function saveIncident(hippoRoot, tenantId, opts, actor = 'cli') {
|
|
|
106
150
|
baseHalfLifeDays: objectHalfLifeDays(hippoRoot),
|
|
107
151
|
tenantId,
|
|
108
152
|
});
|
|
109
|
-
const linkInput = opts.linkedMemoryIds ?? [];
|
|
110
153
|
// Populated inside afterWrite so the linked-id validation, the INSERT, and the
|
|
111
154
|
// memory write all share one SAVEPOINT.
|
|
112
155
|
let savedRow;
|
|
113
156
|
writeEntry(hippoRoot, mem, {
|
|
114
157
|
actor,
|
|
115
158
|
afterWrite: (db, memoryId) => {
|
|
116
|
-
|
|
117
|
-
// memory in the SAME tenant; a cross-tenant or nonexistent id rejects the
|
|
118
|
-
// whole write rather than recording an unverifiable receipt.
|
|
119
|
-
const validated = [];
|
|
120
|
-
for (const linkId of linkInput) {
|
|
121
|
-
// SAFETY: row shape matches the single `id` column named in the SELECT above.
|
|
122
|
-
const exists = db.prepare(`SELECT id FROM memories WHERE id = ? AND tenant_id = ?`).get(linkId, tenantId);
|
|
123
|
-
if (!exists) {
|
|
124
|
-
throw new NotFoundError(`saveIncident: linked memory ${linkId} not found for tenant ${tenantId}`);
|
|
125
|
-
}
|
|
126
|
-
validated.push(linkId);
|
|
127
|
-
}
|
|
128
|
-
const result = db.prepare(`
|
|
129
|
-
INSERT INTO incidents(
|
|
130
|
-
memory_id, tenant_id, incident_text, context,
|
|
131
|
-
status, resolution_text, resolved_at, closed_at, linked_memory_ids, created_at
|
|
132
|
-
) VALUES (?, ?, ?, ?, 'open', NULL, NULL, NULL, ?, ?)
|
|
133
|
-
`).run(memoryId, tenantId, opts.incidentText, opts.context ?? null, JSON.stringify(validated), now);
|
|
134
|
-
const incidentId = Number(result.lastInsertRowid ?? 0);
|
|
135
|
-
// SAFETY: row's shape matches the columns named in INCIDENT_COLS above.
|
|
136
|
-
const row = db.prepare(`SELECT ${INCIDENT_COLS} FROM incidents WHERE id = ?`)
|
|
137
|
-
.get(incidentId);
|
|
138
|
-
if (!row)
|
|
139
|
-
throw new Error('saveIncident: failed to reload saved incident row');
|
|
140
|
-
savedRow = row;
|
|
141
|
-
// GDPR-light metadata: id + flag only, no incident_text.
|
|
142
|
-
appendAuditEvent(db, {
|
|
143
|
-
tenantId,
|
|
144
|
-
actor,
|
|
145
|
-
op: 'incident_open',
|
|
146
|
-
targetId: String(incidentId),
|
|
147
|
-
metadata: {
|
|
148
|
-
incident_id: incidentId,
|
|
149
|
-
has_context: opts.context !== undefined && opts.context !== null && opts.context !== '',
|
|
150
|
-
linked_memory_count: validated.length,
|
|
151
|
-
},
|
|
152
|
-
});
|
|
159
|
+
savedRow = writeIncidentRow(db, memoryId, tenantId, opts, actor, now);
|
|
153
160
|
},
|
|
154
161
|
});
|
|
155
162
|
if (!savedRow) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** Prompt-injection detection for untrusted memory content
|
|
1
|
+
/** Prompt-injection detection for untrusted memory content. Same shape as secret-detect.ts; leaf module, no store/api/shared imports. */
|
|
2
2
|
export interface InstructionDetection {
|
|
3
3
|
flagged: boolean;
|
|
4
4
|
reason: string | null;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** Prompt-injection detection for untrusted memory content
|
|
1
|
+
/** Prompt-injection detection for untrusted memory content. Same shape as secret-detect.ts; leaf module, no store/api/shared imports. */
|
|
2
2
|
const INSTRUCTION_PATTERNS = [
|
|
3
3
|
{ name: 'override-instructions', re: /\b(?:ignore|disregard|forget)\b[^.\n]{0,40}\b(?:previous|prior|above)\b[^.\n]{0,40}\binstructions?\b/i },
|
|
4
4
|
{ name: 'role-reassignment', re: /\b(?:you are now|act as|pretend to be)\b[^.\n]{0,40}\b(?:an?\s+)?(?:ai|assistant|agent|system)\b/i },
|
package/dist/invalidation.d.ts
CHANGED
|
@@ -47,7 +47,10 @@ export interface ChurnRefs {
|
|
|
47
47
|
export declare function extractChurnRefs(content: string): ChurnRefs;
|
|
48
48
|
export interface DetectChurnStaleOptions {
|
|
49
49
|
tenantId: string;
|
|
50
|
+
/** The project id; rows under it or legacyName are checked. */
|
|
50
51
|
projectName: string;
|
|
52
|
+
/** The folder name older rows carry, and the repo folder a memory's paths may start with. */
|
|
53
|
+
legacyName?: string;
|
|
51
54
|
/** Evaluate matches but write nothing. */
|
|
52
55
|
dryRun?: boolean;
|
|
53
56
|
}
|