Concepts
Template Expressions
Access state in agent prompts with template syntax
Template Expressions
Open Harness uses a simple template syntax to inject state into agent prompts.
Syntax
Expressions are wrapped in double curly braces:
prompt: "Analyze this input: {{ state.input }}"State Access
Access any field from your typed state:
type State = {
user: { name: string; email: string };
task: string;
previousResult: string | null;
};
const { agent } = createWorkflow<State>();
const myAgent = agent({
prompt: `User: {{ state.user.name }}
Email: {{ state.user.email }}
Task: {{ state.task }}
Previous: {{ state.previousResult }}`,
activateOn: ["workflow:start"],
});Nested Access
Access deeply nested properties:
prompt: `
Analysis: {{ state.analysis.summary }}
Score: {{ state.analysis.scores.overall }}
`Array Access
Access array elements by index:
prompt: `
First item: {{ state.items[0] }}
Last result: {{ state.history[state.history.length - 1] }}
`In Guard Conditions
The when guard receives a typed context object:
const reviewer = agent({
activateOn: ["code:complete"],
when: (ctx) => {
// Full TypeScript autocomplete
return ctx.state.code !== null && ctx.state.code.length > 0;
},
});Type Safety
The template syntax is evaluated at runtime, but your State type provides compile-time safety in:
whenguards (full TypeScript checking)endWhenconditions (full TypeScript checking)updatesfield (must bekeyof State)
type State = { result: string | null };
const { agent } = createWorkflow<State>();
const myAgent = agent({
updates: "result", // ✓ Type-checked: keyof State
// updates: "invalid", // ✗ Type error: not in State
});Best Practices
Keep Prompts Readable
// ✓ Good: Clear structure
prompt: `
Task: {{ state.task }}
Context:
{{ state.context }}
Previous feedback:
{{ state.feedback }}
`
// ✗ Avoid: Dense inline expressions
prompt: "Do {{ state.task }} with {{ state.context }} considering {{ state.feedback }}"Handle Null Values
Templates render null as the string "null". Consider your prompts:
// If state.previousResult might be null:
prompt: `
Previous result: {{ state.previousResult }}
`
// Renders as: "Previous result: null"
// Better: Use when guard to control activation
when: (ctx) => ctx.state.previousResult !== null,