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/audit-prune.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Audit log retention pruning
|
|
2
|
+
* Audit log retention pruning.
|
|
3
3
|
*
|
|
4
4
|
* The `audit_log` table grows unbounded by default — every recall, write,
|
|
5
5
|
* outcome, sleep, supersede, promote, forget, archive_raw, auth_revoke,
|
|
@@ -7,10 +7,8 @@
|
|
|
7
7
|
* accumulate to millions of rows and slow down both audit queries and
|
|
8
8
|
* incremental SQLite VACUUMs.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* regulatory retention floors (HIPAA, SOX, GDPR) — the prune should be
|
|
13
|
-
* opt-in per tenant and emit its own audit trail event."
|
|
10
|
+
* Regulatory retention floors (HIPAA, SOX, GDPR) are why the prune is opt-in
|
|
11
|
+
* per tenant and emits its own audit trail event.
|
|
14
12
|
*
|
|
15
13
|
* Design notes:
|
|
16
14
|
* - Per-tenant by default (matches existing audit CLI conventions).
|
|
@@ -29,7 +27,7 @@ import type { DatabaseSyncLike } from './db.js';
|
|
|
29
27
|
export interface PruneAuditOpts {
|
|
30
28
|
/** Cutoff in days. Rows with `ts < (now - N days)` are deleted. */
|
|
31
29
|
olderThanDays: number;
|
|
32
|
-
/** Tenant scope. Required
|
|
30
|
+
/** Tenant scope. Required: prune is always tenant-scoped. */
|
|
33
31
|
tenantId: string;
|
|
34
32
|
/** When true, count matching rows but do NOT delete. Default false. */
|
|
35
33
|
dryRun?: boolean;
|
package/dist/audit-prune.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Audit log retention pruning
|
|
2
|
+
* Audit log retention pruning.
|
|
3
3
|
*
|
|
4
4
|
* The `audit_log` table grows unbounded by default — every recall, write,
|
|
5
5
|
* outcome, sleep, supersede, promote, forget, archive_raw, auth_revoke,
|
|
@@ -7,10 +7,8 @@
|
|
|
7
7
|
* accumulate to millions of rows and slow down both audit queries and
|
|
8
8
|
* incremental SQLite VACUUMs.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* regulatory retention floors (HIPAA, SOX, GDPR) — the prune should be
|
|
13
|
-
* opt-in per tenant and emit its own audit trail event."
|
|
10
|
+
* Regulatory retention floors (HIPAA, SOX, GDPR) are why the prune is opt-in
|
|
11
|
+
* per tenant and emits its own audit trail event.
|
|
14
12
|
*
|
|
15
13
|
* Design notes:
|
|
16
14
|
* - Per-tenant by default (matches existing audit CLI conventions).
|
package/dist/audit.js
CHANGED
|
@@ -18,23 +18,16 @@ const VAGUE_ONLY = /^[\w\s,.'"-]+$/;
|
|
|
18
18
|
// CJK words average ~2 characters, so approximate substantive units as one per
|
|
19
19
|
// 2 CJK LETTERS.
|
|
20
20
|
//
|
|
21
|
-
// Two properties this must hold
|
|
22
|
-
// 1. Letters only, enforced by construction
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
// properties are not restricted to letters. The `(?=\p{L})` lookahead
|
|
27
|
-
// makes "letters only" true by definition rather than by assertion, and
|
|
28
|
-
// the category sweep in tests/df3-cjk-quality-floor.test.ts pins it so the
|
|
29
|
-
// next wrong guess fails locally instead of in review.
|
|
21
|
+
// Two properties this must hold:
|
|
22
|
+
// 1. Letters only, enforced by construction: a Katakana BLOCK range counts the
|
|
23
|
+
// middle dot and prolonged sound mark, and `\p{Script=Han}` alone counts
|
|
24
|
+
// Han NON-letters (Kangxi radicals), so the `(?=\p{L})` lookahead makes
|
|
25
|
+
// "letters only" true by definition; tests/df3-cjk-quality-floor.test.ts sweeps it.
|
|
30
26
|
//
|
|
31
27
|
// SCOPE, stated precisely because the constant name says "CJK": this covers
|
|
32
28
|
// Han, Hiragana and Katakana only. Hangul is absent (Korean largely survives
|
|
33
29
|
// the whitespace split already) and other spaceless scripts - Thai, Khmer,
|
|
34
|
-
// Burmese, Lao - still hit the original one-word failure.
|
|
35
|
-
// byte-identical to before this change, so nothing regressed; widening the
|
|
36
|
-
// script set is a separate, deliberately-scoped follow-up rather than another
|
|
37
|
-
// mid-episode guess at this predicate.
|
|
30
|
+
// Burmese, Lao - still hit the original one-word failure.
|
|
38
31
|
// 2. ADD to the latin count, never strip before it. Stripping CJK first can
|
|
39
32
|
// REDUCE the count for short mixed tokens (`UI<han> DB<han> QA<han>` leaves
|
|
40
33
|
// three 2-char latin fragments that fail the `> 2` filter), which would
|
|
@@ -84,25 +77,10 @@ function hasNoSpecificity(text) {
|
|
|
84
77
|
const words = text.toLowerCase().split(/\s+/);
|
|
85
78
|
const hasNumber = /\d/.test(text);
|
|
86
79
|
const hasProperNoun = /[A-Z][a-z]{2,}/.test(text);
|
|
87
|
-
// An ACRONYM is specificity too
|
|
88
|
-
//
|
|
89
|
-
//
|
|
90
|
-
//
|
|
91
|
-
// "The rule is every PR needs two approvals, no exceptions." to "every PR
|
|
92
|
-
// needs two approvals", which then fell under the 40-char vagueness gate
|
|
93
|
-
// and was silently DROPPED - a rule that stored before this branch.
|
|
94
|
-
// ...but an acronym only signals specificity when it stands out AGAINST
|
|
95
|
-
// ordinary prose. Without the lowercase requirement, any shouted phrase
|
|
96
|
-
// qualifies: "FIXED SIGNALS" passed the gate while the identical
|
|
97
|
-
// "fixed signals" was correctly rejected, so capitalization alone bought a
|
|
98
|
-
// bypass - into auditMemory and includeRecent as well, where junk would
|
|
99
|
-
// then occupy recent-context slots. Codex P2, r8.
|
|
100
|
-
// CHAT acronyms are not domain signal. Admitting any all-caps token let
|
|
101
|
-
// "LGTM ship it", "TODO fix this thing", "FYI all done here" through a gate
|
|
102
|
-
// that correctly rejected them before - and this gate is shared, so the
|
|
103
|
-
// effect is retroactive: junk rows already in a user's store were filtered
|
|
104
|
-
// out of recent-context slots and would have started occupying them, and
|
|
105
|
-
// `hippo audit` would have stopped flagging them. Found at the ship gate.
|
|
80
|
+
// An ACRONYM is specificity too: the proper-noun pattern needs lowercase after the capital, so
|
|
81
|
+
// "PR", "CI", "DB", "API", "S3" (the densest domain tokens in a technical memory) all read as vague.
|
|
82
|
+
// It counts only against ordinary prose (the lowercase test), else a shouted phrase bypasses the gate,
|
|
83
|
+
// and CHAT acronyms never count: the gate is shared, so "LGTM ship it" would reach recent-context slots.
|
|
106
84
|
const CHAT_ACRONYMS = /^(?:TODO|FYI|LGTM|IIRC|IMO|IMHO|FWIW|TBD|BTW|ASAP|AFAIK|WIP|NB|PS)$/;
|
|
107
85
|
const domainAcronyms = (text.match(/\b[A-Z]{2,6}\b/g) ?? []).filter(a => !CHAT_ACRONYMS.test(a));
|
|
108
86
|
const hasAcronym = domainAcronyms.length > 0 && /[a-z]/.test(text);
|
|
@@ -173,7 +151,7 @@ export function isContentWorthStoring(content) {
|
|
|
173
151
|
return true;
|
|
174
152
|
}
|
|
175
153
|
// ---------------------------------------------------------------------------
|
|
176
|
-
//
|
|
154
|
+
// Audit log primitives (append-only mutation trail)
|
|
177
155
|
// ---------------------------------------------------------------------------
|
|
178
156
|
// The one list of audit ops: the AuditOp type, `hippo audit list --op` and GET /v1/audit?op= all read it.
|
|
179
157
|
export const AUDIT_OPS = [
|
package/dist/auth.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export declare function apiKeyVerifyStats(): ApiKeyVerifyStats;
|
|
|
11
11
|
export interface CreateApiKeyOpts {
|
|
12
12
|
tenantId: string;
|
|
13
13
|
label?: string;
|
|
14
|
-
/**
|
|
14
|
+
/** 'admin' | 'member'. Defaults to 'admin' (backward-compat for callers that don't specify). */
|
|
15
15
|
role?: 'admin' | 'member';
|
|
16
16
|
}
|
|
17
17
|
export interface CreateApiKeyResult {
|
|
@@ -23,9 +23,9 @@ export interface ValidateResult {
|
|
|
23
23
|
valid: boolean;
|
|
24
24
|
tenantId?: string;
|
|
25
25
|
keyId?: string;
|
|
26
|
-
/**
|
|
26
|
+
/** 'admin' | 'member'. Present only when valid=true. */
|
|
27
27
|
role?: 'admin' | 'member';
|
|
28
|
-
/**
|
|
28
|
+
/** Scope grants for this key. Present only when valid=true. */
|
|
29
29
|
scopes?: string[];
|
|
30
30
|
}
|
|
31
31
|
/** The identity a verified API key carries. */
|
|
@@ -52,11 +52,11 @@ export declare class VerifiedKeyCache {
|
|
|
52
52
|
/** Verify a bearer API key for `hippoRoot`; a cache hit skips both scrypt and the DB open. Null when invalid. */
|
|
53
53
|
export declare function verifyApiKeyCached(hippoRoot: string, plaintext: string): VerifiedApiKey | null;
|
|
54
54
|
export declare function revokeApiKey(db: DatabaseSyncLike, keyId: string): void;
|
|
55
|
-
/**
|
|
55
|
+
/** Grant `keyId` read access to one restricted `scope`. Idempotent. */
|
|
56
56
|
export declare function grantScope(db: DatabaseSyncLike, keyId: string, scope: string): void;
|
|
57
|
-
/**
|
|
57
|
+
/** Revoke `keyId`'s grant on `scope`. Not an error when no such grant exists. */
|
|
58
58
|
export declare function ungrantScope(db: DatabaseSyncLike, keyId: string, scope: string): void;
|
|
59
|
-
/**
|
|
59
|
+
/** Every restricted scope `keyId` may read. */
|
|
60
60
|
export declare function listScopeGrants(db: DatabaseSyncLike, keyId: string): string[];
|
|
61
61
|
export interface ApiKeyListItem {
|
|
62
62
|
keyId: string;
|
|
@@ -65,12 +65,11 @@ export interface ApiKeyListItem {
|
|
|
65
65
|
createdAt: string;
|
|
66
66
|
revokedAt: string | null;
|
|
67
67
|
/**
|
|
68
|
-
*
|
|
69
|
-
* the `role` column (added in schema migration v26 by v1.12.0 sub-1).
|
|
68
|
+
* Authorization role bound to the key, from the `role` column (schema migration v26).
|
|
70
69
|
* Fail-safe-to-member cast: any non-'admin' value reads as 'member'.
|
|
71
70
|
*/
|
|
72
71
|
role: 'admin' | 'member';
|
|
73
|
-
/**
|
|
72
|
+
/** Restricted scopes this key may read. */
|
|
74
73
|
scopes: string[];
|
|
75
74
|
}
|
|
76
75
|
export interface ListApiKeysOpts {
|
package/dist/auth.js
CHANGED
|
@@ -40,10 +40,8 @@ export function createApiKey(db, opts) {
|
|
|
40
40
|
const secret = randBase32(SECRET_LEN);
|
|
41
41
|
const plaintext = `${keyId}.${secret}`;
|
|
42
42
|
const hash = hashKey(plaintext);
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
// handle, so migration v26 (adds role column) is in place before this
|
|
46
|
-
// INSERT runs.
|
|
43
|
+
// openHippoDb runs runMigrations synchronously before returning the db handle,
|
|
44
|
+
// so migration v26 (adds role column) is in place before this INSERT runs.
|
|
47
45
|
db.prepare(`INSERT INTO api_keys (key_id, key_hash, tenant_id, label, created_at, role) VALUES (?, ?, ?, ?, ?, ?)`).run(keyId, hash, opts.tenantId, opts.label ?? null, new Date().toISOString(), opts.role ?? 'admin');
|
|
48
46
|
return { keyId, plaintext };
|
|
49
47
|
}
|
|
@@ -146,18 +144,18 @@ export function revokeApiKey(db, keyId) {
|
|
|
146
144
|
.run(new Date().toISOString(), keyId);
|
|
147
145
|
verifiedKeys.delete(keyId);
|
|
148
146
|
}
|
|
149
|
-
/**
|
|
147
|
+
/** Grant `keyId` read access to one restricted `scope`. Idempotent. */
|
|
150
148
|
export function grantScope(db, keyId, scope) {
|
|
151
149
|
db.prepare(`INSERT INTO api_key_scope_grants (key_id, scope, granted_at) VALUES (?, ?, ?)
|
|
152
150
|
ON CONFLICT(key_id, scope) DO NOTHING`).run(keyId, scope, new Date().toISOString());
|
|
153
151
|
verifiedKeys.delete(keyId);
|
|
154
152
|
}
|
|
155
|
-
/**
|
|
153
|
+
/** Revoke `keyId`'s grant on `scope`. Not an error when no such grant exists. */
|
|
156
154
|
export function ungrantScope(db, keyId, scope) {
|
|
157
155
|
db.prepare(`DELETE FROM api_key_scope_grants WHERE key_id = ? AND scope = ?`).run(keyId, scope);
|
|
158
156
|
verifiedKeys.delete(keyId);
|
|
159
157
|
}
|
|
160
|
-
/**
|
|
158
|
+
/** Every restricted scope `keyId` may read. */
|
|
161
159
|
export function listScopeGrants(db, keyId) {
|
|
162
160
|
// SAFETY: rows' shape matches the single `scope` column named in the SELECT above.
|
|
163
161
|
const rows = db
|
package/dist/autolearn.d.ts
CHANGED
|
@@ -30,7 +30,7 @@ export declare function partitionLessons(lessons: string[]): {
|
|
|
30
30
|
* Check if a substantially similar memory already exists.
|
|
31
31
|
* Returns true if overlap > threshold (default 0.7).
|
|
32
32
|
*
|
|
33
|
-
*
|
|
33
|
+
* `tenantId` is opt-in. Only takes effect when the first argument is a
|
|
34
34
|
* root string (string-overload path). When the first argument is a
|
|
35
35
|
* pre-loaded MemoryEntry[], the caller has already scoped — tenantId is
|
|
36
36
|
* ignored on that path.
|
package/dist/autolearn.js
CHANGED
|
@@ -100,7 +100,7 @@ export function partitionLessons(lessons) {
|
|
|
100
100
|
* Check if a substantially similar memory already exists.
|
|
101
101
|
* Returns true if overlap > threshold (default 0.7).
|
|
102
102
|
*
|
|
103
|
-
*
|
|
103
|
+
* `tenantId` is opt-in. Only takes effect when the first argument is a
|
|
104
104
|
* root string (string-overload path). When the first argument is a
|
|
105
105
|
* pre-loaded MemoryEntry[], the caller has already scoped — tenantId is
|
|
106
106
|
* ignored on that path.
|
package/dist/availability.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// ---------------------------------------------------------------------------
|
|
2
|
-
//
|
|
2
|
+
// Availability-bias detector (biases over memory state)
|
|
3
3
|
// ---------------------------------------------------------------------------
|
|
4
4
|
//
|
|
5
5
|
// Flags when a recall's returned top-K is dominated by recent entries while
|
|
@@ -10,10 +10,8 @@
|
|
|
10
10
|
// creation timestamp, so we can compare the age distribution of what was
|
|
11
11
|
// RETURNED against the age distribution of the pool it was drawn from.
|
|
12
12
|
//
|
|
13
|
-
// Soft warning ONLY
|
|
14
|
-
//
|
|
15
|
-
// agent may choose to act on, exactly like J1 anchoringHint / J3
|
|
16
|
-
// planningFallacyHint / C5 suppressionSummary.
|
|
13
|
+
// Soft warning ONLY: this never filters, reorders, or suppresses a result. It surfaces
|
|
14
|
+
// a hint the calling agent may act on, like anchoringHint / planningFallacyHint / suppressionSummary.
|
|
17
15
|
//
|
|
18
16
|
// PURE: no I/O, no env reads. The env gate (HIPPO_AVAILABILITY=off) and the
|
|
19
17
|
// audit emission live in the callers (api.recall, cmdRecall, MCP), mirroring
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type ProjectRef } from '../project-identity.js';
|
|
1
2
|
import { type SessionTurn } from './transcript.js';
|
|
2
3
|
export interface CaptureOptions {
|
|
3
4
|
source: 'stdin' | 'file' | 'last-session';
|
|
@@ -21,13 +22,13 @@ export interface CaptureOptions {
|
|
|
21
22
|
dryRun: boolean;
|
|
22
23
|
global: boolean;
|
|
23
24
|
/**
|
|
24
|
-
*
|
|
25
|
+
* Tenant scope for the dedup read in `cmdCaptureCore`. When provided
|
|
25
26
|
* AND `global` is false, the dedup check only considers this tenant's
|
|
26
|
-
* existing memories. Undefined
|
|
27
|
-
* behaviour. Ignored when `global: true` (global captures are host-wide).
|
|
27
|
+
* existing memories. Undefined means host-wide dedup. Ignored when `global: true` (global captures are host-wide).
|
|
28
28
|
*/
|
|
29
29
|
tenantId?: string;
|
|
30
|
-
|
|
30
|
+
/** The session's project: rows are stamped with its id, and dedup reads its rows under either name. */
|
|
31
|
+
originProject?: ProjectRef;
|
|
31
32
|
}
|
|
32
33
|
export declare function cmdCapture(hippoRoot: string, options: CaptureOptions): void;
|
|
33
34
|
//# sourceMappingURL=command.d.ts.map
|
package/dist/capture/command.js
CHANGED
|
@@ -14,7 +14,7 @@ import { maskEmails, redactSecretsStrict } from '../secret-detect.js';
|
|
|
14
14
|
import { RejectedValueError, checkRejectionGuard } from '../rejection.js';
|
|
15
15
|
import { openHippoDb, closeHippoDb } from '../db.js';
|
|
16
16
|
import { loadConfig } from '../config.js';
|
|
17
|
-
import { classifyOriginProject } from '../project-identity.js';
|
|
17
|
+
import { classifyOriginProject, projectId } from '../project-identity.js';
|
|
18
18
|
import { isStringValue } from '../capture-contract.js';
|
|
19
19
|
import { errorMessage, log } from '../log.js';
|
|
20
20
|
import { extractFromText } from './extract.js';
|
|
@@ -116,7 +116,7 @@ function cmdCaptureCore(hippoRoot, options) {
|
|
|
116
116
|
console.log('No actionable items found in the input.');
|
|
117
117
|
return;
|
|
118
118
|
}
|
|
119
|
-
// Dedup only against rows this capture's reader sees: another tenant's rows
|
|
119
|
+
// Dedup only against rows this capture's reader sees: another tenant's rows, or another
|
|
120
120
|
// project's, are hidden from it, so they must not stop its own copy.
|
|
121
121
|
const stored = loadAllEntries(targetRoot, useGlobal ? undefined : options.tenantId);
|
|
122
122
|
const origin = options.originProject;
|
|
@@ -175,13 +175,8 @@ function readCaptureText(options) {
|
|
|
175
175
|
function captureExtractedItems(targetRoot, options, extracted, keys) {
|
|
176
176
|
const tally = { captured: 0, skipped: 0, rejected: 0 };
|
|
177
177
|
const baseHalfLifeDays = loadConfig(targetRoot).defaultHalfLifeDays;
|
|
178
|
-
//
|
|
179
|
-
//
|
|
180
|
-
// extraction printed as `[capture]` and counted toward `captured` — the
|
|
181
|
-
// preview lied about what a real run would do. Mirrors importers.ts's
|
|
182
|
-
// importEntries dry-run probe (commit 6146e82): open a read-only handle
|
|
183
|
-
// once, run the same checkRejectionGuard the real write path uses via
|
|
184
|
-
// writeEntry, never write anything.
|
|
178
|
+
// Dry run probes the same checkRejectionGuard the real write uses, on a read-only handle,
|
|
179
|
+
// so a tombstoned item previews as rejected rather than captured.
|
|
185
180
|
const dryRunDb = options.dryRun ? openHippoDb(targetRoot) : null;
|
|
186
181
|
const writeDb = options.dryRun ? null : openStore(targetRoot);
|
|
187
182
|
try {
|
|
@@ -207,16 +202,8 @@ function captureExtractedItems(targetRoot, options, extracted, keys) {
|
|
|
207
202
|
}
|
|
208
203
|
function captureEntry(item, options, baseHalfLifeDays) {
|
|
209
204
|
const useGlobal = options.global;
|
|
210
|
-
//
|
|
211
|
-
//
|
|
212
|
-
// future variant captures full raw session text, it MUST set kind: 'raw'
|
|
213
|
-
// and route deletions through archiveRawMemory(). See MEMORY_ENVELOPE.md.
|
|
214
|
-
// L9: the dedup read above is scoped by options.tenantId — the WRITE
|
|
215
|
-
// must match, or scoped-dedup-passes-then-default-tenant-write breaks
|
|
216
|
-
// the per-tenant contract. Mirror the dedup-read guard: when
|
|
217
|
-
// global: true, the global store is host-wide and tenant is irrelevant
|
|
218
|
-
// (createMemory's default 'default' applies). When global: false,
|
|
219
|
-
// options.tenantId scopes the write to the same tenant as the dedup.
|
|
205
|
+
// kind stays 'distilled': these are curated items, not raw transcript (see MEMORY_ENVELOPE.md).
|
|
206
|
+
// The write tenant must match the dedup read's, or scoped dedup passes and the row lands in 'default'.
|
|
220
207
|
const created = createMemory(item.content, {
|
|
221
208
|
layer: Layer.Episodic,
|
|
222
209
|
tags: item.tags,
|
|
@@ -225,7 +212,7 @@ function captureEntry(item, options, baseHalfLifeDays) {
|
|
|
225
212
|
tenantId: useGlobal ? undefined : options.tenantId,
|
|
226
213
|
baseHalfLifeDays,
|
|
227
214
|
});
|
|
228
|
-
return options.originProject === undefined ? created : { ...created, origin_project: options.originProject };
|
|
215
|
+
return options.originProject === undefined ? created : { ...created, origin_project: projectId(options.originProject) };
|
|
229
216
|
}
|
|
230
217
|
/** Previews or writes one non-duplicate item and names the tally it counts toward. */
|
|
231
218
|
function captureOne(ctx, item, entry) {
|
|
@@ -246,8 +233,7 @@ function captureOne(ctx, item, entry) {
|
|
|
246
233
|
console.log(` [capture] (${item.category}) ${item.content}`);
|
|
247
234
|
}
|
|
248
235
|
else if (writeDb !== null) {
|
|
249
|
-
//
|
|
250
|
-
// rest of this capture's items.
|
|
236
|
+
// One rejected item must not abort the rest of this capture's items.
|
|
251
237
|
const stamped = stampOriginProject(targetRoot, entry);
|
|
252
238
|
const outcome = gatedWrite(writeDb, targetRoot, stamped);
|
|
253
239
|
if (outcome === 'skipped:rejected')
|
|
@@ -7,7 +7,7 @@ export declare const PRE_COMPACT_NEXT_STEP_CAP = 500;
|
|
|
7
7
|
* newest with assistant responses after them, so a head-first cap keeps
|
|
8
8
|
* stale context and drops exactly the newest working state this feature
|
|
9
9
|
* exists to preserve. Keep the LAST maxChars instead, aligned forward to a
|
|
10
|
-
* nearby line start, with a trim marker
|
|
10
|
+
* nearby line start, with a trim marker.
|
|
11
11
|
*/
|
|
12
12
|
export declare function truncateKeepNewest(text: string, maxChars: number): string;
|
|
13
13
|
/**
|
|
@@ -37,7 +37,7 @@ export declare function cmdPreCompact(hippoRoot: string, options: PreCompactOpti
|
|
|
37
37
|
export interface PostCompactOptions {
|
|
38
38
|
stdinText?: string;
|
|
39
39
|
logFile?: string;
|
|
40
|
-
afterSave?: (transcriptPath: string,
|
|
40
|
+
afterSave?: (transcriptPath: string, cwd: string | null, log: (message: string) => void) => void;
|
|
41
41
|
}
|
|
42
42
|
/** PostCompact entry point: saves the summary and its items, replays earlier leftovers, returns the one line to show. Never throws, so the hook exits 0. */
|
|
43
43
|
export declare function cmdPostCompact(hippoRoot: string, options: PostCompactOptions): string | null;
|
package/dist/capture/compact.js
CHANGED
|
@@ -2,7 +2,7 @@ import * as fs from 'fs';
|
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import { isInitialized } from '../store/open.js';
|
|
4
4
|
import { saveActiveTaskSnapshot, loadActiveTaskSnapshot } from '../store/sessions.js';
|
|
5
|
-
import { PRE_COMPACT_INSTRUCTION,
|
|
5
|
+
import { PRE_COMPACT_INSTRUCTION, parsePostCompactPayload, postCompactLine, recordCompactionStart, recordSnapshotSaved, saveCompaction, replayCompactionsAt, COMPACTION_DB_WAIT_MS, } from '../compaction-record.js';
|
|
6
6
|
import { resolveTenantId } from '../tenant.js';
|
|
7
7
|
import { defaultPreCompactLogPath } from '../hooks/shared.js';
|
|
8
8
|
import { maskEmails, redactSecretsStrict } from '../secret-detect.js';
|
|
@@ -21,7 +21,7 @@ export const PRE_COMPACT_NEXT_STEP_CAP = 500;
|
|
|
21
21
|
* newest with assistant responses after them, so a head-first cap keeps
|
|
22
22
|
* stale context and drops exactly the newest working state this feature
|
|
23
23
|
* exists to preserve. Keep the LAST maxChars instead, aligned forward to a
|
|
24
|
-
* nearby line start, with a trim marker
|
|
24
|
+
* nearby line start, with a trim marker.
|
|
25
25
|
*/
|
|
26
26
|
export function truncateKeepNewest(text, maxChars) {
|
|
27
27
|
if (text.length <= maxChars)
|
|
@@ -154,7 +154,7 @@ export function transcriptWorkingState(transcriptPath, log) {
|
|
|
154
154
|
let rawTask = '';
|
|
155
155
|
let rawNextStep = '';
|
|
156
156
|
try {
|
|
157
|
-
// Compaction fires when a big tool_result lands
|
|
157
|
+
// Compaction fires when a big tool_result lands, so the last human and assistant turns can sit
|
|
158
158
|
// megabytes back: grow the window a bounded number of times until both turns are in it.
|
|
159
159
|
const size = fs.statSync(transcriptPath).size;
|
|
160
160
|
for (const cap of [PRE_COMPACT_TAIL_BYTES, PRE_COMPACT_TAIL_BYTES * 4, PRE_COMPACT_TAIL_BYTES * 16]) {
|
|
@@ -176,8 +176,8 @@ export function transcriptWorkingState(transcriptPath, log) {
|
|
|
176
176
|
log('skip: empty summary');
|
|
177
177
|
return null;
|
|
178
178
|
}
|
|
179
|
-
//
|
|
180
|
-
// re-injection token budget and never split a surrogate pair
|
|
179
|
+
// These fields skip the capture content gate and reach a prompt, so the strict scrub runs. The caps protect the
|
|
180
|
+
// re-injection token budget and never split a surrogate pair; `hippo snapshot save` stays uncapped.
|
|
181
181
|
const task = maskEmails(redactSecretsStrict(rawTask));
|
|
182
182
|
const summary = maskEmails(redactSecretsStrict(rawSummary));
|
|
183
183
|
const nextStep = maskEmails(redactSecretsStrict(rawNextStep));
|
|
@@ -189,7 +189,7 @@ export function transcriptWorkingState(transcriptPath, log) {
|
|
|
189
189
|
}
|
|
190
190
|
/** Runs the PreCompact producer: records the compaction, asks the summariser for memories, saves a working-state snapshot. Never extracts memories itself; SessionEnd capture owns that. */
|
|
191
191
|
function runPreCompact(hippoRoot, stdinText, stdinTimedOut, logFile) {
|
|
192
|
-
//
|
|
192
|
+
// The PreCompact hook fires in every Claude Code project, including
|
|
193
193
|
// ones that never ran `hippo init`, so gate before any store-opening call
|
|
194
194
|
// (saveActiveTaskSnapshot etc. call initStore, which would create one).
|
|
195
195
|
if (!isInitialized(hippoRoot)) {
|
|
@@ -219,12 +219,8 @@ function runPreCompact(hippoRoot, stdinText, stdinTimedOut, logFile) {
|
|
|
219
219
|
}
|
|
220
220
|
/** The transcript to snapshot, or null after logging why there is none. */
|
|
221
221
|
function resolvePreCompactTranscript(payloadTranscriptPath, stdinText, logFile) {
|
|
222
|
-
// A payload transcript_path is EXCLUSIVE:
|
|
223
|
-
//
|
|
224
|
-
// fallback would snapshot a DIFFERENT session's transcript under THIS
|
|
225
|
-
// payload's session_id — cross-session contamination with wrong linkage
|
|
226
|
-
// (verify-stage E2E finding, 2026-08-03). Auto-discovery only applies
|
|
227
|
-
// on a true manual invocation (no payload at all).
|
|
222
|
+
// A payload transcript_path is EXCLUSIVE: auto-discovery would snapshot a DIFFERENT session's
|
|
223
|
+
// transcript under THIS payload's session_id, so it runs only on a manual invocation (no payload).
|
|
228
224
|
let transcriptPath;
|
|
229
225
|
if (payloadTranscriptPath !== null) {
|
|
230
226
|
if (isReadableFile(payloadTranscriptPath)) {
|
|
@@ -246,7 +242,7 @@ function resolvePreCompactTranscript(payloadTranscriptPath, stdinText, logFile)
|
|
|
246
242
|
}
|
|
247
243
|
function saveDerivedSnapshot(hippoRoot, logFile, sessionId, recordId, derived) {
|
|
248
244
|
const tenantId = resolveTenantId({});
|
|
249
|
-
// Per-field merge
|
|
245
|
+
// Per-field merge: a tool-heavy tail whose only user turns are
|
|
250
246
|
// tool_result arrays derives an empty task even though the summary is
|
|
251
247
|
// non-empty. Loading the existing snapshot first lets each field fall
|
|
252
248
|
// back independently instead of the whole write clobbering a
|
|
@@ -258,7 +254,7 @@ function saveDerivedSnapshot(hippoRoot, logFile, sessionId, recordId, derived) {
|
|
|
258
254
|
catch {
|
|
259
255
|
// No existing snapshot to merge against — proceed with derived-only.
|
|
260
256
|
}
|
|
261
|
-
//
|
|
257
|
+
// Field fallback must never move content across
|
|
262
258
|
// sessions — session A's task carried into a snapshot saved under session
|
|
263
259
|
// B's id would pass compact-resume's session gate wearing the wrong
|
|
264
260
|
// badge. Fall back only when the existing snapshot has no session, this
|
|
@@ -335,7 +331,7 @@ export function cmdPostCompact(hippoRoot, options) {
|
|
|
335
331
|
storeBusy = saved.deferred;
|
|
336
332
|
if (!storeBusy && payload.transcriptPath !== null && options.afterSave) {
|
|
337
333
|
try {
|
|
338
|
-
options.afterSave(payload.transcriptPath,
|
|
334
|
+
options.afterSave(payload.transcriptPath, payload.cwd, log);
|
|
339
335
|
}
|
|
340
336
|
catch (err) {
|
|
341
337
|
log(`agent memory import failed: ${errorMessage(err)}`);
|