Skip to main content

converge_domain/
drafting.rs

1// Copyright 2024-2025 Aprio One AB, Sweden
2// SPDX-License-Identifier: MIT
3
4//! Deterministic drafting flow (short path).
5//!
6//! Seeds -> Signals (research notes) -> Strategies (draft output)
7
8use converge_core::{Agent, AgentEffect, Context, ContextKey, Fact};
9
10const DRAFT_RESEARCH_PREFIX: &str = "drafting_research:";
11const DRAFT_OUTPUT_PREFIX: &str = "drafting_output:";
12
13/// Drafting research agent (deterministic fallback).
14pub struct DraftingResearchAgent;
15
16impl Agent for DraftingResearchAgent {
17    fn name(&self) -> &str {
18        "DraftingResearchAgent"
19    }
20
21    fn dependencies(&self) -> &[ContextKey] {
22        &[ContextKey::Seeds]
23    }
24
25    fn accepts(&self, ctx: &Context) -> bool {
26        ctx.has(ContextKey::Seeds)
27            && !ctx
28                .get(ContextKey::Signals)
29                .iter()
30                .any(|fact| fact.id.starts_with(DRAFT_RESEARCH_PREFIX))
31    }
32
33    fn execute(&self, ctx: &Context) -> AgentEffect {
34        let summary = ctx
35            .get(ContextKey::Seeds)
36            .iter()
37            .map(|seed| seed.content.clone())
38            .collect::<Vec<_>>()
39            .join(" | ");
40
41        AgentEffect::with_facts(vec![Fact {
42            key: ContextKey::Signals,
43            id: format!("{DRAFT_RESEARCH_PREFIX}notes"),
44            content: format!("Drafting research notes: {summary}"),
45        }])
46    }
47}
48
49/// Drafting composer agent (deterministic fallback).
50pub struct DraftingComposerAgent;
51
52impl Agent for DraftingComposerAgent {
53    fn name(&self) -> &str {
54        "DraftingComposerAgent"
55    }
56
57    fn dependencies(&self) -> &[ContextKey] {
58        &[ContextKey::Signals]
59    }
60
61    fn accepts(&self, ctx: &Context) -> bool {
62        ctx.get(ContextKey::Signals)
63            .iter()
64            .any(|fact| fact.id.starts_with(DRAFT_RESEARCH_PREFIX))
65            && !ctx
66                .get(ContextKey::Strategies)
67                .iter()
68                .any(|fact| fact.id.starts_with(DRAFT_OUTPUT_PREFIX))
69    }
70
71    fn execute(&self, ctx: &Context) -> AgentEffect {
72        let notes = ctx
73            .get(ContextKey::Signals)
74            .iter()
75            .filter(|fact| fact.id.starts_with(DRAFT_RESEARCH_PREFIX))
76            .map(|fact| fact.content.clone())
77            .collect::<Vec<_>>()
78            .join("\n");
79
80        AgentEffect::with_facts(vec![Fact {
81            key: ContextKey::Strategies,
82            id: format!("{DRAFT_OUTPUT_PREFIX}v0"),
83            content: format!("Draft output (deterministic):\n{notes}"),
84        }])
85    }
86}