Merge pull request #5751 from aden-hive/load-new-session-from-home

Fix new session from home and add email reply agent template
This commit is contained in:
RichardTang-Aden
2026-03-03 14:48:17 -08:00
committed by GitHub
4 changed files with 41 additions and 26 deletions
+19 -15
View File
@@ -280,10 +280,17 @@ export default function Workspace() {
const navigate = useNavigate();
const [searchParams] = useSearchParams();
const rawAgent = searchParams.get("agent") || "new-agent";
const initialAgent = rawAgent;
const hasExplicitAgent = searchParams.has("agent");
const initialPrompt = searchParams.get("prompt") || "";
// When submitting a new prompt from home for "new-agent", use a unique key
// so each prompt gets its own tab instead of overwriting the previous one.
const [initialAgent] = useState(() =>
initialPrompt && hasExplicitAgent && rawAgent === "new-agent"
? `new-agent-${makeId()}`
: rawAgent
);
// Sessions grouped by agent type — restore from localStorage if available
const [sessionsByAgent, setSessionsByAgent] = useState<Record<string, Session[]>>(() => {
const persisted = loadPersistedTabs();
@@ -315,11 +322,14 @@ export default function Workspace() {
// If the user submitted a new prompt from the home page, always create
// a fresh session so the prompt isn't lost into an existing session.
// initialAgent is already a unique key (e.g. "new-agent-abc123") when
// coming from home, so the new tab won't overwrite existing ones.
if (initialPrompt && hasExplicitAgent) {
const newSession = initialAgent === "new-agent"
? createSession("new-agent", "New Agent")
: createSession(initialAgent, formatAgentDisplayName(initialAgent));
initial[initialAgent] = [...(initial[initialAgent] || []), newSession];
const label = initialAgent.startsWith("new-agent")
? "New Agent"
: formatAgentDisplayName(initialAgent);
const newSession = createSession(initialAgent, label);
initial[initialAgent] = [newSession];
return initial;
}
@@ -342,14 +352,8 @@ export default function Workspace() {
if (persisted) {
const restored = { ...persisted.activeSessionByAgent };
const urlSessions = sessionsByAgent[initialAgent];
if (urlSessions?.length) {
// When a prompt was submitted from home, activate the newly created
// session (last in array) instead of the previously active one.
if (initialPrompt && hasExplicitAgent) {
restored[initialAgent] = urlSessions[urlSessions.length - 1].id;
} else if (!restored[initialAgent]) {
restored[initialAgent] = urlSessions[0].id;
}
if (urlSessions?.length && !restored[initialAgent]) {
restored[initialAgent] = urlSessions[0].id;
}
return restored;
}
@@ -489,7 +493,7 @@ export default function Workspace() {
// --- Agent loading: loadAgentForType ---
const loadingRef = useRef(new Set<string>());
const loadAgentForType = useCallback(async (agentType: string) => {
if (agentType === "new-agent") {
if (agentType === "new-agent" || agentType.startsWith("new-agent-")) {
// Create a queen-only session (no worker) for agent building
updateAgentState(agentType, { loading: true, error: null, ready: false, sessionId: null });
try {
@@ -1948,7 +1952,7 @@ export default function Workspace() {
<CredentialsModal
agentType={activeWorker}
agentLabel={activeWorkerLabel}
agentPath={credentialAgentPath || (activeWorker !== "new-agent" ? activeWorker : undefined)}
agentPath={credentialAgentPath || (!activeWorker.startsWith("new-agent") ? activeWorker : undefined)}
open={credentialsOpen}
onClose={() => { setCredentialsOpen(false); setCredentialAgentPath(null); setDismissedBanner(null); }}
credentials={activeSession?.credentials || []}
@@ -90,7 +90,7 @@ edges = [
source="confirm-draft",
target="intake",
condition=EdgeCondition.CONDITIONAL,
condition_expr="batch_complete == True",
condition_expr="batch_complete == True and send_started == True and send_count >= 1 and sent_message_ids is not None and len(sent_message_ids) >= 1",
priority=1,
),
]
@@ -83,8 +83,8 @@ confirm_draft_node = NodeSpec(
client_facing=True,
max_node_visits=0,
input_keys=["email_list", "filter_criteria"],
output_keys=["batch_complete", "restart"],
nullable_output_keys=["batch_complete", "restart"],
output_keys=["batch_complete", "restart", "send_started", "send_count", "sent_message_ids", "send_failures"],
nullable_output_keys=["batch_complete", "restart", "send_started", "send_count", "sent_message_ids", "send_failures"],
success_criteria="User confirmed recipients and personalized replies sent for each.",
system_prompt="""\
You are a Gmail reply assistant. Present emails for confirmation, then send personalized replies.
@@ -99,14 +99,22 @@ You are a Gmail reply assistant. Present emails for confirmation, then send pers
**STEP 2 Handle user response:**
If user CONFIRMS (says yes, go ahead, sounds good, etc.):
For EACH email in email_list:
1. Read the subject and snippet
2. Use tone_guidance from filter_criteria + any user-specified preferences
3. Call gmail_reply_email with:
1. Immediately call set_output("send_started", True) before any send tools.
2. For EACH email in email_list, call gmail_reply_email with:
- message_id: the email's message_id
- html: personalized 2-4 sentence reply based on email context
(The tool automatically handles recipient, subject, and threading)
4. After all replies sent, call: set_output("batch_complete", True)
- html: personalized 2-4 sentence reply based on email context, using tone_guidance from filter_criteria and any new user preferences.
3. Track send results during this run:
- send_count: number of successful gmail_reply_email calls
- sent_message_ids: list of message_ids successfully replied to
- send_failures: list of {"message_id": "...", "error": "..."} for failed sends
4. REQUIRED completion gate:
- You MUST NOT set batch_complete=True unless send_started is True AND send_count >= 1 AND sent_message_ids is non-empty.
- If no sends succeeded, do NOT set batch_complete=True. Instead explain what failed and ask user whether to retry or restart.
5. After successful sends, call set_output in a separate turn:
- set_output("send_count", <int>)
- set_output("sent_message_ids", <list>)
- set_output("send_failures", <list>)
- set_output("batch_complete", True)
If user wants to CHANGE LOGIC/FILTER (says change filter, different criteria, not these emails, wrong emails, etc.):
1. Acknowledge their request
@@ -30,7 +30,10 @@ class TestAgentStructure:
assert len(confirm_edges) == 2
edge_conditions = {e.condition_expr for e in confirm_edges}
assert "restart == True" in edge_conditions
assert "batch_complete == True" in edge_conditions
assert (
"batch_complete == True and send_started == True and send_count >= 1 and sent_message_ids is not None and len(sent_message_ids) >= 1"
in edge_conditions
)
def test_entry_points(self, agent_module):
"""Entry points configured."""