-
Notifications
You must be signed in to change notification settings - Fork 39
feat(relay,desktop): canonicalize agent definitions on kind:30175 (Phase 2) #1655
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
2887b97
a95c3ca
b558b6e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -268,14 +268,61 @@ fn inbound_managed_agent_drops_injected_secrets_and_harness() { | |
| ] { | ||
| assert!(!json.contains(needle), "injected value leaked: {needle}"); | ||
| } | ||
| // Projected fields ARE updated from the inbound event. | ||
| // Instance-level projected fields ARE updated from the inbound event. | ||
| assert_eq!(a.name, "Remote Agent"); | ||
| assert_eq!(a.system_prompt, Some("remote prompt".to_string())); | ||
| assert_eq!(a.model, Some("remote-model".to_string())); | ||
| assert_eq!(a.provider, Some("remote-provider".to_string())); | ||
| assert_eq!(a.parallelism, 99); | ||
| assert_eq!(a.respond_to, crate::managed_agents::RespondTo::Anyone); | ||
| assert_eq!(a.respond_to_allowlist, vec!["deadbeef".to_string()]); | ||
| // Definition-linked inbound (persona_id present): the definition quad is | ||
| // NOT applied — those fields resolve through the kind:30175 definition, | ||
| // and absent-on-the-wire must never clear a local snapshot. | ||
| assert_eq!( | ||
| a.system_prompt, | ||
| Some("local prompt".to_string()), | ||
| "linked inbound must not touch the local prompt snapshot" | ||
| ); | ||
| } | ||
|
|
||
| /// Definition-less inbound (persona_id absent) still applies the definition | ||
| /// quad unconditionally — the record is its own definition and the wire is | ||
| /// its sync channel. | ||
| #[test] | ||
| fn inbound_definition_less_agent_applies_quad() { | ||
| use nostr::{EventBuilder, Keys, Kind, Tag}; | ||
| // Same wire shape as the hostile fixture, minus persona_id — a | ||
| // definition-less instance syncing its own definition fields. | ||
| let content = serde_json::json!({ | ||
| "name": "Remote Agent", | ||
| "system_prompt": "remote prompt", | ||
| "model": "remote-model", | ||
| "provider": "remote-provider", | ||
| "mcp_toolsets": "remote", | ||
| "persona_source_version": "remote-version", | ||
| "parallelism": 99, | ||
| "respond_to": "anyone", | ||
| "respond_to_allowlist": ["deadbeef"], | ||
| }); | ||
| let keys = Keys::generate(); | ||
| let event = EventBuilder::new(Kind::Custom(30177), content.to_string()) | ||
| .tags(vec![Tag::parse(["d", AGENT_PUBKEY]).unwrap()]) | ||
| .sign_with_keys(&keys) | ||
| .unwrap(); | ||
|
|
||
| let content = | ||
| crate::managed_agents::agent_events::managed_agent_content_from_event(&event).unwrap(); | ||
| let mut agents = vec![local_agent()]; | ||
| apply_inbound_managed_agent(&mut agents, AGENT_PUBKEY, content); | ||
|
|
||
| let a = &agents[0]; | ||
| assert_eq!(a.persona_id, None); | ||
| assert_eq!(a.system_prompt, Some("remote prompt".to_string())); | ||
| assert_eq!(a.model, Some("remote-model".to_string())); | ||
| assert_eq!(a.provider, Some("remote-provider".to_string())); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Test gap: definition-less apply omits The fixture has no Fix: add |
||
| assert_eq!( | ||
| a.persona_source_version, | ||
| Some("remote-version".to_string()), | ||
| "all four quad fields must apply on a definition-less sync" | ||
| ); | ||
| } | ||
|
|
||
| #[test] | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test gap: relay conformance tests don't exercise the real ingest path.
persona_envelope_accepts_promptless_contentandpersona_envelope_accepts_behavioral_fieldscall onlyvalidate_persona_envelope. The production path calls that helper fromingest_event_innerand then stores via the parameterized-replaceable branch, but these tests skip auth/scope/global-storage/round-trip — and the e2e addition covers legacy fat kind:30177, not 30175. A future content parse or gate introduced in the real ingest path would pass these helper tests silently.For a relay conformance claim, the boundary should be the actual ingest or a round-trip: add an ingest-level unit test (or ignored e2e) that publishes a prompt-less kind:30175 and a behavioral-field kind:30175, asserts
OK accepted, then queries the coordinate and compares content byte-for-byte.