# uuid=8363e43a-4d24-4189-9307-45427acb53fd
# version=1

if version\0 < 1:

    from channels import send_message_to_thread, wait_for_new_messages_on_thread, format_messages
    from llm      import agent

    {|
     | Returns a multi-line string describing the current goal stack.
     |
     | Each frame has a __caller__ which is the frame that invoked it.
     | And each frame has a __goal__ which is the goal that owns it.
     | And each goal optionally has a summary, which we collect here.
     |}
    def format_goal_stack():
        stack = []
        frame = __caller__
        while frame?:
            if (blurb = frame.__goal__.summary)?:
                append(stack, blurb)
            frame = frame.__caller__
        return joinlines(reversed(stack))

    {|
     | Get the user's answer to a question
     |}
    def get_answer_to_question(thread, question, valid_answers, instructions):
        while true:
            if length(thread.messages) > 0:
                recent = thread.messages    -- was trunc(thread.messages, 20) but "threads" are already self-contained enough we should just keep it all?
                msg = $"The current goal stack is:
                            {format_goal_stack()}

                        We need the user's answer to "{question}"

                            {instructions\''}

                        Here are the most recent {length(recent)} messages with the user in this thread:
                        
                            {format_messages(recent)}

                        - If the answer can be _safely_ inferred from what's been said so far,
                          reply with action='answer' and text='<the answer...>' (This includes
                          when the answer seems too obvious to ask.  Just reply with the answer
                          in that case--the question is being posed by dumb code.)  Rephrasing
                          or summarizing the user's answer is fine, as is collecting details
                          from the context, but _avoid adding unsupported details_.

                        - If the user indicates they do not wish to answer this question, reply
                          with action='cancel' and reason='<summary reason why>'

                        - Otherwise, reply with action='confer' and text='<whatever you need to ask or tell the user to get their answer>'

                          For dialogue with the user, please be concise and efficient, like
                          a familiar personal assistant.  Don't start questions with "could
                          you tell me" or other such extended fluff--just ask questions
                          directly!

                          Always attend to the balance between accuracy and efficiency: don't
                          make guesses or assumptions where multiple possibilities are likely
                          (better to ask), but also don't ask for confirmation on something
                          already clear enough.

                          The timestamp on a user message is the local time (and implied timezone)
                          when they sent the message--not nec. applicable to what they are talking
                          about unless they make relative time references.
                        "$
                if valid_answers?:
                    msg = $"{msg}

                            For action='answer' text must be one of:"

                                {joinlines([quote(answer) for answer in valid_answers])}
                            "$
            else:
                -- TODO: we could include the user's preferred language here...
                msg = $"We need the user's answer to "{question}"

                        This is a new thread, so just reply with action='confer' and text='<the above question, optionally paraphrased>'
                        "$

            reply = agent(msg)

            if reply.action == 'answer':
                return reply.text

            else if reply.action == 'confer':
                send_message_to_thread(thread, reply.text)
                wait_for_new_messages_on_thread(thread)

            else if reply.action == 'cancel':
                send_message_to_thread(thread, "(Aborting)")
                print("Canceling this question because {reply.text}")
                return none -- probably should raise

            else:
                send_message_to_thread(thread, "(Internal Error)")
                print("GET_ANSWER: action={reply.action} text={reply.text}")
                return none -- probably should raise