import { UlwLoopError } from "./runtime.js"; import type { UlwLoopToolkitSurface } from "./surface.js"; export const ULW_LOOP_CREATE_GOALS_COMMAND = 'omo-agent-toolkit ulw-loop create-goals --brief "" --json'; export function createGoalsAction(surface: UlwLoopToolkitSurface): string { return surface === "omo-senpi" ? 'agentToolkit.createGoals({ brief: "" })' : ULW_LOOP_CREATE_GOALS_COMMAND; } export interface PlanMissingRecovery { readonly message: string; readonly details?: { readonly existingSessionIds: readonly string[] }; } /** * A missing plan is either "never bootstrapped" or "bootstrapped under a different * session id"; the recovery text has to answer both without the caller guessing. */ export function planMissingRecovery( existingSessionIds: readonly string[], surface: UlwLoopToolkitSurface = "lazycodex", ): PlanMissingRecovery { const lines = [`Recovery: bootstrap the plan with \`${createGoalsAction(surface)}\`.`]; if (existingSessionIds.length === 0) return { message: lines.join("\n") }; lines.push( surface === "omo-senpi" ? `Existing ulw-loop session ids under .omo/ulw-loop/: ${existingSessionIds.join(", ")}. The SDK is bound to the current session; resume the owning session to target its plan.` : `Existing ulw-loop session ids under .omo/ulw-loop/: ${existingSessionIds.join(", ")}. Re-run with \`--session-id \` to target one of them.`, ); return { message: lines.join("\n"), details: { existingSessionIds } }; } export function planMissingError( planPath: string, existingSessionIds: readonly string[], surface: UlwLoopToolkitSurface = "lazycodex", ): UlwLoopError { const recovery = planMissingRecovery(existingSessionIds, surface); return new UlwLoopError(`No ulw-loop plan found at ${planPath}.\n${recovery.message}`, "ULW_LOOP_PLAN_MISSING", { ...(recovery.details === undefined ? {} : { details: recovery.details }), }); } export function sessionScopeRequiredMessage(flag: string, existingSessionIds: readonly string[]): string { const lines = [ "No ulw-loop session scope: neither the session env (OMO_ULW_LOOP_SESSION_ID / CODEX_SESSION_ID / CODEX_THREAD_ID / PI_SESSION_ID) nor the flag names this run, and the shared .omo/ulw-loop root is never used implicitly because every session in this directory would read and overwrite it.", `Recovery: pass the scope explicitly: \`${flag} \` (subprocess, eval, and hook contexts do not inherit the session env).`, ]; if (existingSessionIds.length > 0) { lines.push(`Existing ulw-loop session ids under .omo/ulw-loop/: ${existingSessionIds.join(", ")}.`); } return lines.join("\n"); } export function sessionIdRequiredMessage(flag: string): string { return [ `${flag} requires a non-empty value.`, "Recovery: subprocess, eval, and hook contexts do not inherit the ulw-loop session env (OMO_ULW_LOOP_SESSION_ID / CODEX_SESSION_ID / CODEX_THREAD_ID / PI_SESSION_ID),", `so pass the scope explicitly: \`${flag} \` (for example \`${flag} 01a05b8a-6763-7780-834f-319423b071ce\`).`, ].join("\n"); }