clementine-agent 1.18.53 → 1.18.55

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.
@@ -179,6 +179,20 @@ export declare class PersonalAssistant {
179
179
  /** Fire-and-forget: extract a reusable skill from a successful execution. */
180
180
  private extractSkillFromExecution;
181
181
  private logMemoryExtractionSkip;
182
+ /**
183
+ * Public accessor for the SDK session ID associated with a sessionKey.
184
+ * Used by the canonical chat path so consecutive turns resume the
185
+ * same SDK conversation (the SDK persists sessions to JSONL on disk;
186
+ * resuming gives the agent native conversation history). Returns ''
187
+ * when no session exists yet.
188
+ */
189
+ getSdkSessionId(sessionKey: string): string;
190
+ /**
191
+ * Persist the SDK session ID for a sessionKey. Called after a runAgent
192
+ * call returns so the next call can resume the same conversation.
193
+ * Writes through to disk via the existing saveSessions plumbing.
194
+ */
195
+ setSdkSessionId(sessionKey: string, sdkSessionId: string): void;
182
196
  /**
183
197
  * Public entry point for triggering auto-memory extraction after an
184
198
  * exchange. Used by the new runAgent chat path (Phase 2 migration)
@@ -2681,6 +2681,28 @@ You have a cost budget per message — not a hard turn limit. Work until the tas
2681
2681
  }
2682
2682
  catch { /* telemetry only */ }
2683
2683
  }
