feat(prompt): per-agent PROFILE.md + MEMORY.md injection, 2K-char cap#550
feat(prompt): per-agent PROFILE.md + MEMORY.md injection, 2K-char cap#550senamakel merged 9 commits intotinyhumansai:mainfrom
Conversation
- Updated the `debug-agent-prompts.sh` script to always run `cargo build`, ensuring the latest binary is used and preventing stale binaries from affecting agent behavior. - Modified the output directory handling to wipe and recreate it at the start of each run, ensuring a clean snapshot of the current agent set. - Enhanced the `render_subagent_system_prompt` function to unconditionally inject `PROFILE.md`, ensuring it is included even when identity information is omitted, thus improving personalization for agents like `welcome`. - Added tests to verify the correct injection of `PROFILE.md` under various conditions, ensuring robust functionality and preventing regressions in prompt rendering.
… user's workspace - Updated the `debug-agent-prompts.sh` script to point to the real user's workspace, ensuring onboarding-generated files like `PROFILE.md` are included in the dump. - Improved workspace resolution logic to prioritize the active user's workspace, falling back to default paths as necessary. - Added error handling for cases where the workspace is not found, providing clear guidance for users to complete onboarding or specify a different workspace. - Enhanced output to include the presence state of `PROFILE.md`, improving visibility into the onboarding process.
- Updated agent TOML configurations for orchestrator, trigger reactor, trigger triage, and welcome agents to include user profile data by setting `omit_profile = false`. This allows agents to personalize interactions based on user context derived from `PROFILE.md`. - Refactored the `AgentDefinition` struct to include a new `omit_profile` field, ensuring that agents can opt-in to utilize user profile information. - Enhanced prompt rendering logic to conditionally inject `PROFILE.md` based on the `omit_profile` flag, improving the relevance and personalization of agent responses. - Updated tests to verify the correct behavior of profile inclusion across various agents, ensuring robust functionality and preventing regressions.
…le management - Added a new `omit_profile` field to the `AgentBuilder` struct, allowing agents to specify whether to include user profile data in their responses. - Updated the `Agent` struct to mirror the `omit_profile` flag, ensuring that the profile inclusion logic is consistent across agent instances. - Enhanced the `omit_profile` method in `AgentBuilder` to facilitate the configuration of this flag during agent construction. - Adjusted the default behavior to omit profiles for legacy agents while allowing opt-in for specific agents that require user context. - Updated documentation to clarify the purpose and usage of the `omit_profile` flag in agent definitions.
- Introduced an `omit_profile` flag in the `AgentBuilder` and `Agent` to control the inclusion of user profile data in responses, defaulting to true for legacy paths. - Updated the `Agent` struct to utilize the `omit_profile` flag, ensuring consistent profile inclusion logic across agent instances. - Enhanced prompt rendering logic to conditionally include or exclude `PROFILE.md` based on the `omit_profile` setting, improving personalization for user-facing agents. - Added tests to verify the correct behavior of profile inclusion and omission across various scenarios, ensuring robust functionality and preventing regressions.
Add `omit_memory_md` to `AgentDefinition` (mirror of `omit_profile`) and inject `MEMORY.md` alongside `PROFILE.md` in both the main and sub-agent render paths. Both user-specific files are capped at `USER_FILE_MAX_CHARS = 2_000` (~1000 tokens each) via a new `inject_workspace_file_capped` helper so growing on-disk files can't balloon the system prompt. Opt-in on the same four user-facing agents (welcome, orchestrator, trigger_triage, trigger_reactor). Narrow specialists leave it at the `true` default. KV-cache contract is documented on the flag, the injection sites, and the capped helper: rendered bytes are frozen per session, and mid-session writes only surface on the next session. Pinned with a new `rendered_subagent_system_prompt_is_byte_stable_across_repeat_calls` test plus coverage for injection / opt-out / 2000-char cap.
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 2 minutes and 57 seconds. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
📝 WalkthroughWalkthroughThreaded two new per-agent flags— Changes
Sequence Diagram(s)sequenceDiagram
participant TOML as Agent TOML Config
participant Def as AgentDefinition
participant Builder as AgentBuilder
participant Agent as Agent Instance
participant Prompt as PromptContext
participant Renderer as Prompt Renderer
TOML->>Def: load omit_profile / omit_memory_md (defaults true)
Def->>Builder: Agent::from_config_for_agent() passes flags
Builder->>Agent: set omit_profile / omit_memory_md via builder
Agent->>Prompt: build_system_prompt (invert to include_*)
Prompt->>Renderer: provide include_profile & include_memory_md
Renderer->>Renderer: conditionally inject PROFILE.md (capped)
Renderer->>Renderer: conditionally inject MEMORY.md (capped)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
- Removed unnecessary line breaks and adjusted indentation in test files for better consistency and clarity. - Reformatted the `RewardsCouponSection` test to enhance readability and maintain a uniform style across test cases. - Ensured that all test cases align with the updated formatting standards, improving overall maintainability.
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
src/openhuman/agent/harness/session/builder.rs (1)
782-818: Log the resolved profile/memory omission flags.This branch now decides whether
PROFILE.mdandMEMORY.mdreach the session prompt, but the surrounding diagnostics still hide both values. A debug log here would make prompt-shaping regressions much easier to trace.Possible addition
let effective_omit_profile = target_def.map(|def| def.omit_profile).unwrap_or(true); let effective_omit_memory_md = target_def.map(|def| def.omit_memory_md).unwrap_or(true); + + log::debug!( + "[agent::builder] prompt file flags: agent_id={} omit_profile={} omit_memory_md={}", + agent_id, + effective_omit_profile, + effective_omit_memory_md + );As per coding guidelines "Add substantial, development-oriented logs on new/changed flows; include logs at entry/exit points, branch decisions, external calls, retries/timeouts, state transitions, and error handling paths".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/openhuman/agent/harness/session/builder.rs` around lines 782 - 818, The code computes effective_omit_profile and effective_omit_memory_md but does not log their values, making prompt-shaping regressions hard to trace; add a debug/info log just before constructing the Agent with Agent::builder() that emits both effective_omit_profile and effective_omit_memory_md (and optionally agent_id and model_name for context) so callers can see which branch was chosen; place the log immediately above the Agent::builder() call, using the existing tracing/logger facility in this module to record the two booleans and minimal context.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@scripts/debug-agent-prompts.sh`:
- Around line 104-110: Resolve and validate OUT_DIR before deletion: disallow
relative or symlinked paths by requiring OUT_DIR be absolute (starts with "/")
and canonicalize it (using readlink -f or realpath) into a resolved_out
variable, then compare resolved_out against disallowed targets ("" "/" "${HOME}"
and the canonicalized REPO_ROOT) and reject if it equals any of them or if the
original OUT_DIR is "." or ".."; only call rm -rf on the canonicalized,
validated resolved_out. Ensure checks reference OUT_DIR and REPO_ROOT and
perform symlink resolution so symlinked paths cannot bypass the guard.
In `@src/openhuman/agent/harness/session/turn.rs`:
- Around line 923-924: The debug log currently printing the full system prompt
body (rendered_prompt.text) can leak PROFILE/MEMORY contents when
include_profile or include_memory_md are enabled; change the logging around
where rendered_prompt.text is emitted to omit or redact sensitive content
whenever include_profile == true or include_memory_md == true (or
omit_profile/omit_memory_md are false), e.g., replace the full-body log with
either a short summary, length-limited snippet, or a fixed redaction message
indicating PROFILE/MEMORY were omitted; update the log call(s) that reference
rendered_prompt.text to check include_profile/include_memory_md and avoid
printing full prompt when those flags allow injected sensitive data.
In `@src/openhuman/context/prompt.rs`:
- Around line 1490-1495: Run rustfmt on the changed Rust test hunks and ensure
the file compiles: format the test function
subagent_render_options_invert_definition_flags and the other affected test
blocks (around the ranges near lines 1577-1585 and 1808-1811) so their spacing
and indentation conform to cargo fmt; then run cargo check to ensure no
formatting-induced warnings/errors. Specifically, open
src/openhuman/context/prompt.rs, reformat the test that calls
SubagentRenderOptions::from_definition_flags(true, false, true, false, false)
and the assertions (e.g., assert!(!options.include_identity)), fix any stray
spacing/line breaks per rustfmt, and re-run cargo fmt/cargo check across the
workspace (including root Cargo.toml and app/src-tauri/Cargo.toml) before
committing.
- Around line 392-419: The PROFILE.md and MEMORY.md injections are only inside
IdentitySection so when SystemPromptBuilder::for_subagent() skips
IdentitySection due to omit_identity=true the files never get injected; update
the prompt-building logic so inject_workspace_file_capped is called for
ctx.include_profile and ctx.include_memory_md in the shared subagent rendering
path (e.g., inside render_subagent_system_prompt or
SystemPromptBuilder::for_subagent()) regardless of omit_identity, or move the
injection out of IdentitySection into the common prompt assembly code; reference
inject_workspace_file_capped, ctx.include_profile, ctx.include_memory_md,
SystemPromptBuilder::for_subagent, render_subagent_system_prompt,
IdentitySection, and AgentDefinition::omit_memory_md/omit_profile when making
the change.
---
Nitpick comments:
In `@src/openhuman/agent/harness/session/builder.rs`:
- Around line 782-818: The code computes effective_omit_profile and
effective_omit_memory_md but does not log their values, making prompt-shaping
regressions hard to trace; add a debug/info log just before constructing the
Agent with Agent::builder() that emits both effective_omit_profile and
effective_omit_memory_md (and optionally agent_id and model_name for context) so
callers can see which branch was chosen; place the log immediately above the
Agent::builder() call, using the existing tracing/logger facility in this module
to record the two booleans and minimal context.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: d6c4772b-4a1c-40aa-ac1d-632436e52b15
📒 Files selected for processing (16)
scripts/debug-agent-prompts.shsrc/openhuman/agent/agents/orchestrator/agent.tomlsrc/openhuman/agent/agents/trigger_reactor/agent.tomlsrc/openhuman/agent/agents/trigger_triage/agent.tomlsrc/openhuman/agent/agents/welcome/agent.tomlsrc/openhuman/agent/harness/builtin_definitions.rssrc/openhuman/agent/harness/definition.rssrc/openhuman/agent/harness/session/builder.rssrc/openhuman/agent/harness/session/turn.rssrc/openhuman/agent/harness/session/types.rssrc/openhuman/agent/harness/subagent_runner.rssrc/openhuman/channels/runtime/dispatch.rssrc/openhuman/context/debug_dump.rssrc/openhuman/context/prompt.rssrc/openhuman/learning/prompt_sections.rssrc/openhuman/tools/orchestrator_tools.rs
| include_profile: !self.omit_profile, | ||
| include_memory_md: !self.omit_memory_md, |
There was a problem hiding this comment.
Prevent full prompt-body logging now that PROFILE/MEMORY can be injected.
With these flags enabled, rendered_prompt.text can carry user-sensitive PROFILE.md/MEMORY.md data. The existing debug log at Line 93 prints the full system prompt body, which can leak PII/secrets into logs.
🔒 Proposed fix
- log::debug!("[agent_loop] system prompt body:\n{}", rendered_prompt.text);
+ log::debug!(
+ "[agent_loop] system prompt summary chars={} include_profile={} include_memory_md={}",
+ rendered_prompt.text.chars().count(),
+ !self.omit_profile,
+ !self.omit_memory_md
+ );As per coding guidelines Never log secrets, API keys, JWTs, credentials, or full PII in Rust logs; redact or omit sensitive fields.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/openhuman/agent/harness/session/turn.rs` around lines 923 - 924, The
debug log currently printing the full system prompt body (rendered_prompt.text)
can leak PROFILE/MEMORY contents when include_profile or include_memory_md are
enabled; change the logging around where rendered_prompt.text is emitted to omit
or redact sensitive content whenever include_profile == true or
include_memory_md == true (or omit_profile/omit_memory_md are false), e.g.,
replace the full-body log with either a short summary, length-limited snippet,
or a fixed redaction message indicating PROFILE/MEMORY were omitted; update the
log call(s) that reference rendered_prompt.text to check
include_profile/include_memory_md and avoid printing full prompt when those
flags allow injected sensitive data.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/openhuman/context/prompt.rs`:
- Around line 845-861: Add dev-oriented trace logging around the PROFILE.md and
MEMORY.md injection flow: in the block that checks options.include_profile and
options.include_memory_md (and inside/in front of inject_workspace_file_capped),
log entry/exit, branch decisions (file gated vs skipped), the target filename
and workspace_dir, whether the file was found or missing, and the resulting
truncation length versus USER_FILE_MAX_CHARS; also surface any filesystem/read
errors returned by inject_workspace_file_capped so callers can see why a file
was skipped or truncated. Ensure logs are at trace/debug level and include
unique context (e.g., "inject_workspace_file_capped PROFILE.md",
"inject_workspace_file_capped MEMORY.md") so they can be filtered independently.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 3a30529a-9125-45b8-942c-84944e967c69
📒 Files selected for processing (1)
src/openhuman/context/prompt.rs
| // 1c. PROFILE.md (onboarding enrichment output) and MEMORY.md | ||
| // (archivist-curated long-term memory). Each is gated on its own | ||
| // flag and capped at `USER_FILE_MAX_CHARS` (~1000 tokens) so a | ||
| // growing on-disk file can't push the system prompt out of the | ||
| // cache-friendly prefix range. | ||
| // | ||
| // KV-cache contract: once these files land in a session's | ||
| // rendered prompt the bytes are frozen for the remainder of that | ||
| // session. Do not re-read them mid-turn — a byte change breaks | ||
| // the backend's automatic prefix cache. Mid-session writes to | ||
| // either file are intentionally only visible on the NEXT session. | ||
| if options.include_profile { | ||
| inject_workspace_file_capped(&mut out, workspace_dir, "PROFILE.md", USER_FILE_MAX_CHARS); | ||
| } | ||
| if options.include_memory_md { | ||
| inject_workspace_file_capped(&mut out, workspace_dir, "MEMORY.md", USER_FILE_MAX_CHARS); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Add debug/trace observability for the new PROFILE/MEMORY injection flow.
The new gating/truncation path does filesystem reads and branch decisions silently, which makes prompt-diff debugging hard when PROFILE.md/MEMORY.md are skipped or truncated.
Proposed logging patch
@@
- if options.include_profile {
+ tracing::debug!(
+ include_profile = options.include_profile,
+ include_memory_md = options.include_memory_md,
+ "[context::prompt] subagent user-file injection flags"
+ );
+ if options.include_profile {
inject_workspace_file_capped(&mut out, workspace_dir, "PROFILE.md", USER_FILE_MAX_CHARS);
}
@@
fn inject_workspace_file_capped(
@@
) {
let path = workspace_dir.join(filename);
match std::fs::read_to_string(&path) {
Ok(content) => {
let trimmed = content.trim();
if trimmed.is_empty() {
+ tracing::debug!(
+ file = filename,
+ "[context::prompt] skipped empty workspace file injection"
+ );
return;
}
let _ = writeln!(prompt, "### {filename}\n");
let truncated = if trimmed.chars().count() > max_chars {
+ tracing::debug!(
+ file = filename,
+ max_chars,
+ "[context::prompt] truncating workspace file for prompt injection"
+ );
trimmed
.char_indices()
.nth(max_chars)
.map(|(idx, _)| &trimmed[..idx])
.unwrap_or(trimmed)
} else {
trimmed
};
@@
- Err(_) => {
+ Err(err) => {
+ tracing::debug!(
+ file = filename,
+ error = %err,
+ "[context::prompt] workspace file missing/unreadable; skipping injection"
+ );
// Keep prompt focused: missing optional identity/bootstrap files should not
// add noisy placeholders that dilute tool-calling instructions.
}
}
}As per coding guidelines: “Add substantial, development-oriented logs on new/changed flows; include logs at entry/exit points, branch decisions, external calls, retries/timeouts, state transitions, and error handling paths”.
Also applies to: 1122-1151
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/openhuman/context/prompt.rs` around lines 845 - 861, Add dev-oriented
trace logging around the PROFILE.md and MEMORY.md injection flow: in the block
that checks options.include_profile and options.include_memory_md (and inside/in
front of inject_workspace_file_capped), log entry/exit, branch decisions (file
gated vs skipped), the target filename and workspace_dir, whether the file was
found or missing, and the resulting truncation length versus
USER_FILE_MAX_CHARS; also surface any filesystem/read errors returned by
inject_workspace_file_capped so callers can see why a file was skipped or
truncated. Ensure logs are at trace/debug level and include unique context
(e.g., "inject_workspace_file_capped PROFILE.md", "inject_workspace_file_capped
MEMORY.md") so they can be filtered independently.
…nonicalization - Improved the `debug-agent-prompts.sh` script to validate and canonicalize the output directory (`OUT_DIR`) before performing any file operations. - Added checks to reject relative paths and ensure the output directory is an absolute path, preventing potential catastrophic deletions. - Implemented a `canonicalize` function that uses `realpath` or `readlink` to resolve paths, with a fallback to Python for compatibility on barebones systems. - Enhanced error handling to provide clear feedback when the output directory cannot be validated or canonicalized. - Ensured that the script operates on the canonicalized path for all subsequent commands, maintaining consistency and safety.
Summary
PROFILE.mdcontext: onboarding enrichment writes the file but the welcome sub-agent never saw it because the renderer bundledPROFILE.mdinside the identity block thatomit_identity = truestripped.PROFILE.mdandMEMORY.mdinclusion into explicit, per-agent config (omit_profile/omit_memory_mdinagent.toml), opt-in on user-facing agents only.USER_FILE_MAX_CHARS = 2_000(~1000 tokens) so growing on-disk files can't balloon the system prompt.scripts/debug-agent-prompts.shso prompt dumps reflect what the LLM actually sees.Problem
The welcome agent's prompt was missing
PROFILE.mdeven though the onboarding pipeline had written it to the workspace. Root cause was insrc/openhuman/context/prompt.rs: the subagent renderer's identity block (SOUL.md/IDENTITY.md/PROFILE.md) was gated as one unit oninclude_identity, and the welcome agent setsomit_identity = true(it has its own voice), which silently droppedPROFILE.mdalong with the SOUL/IDENTITY preamble. Once that was untangled, the broader question surfaced: which agents should seePROFILE.md(andMEMORY.md) at all, and what budget should these user-specific files occupy?Solution
Per-file agent config flags —
AgentDefinition::omit_profileandAgentDefinition::omit_memory_md(both#[serde(default = true)]), threaded viaSubagentRenderOptions::include_profile/include_memory_mdintorender_subagent_system_prompt, and via a newAgentBuilder::omit_memory_md()/omit_profile()pair +PromptContextfields intoIdentitySection::buildfor the main-agent path. Opt-in on four agents only:welcome,orchestrator,trigger_triage,trigger_reactor.2000-char budget — new
inject_workspace_file_capped(prompt, dir, filename, max_chars)helper;PROFILE.mdandMEMORY.mduseUSER_FILE_MAX_CHARS = 2_000, bootstrap files (SOUL/IDENTITY/HEARTBEAT) keep the existing 20K budget via a thin wrapper.KV-cache contract documented in code — rustdoc on
omit_memory_md, the injection sites, and the capped helper spells out that rendered bytes are frozen per session. Mid-session writes to either file do NOT update the in-flight system prompt; they land on the next session. Backed by a newrendered_subagent_system_prompt_is_byte_stable_across_repeat_callstest.Debug dumper fixes —
scripts/debug-agent-prompts.sh:openhuman-core(the-xexistence-only guard let a stale binary survive across agent-registry changes, silently skipping newly added agents likewelcome).prompt-dumps/with no timestamp subfolder for cleaner diffs; safety-guarded against wiping$HOME///repo root.~/.openhuman/active_user.toml) so dumps reflect what the LLM actually sees, not an emptymktemp -d.Submission Checklist
src/openhuman/context/prompt.rs(25 total, all green): PROFILE.md / MEMORY.md inject-when-enabled, skip-when-disabled, welcome-flags-load-PROFILE, narrow-flags-skip-PROFILE, 2000-char cap on both files, and byte-stability across repeat spawns (the KV-cache pin).scripts/debug-agent-prompts.shagainst a real user workspace:PROFILE.mdandMEMORY.mdappear only in the 4 opt-in dumps (welcome,orchestrator,trigger_triage,trigger_reactor) +main(= orchestrator), and the truncation marker fires at exactly 2000 chars on a seeded oversize file.USER_FILE_MAX_CHARS,inject_workspace_file_capped, and the renderer comment blocks explaining the session-freeze / KV-cache contract.fork_definitionkeeps both flags atfalse, why the narrow renderer path has a separate gate, why the dumper no longer uses a mktemp workspace) annotated inline.Impact
welcome,orchestrator,trigger_triage,trigger_reactor) now seePROFILE.md+MEMORY.md. Narrow specialists (planner, code_executor, skills_agent, tool_maker, researcher, critic, archivist, morning_briefing) default toomit_profile = true/omit_memory_md = trueand stay lean.(archetype body, tool set, workspace files at load time, model name, options)— pinned by an explicit byte-stability test. Re-entry of the same definition inside one session reuses the backend's prefix cache.#[serde(default = true)], so existing customagents/*.tomloverrides in a user workspace keep working without edits (and stay lean — they won't suddenly start loading PROFILE/MEMORY).Related
omit_*+USER_FILE_MAX_CHARSlogic into a dedicatedprompt::workspace_injectionmodule if we add a third user-file (e.g.GOALS.md), but two files doesn't warrant it yet.Summary by CodeRabbit
New Features
Chores