feat(acp): compose agent identity into system prompt with fallback#1087
Open
tlongwell-block wants to merge 1 commit into
Open
feat(acp): compose agent identity into system prompt with fallback#1087tlongwell-block wants to merge 1 commit into
tlongwell-block wants to merge 1 commit into
Conversation
Move NIP-AE core memory into the composed identity section that rides
the existing system-prompt routing, rather than always emitting it as a
standalone [Agent Memory — core] user section. v2 agents receive it in
the real system role via session/new; legacy v1 agents receive the same
composed string in the user message via the existing format_prompt gate.
No version fork in routing — the per-version split stays in the existing
`!has_system_prompt_support` branch, which kills double-render by
construction.
Core is additive to the operator prompt, not exclusive-or:
- operator prompt present + core present -> base + operator + core
- operator prompt present + core empty -> base + operator
- operator prompt absent + core present -> base + core
- operator prompt absent + core empty -> base + fallback
- core fetch failed (either case) -> no core, no fallback
Replace the overloaded `build_core_section: Option<String>` with a
tri-state `fetch_core -> CoreFetch::{Present, ConfirmedEmpty,
Unavailable}` so "confirmed empty" and "fetch failed" stay distinct —
the distinction the fallback rule depends on. The old onboarding-nudge
const becomes FALLBACK_SYSTEM_PROMPT.
ACP compliance: top-level `systemPrompt` was a Buzz extension, not ACP.
Move it to the namespaced `_meta: { "buzz.systemPrompt": ... }` envelope
in both the sender (acp.rs) and reader (wire.rs). Reader is crash-proof
on malformed _meta and now ignores the bare top-level key; regression
tests pin that.
Rename SessionState.core_sections -> identity_sections to match what it
now holds (resolved identity, not raw core).
Co-authored-by: Tyler Longwell <tlongwell@squareup.com>
Signed-off-by: Tyler Longwell <tlongwell@squareup.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
Agent identity (operator system prompt + NIP-AE core memory) is composed into a single identity section that rides the existing system-prompt routing, instead of core always being emitted as a standalone
[Agent Memory — core]user-message section.buzz-agent,protocol_version >= 2): the composed identity lands in the real system role viasession/new.format_promptlegacy branch.No new version fork in routing — the per-version split stays entirely in the existing
!has_system_prompt_supportgate, which kills double-render by construction.Composition (core is additive, not exclusive-or)
Fallback (the repurposed onboarding text →
FALLBACK_SYSTEM_PROMPT) fires only when there's no operator prompt and core fetch confirmed-empty. A relay hiccup (Unavailable) never hands an established agent a newbie identity.Key changes
engram_fetch.rs—build_core_section: Option<String>→ tri-statefetch_core -> CoreFetch::{Present, ConfirmedEmpty, Unavailable}. The oldOptionflattened "empty" and "fetch failed" into oneNone; the enum keeps them distinct — the distinction the fallback rule depends on.pool.rs— two pure helpers (resolve_identity_section,compose_system_prompt) with the truth table in doc comments. Core fetch hoisted above session creation (is_new_sessionvia a cheap map lookup), resolved section cached for the legacy path.SessionState.core_sections→identity_sections(it holds the resolved identity, not raw core).systemPromptwas a Buzz extension, not ACP. Moved to namespaced_meta: { "buzz.systemPrompt": ... }in both sender (acp.rs) and reader (wire.rs). Reader is crash-proof on malformed_metaand now ignores the bare top-level key; regression tests pin that.Testing
test_format_prompt_modern_agent_suppresses_base_and_system(modern → no duplicate[Agent Memory]in user message) andtest_format_prompt_legacy_agent_emits_base_and_system(legacy → composed identity via[System], delivery-neutral).Reviewer: please also run locally per
TESTING.md.