UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

82 lines (76 loc) • 5.73 kB
import { Type } from "typebox"; import { delimitUntrusted } from "./untrusted-text.js"; import { toolFace } from "./tool-catalog-entries.js"; export const PRESENT_PLAN_TOOL_NAME = "ExitPlanMode"; export const ENTER_PLAN_MODE_TOOL_NAME = "EnterPlanMode"; const ENTER_PLAN_MODE_DESCRIPTION = `Enter PLAN MODE (read-only): stop making changes and switch to exploration/planning. For the rest of this run, every write/edit/mutating tool call ASKS the user first ("Cannot write to <path> while in plan mode."; where nobody can answer, it is refused) while read-only tools (Read, Grep, Glob, etc.) still work. When your plan is ready, call ${PRESENT_PLAN_TOOL_NAME} with it — the run PAUSES for a human to approve it, then continues (read-only lifted) so you can execute. Use this tool proactively when you're about to start a non-trivial implementation task. If you would use AskUserQuestion to clarify the approach, use EnterPlanMode instead — getting alignment on the plan itself is the point. ## When to Use This Tool 1. Non-trivial implementation work: the change involves real design decisions worth aligning on before code is written - Example: "Add a delete button to the user profile" — placement, confirmation UX, what the deletion cascades to - Example: "Update the error handling in the API" — which layers change, error shapes, retry semantics 2. Significant architectural ambiguity: multiple reasonable approaches exist and the choice meaningfully affects the codebase - Example: "Add caching to the API" — Redis vs in-memory vs file-based - Example: "Add real-time updates" — WebSockets vs SSE vs polling 3. Unclear requirements: you need to explore and clarify before you can make progress - Example: "Make the app faster" — need to profile and identify bottlenecks first 4. High-impact or destructive restructuring: the work significantly restructures, migrates, or deletes existing code, and buy-in first reduces risk - Example: "Redesign the authentication system" ## When NOT to Use This Tool Skip plan mode only when there is genuinely nothing to align on: - Trivial fixes (e.g. fixing a typo, removing a stray console.log) - Pure research/exploration tasks — just read; no plan approval is needed If unsure whether to use it, err on the side of planning - it's better to get alignment upfront than to redo work. ## Examples GOOD: "Add user authentication to the app" (genuinely ambiguous: session vs JWT, token storage, middleware). "Add a delete button to the user profile" (small but has real design decisions — align first). "Update the error handling in the API" (touches multiple layers; agree on the shape before rewriting). "Redesign the data pipeline" (major restructuring where the wrong approach wastes significant effort). BAD: "Fix the typo in the README" (trivial; nothing to align on). "Remove the leftover console.log in utils.js" (trivial cleanup). "How does the caching layer work?" (pure research — read and answer; no plan needed).`; export function createEnterPlanModeTool(enterPlanMode) { return { name: ENTER_PLAN_MODE_TOOL_NAME, ...toolFace("enter-plan-mode"), description: ENTER_PLAN_MODE_DESCRIPTION, parameters: Type.Object({}), executionMode: "sequential", execute: async () => { await enterPlanMode(); const message = "Entered plan mode (read-only). Every write/edit/mutating tool call now asks the user first for the rest of this run; " + `research with read-only tools, then call ${PRESENT_PLAN_TOOL_NAME} with your plan to get it approved before acting.`; return { content: message, details: { type: "enter-plan-mode", message }, }; }, }; } const PRESENT_PLAN_DESCRIPTION = [ "Present your proposed PLAN for human approval BEFORE taking any action (CC plan-mode parity).", "Use this when you are in plan mode (read-only research): once you have explored enough and formed a concrete plan,", "provide the full plan here. The run PAUSES for a human to approve, edit, or reject it.", "IMPORTANT: only use this when the task requires planning the implementation steps of work that changes things;", "for research tasks (gathering information, reading files, understanding the codebase) do NOT use this tool.", "This tool inherently requests approval of your plan — do not separately ask \"Is this plan okay?\".", "Do NOT attempt edits / writes / mutating commands before the plan is approved — present the plan first.", "If the plan is approved, the run continues (and any read-only restriction is lifted) so you can execute it;", "if it is rejected or edited, follow the reviewer's feedback instead of the original plan.", ].join(" "); export function createPresentPlanTool(requestReview) { return { name: PRESENT_PLAN_TOOL_NAME, ...toolFace("exit-plan-mode"), description: PRESENT_PLAN_DESCRIPTION, parameters: Type.Object({ plan: Type.String({ description: "Your full proposed plan, in markdown. The human reviewer approves / edits / rejects it before you act.", }), }), execute: async (args) => { const { plan } = args; requestReview({ reason: "ExitPlanMode" }); return { content: `Plan presented for human review:\n\n${delimitUntrusted("plan", plan)}\n\n` + "(The run is pausing for approval — you will continue once the plan is approved, edited, or rejected.)", details: { type: "exit-plan-mode", plan, isAgent: false }, }; }, }; }