Releases: cognesy/instructor-php
Release list
v2.4.1
title: 'v2.4.1'
What's Fixed
v2.4.1 is a corrective patch release for the v2.4.0 cleanup line.
It restores out-of-the-box provider configuration discovery when Instructor packages are
used from another project. The v2.4.0 config cleanup made preset lookup depend too much on
the consumer application's base path, which meant common calls such as
Inference::using('openai'), LLMProvider::using('openai'), and
StructuredOutput::using('openai') could fail outside the monorepo unless the consuming
project copied Instructor's package configuration files.
This release makes bundled package presets discoverable from the installed package again.
Structured Output Dependency
twig/twig is now declared as a runtime dependency of cognesy/instructor-struct, not
only as a development dependency. This fixes structured prompt rendering in standalone
consumer installs.
Release Validation
The public API surface remains compatible with v2.4.0. The patch also stabilizes the
public API and response-model regression fixtures across supported PHP and Symfony
dependency combinations, so the full CI matrix now passes before publication.
Upgrade Notes
No code changes are required. Upgrade all Instructor packages together to v2.4.1.
v2.4.0
title: 'v2.4.0'
What's New
v2.4.0 is an internal architecture and dependency-hygiene release. It introduces a new
low-level package, cognesy/instructor-contracts, and uses it to break a circular
dependency between cognesy/instructor-struct and cognesy/instructor-dynamic. It also
repairs a set of cross-package dependency declarations across the monorepo so that each
package now declares exactly what it uses — which makes standalone (split-package)
installs resolve correctly.
There are no breaking changes to public APIs. Class names and namespaces are unchanged;
the moved contracts keep their existing Cognesy\Instructor\… namespaces.
New package: cognesy/instructor-contracts
A new foundation package holds the small, dependency-light abstractions that several
Instructor packages share — validation, transformation, and deserialization contracts
plus their value objects. It depends only on cognesy/instructor-utils.
Moved into the package (namespaces preserved, so no consumer code changes):
Cognesy\Instructor\Validation\ValidationResult,ValidationErrorCognesy\Instructor\Validation\Contracts\CanValidateSelf,CanValidateObject,CanValidateValueCognesy\Instructor\Transformation\Contracts\CanTransformSelf,CanTransformDataCognesy\Instructor\Deserialization\Contracts\CanDeserializeClass,CanDeserializeSelf
The response-level orchestration classes (ResponseValidator, ResponseTransformer,
and the *Response contracts) remain in cognesy/instructor-struct.
Broken dependency cycle: instructor-struct ↔ dynamic
Previously instructor-struct and dynamic depended on each other in code (each imported
the other's classes), which only worked because the monorepo autoloads every namespace.
Both now depend on cognesy/instructor-contracts instead, removing the cycle:
dynamicno longer depends oninstructor-struct.dynamicdeserializes nested concrete-class fields through theCanDeserializeClass
contract, with a self-contained Symfony-based default implementation
(SymfonyStructureDeserializer).instructor-struct'sSymfonyDeserializeris unchanged.
Cross-package dependency repairs
cognesy/loggingnaming. Six packages required a non-existent
cognesy/instructor-logging; they now correctly requirecognesy/logging. The
Laravel package's stale^1.22constraint on it is aligned to the current line.- Undeclared dependencies. Packages that used a sibling's classes without declaring a
Composer dependency on it now declare it, across 14 packages. This is invisible inside
the monorepo but is required for correct standalone installs. utils↔pipelinecycle.Cognesy\Utils\Strno longer depends on thepipeline
package; its case-normalization is now dependency-free. The Pipeline-based variant is
available asCognesy\Pipeline\Extras\StringNormalizer.
Upgrade notes
No action required. All changes preserve existing class names, namespaces, and public
behavior. Standalone consumers of individual cognesy/* packages benefit from the
corrected dependency metadata automatically.
v2.3.1
title: 'v2.3.1'
What's Fixed
v2.3.1 fixes a Laravel testing regression in StructuredOutput::fake().
If your application constructor-injects Cognesy\Instructor\StructuredOutput, activating the Laravel fake now keeps that dependency type-safe instead of swapping the binding to an incompatible fake object.
v2.3.0
title: 'v2.3.0'
What's New
v2.3.0 introduces the first substantial Symfony integration baseline for InstructorPHP.
The monorepo now includes a first-party Cognesy\Instructor\Symfony\ bundle surface with
framework-owned configuration, service wiring, delivery seams, observability, and testing support.
This release also broadens Symfony 8 compatibility in the root package, tightens the Symfony-facing
logging path, and expands the framework documentation significantly.
Symfony Integration
First-party bundle surface in the monorepo
The main package now autoloads Cognesy\Instructor\Symfony\ and includes the initial Symfony
bundle/configuration baseline directly in the monorepo. The package now owns:
- one
instructorconfig root with explicit subtrees - container bindings for core runtime services
- framework-native wiring for inference, embeddings, and structured output
- AgentCtrl runtime seams for HTTP, CLI, and Messenger flows
- native-agent registry, schema, tool, capability, and session wiring
This is the first release where Symfony stops looking like scattered integration glue and starts
looking like a supported framework surface.
Delivery, progress, telemetry, and sessions
packages/symfony now includes explicit delivery seams for queued AgentCtrl prompts, queued
native-agent prompts, runtime observation forwarding, CLI observation formatting, progress updates,
and package-owned telemetry/exporter lifecycle wiring.
Session persistence is also now explicit under instructor.sessions, with built-in in-memory and
file-backed paths for native agents.
Testing and migration guidance
Symfony now ships a much broader test harness and documentation set, including quickstart,
configuration, runtime surfaces, sessions, telemetry, logging, delivery, operations, testing, and
migration guides.
That gives Symfony users a clearer path for both greenfield adoption and migration from custom
bundle glue.
Logging And Observability
Symfony-facing logging ownership moved further toward the framework package in this release.
packages/symfony now includes package-owned logging factory/wiring, while packages/logging
adds correlation enrichment that merges telemetry and runtime identifiers more consistently.
The legacy standalone Symfony logging bundle path remains present, but it is now documented as a
deprecated compatibility path in favor of instructor.logging and the first-party Symfony package.
Compatibility
Symfony 8 and DocBlock 6
The root package now allows phpdocumentor/reflection-docblock:^5.6 || ^6.0.
This removes the Composer conflict that blocked Symfony 8 applications already pinned to
phpdocumentor/reflection-docblock:^6, which was the main compatibility issue reported after the
v2.2.0 release.
Documentation And QA
This release adds a large amount of framework documentation and also restores docs QA coverage that
had regressed in several packages.
That work is mostly invisible in runtime behavior, but it matters for release quality: the docs
snippets now lint cleanly again across the release-notes/docs QA sweep.
Upgrade Notes
- If you are integrating with Symfony, start from the new
packages/symfony/docs/guides and theinstructorconfig root instead of building custom container glue around lower-level packages. - If you depend on the legacy Symfony logging bundle wiring from
packages/logging, treat it as a compatibility path. The forward-looking integration surface is the Symfony package plusinstructor.logging. - If your Symfony 8 application already requires
phpdocumentor/reflection-docblock:^6, this release removes the root-package conflict and should install cleanly. - The Symfony integration now ships in the monorepo package. Dedicated split-package publication for
cognesy/instructor-symfonyfollows the normal split/bootstrap flow and may lag behind the monorepo release momentarily.
v2.2.0
title: 'v2.2.0'
What's New
v2.2.0 adds deeper telemetry across the stack. More parts of the system now share stable IDs,
correlation data, and smaller event payloads, so it is easier to trace one run from agent control,
through structured output and inference, down to HTTP.
There are also a few user-facing changes: hub can now filter examples by tag, streamed tool events
in agent-ctrl are more accurate, and agent stop handling is more predictable.
Agents And Agent Control
Telemetry from runs, tools, and subagents
agent-ctrl now assigns one executionId to each run and keeps it through the whole bridge flow.
That run can now be traced more clearly in events and in the final response.
Streamed tool events for Claude Code and Gemini were also reworked. Tool-use events now wait for the
matching tool result, so they report the real tool name, input, output, and error state instead of a
placeholder.
Cleaner stop handling in agents
AgentLoop now checks whether execution should stop before the first step and again after beforeStep.
This lets hooks stop a run cleanly without forcing another model or tool cycle.
When multiple stop signals exist, agents now picks the highest-priority reason instead of the first
one that happened to be recorded. That makes the final stop reason more consistent.
Tool calls and subagents also carry stable IDs and trace data, which makes it easier to connect parent
and child work in telemetry and logs.
Structured Output, Inference, And HTTP
End-to-end telemetry in instructor and polyglot
instructor now emits structured-output events with stable request, execution, and attempt IDs.
Streaming and non-streaming runs also report the same kind of response summary data, including finish
reason, token usage, and tool-call counts.
polyglot now carries telemetry correlation from InferenceRequest down into the underlying
HttpRequest. Stream finalization was also tightened up, so completed and failed streamed runs update
execution state more reliably.
Better request correlation in HTTP layers
http-client and http-pool now keep requestId attached more consistently, including pooled
responses and streamed HTTP events. This makes it easier to match a response or stream event back to
the request that created it, and to keep one trace chain across higher-level and lower-level layers.
Failure events in instructor and polyglot were also cleaned up so they report smaller, safer
summaries instead of large raw payloads.
Hub
hub list now supports --tag and --tags, and examples can carry normalized tag metadata. This
makes it easier to find examples by topic without scanning the whole catalog.
Upgrade Notes
- If you read raw event payloads from
agent-ctrl,agents,instructor,polyglot,http-client, orhttp-pool, review them before upgrading. Several events now include new IDs, summary fields, and different payload shapes. - In
agents, the final stop reason may differ from earlier releases because stop signals are now resolved by priority. - In
agent-ctrl, some streamed tool events are emitted later than before, but they now contain the actual tool result data.
v2.1.0
title: 'v2.1.0'
What's New
SessionRuntime::create() — single entry point for session creation
SessionRuntime now exposes a create(AgentDefinition $definition, ?AgentState $seed = null) method
that handles the full session creation lifecycle: instantiation, hook processing, persistence, and
event emission — all in one call.
Before:
$stateFactory = new DefinitionStateFactory();
$sessionFactory = new SessionFactory($stateFactory);
$session = $repo->create($sessionFactory->create($definition));After:
$session = $runtime->create($definition);The new method runs through the same hook and event pipeline as execute(), so session controllers
fire consistently for both creation and updates.
Dedicated BeforeCreate / AfterCreate hook stages
The AgentSessionStage enum gains two new cases: BeforeCreate and AfterCreate.
During SessionRuntime::create(), hooks fire in this order:
BeforeCreate— create-only pre-persist logic (e.g. set defaults, assign IDs)BeforeSave— shared pre-persist logic (fires on both create and execute)- persist
AfterSave— shared post-persist logic (fires on both create and execute)AfterCreate— create-only post-persist logic (e.g. send notifications)
The execute() pipeline is unchanged — it continues to fire only BeforeSave / AfterSave.
This layered approach (inspired by Eloquent's creating/saving/saved/created events)
lets hooks distinguish between first-time creation and subsequent updates.
SessionFactory is now injectable
SessionRuntime accepts an optional ?SessionFactory constructor parameter. It defaults to
new SessionFactory(new DefinitionStateFactory()) when omitted, so existing call sites are
unaffected — but custom state factories can now be injected for testing or advanced use cases.
CanManageAgentSessions contract updated
The CanManageAgentSessions interface now includes create():
interface CanManageAgentSessions
{
public function create(AgentDefinition $definition, ?AgentState $seed = null): AgentSession;
public function listSessions(): SessionInfoList;
public function getSessionInfo(SessionId $sessionId): AgentSessionInfo;
public function getSession(SessionId $sessionId): AgentSession;
public function execute(SessionId $sessionId, CanExecuteSessionAction $action): AgentSession;
}When to use SessionFactory + repo directly
SessionFactory and SessionRepository::create() remain available for the one case where
you already have a fully constructed AgentSession — forking. ForkSession returns a
concrete session instance, so you persist that branch via $repo->create($forked) rather
than going through SessionRuntime::create().
v2.0.0
What's New
v2.0 is a ground-up rework focused on performance, type safety, and a tighter API surface.
- Faster, leaner — streaming is more memory efficient, execution is lazy by default,
and internal code paths have been consolidated. - Tighter APIs — both Instructor and Polyglot have cleaner, more type-safe public APIs
with explicit fields instead of mode-based configuration. - More reliable — extensive unit, feature, integration tests and benchmarks back this
release. A substantial number of bugs have been fixed across the stack. - Agent building blocks — new
cognesy/agentspackage for custom agents, and an expanded
cognesy/agent-ctrlto interact with CLI coding agents from PHP.
Breaking Changes
- Instructor's public API centers on
StructuredOutput,StructuredOutputRuntime,
PendingStructuredOutput,StructuredOutputResponse, andStructuredOutputStream. - Polyglot uses explicit LLM API fields (
responseFormat,tools,toolChoice) instead
of output modes. Streaming moves tostream()->deltas().
Instructor
- Execution flows through
StructuredOutputRuntime, with lazy execution viaPendingStructuredOutput. - Streaming is Instructor-owned:
StructuredOutputStreamexposesresponses(),partials(),
andsequence().StructuredOutputStreamStateaccumulates state internally. - Configuration, validation, transformation, deserialization, and extraction are explicit
parts of the runtime rather than scattered across older code paths.
Polyglot
InferenceRuntimeandEmbeddingsRuntimesit behind theInferenceandEmbeddingsfacades.- New inference drivers:
openai-responses,openresponses,glm,qwen. - Built-in pricing via
Pricing\Cost, per-model pricing objects, and cost calculators. - Polyglot handles raw transport — structured value ownership belongs to Instructor.
Agents (cognesy/agents)
New package. Building blocks for custom agents: AgentLoop, AgentBuilder, hooks, guards,
templates, subagents, skills, and SessionRuntime for persisted workflows.
Compose agents from capabilities like bash, file tools, structured outputs, summarization,
self-critique, planning, execution history, and broadcasting.
Two built-in drivers: ToolCallingDriver (native tool-calling APIs) and ReActDriver
(Thought/Action/Observation loops).
AgentCtrl (cognesy/agent-ctrl)
Unified PHP API for interacting with CLI coding agents — Claude Code, Codex, OpenCode,
Pi (new), and Gemini (new).
AgentCtrl::make()and dedicated builders (::codex(),::openCode(),::pi(),
::gemini()) return normalizedAgentResponseobjects.- Streaming callbacks, session resume/continue, typed IDs (
AgentSessionId,
AgentToolCallId), andAgentCtrlConsoleLoggerfor observability.
Migrating from v1.x
- Instructor: use
->get(),->response(), and->stream(). For partial snapshots,
switch tostream()->responses(),stream()->partials(), orstream()->sequence(). - Polyglot: replace mode-based JSON/tool config with
responseFormat,tools,
toolChoice, and delta-based streaming. - See
packages/instructor/docs/upgrade.mdandpackages/polyglot/docs/upgrade.mdfor details.
v1.22.0
title: 'v1.22.0'
Highlights
- Agent builder and capability namespaces are reorganized into
Cognesy\Addons\AgentBuilder\*, with agent templates inCognesy\Addons\AgentTemplate\*. - Structured extraction is reworked with
ExtractionInput,ResponseContent, and streaming-friendlyExtractingBuffer+PartialJsonExtractor. - Hub example discovery now supports configurable sources and grouping via YAML config files.
Breaking Changes
- Agent builder, capabilities, and tool registry classes moved from
Cognesy\Addons\Agent\*toCognesy\Addons\AgentBuilder\*. - Agent template definitions, registries, and blueprints moved to
Cognesy\Addons\AgentTemplate\*;AgentContractis nowAgentInterfaceandfromConfig()returns anAgentInterface(noResultwrapper). - Task planning capability (
UseTaskPlanning,Todo*classes) andLlmQueryToolwere removed. - Instructor extraction contracts changed:
CanExtractResponse::extract()now acceptsExtractionInputand returns an array, throwing on failure.CanExtractContent,CanParseContent,DataFormat,JsonParser, andExtractingJsonBufferwere removed.
Addons / Agents
- Agent builder moved to
Cognesy\Addons\AgentBuilder\AgentBuilderwith capabilities underCognesy\Addons\AgentBuilder\Capabilities\*. - New
SubagentProvider/SubagentDefinitioncontracts andEmptySubagentProvider;AgentTemplate\Registry\AgentRegistryimplementsSubagentProvider. - Agent template registry and definition flow now live under
Cognesy\Addons\AgentTemplate\*with explicit blueprint exceptions.
Instructor
ResponseExtractornow consumesExtractionInputand usesResponseContentfor tool-mode extraction.- Streaming extraction uses
ExtractingBufferandPartialJsonExtractorfor more resilient partial JSON handling.
Hub
- Example sources can be configured via
config/examples.yaml(multiple source roots supported). - Example grouping and ordering can be configured via
config/examples-groups.yaml(subgroup include/exclude rules).
Migration from v1.21.0
- Update imports to
Cognesy\Addons\AgentBuilder\*(includingAgentBuilder, allCapabilities, andCapabilities\Tools). - Move agent templates and registries to
Cognesy\Addons\AgentTemplate\*and updateAgentContractimplementations toAgentBuilder\Contracts\AgentInterfacewithfromConfig()returning anAgentInterface. - Remove task planning usage (
UseTaskPlanning,Todo*) andLlmQueryToolreferences; replace with custom tools or direct inference calls. - Update custom extractors to
CanExtractResponse::extract(ExtractionInput $input): arrayand throwExtractionException(or anyThrowable); call withExtractionInput::fromResponse(...)orExtractionInput::fromContent(...). - Replace any direct usage of
ExtractingJsonBuffer/JsonParser/DataFormatwithExtractingBufferand the new extractor chain.
v1.21.0
title: 'v1.21.0'
Highlights
- StepResult pattern across Agent, Chat, ToolUse, and Collaboration with immutable steps and explicit continuation outcomes.
- Continuation evaluation is unified around
ContinuationEvaluationandContinuationOutcome, with clearer stop reasons. - Retry policy handling is explicit for Inference and Embeddings (no more
retryPolicyinside options). - Hub status persistence now tolerates malformed UTF-8 output instead of failing to write status data.
Breaking Changes
- Continuation interfaces
CanDecideToContinue,CanExplainContinuation, andCanProvideStopReasonwere removed. Custom criteria must implementCanEvaluateContinuationand returnContinuationEvaluation. - Error handling classes moved from
Cognesy\Addons\StepByStep\ContinuationtoCognesy\Addons\StepByStep\ErrorHandling. retryPolicyis no longer accepted inoptionsorLLMConfigoptions. Use explicit retry policy objects.- Continuation outcomes are no longer stored on step objects. Use
StepResultor state accessors instead.
Agent, Chat, ToolUse, Collaboration
- States now store
StepResultcollections and serialize them. canContinue()reads from the last step result; mismatches between steps and step results now raise a logic error.- Message compilation supports
summaryandbuffersections for inference context. - Token usage is accumulated before continuation evaluation so usage limits are accurate.
StepByStep / Continuation
ContinuationCriteriacomposesCanEvaluateContinuationcriteria and exposesevaluateAll()with aggregated outcomes.- Criteria classes return richer
ContinuationEvaluationobjects with explicit decisions and stop reasons. - New outcome helpers provide derived decision, resolver, and stop reason from evaluations.
Polyglot / Inference and Embeddings
InferenceRequestandEmbeddingsRequestnow carry retry policies explicitly.InferenceRequestBuilderand request builder traits exposewithRetryPolicy().PendingInferenceandPendingEmbeddingspull retry policies from requests rather thanoptions.
Hub
- Status JSON encoding now substitutes invalid UTF-8 bytes to avoid failures when saving example output.
Migration from v1.20.0
- Update custom continuation criteria to implement
CanEvaluateContinuationand returnContinuationEvaluation(replaceCanDecideToContinue,CanExplainContinuation,CanProvideStopReason). - Update imports to the new error handling namespace:
Cognesy\Addons\StepByStep\ErrorHandling\*. - Remove
retryPolicyfrom LLM or request options. UsewithRetryPolicy()on inference/embeddings builders or requests. - Replace any step-level continuation outcome access with
state->continuationOutcome()orstate->lastStepResult().
v1.20.0
title: 'v1.20.0'
Breaking Changes
Renames
ReverbAgentEventAdapter→AgentEventEnvelopeAdapterToolRegistryContract→ToolRegistryInterfaceDeterministicDriver→DeterministicAgentDriver
Agent
AgentStatenow implementsCanMarkExecutionStarted,CanMarkStepStarted,CanTrackExecutionTime- Added
AgentState::recordStep(),AgentState::failWith(),AgentState::withAddedExecutionTime() Agent::applyStep()andAgent::handleError()delegate toAgentStatemethods
StepByStep / Continuation
- New
CanProvideStopReasoninterface for criteria to provide explicit stop reasons - All continuation criteria implement
CanProvideStopReason:StepsLimit→StopReason::StepsLimitReachedTokenUsageLimit→StopReason::TokenLimitReachedExecutionTimeLimit,CumulativeExecutionTimeLimit→StopReason::TimeLimitReachedErrorPolicyCriterion→StopReason::ErrorForbadeFinishReasonCheck→StopReason::FinishReasonReceivedErrorPresenceCheck,RetryLimit→StopReason::GuardForbade
ContinuationEvaluationincludesstopReasonfield- Removed
ContinuationCriteria::inferStopReason()(stop reasons now come from criteria directly) - New state contracts:
CanMarkExecutionStarted,CanMarkStepStarted,CanTrackExecutionTime
Command Builders (agent-ctrl)
ClaudeCommandBuilder, CodexCommandBuilder, OpenCodeCommandBuilder:
stdbuf -o0prefix now conditional (checks availability)- Skipped on Windows
- Skipped when
stdbufnot found on PATH (fixes macOS) - Override via
COGNESY_STDBUFenv var:0= disable,1= force
Polyglot / Inference
InferenceExecution::usage(): fixed double-counting of current attemptInferenceResponse: added<think>...</think>tag parsing for reasoning content fallback- New
ReasoningContentSplitdata class AnthropicBodyFormat: cache marking applies only to last message in sequence (was all messages)DeepseekResponseAdapter: supportsreasoning,analysisfields as alternatives toreasoning_contentInference::with(): parameters now nullable (passnullto skip, was required empty values)PendingInference::asJson(),asJsonData(): use proper JSON extraction with output mode
HTTP Client
CurlHandle::close(): removedcurl_close()call (no-op since PHP 8.0, deprecated in PHP 8.5)
Instructor
StructuredOutputStream: fixed execution reference update during streaming to capture accumulated usage