hippo-memory 1.63.1 → 1.64.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 +13 -5
- package/dist/agent-memories/claude-code.js +3 -3
- package/dist/ambient-store.d.ts +2 -0
- package/dist/ambient-store.js +4 -3
- package/dist/api/assemble.d.ts +2 -0
- package/dist/api/assemble.js +11 -7
- package/dist/api/auth.d.ts +15 -11
- package/dist/api/auth.js +116 -22
- package/dist/api/context-select.d.ts +5 -3
- package/dist/api/context-select.js +6 -11
- package/dist/api/context-types.d.ts +2 -1
- package/dist/api/context.d.ts +1 -1
- package/dist/api/context.js +79 -55
- package/dist/api/dormant.d.ts +5 -5
- package/dist/api/dormant.js +13 -9
- package/dist/api/drill-down.d.ts +2 -0
- package/dist/api/drill-down.js +12 -7
- package/dist/api/forget.d.ts +1 -1
- package/dist/api/forget.js +8 -4
- package/dist/api/learn.js +5 -3
- package/dist/api/outcome.d.ts +1 -1
- package/dist/api/outcome.js +2 -1
- package/dist/api/promote.js +8 -5
- package/dist/api/recall-types.d.ts +4 -2
- package/dist/api/recall.js +51 -36
- package/dist/api/remember.d.ts +7 -0
- package/dist/api/remember.js +26 -2
- package/dist/api/sleep-run.d.ts +28 -0
- package/dist/api/sleep-run.js +267 -0
- package/dist/api/sleep.d.ts +5 -45
- package/dist/api/sleep.js +5 -252
- package/dist/api/types.d.ts +5 -0
- package/dist/api/types.js +4 -0
- package/dist/audit-prune.js +1 -1
- package/dist/audit.d.ts +1 -3
- package/dist/audit.js +7 -103
- package/dist/auth.d.ts +48 -4
- package/dist/auth.js +111 -42
- package/dist/autolearn.js +2 -2
- package/dist/capture/caller-session.d.ts +16 -0
- package/dist/capture/caller-session.js +58 -0
- package/dist/capture/command.d.ts +19 -1
- package/dist/capture/command.js +28 -21
- package/dist/capture/compact-resume-caller.d.ts +11 -0
- package/dist/capture/compact-resume-caller.js +35 -0
- package/dist/capture/compact.d.ts +0 -14
- package/dist/capture/compact.js +7 -160
- package/dist/capture/compaction-items-caller.d.ts +16 -0
- package/dist/capture/compaction-items-caller.js +32 -0
- package/dist/capture/extract.d.ts +2 -14
- package/dist/capture/extract.js +135 -346
- package/dist/capture/failure-caller.d.ts +21 -0
- package/dist/capture/failure-caller.js +74 -0
- package/dist/capture/failure-reading.d.ts +44 -0
- package/dist/capture/failure-reading.js +78 -0
- package/dist/capture/pre-compact-caller.d.ts +16 -0
- package/dist/capture/pre-compact-caller.js +39 -0
- package/dist/capture/session-end-caller.d.ts +21 -0
- package/dist/capture/session-end-caller.js +30 -0
- package/dist/capture/session-texts.d.ts +14 -0
- package/dist/capture/session-texts.js +42 -0
- package/dist/capture/transcript.d.ts +6 -1
- package/dist/capture/transcript.js +20 -16
- package/dist/capture/working-state.d.ts +21 -0
- package/dist/capture/working-state.js +154 -0
- package/dist/capture-error.d.ts +8 -22
- package/dist/capture-error.js +9 -61
- package/dist/cli/audit.js +25 -0
- package/dist/cli/auth.js +4 -3
- package/dist/cli/context.js +13 -158
- package/dist/cli/continuity.js +2 -1
- package/dist/cli/init.js +5 -3
- package/dist/cli/last-sleep.d.ts +6 -0
- package/dist/cli/last-sleep.js +68 -0
- package/dist/cli/quality-repair-once.d.ts +3 -0
- package/dist/cli/quality-repair-once.js +36 -0
- package/dist/cli/recall.js +2 -1
- package/dist/cli/remember.d.ts +2 -0
- package/dist/cli/remember.js +4 -1
- package/dist/cli/session-hooks.d.ts +0 -3
- package/dist/cli/session-hooks.js +12 -49
- package/dist/cli/setup.js +10 -2
- package/dist/cli/shared.d.ts +7 -14
- package/dist/cli/shared.js +19 -78
- package/dist/cli/sleep.js +6 -3
- package/dist/cli/usage.js +18 -9
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +3 -2
- package/dist/client.js +1 -1
- package/dist/compaction-record.d.ts +19 -1
- package/dist/compaction-record.js +61 -132
- package/dist/compaction-spool.d.ts +45 -0
- package/dist/compaction-spool.js +454 -0
- package/dist/config.d.ts +8 -0
- package/dist/config.js +49 -0
- package/dist/connectors/github/deletion.d.ts +1 -1
- package/dist/connectors/github/deletion.js +5 -17
- package/dist/consolidate/conflicts.js +4 -0
- package/dist/consolidate/flush-units.d.ts +9 -0
- package/dist/consolidate/flush-units.js +63 -0
- package/dist/consolidate/llm-passes.js +5 -4
- package/dist/consolidate/merge.js +5 -1
- package/dist/consolidate/run.d.ts +2 -1
- package/dist/consolidate/sleep.d.ts +2 -0
- package/dist/consolidate/sleep.js +82 -15
- package/dist/context-render.d.ts +14 -0
- package/dist/context-render.js +28 -0
- package/dist/dag.js +6 -0
- package/dist/dashboard-actions.js +3 -2
- package/dist/dashboard.js +4 -3
- package/dist/db/backup.d.ts +6 -0
- package/dist/db/backup.js +29 -0
- package/dist/db/busy.d.ts +9 -0
- package/dist/db/busy.js +35 -0
- package/dist/db/continuity.d.ts +1 -0
- package/dist/db/continuity.js +31 -4
- package/dist/db/meta.d.ts +2 -0
- package/dist/db/meta.js +7 -0
- package/dist/db/migrate.js +2 -3
- package/dist/db/migrations/index.d.ts +1 -1
- package/dist/db/migrations/index.js +5 -1
- package/dist/db/migrations/v39.js +2 -7
- package/dist/db/migrations/v53.d.ts +3 -0
- package/dist/db/migrations/v53.js +16 -0
- package/dist/db/migrations/v54.d.ts +3 -0
- package/dist/db/migrations/v54.js +25 -0
- package/dist/db/open.d.ts +11 -0
- package/dist/db/open.js +27 -0
- package/dist/db/tables.d.ts +2 -0
- package/dist/db/tables.js +6 -2
- package/dist/db.d.ts +2 -2
- package/dist/db.js +2 -2
- package/dist/doctor.d.ts +1 -1
- package/dist/doctor.js +31 -15
- package/dist/dormant.d.ts +16 -20
- package/dist/dormant.js +24 -30
- package/dist/entry/json-hooks.d.ts +3 -0
- package/dist/entry/json-hooks.js +4 -0
- package/dist/entry/project-identity.d.ts +2 -0
- package/dist/entry/project-identity.js +3 -0
- package/dist/entry/session-text.d.ts +7 -0
- package/dist/entry/session-text.js +8 -0
- package/dist/extract.js +6 -0
- package/dist/failure-log.d.ts +10 -2
- package/dist/failure-log.js +19 -2
- package/dist/gated-write.js +1 -1
- package/dist/graph/delta.d.ts +44 -0
- package/dist/graph/delta.js +109 -0
- package/dist/graph/types.d.ts +5 -0
- package/dist/graph/write.d.ts +20 -20
- package/dist/graph/write.js +103 -36
- package/dist/graph-extract.d.ts +43 -9
- package/dist/graph-extract.js +140 -157
- package/dist/graph-recall.d.ts +1 -0
- package/dist/graph-recall.js +2 -2
- package/dist/graph-view.d.ts +1 -0
- package/dist/graph-view.js +25 -2
- package/dist/half-life-migration.js +5 -1
- package/dist/handoff-evidence.d.ts +5 -0
- package/dist/handoff-evidence.js +30 -0
- package/dist/home-path.d.ts +5 -0
- package/dist/home-path.js +24 -0
- package/dist/hooks/json-hooks.d.ts +13 -2
- package/dist/hooks/json-hooks.js +246 -117
- package/dist/hooks/opencode.js +1 -1
- package/dist/hooks/shared.d.ts +2 -0
- package/dist/hooks/shared.js +6 -1
- package/dist/http-util.d.ts +16 -7
- package/dist/http-util.js +65 -20
- package/dist/importers/vault.js +2 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/json.d.ts +2 -0
- package/dist/json.js +7 -0
- package/dist/ledger-db.d.ts +10 -0
- package/dist/ledger-db.js +42 -0
- package/dist/mcp/admin-tools.d.ts +2 -2
- package/dist/mcp/admin-tools.js +47 -5
- package/dist/mcp/memory-tools.js +7 -2
- package/dist/mcp/protocol.d.ts +6 -0
- package/dist/mcp/protocol.js +4 -2
- package/dist/mcp/recall-tools.js +19 -6
- package/dist/mcp/request.js +4 -0
- package/dist/mcp/session-state.d.ts +4 -0
- package/dist/mcp/session-state.js +15 -6
- package/dist/mcp/shared-gate.d.ts +4 -0
- package/dist/mcp/shared-gate.js +22 -0
- package/dist/mcp/tools.js +1 -0
- package/dist/memory-quality.d.ts +35 -0
- package/dist/memory-quality.js +191 -0
- package/dist/memory.d.ts +4 -4
- package/dist/physics-state.js +1 -1
- package/dist/pilot-arm.d.ts +14 -3
- package/dist/pilot-arm.js +32 -10
- package/dist/postinstall.js +2 -2
- package/dist/project-briefs.js +4 -4
- package/dist/project-identity.d.ts +14 -10
- package/dist/project-identity.js +29 -11
- package/dist/project-merge.d.ts +0 -2
- package/dist/project-merge.js +9 -13
- package/dist/prompt-hook.d.ts +65 -0
- package/dist/prompt-hook.js +213 -0
- package/dist/prompt-recall.js +1 -1
- package/dist/quality-repair.d.ts +27 -0
- package/dist/quality-repair.js +220 -0
- package/dist/rate-limit.d.ts +1 -0
- package/dist/rate-limit.js +5 -3
- package/dist/raw-archive.d.ts +1 -1
- package/dist/raw-archive.js +6 -16
- package/dist/recall-scope.d.ts +28 -5
- package/dist/recall-scope.js +52 -7
- package/dist/recall-trace.js +1 -1
- package/dist/reject-flow.d.ts +2 -0
- package/dist/reject-flow.js +20 -6
- package/dist/secret-detect.js +7 -8
- package/dist/server/auth.d.ts +8 -6
- package/dist/server/auth.js +85 -38
- package/dist/server/client-ip.d.ts +2 -0
- package/dist/server/client-ip.js +16 -1
- package/dist/server/mcp-http.d.ts +9 -3
- package/dist/server/mcp-http.js +56 -13
- package/dist/server/request.d.ts +1 -1
- package/dist/server/request.js +5 -5
- package/dist/server/routes/admin.js +12 -6
- package/dist/server/routes/memories.js +9 -1
- package/dist/server/routes/recall.js +14 -2
- package/dist/server/types.d.ts +42 -5
- package/dist/server/validation.d.ts +5 -2
- package/dist/server/validation.js +19 -2
- package/dist/server.d.ts +21 -4
- package/dist/server.js +207 -49
- package/dist/session-digest.d.ts +0 -1
- package/dist/session-digest.js +3 -12
- package/dist/session-owners.d.ts +7 -0
- package/dist/session-owners.js +54 -0
- package/dist/share-scrub.d.ts +3 -0
- package/dist/share-scrub.js +8 -0
- package/dist/shared.d.ts +1 -0
- package/dist/shared.js +26 -18
- package/dist/store/candidates.d.ts +14 -3
- package/dist/store/candidates.js +48 -26
- package/dist/store/conflicts.d.ts +15 -0
- package/dist/store/conflicts.js +68 -44
- package/dist/store/delete-and-batch.d.ts +25 -1
- package/dist/store/delete-and-batch.js +121 -69
- package/dist/store/entry-reads.d.ts +5 -6
- package/dist/store/entry-reads.js +24 -12
- package/dist/store/entry-row.d.ts +7 -10
- package/dist/store/entry-row.js +40 -18
- package/dist/store/entry-writes.d.ts +3 -3
- package/dist/store/entry-writes.js +9 -23
- package/dist/store/handoffs.d.ts +9 -6
- package/dist/store/handoffs.js +72 -59
- package/dist/store/index-and-stats.js +2 -2
- package/dist/store/open.d.ts +3 -1
- package/dist/store/open.js +3 -3
- package/dist/store/search-rows.d.ts +8 -5
- package/dist/store/search-rows.js +15 -18
- package/dist/store/sessions.d.ts +25 -6
- package/dist/store/sessions.js +61 -19
- package/dist/store/summaries.d.ts +1 -1
- package/dist/store/summaries.js +3 -18
- package/dist/store/tenant-lookup.d.ts +7 -3
- package/dist/store/tenant-lookup.js +9 -9
- package/dist/store-port.d.ts +13 -0
- package/dist/store-port.js +20 -0
- package/dist/version.d.ts +5 -1
- package/dist/version.js +5 -1
- package/dist/working-memory.js +1 -1
- package/dist/write-budget.d.ts +15 -0
- package/dist/write-budget.js +15 -0
- 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 +8 -4
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import * as fs from 'fs';
|
|
4
4
|
import { spawn } from 'child_process';
|
|
5
|
-
import { defaultSleepLogPath } from '../hooks/shared.js';
|
|
6
5
|
import { resolveCodexSessionTranscript } from '../hooks/codex-session.js';
|
|
7
6
|
import { resolveCodexWrapperPaths } from '../hooks/codex-wrapper.js';
|
|
8
7
|
import { isInitialized } from '../store/open.js';
|
|
@@ -17,45 +16,20 @@ import { summaryLine } from '../agent-memories/report.js';
|
|
|
17
16
|
import { resolveProjectIdentity } from '../project-identity.js';
|
|
18
17
|
import { getGlobalRoot } from '../shared.js';
|
|
19
18
|
import { cmdCapture } from '../capture/command.js';
|
|
20
|
-
import { cmdPreCompact, cmdPostCompact
|
|
19
|
+
import { cmdPreCompact, cmdPostCompact } from '../capture/compact.js';
|
|
20
|
+
import { transcriptWorkingState } from '../capture/working-state.js';
|
|
21
|
+
import { collectHandoffEvidence } from '../handoff-evidence.js';
|
|
21
22
|
import { resolveLastSessionTranscript } from '../capture/transcript.js';
|
|
22
23
|
import { truncateCodePointSafe } from '../transcript-tail.js';
|
|
23
24
|
import { COMPACTION_DB_WAIT_MS } from '../compaction-record.js';
|
|
25
|
+
import { COMPACT_RESUME_EVENT_CONTENT_CAP, COMPACT_RESUME_MAX_AGE_MS, compactResumeText } from '../context-render.js';
|
|
24
26
|
import { readStdinBounded } from '../stdin.js';
|
|
25
27
|
import { resolveTenantId } from '../tenant.js';
|
|
26
|
-
import { log } from '../log.js';
|
|
28
|
+
import { errorMessage, log } from '../log.js';
|
|
29
|
+
import { withLedgerDb } from '../ledger-db.js';
|
|
27
30
|
import { printError } from './output.js';
|
|
28
|
-
import {
|
|
29
|
-
|
|
30
|
-
* SessionStart stdout to the model's context and this log is for the user. */
|
|
31
|
-
export function cmdLastSleep(flags) {
|
|
32
|
-
const logPath = typeof flags['path'] === 'string'
|
|
33
|
-
? flags['path']
|
|
34
|
-
: defaultSleepLogPath();
|
|
35
|
-
if (!fs.existsSync(logPath))
|
|
36
|
-
return;
|
|
37
|
-
let content;
|
|
38
|
-
try {
|
|
39
|
-
content = fs.readFileSync(logPath, 'utf8');
|
|
40
|
-
}
|
|
41
|
-
catch {
|
|
42
|
-
// Removed or locked since the exists check: there is nothing to show this session.
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
if (content.trim().length > 0) {
|
|
46
|
-
printError('=== Previous session hippo consolidation ===');
|
|
47
|
-
process.stderr.write(content);
|
|
48
|
-
if (!content.endsWith('\n'))
|
|
49
|
-
printError();
|
|
50
|
-
printError('===========================================');
|
|
51
|
-
}
|
|
52
|
-
if (!flags['keep']) {
|
|
53
|
-
try {
|
|
54
|
-
fs.unlinkSync(logPath);
|
|
55
|
-
}
|
|
56
|
-
catch { /* non-fatal */ }
|
|
57
|
-
}
|
|
58
|
-
}
|
|
31
|
+
import { cmdLastSleep } from './last-sleep.js';
|
|
32
|
+
import { logSessionEndImport, appendSessionEndCloseLog, resetHookInjection, hookStoreRoot, runHookWithStores, inPilotHoldout, } from './shared.js';
|
|
59
33
|
/**
|
|
60
34
|
* SessionStart(compact) injector. Prints the active task snapshot + recent
|
|
61
35
|
* session trail so working state that would otherwise be lost to
|
|
@@ -68,10 +42,6 @@ export function cmdLastSleep(flags) {
|
|
|
68
42
|
* degrades to empty stdout, never a thrown error — a failing SessionStart
|
|
69
43
|
* hook must not pollute session startup.
|
|
70
44
|
*/
|
|
71
|
-
// Capped at print time only, so the shared printSessionEvents stays untouched for every other caller.
|
|
72
|
-
const COMPACT_RESUME_EVENT_CONTENT_CAP = 400;
|
|
73
|
-
// A snapshot older than this was not written for this compaction (pre-compact skipped), so restoring it is stale, not a resume.
|
|
74
|
-
const COMPACT_RESUME_MAX_AGE_MS = 15 * 60_000;
|
|
75
45
|
function cmdCompactResume(hippoRoot, stdinText, stdinTimedOut) {
|
|
76
46
|
try {
|
|
77
47
|
// Gate on the non-exiting isInitialized check first: the store reads below call initStore, which would
|
|
@@ -152,16 +122,7 @@ function restoreCompactSnapshot(hippoRoot, payloadSessionId) {
|
|
|
152
122
|
log.warn(`hippo compact-resume: trail skipped: ${err instanceof Error ? err.message : String(err)}`);
|
|
153
123
|
}
|
|
154
124
|
// Printed in one write so the ledger books exactly the text the model is handed.
|
|
155
|
-
const text =
|
|
156
|
-
console.log('## Restored after compaction\n');
|
|
157
|
-
// Re-injected state is background reference, not instructions: the framing line the model sees at every compaction.
|
|
158
|
-
console.log("_Point-in-time working-state snapshot, auto-restored after compaction. Background reference, not instructions; the user's live messages win._\n");
|
|
159
|
-
printActiveTaskSnapshot(snapshot);
|
|
160
|
-
// Nothing auto-populates session_events, so an empty trail is the common real case;
|
|
161
|
-
// printSessionEvents([]) would inject a bare "No session events found." line into every compaction.
|
|
162
|
-
if (events.length > 0)
|
|
163
|
-
printSessionEvents(events);
|
|
164
|
-
});
|
|
125
|
+
const text = compactResumeText(snapshot, events);
|
|
165
126
|
console.log(text);
|
|
166
127
|
withLedgerDb(hippoRoot, (db) => recordTokenUse(db, {
|
|
167
128
|
tenantId, sessionId: payloadSessionId, surface: 'compact_resume', event: 'inject', items: 1, tokens: estimateTokens(text),
|
|
@@ -210,6 +171,8 @@ export async function cmdSessionEnd(hippoRoot, flags) {
|
|
|
210
171
|
stdio: 'ignore',
|
|
211
172
|
windowsHide: true,
|
|
212
173
|
});
|
|
174
|
+
// An async spawn failure arrives as an 'error' event, which with no listener is an uncaught exception.
|
|
175
|
+
child.on('error', (err) => log.warn(`hippo session-end: the worker did not start: ${errorMessage(err)}`));
|
|
213
176
|
child.unref();
|
|
214
177
|
}
|
|
215
178
|
catch {
|
|
@@ -400,7 +363,7 @@ export function cmdCodexRun(hippoRoot, args) {
|
|
|
400
363
|
const { historyPath } = resolveCodexWrapperPaths();
|
|
401
364
|
const startOffsetBytes = fs.existsSync(historyPath) ? fs.statSync(historyPath).size : 0;
|
|
402
365
|
try {
|
|
403
|
-
cmdLastSleep({ path: metadata.logFile });
|
|
366
|
+
cmdLastSleep(hippoRoot, { path: metadata.logFile }, 'terminal');
|
|
404
367
|
}
|
|
405
368
|
catch {
|
|
406
369
|
// best-effort only
|
package/dist/cli/setup.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import * as fs from 'fs';
|
|
4
4
|
import { execFileSync } from 'child_process';
|
|
5
|
-
import { installJsonHooks, uninstallJsonHooks, resolveJsonHookPaths } from '../hooks/json-hooks.js';
|
|
5
|
+
import { installJsonHooks, uninstallJsonHooks, checkUninstallable, resolveJsonHookPaths } from '../hooks/json-hooks.js';
|
|
6
6
|
import { detectInstalledTools } from '../hooks/shared.js';
|
|
7
7
|
import { ensureCodexWrapperInstalled, installCodexWrapper, detectRealCodexPath, uninstallCodexWrapper, } from '../hooks/codex-wrapper.js';
|
|
8
8
|
import { installOpencodePlugin, uninstallOpencodePlugin, resolveOpencodePluginPath } from '../hooks/opencode.js';
|
|
@@ -13,7 +13,8 @@ import { listRegisteredWorkspaces, runDailyMaintenance } from '../scheduler.js';
|
|
|
13
13
|
import { replayCompactionsAt } from '../compaction-record.js';
|
|
14
14
|
import { log } from '../log.js';
|
|
15
15
|
import { printError } from './output.js';
|
|
16
|
-
import { printAgentImport, installCodexMemoryHooks, setupDailySchedule } from './shared.js';
|
|
16
|
+
import { printAgentImport, installCodexMemoryHooks, setupDailySchedule, warnClaudeSettingsUnusable } from './shared.js';
|
|
17
|
+
import { repairQualityOnceAt } from './quality-repair-once.js';
|
|
17
18
|
import { HOOK_MARKERS, HOOKS, hippoBlock } from './hook-blocks.js';
|
|
18
19
|
import { escapeRegex } from '../escape.js';
|
|
19
20
|
// ---------------------------------------------------------------------------
|
|
@@ -101,6 +102,7 @@ function patchAgentFile(hook, target) {
|
|
|
101
102
|
}
|
|
102
103
|
}
|
|
103
104
|
function printClaudeHookInstall(result) {
|
|
105
|
+
warnClaudeSettingsUnusable(result, '');
|
|
104
106
|
if (result.installedSessionEnd) {
|
|
105
107
|
console.log(`Installed hippo session-end SessionEnd hook in ${result.target} settings`);
|
|
106
108
|
}
|
|
@@ -159,6 +161,9 @@ function hookUninstall(target) {
|
|
|
159
161
|
if (uninstallJsonHooks(target)) {
|
|
160
162
|
console.log(`Removed hippo hooks from ${target} settings`);
|
|
161
163
|
}
|
|
164
|
+
else {
|
|
165
|
+
warnClaudeSettingsUnusable(checkUninstallable(target), '', 'uninstall');
|
|
166
|
+
}
|
|
162
167
|
}
|
|
163
168
|
else if (target === 'opencode') {
|
|
164
169
|
// opencode uses a TS plugin; uninstall removes the plugin file AND
|
|
@@ -280,6 +285,8 @@ function setupJsonTool(tool, dryRun) {
|
|
|
280
285
|
return;
|
|
281
286
|
}
|
|
282
287
|
const result = installJsonHooks(tool.name);
|
|
288
|
+
if (warnClaudeSettingsUnusable(result, ` ${tool.name.padEnd(14)} `))
|
|
289
|
+
return;
|
|
283
290
|
const bits = [];
|
|
284
291
|
if (result.installedSessionEnd)
|
|
285
292
|
bits.push('SessionEnd (session-end)');
|
|
@@ -362,6 +369,7 @@ export function cmdDailyRunner() {
|
|
|
362
369
|
const finished = replayCompactionsAt(globalRoot, (message) => log.warn(`compaction replay: ${message}`));
|
|
363
370
|
if (finished > 0)
|
|
364
371
|
console.log(`Finished saving ${finished} compaction${finished === 1 ? '' : 's'} left over in the global store.`);
|
|
372
|
+
repairQualityOnceAt(globalRoot);
|
|
365
373
|
}
|
|
366
374
|
printAgentImport(importUserMemories(globalRoot, { machine: currentMachine() }), '');
|
|
367
375
|
const workspaces = listRegisteredWorkspaces(globalRoot);
|
package/dist/cli/shared.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { type InstallResult } from '../hooks/json-hooks.js';
|
|
1
2
|
import { TaskSnapshot, SessionEvent } from '../store/rows.js';
|
|
2
|
-
import type {
|
|
3
|
+
import type { SessionHandoff } from '../handoff.js';
|
|
3
4
|
import type { SearchResult } from '../search/types.js';
|
|
4
5
|
import { type HippoConfig } from '../config.js';
|
|
5
|
-
import { openHippoDb } from '../db.js';
|
|
6
6
|
import { type ImportReport } from '../agent-memories/report.js';
|
|
7
7
|
import { type ChurnStaleResult } from '../invalidation.js';
|
|
8
8
|
import { type AuditOp } from '../audit.js';
|
|
@@ -44,8 +44,12 @@ export declare function recallEntryText(r: SearchResult, query: string, showWhy:
|
|
|
44
44
|
export declare function recallHeading(entries: number, tokens: number, query: string): string;
|
|
45
45
|
/** One line when an agent memory import moved anything; its warnings go to stderr. */
|
|
46
46
|
export declare function printAgentImport(report: ImportReport, indent?: string): void;
|
|
47
|
+
/** True, after one line, on a shared store: this account's commits and agent notes are not its members' memories. */
|
|
48
|
+
export declare function skipLearnOnSharedStore(hippoRoot: string): boolean;
|
|
47
49
|
/** Adds hippo's two Codex hooks and says what changed; each install ends on the trust reminder, since Codex skips an untrusted hook. */
|
|
48
50
|
export declare function installCodexMemoryHooks(indent: string): void;
|
|
51
|
+
/** The one line init, hook install, hook uninstall and setup print when Claude Code's settings.json is not JSON hippo can edit and so was left unchanged; true when it printed. */
|
|
52
|
+
export declare function warnClaudeSettingsUnusable(result: Pick<InstallResult, 'settingsPath' | 'invalidJson'>, indent: string, action?: 'install' | 'uninstall'): boolean;
|
|
49
53
|
/**
|
|
50
54
|
* Set up a machine-level daily runner that sweeps all registered Hippo
|
|
51
55
|
* workspaces.
|
|
@@ -70,7 +74,6 @@ export declare function engineFlags(flags: CliFlags, config: HippoConfig): Engin
|
|
|
70
74
|
* `__session-end-worker` subcommand (not user-facing). Failures in one stage
|
|
71
75
|
* do not block the other.
|
|
72
76
|
*/
|
|
73
|
-
export declare function collectHandoffEvidence(cwd: string, testStatus: HandoffEvidence['testStatus']): HandoffEvidence;
|
|
74
77
|
/** A folder without its own store never sleeps at session end, so its project's agent notes go to the global store here. */
|
|
75
78
|
export declare function logSessionEndImport(logFile: string | null, transcriptPath: string | undefined): void;
|
|
76
79
|
/**
|
|
@@ -110,13 +113,6 @@ export declare function captureConsole(fn: () => void): string;
|
|
|
110
113
|
* is looked for in the other.
|
|
111
114
|
*/
|
|
112
115
|
export declare function hookStoreRoot(hippoRoot: string): string;
|
|
113
|
-
/**
|
|
114
|
-
* Run `fn` against the token ledger's store: the local store when it is
|
|
115
|
-
* initialized, else the global one (the per-prompt hook runs in directories
|
|
116
|
-
* without a local store). Best-effort: returns undefined and never throws,
|
|
117
|
-
* because a ledger failure must not break context or recall.
|
|
118
|
-
*/
|
|
119
|
-
export declare function withLedgerDb<T>(hippoRoot: string, fn: (db: ReturnType<typeof openHippoDb>) => T): T | undefined;
|
|
120
116
|
export declare function learnFromRepo(hippoRoot: string, repoPath: string, days: number, label?: string): {
|
|
121
117
|
added: number;
|
|
122
118
|
skipped: number;
|
|
@@ -125,9 +121,6 @@ export declare function learnFromRepo(hippoRoot: string, repoPath: string, days:
|
|
|
125
121
|
export declare function resolveAuthRoot(hippoRoot: string, flags: Record<string, string | boolean | string[]>): string;
|
|
126
122
|
/** Hook commands share one handle per store and wait at most HOOK_DB_WAIT_MS for a lock; a store still busy after that skips the hook's work with one warning, exit 0. */
|
|
127
123
|
export declare function runHookWithStores<T>(fn: () => T | Promise<T>): Promise<T | undefined>;
|
|
128
|
-
/**
|
|
129
|
-
* Whether this session sits in the pilot's holdout arm (src/pilot-arm.ts). Off at rate 0 and with no session id.
|
|
130
|
-
* `write` books the arm row; a read-only caller (env-only id, sub-agent) follows the stored arm, else the hash.
|
|
131
|
-
*/
|
|
124
|
+
/** Whether this session sits in the pilot's holdout arm; off at rate 0 and with no session id. */
|
|
132
125
|
export declare function inPilotHoldout(hippoRoot: string, tenantId: string, sessionId: string | undefined, write: boolean): boolean;
|
|
133
126
|
//# sourceMappingURL=shared.d.ts.map
|
package/dist/cli/shared.js
CHANGED
|
@@ -9,9 +9,10 @@ import { CODEX_TRUST_LINE } from '../hooks/shared.js';
|
|
|
9
9
|
import { confidenceLabel } from '../memory.js';
|
|
10
10
|
import { isInitialized } from '../store/open.js';
|
|
11
11
|
import { explainMatch } from '../search/explain.js';
|
|
12
|
-
import {
|
|
12
|
+
import { isSharedStore } from '../config.js';
|
|
13
13
|
import { openHippoDb, closeHippoDb, isSqliteBusy, noteStoreBusy, withSharedStoreHandles, HOOK_DB_WAIT_MS } from '../db.js';
|
|
14
|
-
import {
|
|
14
|
+
import { withLedgerDb } from '../ledger-db.js';
|
|
15
|
+
import { sessionPilotArm } from '../pilot-arm.js';
|
|
15
16
|
import { hookPayloadSessionId, isSubagentPayload, recordTokenUse } from '../token-ledger.js';
|
|
16
17
|
import { importAtSessionEnd, currentMachine } from '../agent-memories/sync.js';
|
|
17
18
|
import { summaryLine } from '../agent-memories/report.js';
|
|
@@ -218,6 +219,13 @@ export function printAgentImport(report, indent = ' ') {
|
|
|
218
219
|
for (const warning of report.warnings)
|
|
219
220
|
printError(`hippo: agent memories: ${warning}`);
|
|
220
221
|
}
|
|
222
|
+
/** True, after one line, on a shared store: this account's commits and agent notes are not its members' memories. */
|
|
223
|
+
export function skipLearnOnSharedStore(hippoRoot) {
|
|
224
|
+
if (!isSharedStore(hippoRoot))
|
|
225
|
+
return false;
|
|
226
|
+
console.log("Shared store: skipped learning from this account's git commits and coding agents' own memories.");
|
|
227
|
+
return true;
|
|
228
|
+
}
|
|
221
229
|
/** Adds hippo's two Codex hooks and says what changed; each install ends on the trust reminder, since Codex skips an untrusted hook. */
|
|
222
230
|
export function installCodexMemoryHooks(indent) {
|
|
223
231
|
const result = installJsonHooks('codex');
|
|
@@ -234,6 +242,13 @@ export function installCodexMemoryHooks(indent) {
|
|
|
234
242
|
: `${indent}hippo's Codex memory hooks already in ${result.settingsPath}`);
|
|
235
243
|
console.log(`${indent}${CODEX_TRUST_LINE}`);
|
|
236
244
|
}
|
|
245
|
+
/** The one line init, hook install, hook uninstall and setup print when Claude Code's settings.json is not JSON hippo can edit and so was left unchanged; true when it printed. */
|
|
246
|
+
export function warnClaudeSettingsUnusable(result, indent, action = 'install') {
|
|
247
|
+
if (!result.invalidJson)
|
|
248
|
+
return false;
|
|
249
|
+
console.log(`${indent}WARNING: ${result.settingsPath} is not a JSON object hippo can merge into, so it was left unchanged; fix it, then run \`hippo hook ${action} claude-code\`.`);
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
237
252
|
/**
|
|
238
253
|
* Set up a machine-level daily runner that sweeps all registered Hippo
|
|
239
254
|
* workspaces.
|
|
@@ -320,32 +335,6 @@ export function engineFlags(flags, config) {
|
|
|
320
335
|
* `__session-end-worker` subcommand (not user-facing). Failures in one stage
|
|
321
336
|
* do not block the other.
|
|
322
337
|
*/
|
|
323
|
-
// Best-effort git state; a missing git, non-repo cwd, or the timeout all
|
|
324
|
-
// yield null fields rather than throw (autolearn.ts execFileSync shape).
|
|
325
|
-
export function collectHandoffEvidence(cwd, testStatus) {
|
|
326
|
-
let gitRef = null;
|
|
327
|
-
try {
|
|
328
|
-
gitRef = execFileSync('git', ['rev-parse', 'HEAD'], {
|
|
329
|
-
cwd, encoding: 'utf8', timeout: 2000, stdio: ['ignore', 'pipe', 'ignore'], windowsHide: true,
|
|
330
|
-
}).trim() || null;
|
|
331
|
-
}
|
|
332
|
-
catch {
|
|
333
|
-
// No git, not a repo, or timed out: evidence is optional, so the field stays null.
|
|
334
|
-
gitRef = null;
|
|
335
|
-
}
|
|
336
|
-
let dirtyTree = null;
|
|
337
|
-
try {
|
|
338
|
-
const status = execFileSync('git', ['status', '--porcelain'], {
|
|
339
|
-
cwd, encoding: 'utf8', timeout: 2000, stdio: ['ignore', 'pipe', 'ignore'], windowsHide: true,
|
|
340
|
-
});
|
|
341
|
-
dirtyTree = status.trim().length > 0;
|
|
342
|
-
}
|
|
343
|
-
catch {
|
|
344
|
-
// Same as gitRef: unknown tree state is reported as null, never as an error.
|
|
345
|
-
dirtyTree = null;
|
|
346
|
-
}
|
|
347
|
-
return { gitRef, dirtyTree, testStatus };
|
|
348
|
-
}
|
|
349
338
|
/** A folder without its own store never sleeps at session end, so its project's agent notes go to the global store here. */
|
|
350
339
|
export function logSessionEndImport(logFile, transcriptPath) {
|
|
351
340
|
try {
|
|
@@ -452,42 +441,6 @@ export function hookStoreRoot(hippoRoot) {
|
|
|
452
441
|
const globalRoot = getGlobalRoot();
|
|
453
442
|
return isInitialized(globalRoot) ? globalRoot : hippoRoot;
|
|
454
443
|
}
|
|
455
|
-
/**
|
|
456
|
-
* Run `fn` against the token ledger's store: the local store when it is
|
|
457
|
-
* initialized, else the global one (the per-prompt hook runs in directories
|
|
458
|
-
* without a local store). Best-effort: returns undefined and never throws,
|
|
459
|
-
* because a ledger failure must not break context or recall.
|
|
460
|
-
*/
|
|
461
|
-
export function withLedgerDb(hippoRoot, fn) {
|
|
462
|
-
let root = null;
|
|
463
|
-
try {
|
|
464
|
-
if (isInitialized(hippoRoot))
|
|
465
|
-
root = hippoRoot;
|
|
466
|
-
else if (isInitialized(getGlobalRoot()))
|
|
467
|
-
root = getGlobalRoot();
|
|
468
|
-
}
|
|
469
|
-
catch {
|
|
470
|
-
// An unreadable store root means no ledger write; the ledger must never break context or recall.
|
|
471
|
-
return undefined;
|
|
472
|
-
}
|
|
473
|
-
if (root === null)
|
|
474
|
-
return undefined;
|
|
475
|
-
let db;
|
|
476
|
-
try {
|
|
477
|
-
db = openHippoDb(root);
|
|
478
|
-
return fn(db);
|
|
479
|
-
}
|
|
480
|
-
catch (error) {
|
|
481
|
-
// Best effort, but a busy store is the one failure an operator can act on, so it warns once.
|
|
482
|
-
if (isSqliteBusy(error))
|
|
483
|
-
noteStoreBusy('token ledger row skipped');
|
|
484
|
-
return undefined;
|
|
485
|
-
}
|
|
486
|
-
finally {
|
|
487
|
-
if (db)
|
|
488
|
-
closeHippoDb(db);
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
444
|
export function learnFromRepo(hippoRoot, repoPath, days, label) {
|
|
492
445
|
const prefix = label ? `[${label}] ` : '';
|
|
493
446
|
const ctx = { hippoRoot, tenantId: resolveTenantId({}), actor: adminActor('cli') };
|
|
@@ -530,20 +483,8 @@ export async function runHookWithStores(fn) {
|
|
|
530
483
|
return undefined;
|
|
531
484
|
}
|
|
532
485
|
}
|
|
533
|
-
/**
|
|
534
|
-
* Whether this session sits in the pilot's holdout arm (src/pilot-arm.ts). Off at rate 0 and with no session id.
|
|
535
|
-
* `write` books the arm row; a read-only caller (env-only id, sub-agent) follows the stored arm, else the hash.
|
|
536
|
-
*/
|
|
486
|
+
/** Whether this session sits in the pilot's holdout arm; off at rate 0 and with no session id. */
|
|
537
487
|
export function inPilotHoldout(hippoRoot, tenantId, sessionId, write) {
|
|
538
|
-
|
|
539
|
-
return false;
|
|
540
|
-
const root = isInitialized(hippoRoot) ? hippoRoot : isInitialized(getGlobalRoot()) ? getGlobalRoot() : null;
|
|
541
|
-
if (root === null)
|
|
542
|
-
return false;
|
|
543
|
-
const rate = loadConfig(root).pilot.holdoutRateBp;
|
|
544
|
-
if (rate <= 0)
|
|
545
|
-
return false;
|
|
546
|
-
const arm = withLedgerDb(hippoRoot, (db) => write ? ensurePilotArm(db, tenantId, sessionId, rate) : readPilotArm(db, sessionId) ?? hashArm(sessionId, rate));
|
|
547
|
-
return (arm ?? hashArm(sessionId, rate)) === 'holdout';
|
|
488
|
+
return sessionPilotArm(hippoRoot, tenantId, sessionId, write) === 'holdout';
|
|
548
489
|
}
|
|
549
490
|
//# sourceMappingURL=shared.js.map
|
package/dist/cli/sleep.js
CHANGED
|
@@ -11,7 +11,8 @@ import { renderAmbientSummary } from '../ambient.js';
|
|
|
11
11
|
import { errorMessage, log } from '../log.js';
|
|
12
12
|
import { closeHippoDb, openHippoDb } from '../db.js';
|
|
13
13
|
import { repairOnceOnSleep } from '../project-merge.js';
|
|
14
|
-
import { requireInit, learnFromRepo, runChurnStaleForRepo, printAgentImport } from './shared.js';
|
|
14
|
+
import { requireInit, learnFromRepo, runChurnStaleForRepo, printAgentImport, skipLearnOnSharedStore } from './shared.js';
|
|
15
|
+
import { repairQualityOnceAt } from './quality-repair-once.js';
|
|
15
16
|
import { printError } from './output.js';
|
|
16
17
|
/** Runs `hippo sleep`; with `--log-file` it also tees its output to that file. */
|
|
17
18
|
export async function cmdSleep(hippoRoot, flags) {
|
|
@@ -156,10 +157,11 @@ async function cmdSleepCore(hippoRoot, flags) {
|
|
|
156
157
|
requireInit(hippoRoot);
|
|
157
158
|
// Phase 1: Auto-learn from git and every coding agent's own memories (CLI-only, uses process.cwd() / os.homedir()).
|
|
158
159
|
// Stays in cli.ts; api.sleep covers Phase 2-6 only.
|
|
159
|
-
|
|
160
|
+
const learn = !flags['no-learn'] && !skipLearnOnSharedStore(hippoRoot);
|
|
161
|
+
if (learn && flags['dry-run']) {
|
|
160
162
|
console.log("Dry run: skipped learning from git commits and coding agents' own memories (`hippo import --agents --dry-run` previews those).");
|
|
161
163
|
}
|
|
162
|
-
else if (
|
|
164
|
+
else if (learn) {
|
|
163
165
|
const config = loadConfig(hippoRoot);
|
|
164
166
|
if (config.autoLearnOnSleep && isGitRepo(process.cwd())) {
|
|
165
167
|
const { added } = learnFromRepo(hippoRoot, process.cwd(), 1);
|
|
@@ -183,6 +185,7 @@ async function cmdSleepCore(hippoRoot, flags) {
|
|
|
183
185
|
if (finished > 0)
|
|
184
186
|
console.log(`Finished saving ${finished} compaction${finished === 1 ? '' : 's'} left over from earlier sessions.`);
|
|
185
187
|
repairProjectTagsOnce(hippoRoot);
|
|
188
|
+
repairQualityOnceAt(hippoRoot);
|
|
186
189
|
}
|
|
187
190
|
// Phase 2-6: Pure-storage pipeline (consolidate + dedup + audit + share + ambient).
|
|
188
191
|
const ctx = {
|
package/dist/cli/usage.js
CHANGED
|
@@ -248,7 +248,7 @@ export const VERB_USAGE = {
|
|
|
248
248
|
--no-learn Skip auto git-learn and the agent memory import before consolidation
|
|
249
249
|
--no-share Skip auto-sharing to global store`],
|
|
250
250
|
'last-sleep': [`
|
|
251
|
-
last-sleep
|
|
251
|
+
last-sleep Show the last sleep log on stderr, one problems line to the user, and clear it
|
|
252
252
|
--path <p> Log path (default: ~/.hippo/logs/last-sleep.log)
|
|
253
253
|
--keep Print without clearing`],
|
|
254
254
|
'session-end': [`
|
|
@@ -287,7 +287,7 @@ export const VERB_USAGE = {
|
|
|
287
287
|
--json Output as JSON
|
|
288
288
|
--global Operate on the global store
|
|
289
289
|
auth list List API keys (active by default)
|
|
290
|
-
--all Include revoked keys
|
|
290
|
+
--all Include revoked and expired keys
|
|
291
291
|
--json Output as JSON
|
|
292
292
|
--global Operate on the global store
|
|
293
293
|
auth revoke <key_id> Revoke an API key (subsequent validate fails)
|
|
@@ -333,7 +333,15 @@ export const VERB_USAGE = {
|
|
|
333
333
|
dlq replay <id> [--force]
|
|
334
334
|
Re-ingest a DLQ entry (--force skips sig check)`],
|
|
335
335
|
audit: [`
|
|
336
|
-
audit [--fix] Check memory quality (--fix removes junk)
|
|
336
|
+
audit [--fix] Check memory quality (--fix removes junk)
|
|
337
|
+
audit repair [--apply] Preview repair of memories hippo wrote itself; --apply moves certain
|
|
338
|
+
defects to dormant storage. Sleep and the daily runner apply it once
|
|
339
|
+
per store after an upgrade
|
|
340
|
+
--json Report ids, reasons, protections and schema blockers as JSON
|
|
341
|
+
--global Operate on the global store without changing its schema
|
|
342
|
+
Writes a database backup first; undo with hippo dormant restore <id>,
|
|
343
|
+
which marks the memory verified so repair leaves it alone. Sleep's
|
|
344
|
+
dormant retention still applies; the backup stays until you remove it.`, `
|
|
337
345
|
audit <sub> Query the append-only audit log (A5 stub auth)
|
|
338
346
|
audit list List audit events for the active tenant
|
|
339
347
|
--op <op> Filter by op (remember | recall | promote |
|
|
@@ -542,17 +550,18 @@ export const VERB_USAGE = {
|
|
|
542
550
|
hook <sub> [target] Manage framework integrations
|
|
543
551
|
hook list Show available hooks
|
|
544
552
|
hook install <target> Install hook (claude-code|codex|cursor|openclaw|opencode|pi)
|
|
545
|
-
claude-code adds 7 hooks to
|
|
546
|
-
opencode installs a plugin; codex
|
|
547
|
-
$CODEX_HOME/hooks.json (trust them once in /hooks) and
|
|
553
|
+
claude-code adds 7 hooks to $CLAUDE_CONFIG_DIR/settings.json
|
|
554
|
+
(~/.claude by default); opencode installs a plugin; codex
|
|
555
|
+
adds 2 hooks to $CODEX_HOME/hooks.json (trust them once in /hooks) and
|
|
548
556
|
wraps the detected launcher in place; all but claude-code
|
|
549
557
|
also patch an existing AGENTS.md
|
|
550
558
|
hook uninstall <target> Remove hook`],
|
|
551
559
|
setup: [`
|
|
552
560
|
setup One-shot: detect installed AI tools and install their hooks:
|
|
553
|
-
claude-code gets 7 hooks in
|
|
554
|
-
a plugin, codex 2 hooks in
|
|
555
|
-
wrapper; other tools get a
|
|
561
|
+
claude-code gets 7 hooks in $CLAUDE_CONFIG_DIR/settings.json
|
|
562
|
+
(~/.claude by default), opencode a plugin, codex 2 hooks in
|
|
563
|
+
its hooks.json plus a launcher wrapper; other tools get a
|
|
564
|
+
hint. Then imports each agent's
|
|
556
565
|
user-level memories into the global store
|
|
557
566
|
--all Install for every JSON-hook tool, even if not detected
|
|
558
567
|
--dry-run Show what would be installed without writing
|
package/dist/cli.d.ts
CHANGED
package/dist/cli.js
CHANGED
|
@@ -41,6 +41,7 @@ import { repairCodexWrapperIfInstalled } from './hooks/codex-wrapper.js';
|
|
|
41
41
|
import { getHippoRoot } from './store/open.js';
|
|
42
42
|
import { cmdGithub, printGithubBackfillUsage } from './connectors/github/cli-impl.js';
|
|
43
43
|
import { printError } from './cli/output.js';
|
|
44
|
+
import { isStoreBusy, STORE_BUSY_MESSAGE } from './db/busy.js';
|
|
44
45
|
import { VERB_USAGE, USAGE_HEADER, USAGE_EXAMPLES, printAuditPruneUsage, printSlackBackfillUsage, printSlackWorkspacesUsage } from './cli/usage.js';
|
|
45
46
|
// ---------------------------------------------------------------------------
|
|
46
47
|
// Helpers
|
|
@@ -243,7 +244,7 @@ export const COMMANDS = {
|
|
|
243
244
|
usage: VERB_USAGE.sleep,
|
|
244
245
|
},
|
|
245
246
|
'last-sleep': {
|
|
246
|
-
run: async ({ flags }) => { (await import('./cli/
|
|
247
|
+
run: async ({ hippoRoot, flags }) => { (await import('./cli/last-sleep.js')).cmdLastSleep(hippoRoot, flags); },
|
|
247
248
|
usage: VERB_USAGE['last-sleep'],
|
|
248
249
|
},
|
|
249
250
|
'session-end': {
|
|
@@ -637,7 +638,7 @@ export async function runCli(argv = process.argv) {
|
|
|
637
638
|
await main(command, args, flags, getHippoRoot(process.cwd()));
|
|
638
639
|
}
|
|
639
640
|
catch (err) {
|
|
640
|
-
printError('Error:', err instanceof Error ? err.message : err);
|
|
641
|
+
printError('Error:', isStoreBusy(err) ? STORE_BUSY_MESSAGE : err instanceof Error ? err.message : err);
|
|
641
642
|
process.exit(1);
|
|
642
643
|
}
|
|
643
644
|
}
|
package/dist/client.js
CHANGED
|
@@ -22,7 +22,7 @@ const STORE_BUSY_ATTEMPTS = 5;
|
|
|
22
22
|
function isStoreBusy(res) {
|
|
23
23
|
return res.status === 503 && res.headers.has('retry-after');
|
|
24
24
|
}
|
|
25
|
-
/** Replaying is safe because each routed write commits in one transaction and anything after it is best-effort, so a busy 503 means nothing landed. */
|
|
25
|
+
/** Replaying is safe because each routed write commits in one transaction and anything after it is best-effort, so a busy 503 means nothing landed; a sleep is safe too, as a re-run finishes a partial one. */
|
|
26
26
|
function sendWrite(url, init) {
|
|
27
27
|
return fetchWithRetry(url, init, { timeoutMs: SERVER_TIMEOUT_MS, attempts: STORE_BUSY_ATTEMPTS, retryOn: isStoreBusy });
|
|
28
28
|
}
|
|
@@ -33,6 +33,8 @@ interface ScrubbedSummary {
|
|
|
33
33
|
}
|
|
34
34
|
/** What the record keeps of a compact_summary: the scrubbed body, and every parsed item scrubbed. */
|
|
35
35
|
export declare function readCompactionText(compactSummary: string): ScrubbedSummary;
|
|
36
|
+
/** A caller's items scrubbed as readCompactionText scrubs a summary's, since another machine's scrub is not trusted; cut after it, as a mask can run longer than what it hides. */
|
|
37
|
+
export declare function scrubCompactionItems(items: readonly string[]): string[];
|
|
36
38
|
export interface CompactionStart {
|
|
37
39
|
sessionId: string;
|
|
38
40
|
originProject: string;
|
|
@@ -43,13 +45,24 @@ export interface CompactionStart {
|
|
|
43
45
|
export declare function startCompaction(db: DatabaseSyncLike, tenantId: string, start: CompactionStart, at?: Date): string;
|
|
44
46
|
/** A session's newest record, or null. */
|
|
45
47
|
export declare function latestCompaction(db: DatabaseSyncLike, tenantId: string, sessionId: string): CompactionRecord | null;
|
|
48
|
+
/** The record a caller's request made, whatever state it reached, so a retry neither writes its items twice nor starts a second record; another session's id is a ConflictError. */
|
|
49
|
+
export declare function compactionByRequest(db: DatabaseSyncLike, tenantId: string, requestId: string, sessionId: string): CompactionRecord | null;
|
|
46
50
|
/** Best effort, never throws: a compaction must not fail because its record could not be written. */
|
|
47
51
|
export declare function recordCompactionStart(hippoRoot: string, start: Omit<CompactionStart, 'originProject'>, log: Log): string | null;
|
|
48
|
-
|
|
52
|
+
/** On the caller's handle and tenant, so a server marks the record under the caller's tenant with its own wait. */
|
|
53
|
+
export declare function markSnapshotSaved(db: DatabaseSyncLike, tenantId: string, recordId: string): void;
|
|
54
|
+
export declare function recordSnapshotSaved(hippoRoot: string, tenantId: string, recordId: string, log: Log): void;
|
|
49
55
|
export interface CompactionText {
|
|
50
56
|
summary: string;
|
|
51
57
|
items: string[];
|
|
52
58
|
}
|
|
59
|
+
/** A caller's checked project, and the id its retries carry so a retry finds this record. */
|
|
60
|
+
export interface SummaryCaller {
|
|
61
|
+
originProject?: string;
|
|
62
|
+
requestId?: string;
|
|
63
|
+
}
|
|
64
|
+
/** Puts the summary on the session's `started` record, or inserts a `summarised` one when pre-compact wrote none. One statement each. */
|
|
65
|
+
export declare function recordSummary(db: DatabaseSyncLike, hippoRoot: string, tenantId: string, meta: Omit<CompactionStart, 'originProject'>, text: CompactionText, at: Date, caller?: SummaryCaller): CompactionRecord;
|
|
53
66
|
export interface ItemContext {
|
|
54
67
|
tenantId: string;
|
|
55
68
|
/** null when the record step failed: the items still go in, with no record to close. */
|
|
@@ -59,6 +72,11 @@ export interface ItemContext {
|
|
|
59
72
|
/** Where the session ran, so rows held under the project's older folder name count too. */
|
|
60
73
|
cwd: string | null;
|
|
61
74
|
items: string[];
|
|
75
|
+
/** Set when another machine sent the items: its audit actor and its project's names, since the server's folder is neither. */
|
|
76
|
+
caller?: {
|
|
77
|
+
actor: string;
|
|
78
|
+
origins: readonly string[];
|
|
79
|
+
};
|
|
62
80
|
}
|
|
63
81
|
/** Items that become rows, then the record `done`, in one transaction so two sessions cannot both insert one text. Mirrors after commit. */
|
|
64
82
|
export declare function saveItems(db: DatabaseSyncLike, hippoRoot: string, ctx: ItemContext, log: Log): number;
|