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
|
/// Trailing Pod orchestration guidance, appended when registered tools
|
||||||
/// include Pod-management capabilities.
|
/// include Pod-management capabilities.
|
||||||
PodOrchestrationGuidanceSection,
|
PodOrchestrationGuidanceSection,
|
||||||
|
/// Weak Companion Notify payload for explicit Orchestrator Ticket events.
|
||||||
|
TicketEventCompanionNotice,
|
||||||
/// LLM-facing description for the SpawnPod tool, including discovered
|
/// LLM-facing description for the SpawnPod tool, including discovered
|
||||||
/// profile selectors.
|
/// profile selectors.
|
||||||
SpawnPodToolDescription,
|
SpawnPodToolDescription,
|
||||||
|
|
@ -115,6 +117,7 @@ impl PodPrompt {
|
||||||
Self::ResidentKnowledgeSection => "resident_knowledge_section",
|
Self::ResidentKnowledgeSection => "resident_knowledge_section",
|
||||||
Self::ResidentWorkflowsSection => "resident_workflows_section",
|
Self::ResidentWorkflowsSection => "resident_workflows_section",
|
||||||
Self::PodOrchestrationGuidanceSection => "pod_orchestration_guidance_section",
|
Self::PodOrchestrationGuidanceSection => "pod_orchestration_guidance_section",
|
||||||
|
Self::TicketEventCompanionNotice => "ticket_event_companion_notice",
|
||||||
Self::SpawnPodToolDescription => "spawn_pod_tool_description",
|
Self::SpawnPodToolDescription => "spawn_pod_tool_description",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -135,6 +138,7 @@ impl PodPrompt {
|
||||||
PodPrompt::ResidentKnowledgeSection,
|
PodPrompt::ResidentKnowledgeSection,
|
||||||
PodPrompt::ResidentWorkflowsSection,
|
PodPrompt::ResidentWorkflowsSection,
|
||||||
PodPrompt::PodOrchestrationGuidanceSection,
|
PodPrompt::PodOrchestrationGuidanceSection,
|
||||||
|
PodPrompt::TicketEventCompanionNotice,
|
||||||
PodPrompt::SpawnPodToolDescription,
|
PodPrompt::SpawnPodToolDescription,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -151,6 +155,7 @@ impl PodPrompt {
|
||||||
"resident_knowledge_section",
|
"resident_knowledge_section",
|
||||||
"resident_workflows_section",
|
"resident_workflows_section",
|
||||||
"pod_orchestration_guidance_section",
|
"pod_orchestration_guidance_section",
|
||||||
|
"ticket_event_companion_notice",
|
||||||
"spawn_pod_tool_description",
|
"spawn_pod_tool_description",
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,15 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
use minijinja::Value as TemplateValue;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
use ticket::{LocalTicketBackend, TicketBackend, TicketIdOrSlug};
|
use ticket::{LocalTicketBackend, TicketBackend, TicketIdOrSlug};
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
use crate::discovery::PodDiscovery;
|
use crate::discovery::PodDiscovery;
|
||||||
use crate::hook::{Hook, HookPostToolAction, PostToolCall, ToolResultSummary};
|
use crate::hook::{Hook, HookPostToolAction, PostToolCall, ToolResultSummary};
|
||||||
|
use crate::prompt::catalog::{PodPrompt, PromptCatalog};
|
||||||
use pod_store::PodMetadataStore;
|
use pod_store::PodMetadataStore;
|
||||||
|
|
||||||
const MAX_TITLE_CHARS: usize = 96;
|
const MAX_TITLE_CHARS: usize = 96;
|
||||||
|
|
@ -89,28 +92,56 @@ fn build_ticket_event_notice(
|
||||||
.ok()?;
|
.ok()?;
|
||||||
|
|
||||||
let event_kind = sanitize_one_line(&event_kind, MAX_EVENT_KIND_CHARS);
|
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 title = sanitize_one_line(&ticket.meta.title, MAX_TITLE_CHARS);
|
||||||
let state = ticket.meta.workflow_state.as_str();
|
let state = ticket.meta.workflow_state.as_str();
|
||||||
let output_summary = sanitize_one_line(&output.summary, MAX_SUMMARY_CHARS);
|
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 ref_path = event_ref_path(ticket_id, summary.tool_name.as_str());
|
||||||
let raw_message = format!(
|
let message = render_ticket_event_notice_message(TicketEventNoticeValues {
|
||||||
"Ticket event notice (weak; auto_run=false)\n\
|
ticket_id,
|
||||||
ticket: {ticket_id}\n\
|
title: &title,
|
||||||
title: {title}\n\
|
state,
|
||||||
state: {state}\n\
|
event_kind: &event_kind,
|
||||||
event: {event_kind}\n\
|
summary: &output_summary,
|
||||||
summary: {output_summary}\n\
|
ref_path: &ref_path,
|
||||||
ref: {ref_path}",
|
})?;
|
||||||
ticket_id = ticket.meta.id.as_str(),
|
|
||||||
);
|
|
||||||
|
|
||||||
Some(TicketEventNotice {
|
Some(TicketEventNotice {
|
||||||
ticket_id: ticket.meta.id.as_str().to_string(),
|
ticket_id: ticket_id.to_string(),
|
||||||
event_kind,
|
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> {
|
fn explicit_ticket_event_kind(tool_name: &str, content: &Value) -> Option<String> {
|
||||||
match tool_name {
|
match tool_name {
|
||||||
"TicketComment" => content
|
"TicketComment" => content
|
||||||
|
|
@ -230,6 +261,29 @@ mod tests {
|
||||||
assert!(notice.message.contains("event: state/queued->inprogress"));
|
assert!(notice.message.contains("event: state/queued->inprogress"));
|
||||||
assert!(notice.message.contains("ref: .yoi/tickets/"));
|
assert!(notice.message.contains("ref: .yoi/tickets/"));
|
||||||
assert!(notice.message.chars().count() <= MAX_MESSAGE_CHARS + 1);
|
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]
|
#[test]
|
||||||
|
|
@ -392,7 +446,6 @@ mod tests {
|
||||||
.await;
|
.await;
|
||||||
assert_eq!(action, HookPostToolAction::Continue);
|
assert_eq!(action, HookPostToolAction::Continue);
|
||||||
let message = rx.recv().await.unwrap();
|
let message = rx.recv().await.unwrap();
|
||||||
assert!(message.contains("Ticket event notice"));
|
|
||||||
assert!(message.contains("event: state/queued->inprogress"));
|
assert!(message.contains("event: state/queued->inprogress"));
|
||||||
assert!(message.contains("title: Companion event hook"));
|
assert!(message.contains("title: Companion event hook"));
|
||||||
companion.await.unwrap();
|
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\" %}"
|
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_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.
|
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