Journeys¶
A Journey is a state machine that guides a conversation through structured steps. Journeys are the core differentiator of Cognidesk — they give you deterministic control over conversation flow while still leveraging LLM flexibility.
Why state machines?¶
Most AI frameworks let the model decide what to do next. This works for simple Q&A but breaks down for structured workflows:
- Collecting required information in a specific order
- Enforcing business rules between steps
- Handling conditional paths (auth required, feature flags)
- Providing deterministic audit trails
Cognidesk journeys solve this by defining states and transitions that the runtime enforces.
Anatomy of a journey¶
const ticketStatus = agentBuilder.stateMachineJourney("ticket-status", {
condition: "Customer wants ticket status", // (1)!
examples: ["Can you check ticket ABC123?"], // (2)!
context: z.object({ // (3)!
bookingReference: z.string().optional(),
ticketStatus: z.string().optional(),
}),
});
- Natural language condition for journey activation
- Example utterances that trigger this journey
- Typed context that persists across states
States and transitions¶
const states = ticketStatus.defineStates("identify", "lookup");
const identify = states.get("identify").collect("bookingReference");
const lookup = states.get("lookup").runTool(findTicket, {
input: ({ context }) => ({ bookingReference: context.bookingReference ?? "" }),
assign: { ticketStatus: ({ output }) => output.status },
});
ticketStatus.initial(identify);
identify.transitionTo(lookup);
Guards¶
Guards conditionally allow or block journey activation:
const secured = agentBuilder.stateMachineJourney("secured-review", {
condition: "Customer needs secured review",
context: z.object({}),
guard: ({ app }) =>
app.authenticated
? { allow: true }
: { allow: false, code: "auth_required", message: "Please authenticate first." },
});
Context reuse¶
Journeys can reuse context from previous journey instances:
const status = agentBuilder.stateMachineJourney("ticket-status", {
// ...
contextReuse: {
fields: ["bookingReference"],
when: ({ previousContext, turn }) =>
previousContext.bookingReference !== undefined && turn.reusePreviousTicket === true,
},
});
Journey Events¶
Journeys can declare typed events for app-driven state changes:
const ticketSynced = ticketStatus.event("ticket.synced", {
payload: z.object({ bookingReference: z.string() }),
routing: "activeJourneyOnly",
});
lookup.on(ticketSynced).target(lookup);
Key principles¶
Design principles
- Each journey owns its own typed context
- States are explicit — no hidden transitions
- Guards run before activation, not during
- Journey events enable external systems to drive state changes