clementine-agent 1.9.0 → 1.9.1

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.
@@ -379,13 +379,20 @@ const AUTO_MEMORY_PROMPT = `You are a memory extraction agent. Your ONLY job is
379
379
 
380
380
  {current_memory}
381
381
 
382
+ ## Current User Model (already known — DO NOT re-extract these)
383
+
384
+ {current_user_model}
385
+
382
386
  ## Where to save what (memory routing):
383
387
 
384
388
  **Always-in-context core memory** (use the user_model tool — these stay top-of-mind in every future session):
385
389
  - **Lasting facts about ${OWNER}** (role, location, identifiers, durable preferences, communication style) → user_model(action="append", slot="user_facts", content=...)
386
390
  - **Active goals/intents** (what ${OWNER} is trying to accomplish right now) → user_model(action="append", slot="goals", content=...)
387
391
  - **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.
392
+ - **DEFAULT to action="append"** it adds the new fact alongside what's already there.
393
+ - Only use action="replace" when CORRECTING an existing fact, and you MUST include the FULL slot content (everything from "Current User Model" above, with the correction applied). \`replace\` overwrites the entire slot — passing only the new fact wipes everything else.
394
+ - Never use action="clear" from this extractor. Clearing is a deliberate user action, not a memory-extraction outcome.
395
+ - Slots are capped at 2000 chars — older content rolls off on append automatically.
389
396
 
390
397
  **Vault notes** (use memory_write/note_create — durable but retrieved on demand):
391
398
  - **People mentioned** — names, relationships, context → create or update person notes in 02-People/
@@ -1638,6 +1645,7 @@ You have a cost budget per message — not a hard turn limit. Work until the tas
1638
1645
  'Read', 'Write', 'Edit', 'Bash', 'Glob', 'Grep',
1639
1646
  'WebSearch', 'WebFetch',
1640
1647
  mcpTool('working_memory'),
1648
+ mcpTool('user_model'),
1641
1649
  mcpTool('memory_read'),
1642
1650
  mcpTool('memory_write'),
1643
1651
  mcpTool('memory_search'),
@@ -3683,10 +3691,23 @@ You have a cost budget per message — not a hard turn limit. Work until the tas
3683
3691
  // Non-fatal — proceed without corrections
3684
3692
  }
3685
3693
  }
3694
+ // Render current user_model state so the extractor can: (a) skip
3695
+ // re-extracting facts already there, (b) safely use action="replace"
3696
+ // by passing the full slot content with a correction applied. Scoped
3697
+ // to the active agent — Clementine sees global slots, hired agents
3698
+ // see their own per-agent slots.
3699
+ let currentUserModel = '(empty — no slots populated yet)';
3700
+ try {
3701
+ const rendered = this.memoryStore?.renderUserModel?.(profile?.slug ?? null);
3702
+ if (rendered && rendered.trim())
3703
+ currentUserModel = rendered;
3704
+ }
3705
+ catch { /* non-fatal */ }
3686
3706
  const memPrompt = AUTO_MEMORY_PROMPT
3687
3707
  .replace('{user_message}', userMessage)
3688
3708
  .replace('{assistant_response}', truncatedResponse)
3689
3709
  .replace('{current_memory}', currentMemory || '(empty — no existing memory yet)')
3710
+ .replace('{current_user_model}', currentUserModel)
3690
3711
  .replace('{recent_corrections}', correctionsText);
3691
3712
  const userMessageSnippet = userMessage.slice(0, 500);
3692
3713
  const stream = query({
@@ -3706,6 +3727,13 @@ You have a cost budget per message — not a hard turn limit. Work until the tas
3706
3727
  mcpTool('task_add'),
3707
3728
  mcpTool('note_take'),
3708
3729
  mcpTool('memory_read'),
3730
+ // Auto-extractor needs user_model to populate the always-in-context
3731
+ // core slots (user_facts, goals, relationships, agent_persona).
3732
+ // The MCP server boots with CLEMENTINE_TEAM_AGENT=<slug>, so writes
3733
+ // are scoped to the active agent automatically — Clementine's
3734
+ // sessions populate global slots, hired-agent sessions populate
3735
+ // that agent's per-agent slots.
3736
+ mcpTool('user_model'),
3709
3737
  ],
3710
3738
  mcpServers: {
3711
3739
  [TOOLS_SERVER]: {
@@ -12698,8 +12698,8 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
12698
12698
 
12699
12699
  <!-- User Model — MemGPT-style core memory blocks always loaded into context -->
12700
12700
  <div class="tab-pane" id="tab-intelligence-user-model">
12701
- <div style="color:var(--muted,#888);margin-bottom:12px;font-size:13px">
12702
- What the agent always knows about you. These slots load into every conversation's context (above retrieved memory). Edit directly to correct or steer.
12701
+ <div style="color:var(--muted,#888);margin-bottom:12px;font-size:13px;max-width:760px">
12702
+ <strong style="color:var(--text)">Always-in-context core memory.</strong> Four small slots (capped at 2000 chars each) that load into <em>every</em> conversation distinct from MEMORY.md and the chunk store. The agent appends here automatically as you talk; you can also edit directly to correct or steer. Use the Scope dropdown to view per-agent slots (each hired agent maintains their own).
12703
12703
  </div>
12704
12704
  <div style="display:flex;gap:8px;margin-bottom:12px;align-items:center;flex-wrap:wrap">
12705
12705
  <label style="font-size:13px;color:var(--text-secondary)">Scope:</label>
@@ -19160,7 +19160,20 @@ async function loadUserModel() {
19160
19160
  relationships: 'People, projects, channels they regularly interact with.',
19161
19161
  agent_persona: 'For multi-agent: this agent\\'s self-identity in its working relationship with the user.',
19162
19162
  };
19163
+ // First-run hint: when every slot is empty, show a single explainer
19164
+ // banner above the (still editable) textareas so the user understands
19165
+ // both what this is and how to populate it. Suppressed once anything
19166
+ // is in place — at that point the metadata on each card is enough.
19167
+ var allEmpty = d.blocks.every(function(b) { return !(b.content || '').trim(); });
19163
19168
  var html = '<div style="display:flex;flex-direction:column;gap:14px">';
19169
+ if (allEmpty) {
19170
+ html += '<div class="card" style="padding:14px;border-left:3px solid var(--accent,#f59e0b);background:var(--bg-input,#1a1a1a)">' +
19171
+ '<div style="font-weight:600;margin-bottom:6px">No core memory yet for this scope</div>' +
19172
+ '<div style="font-size:12px;color:var(--text-secondary);line-height:1.5">' +
19173
+ 'These slots auto-populate as you chat — the agent extracts durable facts about you (your role, active goals, recurring people/projects) and appends them after each exchange. ' +
19174
+ 'You can also seed from existing memory in one click, or type directly into any slot below and click Save.' +
19175
+ '</div></div>';
19176
+ }
19164
19177
  for (var i = 0; i < d.blocks.length; i++) {
19165
19178
  var b = d.blocks[i];
19166
19179
  var label = labelMap[b.slot] || b.slot;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clementine-agent",
3
- "version": "1.9.0",
3
+ "version": "1.9.1",
4
4
  "description": "Clementine — Personal AI Assistant (TypeScript)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",