diff --git a/src/cli/App.tsx b/src/cli/App.tsx
index 51295b3..dc0192a 100644
--- a/src/cli/App.tsx
+++ b/src/cli/App.tsx
@@ -193,6 +193,7 @@ import {
toLines,
} from "./helpers/accumulator";
import { classifyApprovals } from "./helpers/approvalClassification";
+import { buildChatUrl } from "./helpers/appUrls";
import { backfillBuffers } from "./helpers/backfill";
import { chunkLog } from "./helpers/chunkLog";
import {
@@ -6275,7 +6276,7 @@ export default function App({
});
// Build success message with hints
- const agentUrl = `https://app.letta.com/projects/default-project/agents/${agent.id}`;
+ const agentUrl = buildChatUrl(agent.id);
const memfsTip = settingsManager.isMemfsEnabled(agent.id)
? "Memory will be auto-initialized on your first message."
: "Tip: use /init to initialize your agent's memory system!";
@@ -6964,10 +6965,9 @@ export default function App({
// Special handling for /ade command - open agent in browser
if (trimmed === "/ade") {
- const adeUrl =
- conversationIdRef.current === "default"
- ? `https://app.letta.com/agents/${agentId}`
- : `https://app.letta.com/agents/${agentId}?conversation=${conversationIdRef.current}`;
+ const adeUrl = buildChatUrl(agentId, {
+ conversationId: conversationIdRef.current,
+ });
const cmd = commandRunner.start("/ade", "Opening ADE...");
diff --git a/src/cli/commands/install-github-app.ts b/src/cli/commands/install-github-app.ts
index 892d174..57746ba 100644
--- a/src/cli/commands/install-github-app.ts
+++ b/src/cli/commands/install-github-app.ts
@@ -9,6 +9,7 @@ import {
} from "node:fs";
import { tmpdir } from "node:os";
import { dirname, join } from "node:path";
+import { buildChatUrl } from "../helpers/appUrls";
const DEFAULT_WORKFLOW_PATH = ".github/workflows/letta.yml";
const ALTERNATE_WORKFLOW_PATH = ".github/workflows/letta-code.yml";
@@ -512,9 +513,7 @@ export async function installGithubApp(
committed: false,
secretAction: "set",
agentId: resolvedAgentId,
- agentUrl: resolvedAgentId
- ? `https://app.letta.com/agents/${resolvedAgentId}`
- : null,
+ agentUrl: resolvedAgentId ? buildChatUrl(resolvedAgentId) : null,
};
}
@@ -540,9 +539,7 @@ export async function installGithubApp(
committed: true,
secretAction: "set",
agentId: resolvedAgentId,
- agentUrl: resolvedAgentId
- ? `https://app.letta.com/agents/${resolvedAgentId}`
- : null,
+ agentUrl: resolvedAgentId ? buildChatUrl(resolvedAgentId) : null,
};
} finally {
rmSync(tempDir, { recursive: true, force: true });
diff --git a/src/cli/commands/listen.ts b/src/cli/commands/listen.ts
index 18be4fd..be24fb7 100644
--- a/src/cli/commands/listen.ts
+++ b/src/cli/commands/listen.ts
@@ -9,6 +9,7 @@ import { settingsManager } from "../../settings-manager";
import { getErrorMessage } from "../../utils/error";
import { registerWithCloud } from "../../websocket/listen-register";
import type { Buffers, Line } from "../helpers/accumulator";
+import { buildChatUrl } from "../helpers/appUrls";
// tiny helper for unique ids
function uid(prefix: string) {
@@ -179,11 +180,10 @@ export async function handleListen(
const buildConnectionUrl = (connId: string): string => {
if (!ctx.agentId) return "";
- let url = `https://app.letta.com/agents/${ctx.agentId}?deviceId=${connId}`;
- if (ctx.conversationId) {
- url += `&conversationId=${ctx.conversationId}`;
- }
- return url;
+ return buildChatUrl(ctx.agentId, {
+ deviceId: connId,
+ conversationId: ctx.conversationId ?? undefined,
+ });
};
// Start listen flow
diff --git a/src/cli/components/AgentInfoBar.tsx b/src/cli/components/AgentInfoBar.tsx
index 7044356..71d48a2 100644
--- a/src/cli/components/AgentInfoBar.tsx
+++ b/src/cli/components/AgentInfoBar.tsx
@@ -6,6 +6,7 @@ import type { ModelReasoningEffort } from "../../agent/model";
import { DEFAULT_AGENT_NAME } from "../../constants";
import { settingsManager } from "../../settings-manager";
import { getVersion } from "../../version";
+import { buildAppUrl, buildChatUrl } from "../helpers/appUrls";
import { useTerminalWidth } from "../hooks/useTerminalWidth";
import { colors } from "./colors";
import { Text } from "./Text";
@@ -71,7 +72,7 @@ export const AgentInfoBar = memo(function AgentInfoBar({
const isCloudUser = serverUrl?.includes("api.letta.com");
const adeConversationUrl =
agentId && agentId !== "loading"
- ? `https://app.letta.com/agents/${agentId}${conversationId && conversationId !== "default" ? `?conversation=${conversationId}` : ""}`
+ ? buildChatUrl(agentId, { conversationId })
: "";
const showBottomBar = agentId && agentId !== "loading";
const reasoningLabel = formatReasoningLabel(currentReasoningEffort);
@@ -130,7 +131,7 @@ export const AgentInfoBar = memo(function AgentInfoBar({
Open in ADE ↗
·
-
+
View usage ↗
@@ -139,7 +140,7 @@ export const AgentInfoBar = memo(function AgentInfoBar({
{truncateText(
- `Open in ADE: ${adeConversationUrl} · Usage: https://app.letta.com/settings/organization/usage`,
+ `Open in ADE: ${adeConversationUrl} · Usage: ${buildAppUrl("/settings/organization/usage")}`,
rightWidth,
)}
diff --git a/src/cli/components/MemfsTreeViewer.tsx b/src/cli/components/MemfsTreeViewer.tsx
index 4cba077..4167b17 100644
--- a/src/cli/components/MemfsTreeViewer.tsx
+++ b/src/cli/components/MemfsTreeViewer.tsx
@@ -11,6 +11,7 @@ import {
type TreeNode,
} from "../../agent/memoryScanner";
import { generateAndOpenMemoryViewer } from "../../web/generate-memory-viewer";
+import { buildChatUrl } from "../helpers/appUrls";
import { useTerminalWidth } from "../hooks/useTerminalWidth";
import { colors } from "./colors";
import { Text } from "./Text";
@@ -51,7 +52,7 @@ export function MemfsTreeViewer({
const terminalWidth = useTerminalWidth();
const solidLine = SOLID_LINE.repeat(Math.max(terminalWidth, 10));
const isTmux = Boolean(process.env.TMUX);
- const adeUrl = `https://app.letta.com/agents/${agentId}?view=memory${conversationId && conversationId !== "default" ? `&conversation=${conversationId}` : ""}`;
+ const adeUrl = buildChatUrl(agentId, { view: "memory", conversationId });
// State
const [selectedIndex, setSelectedIndex] = useState(0);
diff --git a/src/cli/components/MemoryTabViewer.tsx b/src/cli/components/MemoryTabViewer.tsx
index 259b68a..79f886b 100644
--- a/src/cli/components/MemoryTabViewer.tsx
+++ b/src/cli/components/MemoryTabViewer.tsx
@@ -4,6 +4,7 @@ import Link from "ink-link";
import { useEffect, useState } from "react";
import { getClient } from "../../agent/client";
import { debugLog } from "../../utils/debug";
+import { buildChatUrl } from "../helpers/appUrls";
import { useTerminalWidth } from "../hooks/useTerminalWidth";
import { colors } from "./colors";
import { MarkdownDisplay } from "./MarkdownDisplay";
@@ -40,7 +41,7 @@ export function MemoryTabViewer({
const terminalWidth = useTerminalWidth();
const solidLine = SOLID_LINE.repeat(Math.max(terminalWidth, 10));
const isTmux = Boolean(process.env.TMUX);
- const adeUrl = `https://app.letta.com/agents/${agentId}?view=memory${conversationId && conversationId !== "default" ? `&conversation=${conversationId}` : ""}`;
+ const adeUrl = buildChatUrl(agentId, { view: "memory", conversationId });
const [selectedTabIndex, setSelectedTabIndex] = useState(0);
const [scrollOffset, setScrollOffset] = useState(0);
diff --git a/src/cli/components/SessionStats.tsx b/src/cli/components/SessionStats.tsx
index 6c28324..9ed8f10 100644
--- a/src/cli/components/SessionStats.tsx
+++ b/src/cli/components/SessionStats.tsx
@@ -1,4 +1,5 @@
import type { SessionStatsSnapshot } from "../../agent/stats";
+import { buildAppUrl } from "../helpers/appUrls";
import { formatCompact } from "../helpers/format";
export function formatDuration(ms: number): string {
@@ -66,7 +67,7 @@ export function formatUsageStats({
outputLines.push(
`Plan: [${balance.billing_tier}]`,
- "https://app.letta.com/settings/organization/usage",
+ buildAppUrl("/settings/organization/usage"),
"",
`Available credits: ◎${formatNumber(totalCredits)} ($${toDollars(totalCredits)})`,
`Monthly credits: ◎${formatNumber(monthlyCredits)} ($${toDollars(monthlyCredits)})`,
diff --git a/src/cli/helpers/appUrls.ts b/src/cli/helpers/appUrls.ts
new file mode 100644
index 0000000..25a41bb
--- /dev/null
+++ b/src/cli/helpers/appUrls.ts
@@ -0,0 +1,36 @@
+const APP_BASE = "https://app.letta.com";
+
+/**
+ * Build a chat URL for an agent, with optional conversation and extra query params.
+ */
+export function buildChatUrl(
+ agentId: string,
+ options?: {
+ conversationId?: string;
+ view?: string;
+ deviceId?: string;
+ },
+): string {
+ const base = `${APP_BASE}/chat/${agentId}`;
+ const params = new URLSearchParams();
+
+ if (options?.view) {
+ params.set("view", options.view);
+ }
+ if (options?.deviceId) {
+ params.set("deviceId", options.deviceId);
+ }
+ if (options?.conversationId && options.conversationId !== "default") {
+ params.set("conversation", options.conversationId);
+ }
+
+ const qs = params.toString();
+ return qs ? `${base}?${qs}` : base;
+}
+
+/**
+ * Build a non-agent app URL (e.g. settings pages).
+ */
+export function buildAppUrl(path: string): string {
+ return `${APP_BASE}${path}`;
+}
diff --git a/src/cli/helpers/errorFormatter.ts b/src/cli/helpers/errorFormatter.ts
index a716bd4..6bd4e21 100644
--- a/src/cli/helpers/errorFormatter.ts
+++ b/src/cli/helpers/errorFormatter.ts
@@ -1,10 +1,10 @@
import { APIError } from "@letta-ai/letta-client/core/error";
+import { buildAppUrl, buildChatUrl } from "./appUrls";
import { getErrorContext } from "./errorContext";
import { checkZaiError } from "./zaiErrors";
-const LETTA_USAGE_URL = "https://app.letta.com/settings/organization/usage";
-const LETTA_AGENTS_URL =
- "https://app.letta.com/projects/default-project/agents";
+const LETTA_USAGE_URL = buildAppUrl("/settings/organization/usage");
+const LETTA_AGENTS_URL = buildAppUrl("/projects/default-project/agents");
function extractReasonList(value: unknown): string[] {
if (!Array.isArray(value)) return [];
@@ -756,6 +756,6 @@ function createAgentLink(
agentId: string,
conversationId?: string,
): string {
- const url = `https://app.letta.com/agents/${agentId}${conversationId && conversationId !== "default" ? `?conversation=${conversationId}` : ""}`;
+ const url = buildChatUrl(agentId, { conversationId });
return `View agent: \x1b]8;;${url}\x1b\\${agentId}\x1b]8;;\x1b\\ (run: ${runId})`;
}
diff --git a/src/tests/cli/install-github-app.test.ts b/src/tests/cli/install-github-app.test.ts
index 3dcc2a9..63443a5 100644
--- a/src/tests/cli/install-github-app.test.ts
+++ b/src/tests/cli/install-github-app.test.ts
@@ -271,12 +271,12 @@ describe("success screen content", () => {
secretAction: "set",
agentId: "agent-aaaabbbb-cccc-dddd-eeee-ffffffffffff",
agentUrl:
- "https://app.letta.com/agents/agent-aaaabbbb-cccc-dddd-eeee-ffffffffffff",
+ "https://app.letta.com/chat/agent-aaaabbbb-cccc-dddd-eeee-ffffffffffff",
};
- test("agentUrl points to app.letta.com ADE", () => {
+ test("agentUrl points to app.letta.com chat", () => {
expect(baseResult.agentUrl).toBe(
- `https://app.letta.com/agents/${baseResult.agentId}`,
+ `https://app.letta.com/chat/${baseResult.agentId}`,
);
});
@@ -361,15 +361,15 @@ describe("success screen content", () => {
expect(allText).not.toContain("Agent configured");
});
- test("agent URL uses correct ADE format for any agent ID", () => {
+ test("agent URL uses correct chat format for any agent ID", () => {
const agentId = "agent-12345678-abcd-efgh-ijkl-123456789012";
- const expectedUrl = `https://app.letta.com/agents/${agentId}`;
+ const expectedUrl = `https://app.letta.com/chat/${agentId}`;
// This mirrors the logic in installGithubApp
- const agentUrl = agentId ? `https://app.letta.com/agents/${agentId}` : null;
+ const agentUrl = agentId ? `https://app.letta.com/chat/${agentId}` : null;
expect(agentUrl).toBe(expectedUrl);
- expect(agentUrl).toContain("app.letta.com/agents/");
+ expect(agentUrl).toContain("app.letta.com/chat/");
expect(agentUrl).toContain(agentId);
});
});
diff --git a/src/tests/tools/task-background-helper.test.ts b/src/tests/tools/task-background-helper.test.ts
index ab013e5..325cbb4 100644
--- a/src/tests/tools/task-background-helper.test.ts
+++ b/src/tests/tools/task-background-helper.test.ts
@@ -232,7 +232,7 @@ describe("waitForBackgroundSubagentLink", () => {
setTimeout(() => {
updateSubagent("subagent-link-1", {
- agentURL: "https://app.letta.com/agents/agent-123",
+ agentURL: "https://app.letta.com/chat/agent-123",
});
}, 20);
diff --git a/src/web/memory-viewer-template.txt b/src/web/memory-viewer-template.txt
index f96a511..9c92976 100644
--- a/src/web/memory-viewer-template.txt
+++ b/src/web/memory-viewer-template.txt
@@ -952,7 +952,7 @@ html.dark .warning-badge { background: hsl(42, 30%, 18%); color: hsl(42, 80%, 70
} else {
adeBase = 'https://app.letta.com';
}
- agentIdEl.href = adeBase + '/agents/' + encodeURIComponent(agentId);
+ agentIdEl.href = adeBase + '/chat/' + encodeURIComponent(agentId);
agentIdEl.target = '_blank';
}
document.getElementById('generated-at').textContent = 'Generated ' + new Date(DATA.generatedAt).toLocaleString();