From 4c702057e0a3a6a17aefaf673263ccf3aef3cdc8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 28 Jan 2026 16:24:17 -0800 Subject: [PATCH] feat: inject LETTA_AGENT_ID into hook environment context (#731) Co-authored-by: letta-code <248085862+letta-code@users.noreply.github.com> Co-authored-by: Cameron --- src/hooks/executor.ts | 10 +++++++- src/tests/hooks/executor.test.ts | 40 ++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/src/hooks/executor.ts b/src/hooks/executor.ts index 0334e7c..c5c9ae8 100644 --- a/src/hooks/executor.ts +++ b/src/hooks/executor.ts @@ -29,13 +29,21 @@ function trySpawnWithLauncher( throw new Error("Empty launcher"); } + // Extract agent_id if present (available on many hook input types) + const agentId = "agent_id" in input ? input.agent_id : undefined; + + // Build environment: start with parent env but exclude LETTA_AGENT_ID to prevent inheritance + // We only want to pass the agent ID that's explicitly provided in the hook input + const { LETTA_AGENT_ID: _, ...parentEnv } = process.env; + return spawn(executable, args, { cwd: workingDirectory, env: { - ...process.env, + ...parentEnv, // Add hook-specific environment variables LETTA_HOOK_EVENT: input.event_type, LETTA_WORKING_DIR: workingDirectory, + ...(agentId && { LETTA_AGENT_ID: agentId }), }, stdio: ["pipe", "pipe", "pipe"], }); diff --git a/src/tests/hooks/executor.test.ts b/src/tests/hooks/executor.test.ts index 494c04f..0f42635 100644 --- a/src/tests/hooks/executor.test.ts +++ b/src/tests/hooks/executor.test.ts @@ -160,6 +160,46 @@ describe.skipIf(isWindows)("Hooks Executor", () => { expect(result.exitCode).toBe(HookExitCode.ALLOW); expect(result.stdout).toBe("PreToolUse"); }); + + test("receives LETTA_AGENT_ID environment variable when agent_id is provided", async () => { + const hook: HookCommand = { + type: "command", + command: "echo $LETTA_AGENT_ID", + }; + + const input: PreToolUseHookInput = { + event_type: "PreToolUse", + working_directory: tempDir, + tool_name: "Bash", + tool_input: {}, + agent_id: "agent-test-12345", + }; + + const result = await executeHookCommand(hook, input, tempDir); + + expect(result.exitCode).toBe(HookExitCode.ALLOW); + expect(result.stdout).toBe("agent-test-12345"); + }); + + test("LETTA_AGENT_ID is not set when agent_id is not provided", async () => { + const hook: HookCommand = { + type: "command", + command: "echo \"agent_id:${LETTA_AGENT_ID:-empty}\"", + }; + + const input: PreToolUseHookInput = { + event_type: "PreToolUse", + working_directory: tempDir, + tool_name: "Bash", + tool_input: {}, + // Note: agent_id is not provided + }; + + const result = await executeHookCommand(hook, input, tempDir); + + expect(result.exitCode).toBe(HookExitCode.ALLOW); + expect(result.stdout).toBe("agent_id:empty"); + }); }); describe("executeHooks", () => {