Intent modes
# Intent modes
HUMΛN separates what the user said this turn from what they want built. Companion chat returns a per-turn classification (question | context | intent). The Intent-to-Capability pipeline (POST /v1/intent) turns durable goals into resolution plans, scaffolds, and provisioning.
Overview
question — user wants an answer; render text + citations.context — user is sharing session context; acknowledge only.intent — user wants something done; surface intent_action when present, or loop clarifying turns.Mis-parsing an intent turn as a question is how you get confident wrong answers instead of governed action.
Per-turn classification (Companion clients)
Every Companion response that participates in governed action includes classification. Branch before you render:
type TurnClassification = 'question' | 'context' | 'intent';interface CompanionTurn {
classification: TurnClassification;
text: string;
citations?: Array<{ uri: string; title: string }>;
intent_action?: {
tool_id: string;
params: Record;
autonomy: 'observe' | 'propose' | 'auto';
requires_approval: boolean;
reversible: boolean;
human_readable: string;
consequence: string;
provenance_scope: string;
} | null;
}
function handleTurn(turn: CompanionTurn) {
switch (turn.classification) {
case 'question':
// Render answer + citations only
return { mode: 'answer', body: turn.text, citations: turn.citations ?? [] };
case 'context':
// Acknowledge — context is persisted server-side
return { mode: 'ack', body: turn.text };
case 'intent':
if (!turn.intent_action) {
// Fuzzy intent — clarifying question; send next user message back through Companion
return { mode: 'clarify', body: turn.text };
}
// Clear intent — show approval card; call human.call only after explicit approval
return {
mode: 'propose',
body: turn.text,
approval: {
title: turn.intent_action.human_readable,
detail: turn.intent_action.consequence,
requiresApproval: turn.intent_action.requires_approval,
toolId: turn.intent_action.tool_id,
params: turn.intent_action.params,
},
};
}
}
Fourth Law: when requires_approval: true, wait for human respond — do not upgrade autonomy client-side.
Intent-to-Capability API (/v1/intent)
For durable “build this” flows (not just the current chat turn), use IntentClient from @human/sdk:
Multimodal ingest
Same brief pipeline for voice or pasted docs:
await client.intent.transcribe({
transcript: 'Keep invoice reconciliation moving every Monday morning',
source_surface: 'companion',
});await client.intent.extract({
document_text: 'Refund policy: auto-approve under $500 with finance review above',
source_surface: 'workflow_designer',
});
await client.intent.ingest({
text: 'Automate refund approvals under $500',
document_text: policyPdfText,
source_surface: 'agent_builder',
});
Shipped surfaces
| Surface | Purpose | ||
|---|---|---|---|
Companion turn classification | Per-message UX branching (question \ | context \ | intent) |
client.intent.* | Intent brief lifecycle — build, shape, compile, provision, lineage | ||
POST /v1/intent | Create brief from natural language | ||
POST /v1/intent/:id/compile | Capability-first resolution paths | ||
POST /v1/intent/:id/provision | Start provisioning from selected path | ||
GET /v1/intent/:id/lineage | Audit chain for brief → execution |
packages/sdk/src/intent.ts, apps/api/src/routes/intent/.Use cases
classification before auto-running tools; show approval cards for intent_action.intent.build → compile → scaffold for repeatable scaffolding.client.intent.adminIntentActivity() for org-scoped brief lists.intent turns; session is durable.Security considerations
DO
Branch on classification before invoking tools
Respect requires_approval and server-set autonomy on intent_action
Use intent lineage for audit when provisioning agents or bundles
DON'T
Treat intent_action as optional decoration on intent turns
Auto-execute because autonomy returned auto without checking reversible
Re-use a spent approval_id on human.call
See also
/ai/articles/companion-intent-modes