feat: add session context system reminder on first message (#229)

Co-authored-by: Letta <noreply@letta.com>
This commit is contained in:
Charles Packer
2025-12-15 19:44:34 -08:00
committed by GitHub
parent f354e6aa41
commit 4cc4928bb0
3 changed files with 280 additions and 2 deletions

View File

@@ -0,0 +1,243 @@
// src/cli/helpers/sessionContext.ts
// Generates session context system reminder for the first message of each CLI session
import { execSync } from "node:child_process";
import { platform } from "node:os";
import { LETTA_CLOUD_API_URL } from "../../auth/oauth";
import { settingsManager } from "../../settings-manager";
import { getVersion } from "../../version";
interface AgentInfo {
id: string;
name: string | null;
description?: string | null;
lastRunAt?: string | null;
}
interface SessionContextOptions {
agentInfo: AgentInfo;
serverUrl?: string;
}
/**
* Get the current local time in a human-readable format
*/
function getLocalTime(): string {
const now = new Date();
return now.toLocaleString(undefined, {
weekday: "long",
year: "numeric",
month: "long",
day: "numeric",
hour: "2-digit",
minute: "2-digit",
timeZoneName: "short",
});
}
/**
* Get device type based on platform
*/
function getDeviceType(): string {
const p = platform();
switch (p) {
case "darwin":
return "macOS";
case "win32":
return "Windows";
case "linux":
return "Linux";
default:
return p;
}
}
/**
* Format relative time from a date string
*/
function getRelativeTime(dateStr: string): string {
const date = new Date(dateStr);
const now = new Date();
const diffMs = now.getTime() - date.getTime();
const diffSecs = Math.floor(diffMs / 1000);
const diffMins = Math.floor(diffSecs / 60);
const diffHours = Math.floor(diffMins / 60);
const diffDays = Math.floor(diffHours / 24);
if (diffDays > 0) {
return `${diffDays} day${diffDays === 1 ? "" : "s"} ago`;
}
if (diffHours > 0) {
return `${diffHours} hour${diffHours === 1 ? "" : "s"} ago`;
}
if (diffMins > 0) {
return `${diffMins} minute${diffMins === 1 ? "" : "s"} ago`;
}
return "just now";
}
/**
* Safely execute a git command, returning null on failure
*/
function safeGitExec(command: string, cwd: string): string | null {
try {
return execSync(command, { cwd, encoding: "utf-8", stdio: "pipe" }).trim();
} catch {
return null;
}
}
/**
* Gather git information if in a git repository
* Returns truncated commits (3) and status (20 lines)
* Each field is gathered independently with fallbacks
*/
function getGitInfo(): {
isGitRepo: boolean;
branch?: string;
recentCommits?: string;
status?: string;
} {
const cwd = process.cwd();
try {
// Check if we're in a git repo
execSync("git rev-parse --git-dir", { cwd, stdio: "pipe" });
// Get current branch (with fallback)
const branch = safeGitExec("git branch --show-current", cwd) ?? "(unknown)";
// Get recent commits (3 commits with author, with fallback)
const recentCommits =
safeGitExec('git log --format="%h %s (%an)" -3', cwd) ??
"(failed to get commits)";
// Get git status (truncate to 20 lines, with fallback)
const fullStatus =
safeGitExec("git status --short", cwd) ?? "(failed to get status)";
const statusLines = fullStatus.split("\n");
let status = fullStatus;
if (statusLines.length > 20) {
status =
statusLines.slice(0, 20).join("\n") +
`\n... and ${statusLines.length - 20} more files`;
}
return {
isGitRepo: true,
branch,
recentCommits,
status: status || "(clean working tree)",
};
} catch {
return { isGitRepo: false };
}
}
/**
* Build the full session context system reminder
* Returns empty string on any failure (graceful degradation)
*/
export function buildSessionContext(options: SessionContextOptions): string {
try {
const { agentInfo, serverUrl } = options;
const cwd = process.cwd();
// Gather info with safe fallbacks
let version = "unknown";
try {
version = getVersion();
} catch {
// version stays "unknown"
}
let deviceType = "unknown";
try {
deviceType = getDeviceType();
} catch {
// deviceType stays "unknown"
}
let localTime = "unknown";
try {
localTime = getLocalTime();
} catch {
// localTime stays "unknown"
}
const gitInfo = getGitInfo();
// Get server URL
let actualServerUrl = LETTA_CLOUD_API_URL;
try {
const settings = settingsManager.getSettings();
actualServerUrl =
serverUrl ||
process.env.LETTA_BASE_URL ||
settings.env?.LETTA_BASE_URL ||
LETTA_CLOUD_API_URL;
} catch {
// actualServerUrl stays default
}
// Format last run info
let lastRunInfo = "No previous messages";
if (agentInfo.lastRunAt) {
try {
const lastRunDate = new Date(agentInfo.lastRunAt);
const localLastRun = lastRunDate.toLocaleString();
const relativeTime = getRelativeTime(agentInfo.lastRunAt);
lastRunInfo = `${localLastRun} (${relativeTime})`;
} catch {
lastRunInfo = "(failed to parse last run time)";
}
}
// Build the context
let context = `<system-reminder>
This is an automated message providing context about the user's environment.
The user has just initiated a new connection via the [Letta Code CLI client](https://docs.letta.com/letta-code/index.md).
## Device Information
- **Local time**: ${localTime}
- **Device type**: ${deviceType}
- **Letta Code version**: ${version}
- **Current working directory**: ${cwd}
`;
// Add git info if available
if (gitInfo.isGitRepo) {
context += `- **Git repository**: Yes (branch: ${gitInfo.branch})
### Recent Commits
\`\`\`
${gitInfo.recentCommits}
\`\`\`
### Git Status
\`\`\`
${gitInfo.status}
\`\`\`
`;
} else {
context += `- **Git repository**: No
`;
}
// Add agent info
context += `
## Agent Information (i.e. information about you)
- **Agent ID**: ${agentInfo.id}
- **Agent name**: ${agentInfo.name || "(unnamed)"} (the user can change this with /rename)
- **Agent description**: ${agentInfo.description || "(no description)"} (the user can change this with /description)
- **Last message**: ${lastRunInfo}
- **Server location**: ${actualServerUrl}
</system-reminder>`;
return context;
} catch {
// If anything fails catastrophically, return empty string
// This ensures the user's message still gets sent
return "";
}
}