clementine-agent 1.1.31 → 1.2.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.
@@ -24,6 +24,7 @@ import { StallGuard } from './stall-guard.js';
24
24
  import { collectToolCalls, detectContradiction, buildCorrectionPrompt } from './contradiction-validator.js';
25
25
  import { recordToolOutcome as recordMcpToolOutcome } from './mcp-circuit-breaker.js';
26
26
  import { assembleContext } from '../memory/context-assembler.js';
27
+ import * as embeddingsModule from '../memory/embeddings.js';
27
28
  import { PromptCache } from './prompt-cache.js';
28
29
  import { searchSkills as searchSkillsSync } from './skill-extractor.js';
29
30
  import { classifyIntent, getStrategyGuidance } from './intent-classifier.js';
@@ -378,13 +379,23 @@ const AUTO_MEMORY_PROMPT = `You are a memory extraction agent. Your ONLY job is
378
379
 
379
380
  {current_memory}
380
381
 
381
- ## What to extract:
382
- - **Facts about ${OWNER}** — preferences, opinions, decisions, personal details → update_memory in "About ${OWNER}" section
382
+ ## Where to save what (memory routing):
383
+
384
+ **Always-in-context core memory** (use the user_model tool — these stay top-of-mind in every future session):
385
+ - **Lasting facts about ${OWNER}** (role, location, identifiers, durable preferences, communication style) → user_model(action="append", slot="user_facts", content=...)
386
+ - **Active goals/intents** (what ${OWNER} is trying to accomplish right now) → user_model(action="append", slot="goals", content=...)
387
+ - **Key people/projects** (recurring relationships) → user_model(action="append", slot="relationships", content=...)
388
+ - Use action="replace" instead of "append" if you're updating an existing fact rather than adding a new one. Slots are capped at 2000 chars — older content rolls off on append.
389
+
390
+ **Vault notes** (use memory_write/note_create — durable but retrieved on demand):
383
391
  - **People mentioned** — names, relationships, context → create or update person notes in 02-People/
384
392
  - **Projects/work** — project names, status updates, decisions → update relevant project notes
385
- - **Tasks** — anything ${OWNER} asked to be done later → task_add
386
- - **Preferences** — tools, workflows, foods, styles, etc.update_memory in "Preferences" section
387
- - **Dates/events** — meetings, deadlines, appointments → note in daily log or task with due date
393
+ - **Dates/events** — meetings, deadlines, appointments note in daily log
394
+ - **Specific episodes** — "on Tuesday we discussed X"memory_write(action="append_daily")
395
+
396
+ **Tasks** — anything ${OWNER} asked to be done later → task_add
397
+
398
+ Routing rule: if the fact is something the agent should *always know* (not just "find when relevant"), it belongs in user_model. Episodic events and topical knowledge belong in the vault.
388
399
 
389
400
  ## What to skip:
390
401
  - Greetings, small talk, "thanks", "ok"
@@ -401,7 +412,7 @@ const AUTO_MEMORY_PROMPT = `You are a memory extraction agent. Your ONLY job is
401
412
  - Only save genuinely NEW facts not already present in the Current Memory above.
402
413
  - If updating an existing topic, use memory_write(action="update_memory") to REPLACE the section, not append duplicates.
403
414
  - If there's nothing new to save, respond "No new facts." and exit — do NOT call any tools.
404
- - Use the MCP tools (memory_write, note_create, task_add, note_take).
415
+ - Use the MCP tools (user_model, memory_write, note_create, task_add, note_take).
405
416
  - NEVER respond to ${OWNER}. You are invisible. Just save facts and exit.
406
417
 
407
418
  ## Behavioral Correction Detection:
@@ -1963,7 +1974,27 @@ You have a cost budget per message — not a hard turn limit. Work until the tas
1963
1974
  if (enrichedQuery.length > 1000) {
1964
1975
  enrichedQuery = enrichedQuery.slice(0, 1000);
1965
1976
  }
1966
- const results = this.memoryStore.searchContext(enrichedQuery, { limit: SEARCH_CONTEXT_LIMIT, recencyLimit: SEARCH_RECENCY_LIMIT, agentSlug, strict: strictIsolation });
1977
+ // Pre-compute dense query embedding if the model is ready. Done outside
1978
+ // searchContext (which is sync) so the dense path doesn't force the
1979
+ // entire call chain to be async. If embedDense fails or isn't available,
1980
+ // searchContext falls back to TF-IDF.
1981
+ let queryDenseVec;
1982
+ try {
1983
+ if (embeddingsModule.isDenseReady()) {
1984
+ const v = await embeddingsModule.embedDense(enrichedQuery, true);
1985
+ if (v)
1986
+ queryDenseVec = v;
1987
+ }
1988
+ }
1989
+ catch { /* fallback to sparse */ }
1990
+ const results = this.memoryStore.searchContext(enrichedQuery, {
1991
+ limit: SEARCH_CONTEXT_LIMIT,
1992
+ recencyLimit: SEARCH_RECENCY_LIMIT,
1993
+ agentSlug,
1994
+ strict: strictIsolation,
1995
+ sessionKey: sessionKey ?? undefined,
1996
+ queryDenseVec,
1997
+ });
1967
1998
  if (results?.length > 0) {
1968
1999
  const accessedIds = results
1969
2000
  .map((r) => r.chunkId)
@@ -2052,6 +2083,19 @@ You have a cost budget per message — not a hard turn limit. Work until the tas
2052
2083
  return undefined;
2053
2084
  })(),
2054
2085
  ]);
2086
+ // Render user model block (MemGPT-style core memory). Per-agent if
2087
+ // running as a hired agent, otherwise the global block. Empty string
2088
+ // when no slots are populated yet — context-assembler will skip the
2089
+ // slot rather than emit an empty heading.
2090
+ let userModelBlock = null;
2091
+ try {
2092
+ userModelBlock = this.memoryStore.renderUserModel?.(agentSlug ?? null) ?? null;
2093
+ if (!userModelBlock)
2094
+ userModelBlock = null;
2095
+ }
2096
+ catch {
2097
+ userModelBlock = null;
2098
+ }
2055
2099
  // Assemble context within a priority-based budget
2056
2100
  const assembled = await assembleContext({
2057
2101
  totalBudget: SYSTEM_PROMPT_MAX_CONTEXT_CHARS,
@@ -2060,6 +2104,7 @@ You have a cost budget per message — not a hard turn limit. Work until the tas
2060
2104
  memoryResults: results,
2061
2105
  skillContext,
2062
2106
  graphContext,
2107
+ userModelBlock,
2063
2108
  isAutonomous: isAutonomous ?? false,
2064
2109
  });
2065
2110
  return assembled.text;