fix: render ticket event notice from prompt resource
This commit is contained in:
parent
465ef1004b
commit
6f8571f77f
|
|
@ -95,6 +95,8 @@ pub enum PodPrompt {
|
|||
/// Trailing Pod orchestration guidance, appended when registered tools
|
||||
/// include Pod-management capabilities.
|
||||
PodOrchestrationGuidanceSection,
|
||||
/// Weak Companion Notify payload for explicit Orchestrator Ticket events.
|
||||
TicketEventCompanionNotice,
|
||||
/// LLM-facing description for the SpawnPod tool, including discovered
|
||||
/// profile selectors.
|
||||
SpawnPodToolDescription,
|
||||
|
|
@ -115,6 +117,7 @@ impl PodPrompt {
|
|||
Self::ResidentKnowledgeSection => "resident_knowledge_section",
|
||||
Self::ResidentWorkflowsSection => "resident_workflows_section",
|
||||
Self::PodOrchestrationGuidanceSection => "pod_orchestration_guidance_section",
|
||||
Self::TicketEventCompanionNotice => "ticket_event_companion_notice",
|
||||
Self::SpawnPodToolDescription => "spawn_pod_tool_description",
|
||||
}
|
||||
}
|
||||
|
|
@ -135,6 +138,7 @@ impl PodPrompt {
|
|||
PodPrompt::ResidentKnowledgeSection,
|
||||
PodPrompt::ResidentWorkflowsSection,
|
||||
PodPrompt::PodOrchestrationGuidanceSection,
|
||||
PodPrompt::TicketEventCompanionNotice,
|
||||
PodPrompt::SpawnPodToolDescription,
|
||||
];
|
||||
|
||||
|
|
@ -151,6 +155,7 @@ impl PodPrompt {
|
|||
"resident_knowledge_section",
|
||||
"resident_workflows_section",
|
||||
"pod_orchestration_guidance_section",
|
||||
"ticket_event_companion_notice",
|
||||
"spawn_pod_tool_description",
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,15 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use minijinja::Value as TemplateValue;
|
||||
use serde_json::Value;
|
||||
use std::collections::BTreeMap;
|
||||
use ticket::{LocalTicketBackend, TicketBackend, TicketIdOrSlug};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::discovery::PodDiscovery;
|
||||
use crate::hook::{Hook, HookPostToolAction, PostToolCall, ToolResultSummary};
|
||||
use crate::prompt::catalog::{PodPrompt, PromptCatalog};
|
||||
use pod_store::PodMetadataStore;
|
||||
|
||||
const MAX_TITLE_CHARS: usize = 96;
|
||||
|
|
@ -89,28 +92,56 @@ fn build_ticket_event_notice(
|
|||
.ok()?;
|
||||
|
||||
let event_kind = sanitize_one_line(&event_kind, MAX_EVENT_KIND_CHARS);
|
||||
let ticket_id = ticket.meta.id.as_str();
|
||||
let title = sanitize_one_line(&ticket.meta.title, MAX_TITLE_CHARS);
|
||||
let state = ticket.meta.workflow_state.as_str();
|
||||
let output_summary = sanitize_one_line(&output.summary, MAX_SUMMARY_CHARS);
|
||||
let ref_path = event_ref_path(&ticket.meta.id, summary.tool_name.as_str());
|
||||
let raw_message = format!(
|
||||
"Ticket event notice (weak; auto_run=false)\n\
|
||||
ticket: {ticket_id}\n\
|
||||
title: {title}\n\
|
||||
state: {state}\n\
|
||||
event: {event_kind}\n\
|
||||
summary: {output_summary}\n\
|
||||
ref: {ref_path}",
|
||||
ticket_id = ticket.meta.id.as_str(),
|
||||
);
|
||||
let ref_path = event_ref_path(ticket_id, summary.tool_name.as_str());
|
||||
let message = render_ticket_event_notice_message(TicketEventNoticeValues {
|
||||
ticket_id,
|
||||
title: &title,
|
||||
state,
|
||||
event_kind: &event_kind,
|
||||
summary: &output_summary,
|
||||
ref_path: &ref_path,
|
||||
})?;
|
||||
|
||||
Some(TicketEventNotice {
|
||||
ticket_id: ticket.meta.id.as_str().to_string(),
|
||||
ticket_id: ticket_id.to_string(),
|
||||
event_kind,
|
||||
message: bound_chars(&raw_message, MAX_MESSAGE_CHARS),
|
||||
message: bound_chars(&message, MAX_MESSAGE_CHARS),
|
||||
})
|
||||
}
|
||||
|
||||
struct TicketEventNoticeValues<'a> {
|
||||
ticket_id: &'a str,
|
||||
title: &'a str,
|
||||
state: &'a str,
|
||||
event_kind: &'a str,
|
||||
summary: &'a str,
|
||||
ref_path: &'a str,
|
||||
}
|
||||
|
||||
fn render_ticket_event_notice_message(values: TicketEventNoticeValues<'_>) -> Option<String> {
|
||||
PromptCatalog::builtins_only()
|
||||
.ok()?
|
||||
.render(PodPrompt::TicketEventCompanionNotice, values.to_template())
|
||||
.ok()
|
||||
}
|
||||
|
||||
impl TicketEventNoticeValues<'_> {
|
||||
fn to_template(&self) -> TemplateValue {
|
||||
let mut values: BTreeMap<&'static str, TemplateValue> = BTreeMap::new();
|
||||
values.insert("ticket_id", TemplateValue::from(self.ticket_id));
|
||||
values.insert("title", TemplateValue::from(self.title));
|
||||
values.insert("state", TemplateValue::from(self.state));
|
||||
values.insert("event_kind", TemplateValue::from(self.event_kind));
|
||||
values.insert("summary", TemplateValue::from(self.summary));
|
||||
values.insert("ref_path", TemplateValue::from(self.ref_path));
|
||||
TemplateValue::from(values)
|
||||
}
|
||||
}
|
||||
|
||||
fn explicit_ticket_event_kind(tool_name: &str, content: &Value) -> Option<String> {
|
||||
match tool_name {
|
||||
"TicketComment" => content
|
||||
|
|
@ -230,6 +261,29 @@ mod tests {
|
|||
assert!(notice.message.contains("event: state/queued->inprogress"));
|
||||
assert!(notice.message.contains("ref: .yoi/tickets/"));
|
||||
assert!(notice.message.chars().count() <= MAX_MESSAGE_CHARS + 1);
|
||||
|
||||
let expected = PromptCatalog::builtins_only()
|
||||
.expect("load prompt catalog")
|
||||
.render(
|
||||
PodPrompt::TicketEventCompanionNotice,
|
||||
TicketEventNoticeValues {
|
||||
ticket_id: ¬ice.ticket_id,
|
||||
title: &sanitize_one_line(
|
||||
"A very long title that should be bounded but still identify the ticket precisely enough for Companion",
|
||||
MAX_TITLE_CHARS,
|
||||
),
|
||||
state: "planning",
|
||||
event_kind: "state/queued->inprogress",
|
||||
summary: &sanitize_one_line(
|
||||
"Changed ticket state from queued to inprogress with a deliberately long summary that should be bounded before entering the weak notification payload and should not contain large logs",
|
||||
MAX_SUMMARY_CHARS,
|
||||
),
|
||||
ref_path: &format!(".yoi/tickets/{}/item.md", ticket_id),
|
||||
}
|
||||
.to_template(),
|
||||
)
|
||||
.expect("render prompt resource");
|
||||
assert_eq!(notice.message, bound_chars(&expected, MAX_MESSAGE_CHARS));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -392,7 +446,6 @@ mod tests {
|
|||
.await;
|
||||
assert_eq!(action, HookPostToolAction::Continue);
|
||||
let message = rx.recv().await.unwrap();
|
||||
assert!(message.contains("Ticket event notice"));
|
||||
assert!(message.contains("event: state/queued->inprogress"));
|
||||
assert!(message.contains("title: Companion event hook"));
|
||||
companion.await.unwrap();
|
||||
|
|
|
|||
|
|
@ -68,6 +68,8 @@ The following workflows are advertised resident. When a user request matches one
|
|||
|
||||
pod_orchestration_guidance_section = "{% include \"$yoi/common/pod-orchestration\" %}"
|
||||
|
||||
ticket_event_companion_notice = "{% include \"$yoi/pod/ticket_event_companion_notice\" %}"
|
||||
|
||||
spawn_pod_tool_description = """\
|
||||
Spawn a new Pod process to work on a delegated task. The spawner's write scope is reduced by the scope passed here; the spawned Pod receives its own socket and starts running `task` immediately. The spawned Pod outlives the spawner's current turn and can be contacted again through its socket path.
|
||||
|
||||
|
|
|
|||
7
resources/prompts/pod/ticket_event_companion_notice.md
Normal file
7
resources/prompts/pod/ticket_event_companion_notice.md
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
Ticket event notice (weak; auto_run=false)
|
||||
ticket: {{ ticket_id }}
|
||||
title: {{ title }}
|
||||
state: {{ state }}
|
||||
event: {{ event_kind }}
|
||||
summary: {{ summary }}
|
||||
ref: {{ ref_path }}
|
||||
Loading…
Reference in New Issue
Block a user