Documentation Index
Fetch the complete documentation index at: https://docs.idyllic.so/llms.txt
Use this file to discover all available pages before exploring further.
Human-in-the-Loop
AI applications frequently need human judgment at key decision points. A user might approve a deployment, choose between generated options, or provide feedback on a draft. Idyllic supports these patterns through state fields that signal pending input, allowing your system to wait efficiently until the user responds.
The Pattern
Set state indicating you’re waiting for input, then return from the action. The Durable Object hibernates with zero compute cost while the user decides. When they respond, their input arrives as a new action call:
export default class DocumentWriter extends AgenticSystem {
@field draft = stream<string>('');
@field status: 'idle' | 'drafting' | 'awaiting_approval' | 'published' = 'idle';
@field pendingConfirm: { message: string } | null = null;
@action()
async createDocument(topic: string) {
this.status = 'drafting';
for await (const chunk of ai.stream(`Write about: ${topic}`)) {
this.draft.append(chunk);
}
this.draft.complete();
// Signal waiting for input
this.status = 'awaiting_approval';
this.pendingConfirm = { message: 'Publish this document?' };
// Action completes—DO can hibernate
}
@action()
async respond(approved: boolean, feedback?: string) {
this.pendingConfirm = null;
if (approved) {
await this.publish();
this.status = 'published';
} else if (feedback) {
this.status = 'drafting';
this.draft.reset();
for await (const chunk of ai.stream(`Revise based on: ${feedback}`)) {
this.draft.append(chunk);
}
this.draft.complete();
this.status = 'awaiting_approval';
this.pendingConfirm = { message: 'Publish this revision?' };
}
}
}
How It Works
There’s no special pause primitive. When your action sets pendingConfirm, that broadcasts to clients. The action completes normally. The Durable Object becomes eligible for hibernation.
On the client, the component re-renders. When pendingConfirm has data, render the dialog. When the user responds, call actions.respond(), which wakes the Durable Object and continues the workflow.
The system can wait indefinitely at zero compute cost.
Confirmation UI
Render based on pendingConfirm state:
function DocumentEditor() {
const { draft, pendingConfirm, respond } = useSystem<DocumentWriter>();
return (
<div>
<div className="draft">
{draft.current}
{draft.status === 'streaming' && <Cursor />}
</div>
{pendingConfirm && (
<ConfirmDialog
message={pendingConfirm.message}
onApprove={() => respond(true)}
onReject={(feedback) => respond(false, feedback)}
/>
)}
</div>
);
}
Multi-Step Approval
Track approval stages:
@field stage: 'drafting' | 'content_review' | 'legal_review' | 'published' = 'drafting';
@field pendingConfirm: { message: string; stage: string } | null = null;
@action()
async submitForReview() {
this.stage = 'content_review';
this.pendingConfirm = { message: 'Content approved?', stage: 'content' };
}
@action()
async approveStage(approved: boolean) {
if (!approved) {
this.stage = 'drafting';
this.pendingConfirm = null;
return;
}
if (this.stage === 'content_review') {
this.stage = 'legal_review';
this.pendingConfirm = { message: 'Legal approved?', stage: 'legal' };
} else if (this.stage === 'legal_review') {
this.stage = 'published';
this.pendingConfirm = null;
await this.publish();
}
}
Timeouts
Use schedule() for timeout behavior:
@field pendingConfirm: { message: string; expiresAt: number } | null = null;
@action()
async requestApproval() {
this.pendingConfirm = {
message: 'Approve deployment?',
expiresAt: Date.now() + 24 * 60 * 60 * 1000,
};
this.schedule(24 * 60 * 60 * 1000, 'checkTimeout');
}
async checkTimeout() {
if (this.pendingConfirm && Date.now() > this.pendingConfirm.expiresAt) {
this.pendingConfirm = null;
this.status = 'timed_out';
}
}
FAQ
What if the user never responds?
The system waits indefinitely at zero cost. Implement timeouts if needed.
How long can the system wait?
Indefinitely. State persists in SQLite. Close your browser, return weeks later—the pending confirmation is still there.
Can I resume in a different browser?
Yes. State persists across sessions and devices. Same instance ID shows same state.
What about authentication?
Verify user identity in the respond action. Check connection metadata or pass user info as arguments.