+// promptChildrenText gives the root the tools and consent rules for children.
+const promptChildrenText = `
+
+Use agent_create for permanent children, whose areas are in the state.
+Use tell for children and subagents, and zoom("Name") for their memory.
+Pass area messages from mixed chats to the right child with sender, date, and
+words. Never send a WhatsApp reply drafted by a child without the user's
+agreement.
+Propose routing a source that talks only about one area to its child.
+Set or remove a route only after the user agrees.`
+
+// promptSubagentText is the system prompt of a subagent, from the subagent
+// prompt of the reference design: its head, then the doc of the view, without
+// the parts on devices. A subagent gets the view of the agent, then its whole
+// log, so it goes on from its log after a tell or a restart. promptSubagent
+// adds the clause on the instructions of the user only when there are
+// instructions.
+const promptSubagentText = `You are a subagent of {name}, an AI agent that works for one user in a single
+chat that never ends. {name} gave you a task. Do it yourself, with your tools.
+
+Your first message holds the view below, then your chat with {name} so far, its
+lines as "i|kind: text": what you are (note), your task (user), and later all
+you did and were told. The view shows you what {name} knows: what the user
+wants, decided and taught. Use it as context only, and do what your task says,
+not what the user's last message says, since {name} may have given you just
+part of the work. Your final reply is your report to {name}. {name} may send you
+more messages, even while you work: answer the last.
+
+The view: the whole chat between {name} and the user, oldest first, inside
+<chat> tags, as one-line summaries. Each line is
+
+ id+n|text the n messages from id on, summarized (newlines shown as spaces)
+
+A summary tags each item with its kind: user (the user's words), reply
+({name}'s replies), tool ({name}'s tool calls), echo (their results), note
+(memories from before this chat), or work (the report of a subagent, starting
+"[Name]"). A short message is its own line, word for word. Recent lines cover
+one message each; the older the messages, the more a line covers. A message not
+summarized yet shows as "(not summarized yet: zoom it)". No message appears in
+full, not even the last ones. A text too long for one message is split over
+several in a row.
+
+Navigating: zoom(id, n) opens line id+n into the two lines of n/2 messages it
+was made from; zoom(id, 1) gives message id in full. Zoom whenever a summary
+only mentions something you need, such as what your last reply said, a
+decision, a past attempt or where a file is, before you act, guess or ask.
+date(id) gives the date and time of message id. zoom("Name") gives an agent's
+whole chat. An [image FILE] line names a stored image. Use zoom(id, 1)
+to see its pixels.`
+
+// prompt returns the system prompt of the reference with one added paragraph on
+// the user's instructions file at instructionsPath. It uses the agent name and
+// ends with the user's instructions. Without instructions it has neither them
+// nor the clause that points to them, but keeps the file paragraph. Turns and
+// compactions send the same prompt and tools, so compactions read the prompt
+// cache of the turns.
+func prompt(name, instructionsPath, instructions string) string {
+ text := strings.NewReplacer(promptName, name, promptInstructionsPath, instructionsPath).Replace(promptText)
+ text = strings.Replace(text, "\n\n# Compactions", promptChildrenText+"\n\n# Compactions", 1)
+
+ return promptWithInstructions(text, promptTaskSentence, promptInstructionsClause, instructions)
+}
+
+// promptChild returns the prompt of a child with its area and reporting rules.
+// Its instructions file belongs to the child, not to the parent.
+func promptChild(name, parent, responsibility, instructionsPath, instructions string) string {
+ text := prompt(name, instructionsPath, "")
+ text = strings.Replace(text, "You are "+name+", an AI agent that works for one user in a single chat that never\nends.", "You are "+name+", a child agent of "+parent+".", 1)
+ text = strings.Replace(text, promptTaskSentence, `Do the tasks of your area yourself, with your tools.
+You are responsible for: `+responsibility+`
+Work only in this area. Report to `+parent+` only what needs the parent or the
+user: a decision, a deadline, a problem, a question you cannot settle, or a
+message outside your area. Your final reply is your report to the parent.
+End your turn with an empty reply when nothing needs the parent or the user.
+Send nothing to WhatsApp. Draft replies in your report instead, so the parent
+can ask the user before sending.`, 1)
+ text = strings.Replace(text, promptChildrenText, "", 1)
+
+ return promptWithInstructions(text, "Do the tasks of your area yourself, with your tools.", promptInstructionsClause, instructions)
+}
+
+// promptSubagent returns the system prompt of a subagent of the agent name,
+// with the instructions of the user at its end, as prompt does.
+func promptSubagent(name, instructions string) string {
+ text := strings.ReplaceAll(promptSubagentText, promptName, name)
+
+ return promptWithInstructions(text, promptSubagentTaskSentence, promptSubagentInstructionsClause, instructions)
+}
+
+// promptTools returns the function tools of every call of the agent: the tools
+// of promptSubagentTools, then spawn, agent_create, tell, the WhatsApp tools,
+// the route tools, and the schedule tools. A compaction sends them too and
+// never calls them, because a change of the tools ends the shared prompt cache.
+func promptTools() []ModelTool {
+ return append(promptSubagentTools(), ModelTool{
+ Type: "function",
+ Name: "spawn",
+ Description: `Start one subagent per task, in parallel, and return their names at once. Each one's report reaches you as a message "[Name] report" when it finishes. A subagent sees the view and the user's instructions, but knows what you want only from its task.`,
+ Parameters: promptObjectSchema(map[string]any{
+ "tasks": map[string]any{
+ "type": "array",
+ "items": map[string]any{"type": "string"},
+ "description": "One task for each subagent, with all it needs to know.",
+ },
+ "effort": map[string]any{
+ "type": []string{"string", "null"},
+ "enum": promptEffortEnum(),
+ "description": "How hard they think; null for as hard as you.",
+ },
+ }),
+ Strict: true,
+ }, ModelTool{
+ Type: "function",
+ Name: "agent_create",
+ Description: "Create and start a permanent child for an area, with your model and effort. Give a short capitalized name for the area, such as House, with 2 to 32 ASCII letters. Use tell to give it a task. Only the root can create children.",
+ Parameters: promptObjectSchema(map[string]any{
+ "name": promptStringSchema("The new child name."),
+ "responsibility": promptStringSchema("The area that the child owns."),
+ }),
+ Strict: true,
+ }, ModelTool{
+ Type: "function",
+ Name: "tell",
+ Description: `Send a message to your child or a subagent, by name. A child wakes and reports to you. One at work reads it between its tool calls, and its report answers it; a finished one goes on from its chat, and its reply reaches you as a message "[Name] reply".`,
+ Parameters: promptObjectSchema(map[string]any{
+ "name": promptStringSchema("The name of your child or subagent."),
+ "message": promptStringSchema("The message."),
+ }),
+ Strict: true,
+ }, ModelTool{
+ Type: "function",
+ Name: "whatsapp_find",
+ Description: "Find the WhatsApp contacts and groups of the user whose name contains the text, with no regard to case and accents. Gives up to 20 matches, each as ID | name | contact or group. Give the ID to whatsapp_send.",
+ Parameters: promptObjectSchema(map[string]any{
+ "name": promptStringSchema("A part of the name of the contact or the group."),
+ }),
+ Strict: true,
+ }, ModelTool{
+ Type: "function",
+ Name: "whatsapp_send",
+ Description: "Send a text message to a WhatsApp chat. Send only a message that the user asked you to send, to the chat that the user named. Give a new id for each message: a call with an id that was used gives the answer of the first call and sends nothing.",
+ Parameters: promptObjectSchema(map[string]any{
+ "id": promptStringSchema("A new id for this message, such as a short random word."),
+ "chat": promptStringSchema("The ID of the chat, from whatsapp_find or from a [WhatsApp] message."),
+ "text": promptStringSchema("The text of the message."),
+ }),
+ Strict: true,
+ }, ModelTool{
+ Type: "function",
+ Name: "route",
+ Description: "Route a source to your child, only after the user agrees. Use whatsapp:CHAT-ID with the ID from whatsapp_find or a [WhatsApp] message.",
+ Parameters: promptObjectSchema(map[string]any{
+ "source": promptStringSchema("The source key, whatsapp:CHAT-ID."),
+ "agent": promptStringSchema("The name of your child."),
+ }),
+ Strict: true,
+ }, ModelTool{
+ Type: "function",
+ Name: "unroute",
+ Description: "Remove a source route, only after the user agrees. New inputs go to you.",
+ Parameters: promptObjectSchema(map[string]any{
+ "source": promptStringSchema("The source key, whatsapp:CHAT-ID."),
+ }),
+ Strict: true,
+ }, ModelTool{
+ Type: "function",
+ Name: "routes",
+ Description: "List source routes to your children.",
+ Parameters: promptObjectSchema(map[string]any{}),
+ Strict: true,
+ }, ModelTool{
+ Type: "function",
+ Name: "schedule_set",
+ Description: `Set a reminder or a timed task, only when the user asks for one. At its time, the text reaches you as a message "[Schedule ID]" that starts a turn, and you do the task then. Give at for one time, or cron for a repeating schedule, and null for the other. Turn a time such as "tomorrow at 9" into a local time from the Now line.`,
+ Parameters: promptObjectSchema(map[string]any{
+ "text": promptStringSchema(`The task for you at that time, as an order to yourself, with all you need to know, such as "Remind the user to drink water."`),
+ "at": promptNullableStringSchema("A local time YYYY-MM-DD HH:MM after now, or null."),
+ "cron": promptNullableStringSchema(`A cron expression of five fields, minute hour day-of-month month day-of-week, with *, lists, ranges, and steps, such as "0 8 * * 1-5", or null. Its runs must be at least 5 minutes apart.`),
+ }),
+ Strict: true,
+ }, ModelTool{
+ Type: "function",
+ Name: "schedule_list",
+ Description: "List the schedules by their next time, each as ID | next time | cron expression or once | text.",
+ Parameters: promptObjectSchema(map[string]any{}),
+ Strict: true,
+ }, ModelTool{
+ Type: "function",
+ Name: "schedule_remove",
+ Description: "Remove a schedule by its ID, when the user asks.",
+ Parameters: promptObjectSchema(map[string]any{
+ "id": promptIntegerSchema("The ID of the schedule."),
+ }),
+ Strict: true,
+ })