SOP Sessions

The SOP-based regime implements plan-first execution. Instead of selecting the next action at every step, it first compiles a Plan SOPlang artifact over a bounded skill space and then executes that artifact through a commands registry.

Planning Surface and Session State

SOPAgenticSession is defined in LLMAgents/SOPAgenticSession.mjs. It is initialized over a skillsDescription object, not over executable tool handlers directly. The constructor adds the reserved completion commands final_answer and cannot_complete to that planning surface automatically, normalizes planOnly, and keeps separate option groups for plan generation and interpreter execution. When a concrete execution registry is provided, the session wraps it so completion commands and interactive command results can be handled under the same bounded contract.

The session keeps explicit state for currentPlan, lastExecution, lastRunFailures, pendingTool, preparation context, and a bounded maximum number of plan attempts. The important architectural point is that the planning surface and the execution surface are related but not identical. skillsDescription is the symbolic space in which the planner composes a script. commandsRegistry is the runtime surface that actually executes the resulting commands.

When newPrompt() begins, the session may first run a bounded preparation stage. That preparation stage itself uses a SOP session and may contribute either comment-based or injected preparation context to the main plan. Once preparation is complete, the main planning cycle builds instructions through buildSOPAgenticInstructions() and then invokes the plan-generation path of the Plan SOPlang interpreter with generateOnly enabled. The result is an executable script rather than a free-form textual recommendation.

Execution Registry and Interactive Continuation

If no executable registry is present, or if planOnly is true, the session stops after plan generation and returns the current plan without executing it. Otherwise the generated script is run through Plan SOPlang Interpreter against the wrapped commands registry. This separation is what makes the regime inspectable: plan generation is explicit, and plan execution is explicit.

The wrapped execution registry intercepts the reserved completion commands directly and normalizes them into the final answer state. It also watches for structured command results that signal requiresInput or requiresConfirmation. When such a result appears, the session records the pending command, appends an awaiting_input entry to history, and stops normal execution flow. If a subsequent user prompt appears to continue that pending interaction, the session does not immediately regenerate a full plan. Instead, it builds a minimal direct plan that calls the pending command and then forwards the returned value into final_answer.

This behavior gives the SOP regime a more governed form of interactivity than a plain static plan. The plan remains the central execution artifact, but the runtime can still suspend and resume around a pending command when user confirmation or corrective input is required.

Bounded Replanning and Return Surface

After a plan executes, the session inspects the interpreter outcome and records any reported failures. If failures exist, the session may ask the planning layer to repair only the Plan SOPlang code, injecting a structured feedback block that lists failed variables and any available variable snapshot. This repair cycle is bounded by maxPlanAttempts, whose default is limited rather than open-ended. The regime therefore supports recovery, but it does not allow indefinite replanning drift.

The final answer may come directly from the reserved completion command or may be derived from the execution variables when no explicit final answer wrapper was captured. The derivation logic first checks for lastAnswer, then for several priority variable names, and finally for the sole variable when exactly one variable exists. This makes the return surface narrower and more stable than the raw interpreter state.

getPlan() returns the last generated script. getLastResult() returns the last resolved answer. getVariables() returns a richer state object than the loop regime, including lastPlan, lastAnswer, the current execution variables, and a simple active-or-idle status. The SOP regime is therefore appropriate when the reader or caller benefits from an explicit executable artifact and from more inspectable intermediate structure than a turn-by-turn deliberation loop normally provides.

const session = await llmAgent.startSOPLangAgentSession(skillsDescription, 'Prepare the report', {
    commandsRegistry,
    planOnly: false,
    maxPlanAttempts: 3,
});

const plan = await session.getPlan();
const answer = session.getLastResult();
const state = await session.getVariables();