Skip to content

v0.16.0 - typed workflow i/o and HITL#5

Merged
nirvanatikku merged 1 commit intomainfrom
feature/workflow-io-and-human-in-the-loop-intent-suspension
Mar 24, 2026
Merged

v0.16.0 - typed workflow i/o and HITL#5
nirvanatikku merged 1 commit intomainfrom
feature/workflow-io-and-human-in-the-loop-intent-suspension

Conversation

@nirvanatikku
Copy link
Copy Markdown
Contributor

v0.16.0 — RFC-0024 Workflow I/O Contracts + RFC-0025 Human-in-the-Loop + OpenAI Adapter Hardening

Summary

This PR ships two new protocol RFCs — typed input/output contracts for workflow phases (RFC-0024) and first-class human-in-the-loop intent suspension (RFC-0025) — plus a follow-on patch that hardens the OpenAI adapter and try demo script against the max_tokens deprecation in gpt-5+ and o-series models. All changes are fully backward compatible.


RFC-0024: Workflow I/O Contracts

Resolves RFC-0012 Open Question #4. The executor now owns all data wiring between phases; agents simply read ctx.input and return a plain dict.

What's new

outputs schema on PhaseConfig
Declare what a phase produces as a key → type mapping. Supported types: string, number, boolean, object, array, any named type from the top-level types block, or an inline {type, required: false} dict for optional fields. The legacy list-of-strings form is normalised automatically, so existing YAML continues to work.

workflow:
  analyze:
    title: Analyze document
    assign: analyst-agent
    outputs:
      summary: string
      confidence: number
      decision:
        type: ApprovalDecision   # named enum from types block
        required: false

inputs wiring on PhaseConfig
Map local key names to upstream references. The executor resolves these before invoking the agent handler and places the result in ctx.input.

  review:
    title: Human review
    assign: review-agent
    depends_on: [analyze]
    inputs:
      doc_summary: analyze.summary
      trigger_owner: $trigger.owner
    outputs:
      approved: boolean

Parse-time wiring validation
Every from_yaml() / from_string() call now validates that every phase_name.key input reference names a phase listed in depends_on and a key declared in that phase's outputs. Raises InputWiringError immediately — broken wiring is caught at startup, not at runtime.

Three executor-owned gates

Method When called What it does
resolve_task_inputs() Pre-handoff Populates ctx.input from upstream outputs, trigger payload, or initial state
validate_claim_inputs() Claim time Rejects a claim early if upstream outputs aren't ready yet
validate_task_outputs() Completion time Validates the agent's return dict against declared output types

Named error types — all exported from openintent top-level:

  • MissingOutputError — required output key absent from agent return value
  • OutputTypeMismatchError — value doesn't match declared type (primitive, named struct, or enum)
  • UnresolvableInputError — declared input ref can't be resolved from available state
  • InputWiringError — structural error detected at parse time

types block
Define reusable named struct and enum types at the top of any workflow YAML:

types:
  ApprovalDecision:
    enum: [approved, rejected, needs_review]
  DocumentSummary:
    fields: [title, body, word_count]

Incremental adoption
Phases without outputs or inputs are completely unaffected. Validation only activates for phases that declare contracts — you can adopt RFC-0024 one phase at a time.


RFC-0025: Human-in-the-Loop Intent Suspension

Agents can now suspend an intent mid-execution, ask an operator a structured question, and resume after a response arrives — all as a first-class protocol primitive.

What's new

suspended_awaiting_input lifecycle state
New IntentStatus value. The reaper and lease-expiry workers skip intents in this state; lease renewal succeeds for suspended intents so agents retain ownership across the suspension window.

BaseAgent.request_input()
The primary API. Suspends the intent, fires @on_input_requested hooks, polls for operator response, and returns the response value.

decision = await self.request_input(
    question="Refund amount exceeds policy limit. Approve?",
    response_type=ResponseType.CONFIRM,
    confidence=0.45,
    timeout_seconds=3600,
    fallback_policy="reject",
)
if decision == "yes":
    await self.approve_refund(intent)

ResponseType enumchoice, confirm, text, form

SuspensionChoice — structured operator option with value, label, description, style ("primary" / "danger" / "default"), and arbitrary metadata.

should_request_input()
Rule-based engagement-decision engine. Returns one of four modes:

