clementine-agent 1.18.53 → 1.18.54

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)
@@ -1801,6 +1801,23 @@ export class Gateway {
1801
1801
  profile: resolvedProfile,
1802
1802
  profileAppend: resolvedProfile?.systemPromptBody,
1803
1803
  });
1804
+ // Per-turn context — recall of recent transcripts, persistent
1805
+ // learnings, silent context blocks, security advisories, and
1806
+ // toolset directives accumulated above. This is what gives
1807
+ // continuity across daemon restarts (SDK in-memory session is
1808
+ // gone, but transcripts in SQLite persist; recall surfaces
1809
+ // them). Prefixed to the user message in a clearly-delimited
1810
+ // [Context] block so the model knows it's framing, not user
1811
+ // input.
1812
+ const turnContextPrefix = securityAnnotation.trim()
1813
+ ? `[Context — read this for continuity, then respond to the user message below]\n${securityAnnotation}\n[/Context]\n\n`
1814
+ : '';
1815
+ const finalPrompt = turnContextPrefix + chatPrompt;
1816
+ // Resume the prior SDK session when one exists for this
1817
+ // sessionKey. The SDK persists session JSONLs to disk, so
1818
+ // resume works across daemon restarts. Without this, every
1819
+ // turn is a fresh SDK session with zero conversation history.
1820
+ const priorSdkSessionId = this.assistant.getSdkSessionId(effectiveSessionKey);
1804
1821
  logger.info({
1805
1822
  sessionKey: effectiveSessionKey,
1806
1823
  profile: resolvedProfile?.slug,
@@ -1808,8 +1825,10 @@ export class Gateway {
1808
1825
  composioConnected: chatMcp.composioConnected.length,
1809
1826
  externalConnected: chatMcp.externalConnected.length,
1810
1827
  systemAppendChars: chatSystemAppend.length,
1828
+ turnContextChars: turnContextPrefix.length,
1829
+ resumingSdkSessionId: priorSdkSessionId || null,
1811
1830
  }, 'Routing chat through runAgent');
1812
- const runAgentResult = await runAgent(chatPrompt, {
1831
+ const runAgentResult = await runAgent(finalPrompt, {
1813
1832
  sessionKey: effectiveSessionKey,
1814
1833
  source: 'chat',
1815
1834
  profile: resolvedProfile,
@@ -1818,6 +1837,7 @@ export class Gateway {
1818
1837
  ...(effectiveModel ? { model: effectiveModel } : {}),
1819
1838
  ...(maxTurns ? { maxTurns } : {}),
1820
1839
  ...(chatSystemAppend ? { systemPromptAppend: chatSystemAppend } : {}),
1840
+ ...(priorSdkSessionId ? { resumeSessionId: priorSdkSessionId } : {}),
1821
1841
  extraMcpServers: chatMcp.servers,
1822
1842
  onText: wrappedOnText,
1823
1843
  onToolActivity: ({ tool, input }) => {
@@ -1829,6 +1849,11 @@ export class Gateway {
1829
1849
  },
1830
1850
  abortSignal: chatAc.signal,
1831
1851
  });
1852
+ // Persist the SDK session ID so the next turn resumes the
1853
+ // same conversation. Survives daemon restarts via SESSIONS_FILE.
1854
+ if (runAgentResult.sessionId) {
1855
+ this.assistant.setSdkSessionId(effectiveSessionKey, runAgentResult.sessionId);
1856
+ }
1832
1857
  clearTimeout(chatTimer);
1833
1858
  clearTimeout(hardWallTimer);
1834
1859
  // Mirror transcript so memory + recall continue working.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clementine-agent",
3
- "version": "1.18.53",
3
+ "version": "1.18.54",
4
4
  "description": "Clementine — Personal AI Assistant (TypeScript)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",