2684
+ /**
2685
+ * Public accessor for the SDK session ID associated with a sessionKey.
2686
+ * Used by the canonical chat path so consecutive turns resume the
2687
+ * same SDK conversation (the SDK persists sessions to JSONL on disk;
2688
+ * resuming gives the agent native conversation history). Returns ''
2689
+ * when no session exists yet.
2690
+ */
2691
+ getSdkSessionId(sessionKey) {
2692
+ return this.sessions.get(sessionKey) ?? '';
2693
+ }
2694
+ /**
2695
+ * Persist the SDK session ID for a sessionKey. Called after a runAgent
2696
+ * call returns so the next call can resume the same conversation.
2697
+ * Writes through to disk via the existing saveSessions plumbing.
2698
+ */
2699
+ setSdkSessionId(sessionKey, sdkSessionId) {
2700
+ if (!sdkSessionId)
2701
+ return;
2702
+ this.sessions.set(sessionKey, sdkSessionId);
2703
+ this.sessionTimestamps.set(sessionKey, new Date());
2704
+ this.saveSessions();
2705
+ }
2684
2706
  /**
2685
2707
  * Public entry point for triggering auto-memory extraction after an
2686
2708
  * exchange. Used by the new runAgent chat path (Phase 2 migration)
@@ -1780,45 +1780,72 @@ export class Gateway {
1780
1780
  const { runAgent } = await import('../agent/run-agent.js');
1781
1781
  const { buildExtraMcpForRunAgent } = await import('../agent/run-agent-mcp.js');
1782
1782
  const { buildChatSystemAppend } = await import('../agent/run-agent-context.js');
1783
- // Wire Composio + external MCP servers (Outlook, Gmail,
1784
- // Salesforce, etc) so chat can reach the same tools the
1785
- // legacy chat path did. Profile allowlists override the
1786
- // bundle router when set.
1787
- //
1788
- // Use originalText (not chatPrompt) for scope routing
1789
- // chatPrompt may have the partial-interrupt banner folded in,
1790
- // which would skew bundle matching.
1791
- const chatMcp = await buildExtraMcpForRunAgent({
1792
- scopeText: originalText,
1793
- profile: resolvedProfile,
1794
- });
1795
- // Inject vault context (SOUL.md / MEMORY.md / AGENTS.md +
1796
- // optional profile body) into the system-prompt append so
1797
- // the agent has personality + long-term memory + team
1798
- // awareness. Profile-specific MEMORY.md takes precedence
1799
- // over the global one when a hired agent is active.
1800
- const chatSystemAppend = buildChatSystemAppend({
1801
- profile: resolvedProfile,
1802
- profileAppend: resolvedProfile?.systemPromptBody,
1803
- });
1783
+ // Builder sessions (dashboard trick/skill/cron/agent builder)
1784
+ // are conversational JSON-drafting flows, not real chat. They
1785
+ // don't need vault context, MCP tools, recall, or auto-memory
1786
+ // extraction the builder prefix IS the system prompt and
1787
+ // the agent only emits json-artifact blocks. Strip everything
1788
+ // expensive; keep just SDK session resume so multi-turn
1789
+ // artifact iteration sees its own prior turns.
1790
+ const isBuilderSession = sessionKey.startsWith('dashboard:builder:');
1791
+ // Wire Composio + external MCP only for real chat. Builder
1792
+ // skips entirely — builder turns never call tools.
1793
+ const chatMcp = isBuilderSession
1794
+ ? null
1795
+ : await buildExtraMcpForRunAgent({
1796
+ scopeText: originalText,
1797
+ profile: resolvedProfile,
1798
+ });
1799
+ // Vault context (SOUL.md / MEMORY.md / AGENTS.md + optional
1800
+ // profile body) — real chat only. Builder gets just its own
1801
+ // prefix as the system prompt.
1802
+ const chatSystemAppend = isBuilderSession
1803
+ ? ''
1804
+ : buildChatSystemAppend({
1805
+ profile: resolvedProfile,
1806
+ profileAppend: resolvedProfile?.systemPromptBody,
1807
+ });
1808
+ // Per-turn context (recall + persistent learnings + silent
1809
+ // blocks + security/toolset directives) — real chat only.
1810
+ // Builder doesn't need recall of unrelated transcripts.
1811
+ const turnContextPrefix = !isBuilderSession && securityAnnotation.trim()
1812
+ ? `[Context — read this for continuity, then respond to the user message below]\n${securityAnnotation}\n[/Context]\n\n`
1813
+ : '';
1814
+ const finalPrompt = turnContextPrefix + chatPrompt;
1815
+ // Resume the prior SDK session when one exists for this
1816
+ // sessionKey. The SDK persists session JSONLs to disk, so
1817
+ // resume works across daemon restarts AND for builder
1818
+ // multi-turn artifact iteration.
1819
+ const priorSdkSessionId = this.assistant.getSdkSessionId(effectiveSessionKey);
1820
+ // Builder cost knobs: Haiku is plenty for JSON drafting,
1821
+ // tight budget, no tools surfaced in the system prompt.
1822
+ const builderModel = isBuilderSession ? MODELS.haiku : effectiveModel;
1823
+ const builderBudget = isBuilderSession ? 0.10 : undefined;
1824
+ const builderAllowedTools = isBuilderSession ? [] : undefined;
1804
1825
  logger.info({
1805
1826
  sessionKey: effectiveSessionKey,
1806
1827
  profile: resolvedProfile?.slug,
1807
- path: 'runagent_chat',
1808
- composioConnected: chatMcp.composioConnected.length,
1809
- externalConnected: chatMcp.externalConnected.length,
1828
+ path: isBuilderSession ? 'runagent_builder' : 'runagent_chat',
1829
+ composioConnected: chatMcp?.composioConnected.length ?? 0,
1830
+ externalConnected: chatMcp?.externalConnected.length ?? 0,
1810
1831
  systemAppendChars: chatSystemAppend.length,
1832
+ turnContextChars: turnContextPrefix.length,
1833
+ resumingSdkSessionId: priorSdkSessionId || null,
1834
+ isBuilderSession,
1811
1835
  }, 'Routing chat through runAgent');
1812
- const runAgentResult = await runAgent(chatPrompt, {
1836
+ const runAgentResult = await runAgent(finalPrompt, {
1813
1837
  sessionKey: effectiveSessionKey,
1814
1838
  source: 'chat',
1815
1839
  profile: resolvedProfile,
1816
1840
  agentManager: this.getAgentManager(),
1817
1841
  memoryStore: this.assistant.getMemoryStore?.() ?? null,
1818
- ...(effectiveModel ? { model: effectiveModel } : {}),
1842
+ ...(builderModel ? { model: builderModel } : {}),
1819
1843
  ...(maxTurns ? { maxTurns } : {}),
1844
+ ...(builderBudget !== undefined ? { maxBudgetUsd: builderBudget } : {}),
1845
+ ...(builderAllowedTools ? { allowedTools: builderAllowedTools } : {}),
1820
1846
  ...(chatSystemAppend ? { systemPromptAppend: chatSystemAppend } : {}),
1821
- extraMcpServers: chatMcp.servers,
1847
+ ...(priorSdkSessionId ? { resumeSessionId: priorSdkSessionId } : {}),
1848
+ ...(chatMcp ? { extraMcpServers: chatMcp.servers } : {}),
1822
1849
  onText: wrappedOnText,
1823
1850
  onToolActivity: ({ tool, input }) => {
1824
1851
  toolActivityCount++;
@@ -1829,11 +1856,18 @@ export class Gateway {
1829
1856
  },
1830
1857
  abortSignal: chatAc.signal,
1831
1858
  });
1859
+ // Persist the SDK session ID so the next turn resumes the
1860
+ // same conversation. Survives daemon restarts via SESSIONS_FILE.
1861
+ if (runAgentResult.sessionId) {
1862
+ this.assistant.setSdkSessionId(effectiveSessionKey, runAgentResult.sessionId);
1863
+ }
1832
1864
  clearTimeout(chatTimer);
1833
1865
  clearTimeout(hardWallTimer);
1834
- // Mirror transcript so memory + recall continue working.
1866
+ // Mirror transcript so memory + recall continue working — but
1867
+ // skip for builder sessions since their turns are spec-drafting,
1868
+ // not real conversation worth recalling later.
1835
1869
  const memoryStore = this.assistant.getMemoryStore?.();
1836
- if (memoryStore) {
1870
+ if (memoryStore && !isBuilderSession) {
1837
1871
  try {
1838
1872
  memoryStore.saveTurn(effectiveSessionKey, 'user', originalText);
1839
1873
  memoryStore.saveTurn(effectiveSessionKey, 'assistant', runAgentResult.text);
@@ -1842,10 +1876,13 @@ export class Gateway {
1842
1876
  logger.debug({ err }, 'chat: transcript mirror failed (non-fatal)');
1843
1877
  }
1844
1878
  }
1845
- // Fire auto-memory extraction in the background.
1846
- this.assistant
1847
- .triggerMemoryExtractionPostExchange(originalText, runAgentResult.text, effectiveSessionKey, resolvedProfile)
1848
- .catch(err => logger.debug({ err, sessionKey: effectiveSessionKey }, 'chat: auto-memory failed (non-fatal)'));
1879
+ // Fire auto-memory extraction in the background — builder
1880
+ // turns are JSON-drafting noise, not memorable exchanges.
1881
+ if (!isBuilderSession) {
1882
+ this.assistant
1883
+ .triggerMemoryExtractionPostExchange(originalText, runAgentResult.text, effectiveSessionKey, resolvedProfile)
1884
+ .catch(err => logger.debug({ err, sessionKey: effectiveSessionKey }, 'chat: auto-memory failed (non-fatal)'));
1885
+ }
1849
1886
  // Auth recovered if we got a clean response.
1850
1887
  this.clearAuthFailure();
1851
1888
  logger.info({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clementine-agent",
3
- "version": "1.18.53",
3
+ "version": "1.18.55",
4
4
  "description": "Clementine — Personal AI Assistant (TypeScript)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",