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
package/dist/capture/compact.js
CHANGED
|
@@ -5,103 +5,13 @@ import { saveActiveTaskSnapshot, loadActiveTaskSnapshot } from '../store/session
|
|
|
5
5
|
import { PRE_COMPACT_INSTRUCTION, parsePostCompactPayload, postCompactLine, recordCompactionStart, recordSnapshotSaved, saveCompaction, replayCompactionsAt, COMPACTION_DB_WAIT_MS, } from '../compaction-record.js';
|
|
6
6
|
import { resolveTenantId } from '../tenant.js';
|
|
7
7
|
import { defaultPreCompactLogPath } from '../hooks/shared.js';
|
|
8
|
-
import {
|
|
9
|
-
import { isObjectLike, isStringValue, readClaudeCodePreCompact } from '../capture-contract.js';
|
|
8
|
+
import { readClaudeCodePreCompact } from '../capture-contract.js';
|
|
10
9
|
import { errorMessage } from '../log.js';
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
10
|
+
import { resolveLastSessionTranscript } from './transcript.js';
|
|
11
|
+
import { mergeWorkingState, transcriptWorkingState } from './working-state.js';
|
|
13
12
|
// ---------------------------------------------------------------------------
|
|
14
13
|
// `hippo pre-compact` — PreCompact hook producer
|
|
15
14
|
// ---------------------------------------------------------------------------
|
|
16
|
-
export const PRE_COMPACT_TASK_CAP = 200;
|
|
17
|
-
export const PRE_COMPACT_SUMMARY_CAP = 2000;
|
|
18
|
-
export const PRE_COMPACT_NEXT_STEP_CAP = 500;
|
|
19
|
-
/**
|
|
20
|
-
* Cap from the RECENT end: summariseTranscript emits user turns oldest-to-
|
|
21
|
-
* newest with assistant responses after them, so a head-first cap keeps
|
|
22
|
-
* stale context and drops exactly the newest working state this feature
|
|
23
|
-
* exists to preserve. Keep the LAST maxChars instead, aligned forward to a
|
|
24
|
-
* nearby line start, with a trim marker.
|
|
25
|
-
*/
|
|
26
|
-
export function truncateKeepNewest(text, maxChars) {
|
|
27
|
-
if (text.length <= maxChars)
|
|
28
|
-
return text;
|
|
29
|
-
let start = text.length - maxChars;
|
|
30
|
-
const code = text.charCodeAt(start);
|
|
31
|
-
if (code >= 0xdc00 && code <= 0xdfff)
|
|
32
|
-
start += 1; // never start on a low surrogate
|
|
33
|
-
const nl = text.indexOf('\n', start);
|
|
34
|
-
if (nl !== -1 && nl + 1 < text.length && nl - start < 200)
|
|
35
|
-
start = nl + 1;
|
|
36
|
-
return '[...earlier turns trimmed]\n' + text.slice(start);
|
|
37
|
-
}
|
|
38
|
-
/** Most recent plain-text user message in a JSONL tail. Claude Code transcript shape only (PreCompact is claude-code-only). */
|
|
39
|
-
function lastPlainUserMessage(jsonl) {
|
|
40
|
-
const lines = jsonl.split('\n').filter((l) => l.trim());
|
|
41
|
-
for (let i = lines.length - 1; i >= 0; i--) {
|
|
42
|
-
let entry;
|
|
43
|
-
try {
|
|
44
|
-
entry = JSON.parse(lines[i]);
|
|
45
|
-
}
|
|
46
|
-
catch {
|
|
47
|
-
continue; // the tail read can start mid-line; skip the torn fragment
|
|
48
|
-
}
|
|
49
|
-
if (!isObjectLike(entry))
|
|
50
|
-
continue;
|
|
51
|
-
if (!('type' in entry) || entry.type !== 'user')
|
|
52
|
-
continue;
|
|
53
|
-
// Meta/sidechain lines carry type:'user' but are not the human: after a
|
|
54
|
-
// FIRST compaction the transcript holds the compact summary as an isMeta
|
|
55
|
-
// user line, and sub-agent turns are isSidechain — deriving "task" from
|
|
56
|
-
// either yields junk on every later compaction.
|
|
57
|
-
if (('isMeta' in entry && entry.isMeta === true) || ('isSidechain' in entry && entry.isSidechain === true))
|
|
58
|
-
continue;
|
|
59
|
-
const message = 'message' in entry && isObjectLike(entry.message) ? entry.message : undefined;
|
|
60
|
-
if (!message)
|
|
61
|
-
continue;
|
|
62
|
-
const text = humanUserText(entry, message);
|
|
63
|
-
if (text)
|
|
64
|
-
return text;
|
|
65
|
-
}
|
|
66
|
-
return '';
|
|
67
|
-
}
|
|
68
|
-
/** Last assistant text block in a JSONL tail (skips thinking + tool_use, same as summariseTranscript). */
|
|
69
|
-
function lastAssistantTextBlock(jsonl) {
|
|
70
|
-
const lines = jsonl.split('\n').filter((l) => l.trim());
|
|
71
|
-
for (let i = lines.length - 1; i >= 0; i--) {
|
|
72
|
-
let entry;
|
|
73
|
-
try {
|
|
74
|
-
entry = JSON.parse(lines[i]);
|
|
75
|
-
}
|
|
76
|
-
catch {
|
|
77
|
-
continue; // the tail read can start mid-line; skip the torn fragment
|
|
78
|
-
}
|
|
79
|
-
if (!isObjectLike(entry))
|
|
80
|
-
continue;
|
|
81
|
-
if (!('type' in entry) || entry.type !== 'assistant')
|
|
82
|
-
continue;
|
|
83
|
-
// Same meta/sidechain guard as lastPlainUserMessage: sub-agent turns
|
|
84
|
-
// (isSidechain) are not this session's next step.
|
|
85
|
-
if (('isMeta' in entry && entry.isMeta === true) || ('isSidechain' in entry && entry.isSidechain === true))
|
|
86
|
-
continue;
|
|
87
|
-
const message = 'message' in entry && isObjectLike(entry.message) ? entry.message : undefined;
|
|
88
|
-
if (!message)
|
|
89
|
-
continue;
|
|
90
|
-
const content = 'content' in message ? message.content : undefined;
|
|
91
|
-
if (!Array.isArray(content))
|
|
92
|
-
continue;
|
|
93
|
-
for (let j = content.length - 1; j >= 0; j--) {
|
|
94
|
-
const block = content[j];
|
|
95
|
-
if (isObjectLike(block)) {
|
|
96
|
-
const blockText = 'type' in block && block.type === 'text' && 'text' in block ? block.text : undefined;
|
|
97
|
-
if (isStringValue(blockText) && blockText.trim()) {
|
|
98
|
-
return blockText.trim();
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
return '';
|
|
104
|
-
}
|
|
105
15
|
// Diagnostic-only log; a long-lived install must not grow it unbounded.
|
|
106
16
|
const PRE_COMPACT_LOG_MAX_BYTES = 256 * 1024;
|
|
107
17
|
/**
|
|
@@ -148,45 +58,6 @@ function printPreCompactInstruction(logFile) {
|
|
|
148
58
|
appendPreCompactLog(logFile, `instruction not printed: ${errorMessage(err)}`);
|
|
149
59
|
}
|
|
150
60
|
}
|
|
151
|
-
/** A session's task, summary and next step from its transcript tail, secrets scrubbed and capped, '' where none; null with a logged reason when nothing is derivable. */
|
|
152
|
-
export function transcriptWorkingState(transcriptPath, log) {
|
|
153
|
-
let tail = '';
|
|
154
|
-
let rawTask = '';
|
|
155
|
-
let rawNextStep = '';
|
|
156
|
-
try {
|
|
157
|
-
// Compaction fires when a big tool_result lands, so the last human and assistant turns can sit
|
|
158
|
-
// megabytes back: grow the window a bounded number of times until both turns are in it.
|
|
159
|
-
const size = fs.statSync(transcriptPath).size;
|
|
160
|
-
for (const cap of [PRE_COMPACT_TAIL_BYTES, PRE_COMPACT_TAIL_BYTES * 4, PRE_COMPACT_TAIL_BYTES * 16]) {
|
|
161
|
-
if (cap > PRE_COMPACT_TAIL_BYTES)
|
|
162
|
-
log(`tail window grown to ${cap} bytes (last ${rawTask ? 'assistant' : 'user'} turn is further back)`);
|
|
163
|
-
tail = readTranscriptTail(transcriptPath, cap);
|
|
164
|
-
rawTask = lastPlainUserMessage(tail);
|
|
165
|
-
rawNextStep = lastAssistantTextBlock(tail);
|
|
166
|
-
if ((rawTask && rawNextStep) || size <= cap)
|
|
167
|
-
break;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
catch (err) {
|
|
171
|
-
log(`skip: could not read transcript tail: ${errorMessage(err)}`);
|
|
172
|
-
return null;
|
|
173
|
-
}
|
|
174
|
-
const rawSummary = summariseTranscript(tail);
|
|
175
|
-
if (!rawTask.trim() && !rawSummary.trim() && !rawNextStep.trim()) {
|
|
176
|
-
log('skip: empty summary');
|
|
177
|
-
return null;
|
|
178
|
-
}
|
|
179
|
-
// These fields skip the capture content gate and reach a prompt, so the strict scrub runs. The caps protect the
|
|
180
|
-
// re-injection token budget and never split a surrogate pair; `hippo snapshot save` stays uncapped.
|
|
181
|
-
const task = maskEmails(redactSecretsStrict(rawTask));
|
|
182
|
-
const summary = maskEmails(redactSecretsStrict(rawSummary));
|
|
183
|
-
const nextStep = maskEmails(redactSecretsStrict(rawNextStep));
|
|
184
|
-
return {
|
|
185
|
-
task: task.trim() ? truncateCodePointSafe(task, PRE_COMPACT_TASK_CAP) : '',
|
|
186
|
-
summary: summary.trim() ? truncateKeepNewest(summary, PRE_COMPACT_SUMMARY_CAP) : '',
|
|
187
|
-
next_step: nextStep.trim() ? truncateCodePointSafe(nextStep, PRE_COMPACT_NEXT_STEP_CAP) : '',
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
61
|
/** Runs the PreCompact producer: records the compaction, asks the summariser for memories, saves a working-state snapshot. Never extracts memories itself; SessionEnd capture owns that. */
|
|
191
62
|
function runPreCompact(hippoRoot, stdinText, stdinTimedOut, logFile) {
|
|
192
63
|
// The PreCompact hook fires in every Claude Code project, including
|
|
@@ -242,11 +113,6 @@ function resolvePreCompactTranscript(payloadTranscriptPath, stdinText, logFile)
|
|
|
242
113
|
}
|
|
243
114
|
function saveDerivedSnapshot(hippoRoot, logFile, sessionId, recordId, derived) {
|
|
244
115
|
const tenantId = resolveTenantId({});
|
|
245
|
-
// Per-field merge: a tool-heavy tail whose only user turns are
|
|
246
|
-
// tool_result arrays derives an empty task even though the summary is
|
|
247
|
-
// non-empty. Loading the existing snapshot first lets each field fall
|
|
248
|
-
// back independently instead of the whole write clobbering a
|
|
249
|
-
// user-authored field with blank text.
|
|
250
116
|
let existing = null;
|
|
251
117
|
try {
|
|
252
118
|
existing = loadActiveTaskSnapshot(hippoRoot, tenantId);
|
|
@@ -254,36 +120,17 @@ function saveDerivedSnapshot(hippoRoot, logFile, sessionId, recordId, derived) {
|
|
|
254
120
|
catch {
|
|
255
121
|
// No existing snapshot to merge against — proceed with derived-only.
|
|
256
122
|
}
|
|
257
|
-
// Field fallback must never move content across
|
|
258
|
-
// sessions — session A's task carried into a snapshot saved under session
|
|
259
|
-
// B's id would pass compact-resume's session gate wearing the wrong
|
|
260
|
-
// badge. Fall back only when the existing snapshot has no session, this
|
|
261
|
-
// payload has none, or they match.
|
|
262
|
-
const fallback = existing !== null &&
|
|
263
|
-
(existing.session_id === null || sessionId === null || existing.session_id === sessionId)
|
|
264
|
-
? existing
|
|
265
|
-
: null;
|
|
266
123
|
// Carried-over fields are not re-capped, as `hippo snapshot save` stays uncapped; saveActiveTaskSnapshot scrubs every field.
|
|
267
|
-
const
|
|
268
|
-
|
|
269
|
-
const nextStep = derived.next_step || (fallback?.next_step ?? '');
|
|
270
|
-
// All-empty fields (cross-session tail with nothing derivable) skip the
|
|
271
|
-
// write so a foreign session's junk never displaces the owning snapshot.
|
|
272
|
-
if (!task && !summary && !nextStep) {
|
|
124
|
+
const merged = mergeWorkingState(derived, existing, sessionId);
|
|
125
|
+
if (merged === null) {
|
|
273
126
|
appendPreCompactLog(logFile, 'skip: no snapshot content for this session (nothing derivable; fallback blocked or empty)');
|
|
274
127
|
}
|
|
275
128
|
else {
|
|
276
129
|
try {
|
|
277
|
-
saveActiveTaskSnapshot(hippoRoot, tenantId, {
|
|
278
|
-
task,
|
|
279
|
-
summary,
|
|
280
|
-
next_step: nextStep,
|
|
281
|
-
source: 'pre-compact',
|
|
282
|
-
session_id: sessionId,
|
|
283
|
-
});
|
|
130
|
+
saveActiveTaskSnapshot(hippoRoot, tenantId, { ...merged, source: 'pre-compact', session_id: sessionId });
|
|
284
131
|
appendPreCompactLog(logFile, 'snapshot saved');
|
|
285
132
|
if (recordId !== null)
|
|
286
|
-
recordSnapshotSaved(hippoRoot, recordId, (message) => appendPreCompactLog(logFile, message));
|
|
133
|
+
recordSnapshotSaved(hippoRoot, tenantId, recordId, (message) => appendPreCompactLog(logFile, message));
|
|
287
134
|
}
|
|
288
135
|
catch (err) {
|
|
289
136
|
appendPreCompactLog(logFile, `snapshot save failed: ${errorMessage(err)}`);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Context } from '../api/types.js';
|
|
2
|
+
import type { CallerProject } from '../prompt-hook.js';
|
|
3
|
+
export interface CallerItemsRequest {
|
|
4
|
+
readonly sessionId: string;
|
|
5
|
+
readonly project: CallerProject;
|
|
6
|
+
readonly trigger: string | null;
|
|
7
|
+
/** The "Memories for hippo" list the caller parsed from its compact summary. */
|
|
8
|
+
readonly items: readonly string[];
|
|
9
|
+
readonly requestId: string;
|
|
10
|
+
}
|
|
11
|
+
export interface CallerItemsResult {
|
|
12
|
+
readonly written: number;
|
|
13
|
+
}
|
|
14
|
+
/** Busy throws, for the server's 503: the retry carries the same request id, so nothing is spooled or deferred here. */
|
|
15
|
+
export declare function saveCompactionItemsForCaller(ctx: Context, req: CallerItemsRequest): CallerItemsResult;
|
|
16
|
+
//# sourceMappingURL=compaction-items-caller.d.ts.map
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// PostCompact for a caller on another machine: the items it parsed become rows under its owner and project, and a retried request id writes nothing twice.
|
|
2
|
+
import { BadRequestError } from '../api-errors.js';
|
|
3
|
+
import { COMPACTION_ITEM_MAX_CHARS } from '../compaction-items.js';
|
|
4
|
+
import { compactionByRequest, recordSummary, saveItems, scrubCompactionItems } from '../compaction-record.js';
|
|
5
|
+
import { log } from '../log.js';
|
|
6
|
+
import { assertRequestId, assertTrigger, bindCaller, callerInHoldout, withCallerDb } from './caller-session.js';
|
|
7
|
+
/** Busy throws, for the server's 503: the retry carries the same request id, so nothing is spooled or deferred here. */
|
|
8
|
+
export function saveCompactionItemsForCaller(ctx, req) {
|
|
9
|
+
assertTrigger(req.trigger);
|
|
10
|
+
assertRequestId(req.requestId);
|
|
11
|
+
// Refused before the scrub, as failure text is, so the cut after it only ever takes back a mask's growth.
|
|
12
|
+
if (req.items.some((item) => item.length > COMPACTION_ITEM_MAX_CHARS))
|
|
13
|
+
throw new BadRequestError(`items: each at most ${COMPACTION_ITEM_MAX_CHARS} characters`);
|
|
14
|
+
const key = bindCaller(ctx, req.sessionId, req.project);
|
|
15
|
+
if (callerInHoldout(ctx, req.sessionId))
|
|
16
|
+
return { written: 0 };
|
|
17
|
+
return withCallerDb(ctx, (db) => {
|
|
18
|
+
const earlier = compactionByRequest(db, ctx.tenantId, req.requestId, req.sessionId);
|
|
19
|
+
// Past `summarised` the first try finished, so its count is the answer; a `summarised` one failed at the items and is reused.
|
|
20
|
+
if (earlier !== null && earlier.status !== 'summarised')
|
|
21
|
+
return { written: earlier.itemsWritten };
|
|
22
|
+
const meta = { sessionId: req.sessionId, trigger: req.trigger, cwd: null, transcriptPath: null };
|
|
23
|
+
const text = { summary: '', items: scrubCompactionItems(req.items) };
|
|
24
|
+
const record = earlier ?? recordSummary(db, ctx.hippoRoot, ctx.tenantId, meta, text, new Date(), { originProject: req.project.name, requestId: req.requestId });
|
|
25
|
+
const written = saveItems(db, ctx.hippoRoot, {
|
|
26
|
+
tenantId: ctx.tenantId, recordId: record.id, sessionId: req.sessionId, originProject: record.originProject, cwd: null,
|
|
27
|
+
items: record.items, caller: { actor: key.owner, origins: key.project },
|
|
28
|
+
}, (message) => log.info(`post-compact: ${message}`));
|
|
29
|
+
return { written };
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=compaction-items-caller.js.map
|
|
@@ -1,21 +1,9 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Capture actionable items from conversation text.
|
|
3
|
-
*
|
|
4
|
-
* Uses heuristic pattern matching (no LLM) to extract:
|
|
5
|
-
* - Decisions ("we decided", "let's do", "going with")
|
|
6
|
-
* - Specs / requirements (bullet lists after spec/feature/plan headings)
|
|
7
|
-
* - Rules / constraints ("never", "always", "the rule is", "must")
|
|
8
|
-
* - Errors / gotchas ("error:", "bug:", "gotcha:", "watch out")
|
|
9
|
-
* - Preferences ("prefer", "use X instead of Y", "don't use")
|
|
10
|
-
*/
|
|
11
1
|
export interface ExtractedItem {
|
|
12
2
|
content: string;
|
|
13
3
|
category: string;
|
|
14
4
|
tags: string[];
|
|
15
5
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
* Main extraction function. Scans text for actionable items using heuristics.
|
|
19
|
-
*/
|
|
6
|
+
/** Captures complete supported statements, each text parsed alone so a fence or heading in one never reaches the next. */
|
|
7
|
+
export declare function extractFromTexts(texts: readonly string[]): ExtractedItem[];
|
|
20
8
|
export declare function extractFromText(text: string): ExtractedItem[];
|
|
21
9
|
//# sourceMappingURL=extract.d.ts.map
|