From 6103ca341c2671bbe80d0b6d1a6a9b49cf3c5f85 Mon Sep 17 00:00:00 2001 From: rabbitblood Date: Mon, 20 Apr 2026 00:22:47 -0700 Subject: [PATCH 1/2] feat(runtime): inject HMA memory instructions at platform level (#1047) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every agent now gets hierarchical memory instructions in their system prompt automatically — no template configuration needed. Instructions cover commit_memory (LOCAL/TEAM/GLOBAL scopes), recall_memory, and when to use each proactively. Follows the same pattern as A2A instructions: defined in executor_helpers.py, injected by _build_system_prompt() in the claude_sdk_executor. Co-Authored-By: Claude Opus 4.6 (1M context) --- workspace/claude_sdk_executor.py | 9 +++++---- workspace/executor_helpers.py | 25 +++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/workspace/claude_sdk_executor.py b/workspace/claude_sdk_executor.py index 76421a46..8569fb60 100644 --- a/workspace/claude_sdk_executor.py +++ b/workspace/claude_sdk_executor.py @@ -49,6 +49,7 @@ from executor_helpers import ( commit_memory, extract_message_text, get_a2a_instructions, + get_hma_instructions, get_mcp_server_path, get_system_prompt, read_delegation_results, @@ -210,12 +211,12 @@ class ClaudeSDKExecutor(AgentExecutor): return CONFIG_MOUNT def _build_system_prompt(self) -> str | None: - """Compose system prompt from file + A2A delegation instructions.""" + """Compose system prompt from file + A2A + HMA memory instructions.""" base = get_system_prompt(self.config_path, fallback=self.system_prompt) a2a = get_a2a_instructions(mcp=True) - if base and a2a: - return f"{base}\n\n{a2a}" - return base or a2a + hma = get_hma_instructions() + parts = [p for p in (base, a2a, hma) if p] + return "\n\n".join(parts) if parts else None def _prepare_prompt(self, user_input: str) -> str: """Prepend delegation results that arrived while idle.""" diff --git a/workspace/executor_helpers.py b/workspace/executor_helpers.py index 848dd6a2..ce165bfd 100644 --- a/workspace/executor_helpers.py +++ b/workspace/executor_helpers.py @@ -288,6 +288,31 @@ def get_a2a_instructions(mcp: bool = True) -> str: return _A2A_INSTRUCTIONS_MCP if mcp else _A2A_INSTRUCTIONS_CLI +_HMA_INSTRUCTIONS = """## Hierarchical Memory (HMA) +You have persistent memory tools that survive across sessions and restarts: + +- **commit_memory(content, scope)**: Save important information. + - LOCAL: private to you only (default) + - TEAM: shared with your parent workspace and siblings (same team) + - GLOBAL: shared with the entire org (only root workspaces can write) + +- **recall_memory(query)**: Search your accessible memories. Returns LOCAL + TEAM + GLOBAL matches. + +**When to use memory:** +- After making a decision or learning something non-obvious → commit_memory("decision X because Y", scope="TEAM") +- Before starting work → recall_memory("what did the team decide about X") +- When you discover org-wide knowledge (repo locations, API patterns, conventions) → commit_memory(fact, scope="GLOBAL") if you are a root workspace, or scope="TEAM" to share with your team +- After completing a task → commit_memory("completed task X, PR #N opened", scope="TEAM") so your lead and teammates know + +**Memory is automatically recalled** at the start of each new session. Use it proactively during work to share context. +""" + + +def get_hma_instructions() -> str: + """Return HMA memory instructions for system-prompt injection.""" + return _HMA_INSTRUCTIONS + + # ======================================================================== # Misc text helpers # ======================================================================== From 5bc3edfbddf968ddfa97286557319e596a2efe75 Mon Sep 17 00:00:00 2001 From: rabbitblood Date: Mon, 20 Apr 2026 01:05:05 -0700 Subject: [PATCH 2/2] Fix test assertions to account for HMA instructions in system prompt Mock get_hma_instructions in exact-match tests so they don't break when HMA content is appended. Add a dedicated test for HMA inclusion. Co-Authored-By: Claude Opus 4.6 (1M context) --- workspace/tests/test_claude_sdk_executor.py | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/workspace/tests/test_claude_sdk_executor.py b/workspace/tests/test_claude_sdk_executor.py index e3781ad9..ac3b0c3d 100644 --- a/workspace/tests/test_claude_sdk_executor.py +++ b/workspace/tests/test_claude_sdk_executor.py @@ -479,7 +479,8 @@ def test_build_system_prompt_combines_base_and_a2a_via_fixture(): """Direct test bypassing the execute() path.""" e = _make_executor() with patch("claude_sdk_executor.get_system_prompt", return_value="BASE"), \ - patch("claude_sdk_executor.get_a2a_instructions", return_value="A2A"): + patch("claude_sdk_executor.get_a2a_instructions", return_value="A2A"), \ + patch("claude_sdk_executor.get_hma_instructions", return_value=""): out = e._build_system_prompt() assert out == "BASE\n\nA2A" @@ -487,17 +488,31 @@ def test_build_system_prompt_combines_base_and_a2a_via_fixture(): def test_build_system_prompt_base_only(): e = _make_executor() with patch("claude_sdk_executor.get_system_prompt", return_value="BASE"), \ - patch("claude_sdk_executor.get_a2a_instructions", return_value=""): + patch("claude_sdk_executor.get_a2a_instructions", return_value=""), \ + patch("claude_sdk_executor.get_hma_instructions", return_value=""): assert e._build_system_prompt() == "BASE" def test_build_system_prompt_a2a_only(): e = _make_executor() with patch("claude_sdk_executor.get_system_prompt", return_value=None), \ - patch("claude_sdk_executor.get_a2a_instructions", return_value="A2A"): + patch("claude_sdk_executor.get_a2a_instructions", return_value="A2A"), \ + patch("claude_sdk_executor.get_hma_instructions", return_value=""): assert e._build_system_prompt() == "A2A" +def test_build_system_prompt_includes_hma(): + """HMA instructions are appended when present.""" + e = _make_executor() + with patch("claude_sdk_executor.get_system_prompt", return_value="BASE"), \ + patch("claude_sdk_executor.get_a2a_instructions", return_value="A2A"), \ + patch("claude_sdk_executor.get_hma_instructions", return_value="## Hierarchical Memory"): + out = e._build_system_prompt() + assert "BASE" in out + assert "A2A" in out + assert "## Hierarchical Memory" in out + + def test_prepare_prompt_no_delegation_returns_unchanged(): e = _make_executor() with patch("claude_sdk_executor.read_delegation_results", return_value=""):