Mode Meaning
autonomous High confidence, low risk — proceed without asking
request_input Moderate uncertainty — ask but don't block
require_input Low confidence or high risk — must get a response
defer Extreme risk or irreversibility — escalate to a human

POST /api/v1/intents/{id}/suspend/respond
REST endpoint for operators (or bots) to submit responses. Validates suspension_id and choice value, transitions intent back to active, emits intent.resumed, and broadcasts via SSE. Returns choice_label and choice_description in the response body.

Four new lifecycle decorators

@on_input_requested
async def notify_slack(self, suspension: SuspensionRecord): ...

@on_input_received
async def log_response(self, response: InputResponse): ...

@on_suspension_expired
async def apply_fallback(self, suspension: SuspensionRecord): ...

@on_engagement_decision
async def audit_decision(self, decision: EngagementDecision): ...

New exception types

  • InputTimeoutError — raised when fallback_policy="fail" and the suspension window expires
  • InputCancelledError — raised when a suspension is explicitly cancelled

OpenAI Adapter: max_tokensmax_completion_tokens for gpt-5+ / o-series

OpenAI deprecated max_tokens for newer model families (gpt-5*, o1*, o3*), which caused 400 errors in the try demo script and any direct adapter call using that parameter.

openai_adapter.py

Added OpenAIAdapter._requires_max_completion_tokens(model) — a static helper that matches model names against the affected families via regex. In _create_completion, if the caller passes max_tokens and the model matches, it is silently remapped to max_completion_tokens (unless the caller already supplied max_completion_tokens explicitly). Older models (gpt-4, gpt-3.5-turbo, etc.) are unaffected.

client/public/try demo script

Updated ResearchAgent._do_research and SummarizerAgent._summarize to pass max_completion_tokens directly instead of max_tokens in both the streaming and non-streaming paths.


Tests

File Tests Coverage
tests/test_workflow_io.py 93 RFC-0024 error types, YAML parsing, parse-time wiring validation, resolve_task_inputs, validate_task_outputs, validate_claim_inputs, _check_value_type (all primitives + named struct + enum), incremental adoption, package exports
tests/test_hitl.py 110 RFC-0025 models, choices, validation, decorators, engagement modes, suspend/respond endpoint
tests/test_workflow.py 46 Core workflow machinery (existing, all passing)
tests/test_adapters.py 25 OpenAI adapter max_tokens detection + remapping (7 new), existing adapter coverage

228 tests passing, zero failures.


Docs

  • docs/rfcs/0024-workflow-io-contracts.md — full RFC-0024 specification
  • docs/rfcs/0025-human-in-the-loop.md — full RFC-0025 specification
  • docs/guide/human-in-the-loop.md — HITL user guide with quick-start, decorator reference, fallback policies, and a complete refund-agent example
  • mkdocs.yml — RFC-0024 and RFC-0025 added to nav; HITL guide added to User Guide nav; announcement bar updated to v0.16.0
  • CHANGELOG.md — full v0.16.0 entry

Version bumps

  • pyproject.toml: 0.15.10.16.0
  • mcp-server/package.json: 0.15.10.16.0
  • mcp-server/src/index.ts: 0.15.10.16.0
  • openintent/__init__.py: __version__ already at 0.16.0

Checklist

  • Both RFCs implemented in openintent/workflow.py and openintent/agents.py
  • All new error types exported from openintent top-level
  • Parse-time wiring validation integrated into from_yaml() / from_string()
  • validate_task_outputs covers primitives, named structs, and enums
  • REST endpoint validates choices and returns enriched response
  • OpenAIAdapter._requires_max_completion_tokens() remaps parameter for gpt-5+ / o-series
  • try demo script uses max_completion_tokens in all call paths
  • 228 tests passing, zero failures
  • ruff check and ruff format clean
  • TypeScript tsc --noEmit clean
  • All version references updated to 0.16.0
  • Website changelog updated with both RFCs
  • Docs homepage stats updated (25 RFCs, 1,088+ tests)

@nirvanatikku nirvanatikku merged commit 3fd5a4f into main Mar 24, 2026
3 checks passed
@nirvanatikku nirvanatikku changed the title v0.16.0 - workflow i/o and HITL v0.16.0 - typed workflow i/o and HITL Mar 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant