hippo-memory 1.52.8 → 1.53.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 +185 -101
- package/dist/agent-memories/apply.d.ts +47 -0
- package/dist/agent-memories/apply.js +253 -0
- package/dist/agent-memories/claude-code.d.ts +11 -0
- package/dist/agent-memories/claude-code.js +113 -0
- package/dist/agent-memories/codex.d.ts +3 -0
- package/dist/agent-memories/codex.js +47 -0
- package/dist/agent-memories/copilot.d.ts +3 -0
- package/dist/agent-memories/copilot.js +125 -0
- package/dist/agent-memories/files.d.ts +37 -0
- package/dist/agent-memories/files.js +77 -0
- package/dist/agent-memories/folder-store.d.ts +17 -0
- package/dist/agent-memories/folder-store.js +44 -0
- package/dist/agent-memories/gemini.d.ts +3 -0
- package/dist/agent-memories/gemini.js +103 -0
- package/dist/agent-memories/git.d.ts +8 -0
- package/dist/agent-memories/git.js +11 -0
- package/dist/agent-memories/keys.d.ts +9 -0
- package/dist/agent-memories/keys.js +20 -0
- package/dist/agent-memories/legacy.d.ts +17 -0
- package/dist/agent-memories/legacy.js +45 -0
- package/dist/agent-memories/markdown.d.ts +13 -0
- package/dist/agent-memories/markdown.js +123 -0
- package/dist/agent-memories/openclaw.d.ts +3 -0
- package/dist/agent-memories/openclaw.js +42 -0
- package/dist/agent-memories/plan.d.ts +78 -0
- package/dist/agent-memories/plan.js +123 -0
- package/dist/agent-memories/qwen-code.d.ts +5 -0
- package/dist/agent-memories/qwen-code.js +50 -0
- package/dist/agent-memories/report.d.ts +52 -0
- package/dist/agent-memories/report.js +88 -0
- package/dist/agent-memories/source.d.ts +16 -0
- package/dist/agent-memories/source.js +32 -0
- package/dist/agent-memories/sync.d.ts +33 -0
- package/dist/agent-memories/sync.js +336 -0
- package/dist/agent-memories/tools.d.ts +33 -0
- package/dist/agent-memories/tools.js +19 -0
- package/dist/agent-memories/types.d.ts +42 -0
- package/dist/agent-memories/types.js +2 -0
- package/dist/api.d.ts +53 -20
- package/dist/api.js +141 -97
- package/dist/audit.d.ts +2 -1
- package/dist/audit.js +68 -2
- package/dist/capture.d.ts +48 -22
- package/dist/capture.js +186 -161
- package/dist/cli.d.ts +0 -2
- package/dist/cli.js +750 -797
- package/dist/codex-patch.d.ts +12 -0
- package/dist/codex-patch.js +71 -0
- package/dist/compaction-items.d.ts +18 -0
- package/dist/compaction-items.js +60 -0
- package/dist/compaction-record.d.ts +94 -0
- package/dist/compaction-record.js +546 -0
- package/dist/config.d.ts +4 -1
- package/dist/config.js +13 -4
- package/dist/connectors/slack/types.d.ts +0 -1
- package/dist/consolidate.js +87 -34
- package/dist/context-render.d.ts +36 -0
- package/dist/context-render.js +154 -0
- package/dist/dag.js +3 -2
- package/dist/db.d.ts +5 -1
- package/dist/db.js +46 -14
- package/dist/dedupe.d.ts +6 -6
- package/dist/dedupe.js +10 -9
- package/dist/doctor.d.ts +1 -1
- package/dist/doctor.js +69 -4
- package/dist/dormant.d.ts +9 -3
- package/dist/dormant.js +26 -2
- package/dist/embedding-provider.d.ts +2 -1
- package/dist/embedding-provider.js +2 -1
- package/dist/embeddings.js +23 -3
- package/dist/extract.js +5 -1
- package/dist/forward-claim-detector.d.ts +1 -1
- package/dist/forward-claim-detector.js +1 -1
- package/dist/gated-write.d.ts +9 -0
- package/dist/gated-write.js +24 -0
- package/dist/graph-recall.d.ts +3 -1
- package/dist/graph-recall.js +5 -3
- package/dist/hooks.d.ts +18 -2
- package/dist/hooks.js +128 -32
- package/dist/importers.js +5 -12
- package/dist/judgment.d.ts +30 -0
- package/dist/judgment.js +122 -0
- package/dist/mcp/server.js +171 -210
- package/dist/memory.d.ts +19 -2
- package/dist/memory.js +35 -3
- package/dist/merged-row.d.ts +6 -0
- package/dist/merged-row.js +35 -0
- package/dist/multihop.d.ts +2 -1
- package/dist/multihop.js +7 -4
- package/dist/physics-state.d.ts +0 -4
- package/dist/physics-state.js +0 -6
- package/dist/predictions.d.ts +2 -17
- package/dist/predictions.js +2 -15
- package/dist/reject-flow.d.ts +7 -5
- package/dist/reject-flow.js +41 -12
- package/dist/salience.js +12 -5
- package/dist/same-text.d.ts +17 -0
- package/dist/same-text.js +38 -0
- package/dist/scheduler.d.ts +4 -0
- package/dist/scheduler.js +8 -0
- package/dist/search.d.ts +7 -0
- package/dist/search.js +16 -32
- package/dist/secret-detect.d.ts +2 -0
- package/dist/secret-detect.js +6 -0
- package/dist/server-detect.js +9 -33
- package/dist/server.js +6 -62
- package/dist/session-digest.d.ts +79 -0
- package/dist/session-digest.js +528 -0
- package/dist/shared.d.ts +10 -2
- package/dist/shared.js +44 -36
- package/dist/store.d.ts +9 -2
- package/dist/store.js +25 -2
- package/dist/token-ledger.d.ts +46 -8
- package/dist/token-ledger.js +140 -21
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/extensions/openclaw-plugin/README.md +4 -4
- package/extensions/openclaw-plugin/openclaw.plugin.json +2 -2
- package/extensions/openclaw-plugin/package.json +1 -1
- package/openclaw.plugin.json +2 -2
- package/package.json +2 -2
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Codex prints this line only when a patch applied (apply-patch lib.rs). */
|
|
2
|
+
export declare const PATCH_SUCCESS_LINE = "Success. Updated the following files:";
|
|
3
|
+
/** Paths a patch body adds, deletes, updates or moves to, in patch order; [] when the text holds no patch. */
|
|
4
|
+
export declare function patchPaths(body: string): string[];
|
|
5
|
+
export interface ShellPatch {
|
|
6
|
+
body: string;
|
|
7
|
+
/** The `cd <dir> &&` target, relative to the call's working directory. */
|
|
8
|
+
cd: string | null;
|
|
9
|
+
}
|
|
10
|
+
/** The patch a shell call hands to apply_patch, or null for any other command. */
|
|
11
|
+
export declare function shellPatch(command: readonly string[] | string): ShellPatch | null;
|
|
12
|
+
//# sourceMappingURL=codex-patch.d.ts.map
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// Files named by a Codex apply_patch call, read the way openai/codex codex-rs/apply-patch reads them
|
|
2
|
+
// (parser.rs for the body, invocation.rs for the shell forms it runs as apply_patch).
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import { isStringValue } from './capture.js';
|
|
5
|
+
/** Codex prints this line only when a patch applied (apply-patch lib.rs). */
|
|
6
|
+
export const PATCH_SUCCESS_LINE = 'Success. Updated the following files:';
|
|
7
|
+
const BEGIN_PATCH = '*** Begin Patch';
|
|
8
|
+
const END_PATCH = '*** End Patch';
|
|
9
|
+
const MOVE_TO = '*** Move to: ';
|
|
10
|
+
const FILE_HEADER = /^\*\*\* (Add|Delete|Update) File: (.+)$/;
|
|
11
|
+
/** Paths a patch body adds, deletes, updates or moves to, in patch order; [] when the text holds no patch. */
|
|
12
|
+
export function patchPaths(body) {
|
|
13
|
+
const lines = body.split(/\r?\n/);
|
|
14
|
+
const start = lines.findIndex((l) => l.trim() === BEGIN_PATCH);
|
|
15
|
+
if (start < 0)
|
|
16
|
+
return [];
|
|
17
|
+
const paths = [];
|
|
18
|
+
let updating = false;
|
|
19
|
+
let canMove = false;
|
|
20
|
+
for (const line of lines.slice(start + 1)) {
|
|
21
|
+
// Inside an update hunk a leading space marks a context line, so headers match on trimEnd only there.
|
|
22
|
+
const head = updating ? line.trimEnd() : line.trim();
|
|
23
|
+
if (head === END_PATCH)
|
|
24
|
+
break;
|
|
25
|
+
const header = FILE_HEADER.exec(head);
|
|
26
|
+
if (header) {
|
|
27
|
+
paths.push(header[2].trim());
|
|
28
|
+
updating = header[1] === 'Update';
|
|
29
|
+
canMove = updating;
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
if (canMove && head.startsWith(MOVE_TO))
|
|
33
|
+
paths.push(head.slice(MOVE_TO.length).trim());
|
|
34
|
+
canMove = false;
|
|
35
|
+
}
|
|
36
|
+
return paths.filter(Boolean);
|
|
37
|
+
}
|
|
38
|
+
const HEREDOC = /^\s*(?:cd\s+(?<cd>'[^']*'|"[^"]*"|[^\s'"&|;]+)\s+&&\s+)?(?:apply_patch|applypatch)\s+<<\s*(?<q>['"]?)(?<tag>\w+)\k<q>[ \t]*\r?\n(?<body>[\s\S]*?)\r?\n\s*\k<tag>\s*$/;
|
|
39
|
+
/** The script a shell argv runs, for the shells Codex checks for an apply_patch heredoc. */
|
|
40
|
+
function shellScript(argv) {
|
|
41
|
+
const exe = path.basename(argv[0] ?? '').toLowerCase().replace(/\.exe$/, '');
|
|
42
|
+
if ((exe === 'bash' || exe === 'zsh' || exe === 'sh') && argv.length === 3 && (argv[1] === '-lc' || argv[1] === '-c'))
|
|
43
|
+
return argv[2];
|
|
44
|
+
if (exe === 'pwsh' || exe === 'powershell') {
|
|
45
|
+
const rest = argv.slice(1).filter((a) => a.toLowerCase() !== '-noprofile');
|
|
46
|
+
if (rest.length === 2 && rest[0].toLowerCase() === '-command')
|
|
47
|
+
return rest[1];
|
|
48
|
+
}
|
|
49
|
+
if (exe === 'cmd' && argv.length === 3 && argv[1].toLowerCase() === '/c')
|
|
50
|
+
return argv[2];
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
/** The patch a shell call hands to apply_patch, or null for any other command. */
|
|
54
|
+
export function shellPatch(command) {
|
|
55
|
+
let script;
|
|
56
|
+
if (isStringValue(command)) {
|
|
57
|
+
script = command;
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
const exe = path.basename(command[0] ?? '').toLowerCase();
|
|
61
|
+
if (command.length === 2 && (exe === 'apply_patch' || exe === 'applypatch'))
|
|
62
|
+
return { body: command[1], cd: null };
|
|
63
|
+
script = shellScript(command);
|
|
64
|
+
}
|
|
65
|
+
const match = script === null ? null : HEREDOC.exec(script);
|
|
66
|
+
if (!match?.groups)
|
|
67
|
+
return null;
|
|
68
|
+
const cd = match.groups.cd ?? null;
|
|
69
|
+
return { body: match.groups.body, cd: cd === null ? null : cd.replace(/^(['"])(.*)\1$/, '$2') };
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=codex-patch.js.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare const COMPACTION_ITEM_MAX_CHARS = 500;
|
|
2
|
+
export declare const COMPACTION_ITEM_ROW_CAP = 10;
|
|
3
|
+
export interface ParsedCompactionItems {
|
|
4
|
+
found: boolean;
|
|
5
|
+
items: string[];
|
|
6
|
+
}
|
|
7
|
+
export interface CompactionItemRows {
|
|
8
|
+
rows: string[];
|
|
9
|
+
tooLong: number;
|
|
10
|
+
capped: number;
|
|
11
|
+
}
|
|
12
|
+
/** The <summary> body of a PostCompact compact_summary with any <analysis> block removed; the whole text trimmed when there is no <summary> tag. */
|
|
13
|
+
export declare function compactSummaryBody(compactSummary: string): string;
|
|
14
|
+
/** found=false when no "Memories for hippo" heading; items in order, "none" dropped, never cut. */
|
|
15
|
+
export declare function parseCompactionItems(summaryBody: string): ParsedCompactionItems;
|
|
16
|
+
/** First COMPACTION_ITEM_ROW_CAP items of at most COMPACTION_ITEM_MAX_CHARS chars become rows. */
|
|
17
|
+
export declare function selectItemRows(items: readonly string[]): CompactionItemRows;
|
|
18
|
+
//# sourceMappingURL=compaction-items.d.ts.map
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// Reads the "Memories for hippo" list out of a compaction summary. Pure text in, text out: hooks and replay share it.
|
|
2
|
+
export const COMPACTION_ITEM_MAX_CHARS = 500;
|
|
3
|
+
export const COMPACTION_ITEM_ROW_CAP = 10;
|
|
4
|
+
const HEADING = 'memories for hippo';
|
|
5
|
+
const ITEM_LINE = /^\s*(?:[-*]|\d+[.)])(?:\s+(.*))?$/;
|
|
6
|
+
/** The <summary> body of a PostCompact compact_summary with any <analysis> block removed; the whole text trimmed when there is no <summary> tag. */
|
|
7
|
+
export function compactSummaryBody(compactSummary) {
|
|
8
|
+
const text = compactSummary.replace(/<analysis>[\s\S]*?<\/analysis>/g, '');
|
|
9
|
+
const open = text.lastIndexOf('<summary>');
|
|
10
|
+
if (open < 0)
|
|
11
|
+
return text.trim();
|
|
12
|
+
const from = open + '<summary>'.length;
|
|
13
|
+
const close = text.indexOf('</summary>', from);
|
|
14
|
+
return text.slice(from, close < 0 ? undefined : close).trim();
|
|
15
|
+
}
|
|
16
|
+
function isHeading(line) {
|
|
17
|
+
const bare = line.replace(/^(?:[#*\s]|\d+\.)+/, '').replace(/[*:\s]+$/, '');
|
|
18
|
+
return bare.toLowerCase() === HEADING;
|
|
19
|
+
}
|
|
20
|
+
/** found=false when no "Memories for hippo" heading; items in order, "none" dropped, never cut. */
|
|
21
|
+
export function parseCompactionItems(summaryBody) {
|
|
22
|
+
const lines = summaryBody.split(/\r?\n/);
|
|
23
|
+
let heading = -1;
|
|
24
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
25
|
+
if (isHeading(lines[i])) {
|
|
26
|
+
heading = i;
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (heading < 0)
|
|
31
|
+
return { found: false, items: [] };
|
|
32
|
+
const after = lines.slice(heading + 1).join('\n');
|
|
33
|
+
const end = after.indexOf('</summary>');
|
|
34
|
+
const items = [];
|
|
35
|
+
let current = -1;
|
|
36
|
+
for (const line of (end < 0 ? after : after.slice(0, end)).split('\n')) {
|
|
37
|
+
if (line.trim() === '')
|
|
38
|
+
continue;
|
|
39
|
+
const marker = ITEM_LINE.exec(line);
|
|
40
|
+
if (marker) {
|
|
41
|
+
const text = (marker[1] ?? '').trim();
|
|
42
|
+
current = text === '' ? -1 : items.push(text) - 1;
|
|
43
|
+
}
|
|
44
|
+
else if (/^\s/.test(line)) {
|
|
45
|
+
if (current >= 0)
|
|
46
|
+
items[current] += ` ${line.trim()}`;
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return { found: true, items: items.filter((item) => !/^none\.?$/i.test(item)) };
|
|
53
|
+
}
|
|
54
|
+
/** First COMPACTION_ITEM_ROW_CAP items of at most COMPACTION_ITEM_MAX_CHARS chars become rows. */
|
|
55
|
+
export function selectItemRows(items) {
|
|
56
|
+
const fitting = items.filter((item) => item.length <= COMPACTION_ITEM_MAX_CHARS);
|
|
57
|
+
const rows = fitting.slice(0, COMPACTION_ITEM_ROW_CAP);
|
|
58
|
+
return { rows, tooLong: items.length - fitting.length, capped: fitting.length - rows.length };
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=compaction-items.js.map
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { type DatabaseSyncLike } from './db.js';
|
|
2
|
+
/** PostCompact has 10 s in all (PreCompact 30 s), so a locked store must be given up on early. */
|
|
3
|
+
export declare const COMPACTION_DB_WAIT_MS = 2000;
|
|
4
|
+
/** Tested verbatim: Claude Code hands PreCompact stdout to the summariser as instructions. */
|
|
5
|
+
export declare const PRE_COMPACT_INSTRUCTION = "In your summary, add a last section titled 'Memories for hippo'. List, one per line starting with '- ', each lesson learned, decision made (with its reason) and correction the user gave in this session that should outlive it. Write each as a standalone sentence that names its subject. Leave out anything an earlier summary already listed under 'Memories for hippo'. Write '- none' if nothing new remains.";
|
|
6
|
+
/** Long enough that a live hook has finished with its own record. */
|
|
7
|
+
export declare const REPLAY_AFTER_MS: number;
|
|
8
|
+
/** Claude Code deletes transcripts after 30 days, so an older gap can never be filled. */
|
|
9
|
+
export declare const TRANSCRIPT_FILL_WINDOW_MS: number;
|
|
10
|
+
export type CompactionStatus = 'started' | 'summarised' | 'done' | 'no-summary';
|
|
11
|
+
export interface CompactionRecord {
|
|
12
|
+
id: string;
|
|
13
|
+
tenantId: string;
|
|
14
|
+
sessionId: string;
|
|
15
|
+
originProject: string;
|
|
16
|
+
trigger: string | null;
|
|
17
|
+
cwd: string | null;
|
|
18
|
+
transcriptPath: string | null;
|
|
19
|
+
snapshotSaved: boolean;
|
|
20
|
+
startedAt: string;
|
|
21
|
+
summarisedAt: string | null;
|
|
22
|
+
summary: string | null;
|
|
23
|
+
items: string[];
|
|
24
|
+
itemsWritten: number;
|
|
25
|
+
status: CompactionStatus;
|
|
26
|
+
}
|
|
27
|
+
export type Log = (message: string) => void;
|
|
28
|
+
/** Where the session ran: rows written through the global store keep the project the session was in. */
|
|
29
|
+
export declare function compactionOrigin(hippoRoot: string, cwd: string | null): string;
|
|
30
|
+
interface ScrubbedSummary {
|
|
31
|
+
summary: string;
|
|
32
|
+
items: string[];
|
|
33
|
+
/** False when the summary has no memories section. */
|
|
34
|
+
found: boolean;
|
|
35
|
+
}
|
|
36
|
+
/** What the record keeps of a compact_summary: the scrubbed body, and every parsed item scrubbed. */
|
|
37
|
+
export declare function readCompactionText(compactSummary: string): ScrubbedSummary;
|
|
38
|
+
export interface CompactionStart {
|
|
39
|
+
sessionId: string;
|
|
40
|
+
originProject: string;
|
|
41
|
+
trigger: string | null;
|
|
42
|
+
cwd: string | null;
|
|
43
|
+
transcriptPath: string | null;
|
|
44
|
+
}
|
|
45
|
+
export declare function startCompaction(db: DatabaseSyncLike, tenantId: string, start: CompactionStart, at?: Date): string;
|
|
46
|
+
/** A session's newest record, or null. */
|
|
47
|
+
export declare function latestCompaction(db: DatabaseSyncLike, tenantId: string, sessionId: string): CompactionRecord | null;
|
|
48
|
+
/** Best effort, never throws: a compaction must not fail because its record could not be written. */
|
|
49
|
+
export declare function recordCompactionStart(hippoRoot: string, start: Omit<CompactionStart, 'originProject'>, log: Log): string | null;
|
|
50
|
+
export declare function recordSnapshotSaved(hippoRoot: string, recordId: string, log: Log): void;
|
|
51
|
+
export interface CompactionText {
|
|
52
|
+
summary: string;
|
|
53
|
+
items: string[];
|
|
54
|
+
}
|
|
55
|
+
export interface ItemContext {
|
|
56
|
+
tenantId: string;
|
|
57
|
+
/** null when the record step failed: the items still go in, with no record to close. */
|
|
58
|
+
recordId: string | null;
|
|
59
|
+
sessionId: string;
|
|
60
|
+
originProject: string;
|
|
61
|
+
items: string[];
|
|
62
|
+
}
|
|
63
|
+
/** Items that become rows, then the record `done`, in one transaction so two sessions cannot both insert one text. Mirrors after commit. */
|
|
64
|
+
export declare function saveItems(db: DatabaseSyncLike, hippoRoot: string, ctx: ItemContext, log: Log): number;
|
|
65
|
+
export interface PostCompactPayload {
|
|
66
|
+
sessionId: string;
|
|
67
|
+
trigger: string | null;
|
|
68
|
+
cwd: string | null;
|
|
69
|
+
transcriptPath: string | null;
|
|
70
|
+
/** null when Claude Code sent none; the transcript fills the record later. */
|
|
71
|
+
compactSummary: string | null;
|
|
72
|
+
}
|
|
73
|
+
/** null when the text is not a PostCompact payload naming a session. */
|
|
74
|
+
export declare function parsePostCompactPayload(stdinText: string | undefined): PostCompactPayload | null;
|
|
75
|
+
export interface CompactionSaveResult {
|
|
76
|
+
/** Memories written; null when neither the record nor the items could be saved. */
|
|
77
|
+
written: number | null;
|
|
78
|
+
/** True when the rest waits for the next sleep or post-compact. */
|
|
79
|
+
deferred: boolean;
|
|
80
|
+
snapshotSaved: boolean;
|
|
81
|
+
}
|
|
82
|
+
/** The PostCompact work: record the summary, then write its items. Each step is independent; a busy store spools or defers. Never throws. */
|
|
83
|
+
export declare function saveCompaction(hippoRoot: string, payload: PostCompactPayload, log: Log): CompactionSaveResult;
|
|
84
|
+
/** The one line PostCompact prints, or null when nothing was saved. */
|
|
85
|
+
export declare function postCompactLine(result: CompactionSaveResult): string | null;
|
|
86
|
+
/** Finishes what a killed hook or a busy store left: `summarised` records, spool files, and `started` records the transcript can fill. Returns how many compactions it saved. */
|
|
87
|
+
export declare function replayCompactions(db: DatabaseSyncLike, hippoRoot: string, log: Log, deadline?: number): number;
|
|
88
|
+
/** For `hippo sleep` and post-compact: opens the store itself and never throws. */
|
|
89
|
+
export declare function replayCompactionsAt(hippoRoot: string, log: Log, opts?: {
|
|
90
|
+
busyWaitMs?: number;
|
|
91
|
+
deadline?: number;
|
|
92
|
+
}): number;
|
|
93
|
+
export {};
|
|
94
|
+
//# sourceMappingURL=compaction-record.d.ts.map
|