From f6977b043d08bc06a171bc686811baf74290eca5 Mon Sep 17 00:00:00 2001 From: cpacker Date: Sat, 31 Jan 2026 14:45:16 -0800 Subject: [PATCH] feat(headless): accept sender id via --from-agent --- src/headless.ts | 38 +++++++++++++++++++- src/index.ts | 2 ++ src/skills/builtin/messaging-agents/SKILL.md | 27 +++++++------- 3 files changed, 52 insertions(+), 15 deletions(-) diff --git a/src/headless.ts b/src/headless.ts index 196a9fd..07070de 100644 --- a/src/headless.ts +++ b/src/headless.ts @@ -41,6 +41,7 @@ import { formatErrorDetails } from "./cli/helpers/errorFormatter"; import { safeJsonParseOr } from "./cli/helpers/safeJsonParse"; import { drainStreamWithResume } from "./cli/helpers/stream"; import { StreamProcessor } from "./cli/helpers/streamProcessor"; +import { SYSTEM_REMINDER_CLOSE, SYSTEM_REMINDER_OPEN } from "./constants"; import { settingsManager } from "./settings-manager"; import { checkToolPermission } from "./tools/manager"; import type { @@ -105,6 +106,7 @@ export async function handleHeadlessCommand( "output-format": { type: "string" }, "input-format": { type: "string" }, "include-partial-messages": { type: "boolean" }, + "from-agent": { type: "string" }, // Additional flags from index.ts that need to be filtered out help: { type: "boolean", short: "h" }, version: { type: "boolean", short: "v" }, @@ -209,7 +211,8 @@ export async function handleHeadlessCommand( } // --new: Create a new conversation (for concurrent sessions) - const forceNewConversation = (values.new as boolean | undefined) ?? false; + let forceNewConversation = (values.new as boolean | undefined) ?? false; + const fromAgentId = values["from-agent"] as string | undefined; // Resolve agent (same logic as interactive mode) let agent: AgentState | null = null; @@ -262,6 +265,26 @@ export async function handleHeadlessCommand( process.exit(1); } + if (fromAgentId) { + if (!specifiedAgentId && !specifiedConversationId) { + console.error( + "Error: --from-agent requires --agent or --conversation .", + ); + process.exit(1); + } + if (shouldContinue) { + console.error("Error: --from-agent cannot be used with --continue"); + process.exit(1); + } + if (forceNew) { + console.error("Error: --from-agent cannot be used with --new-agent"); + process.exit(1); + } + if (!specifiedConversationId && !forceNewConversation) { + forceNewConversation = true; + } + } + // Validate --conversation flag (mutually exclusive with agent-selection flags) // Exception: --conv default requires --agent if (specifiedConversationId && specifiedConversationId !== "default") { @@ -988,6 +1011,19 @@ export async function handleHeadlessCommand( const { hasLoadedSkills } = await import("./agent/context"); let messageContent = ""; + if (fromAgentId) { + const senderAgentId = fromAgentId; + const senderAgent = await client.agents.retrieve(senderAgentId); + const systemReminder = `${SYSTEM_REMINDER_OPEN} +This message is from "${senderAgent.name}" (agent ID: ${senderAgentId}), an agent currently running inside the Letta Code CLI (docs.letta.com/letta-code). +The sender will only see the final message you generate (not tool calls or reasoning). +If you need to share detailed information, include it in your response text. +${SYSTEM_REMINDER_CLOSE} + +`; + messageContent += systemReminder; + } + // Add plan mode reminder if in plan mode (highest priority) if (permissionMode.getMode() === "plan") { const { PLAN_MODE_REMINDER } = await import("./agent/promptAssets"); diff --git a/src/index.ts b/src/index.ts index 5935463..05317d2 100755 --- a/src/index.ts +++ b/src/index.ts @@ -76,6 +76,7 @@ OPTIONS When set, reads JSON messages from stdin for bidirectional communication --include-partial-messages Emit stream_event wrappers for each chunk (stream-json only) + --from-agent Inject agent-to-agent system reminder (headless mode) --skills Custom path to skills directory (default: .skills in current directory) --sleeptime Enable sleeptime memory management (only for new agents) --from-af Create agent from an AgentFile (.af) template @@ -435,6 +436,7 @@ async function main(): Promise { "output-format": { type: "string" }, "input-format": { type: "string" }, "include-partial-messages": { type: "boolean" }, + "from-agent": { type: "string" }, skills: { type: "string" }, sleeptime: { type: "boolean" }, "from-af": { type: "string" }, diff --git a/src/skills/builtin/messaging-agents/SKILL.md b/src/skills/builtin/messaging-agents/SKILL.md index a576ce6..9b15444 100644 --- a/src/skills/builtin/messaging-agents/SKILL.md +++ b/src/skills/builtin/messaging-agents/SKILL.md @@ -58,27 +58,26 @@ letta messages search --query "topic" --all-agents ``` Results include `agent_id` for each matching message. -## CLI Usage +## CLI Usage (agent-to-agent) ### Starting a New Conversation ```bash -letta messages start-conversation --agent --message "" +letta -p --from-agent $LETTA_AGENT_ID --agent "message text" ``` **Arguments:** | Arg | Required | Description | |-----|----------|-------------| | `--agent ` | Yes | Target agent ID to message | -| `--agent-id ` | No | Alias for `--agent` | -| `--message ` | Yes | Message to send | -| `--timeout ` | No | Max wait time in ms (default: 120000) | +| `--from-agent ` | Yes | Sender agent ID (injects agent-to-agent system reminder) | +| `"message text"` | Yes | Message body (positional after flags) | **Example:** ```bash -letta messages start-conversation \ +letta -p --from-agent $LETTA_AGENT_ID \ --agent agent-abc123 \ - --message "What do you know about the authentication system?" + "What do you know about the authentication system?" ``` **Response:** @@ -94,21 +93,21 @@ letta messages start-conversation \ ### Continuing a Conversation ```bash -letta messages continue-conversation --conversation-id --message "" +letta -p --from-agent $LETTA_AGENT_ID --conversation "message text" ``` **Arguments:** | Arg | Required | Description | |-----|----------|-------------| -| `--conversation-id ` | Yes | Existing conversation ID | -| `--message ` | Yes | Follow-up message to send | -| `--timeout ` | No | Max wait time in ms (default: 120000) | +| `--conversation ` | Yes | Existing conversation ID | +| `--from-agent ` | Yes | Sender agent ID (injects agent-to-agent system reminder) | +| `"message text"` | Yes | Follow-up message (positional after flags) | **Example:** ```bash -letta messages continue-conversation \ - --conversation-id conversation-xyz789 \ - --message "Can you explain more about the token refresh flow?" +letta -p --from-agent $LETTA_AGENT_ID \ + --conversation conversation-xyz789 \ + "Can you explain more about the token refresh flow?" ``` ## Understanding the Response