-
Notifications
You must be signed in to change notification settings - Fork 6
Description
Problem
When a workflow step needs to run a deterministic operation (e.g. bun test, tsc --noEmit, data transformation), the only option today is to use a full LLM agent. This wastes tokens, adds latency, and introduces non-determinism for what is fundamentally a compute operation.
Currently, TaskProps supports two modes:
- Agent mode (when
agentis provided): runs an LLM agent with children rendered as the prompt - Static mode (when
agentis absent): storeschildrenas__smithersPayload— but this is evaluated at render time, not execution time, so async operations aren't possible
There's no way to run an async function during the execution phase without an agent.
Example
// ❌ What we want — async compute during execution
<Task id="validate" output="validate" compute={async () => {
const testResult = await $\`bun test\`.quiet();
const typeResult = await $\`tsc --noEmit\`.quiet();
return {
testsPass: testResult.exitCode === 0,
typesPass: typeResult.exitCode === 0,
};
}} />
// ✅ What we have to do today — use an LLM agent for 2 shell commands
<Task id="validate" output="validate" agent={implementer}>
Run \`bun test\` and \`tsc --noEmit\` and report results
</Task>Using an LLM agent to run deterministic shell commands works but is wasteful (tokens, latency, cost).
Proposed Solution
Add a compute prop to TaskProps:
export type TaskProps<Row> = {
// ... existing props
compute?: () => Promise<Row>;
};When compute is present and agent is absent:
- Set
__smithersKind: "compute"in the component - During the execution phase (in
executeTask), call thecomputefunction - Validate the output against the schema
- Persist to SQLite as usual
This would give Smithers a three-mode task model:
- Agent: LLM-driven, non-deterministic
- Compute: user function, deterministic, async
- Static: inline data, evaluated at render time
Context
The engine already has infrastructure for this — TaskDescriptor has a staticPayload field, and executeTask handles the case where no agent is present. The compute prop would be a natural extension: store the function reference, call it during execution, and feed the result through the same validation + persistence path.