hippo-memory 1.55.0 → 1.57.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 +11 -0
- package/dist/api.d.ts +19 -9
- package/dist/api.js +112 -35
- package/dist/card-detail.d.ts +1 -1
- package/dist/card-detail.js +1 -1
- package/dist/cli/shared.d.ts +137 -0
- package/dist/cli/shared.js +830 -0
- package/dist/cli/sleep.d.ts +10 -0
- package/dist/cli/sleep.js +171 -0
- package/dist/cli.d.ts +0 -7
- package/dist/cli.js +313 -1806
- package/dist/config.d.ts +5 -0
- package/dist/config.js +21 -0
- package/dist/connectors/github/webhook.d.ts +19 -0
- package/dist/connectors/github/webhook.js +313 -0
- package/dist/connectors/slack/webhook.d.ts +22 -0
- package/dist/connectors/slack/webhook.js +203 -0
- package/dist/consolidate.js +3 -2
- package/dist/context-auto.d.ts +3 -0
- package/dist/context-auto.js +34 -0
- package/dist/customer-notes.js +2 -1
- package/dist/dashboard.js +2 -1
- package/dist/db.js +67 -1
- package/dist/decisions.js +2 -1
- package/dist/delivery-recorder.d.ts +127 -0
- package/dist/delivery-recorder.js +218 -0
- package/dist/eval-stats.d.ts +58 -0
- package/dist/eval-stats.js +111 -0
- package/dist/goals.d.ts +49 -25
- package/dist/goals.js +39 -22
- package/dist/graph-extract.js +1 -1
- package/dist/graph-recall.d.ts +1 -1
- package/dist/graph-recall.js +1 -1
- package/dist/graph.js +1 -1
- package/dist/hooks.d.ts +1 -3
- package/dist/hooks.js +2 -4
- package/dist/http-util.d.ts +31 -0
- package/dist/http-util.js +46 -0
- package/dist/incidents.js +2 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.js +5 -2
- package/dist/mcp/server.js +173 -285
- package/dist/memory.d.ts +19 -0
- package/dist/memory.js +38 -0
- package/dist/policies.js +2 -1
- package/dist/predictions.js +2 -1
- package/dist/processes.js +2 -1
- package/dist/project-briefs.js +3 -1
- package/dist/prompt-recall.js +1 -1
- package/dist/recall-history.d.ts +5 -0
- package/dist/recall-history.js +9 -0
- package/dist/recall-pipeline.d.ts +101 -0
- package/dist/recall-pipeline.js +313 -0
- package/dist/recall-scope.d.ts +22 -0
- package/dist/recall-scope.js +27 -1
- package/dist/recall-trace.d.ts +69 -0
- package/dist/recall-trace.js +136 -0
- package/dist/search.d.ts +0 -20
- package/dist/search.js +2 -49
- package/dist/server.js +1901 -2384
- package/dist/skills.js +2 -1
- package/dist/store-cards.d.ts +53 -0
- package/dist/store-cards.js +512 -0
- package/dist/store.d.ts +2 -89
- package/dist/store.js +6 -562
- package/dist/tenant.d.ts +22 -0
- package/dist/tenant.js +26 -0
- package/dist/token-ledger.d.ts +2 -0
- package/dist/token-ledger.js +5 -0
- package/dist/tokenize.d.ts +2 -0
- package/dist/tokenize.js +8 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +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 +2 -1
package/README.md
CHANGED
|
@@ -548,6 +548,17 @@ crashed, shows what was sent only. Re-reads usually bill at the provider's cache
|
|
|
548
548
|
a fraction of the full input price. Counts are estimates (characters / 4), the same estimate
|
|
549
549
|
every budget uses. Rows older than 90 days are pruned.
|
|
550
550
|
|
|
551
|
+
**See why a memory did or did not reach the agent.** Turn on the delivery ledger with
|
|
552
|
+
`{"deliveryLedger":{"enabled":true}}` in `.hippo/config.json` (off by default). The flag is
|
|
553
|
+
read from the store the token ledger writes to: the project's local store when it has one,
|
|
554
|
+
else the global store. Each per-prompt hook call then records one event (session, turn
|
|
555
|
+
number, whether the block was sent, reused, empty or disabled, counts and token totals) and
|
|
556
|
+
one row per candidate memory: emitted, reused or rejected, with the stage and the
|
|
557
|
+
reason it was dropped. With prompt recall on, recent memories dropped by the quality filter
|
|
558
|
+
are not recorded yet. It holds ids, hashes, counts and reasons only, never prompt or memory
|
|
559
|
+
text; the prompt hash is unsalted, so a very short prompt can be guessed. A ledger failure prints one stderr line and never changes what the hook prints. Rows
|
|
560
|
+
older than 90 days are pruned; at a heavy 300 prompts a day that is about 190 MB per store.
|
|
561
|
+
|
|
551
562
|
---
|
|
552
563
|
|
|
553
564
|
### Outcome feedback
|
package/dist/api.d.ts
CHANGED
|
@@ -17,8 +17,9 @@ import { type SessionHandoff } from './handoff.js';
|
|
|
17
17
|
import { type MemoryKind, type MemoryEntry } from './memory.js';
|
|
18
18
|
import { auditMemories, type AuditEvent, type AuditOp } from './audit.js';
|
|
19
19
|
import { autoShare } from './shared.js';
|
|
20
|
+
import type { DeliveryObserver } from './delivery-recorder.js';
|
|
20
21
|
import { type ApiKeyListItem } from './auth.js';
|
|
21
|
-
import { type RerankStep } from './search.js';
|
|
22
|
+
import { type RerankStep, type SearchResult } from './search.js';
|
|
22
23
|
import { consolidate } from './consolidate.js';
|
|
23
24
|
import { loadConfig } from './config.js';
|
|
24
25
|
import { deduplicateStore } from './dedupe.js';
|
|
@@ -91,9 +92,8 @@ export type { TokenSummary, TokenSurface, TokenSurfaceSummary } from './token-le
|
|
|
91
92
|
export type { FailureSummary } from './failure-log.js';
|
|
92
93
|
export { classifyOriginProject } from './project-identity.js';
|
|
93
94
|
/**
|
|
94
|
-
* v39 S4: the secret half of the ambient policy on its own, for
|
|
95
|
-
*
|
|
96
|
-
* exact-match). A flagged row is only admitted inside its owning project;
|
|
95
|
+
* v39 S4: the secret half of the ambient policy on its own, for callers
|
|
96
|
+
* that apply their own scope rule. A flagged row is only admitted inside its owning project;
|
|
97
97
|
* flagged rows with no project origin never ambient-inject.
|
|
98
98
|
*/
|
|
99
99
|
export declare function ambientSecretAdmit(e: MemoryEntry, currentProjectName: string): boolean;
|
|
@@ -269,16 +269,22 @@ export interface RecallOpts {
|
|
|
269
269
|
* Mirrors `suppressAvailabilityHint`'s pattern: callers that run their OWN
|
|
270
270
|
* tracing over a DIFFERENT result set must suppress api.recall's copy so
|
|
271
271
|
* the training corpus doesn't get a trace mislabeled as 'api' pipeline
|
|
272
|
-
* when the caller's actual user-visible results came from elsewhere.
|
|
273
|
-
*
|
|
274
|
-
*
|
|
275
|
-
* tracing is the reserved 'mcp' pipeline, a follow-up). HTTP / direct SDK
|
|
276
|
-
* callers leave this unset and get the trace.
|
|
272
|
+
* when the caller's actual user-visible results came from elsewhere. Under
|
|
273
|
+
* `showRanked` it also drops the 'mcp' trace of the shown list. HTTP /
|
|
274
|
+
* direct SDK callers leave this unset and get the trace.
|
|
277
275
|
*/
|
|
278
276
|
suppressRecallTrace?: boolean;
|
|
279
277
|
/** Set only by the MCP recall tool, which ranks with its own scorer and drops copies from its own final list: this call
|
|
280
278
|
* then keeps a memory that a merged row in the same result holds word for word. Other callers leave it unset. */
|
|
281
279
|
keepHeldCopies?: boolean;
|
|
280
|
+
/** MCP recall only: `retrieve` ranks the whole scoped store and strengthens and traces (pipeline 'mcp') just the ids this returns; `results` stays the window band. */
|
|
281
|
+
showRanked?: (ranking: StoreRanking, result: RecallResult) => readonly string[];
|
|
282
|
+
}
|
|
283
|
+
/** `ranked`: every scored row, best first, goal boost applied, entries as loaded; `pool`: the store after the scope filter. */
|
|
284
|
+
export interface StoreRanking {
|
|
285
|
+
ranked: SearchResult[];
|
|
286
|
+
pool: MemoryEntry[];
|
|
287
|
+
droppedByScope: number;
|
|
282
288
|
}
|
|
283
289
|
export interface ContinuityBlock {
|
|
284
290
|
activeSnapshot: TaskSnapshot | null;
|
|
@@ -945,6 +951,8 @@ export interface ContextOpts {
|
|
|
945
951
|
limit?: number;
|
|
946
952
|
pinnedOnly?: boolean;
|
|
947
953
|
scope?: string;
|
|
954
|
+
/** Envelope scope to match exactly, as in `recall`: admits that scope even when private, after the actor's scope check. */
|
|
955
|
+
exactScope?: string;
|
|
948
956
|
/** With `pinnedOnly`, also inject the N most recent writes that pass the
|
|
949
957
|
* quality floor (`isContentWorthStoring`, DF3). Filtering happens BEFORE
|
|
950
958
|
* the take-N, so a caller asking for 5 gets 5 qualifying entries rather
|
|
@@ -974,6 +982,8 @@ export interface ContextOpts {
|
|
|
974
982
|
prompt?: string;
|
|
975
983
|
/** What the budget pays for, from the caller that renders the block. Absent = the memory text alone. */
|
|
976
984
|
cost?: ContextCost;
|
|
985
|
+
/** @internal The CLI's delivery-ledger observer; it only reads, so selection is the same with or without it. */
|
|
986
|
+
deliveryObserver?: DeliveryObserver;
|
|
977
987
|
}
|
|
978
988
|
/** Budget prices in the text a caller prints, so the budget bounds what reaches the model. */
|
|
979
989
|
export interface ContextCost {
|
package/dist/api.js
CHANGED
|
@@ -16,7 +16,7 @@ import { detectInstruction } from './instruction-detect.js';
|
|
|
16
16
|
import { quarantineScopeFor, recordQuarantine, getQuarantineRow, listQuarantineRows, approveQuarantineRow, rejectQuarantineRow, } from './quarantine.js';
|
|
17
17
|
import { summarizeFailures } from './failure-log.js';
|
|
18
18
|
import { formatHandoffEvidenceLine } from './handoff.js';
|
|
19
|
-
import { createMemory, createSuccessor, applyOutcome, calculateStrength, CHURN_STALE_TAG, COMPACTION_MEMORY_TAG, } from './memory.js';
|
|
19
|
+
import { createMemory, createSuccessor, applyOutcome, calculateStrength, markRetrieved, CHURN_STALE_TAG, COMPACTION_MEMORY_TAG, } from './memory.js';
|
|
20
20
|
import { appendAuditEvent, auditQueryFields, queryAuditEvents, auditMemories, isContentWorthStoring, } from './audit.js';
|
|
21
21
|
import { promoteToGlobal, getGlobalRoot, autoShare, searchBothHybrid } from './shared.js';
|
|
22
22
|
import { writeRecallTrace, writeRecallTraceAtRoot, recordTraceOutcome } from './recall-trace.js';
|
|
@@ -24,7 +24,7 @@ import { evalNow } from './ablation.js';
|
|
|
24
24
|
import { archiveRawMemory } from './raw-archive.js';
|
|
25
25
|
import { createApiKey, listApiKeys, revokeApiKey, grantScope, ungrantScope, } from './auth.js';
|
|
26
26
|
import { applyGoalStackBoost } from './goals.js';
|
|
27
|
-
import {
|
|
27
|
+
import { estimateTokens, hybridSearch, physicsSearch, churnStaleFactor } from './search.js';
|
|
28
28
|
import { compareEntryIdentity, compareScoredResults } from './compare.js';
|
|
29
29
|
import { dropHeldCopies, duplicateKey, storedTextKeys } from './same-text.js';
|
|
30
30
|
import { scopeMatch } from './scope.js';
|
|
@@ -39,7 +39,7 @@ import { computeAmbientState } from './ambient.js';
|
|
|
39
39
|
import { loadPendingExtractionTenants, markPendingProcessedUpTo } from './graph.js';
|
|
40
40
|
import { extractGraph } from './graph-extract.js';
|
|
41
41
|
import { computePlanningFallacyOutput, } from './predictions.js';
|
|
42
|
-
import { detectAnchoring, hashQueryText, } from './recall-history.js';
|
|
42
|
+
import { detectAnchoring, hashQueryText, biasHintEnabled, } from './recall-history.js';
|
|
43
43
|
import { detectAvailabilityBias } from './availability.js';
|
|
44
44
|
/**
|
|
45
45
|
* Helper for building process-local (admin-by-default) Actor values. v1.12.0
|
|
@@ -107,23 +107,22 @@ export { classifyOriginProject } from './project-identity.js';
|
|
|
107
107
|
* injects inside its owning project; flagged rows with no project origin
|
|
108
108
|
* (''/null) never ambient-inject at all. Explicit recall is unaffected -
|
|
109
109
|
* recalling a secret is a deliberate act.
|
|
110
|
-
* - S2 envelope parity: private scopes
|
|
110
|
+
* - S2 envelope parity: private/quarantine scopes never inject unless `exactScope` names one.
|
|
111
111
|
* - S3 origin partition: other-project rows are excluded unless
|
|
112
112
|
* `includeCrossProject`.
|
|
113
113
|
*/
|
|
114
|
-
function ambientAdmitEntry(e, currentProjectName, includeCrossProject) {
|
|
114
|
+
function ambientAdmitEntry(e, currentProjectName, includeCrossProject, exactScope) {
|
|
115
115
|
if (!ambientSecretAdmit(e, currentProjectName))
|
|
116
116
|
return false;
|
|
117
|
-
if (!passesScopeFilterForRecall(e.scope ?? null,
|
|
117
|
+
if (!passesScopeFilterForRecall(e.scope ?? null, exactScope))
|
|
118
118
|
return false;
|
|
119
119
|
if (includeCrossProject)
|
|
120
120
|
return true;
|
|
121
121
|
return classifyOriginProject(e.origin_project, currentProjectName) !== 'cross-project';
|
|
122
122
|
}
|
|
123
123
|
/**
|
|
124
|
-
* v39 S4: the secret half of the ambient policy on its own, for
|
|
125
|
-
*
|
|
126
|
-
* exact-match). A flagged row is only admitted inside its owning project;
|
|
124
|
+
* v39 S4: the secret half of the ambient policy on its own, for callers
|
|
125
|
+
* that apply their own scope rule. A flagged row is only admitted inside its owning project;
|
|
127
126
|
* flagged rows with no project origin never ambient-inject.
|
|
128
127
|
*/
|
|
129
128
|
export function ambientSecretAdmit(e, currentProjectName) {
|
|
@@ -135,12 +134,19 @@ export function ambientSecretAdmit(e, currentProjectName) {
|
|
|
135
134
|
return origin === currentProjectName;
|
|
136
135
|
}
|
|
137
136
|
// The pinned-only branch needs pins and recent-N candidates, not the corpus; `recall` applies there only.
|
|
138
|
-
function loadAmbientEntries(hippoRoot, tenantId, pinnedOnly, includeRecent, admit, recall) {
|
|
137
|
+
function loadAmbientEntries(hippoRoot, tenantId, pinnedOnly, includeRecent, admit, recall, onQualityDrop) {
|
|
139
138
|
if (!pinnedOnly)
|
|
140
139
|
return { entries: loadAllEntries(hippoRoot, tenantId).filter(admit) };
|
|
141
140
|
// DF3's quality floor runs on the recent-N slice AFTER this load, so the load
|
|
142
141
|
// counts by it too, or it stops short of a store whose newest rows are junk.
|
|
143
|
-
const admitAmbient = (e) =>
|
|
142
|
+
const admitAmbient = (e) => {
|
|
143
|
+
if (!admit(e))
|
|
144
|
+
return false;
|
|
145
|
+
if (e.pinned || isContentWorthStoring(e.content))
|
|
146
|
+
return true;
|
|
147
|
+
onQualityDrop?.(e);
|
|
148
|
+
return false;
|
|
149
|
+
};
|
|
144
150
|
return loadAmbientCandidates(hippoRoot, tenantId, includeRecent, admitAmbient, recall);
|
|
145
151
|
}
|
|
146
152
|
// Share and promote copy a memory to the global store under a new id, so equal content is the only link.
|
|
@@ -237,6 +243,8 @@ export function recall(ctx, opts) {
|
|
|
237
243
|
export async function retrieve(ctx, opts) {
|
|
238
244
|
assertScopeRequestAllowed(ctx.actor, opts.scope);
|
|
239
245
|
const windowSize = recallWindowSize(opts);
|
|
246
|
+
if (opts.showRanked)
|
|
247
|
+
return retrieveFromStore(ctx, opts, windowSize, opts.showRanked);
|
|
240
248
|
let candidates = loadRecallSearchEntries(ctx.hippoRoot, opts.query, windowSize, ctx.tenantId, opts.scope, 'exact', false);
|
|
241
249
|
if (opts.mode === 'hybrid' || opts.mode === 'physics') {
|
|
242
250
|
const searchOpts = { budget: Infinity, hippoRoot: ctx.hippoRoot, scope: opts.scope ?? null };
|
|
@@ -250,6 +258,40 @@ export async function retrieve(ctx, opts) {
|
|
|
250
258
|
strengthenRetrieved(ctx.hippoRoot, result.results.map((r) => r.id), ctx.tenantId);
|
|
251
259
|
return result;
|
|
252
260
|
}
|
|
261
|
+
/** `retrieve` under `showRanked`: physics when `mode` says so, hybrid otherwise, over every admitted row. */
|
|
262
|
+
async function retrieveFromStore(ctx, opts, windowSize, show) {
|
|
263
|
+
const store = loadAllEntries(ctx.hippoRoot, ctx.tenantId);
|
|
264
|
+
const pool = store.filter((e) => passesScopeFilterForRecall(e.scope ?? null, opts.scope));
|
|
265
|
+
// No scope option: the scope boost follows HIPPO_SCOPE and the skill env, as MCP recall always ranked.
|
|
266
|
+
const searchOpts = { budget: Infinity, hippoRoot: ctx.hippoRoot };
|
|
267
|
+
let ranked = opts.mode === 'physics'
|
|
268
|
+
? await physicsSearch(opts.query, pool, { ...searchOpts, physicsConfig: loadConfig(ctx.hippoRoot).physics })
|
|
269
|
+
: await hybridSearch(opts.query, pool, searchOpts);
|
|
270
|
+
if (opts.sessionId && !opts.goalTag) {
|
|
271
|
+
const db = openHippoDb(ctx.hippoRoot);
|
|
272
|
+
try {
|
|
273
|
+
ranked = applyGoalStackBoost(db, ranked, { sessionId: opts.sessionId, tenantId: ctx.tenantId, limit: ranked.length });
|
|
274
|
+
}
|
|
275
|
+
finally {
|
|
276
|
+
closeHippoDb(db);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
const window = ranked.slice(0, windowSize).map((r) => r.entry);
|
|
280
|
+
const result = recallFrom(ctx, { ...opts, suppressRecallTrace: true }, windowSize, window);
|
|
281
|
+
const shown = show({ ranked, pool, droppedByScope: store.length - pool.length }, result);
|
|
282
|
+
strengthenRetrieved(ctx.hippoRoot, shown, ctx.tenantId);
|
|
283
|
+
if (!opts.suppressRecallTrace) {
|
|
284
|
+
const scores = new Map(ranked.map((r) => [r.entry.id, r.score]));
|
|
285
|
+
writeRecallTraceAtRoot(ctx.hippoRoot, {
|
|
286
|
+
tenantId: ctx.tenantId,
|
|
287
|
+
sessionId: opts.sessionId ?? null,
|
|
288
|
+
pipeline: 'mcp',
|
|
289
|
+
query: opts.query,
|
|
290
|
+
results: shown.map((id) => ({ memoryId: id, score: scores.get(id) ?? 0 })),
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
return result;
|
|
294
|
+
}
|
|
253
295
|
/** Contract preflight: throws before any store-touching work. */
|
|
254
296
|
function recallWindowSize(opts) {
|
|
255
297
|
// F5 (v1.6.5) preflight — codex P1: original guard fired AFTER
|
|
@@ -557,9 +599,9 @@ function recallFrom(ctx, opts, windowSize, all) {
|
|
|
557
599
|
// handle. v1.11.5 contract lock holds — api.recall does NOT write
|
|
558
600
|
// last_trace_id (tests/api-recall-no-side-effects.test.ts); a trace INSERT
|
|
559
601
|
// is the same observability class as the audit row it sits beside, not
|
|
560
|
-
// retrieval state. F2 fix: suppressed when the caller
|
|
561
|
-
//
|
|
562
|
-
//
|
|
602
|
+
// retrieval state. F2 fix: suppressed when the caller traces its own,
|
|
603
|
+
// different result set (retrieve under showRanked traces the shown list as
|
|
604
|
+
// 'mcp'). Fail-soft internally; never throws.
|
|
563
605
|
if (!opts.suppressRecallTrace) {
|
|
564
606
|
writeRecallTrace(db, {
|
|
565
607
|
tenantId: ctx.tenantId,
|
|
@@ -649,7 +691,7 @@ function recallFrom(ctx, opts, windowSize, all) {
|
|
|
649
691
|
// detect call returns null and api.recall's anchoringHint stays absent.
|
|
650
692
|
let anchoringHint = null;
|
|
651
693
|
let suppressedByInterferenceCount = 0;
|
|
652
|
-
if (
|
|
694
|
+
if (biasHintEnabled('anchoring') && opts.recallHistory) {
|
|
653
695
|
const queryHash = hashQueryText(opts.query);
|
|
654
696
|
const topMemoryId = rankedOut[0]?.id ?? null;
|
|
655
697
|
anchoringHint = detectAnchoring(opts.recallHistory, queryHash, topMemoryId);
|
|
@@ -702,7 +744,7 @@ function recallFrom(ctx, opts, windowSize, all) {
|
|
|
702
744
|
// opts.recallHistory gate above so we never double-emit the audit op. Audit
|
|
703
745
|
// emission is pipeline-local, mirroring the J1 block above.
|
|
704
746
|
let availabilityHint = null;
|
|
705
|
-
if (
|
|
747
|
+
if (biasHintEnabled('availability') && !opts.suppressAvailabilityHint) {
|
|
706
748
|
availabilityHint = detectAvailabilityBias({
|
|
707
749
|
topK: baseSlice.map((e) => ({ id: e.id, created: e.created })),
|
|
708
750
|
pool: entries.map((e) => ({ id: e.id, created: e.created })),
|
|
@@ -1544,6 +1586,8 @@ export async function getContext(ctx, opts = {}) {
|
|
|
1544
1586
|
const limit = opts.limit ?? Number.POSITIVE_INFINITY;
|
|
1545
1587
|
const includeRecent = opts.includeRecent ?? 0;
|
|
1546
1588
|
const activeScope = opts.scope ?? '';
|
|
1589
|
+
assertScopeRequestAllowed(ctx.actor, opts.exactScope);
|
|
1590
|
+
const exactScope = opts.exactScope || undefined;
|
|
1547
1591
|
if (budget <= 0) {
|
|
1548
1592
|
return { entries: [], tokens: 0 };
|
|
1549
1593
|
}
|
|
@@ -1555,10 +1599,7 @@ export async function getContext(ctx, opts = {}) {
|
|
|
1555
1599
|
const primaryIsGlobal = isGlobalStoreRoot(ctx.hippoRoot);
|
|
1556
1600
|
const hasLocalTaskState = hasLocal && !primaryIsGlobal;
|
|
1557
1601
|
// v39 memory scope isolation (docs/plans/2026-07-01-memory-scope-isolation.md).
|
|
1558
|
-
// S2: envelope-filter parity with api.recall
|
|
1559
|
-
// scopes and quarantine buckets never inject. `requested` is deliberately
|
|
1560
|
-
// undefined: opts.scope is the scope-TAG boost input here, not an
|
|
1561
|
-
// envelope-scope request (api.recall's exact-match semantics don't apply).
|
|
1602
|
+
// S2: envelope-filter parity with api.recall; opts.scope is only the tag boost, opts.exactScope the envelope request.
|
|
1562
1603
|
// S3: origin partition - other-project memories are excluded unless the
|
|
1563
1604
|
// caller explicitly asks for them (crossProject) or isolation is disabled.
|
|
1564
1605
|
const config = loadConfig(ctx.hippoRoot);
|
|
@@ -1579,6 +1620,10 @@ export async function getContext(ctx, opts = {}) {
|
|
|
1579
1620
|
? cost.entry({ entry, isGlobal, promptRecall, origin: entry.origin_project ?? null, category: classifyOriginProject(entry.origin_project, currentProjectName) })
|
|
1580
1621
|
: estimateTokens(entry.content);
|
|
1581
1622
|
const blockBudget = pinnedOnly && opts.budget === undefined ? config.pinnedInject.budget : budget;
|
|
1623
|
+
const obs = opts.deliveryObserver;
|
|
1624
|
+
obs?.facts({ projectName: currentProjectName, budgetTokens: blockBudget, promptRecall: promptRecallPending });
|
|
1625
|
+
if (pinnedOnly && !config.pinnedInject.enabled)
|
|
1626
|
+
obs?.disabled();
|
|
1582
1627
|
let left = cost
|
|
1583
1628
|
? Math.max(0, blockBudget - cost.fixed(blockBudget, { cross: includeCrossProject, promptRecall: promptRecallPending, ambient: !pinnedOnly && config.ambient.enabled }))
|
|
1584
1629
|
: blockBudget;
|
|
@@ -1601,9 +1646,8 @@ export async function getContext(ctx, opts = {}) {
|
|
|
1601
1646
|
sessionId: opts.currentSessionId,
|
|
1602
1647
|
})
|
|
1603
1648
|
: null;
|
|
1604
|
-
// W1:
|
|
1605
|
-
|
|
1606
|
-
const activeSnapshot = rawActiveSnapshot && passesScopeFilterForRecall(rowScope(rawActiveSnapshot), undefined)
|
|
1649
|
+
// W1: the same envelope rule ambientAdmitEntry applies to memory rows.
|
|
1650
|
+
const activeSnapshot = rawActiveSnapshot && passesScopeFilterForRecall(rowScope(rawActiveSnapshot), exactScope)
|
|
1607
1651
|
? rawActiveSnapshot
|
|
1608
1652
|
: null;
|
|
1609
1653
|
// Key on the RAW snapshot: a scope-hidden active session must not fall through to another session's ambient handoff.
|
|
@@ -1617,7 +1661,7 @@ export async function getContext(ctx, opts = {}) {
|
|
|
1617
1661
|
// codex P2: admit scope in SQL so a newer denied row can't hide an older eligible one before LIMIT 1.
|
|
1618
1662
|
scopeFilter: 'default-deny',
|
|
1619
1663
|
});
|
|
1620
|
-
const sessionHandoff = rawSessionHandoff && passesScopeFilterForRecall(rowScope(rawSessionHandoff),
|
|
1664
|
+
const sessionHandoff = rawSessionHandoff && passesScopeFilterForRecall(rowScope(rawSessionHandoff), exactScope)
|
|
1621
1665
|
? rawSessionHandoff
|
|
1622
1666
|
: null;
|
|
1623
1667
|
// Raw session id here too: each event is admitted on its own scope, same as recall and the CLI.
|
|
@@ -1625,11 +1669,12 @@ export async function getContext(ctx, opts = {}) {
|
|
|
1625
1669
|
? listSessionEvents(ctx.hippoRoot, ctx.tenantId, {
|
|
1626
1670
|
session_id: rawActiveSnapshot.session_id,
|
|
1627
1671
|
limit: 5,
|
|
1628
|
-
}).filter((e) => passesScopeFilterForRecall(rowScope(e),
|
|
1672
|
+
}).filter((e) => passesScopeFilterForRecall(rowScope(e), exactScope))
|
|
1629
1673
|
: [];
|
|
1630
1674
|
const shownSnapshot = activeSnapshot && (!cost || pays(cost.snapshot(activeSnapshot))) ? activeSnapshot : null;
|
|
1631
1675
|
const shownHandoff = sessionHandoff && (!cost || pays(cost.handoff(sessionHandoff))) ? sessionHandoff : null;
|
|
1632
1676
|
const shownEvents = recentSessionEvents.length > 0 && (!cost || pays(cost.trail(recentSessionEvents))) ? recentSessionEvents : [];
|
|
1677
|
+
obs?.sections(Number(shownSnapshot !== null) + Number(shownHandoff !== null) + Number(shownEvents.length > 0), Number(activeSnapshot !== shownSnapshot) + Number(sessionHandoff !== shownHandoff) + Number(recentSessionEvents.length !== shownEvents.length));
|
|
1633
1678
|
const transcriptHandoffSession = shownHandoff?.evidence?.derivedFrom === 'transcript' ? shownHandoff.sessionId : null;
|
|
1634
1679
|
let digestHiddenForHandoff = false;
|
|
1635
1680
|
const ambientAdmit = (e) => {
|
|
@@ -1638,7 +1683,7 @@ export async function getContext(ctx, opts = {}) {
|
|
|
1638
1683
|
digestHiddenForHandoff = true;
|
|
1639
1684
|
return false;
|
|
1640
1685
|
}
|
|
1641
|
-
return ambientAdmitEntry(e, currentProjectName, includeCrossProject);
|
|
1686
|
+
return ambientAdmitEntry(e, currentProjectName, includeCrossProject, exactScope);
|
|
1642
1687
|
};
|
|
1643
1688
|
const ownSessionId = opts.currentSessionId || '';
|
|
1644
1689
|
// Inside admit, not after the load, so the loader's window widens past a session's own items.
|
|
@@ -1647,12 +1692,14 @@ export async function getContext(ctx, opts = {}) {
|
|
|
1647
1692
|
e.tags.includes(COMPACTION_MEMORY_TAG);
|
|
1648
1693
|
// Superseded rows never inject; which rows reach ambientAdmitEntry matters because it regex-scans content for secrets.
|
|
1649
1694
|
const admit = (e) => !e.superseded_by && !isOwnCompactionItem(e) && ambientAdmit(e);
|
|
1695
|
+
const loadAdmit = obs ? obs.watchAdmit(admit) : admit;
|
|
1696
|
+
const qualityDrop = (isGlobal) => obs && !promptRecallPending ? (e) => obs.qualityDropped(e, isGlobal) : undefined;
|
|
1650
1697
|
// Tenant-scoped loads (v1.11.1 lesson: NEVER resolveTenantId({}) here).
|
|
1651
1698
|
const localLoad = hasLocal
|
|
1652
|
-
? loadAmbientEntries(ctx.hippoRoot, ctx.tenantId, pinnedOnly, includeRecent,
|
|
1699
|
+
? loadAmbientEntries(ctx.hippoRoot, ctx.tenantId, pinnedOnly, includeRecent, loadAdmit, recallRequest, qualityDrop(primaryIsGlobal))
|
|
1653
1700
|
: { entries: [] };
|
|
1654
1701
|
const globalLoad = hasGlobal && !primaryIsGlobal
|
|
1655
|
-
? loadAmbientEntries(globalRoot, ctx.tenantId, pinnedOnly, includeRecent,
|
|
1702
|
+
? loadAmbientEntries(globalRoot, ctx.tenantId, pinnedOnly, includeRecent, loadAdmit, recallRequest, qualityDrop(true))
|
|
1656
1703
|
: { entries: [] };
|
|
1657
1704
|
let localEntries = localLoad.entries;
|
|
1658
1705
|
let globalEntries = globalLoad.entries;
|
|
@@ -1677,7 +1724,10 @@ export async function getContext(ctx, opts = {}) {
|
|
|
1677
1724
|
// Effective budget: explicit opts.budget wins over config, less what the sections took.
|
|
1678
1725
|
const effBudget = left;
|
|
1679
1726
|
const nowP = evalNow(); // honors HIPPO_FAKE_NOW (eval-only; see ablation.ts)
|
|
1727
|
+
obs?.offer(localEntries, primaryIsGlobal);
|
|
1728
|
+
obs?.offer(globalEntries, true);
|
|
1680
1729
|
const [localPool, globalPool] = oneCopyPerMemory(localEntries, globalEntries, nowP);
|
|
1730
|
+
obs?.dropMissing([...localEntries, ...globalEntries], [...localPool, ...globalPool], 'load', 'duplicate');
|
|
1681
1731
|
const selectedIds = new Set();
|
|
1682
1732
|
let usedP = 0;
|
|
1683
1733
|
// Pinned entries are explicit user intent, the recent-N list an automatic
|
|
@@ -1743,8 +1793,23 @@ export async function getContext(ctx, opts = {}) {
|
|
|
1743
1793
|
// Candidates came off the ambient load's own connection (recallRequest above), not a fresh open.
|
|
1744
1794
|
// A candidate carrying a pin's text would inject that memory a second time.
|
|
1745
1795
|
const pinnedText = new Set(rankedPinned.map((r) => r.entry.content));
|
|
1746
|
-
const
|
|
1747
|
-
|
|
1796
|
+
const ineligibleReason = (e) => !admit(e) ? 'scope'
|
|
1797
|
+
: e.pinned ? 'pinned'
|
|
1798
|
+
: !isContentWorthStoring(e.content) ? 'quality'
|
|
1799
|
+
: pinnedText.has(e.content) ? 'duplicate'
|
|
1800
|
+
: null;
|
|
1801
|
+
const eligible = (e) => {
|
|
1802
|
+
const why = ineligibleReason(e);
|
|
1803
|
+
if (why !== null && why !== 'pinned')
|
|
1804
|
+
obs?.reject(e, 'eligible', why);
|
|
1805
|
+
return why === null;
|
|
1806
|
+
};
|
|
1807
|
+
obs?.offer(localLoad.recall ?? [], primaryIsGlobal, 'prompt-recall');
|
|
1808
|
+
obs?.offer(globalLoad.recall ?? [], true, 'prompt-recall');
|
|
1809
|
+
const localEligible = (localLoad.recall ?? []).filter(eligible);
|
|
1810
|
+
const globalEligible = (globalLoad.recall ?? []).filter(eligible);
|
|
1811
|
+
const [localCandidates, globalCandidates] = oneCopyPerMemory(localEligible, globalEligible, nowP);
|
|
1812
|
+
obs?.dropMissing([...localEligible, ...globalEligible], [...localCandidates, ...globalCandidates], 'eligible', 'duplicate');
|
|
1748
1813
|
const seenCandidateIds = new Set();
|
|
1749
1814
|
const candidateItems = [];
|
|
1750
1815
|
// Local wins the id collision (a global row synced into the local store).
|
|
@@ -1761,12 +1826,15 @@ export async function getContext(ctx, opts = {}) {
|
|
|
1761
1826
|
candidateItems.push({ id: e.id, tokens: contentTokens(e.content), entry: e, isGlobal: true });
|
|
1762
1827
|
}
|
|
1763
1828
|
const gated = gatePromptRecall(p, candidateItems, gate);
|
|
1829
|
+
obs?.gated(p, candidateItems, gate, gated);
|
|
1764
1830
|
for (const g of gated) {
|
|
1765
1831
|
if (selectedIds.has(g.item.id))
|
|
1766
1832
|
continue;
|
|
1767
1833
|
const tokens = price(g.item.entry, g.item.isGlobal, true);
|
|
1768
|
-
if (usedP + tokens > recentBudget)
|
|
1834
|
+
if (usedP + tokens > recentBudget) {
|
|
1835
|
+
obs?.reject(g.item.entry, 'budget', 'budget', g.score, tokens);
|
|
1769
1836
|
continue;
|
|
1837
|
+
}
|
|
1770
1838
|
selectedItems.push({ entry: g.item.entry, score: g.score, tokens, isGlobal: g.item.isGlobal, promptRecall: true });
|
|
1771
1839
|
selectedIds.add(g.item.id);
|
|
1772
1840
|
usedP += tokens;
|
|
@@ -1818,8 +1886,10 @@ export async function getContext(ctx, opts = {}) {
|
|
|
1818
1886
|
for (const r of recent) {
|
|
1819
1887
|
if (selectedIds.has(r.entry.id))
|
|
1820
1888
|
continue;
|
|
1821
|
-
if (usedP + r.tokens > recentBudget)
|
|
1889
|
+
if (usedP + r.tokens > recentBudget) {
|
|
1890
|
+
obs?.reject(r.entry, 'budget', 'budget', r.score, r.tokens);
|
|
1822
1891
|
continue;
|
|
1892
|
+
}
|
|
1823
1893
|
selectedItems.push(r);
|
|
1824
1894
|
selectedIds.add(r.entry.id);
|
|
1825
1895
|
usedP += r.tokens;
|
|
@@ -1834,8 +1904,10 @@ export async function getContext(ctx, opts = {}) {
|
|
|
1834
1904
|
for (const r of rankedPinned) {
|
|
1835
1905
|
if (selectedIds.has(r.entry.id))
|
|
1836
1906
|
continue;
|
|
1837
|
-
if (usedP + r.tokens > effBudget)
|
|
1907
|
+
if (usedP + r.tokens > effBudget) {
|
|
1908
|
+
obs?.reject(r.entry, 'budget', 'budget', r.score, r.tokens);
|
|
1838
1909
|
continue;
|
|
1910
|
+
}
|
|
1839
1911
|
selectedItems.push(r);
|
|
1840
1912
|
selectedIds.add(r.entry.id);
|
|
1841
1913
|
usedP += r.tokens;
|
|
@@ -1968,9 +2040,13 @@ export async function getContext(ctx, opts = {}) {
|
|
|
1968
2040
|
}
|
|
1969
2041
|
}
|
|
1970
2042
|
if (limit < selectedItems.length) {
|
|
1971
|
-
|
|
2043
|
+
const cut = selectedItems.slice(0, limit);
|
|
2044
|
+
obs?.dropMissing(selectedItems.map((r) => r.entry), cut.map((r) => r.entry), 'limit', 'limit');
|
|
2045
|
+
selectedItems = cut;
|
|
1972
2046
|
}
|
|
1973
|
-
|
|
2047
|
+
const heldDropped = dropHeldCopies(selectedItems, (r) => r.entry); // after the last cut, so a merged row that was cut hides nothing
|
|
2048
|
+
obs?.dropMissing(selectedItems.map((r) => r.entry), heldDropped.map((r) => r.entry), 'limit', 'duplicate');
|
|
2049
|
+
selectedItems = heldDropped;
|
|
1974
2050
|
totalTokens = selectedItems.reduce((sum, r) => sum + r.tokens, 0);
|
|
1975
2051
|
// v39: annotate every returned entry with its origin and how it relates to
|
|
1976
2052
|
// the active project, so renderers can demarcate cross-project inclusions.
|
|
@@ -1979,6 +2055,7 @@ export async function getContext(ctx, opts = {}) {
|
|
|
1979
2055
|
origin: r.entry.origin_project ?? null,
|
|
1980
2056
|
category: classifyOriginProject(r.entry.origin_project, currentProjectName),
|
|
1981
2057
|
}));
|
|
2058
|
+
obs?.selected(selectedItems);
|
|
1982
2059
|
if (selectedItems.length === 0 &&
|
|
1983
2060
|
!shownSnapshot &&
|
|
1984
2061
|
!shownHandoff &&
|
package/dist/card-detail.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Card, CardComment, CardRun } from './card.js';
|
|
2
2
|
import type { SessionHandoff } from './handoff.js';
|
|
3
|
-
import { loadCardDeps } from './store.js';
|
|
3
|
+
import { loadCardDeps } from './store-cards.js';
|
|
4
4
|
/** A card plus everything `hippo card show` prints about it. */
|
|
5
5
|
export interface CardDetail {
|
|
6
6
|
card: Card;
|
package/dist/card-detail.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { loadCard, loadCardComments, loadCardDeps, loadCardRuns, loadLatestHandoffForCard } from './store.js';
|
|
1
|
+
import { loadCard, loadCardComments, loadCardDeps, loadCardRuns, loadLatestHandoffForCard } from './store-cards.js';
|
|
2
2
|
/** Loads the detail behind `hippo card show` and `GET /api/cards/:id` (null when the tenant has no such card); five separate reads, so a write landing between them can show a mixed view, as `card show` always could. */
|
|
3
3
|
export function loadCardDetail(hippoRoot, tenantId, id) {
|
|
4
4
|
const card = loadCard(hippoRoot, tenantId, id);
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { TaskSnapshot, SessionEvent } from '../store.js';
|
|
2
|
+
import type { HandoffEvidence, SessionHandoff } from '../handoff.js';
|
|
3
|
+
import { type SearchResult } from '../search.js';
|
|
4
|
+
import { type HippoConfig } from '../config.js';
|
|
5
|
+
import { openHippoDb } from '../db.js';
|
|
6
|
+
import { type ImportReport } from '../agent-memories/report.js';
|
|
7
|
+
import { type ChurnStaleResult } from '../invalidation.js';
|
|
8
|
+
import { type AuditOp } from '../audit.js';
|
|
9
|
+
import { type ServerInfo } from '../server-detect.js';
|
|
10
|
+
import type { RecallSearchOpts } from '../recall-pipeline.js';
|
|
11
|
+
export declare function parseLimitFlag(value: string | boolean | string[] | undefined): number;
|
|
12
|
+
export declare function parseCountFlag(value: string | boolean | string[] | undefined): number;
|
|
13
|
+
export declare function parseBudgetFlag(value: string | boolean | string[] | undefined, fallback: number): number;
|
|
14
|
+
/**
|
|
15
|
+
* Emit an audit event against `hippoRoot`'s db. Opens its own short-lived
|
|
16
|
+
* connection so callers don't have to thread a db handle. Swallows all errors
|
|
17
|
+
* — audit must never crash a CLI command.
|
|
18
|
+
*/
|
|
19
|
+
export declare function emitCliAudit(hippoRoot: string, op: AuditOp, targetId?: string, metadata?: Record<string, unknown>): void;
|
|
20
|
+
export declare function requireInit(hippoRoot: string): void;
|
|
21
|
+
/** Runs detectChurnStale against every store this repo's memories can live in. */
|
|
22
|
+
export declare function runChurnStaleForRepo(hippoRoot: string, dryRun: boolean): {
|
|
23
|
+
root: string;
|
|
24
|
+
result: ChurnStaleResult;
|
|
25
|
+
}[];
|
|
26
|
+
/**
|
|
27
|
+
* Run an HTTP-routed command if a `hippo serve` instance is detected for
|
|
28
|
+
* `hippoRoot`. Returns:
|
|
29
|
+
* - true if the HTTP path ran (success OR a structured server error that
|
|
30
|
+
* was already surfaced to stdout/stderr by `httpFn`),
|
|
31
|
+
* - false if no server was detected, or if the detected pidfile turned out
|
|
32
|
+
* to be stale (connection refused). On stale, the pidfile is removed
|
|
33
|
+
* if it still names that dead server (a newer one may have replaced
|
|
34
|
+
* it) and the caller should fall back to the direct path.
|
|
35
|
+
*
|
|
36
|
+
* Stale pidfiles must self-heal, not crash.
|
|
37
|
+
* When HIPPO_REQUIRE_SERVER is set, both fallback paths throw instead of
|
|
38
|
+
* returning false, so a missing server fails loudly rather than silently
|
|
39
|
+
* degrading to direct mode.
|
|
40
|
+
*/
|
|
41
|
+
export declare function runViaServerIfAvailable(hippoRoot: string, httpFn: (info: ServerInfo, apiKey: string | undefined) => Promise<void>): Promise<boolean>;
|
|
42
|
+
export declare function fmt(n: number, digits?: number): string;
|
|
43
|
+
export declare function recallEntryText(r: SearchResult, query: string, showWhy: boolean, isGlobal: boolean): string;
|
|
44
|
+
export declare function recallHeading(entries: number, tokens: number, query: string): string;
|
|
45
|
+
/** One line when an agent memory import moved anything; its warnings go to stderr. */
|
|
46
|
+
export declare function printAgentImport(report: ImportReport, indent?: string): void;
|
|
47
|
+
/** The first hippo block in `text` and the agent whose current or shipped text it is; `owner` is undefined for an edited block. */
|
|
48
|
+
export declare function hippoBlock(text: string): {
|
|
49
|
+
start: number;
|
|
50
|
+
end: number;
|
|
51
|
+
eol: string;
|
|
52
|
+
inner: string;
|
|
53
|
+
owner?: string;
|
|
54
|
+
} | null;
|
|
55
|
+
/** Adds hippo's two Codex hooks and says what changed; each install ends on the trust reminder, since Codex skips an untrusted hook. */
|
|
56
|
+
export declare function installCodexMemoryHooks(indent: string): void;
|
|
57
|
+
/**
|
|
58
|
+
* Set up a machine-level daily runner that sweeps all registered Hippo
|
|
59
|
+
* workspaces.
|
|
60
|
+
* Linux/macOS: writes to user crontab.
|
|
61
|
+
* Windows: creates a scheduled task.
|
|
62
|
+
* Skips if already installed.
|
|
63
|
+
*/
|
|
64
|
+
export declare function setupDailySchedule(globalRoot: string): void;
|
|
65
|
+
export type CliFlags = Record<string, string | boolean | string[]>;
|
|
66
|
+
export type EngineFlags = Pick<RecallSearchOpts, 'usePhysics' | 'physicsConfig' | 'mmr' | 'mmrLambda' | 'localBump'>;
|
|
67
|
+
export declare function parseAsOfFlag(flags: CliFlags): string | undefined;
|
|
68
|
+
/** --physics forces physics, --classic forces BM25+cosine, else physics unless the config turns it off. */
|
|
69
|
+
export declare function engineFlags(flags: CliFlags, config: HippoConfig): EngineFlags;
|
|
70
|
+
/**
|
|
71
|
+
* Detached worker that counts re-reads, runs sleep, then capture. Invoked via the internal
|
|
72
|
+
* `__session-end-worker` subcommand (not user-facing). Failures in one stage
|
|
73
|
+
* do not block the other.
|
|
74
|
+
*/
|
|
75
|
+
export declare function collectHandoffEvidence(cwd: string, testStatus: HandoffEvidence['testStatus']): HandoffEvidence;
|
|
76
|
+
/** A folder without its own store never sleeps at session end, so its project's agent notes go to the global store here. */
|
|
77
|
+
export declare function logSessionEndImport(logFile: string | null, transcriptPath: string | undefined): void;
|
|
78
|
+
/**
|
|
79
|
+
* Best-effort log line for the snapshot-close step in
|
|
80
|
+
* `cmdSessionEndWorker`. `cmdSleep`/`cmdCapture` each tee console output to
|
|
81
|
+
* `logFile` only for their own duration (the tee is restored before this
|
|
82
|
+
* runs), so a plain `console.log` here would be silently discarded under
|
|
83
|
+
* the detached worker's `stdio: 'ignore'` — write straight to the file
|
|
84
|
+
* instead, matching capture.ts's `appendPreCompactLog` convention.
|
|
85
|
+
*/
|
|
86
|
+
export declare function appendSessionEndCloseLog(logFile: string | null, message: string, opts?: {
|
|
87
|
+
startFresh?: boolean;
|
|
88
|
+
}): void;
|
|
89
|
+
export declare function printActiveTaskSnapshot(snapshot: TaskSnapshot): void;
|
|
90
|
+
export declare function printSessionEvents(events: SessionEvent[]): void;
|
|
91
|
+
export declare function printHandoff(handoff: SessionHandoff): void;
|
|
92
|
+
export declare function cardStringFlag(flags: Record<string, string | boolean | string[]>, key: string): string | undefined;
|
|
93
|
+
export declare function hostSessionId(): string | undefined;
|
|
94
|
+
/**
|
|
95
|
+
* Compaction drops the pinned blocks the per-prompt hook injected
|
|
96
|
+
* earlier, so record a `reset` for the payload's session and the next prompt
|
|
97
|
+
* injects again even if nothing changed. `requiredSource` limits it to hook
|
|
98
|
+
* payloads with that `source` (SessionStart fires for other reasons too).
|
|
99
|
+
* Best-effort and silent: a malformed payload records nothing.
|
|
100
|
+
*/
|
|
101
|
+
export declare function resetHookInjection(hippoRoot: string, stdinText: string | undefined, requiredSource: string | null): void;
|
|
102
|
+
/**
|
|
103
|
+
* Run `fn` with console.log captured; returns the captured lines joined by
|
|
104
|
+
* newlines (what the same calls would have printed, minus the final newline).
|
|
105
|
+
*/
|
|
106
|
+
export declare function captureConsole(fn: () => void): string;
|
|
107
|
+
/**
|
|
108
|
+
* The store a Claude Code hook writes to: the project store when there is
|
|
109
|
+
* one, else an existing global store, else the project path (which the hook
|
|
110
|
+
* then skips, since hooks fire in every directory and must not create one).
|
|
111
|
+
* Pre-compact and compact-resume must agree, or a snapshot saved to one store
|
|
112
|
+
* is looked for in the other.
|
|
113
|
+
*/
|
|
114
|
+
export declare function hookStoreRoot(hippoRoot: string): string;
|
|
115
|
+
/**
|
|
116
|
+
* Run `fn` against the token ledger's store: the local store when it is
|
|
117
|
+
* initialized, else the global one (the per-prompt hook runs in directories
|
|
118
|
+
* without a local store). Best-effort: returns undefined and never throws,
|
|
119
|
+
* because a ledger failure must not break context or recall.
|
|
120
|
+
*/
|
|
121
|
+
export declare function withLedgerDb<T>(hippoRoot: string, fn: (db: ReturnType<typeof openHippoDb>) => T): T | undefined;
|
|
122
|
+
export declare function learnFromRepo(hippoRoot: string, repoPath: string, days: number, label?: string): {
|
|
123
|
+
added: number;
|
|
124
|
+
skipped: number;
|
|
125
|
+
lowInfo: number;
|
|
126
|
+
};
|
|
127
|
+
export declare const HOOK_MARKERS: {
|
|
128
|
+
start: string;
|
|
129
|
+
end: string;
|
|
130
|
+
};
|
|
131
|
+
export declare const HOOKS: Record<string, {
|
|
132
|
+
file: string;
|
|
133
|
+
content: string;
|
|
134
|
+
description: string;
|
|
135
|
+
}>;
|
|
136
|
+
export declare function resolveAuthRoot(hippoRoot: string, flags: Record<string, string | boolean | string[]>): string;
|
|
137
|
+
//# sourceMappingURL=shared.d.ts.map
|