YourTechBud
01/10/2026, 4:19 AM'action' is a type alias HistoryAction (resolves to (class SpeakAction | class WriteCodeAction | class HighlightCodeAction | class TerminalAction | class UserResponseAction)), expected class
Snippets:
baml
type HistoryAction = SpeakAction | WriteCodeAction | HighlightCodeAction | TerminalAction | UserResponseAction
// Unified template for all modes - handles all action types in HistoryAction
// Note: USER_PROMPT is filtered out before passing to BAML, so not handled here
template_string FormatPreviousActions(actions: HistoryAction[]) #"
<PREVIOUS_ACTIONS>
{% if actions | length > 0 %}
{% for action in actions %}
- type: {{ action.type }}
{% if action.type == "SPEAK" %}
speakerId: {{ action.speakerId }}
content: {{ action.content }}
tone: {{ action.tone }}
{% elif action.type == "WRITE_CODE" or action.type == "HIGHLIGHT_CODE" %}
description: {{ action.description }} # <----- The error is here
{% elif action.type == "TERMINAL" %}
command: {{ action.command }}
{% elif action.type == "USER_RESPONSE" %}
content: {{ action.content }}
{% endif %}
{% endfor %}
{% else %}
(No previous actions - just starting this section)
{% endif %}
</PREVIOUS_ACTIONS>
"#YourTechBud
01/11/2026, 6:52 PMgreg (BAML)
01/12/2026, 1:21 AMgreg (BAML)
01/12/2026, 4:09 AMYourTechBud
01/12/2026, 4:28 PM// =============================================================================
// ACTION DEFINITIONS
// =============================================================================
// All action classes for script generation (narrative and answer scripts)
// -----------------------------------------------------------------------------
// ACTION CLASSES
// -----------------------------------------------------------------------------
class SpeakAction {
type "SPEAK" @description("Dialogue from a speaker. Use for explanations, responses, and conversation.")
speakerId int @description("ID of the speaker delivering this dialogue. Use the primary instructor for answers, secondary speakers can chime in briefly.")
content string @description("The dialogue text to be spoken. Keep it conversational and natural.")
tone string @description("Styling hint for speech synthesis (e.g., 'informative', 'encouraging', 'friendly', 'curious')")
@@stream.done
}
class WriteCodeAction {
type "WRITE_CODE" @description("Describe code to write. Use sparingly - prefer HIGHLIGHT_CODE for existing code. Coding agent interprets this with live codebase context. Emit BEFORE explaining in dialogue.")
description string @description("Natural language description of what code to write and where (e.g., 'Add a messages key to the AgentState TypedDict in agent_state.py')")
@@stream.done
}
class HighlightCodeAction {
type "HIGHLIGHT_CODE" @description("Draw attention to existing code. Prefer this over WRITE_CODE when referencing code that already exists. Emit BEFORE explaining in dialogue.")
description string @description("Natural language description of which code to highlight (e.g., 'Highlight the messages key in the AgentState TypedDict in agent_state.py')")
@@stream.done
}
class TerminalAction {
type "TERMINAL" @description("Execute a terminal command. Use for running scripts, installing packages, or demonstrating CLI tools.")
command string @description("The terminal command to execute")
@@stream.done
}
class UserPromptAction {
type "USER_PROMPT" @description("Literal string USER_PROMPT")
promptType ("question" | "proceed") @description("'question' asks user for text input, 'proceed' shows a continue button")
question string @description(#"
A concise question or prompt to display to the user.
For 'question' type: the specific question being asked (e.g., 'What approach would you use to handle authentication?').
For 'proceed' type: a contextual continuation prompt (e.g., 'Ready to move on to implementing the API?').
"#)
@@stream.done
}
class UserResponseAction {
type "USER_RESPONSE" @description("Captures what the user said. Inserted by caller, not generated by BAML.")
content string @description("The user's spoken or typed response")
}
class UnitCompleteAction {
type "UNIT_COMPLETE" @description("Signals the narrative unit is complete. Emit only when all realization points have been covered (Socratic mode) or auto-appended by handler (Guided mode).")
@@stream.done
}
// -----------------------------------------------------------------------------
// TYPE UNIONS
// -----------------------------------------------------------------------------
// INPUT TYPE: What can appear in conversation history (previousActions in all functions)
// Contains AI-generated content + user responses
// Note: USER_PROMPT is filtered out before passing to BAML (it's just a repeat of the last SPEAK)
type HistoryAction = SpeakAction | WriteCodeAction | HighlightCodeAction | TerminalAction | UserResponseActiongreg (BAML)
01/12/2026, 5:18 PMif (a.type == This or a.type == That) - we don't do disjunctive narrowing at all (so our jinja typechecker doesn't know that a will have the fields that are in both This and That. I'm spiking a solution now.YourTechBud
01/12/2026, 10:36 PMgreg (BAML)
01/13/2026, 5:50 PMbaml-cli generate?YourTechBud
01/13/2026, 8:31 PMYourTechBud
01/13/2026, 8:31 PMYourTechBud
01/15/2026, 4:58 PMgreg (BAML)
01/17/2026, 2:16 AMYourTechBud
01/17/2026, 2:17 AMYourTechBud
01/17/2026, 2:17 AMgreg (BAML)
01/17/2026, 2:19 AM