repositories / pi-ext
pi-ext
bugabingas pi extensions
owned by admin
extensions/ultra/implementation.ts
Raw// ultra runtime: loaded only when the human invokes or completes /ultra.
import { dirname, resolve } from "node:path";
import {
createAgentSession,
DefaultPackageManager,
DefaultResourceLoader,
type ExtensionAPI,
type ExtensionCommandContext,
type ExtensionContext,
getAgentDir,
parseArgs,
SessionManager,
SettingsManager,
} from "@earendil-works/pi-coding-agent";
import { Type } from "typebox";
import { createActionSummarizer } from "./action-summarizer.ts";
import { renderUltraCall } from "./call.ts";
import { formatUsage } from "./display.ts";
import {
BUILTIN_TOOL_NAMES,
extensionLabel,
loadSubagentFlagCatalog,
makeResolveModel,
makeSessionFactory,
makeSubagentSessionManager,
type SessionFactoryDeps,
type SubagentFlagEntry,
subagentFlagPrompt,
} from "./drivers.ts";
import type { DynamicExtensionResolver } from "./dynamic-types.ts";
import { runWorkflow, type WorkflowResult } from "./engine.ts";
import {
createSessionWorkflowJournal,
type SessionWorkflowJournal,
} from "./journal.ts";
import { initialReducerState } from "./progress.ts";
import {
type BaseStepOptions,
makeStepRunner,
type RunnerContext,
resolveModelTier,
type StepDrivers,
type StepRunner,
} from "./runner.ts";
import {
effectiveModelTiers,
loadUltraSettings,
type UltraSettings,
} from "./settings.ts";
import {
DYNAMIC_EXTENSION_TOOL_NAMES,
discoverWorkflowCatalog,
missingWorkflowTools,
type WorkflowCatalog,
type WorkflowSpec,
WorkflowSpecSchema,
workflowUsesDynamicExtensions,
} from "./spec.ts";
import { span } from "./src/debug.ts";
import { renderUltraResult, UltraOverlayComponent } from "./ui.ts";
import {
type AutocompleteItem,
completeUltraCommand,
formatWorkflowToolResult,
makeToolProgressSink,
parseCommandLine,
parseUltraCommand,
resolveWorkflowInput,
} from "./wire.ts";
export interface UltraRuntime {
activateWorkflowTool(): void;
subagentFlagSection(ctx: ExtensionContext): Promise<string | undefined>;
getArgumentCompletions(
prefix: string,
projectTrusted: boolean,
): AutocompleteItem[] | null;
handleCommand(
argString: string,
ctx: ExtensionCommandContext,
): Promise<
| "completed"
| "incomplete"
| "aborted"
| "disabled"
| "invalid"
| "failed"
| "skipped"
>;
}
/** Pi's initial built-in selection when no `tools` allowlist or `defaultTools` setting applies. */
const PI_DEFAULT_ACTIVE_TOOLS: ReadonlySet<string> = new Set([
"read",
"bash",
"edit",
"write",
]);
interface SubagentExtensionSelection {
all: boolean;
paths: string[];
pathKeys: Set<string>;
}
function pathKey(path: string): string {
const absolute = resolve(path).replaceAll("\\", "/");
return process.platform === "win32" ? absolute.toLowerCase() : absolute;
}
function notifyWorkflowCompletion(
ctx: ExtensionContext,
result: WorkflowResult,
): void {
if (!ctx.hasUI) return;
const dropped = result.phases.reduce(
(count, phase) => count + phase.dropped,
0,
);
const status = result.aborted
? "aborted"
: dropped > 0
? "incomplete"
: "complete";
const usage = formatUsage(result.tokenUsage).summary;
ctx.ui.notify(
`ultra: "${result.workflow}" ${status} · ${result.phases.length} phases · ${usage}.`,
status === "complete" ? "info" : "warning",
);
}
export async function resolveSubagentExtensions(
setting: UltraSettings["subagentExtensions"],
options: {
cwd: string;
agentDir: string;
projectTrusted: boolean;
},
): Promise<SubagentExtensionSelection> {
if (setting === "all") return { all: true, paths: [], pathKeys: new Set() };
if (setting === "none") return { all: false, paths: [], pathKeys: new Set() };
const settingsManager = SettingsManager.create(
options.cwd,
options.agentDir,
{
projectTrusted: options.projectTrusted,
},
);
const packages = new DefaultPackageManager({
cwd: options.cwd,
agentDir: options.agentDir,
settingsManager,
});
const paths = new Set<string>();
for (const source of setting) {
const resolved = await packages.resolveExtensionSources([source], {
temporary: true,
});
const enabled = resolved.extensions.filter(
(extension) => extension.enabled,
);
if (!enabled.length) {
if (/^[a-z0-9][a-z0-9-]*$/u.test(source))
throw new Error(
`ultra: subagentExtensions no longer accepts bare extension nickname "${source}"; replace it with a Pi extension source path or package source.`,
);
throw new Error(
`ultra: no enabled extensions resolved from ${JSON.stringify(source)}`,
);
}
for (const extension of enabled) paths.add(extension.path);
}
return {
all: false,
paths: [...paths],
pathKeys: new Set([...paths].map(pathKey)),
};
}
export function createUltraRuntime(pi: ExtensionAPI): UltraRuntime {
const settings = (ctx: ExtensionContext): UltraSettings => {
const cfg = loadUltraSettings(pi, ctx);
return {
...cfg,
modelTiers: effectiveModelTiers(cfg, ctx.model?.provider),
};
};
// Workflow files are tiny and human-authored during live sessions. Rescan at
// each use so a newly saved workflow immediately works without `/reload`.
// Project workflows are repo-controlled: an untrusted project never
// contributes runnable workflows (see `discoverWorkflowCatalog`).
const discover = (cwd: string, projectTrusted: boolean): WorkflowCatalog =>
discoverWorkflowCatalog(cwd, projectTrusted);
const activateWorkflowTool = () => {
const active = pi.getActiveTools();
if (!active.includes("run_workflow")) {
pi.setActiveTools([...active, "run_workflow"]);
}
};
const deactivateWorkflowTool = () => {
const active = pi.getActiveTools();
if (active.includes("run_workflow")) {
pi.setActiveTools(active.filter((name) => name !== "run_workflow"));
}
};
const availableSubagentTools = (
extensions: SubagentExtensionSelection,
): string[] =>
pi
.getAllTools()
.filter(
(tool) =>
tool.name !== "run_workflow" &&
(extensions.all ||
BUILTIN_TOOL_NAMES.has(tool.name) ||
extensions.pathKeys.has(pathKey(tool.sourceInfo.path))),
)
.map((tool) => tool.name);
// One extension load per cwd and selection; children load the same set per step.
const flagCatalogs = new Map<
string,
ReturnType<typeof loadSubagentFlagCatalog>
>();
const subagentFlagCatalog = async (ctx: ExtensionContext) => {
const extensions = await resolveSubagentExtensions(
settings(ctx).subagentExtensions,
{
cwd: ctx.cwd,
agentDir: getAgentDir(),
projectTrusted: ctx.isProjectTrusted(),
},
);
const key = JSON.stringify([ctx.cwd, extensions.all, extensions.paths]);
let catalog = flagCatalogs.get(key);
if (!catalog) {
catalog = loadSubagentFlagCatalog({
DefaultResourceLoader:
DefaultResourceLoader as unknown as SessionFactoryDeps["DefaultResourceLoader"],
getAgentDir,
cwd: ctx.cwd,
loadExtensions: extensions.all,
extensionPaths: extensions.paths,
});
flagCatalogs.set(key, catalog);
// A failed load is retried on the next turn instead of being cached.
catalog.catch(() => flagCatalogs.delete(key));
}
return catalog;
};
// Children inherit this Pi's CLI flags, so a tool an extension switched off
// here at startup is switched off in every child too. Refuse before creating
// any child unless the step sets its own flags (then the child-startup check
// in drivers.ts is the authority). Pi activates its default builtins and
// every extension tool at startup; the remaining builtins (grep, find, ls,
// powershell) are inactive by default and say nothing about extensions, and
// children activate them explicitly.
const inactiveToolsError = async (
ctx: ExtensionContext,
spec: WorkflowSpec,
): Promise<string | undefined> => {
const all = new Set(pi.getAllTools().map((tool) => tool.name));
const active = new Set(pi.getActiveTools());
const activeByDefault = (name: string) =>
!BUILTIN_TOOL_NAMES.has(name) || PI_DEFAULT_ACTIVE_TOOLS.has(name);
const inactive = [
...new Set(
spec.phases
.filter((phase) => phase.step.flags === undefined)
.flatMap((phase) => phase.step.tools ?? [])
.filter(
(name) =>
name !== "run_workflow" &&
all.has(name) &&
!active.has(name) &&
activeByDefault(name),
),
),
];
if (inactive.length === 0) return undefined;
const catalog = await Promise.resolve()
.then(() => subagentFlagCatalog(ctx))
.catch((): SubagentFlagEntry[] => []);
const candidates = catalog
.filter((entry) => entry.flags.length > 0)
.map(
(entry) =>
`${entry.extension} (${entry.flags
.map(
(flag) =>
`--${flag.name}${flag.description ? `: ${flag.description}` : ""}`,
)
.join(", ")})`,
);
const shellInactive = inactive.some(
(name) => name === "bash" || name === "powershell",
);
return [
`ultra: step tools are inactive in this Pi and children inherit its flags: ${inactive.join(", ")}.`,
...(candidates.length
? [
`Possibly deactivated by an extension with startup flags: ${candidates.join("; ")}.`,
]
: []),
"Use an active alternative or set step.flags.",
...(shellInactive && active.has("nu")
? ["The nu tool is active; prefer it."]
: []),
].join(" ");
};
const workflowError = async (
ctx: ExtensionContext,
spec: WorkflowSpec,
extensions: SubagentExtensionSelection,
cfg: UltraSettings,
): Promise<string | undefined> => {
const cwd = ctx.cwd;
try {
for (const phase of spec.phases)
resolveModelTier(phase.step, cfg.modelTiers);
} catch (error) {
return error instanceof Error ? error.message : String(error);
}
const available = [
...availableSubagentTools(extensions),
...DYNAMIC_EXTENSION_TOOL_NAMES,
];
const missing = missingWorkflowTools(spec, available);
if (missing.length === 0) return inactiveToolsError(ctx, spec);
const owners = pi
.getAllTools()
.filter((tool) => missing.includes(tool.name));
const selectedEntriesForOwner = (ownerPath: string): string[] =>
extensions.paths.filter(
(path) =>
pathKey(path) !== pathKey(ownerPath) &&
pathKey(dirname(path)) === pathKey(dirname(ownerPath)),
);
const inspectionPaths = new Set<string>();
const details = [
`ultra: workflow "${spec.name}" requires unavailable tools: ${missing.join(", ")}`,
...missing.map((name) => {
const owner = owners.find((tool) => tool.name === name);
if (!owner)
return `${name}: no registered owning extension; check step.tools and installed extensions.`;
const ownerPath = owner.sourceInfo.path;
const selectedEntries = selectedEntriesForOwner(ownerPath);
if (selectedEntries.length) {
for (const path of selectedEntries) inspectionPaths.add(path);
return `${name}: loaded owning extension "${extensionLabel(ownerPath)}" at ${JSON.stringify(ownerPath)} does not match selected extension entries ${selectedEntries.map((path) => JSON.stringify(path)).join(", ")}; runtime may be stale or explicit selection differs. Reload/restart Pi and verify the selected entry; do not blindly add the obsolete loaded path.`;
}
inspectionPaths.add(ownerPath);
const label = extensionLabel(ownerPath);
return `${name}: owning extension "${label}" is excluded by ultra.subagentExtensions; include ${JSON.stringify(ownerPath)} in that setting before using its step.flags.`;
}),
];
if (inspectionPaths.size > 0) {
try {
const catalog = await loadSubagentFlagCatalog({
DefaultResourceLoader:
DefaultResourceLoader as unknown as SessionFactoryDeps["DefaultResourceLoader"],
getAgentDir,
cwd,
extensionPaths: [...inspectionPaths],
});
if (catalog.length) details.push(subagentFlagPrompt(catalog));
} catch (error) {
details.push(
`Could not inspect startup flags: ${error instanceof Error ? error.message : String(error)}`,
);
}
}
return details.join("\n");
};
const createJournal = (
ctx: ExtensionContext,
spec: WorkflowSpec,
cfg: UltraSettings,
args?: Record<string, unknown>,
) =>
createSessionWorkflowJournal({
cwd: ctx.cwd,
spec,
args,
modelRouting: spec.phases.map(({ step }) => {
const resolved = resolveModelTier(step, cfg.modelTiers);
return {
model:
resolved.model ??
(ctx.model ? `${ctx.model.provider}/${ctx.model.id}` : undefined),
thinkingLevel: resolved.thinkingLevel,
};
}),
entries: ctx.sessionManager.getBranch(),
appendEntry: (customType, data) => pi.appendEntry(customType, data),
});
// Assemble a real `StepRunner` from the injected SDK drivers + this ctx.
const buildStepRunner = (
ctx: ExtensionContext,
spec: WorkflowSpec,
cfg: UltraSettings,
journal: SessionWorkflowJournal,
extensions: SubagentExtensionSelection,
dynamicExtensions?: DynamicExtensionResolver,
): StepRunner => {
// The injected-dep types are deliberately broader than the SDK's specific
// signatures (so the factories unit-test with simple fakes); the real SDK
// values are adapted to them here at the boundary. Runtime correctness of
// these calls is the Task-10 live gate.
const drivers: StepDrivers = {
sessionFactory: makeSessionFactory({
createAgentSession:
createAgentSession as unknown as SessionFactoryDeps["createAgentSession"],
DefaultResourceLoader:
DefaultResourceLoader as unknown as SessionFactoryDeps["DefaultResourceLoader"],
getAgentDir,
cwd: ctx.cwd,
loadExtensions: extensions.all,
extensionPaths: extensions.paths,
loadSkills: cfg.subagentSkills === "all",
// Children inherit the parent's CLI extension flags, parsed exactly as Pi parses them.
inheritedFlags: parseArgs(process.argv.slice(2)).unknownFlags,
}),
resolveModel: makeResolveModel({
modelRegistry: ctx.modelRegistry,
defaultModel: ctx.model,
}),
};
const manageSubagentSession = makeSubagentSessionManager({
SessionManager,
cwd: ctx.cwd,
parentSession: ctx.sessionManager.getSessionFile(),
sessionDir: ctx.sessionManager.getSessionDir(),
retention: cfg.subagentSessionRetention,
});
const ctxLike: RunnerContext = {
newSessionManager(sessionKey, agentId) {
const retained = journal.getSession(sessionKey);
const child = manageSubagentSession({
name: `ultra ${spec.name} ${journal.id.slice(0, 8)} ${agentId}`,
retained,
});
if (
child.ref &&
(!retained ||
child.ref.id !== retained.id ||
child.ref.path !== retained.path)
)
journal.setSession(sessionKey, child.ref);
return child;
},
};
const baseOpts: BaseStepOptions = {
maxRetries: cfg.maxRetries,
schemas: spec.schemas,
modelTiers: cfg.modelTiers,
actionSummarizer: createActionSummarizer(ctx, cfg.actionSummarizerModel),
dynamicExtensionResolver: dynamicExtensions,
};
return makeStepRunner(drivers, ctxLike, baseOpts);
};
const dynamicResolver = async (
ctx: ExtensionContext,
spec: WorkflowSpec,
journal: SessionWorkflowJournal,
): Promise<DynamicExtensionResolver | undefined> => {
if (!workflowUsesDynamicExtensions(spec)) return undefined;
const { createDynamicExtensionCoordinator } = await import(
"./dynamic-catalog.ts"
);
return createDynamicExtensionCoordinator({
agentDir: getAgentDir(),
parentSessionId: ctx.sessionManager.getSessionId(),
workflowRunId: journal.id,
});
};
const prepareRun = async (
ctx: ExtensionContext,
spec: WorkflowSpec,
cfg: UltraSettings,
args: Record<string, unknown> | undefined,
extensions: SubagentExtensionSelection,
): Promise<{
journal: SessionWorkflowJournal;
dynamicExtensions?: DynamicExtensionResolver | undefined;
stepRunner: StepRunner;
}> => {
const journal = createJournal(ctx, spec, cfg, args);
const dynamicExtensions = await dynamicResolver(ctx, spec, journal);
return {
journal,
dynamicExtensions,
stepRunner: buildStepRunner(
ctx,
spec,
cfg,
journal,
extensions,
dynamicExtensions,
),
};
};
// ── `run_workflow` tool (model-callable, mid-turn, read-only onUpdate surface) ──
pi.registerTool({
name: "run_workflow",
label: "run_workflow",
exposure: "model-only",
annotations: { readOnlyHint: false },
description:
"run saved or inline multi-agent workflows. An inline spec takes precedence when both spec and name are supplied; args provide workflow inputs. " +
"return selected output, failures, usage, and fullOutputPath for complete evidence; results above 50KB or 2000 lines return a bounded file reference.",
promptSnippet: "run saved or inline multi-agent workflows.",
promptGuidelines: [
"Use run_workflow when decomposition into independent or sequential child-agent steps reduces context or verification risk.",
"Use dynamic extensions when a workflow needs a reusable, task-specific capability; read the ultra-authoring skill for how to build and load them.",
],
parameters: Type.Object({
name: Type.Optional(
Type.String({
description: "Saved workflow name (project/global/bundled).",
}),
),
spec: Type.Optional(WorkflowSpecSchema),
args: Type.Optional(
Type.Record(Type.String(), Type.Unknown(), {
description: "Workflow inputs, available as {args.NAME}.",
}),
),
}),
renderCall: renderUltraCall,
renderResult: renderUltraResult,
async execute(_toolCallId, params, signal, onUpdate, ctx) {
const finish = span?.("workflow.execute");
try {
const cfg = settings(ctx);
if (!cfg.enabled) {
finish?.("finish", { outcome: "disabled" });
return {
content: [
{
type: "text",
text: "ultra is disabled (ultra.enabled = false).",
},
],
details: initialReducerState(),
};
}
const catalog = discover(ctx.cwd, ctx.isProjectTrusted());
const { spec, args } = resolveWorkflowInput(
params,
catalog.available,
undefined,
cfg.fanoutToolAllowlist,
catalog.refused,
);
const extensions = await resolveSubagentExtensions(
cfg.subagentExtensions,
{
cwd: ctx.cwd,
agentDir: getAgentDir(),
projectTrusted: ctx.isProjectTrusted(),
},
);
const error = await workflowError(ctx, spec, extensions, cfg);
if (error) throw new Error(error);
const prepared = await prepareRun(ctx, spec, cfg, args, extensions);
// onProgress folds into the reducer + pushes through `onUpdate`; headless
// (no `onUpdate`) degrades to a no-op sink that still tracks final state.
const progress = makeToolProgressSink(onUpdate ?? (() => {}), {
workflowName: spec.name,
workflowDescription: spec.description,
});
const result = await runWorkflow(spec, args, {
stepRunner: prepared.stepRunner,
concurrency: cfg.concurrency,
journal: prepared.journal,
dynamicExtensions: prepared.dynamicExtensions,
signal, // the SDK execute() signal: stops the pool + fans session.abort()
onProgress: progress.sink,
});
const output = await formatWorkflowToolResult(result);
notifyWorkflowCompletion(ctx, result);
finish?.("finish", {
outcome: result.aborted
? "aborted"
: result.phases.some((phase) => phase.dropped > 0)
? "incomplete"
: "completed",
});
return {
content: [{ type: "text", text: output.text }],
details: {
...progress.finish({
...result,
fullOutputPath: output.fullOutputPath,
}),
fullOutputPath: output.fullOutputPath,
},
terminate: false, // the agent keeps reasoning over the results
};
} catch (error) {
finish?.("error", { outcome: "failed" });
throw error;
}
},
});
deactivateWorkflowTool();
const sendExecPrompt = (request: string) => {
activateWorkflowTool();
pi.sendUserMessage(`# ultra exec\n\n# user request\n\n${request}`);
};
// ── `/ultra [exec | run]` command ──
return {
activateWorkflowTool,
/** Undefined when no sub-agent extension owns both tools and flags, or loading failed. */
subagentFlagSection: async (ctx: ExtensionContext) => {
const catalog = await subagentFlagCatalog(ctx).catch(() => []);
return catalog.length ? subagentFlagPrompt(catalog) : undefined;
},
getArgumentCompletions: (prefix: string, projectTrusted: boolean) =>
completeUltraCommand(
discover(process.cwd(), projectTrusted).available,
prefix,
),
handleCommand: async (argString: string, ctx: ExtensionCommandContext) => {
const cfg = settings(ctx);
if (!cfg.enabled) {
if (ctx.hasUI)
ctx.ui.notify(
"ultra is disabled (ultra.enabled = false).",
"warning",
);
return "disabled";
}
// The command surface is the focus-grabbing overlay — interactive only.
// Headless / non-UI callers use the `run_workflow` tool instead.
if (!ctx.hasUI) {
return "skipped";
}
const command = parseUltraCommand(argString);
if (command.subcommand === "activate") {
activateWorkflowTool();
ctx.ui.notify("ultra: run_workflow tool enabled.", "info");
return "completed";
}
if (command.subcommand === "exec") {
sendExecPrompt(command.instructions);
return "completed";
}
if (command.subcommand === "invalid") {
ctx.ui.notify(
"Usage: /ultra | /ultra exec <instructions> | /ultra run <workflow> [args]",
"warning",
);
return "invalid";
}
const catalog = discover(ctx.cwd, ctx.isProjectTrusted());
const parsed = parseCommandLine(command.invocation);
let resolved: {
spec: WorkflowSpec;
args?: Record<string, unknown> | undefined;
};
try {
resolved = resolveWorkflowInput(
{ name: parsed.name, args: parsed.args },
catalog.available,
parsed.rest,
cfg.fanoutToolAllowlist,
catalog.refused,
);
} catch (err) {
ctx.ui.notify(
err instanceof Error ? err.message : String(err),
"error",
);
return "invalid";
}
const { spec, args: runArgs } = resolved;
let extensions: SubagentExtensionSelection;
try {
extensions = await resolveSubagentExtensions(cfg.subagentExtensions, {
cwd: ctx.cwd,
agentDir: getAgentDir(),
projectTrusted: ctx.isProjectTrusted(),
});
} catch (error) {
ctx.ui.notify(
error instanceof Error ? error.message : String(error),
"error",
);
return "failed";
}
const error = await workflowError(ctx, spec, extensions, cfg);
if (error) {
ctx.ui.notify(error, "error");
return "invalid";
}
const prepared = await prepareRun(ctx, spec, cfg, runArgs, extensions);
// The command owns its AbortController (idle ⇒ ctx.signal is undefined).
// Global Esc / per-agent abort fire it; the engine fans session.abort().
const controller = new AbortController();
const outcome = await ctx.ui.custom<
| { status: "completed"; result: WorkflowResult }
| { status: "aborted"; result?: WorkflowResult }
| { status: "failed"; message: string }
| null
>(
(tui, theme, keybindings, done) => {
const overlay = new UltraOverlayComponent({
host: tui,
theme,
onRunAbort: () => controller.abort(),
workflowName: spec.name,
workflowDescription: spec.description,
toolUi: tui,
toolsExpanded: () => ctx.ui.getToolsExpanded(),
hideToolPreviews: cfg.actionSummarizerModel !== null,
isToolsExpandKey: (data) =>
keybindings.matches(data, "app.tools.expand"),
onToolsExpand: () =>
ctx.ui.setToolsExpanded(!ctx.ui.getToolsExpanded()),
cwd: ctx.cwd,
});
// P3: return the Component SYNCHRONOUSLY; run the engine DETACHED, or
// the overlay never renders. `done` resolves the custom() promise.
void (async () => {
try {
const r = await runWorkflow(spec, runArgs, {
stepRunner: prepared.stepRunner,
concurrency: cfg.concurrency,
journal: prepared.journal,
dynamicExtensions: prepared.dynamicExtensions,
signal: controller.signal,
onProgress: (e) => overlay.handleProgress(e),
});
done(
controller.signal.aborted
? { status: "aborted", result: r }
: { status: "completed", result: r },
);
} catch (error) {
done(
controller.signal.aborted
? { status: "aborted" }
: {
status: "failed",
message:
error instanceof Error ? error.message : String(error),
},
);
}
})();
return overlay;
},
{
overlay: true,
overlayOptions: {
anchor: "bottom-left",
width: "100%",
maxHeight: "100%",
},
},
);
if (!outcome) {
ctx.ui.notify(`ultra: "${spec.name}" aborted.`, "warning");
return "aborted";
}
if (outcome.status === "failed") {
ctx.ui.notify(outcome.message, "error");
return "failed";
}
const result = outcome.result;
if (!result) {
ctx.ui.notify(`ultra: "${spec.name}" aborted.`, "warning");
return "aborted";
}
const output = await formatWorkflowToolResult(result);
pi.sendMessage({
customType: "ultra-result",
content: output.text,
display: true,
details: { ...result, fullOutputPath: output.fullOutputPath },
});
notifyWorkflowCompletion(ctx, result);
return result.aborted
? "aborted"
: result.phases.some((phase) => phase.dropped > 0)
? "incomplete"
: "completed";
},
};
}