Schedules
Schedules often start bounded work such as daily summaries, recurring reports, data synchronization, or cleanup. Model that work as a Workflow and admit each occurrence with invoke(...). Every occurrence gets an independent runId, lifecycle events, and run history.
Use dispatch(...) instead when the schedule is an event for an intentionally continuing Agent instance. Reusing the same agent instance ID also reuses its persistent session, which is useful only when successive occurrences should share conversation state.
Bapx does not prescribe a scheduling library. Use the scheduler provided by your deployment environment and choose Workflow invocation or Agent dispatch based on the work’s lifetime.
Scheduling a Workflow on Cloudflare
Add a Cron Trigger to your project’s wrangler.jsonc:
{
"triggers": {
"crons": ["0 9 * * *"],
},
}
Then import the discovered Workflow’s default export and invoke it from src/cloudflare.ts:
import { invoke } from '@bapX/runtime';
import dailySummary from './workflows/daily-summary.ts';
export default {
async scheduled(controller: ScheduledController) {
await invoke(dailySummary, {
input: {
prompt: 'Review recent activity and prepare the daily summary.',
scheduledAt: new Date(controller.scheduledTime).toISOString(),
},
});
},
};
invoke(...) resolves after the Workflow Run is admitted and returns its runId; it does not wait for completion. The Workflow does not need to export an HTTP route. Cron Triggers use UTC. See Cloudflare’s scheduled() handler documentation for the complete API.
Scheduling a Workflow on Node.js
Node.js does not include a built-in cron scheduler, so choose an ecosystem option that fits how your application is deployed. This example uses Croner, a lightweight scheduler with async callbacks, overlap protection, and timezone support:
import { invoke } from '@bapX/runtime';
import { Cron } from 'croner';
import dailySummary from './workflows/daily-summary.ts';
new Cron(
'0 9 * * *',
{
protect: true,
timezone: 'UTC',
catch: (error) => console.error('Scheduled workflow admission failed', error),
},
async () => {
await invoke(dailySummary, {
input: {
prompt: 'Review recent activity and prepare the daily summary.',
scheduledAt: new Date().toISOString(),
},
});
},
);
For production schedules that must survive restarts or coordinate across replicas, use a persistent scheduler such as BullMQ. An in-process scheduler like Croner only runs while that Node process is alive.
Scheduling input for a continuing Agent
Use dispatch(...) when scheduled occurrences should enter one persistent Agent session:
import { dispatch } from '@bapX/runtime';
import dailySummary from './agents/daily-summary.ts';
await dispatch(dailySummary, {
id: 'daily-summary',
message: {
kind: 'signal',
type: 'schedule',
body: 'Review recent activity and prepare the daily summary.',
attributes: { scheduledAt: new Date().toISOString() },
},
});
The stable id means every occurrence targets the same AgentInstance and conversation. Dispatched work returns a dispatchId and does not create Workflow Run history.
Next steps
- Workflows — define finite scheduled operations and inspect their runs.
- Agents — define a continuing agent that receives scheduled input.
- Cloudflare — configure the Cloudflare target and
cloudflare.tsentrypoint. - Node.js — build and operate the generated Node.js server.