Skip to content

Orchestration Overview

Prod path: the orchestration packages are currently pre-1.0 (0.0.2) in this worktree. Start with Jobs and Orchestration if you are evaluating whether to adopt them.

Slingshot orchestration is a portable runtime for two related concepts:

  • Tasks: named, retryable units of work
  • Workflows: ordered sequences of tasks with optional parallel branches and durable sleeps

The point of the abstraction is that application code should not care whether runs execute in memory, SQLite, or BullMQ. You define the work once, then swap adapters as infrastructure changes.

PackageRole
@lastshotlabs/slingshot-orchestration-enginePortable task/workflow definitions, runtime, memory adapter, SQLite adapter
@lastshotlabs/slingshot-orchestrationSlingshot plugin integration, runtime lookup helpers, HTTP routes
@lastshotlabs/slingshot-orchestration-bullmqBullMQ-backed adapter for Redis deployments
@lastshotlabs/slingshot-orchestration-temporalTemporal-backed adapter and worker supervisor for distributed orchestration
Adapter / ProviderStatusNotes
MemorySupportedIn-process, non-durable
SQLiteSupportedDurable single-node execution
BullMQSupportedRedis-backed queues, scheduling, progress
TemporalSupportedDistributed workers, schedules, signals
Manifest modeSupportedHandlers-file task/workflow discovery
Trigger.devNot shipped yetPlanned follow-up provider
Step FunctionsNot shipped yetPlanned follow-up provider

Current provider support now includes @lastshotlabs/slingshot-orchestration-temporal. Trigger.dev and the other cloud-provider adapters are still follow-up work, but Temporal support is implemented in this worktree.

Define tasks and workflows in TypeScript, then compose them into a runtime or Slingshot plugin.

import { z } from 'zod';
import {
createMemoryAdapter,
createOrchestrationRuntime,
defineTask,
} from '@lastshotlabs/slingshot-orchestration-engine';
const resizeImage = defineTask({
name: 'resize-image',
input: z.object({ assetId: z.string() }),
output: z.object({ ok: z.boolean() }),
async handler(input) {
return { ok: true };
},
});
const runtime = createOrchestrationRuntime({
adapter: createMemoryAdapter({ concurrency: 10 }),
tasks: [resizeImage],
});

For Slingshot integration:

import type { MiddlewareHandler } from 'hono';
import { z } from 'zod';
import { createOrchestrationPackage } from '@lastshotlabs/slingshot-orchestration';
import { createMemoryAdapter, defineTask } from '@lastshotlabs/slingshot-orchestration-engine';
const resizeImage = defineTask({
name: 'resize-image',
input: z.object({ assetId: z.string() }),
output: z.object({ ok: z.boolean() }),
async handler() {
return { ok: true };
},
});
const requireAdmin: MiddlewareHandler = async (_c, next) => {
await next();
};
const orchestrationPlugin = createOrchestrationPackage({
adapter: createMemoryAdapter({ concurrency: 10 }),
tasks: [resizeImage],
workflows: [],
routes: true,
routeMiddleware: [requireAdmin],
});

When the adapter is passed directly into createOrchestrationRuntime(), the built-in adapters are ready to use immediately. Memory and SQLite still manage their own lifecycle as before, and the BullMQ adapter now lazily starts itself on first use so direct runtime composition is safe.

Manifest-driven orchestration is now supported.

  • You can still define tasks and workflows directly in TypeScript and pass them into the runtime.
  • createServer() loads ResolvedTask and ResolvedWorkflow exports from the handlers file or handlers directory.
  • The built-in slingshot-orchestration-engine plugin resolves task and workflow names from that registry before the plugin is instantiated.

That means orchestration definitions stay in code alongside the rest of the app config.

The orchestration domain owns:

  • task and workflow definitions
  • retries, timeouts, cancellation, progress, and run state
  • provider adapter contracts

Outer integration layers own:

  • Slingshot plugin lifecycle
  • HTTP routes
  • manifest bootstrap
  • provider SDK wiring

That split is deliberate. It keeps the runtime transport-neutral and makes future providers possible without rewriting application code.