hippo-memory 0.37.0 → 0.38.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 +52 -260
- package/dist/cli.js +367 -0
- package/dist/cli.js.map +1 -1
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +60 -1
- package/dist/db.js.map +1 -1
- package/dist/goals.d.ts +73 -0
- package/dist/goals.d.ts.map +1 -0
- package/dist/goals.js +227 -0
- package/dist/goals.js.map +1 -0
- package/dist/mcp/server.js +1 -1
- package/dist/server.js +1 -1
- package/dist/src/cli.js +367 -0
- package/dist/src/cli.js.map +1 -1
- package/dist/src/db.js +60 -1
- package/dist/src/db.js.map +1 -1
- package/dist/src/goals.js +227 -0
- package/dist/src/goals.js.map +1 -0
- package/dist/src/mcp/server.js +1 -1
- package/dist/src/server.js +1 -1
- package/extensions/openclaw-plugin/openclaw.plugin.json +1 -1
- package/extensions/openclaw-plugin/package.json +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
package/dist/src/cli.js
CHANGED
|
@@ -41,6 +41,8 @@ import { loadPhysicsState, resetAllPhysicsState } from './physics-state.js';
|
|
|
41
41
|
import { computeSystemEnergy, vecNorm } from './physics.js';
|
|
42
42
|
import { loadConfig } from './config.js';
|
|
43
43
|
import { openHippoDb, closeHippoDb } from './db.js';
|
|
44
|
+
import { getActiveGoalsWithDb, MAX_FINAL_MULTIPLIER, pushGoal, getActiveGoals, completeGoal, suspendGoal, resumeGoal } from './goals.js';
|
|
45
|
+
import { rowToGoal } from './goals.js';
|
|
44
46
|
import { captureError, extractLessons, deduplicateLesson, runWatched, fetchGitLog, isGitRepo, } from './autolearn.js';
|
|
45
47
|
import { extractInvalidationTarget, invalidateMatching } from './invalidation.js';
|
|
46
48
|
import { extractPathTags } from './path-context.js';
|
|
@@ -820,6 +822,142 @@ async function cmdRecall(hippoRoot, query, flags) {
|
|
|
820
822
|
.map((r) => (r.entry.tags?.includes(goalTag) ? { ...r, score: r.score * 1.5 } : r))
|
|
821
823
|
.sort((a, b) => b.score - a.score);
|
|
822
824
|
}
|
|
825
|
+
// dlPFC depth (B3, v0.38). When HIPPO_SESSION_ID is set (env or
|
|
826
|
+
// --session-id flag) and the (tenant, session) has active goals, boost
|
|
827
|
+
// memories whose tags overlap any active goal's name. Final multiplier is
|
|
828
|
+
// hard-capped at MAX_FINAL_MULTIPLIER (3.0x). Each boosted (memory, goal)
|
|
829
|
+
// pair is logged into goal_recall_log for outcome propagation.
|
|
830
|
+
//
|
|
831
|
+
// Runs AFTER the explicit `--goal <tag>` block so an explicit flag always
|
|
832
|
+
// wins: if the user passed `--goal X`, this block is skipped entirely
|
|
833
|
+
// (gated on `goalTag === ''`).
|
|
834
|
+
//
|
|
835
|
+
// db-handle note (plan-eng-review fix #5): the surrounding cmdRecall path
|
|
836
|
+
// does NOT keep an open db handle in scope at this point — earlier search
|
|
837
|
+
// helpers (loadSearchEntries, hybridSearch, ...) each open and close their
|
|
838
|
+
// own short-lived handles. Reusing isn't practical here; we open a fresh
|
|
839
|
+
// short-lived handle for this block, mirroring the existing CLI pattern
|
|
840
|
+
// (e.g. emitCliAudit). Closed in `finally`.
|
|
841
|
+
const sessionId = (flags['session-id'] !== undefined
|
|
842
|
+
? String(flags['session-id'])
|
|
843
|
+
: process.env.HIPPO_SESSION_ID ?? '').trim();
|
|
844
|
+
if (sessionId && goalTag === '') {
|
|
845
|
+
// Use the same tenant as the recall path — see cmdRecall:778.
|
|
846
|
+
const tenantIdForGoals = tenantId;
|
|
847
|
+
const dbForGoals = openHippoDb(hippoRoot);
|
|
848
|
+
try {
|
|
849
|
+
const active = getActiveGoalsWithDb(dbForGoals, {
|
|
850
|
+
sessionId,
|
|
851
|
+
tenantId: tenantIdForGoals,
|
|
852
|
+
});
|
|
853
|
+
if (active.length > 0) {
|
|
854
|
+
const goalsByTag = new Map(active.map((g) => [g.goalName, g]));
|
|
855
|
+
// Task 7: load retrieval_policy rows for active goals so per-policy
|
|
856
|
+
// multipliers can compose onto the base goal-tag boost. The composed
|
|
857
|
+
// result is hard-capped at MAX_FINAL_MULTIPLIER (3.0x) BEFORE applying
|
|
858
|
+
// to score — even an `errorPriority: 9.0` policy cannot exceed 3.0x.
|
|
859
|
+
const policiesByGoalId = new Map();
|
|
860
|
+
for (const g of active) {
|
|
861
|
+
if (!g.retrievalPolicyId)
|
|
862
|
+
continue;
|
|
863
|
+
const row = dbForGoals.prepare(`
|
|
864
|
+
SELECT id, goal_id, policy_type, weight_schema_fit, weight_recency, weight_outcome, error_priority
|
|
865
|
+
FROM retrieval_policy WHERE id = ?
|
|
866
|
+
`).get(g.retrievalPolicyId);
|
|
867
|
+
if (row) {
|
|
868
|
+
policiesByGoalId.set(g.id, {
|
|
869
|
+
id: row.id,
|
|
870
|
+
goalId: row.goal_id,
|
|
871
|
+
policyType: row.policy_type,
|
|
872
|
+
weightSchemaFit: row.weight_schema_fit,
|
|
873
|
+
weightRecency: row.weight_recency,
|
|
874
|
+
weightOutcome: row.weight_outcome,
|
|
875
|
+
errorPriority: row.error_priority,
|
|
876
|
+
});
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
results = results
|
|
880
|
+
.map((r) => {
|
|
881
|
+
const tags = r.entry.tags ?? [];
|
|
882
|
+
const matches = tags.filter((t) => goalsByTag.has(t));
|
|
883
|
+
if (matches.length === 0)
|
|
884
|
+
return r;
|
|
885
|
+
// Base 2.0x for first match, +0.5x per additional, capped at 3.0x.
|
|
886
|
+
let multiplier = Math.min(2.0 + 0.5 * (matches.length - 1), MAX_FINAL_MULTIPLIER);
|
|
887
|
+
// Compose per-policy multipliers per matched tag.
|
|
888
|
+
for (const tag of matches) {
|
|
889
|
+
const goal = goalsByTag.get(tag);
|
|
890
|
+
const policy = policiesByGoalId.get(goal.id);
|
|
891
|
+
if (!policy)
|
|
892
|
+
continue;
|
|
893
|
+
if (policy.policyType === 'error-prioritized' && tags.includes('error')) {
|
|
894
|
+
multiplier *= policy.errorPriority;
|
|
895
|
+
}
|
|
896
|
+
else if (policy.policyType === 'schema-fit-biased') {
|
|
897
|
+
// Linearly weight schema_fit in [0,1] up to (weightSchemaFit)x.
|
|
898
|
+
// Default 1.0 is a no-op.
|
|
899
|
+
multiplier *=
|
|
900
|
+
1.0 +
|
|
901
|
+
Math.max(0, policy.weightSchemaFit - 1.0) *
|
|
902
|
+
(r.entry.schema_fit ?? 0.5);
|
|
903
|
+
}
|
|
904
|
+
else if (policy.policyType === 'recency-first') {
|
|
905
|
+
multiplier *= policy.weightRecency;
|
|
906
|
+
}
|
|
907
|
+
else if (policy.policyType === 'hybrid') {
|
|
908
|
+
multiplier *= policy.weightOutcome;
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
// Hard cap AFTER all composition.
|
|
912
|
+
multiplier = Math.min(multiplier, MAX_FINAL_MULTIPLIER);
|
|
913
|
+
return {
|
|
914
|
+
...r,
|
|
915
|
+
score: r.score * multiplier,
|
|
916
|
+
_goalMatches: matches,
|
|
917
|
+
};
|
|
918
|
+
})
|
|
919
|
+
.sort((a, b) => b.score - a.score);
|
|
920
|
+
// Filter to local memories only — global memory IDs aren't in this
|
|
921
|
+
// DB's memories table, so the FK on goal_recall_log.memory_id would
|
|
922
|
+
// fail. dlPFC depth's outcome propagation is session-scoped to local;
|
|
923
|
+
// boost on ranking still applies to global results, just no log row
|
|
924
|
+
// -> no propagation.
|
|
925
|
+
const topKIds = results.slice(0, limit).map((r) => r.entry.id);
|
|
926
|
+
const localIds = new Set();
|
|
927
|
+
if (topKIds.length > 0) {
|
|
928
|
+
const placeholders = topKIds.map(() => '?').join(',');
|
|
929
|
+
const localRows = dbForGoals.prepare(`SELECT id FROM memories WHERE id IN (${placeholders})`).all(...topKIds);
|
|
930
|
+
for (const row of localRows)
|
|
931
|
+
localIds.add(row.id);
|
|
932
|
+
}
|
|
933
|
+
// Log top-K boosted recalls. INSERT OR IGNORE because
|
|
934
|
+
// UNIQUE(memory_id, goal_id) means a re-recall during the same goal
|
|
935
|
+
// life is a no-op for outcome attribution.
|
|
936
|
+
const recalledAt = new Date().toISOString();
|
|
937
|
+
const insertLog = dbForGoals.prepare(`
|
|
938
|
+
INSERT OR IGNORE INTO goal_recall_log
|
|
939
|
+
(goal_id, memory_id, tenant_id, session_id, recalled_at, score)
|
|
940
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
941
|
+
`);
|
|
942
|
+
for (const r of results.slice(0, limit)) {
|
|
943
|
+
if (!localIds.has(r.entry.id))
|
|
944
|
+
continue; // global -> skip log insert
|
|
945
|
+
const matches = r._goalMatches;
|
|
946
|
+
if (!matches || matches.length === 0)
|
|
947
|
+
continue;
|
|
948
|
+
for (const tag of matches) {
|
|
949
|
+
const goal = goalsByTag.get(tag);
|
|
950
|
+
if (!goal)
|
|
951
|
+
continue;
|
|
952
|
+
insertLog.run(goal.id, r.entry.id, tenantIdForGoals, sessionId, recalledAt, r.score);
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
finally {
|
|
958
|
+
closeHippoDb(dbForGoals);
|
|
959
|
+
}
|
|
960
|
+
}
|
|
823
961
|
// Pineal salience MVP (RESEARCH.md §"AI Pineal Gland — Intuition and Awareness
|
|
824
962
|
// Module"). When --salience-threshold T is set (T > 0), memories whose
|
|
825
963
|
// retrieval_count is below T are downweighted: score *= max(0.5, count / T).
|
|
@@ -4030,6 +4168,211 @@ function cmdAuditLog(hippoRoot, args, flags) {
|
|
|
4030
4168
|
console.error(`Unknown audit subcommand: ${sub}. Expected: list.`);
|
|
4031
4169
|
process.exit(1);
|
|
4032
4170
|
}
|
|
4171
|
+
// ---------------------------------------------------------------------------
|
|
4172
|
+
// `hippo goal <push|list|complete|suspend|resume>` — B3 dlPFC depth (Task 10)
|
|
4173
|
+
// ---------------------------------------------------------------------------
|
|
4174
|
+
const GOAL_POLICY_TYPES = [
|
|
4175
|
+
'schema-fit-biased',
|
|
4176
|
+
'error-prioritized',
|
|
4177
|
+
'recency-first',
|
|
4178
|
+
'hybrid',
|
|
4179
|
+
];
|
|
4180
|
+
function sanitizeGoalName(s) {
|
|
4181
|
+
// Strip C0 control chars + DEL to prevent terminal escape injection.
|
|
4182
|
+
return s.replace(/[\x00-\x1f\x7f]/g, '?');
|
|
4183
|
+
}
|
|
4184
|
+
function resolveGoalSession(flags) {
|
|
4185
|
+
const sessionId = (flags['session-id'] !== undefined
|
|
4186
|
+
? String(flags['session-id'])
|
|
4187
|
+
: process.env.HIPPO_SESSION_ID ?? '').trim();
|
|
4188
|
+
if (!sessionId) {
|
|
4189
|
+
console.error('session id required (set HIPPO_SESSION_ID or pass --session-id)');
|
|
4190
|
+
process.exit(1);
|
|
4191
|
+
}
|
|
4192
|
+
const tenantId = (flags['tenant-id'] !== undefined
|
|
4193
|
+
? String(flags['tenant-id'])
|
|
4194
|
+
: process.env.HIPPO_TENANT ?? 'default').trim() || 'default';
|
|
4195
|
+
return { sessionId, tenantId };
|
|
4196
|
+
}
|
|
4197
|
+
function cmdGoalPush(hippoRoot, args, flags) {
|
|
4198
|
+
const rawName = args.join(' ').trim();
|
|
4199
|
+
if (!rawName) {
|
|
4200
|
+
console.error('Usage: hippo goal push <name> [--policy <type>] [--success "<condition>"] [--level N] [--parent <goalId>]');
|
|
4201
|
+
process.exit(1);
|
|
4202
|
+
}
|
|
4203
|
+
// Sanitize at WRITE time so corrupt names never enter the DB.
|
|
4204
|
+
const name = sanitizeGoalName(rawName);
|
|
4205
|
+
if (name !== rawName) {
|
|
4206
|
+
console.error('note: stripped control characters from goal name');
|
|
4207
|
+
}
|
|
4208
|
+
const { sessionId, tenantId } = resolveGoalSession(flags);
|
|
4209
|
+
let policy;
|
|
4210
|
+
const policyRaw = flags['policy'];
|
|
4211
|
+
if (policyRaw === true) {
|
|
4212
|
+
console.error('--policy requires a value (e.g., --policy error-prioritized)');
|
|
4213
|
+
process.exit(1);
|
|
4214
|
+
}
|
|
4215
|
+
if (typeof policyRaw === 'string') {
|
|
4216
|
+
if (!GOAL_POLICY_TYPES.includes(policyRaw)) {
|
|
4217
|
+
console.error(`Unknown --policy '${policyRaw}'. Expected one of: ${GOAL_POLICY_TYPES.join(' | ')}.`);
|
|
4218
|
+
process.exit(1);
|
|
4219
|
+
}
|
|
4220
|
+
policy = { policyType: policyRaw };
|
|
4221
|
+
}
|
|
4222
|
+
const successRaw = flags['success'];
|
|
4223
|
+
if (successRaw === true) {
|
|
4224
|
+
console.error('--success requires a value (e.g., --success "<condition>")');
|
|
4225
|
+
process.exit(1);
|
|
4226
|
+
}
|
|
4227
|
+
const successCondition = typeof successRaw === 'string' ? successRaw : undefined;
|
|
4228
|
+
const levelRaw = flags['level'];
|
|
4229
|
+
let level;
|
|
4230
|
+
if (levelRaw === true) {
|
|
4231
|
+
console.error('--level requires a value (e.g., --level 1)');
|
|
4232
|
+
process.exit(1);
|
|
4233
|
+
}
|
|
4234
|
+
if (levelRaw !== undefined) {
|
|
4235
|
+
const parsed = Number(levelRaw);
|
|
4236
|
+
if (!Number.isFinite(parsed) || parsed < 0 || parsed > 2 || !Number.isInteger(parsed)) {
|
|
4237
|
+
console.error('--level must be an integer in [0, 2]');
|
|
4238
|
+
process.exit(1);
|
|
4239
|
+
}
|
|
4240
|
+
level = parsed;
|
|
4241
|
+
}
|
|
4242
|
+
const parentRaw = flags['parent'];
|
|
4243
|
+
if (parentRaw === true) {
|
|
4244
|
+
console.error('--parent requires a value (e.g., --parent <goalId>)');
|
|
4245
|
+
process.exit(1);
|
|
4246
|
+
}
|
|
4247
|
+
const parentGoalId = typeof parentRaw === 'string' ? parentRaw : undefined;
|
|
4248
|
+
const goal = pushGoal(hippoRoot, {
|
|
4249
|
+
sessionId,
|
|
4250
|
+
tenantId,
|
|
4251
|
+
goalName: name,
|
|
4252
|
+
level,
|
|
4253
|
+
parentGoalId,
|
|
4254
|
+
successCondition,
|
|
4255
|
+
policy,
|
|
4256
|
+
});
|
|
4257
|
+
console.log(goal.id);
|
|
4258
|
+
}
|
|
4259
|
+
function listAllGoals(hippoRoot, sessionId, tenantId) {
|
|
4260
|
+
const db = openHippoDb(hippoRoot);
|
|
4261
|
+
try {
|
|
4262
|
+
const rows = db.prepare(`
|
|
4263
|
+
SELECT id, session_id, tenant_id, goal_name, level, parent_goal_id, status,
|
|
4264
|
+
success_condition, retrieval_policy_id, created_at, completed_at, outcome_score
|
|
4265
|
+
FROM goal_stack
|
|
4266
|
+
WHERE tenant_id = ? AND session_id = ?
|
|
4267
|
+
ORDER BY created_at ASC
|
|
4268
|
+
`).all(tenantId, sessionId);
|
|
4269
|
+
return rows.map(rowToGoal);
|
|
4270
|
+
}
|
|
4271
|
+
finally {
|
|
4272
|
+
closeHippoDb(db);
|
|
4273
|
+
}
|
|
4274
|
+
}
|
|
4275
|
+
function cmdGoalList(hippoRoot, flags) {
|
|
4276
|
+
const { sessionId, tenantId } = resolveGoalSession(flags);
|
|
4277
|
+
const showAll = Boolean(flags['all']);
|
|
4278
|
+
const goals = showAll
|
|
4279
|
+
? listAllGoals(hippoRoot, sessionId, tenantId)
|
|
4280
|
+
: getActiveGoals(hippoRoot, { sessionId, tenantId });
|
|
4281
|
+
if (goals.length === 0) {
|
|
4282
|
+
console.log('(no goals)');
|
|
4283
|
+
return;
|
|
4284
|
+
}
|
|
4285
|
+
// 4-column table: id, status, goal_name, outcome. Plan calls it a "2-column"
|
|
4286
|
+
// table but the assertion list (id, status, goal_name, outcome) needs four;
|
|
4287
|
+
// tests check for substrings ('active', '0.9', name) so column count is
|
|
4288
|
+
// observably four but not asserted.
|
|
4289
|
+
const rows = goals.map(g => ({
|
|
4290
|
+
id: g.id,
|
|
4291
|
+
status: g.status,
|
|
4292
|
+
name: sanitizeGoalName(g.goalName),
|
|
4293
|
+
outcome: g.outcomeScore !== undefined ? g.outcomeScore.toString() : '-',
|
|
4294
|
+
}));
|
|
4295
|
+
const widths = {
|
|
4296
|
+
id: Math.max(2, ...rows.map(r => r.id.length)),
|
|
4297
|
+
status: Math.max(6, ...rows.map(r => r.status.length)),
|
|
4298
|
+
name: Math.max(4, ...rows.map(r => r.name.length)),
|
|
4299
|
+
outcome: Math.max(7, ...rows.map(r => r.outcome.length)),
|
|
4300
|
+
};
|
|
4301
|
+
const pad = (s, w) => s + ' '.repeat(Math.max(0, w - s.length));
|
|
4302
|
+
console.log(`${pad('id', widths.id)} ${pad('status', widths.status)} ${pad('name', widths.name)} ${pad('outcome', widths.outcome)}`);
|
|
4303
|
+
for (const r of rows) {
|
|
4304
|
+
console.log(`${pad(r.id, widths.id)} ${pad(r.status, widths.status)} ${pad(r.name, widths.name)} ${pad(r.outcome, widths.outcome)}`);
|
|
4305
|
+
}
|
|
4306
|
+
}
|
|
4307
|
+
function cmdGoalComplete(hippoRoot, args, flags) {
|
|
4308
|
+
const id = args[0];
|
|
4309
|
+
if (!id) {
|
|
4310
|
+
console.error('Usage: hippo goal complete <id> [--outcome <0..1>]');
|
|
4311
|
+
process.exit(1);
|
|
4312
|
+
}
|
|
4313
|
+
let outcomeScore;
|
|
4314
|
+
const outcomeRaw = flags['outcome'];
|
|
4315
|
+
if (outcomeRaw === true) {
|
|
4316
|
+
console.error('--outcome requires a value (e.g., --outcome 0.9)');
|
|
4317
|
+
process.exit(1);
|
|
4318
|
+
}
|
|
4319
|
+
if (outcomeRaw !== undefined) {
|
|
4320
|
+
const parsed = Number(outcomeRaw);
|
|
4321
|
+
if (!Number.isFinite(parsed) || parsed < 0 || parsed > 1) {
|
|
4322
|
+
console.error('--outcome must be a number in [0, 1]');
|
|
4323
|
+
process.exit(1);
|
|
4324
|
+
}
|
|
4325
|
+
outcomeScore = parsed;
|
|
4326
|
+
}
|
|
4327
|
+
completeGoal(hippoRoot, id, { outcomeScore });
|
|
4328
|
+
console.log('ok');
|
|
4329
|
+
}
|
|
4330
|
+
function cmdGoalSuspend(hippoRoot, args) {
|
|
4331
|
+
const id = args[0];
|
|
4332
|
+
if (!id) {
|
|
4333
|
+
console.error('Usage: hippo goal suspend <id>');
|
|
4334
|
+
process.exit(1);
|
|
4335
|
+
}
|
|
4336
|
+
suspendGoal(hippoRoot, id);
|
|
4337
|
+
console.log('ok');
|
|
4338
|
+
}
|
|
4339
|
+
function cmdGoalResume(hippoRoot, args) {
|
|
4340
|
+
const id = args[0];
|
|
4341
|
+
if (!id) {
|
|
4342
|
+
console.error('Usage: hippo goal resume <id>');
|
|
4343
|
+
process.exit(1);
|
|
4344
|
+
}
|
|
4345
|
+
resumeGoal(hippoRoot, id);
|
|
4346
|
+
console.log('ok');
|
|
4347
|
+
}
|
|
4348
|
+
function cmdGoal(hippoRoot, args, flags) {
|
|
4349
|
+
const sub = args[0];
|
|
4350
|
+
if (!sub) {
|
|
4351
|
+
console.error('Usage: hippo goal <push|list|complete|suspend|resume> [args]');
|
|
4352
|
+
process.exit(1);
|
|
4353
|
+
}
|
|
4354
|
+
const subArgs = args.slice(1);
|
|
4355
|
+
switch (sub) {
|
|
4356
|
+
case 'push':
|
|
4357
|
+
cmdGoalPush(hippoRoot, subArgs, flags);
|
|
4358
|
+
return;
|
|
4359
|
+
case 'list':
|
|
4360
|
+
cmdGoalList(hippoRoot, flags);
|
|
4361
|
+
return;
|
|
4362
|
+
case 'complete':
|
|
4363
|
+
cmdGoalComplete(hippoRoot, subArgs, flags);
|
|
4364
|
+
return;
|
|
4365
|
+
case 'suspend':
|
|
4366
|
+
cmdGoalSuspend(hippoRoot, subArgs);
|
|
4367
|
+
return;
|
|
4368
|
+
case 'resume':
|
|
4369
|
+
cmdGoalResume(hippoRoot, subArgs);
|
|
4370
|
+
return;
|
|
4371
|
+
default:
|
|
4372
|
+
console.error(`Unknown goal subcommand: ${sub}. Expected: push | list | complete | suspend | resume.`);
|
|
4373
|
+
process.exit(1);
|
|
4374
|
+
}
|
|
4375
|
+
}
|
|
4033
4376
|
function cmdAuth(hippoRoot, args, flags) {
|
|
4034
4377
|
const sub = args[0];
|
|
4035
4378
|
if (!sub) {
|
|
@@ -4185,6 +4528,12 @@ Commands:
|
|
|
4185
4528
|
--goal <tag> dlPFC goal-conditioned recall: memories tagged with
|
|
4186
4529
|
the goal tag get a 1.5x score boost and results are
|
|
4187
4530
|
re-sorted. Default off. RESEARCH.md §PFC.dlPFC.
|
|
4531
|
+
--session-id <id> Session identifier for dlPFC goal-stack boost.
|
|
4532
|
+
Defaults to \$HIPPO_SESSION_ID. When set and the
|
|
4533
|
+
(tenant, session) has active goals (see
|
|
4534
|
+
'hippo goal push'), recall auto-boosts memories
|
|
4535
|
+
whose tags match an active goal name. Boost stacks
|
|
4536
|
+
on top of base BM25 score, capped at 3.0x.
|
|
4188
4537
|
--salience-threshold <n>
|
|
4189
4538
|
Pineal salience: down-weight memories whose
|
|
4190
4539
|
retrieval_count is below n. score *= max(0.5,
|
|
@@ -4370,6 +4719,21 @@ Commands:
|
|
|
4370
4719
|
dashboard Open web dashboard for memory health
|
|
4371
4720
|
--port <n> Port to serve on (default: 3333)
|
|
4372
4721
|
mcp Start MCP server (stdio transport)
|
|
4722
|
+
goal <sub> dlPFC goal stack (B3) — scoped per session
|
|
4723
|
+
goal push <name> Push a new active goal; prints the new goal id
|
|
4724
|
+
--policy <type> schema-fit-biased | error-prioritized |
|
|
4725
|
+
recency-first | hybrid
|
|
4726
|
+
--success "<cond>" Optional success condition text
|
|
4727
|
+
--level <n> Goal level (default: 0)
|
|
4728
|
+
--parent <goalId> Parent goal id (for sub-goals)
|
|
4729
|
+
--session-id <s> Override session (defaults to HIPPO_SESSION_ID)
|
|
4730
|
+
--tenant-id <t> Override tenant (defaults to HIPPO_TENANT)
|
|
4731
|
+
goal list Show active goals as a table
|
|
4732
|
+
--all Include suspended/completed goals
|
|
4733
|
+
goal complete <id> Mark a goal completed
|
|
4734
|
+
--outcome <0..1> Outcome score; >=0.7 boosts, <0.3 decays recalled mems
|
|
4735
|
+
goal suspend <id> Move an active goal to suspended
|
|
4736
|
+
goal resume <id> Move a suspended goal back to active (depth-capped)
|
|
4373
4737
|
auth <sub> Manage API keys (A5 stub auth)
|
|
4374
4738
|
auth create Mint a new API key (plaintext shown ONCE)
|
|
4375
4739
|
--label <s> Optional human label
|
|
@@ -4567,6 +4931,9 @@ async function main() {
|
|
|
4567
4931
|
case 'auth':
|
|
4568
4932
|
cmdAuth(hippoRoot, args, flags);
|
|
4569
4933
|
break;
|
|
4934
|
+
case 'goal':
|
|
4935
|
+
cmdGoal(hippoRoot, args, flags);
|
|
4936
|
+
break;
|
|
4570
4937
|
case 'slack':
|
|
4571
4938
|
cmdSlack(hippoRoot, args, flags);
|
|
4572
4939
|
break;
|